Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
d2b984d
fix(completion): use the referenced version's inputs (#355)
X-Guardian Oct 22, 2026
8e40db7
refactor(errors): keep the errors barrel loadable without vscode (#356)
eFAILution Oct 22, 2026
7029ee6
chore(release): 0.21.0 [skip ci]
github-actions[bot] Oct 22, 2026
a913c18
refactor(core): add host-neutral version findings and cache lookups
eFAILution Oct 22, 2026
c86612a
refactor(validation): map core findings onto diagnostics in the provider
eFAILution Oct 22, 2026
c50529e
refactor(core): lift the reference resolver over a repo context port
eFAILution Oct 22, 2026
e653017
refactor(core): resolve local includes over a workspace port
eFAILution Oct 22, 2026
9693657
refactor(core): add host ports and route logger and HTTP config throu…
eFAILution Oct 22, 2026
85b979a
chore(release): 0.21.1 [skip ci]
github-actions[bot] Oct 22, 2026
8200c4c
refactor(core): read the remaining core settings through the config p…
eFAILution Oct 22, 2026
a00c93e
chore(release): 0.21.2 [skip ci]
github-actions[bot] Oct 22, 2026
a163429
fix(core): harden local includes, git spawning, and YAML handling
eFAILution Oct 22, 2026
05c7bb2
refactor(core): move tokens onto the secrets port and the auth prompt…
eFAILution Oct 22, 2026
8f556ac
fix(hover): only trust the detach command in hover markdown (#362)
eFAILution Oct 22, 2026
e308326
chore(release): 0.21.3 [skip ci]
github-actions[bot] Oct 22, 2026
6f25535
refactor(core): persist the component cache through the storage port …
eFAILution Oct 22, 2026
bcc918b
chore(release): 0.21.4 [skip ci]
github-actions[bot] Oct 22, 2026
5fa25f6
refactor(core): add a host-neutral include validator
eFAILution Oct 22, 2026
60e1b74
chore(release): 0.21.5 [skip ci]
github-actions[bot] Oct 22, 2026
6e2d336
feat(mcp): add a stdio MCP server with get_component_spec
eFAILution Oct 22, 2026
a26dede
feat(mcp): add Node adapters for config, tokens, storage, files, and …
eFAILution Oct 22, 2026
3a96317
feat(mcp): add validation, version, and usage tools
eFAILution Oct 22, 2026
8605244
feat(mcp): register the bundled MCP server with VS Code
eFAILution Oct 22, 2026
46dc54b
docs(mcp): add the MCP server guide and record the design decisions
eFAILution Oct 22, 2026
f626327
fix(mcp): allow self-managed GitLab on private networks
eFAILution Oct 22, 2026
1cafe4b
fix(mcp): hand tokens over exactly and keep host trust with the launcher
eFAILution Oct 22, 2026
0f7fac7
fix(mcp): harden paths, reads, output text, addresses, and the runtim…
eFAILution Oct 22, 2026
5eaa33a
fix(http): cap responses, add a request deadline, and bound tool calls
eFAILution Oct 22, 2026
9dc2fb1
fix(mcp): report failed lookups, bound walks, and harden the protocol…
eFAILution Oct 22, 2026
473e004
fix(mcp): let GCH_TOKEN_GITLAB_COM bind to gitlab.com by default
eFAILution Oct 22, 2026
9026d22
docs(mcp): document the token hand-off, host trust, limits, and mcp.e…
eFAILution Oct 22, 2026
dafc79b
fix(http): clear the deadline when a request fails to start
eFAILution Oct 22, 2026
5d1a35e
fix(mcp): clean tokens, keep network settings user-scoped, and vet sy…
eFAILution Oct 22, 2026
456a38b
docs(mcp): recommend GCH_TOKENS_JSON and describe token and symlink r…
eFAILution Oct 22, 2026
e8941e9
feat(mcp): turn the MCP server off by default
eFAILution Oct 22, 2026
03386f4
feat(mcp): hand tokens to the server through a one-time file
eFAILution Oct 22, 2026
90f766f
fix(mcp): only read hand-off files from the private hand-off folder
eFAILution Oct 22, 2026
6bada93
chore(release): 0.21.6 [skip ci]
github-actions[bot] Oct 22, 2026
8a42864
docs(readme): explain how to turn on and use the MCP server (#365)
eFAILution Oct 22, 2026
e5c1fd8
chore(release): 0.21.7 [skip ci]
github-actions[bot] Oct 22, 2026
c31d92c
docs(agents): load AGENTS.md from CLAUDE.md
eFAILution Oct 22, 2026
055f84e
ci(decisions): add an issue-keyed decision record checker
eFAILution Oct 22, 2026
2c73696
ci(decisions): run the decision record check on PRs and pushes
eFAILution Oct 22, 2026
eb7f335
docs(agents): key branches, PRs and decision records to the issue number
eFAILution Oct 22, 2026
c473c4a
Merge pull request #367 from eFAILution/chore/366-issue-keyed-workflow
eFAILution Oct 23, 2026
ab02781
chore(ai): regenerate .ai/index.yaml
eFAILution Oct 23, 2026
0cedce2
feat(mcp): report why a component fetch failed (#371)
eFAILution Oct 23, 2026
f1c5552
chore(release): 0.21.8 [skip ci]
github-actions[bot] Oct 23, 2026
325a947
fix(ci): skip the decision record branch rule on the release PR (#369)
eFAILution Oct 23, 2026
b3b39ed
chore(release): 0.21.9 [skip ci]
github-actions[bot] Oct 23, 2026
2d965bf
feat(mcp): cache component specs and start from the extension's cache…
eFAILution Oct 23, 2026
d3a5890
fix(component): resolve component versions that contain a slash (#375)
X-Guardian Oct 23, 2026
1bf3ab5
chore(release): 0.21.10 [skip ci]
github-actions[bot] Oct 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions .ai/architecture.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,7 @@ components:
requestDeduplicator.ts: Reuses pending promises for identical concurrent requests
performanceMonitor.ts: Singleton tracking slow operations (>1000ms threshold)
yamlParser.ts: Parse GitLab CI YAML files
logger.ts: Centralized logging
outputChannel.ts: VS Code output channel management
logger.ts: Centralized logging over the runtime log sink
gitlabVariables.ts: GitLab CI variable expansion
depends_on:
- types
Expand All @@ -132,6 +131,43 @@ components:
PersistentCacheData
api.ts: GitLab API request/response types
index.ts: Barrel export
core:
location: src/core/
purpose: Host-neutral ports, runtime, and validation logic shared by the extension and the MCP server
files:
ports.ts: ConfigPort, SecretsPort, StoragePort, LogSink, AuthPromptPort, NetworkPolicyPort
runtime.ts: setRuntime/getRuntime/lockRuntime; defaults need no setup
referenceResolver.ts: Expands CI variables in component references over a RepoContextPort
localInclude.ts: Resolves and parses local includes over a WorkspacePort
validation/: validateIncludes, versionFindings, findings (codes, messages, ranges, suggestedEdits)
depends_on:
- utils
- types
notes: Must load under plain Node; never import vscode (tests/unit/coreValidation.test.ts checks)
adapters:
location: src/adapters/
purpose: Port implementations per host
files:
vscode/: Config, SecretStorage, globalState, output channel, auth prompt, workspace, MCP definition provider
node/: git CLI repo context, bounded file reads
depends_on:
- core
mcp:
location: src/mcp/
purpose: Standalone MCP server (stdio JSON-RPC) exposing validation and component tools to agents
files:
main.ts: Entry point; claims stdout for the transport, bootstraps, serves
bootstrap.ts: Builds and locks the runtime from args and env
server.ts: Dispatcher, tool registry, argument validation, result size cap
protocol/: JSON-RPC parsing, MCP types and version negotiation, schema validation
transport/stdio.ts: Newline-delimited JSON over streams; the only stdout writer
adapters/: Layered config, env tokens, file storage (0600), guarded workspace, network allowlist, git path
tools/: validate_ci_file, get_component_spec, check_component_versions, list_component_usages, list_component_versions
launchConfig.ts: Pure args/env/token hand-off the VS Code provider uses
depends_on:
- core
- services
notes: Bundled to out/mcp/server.js by esbuild.mcp.js; the build fails on any vscode import
scripts:
location: scripts/
purpose: Build, release, and automation scripts
Expand All @@ -143,6 +179,9 @@ components:
publish-release.sh: Publish to VS Code marketplace
release-and-publish.sh: Combined release and publish
setup-github-token.sh: GitHub token configuration
ci/decisionRecords.ts: 'Decision record CLI (check, list, new), run by the Decision Records workflow (ADR-366)'
ci/decisionRecordsStore.ts: Loads both decision stores and shape-checks record files
ci/decisionRecordsBranch.ts: Branch rule; a record a PR adds must carry the branch's issue number
demo_gifs:
location: scripts/demo-gifs/
purpose: Maintainer tool that records the README demo GIFs by driving a real VS Code through Playwright over CDP
Expand Down
12 changes: 10 additions & 2 deletions .ai/context.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ project:
type: plugin
language: typescript
node_version: '>=22.0.0'
vscode_version: '>=1.109.0'
vscode_version: '>=1.120.0'
description: VS Code extension providing intellisense for GitLab CI components
purpose: Smart autocomplete, hover docs, and component browsing for GitLab CI/CD
repository:
Expand All @@ -14,6 +14,8 @@ repository:
entrypoints:
extension: src/extension.ts
main_output: out/extension.js
mcp_server: src/mcp/main.ts
mcp_output: out/mcp/server.js
structure:
source: src/
compiled: out/
Expand All @@ -29,11 +31,15 @@ structure:
types: src/types/
utils: src/utils/
scripts: scripts/
decision_records: .ai/decisions/ (ADR-<issue>.yaml, one record per issue; .ai/decisions.yaml is closed to new keys
since #366; npm run decisions:list shows both)
decision_records_checker: scripts/ci/decisionRecords.ts
demo_gifs: scripts/demo-gifs/
context_modules:
architecture: architecture.yaml
workflows: workflows.yaml
decisions: decisions.yaml
decision_records: decisions/
errors: errors.yaml
dependencies:
runtime:
Expand All @@ -52,6 +58,8 @@ common_tasks:
test: npm test
test_extension_host: npm run test:extension-host
package: npm run package
decisions_check: npm run decisions:check
decisions_list: npm run decisions:list
build_system:
bundler: esbuild
config: esbuild.js
Expand Down Expand Up @@ -81,7 +89,7 @@ release:
dry_run_main: npm run release:main:dry
dry_run_beta: npm run release:beta:dry
constraints:
- Must work on VS Code 1.102.0+
- Must work on VS Code 1.120.0+
- Compatible with Node.js 22+
- Support both gitlab.com and self-hosted GitLab instances
- Component data cached for performance
Expand Down
101 changes: 101 additions & 0 deletions .ai/decisions.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Closed to new keys since #366. Existing entries here can still be edited.
# Record a new decision as .ai/decisions/ADR-<issue>.yaml instead (see AGENTS.md,
# "Issues, branches, and records"). `npm run decisions:list` shows both stores.
decisions:
USE_ESBUILD:
date: '2024'
Expand Down Expand Up @@ -459,6 +462,104 @@ decisions:
- scripts/demo-gifs/record.mjs
- .ai/workflows.yaml (record_demo_gifs)
title: Demo GIFs Recorded By Script
MCP_STDIO_HANDROLLED:
date: '2026-10'
status: accepted
context: Agents need the extension's component validation; the official MCP SDK pulls in zod, ajv, express, hono,
jose and more
decision: Hand-roll MCP over stdio (newline-delimited JSON-RPC 2.0) in src/mcp, about 300 lines
rationale:
- Only initialize, ping, tools/list, and tools/call are needed
- Keeps the bundle small and the supply chain short, like the hand-rolled httpClient
- Full control over output shaping and stdout hygiene
implementation:
versions: 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05 (SUPPORTED_PROTOCOL_VERSIONS in src/mcp/protocol/types.ts)
structured_content: only for 2025-06-18 and later; a text block is always sent
stateless_2026_07_28: not implemented; server/discover returns method-not-found so dual-era clients fall back
alternatives_considered:
- name: '@modelcontextprotocol/sdk'
rejected_because: Large dependency tree for a small protocol surface
consequences:
positive:
- Small bundle, few dependencies
negative:
- New spec revisions need manual work
references:
- src/mcp/server.ts
- src/mcp/protocol/
title: MCP Over Hand-Rolled Stdio
MCP_SERVER_NEVER_WRITES_FILES:
date: '2026-10'
status: accepted
context: Agents act on repositories with their own edit and approval flow
decision: The MCP server is read-only; findings carry suggestedEdits (file, range, newText, title) for the agent to apply
rationale:
- Edits go through the agent's existing review UI
- A compromised or confused server can't change files
consequences:
positive:
- Every tool is readOnlyHint
negative:
- Fixes that need judgment (missing inputs) come as data plus the spec, not as edits
references:
- src/mcp/tools/findingOutput.ts
- src/core/validation/versionFindings.ts
title: MCP Server Never Writes Files
MCP_DISTRIBUTED_AS_RELEASE_ASSET:
date: '2026-10'
status: accepted
context: Users outside VS Code need a way to run the server
decision: Ship out/mcp/server.js inside the VSIX now, and as a GitHub release asset next; publish to npm later, only
if users ask
rationale:
- VS Code users get it with the extension and no setup
- Avoids running an npm account and trusted publishing before there is demand
consequences:
positive:
- One version for the extension and the server
negative:
- Other clients need the extension's install path (or the release asset) instead of npx
references:
- esbuild.mcp.js
- docs/mcp.md
title: MCP Distributed As A Release Asset
MCP_TOKENS_VIA_HANDOFF_FILE:
date: '2026-10'
status: accepted
context: The server runs as a child process that VS Code starts from an McpStdioServerDefinition, which only takes
command, args, and env. A process's starting environment stays readable to the same OS user (ps eww,
/proc/<pid>/environ) even after the process deletes a variable, so an AI agent running terminal commands could
read tokens passed in env
decision: In resolveMcpServerDefinition, the extension writes one exact host-to-token map to a one-time file in
its globalStorage (0600 in a 0700 dir, random name) and passes only its path as GCH_TOKENS_HANDOFF. The server
reads it once at startup and deletes it
rationale:
- Nothing secret sits in the server's environment or command line
- The definition API has no stdin or pipe for secrets (stdin is the MCP channel)
- An exact host key avoids the lossy GCH_TOKEN_<HOST> naming, where gitlab-example.com and gitlab.example.com
share a variable (a review found a repo could claim a token that way)
mitigations:
- The server refuses a hand-off that is a symlink, over 64 KB, readable by group/other, or owned by another user
- The extension deletes its own unread hand-off files after 30s and on dispose, and purges stale ones (older
than 30s, since windows share the directory) at activation
- Child processes (git) get an env stripped of GCH_* and GITLAB_TOKEN
- The extension picks token hosts from the token index and the user's global componentSources only, never
workspace or folder settings
- GCH_TOKEN_<HOST> (terminal use) binds only to hosts in GCH_TOKEN_HOSTS or --config, and not at all when two
such hosts share a name
- Only launcher-trusted hosts (--config, GCH_TRUSTED_HOSTS from user settings, token hosts, gitlab.com) are
contacted; repo files can't widen the allowlist or set network settings
- gitlabToken is refused as a setting; tokens are never accepted as tool arguments or logged
- gitlabComponentHelper.mcp.enabled (application scope, default off) gates the server
consequences:
negative:
- The token file exists on disk for the milliseconds between writing and the server reading it
- Terminal users passing GCH_TOKEN_* or GCH_TOKENS_JSON still have the usual env exposure
references:
- src/mcp/tokenHandoff.ts
- src/adapters/vscode/mcpDefinitionProvider.ts
- src/mcp/adapters/secrets.ts
title: MCP Tokens Via A One-Time Hand-Off File
future_decisions:
- question: Should we support other CI/CD platforms (GitHub Actions, CircleCI)?
status: under_consideration
Expand Down
88 changes: 88 additions & 0 deletions .ai/decisions/ADR-366.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Decision record for issue #366. One record per file, keyed to its issue.
# How to add one: AGENTS.md, "Issues, branches, and records".
version: '2.0'
decisions:
ADR-366:
title: Key branches, PRs and decision records to the issue number
date: '2026-10-22'
status: accepted
context: |-
Issue #366. Several agent sessions and people now work on this repo at
the same time, each in its own worktree and branch. Three things kept
colliding.

Branch names had no shared allocator. AGENTS.md asked for
feature/description or fix/description, so two sessions picking up
related work could choose the same name, and nothing on a branch said
which issue it was for.

Every decision went into .ai/decisions.yaml, one mapping with one
append point at the end. Two PRs that each recorded a decision edited
the same lines and conflicted, even though their keys differed. A
sloppy merge could also leave a key twice, and a YAML reader that
keeps the last copy would drop the first decision without a word.

.ai/index.yaml lists every decision key. The AICaC action regenerates
and commits it, so each new key also meant a change to that shared
file.
decision: |-
Use the GitHub issue number as the key for a change and everything it
produces.

- Every change starts from an issue, one PR per issue. Big work is
split into sub-issues, each with its own PR.
- Branches are named <type>/<issue>-<short-description>, with a
commitlint type and the number right after it, for example
chore/366-issue-keyed-workflow. A sub-issue's PR uses the
sub-issue's number.
- The PR body opens with Closes #<issue>, or Refs #<issue> for partial
work. A draft PR goes up with the first commit so others can see the
issue is taken.
- A new decision is its own file, .ai/decisions/ADR-<issue>.yaml. Each
file is a full AICaC v2 document (version: '2.0' and a decisions
mapping) with exactly one key, ADR-<issue>, and the same entry
fields the legacy file uses.
- .ai/decisions.yaml keeps its twenty SCREAMING_SNAKE entries. It is
closed to new keys; existing entries can still be edited.
- No index is kept by hand. The list command builds one from both
stores at read time.
- scripts/ci/decisionRecords.ts checks all of this, and the Decision
Records workflow runs it on every PR and push to main and beta. It
fails on duplicate keys, new legacy keys, badly named or badly shaped
record files, dangling supersedes, and a record whose number is not
the branch's issue. A branch with no issue number that adds no record
only gets a warning, so Dependabot, forks and the beta to main release
PR still pass.
rationale:
- GitHub hands out each issue number once, so it is a unique allocator nobody has to coordinate.
- The number exists before any code does, so a branch and a record can carry it from the first commit.
- One file per record means two PRs that each add a decision touch different files and never conflict.
- Leaving the legacy entries in place keeps every existing reference to .ai/decisions.yaml working.
- Each record being a full AICaC document keeps the format the rest of .ai/ uses, so no converter is needed.
alternatives_considered:
- name: Keep the single file and add only a duplicate-key check
rejected_because: It catches a clash after both PRs are written, and new entries still append at the same spot, so the merge conflicts stay.
- name: Keep the single file and prefix new keys with ISSUE_<n>_
rejected_because: The keys stop clashing, but every new entry still lands at the end of one mapping, so two PRs still edit the same lines.
- name: Move all legacy entries into per-file records now
rejected_because: A large mechanical diff that rewrites the target of every existing reference, for no reader that needs one layout today. It can still be its own issue later.
consequences:
positive:
- Parallel PRs can each add a decision without conflicts or renumbering.
- A branch name, a PR and a record all say which issue they belong to.
- A duplicate key in either store fails CI instead of being silently dropped.
negative:
- Decisions live in two places. Readers use the list command or look in both.
- The AICaC validator only reads .ai/*.yaml, so the checker repeats part of its decisions schema for the record files.
- Branch names are checked only for PRs that add a record. Other branches get a warning, and a ruleset is needed to enforce prefixes.
- The draft-PR-first and check-before-you-start steps are conventions that rely on people and agents following AGENTS.md.
implementation: |-
scripts/ci/decisionRecords.ts (CLI: check, list, new),
scripts/ci/decisionRecordsStore.ts (loading and shape checks),
scripts/ci/decisionRecordsBranch.ts (branch rule),
tests/unit/decisionRecords.test.ts, .github/workflows/decision-records.yml,
npm scripts decisions:check, decisions:list and decisions:new, AGENTS.md
"Issues, branches, and records", .github/pull_request_template.md, and
.ai/workflows.yaml start_change_from_issue.
references:
- https://github.com/eFAILution/gitlab-component-helper/issues/366
Loading
Loading