All notable changes to the Pipeline-Check VS Code extension. The format follows Keep a Changelog; versions follow SemVer.
⚠ Release note for publish.yml: the release-notes extractor (an
awkscript in publish.yml) prints every line between the first and second## [headers. When cutting a release, fold the entries under## [Unreleased]into the new## [X.Y.Z] — <date>section above Unreleased, or remove the Unreleased block for the release commit. Otherwise the GitHub release ships boilerplate.
- Status bar background colour reflects severity. A workspace with
any CRITICAL finding tints the bar to
statusBarItem.errorBackground(red in the default themes); a workspace with HIGH but no CRITICAL tints tostatusBarItem.warningBackground(yellow). MEDIUM / LOW / INFO keep the default fg colour so a "1 medium" workspace doesn't shout. Same ThemeColor tokens ESLint and Error Lens use, so the visual language reads correctly to any existing VS Code user. - "What's new" notification on upgrade. First activation after a version bump shows a one-time toast — "Pipeline-Check 0.X.Y is here …" — with a "See release notes" button that opens the matching GitHub release. Persists the seen-version before showing so a missed dismissal doesn't loop next launch. Suppressed when the stored version equals the manifest version (same launch).
Pipeline-Check: Scan Workspacecommand. Walks every CI/config file in the workspace (matching the same patterns the LSP'sdocumentSelectoruses), opens each viavscode.workspace.openTextDocumentso the LSP'sdidOpenpipeline picks them up, and lets the Findings panel re-render from the diagnostic stream as scans complete. Progress toast with cancellation; partial failures (read errors, unsupported encodings) are counted but don't abort the scan. Surfaced from a$(play)button on the Findings view title bar, the Command Palette, and a link in the Findings welcome state. (R10, R15)
- "Refresh Findings" now triggers a real scan instead of just re-painting the tree from already-published diagnostics. Matches the user's mental model: clicking a refresh icon should fetch new data, not re-render stale data. (R10)
SCAN_PATTERNSremoved in favour ofTRIGGER_PATTERNSfromproviders.ts. The single source of truth for which files are CI-relevant now drives the documentSelector, the activationEvents, and the workspace scan — three surfaces that used to drift apart.
Closes 24 of 29 items from the 2026-05-19 in-depth UX/code review.
Adds the activity-bar Findings tree's missing affordances (status bar,
CodeLens, navigation, context menus, per-provider toggles), the
release-tooling polish (production environment gate, pre-release
channel, three-OS CI, integration tests), and the discovery /
accessibility pass.
Heads-up for users with non-standard workflow paths: the
extension's activationEvents now match only the
workspaceContains: patterns shared with the LSP documentSelector
(plus onStartupFinished so the activity-bar slot is always
visible). If your repo keeps CI definitions outside the standard
locations (e.g. pipelines/build.yml instead of
.github/workflows/*.yml), the extension still activates on
onStartupFinished, but the LSP only scans files matching the
document selector. Use pipelineCheck.serverArgs to point the LSP
at a different path or symlink your custom config into a standard
location.
- Inline CodeLens summary. Each scanned file carries a
Pipeline-Check: 2 critical · 1 highlens at line 1. Click reveals the Findings panel. Re-emits on every diagnostic publish so the text tracks the latest scan. (R26) - Status bar item. Bottom-left of the window, shows the top two
non-zero severities (e.g.
$(shield) 3C 1H) with a tooltip that breaks down the full per-severity tally. Click reveals the Findings panel. (R9) - Keyboard navigation.
Alt+F8/Shift+Alt+F8jump between Pipeline-Check findings in editor order (fsPath ascending, then by line); wraps at both ends. Mirrors VS Code'sF8muscle memory for the global "Next Problem" command. (R12) - Per-provider toggles. New
pipelineCheck.disabledProviderssetting silences whole providers.dockerfilecovers bothDockerfileandContainerfile(same syntax). Useful in a monorepo where Pipeline-Check would otherwise lint a sub-project's Dockerfile that has its own lint pipeline. (R25) - Rule documentation link in leaf tooltip. When the server
publishes a
Diagnostic.code.targetURL, the Findings tree's leaf tooltip appends a clickable$(book) <rule-id> documentationlink below the message body. (R8) - Client-side structured logging. The extension's output channel
now interleaves
[client] HH:MM:SS.mmmlines around activation and command invocations with the LSP'swindow/logMessagetraffic. Easier to triage bug reports — start/ok/failed breadcrumbs land in the same surface users already focus via Show language server output. (R16) - Pre-release channel. Tags like
v0.2.0-rc.1ship to the marketplace pre-release channel; the matching GitHub release is markedprerelease. Detection is by the presence of a-after the semver core. (R24) - Right-click context menu on Findings tree leaves. Open Rule
Documentation opens the URL the server published via
Diagnostic.code.targetin the system browser; Copy Rule ID writes the rule's identifier to the clipboard. Same data the leaf tooltip already surfaces, now available without keeping the tooltip open. pipelineCheck.codeLens.enabledsetting. Defaults totrue. Hides the line-1 file-summary CodeLens for users who find it intrusive without disabling CodeLens globally. Toggle takes effect on the next render — no extension restart.pipelineCheck.copyInstallCommandcommand. Copiespip install "pipeline-check[lsp]"to the clipboard. Surfaced from the Findings welcome state and from the Command Palette so users can re-find it after dismissing the first-run error toast.
-
Welcome state of the Findings panel teaches. Now leads with what Pipeline-Check does + a Copy install command link for the Python
[lsp]extra, then onboarding ("open a workflow…"), then the Alt+F8 / Shift+Alt+F8 keyboard hint, then a---separator and the recovery actions (Restart, Open Log) demoted below. -
onStartupFinishedactivation event. The extension now wakes up after VS Code's start-up barrier so the activity-bar slot is visible in every workspace — not just ones with aworkspaceContains:match. The LSP child process still only spawns when thedocumentSelectormatches an open document, so there's no idle-Python-process cost. -
Status bar item hides in non-CI workspaces. On activation we do a one-shot
findFilesfor any of the trigger patterns; the status bar item only shows once we've seen evidence the workspace is CI-relevant (either a match or an actual diagnostic publish). Stops$(shield) cleancluttering the bottom-left in frontend projects that happen to have Pipeline-Check installed alongside other linters. -
Status bar accessibility label. Screen readers now hear "Pipeline-Check: 3 critical, 1 high" instead of the codicon shortcode + letter-by-letter abbreviation.
-
Status bar tooltip teaches Alt+F8. The trailing line of the tooltip ("Alt+F8 / Shift+Alt+F8 to step through findings") is the primary discovery surface for the navigation keybindings.
-
Command titles use title case for VS Code's convention: "Restart Language Server", "Show Language Server Output", "Refresh Findings". Existing "Go to Next Finding" and "Change Grouping" stay the same. Command IDs are unchanged — settings, keybindings, and automation continue to work.
-
@vscode/test-electronintegration suite now runs in CI (Linux only, viaxvfb-run -a). Five tests pin activation, the command-registration contract, the Findings view registration, the configuration schema completeness, and the workspace-trust capability declarations. Catches what unit tests can only approximate. (R17) -
Three-OS test matrix —
[ubuntu-latest, windows-latest, macos-latest]. The LSP child-process spawn path is Windows-sensitive; matrix CI catches the LF/CRLF and path-separator bugs single-OS CI silently misses. (R21) -
Activation surface narrowed. Triggers are
workspaceContains:patterns matching the providers we actually scan (thedocumentSelectoruses the same patterns). Opening an unrelatedpackage.jsonormkdocs.ymlno longer wakes up the extension. (H4) -
Trigger-pattern list lives in one place. Extracted into
src/providers.tsas a singlePROVIDERSmap; thedocumentSelector,activationEvents, and the LSP middleware's per-provider filter all read from it. A regression test asserts the manifest'sactivationEventsstay in lockstep with the patterns. (R14) -
Shared
vi.mock("vscode")factory undersrc/__testStubs__/. Unit tests now share a single stub instead of redefining the surface per file. (R18) -
Marketplace description length gated in CI at the 145-character truncation point so future edits don't blow it. (R20)
Restart language servertoast no longer fires on failure — if the server failed to come up, the error notification already carries the install hint; the success toast used to fire too, giving the user contradictory signals. (R2)stopClienthas a 2-second hard ceiling on the LSP child's shutdown. A deadlocked server used to hold the deactivate path indefinitely; VS Code reported "Window not responding" until the user force-quit. (R3)groupByFileno longer round-trips Uri through string for every group node. Bucket value carries the original Uri. (R4)compareByLocationsorts onfsPathinstead of the full URI string. Cross-scheme entries (file:// vs untitled://) no longer bunch at one end. (R5)collectFindingsis memoised per refresh — buildRoot and updateBadge used to walk the global diagnostic store twice per refresh. (R6)onDidChangeDiagnosticsskips refreshes from unrelated publishers. ESLint / mypy / redhat.yaml keystroke chatter no longer rebuilds the tree. The skip-check also catches clears (a stale leaf can't outlive a cleared file). (R7)
Production-readiness pass. v0.1.0 was effectively unusable on a clean install (see Fixed below); v0.1.1 is the first release that actually loads in VS Code. Also lands the Findings panel and the security hardening from the pre-marketplace review.
-
Findings panel. A dedicated activity-bar slot ("Pipeline-Check" — custom inverted-Y pipeline glyph at
media/pipeline-check.svg) carries aFindingstree that re-groups the diagnostics the LSP server has already published. Strictly a re-presentation: never triggers its own scan, so the thin-transport-adapter promise inextension.tsstays intact. The activity-bar icon carries a live count badge so "how many findings does this workspace have right now?" is answerable without expanding the panel. Three group modes — severity (default), file, rule — are switched via aChange GroupingQuick Pick that marks the active mode with$(check). Each leaf renders as the rule title plus aRULE · file:LINEdescription that drops whichever component is already implied by the parent group; clicking opens the file at the diagnostic range. CRITICAL is rendered asflameand HIGH aserrorso the two distinguish in the severity-grouped tree without breaking parity with the editor gutter (which has no "more red than red" state); INFO usescircle-outlinethemed todescriptionForegroundso it is visibly the quietest row instead of inheriting the default foreground. The welcome state leads with what the extension does rather than what is missing; the diagnostic recovery links sit on a secondary "Not seeing findings?" line. -
pipelineCheck.severityThresholdsetting. A new enum knob (low/medium/high/critical, defaultlow) that mirrors the CLI's--severity-threshold. Drives a client-sidehandleDiagnosticsmiddleware that filters out diagnostics whose upstream pipeline-check severity falls below the threshold before they reach the gutter or Problems panel, so the editor surface can be tuned independently of the CLI's report. The filter readsDiagnostic.data["severity"](set by the v1.0.6 server) so it can distinguishCRITICALfromHIGH(both map to LSPError). Diagnostics without thedata.severitymetadata pass through unconditionally, so an older server (or a non-pipeline-check publish) is never hidden.
pipelineCheck.serverCommandandpipelineCheck.serverArgsare nowmachine-overridable. Workspace overrides require an explicit prompt, so a malicious.vscode/settings.jsoncan't silently swap the interpreter or inject-c "<code>"once the user trusts the workspace.- Declared
capabilities.untrustedWorkspaces: "limited"andvirtualWorkspaces: false. The extension stays inactive in untrusted workspaces until the user trusts them, so the LSP child process never spawns from a freshly-cloned, untrusted repo. - Hardened the publish workflow. Pinned
@vscode/vsceandovsxto specific versions (no more@latestwith PATs in env), added agit merge-basecheck that refuses to publish a tag that isn't onmain, added a CHANGELOG-fold check, and narrowed workflow-level permissions tocontents: readwith the publish job opting up tocontents: write. The publish job is gated on theproductionGitHub Environment soVSCE_PAT/OVSX_PATare only readable from a run that has cleared required reviewers. - Added SECURITY.md with GitHub Private Vulnerability Reporting as the disclosure channel, response SLAs, and a published threat model.
- Vitest unit suite added. 25 tests covering the severity threshold
filter (extracted into src/severityFilter.ts)
and the Findings tree's pure logic (collection from
diagnostics, group-by-severity / file / rule, severity normalisation,
no-refresh-storm contract).
npm testruns the suite; both ci.yml and publish.yml gate on it. Test files live next to the code they cover and are stripped from the .vsix.
- The published
.vsixwas missing its runtime dependency. The previous build emittedout/extension.jsviatscbut excludednode_modules/from the package, sorequire("vscode-languageclient/node")threw on activation in a clean install. Now bundled with esbuild into a singledist/extension.js(the only JS in the.vsix); a CI smoke step (scripts/smoke.js) stubs thevscodemodule, loads the bundle, and assertsactivate/deactivateare exported so this regression class fails the build instead of the user.
-
npm audit --omit=dev --audit-level=highnow runs on every push tomainso advisories filed after a PR has merged still surface. -
Activation tightened. The extension used to activate on every YAML / JSON / Dockerfile / Terraform / Groovy file in any workspace, then rely on the server's content filter to drop unrelated documents.
activationEventsis now aworkspaceContains:list of the trigger files we actually scan (.github/workflows/*,.gitlab-ci.yml,azure-pipelines.yml, etc.). The LSP'sdocumentSelectoris switched from language IDs to matching file-path globs, so the server only sees candidate documents — no more spurious activations onpackage.json,mkdocs.yml, or a Helmvalues.yaml. -
@vscode/vsceandovsxare pinned devDependencies. Workflows invoke them via the locally-installed binaries (npx vsce,npx ovsx) afternpm ci. Versions live inpackage-lock.jsonand Dependabot's existing npm config keeps them current. -
Marketplace metadata polish. Added
Othertocategories, pointedqnaat the repo Discussions page. -
Marketplace polish pass. The
package.jsondescriptionis rewritten so the numbers that differentiate this extension (22 providers, 14 compliance frameworks beyond OWASP Top 10 CI/CD, 810+ rules) land in the first 100 characters and survive the ~145-character truncation the marketplace search results impose. Command titles renamed to Restart language server and Show language server output so the palette entries are self-disambiguating without leaning on thePipeline-Checkcategory prefix. Every configuration setting switched fromdescriptiontomarkdownDescriptionso backtick'd literals (python,python -m pipeline_check.lsp) render as code in the Settings UI rather than plain quoted text. -
README restructured for the marketplace listing. The first paragraph carries the same numbers as the marketplace description, followed by a What it scans provider table (the differentiating content that prospective users land on the listing to see). Dropped the
## Statussection (the marketplace already shows the version in the listing's metadata bar). Collapsed the architecture diagram behind a<details>block near the bottom of the page; it's a useful engineering reference but not the first scroll of a marketplace listing. -
Start-failure notification carries two actions, not one. When
python -m pipeline_check.lspfails to launch (Python missing fromPATH,[lsp]extra not installed, server crash on import), the notification now exposes Copy install command alongside Open server log. The user can act on either without re-reading the message body. Notification copy stripped of the redundantPipeline-Check:prefix that the notification chrome already shows.
First public release. Wires the editor surface to the upstream
pipeline_check.lsp
server.
- TypeScript LSP client that spawns
python -m pipeline_check.lspover stdio (vscode-languageclientv9). Document selector covers YAML, JSON, Dockerfile, Terraform, and Groovy files; the server filters further by path and content so unrelated documents are not analyzed. - Pilot provider coverage: GitHub Actions, GitLab CI, Azure Pipelines, Bitbucket Pipelines, CircleCI, Google Cloud Build, Buildkite, Drone, Jenkins, and Dockerfile. Multi-file / context-heavy providers (Kubernetes, Helm, Terraform plans, live AWS, CloudFormation, SCM posture) follow in a later release.
- Three configuration knobs:
pipelineCheck.serverCommand,pipelineCheck.serverArgs, andpipelineCheck.trace.serverfor overriding the Python interpreter, module path, and LSP trace level. - Two commands under the
Pipeline-Checkcategory:- Restart server — stops the running client and respawns.
- Show server log — focuses the
Pipeline-Checkoutput channel wherewindow/logMessagetraffic lands.
- Graceful start-failure handling: when
python -m pipeline_check.lspfails (Python not onPATH,[lsp]extra not installed, server crash on import), the editor surfaces a notification with the install hint plus anOpen server logbutton. - Two F5 debug profiles in
.vscode/launch.json:- Run Extension — fresh extension-host window, no workspace.
- Run Extension (sample workflow) — opens
test-fixtures/sample-workflow/with a deliberately-vulnerable GitHub Actions workflow that fires GHA-001, GHA-004, GHA-015, and GHA-016 on open.
- VS Code 1.85+.
- Python 3.11+ with
pipeline-check[lsp]installed:pip install "pipeline-check[lsp]"