Skip to content

feat: add GitHub Copilot and VS Code integrations - #71

Open
AntonioLujanoLuna wants to merge 2 commits into
zvec-ai:mainfrom
AntonioLujanoLuna:claude/copilot-cli-integration-l2o6co
Open

AntonioLujanoLuna wants to merge 2 commits into
zvec-ai:mainfrom
AntonioLujanoLuna:claude/copilot-cli-integration-l2o6co

Conversation

@AntonioLujanoLuna

Copy link
Copy Markdown

Adds copilot and vscode as zg install / zg uninstall targets, so GitHub Copilot CLI, Agent Host, and VS Code join Codex, Claude Code, Qwen Code, Qoder, OpenCode, and Cursor as supported agents.

Both reuse the existing installer machinery — the JSON/JSONC MCP helpers and the ZVEC_GREP_START/ZVEC_GREP_END marked-block writer — so unrelated servers, unrelated instructions, JSONC comments, and unmanaged zvec_grep entries are preserved, --force is still required to replace an unmanaged entry, and uninstall removes only zvec-grep-owned state.

copilot

Manages the user-level Copilot configuration under ${COPILOT_HOME:-~/.copilot}:

  • mcp-config.json — a managed zvec_grep entry under mcpServers, type: "local" (zg server --stdio) for stdio transport, type: "http" with an optional Authorization header for HTTP.
  • copilot-instructions.md — the existing zvec-grep search-routing guidance, so Copilot learns when to prefer semantic search over native grep.

The entry sets tools to ["*"] so the server-side --mcp-toolset selection stays the single place that decides which tools are exposed. Pinning a tool list here would silently truncate --mcp-toolset full and hide zvec_grep_rg, which the installed guidance itself references.

vscode

Agent Host reads ~/.copilot/mcp-config.json natively, so the copilot target already covers it — but not VS Code's own agent mode, which keeps its servers in mcp.json and forwards them to Agent Host. Hence a separate target.

It manages the mcp.json of the default user profile, resolved the way VS Code resolves it: VSCODE_PORTABLE, then VSCODE_APPDATA, then the per-platform application data directory. VSCODE_USER_DIR points at another profile, Insiders, or a derivative.

Two constraints shaped the entry, and they are why it is not simply a copy of the Copilot one:

  • VS Code validates servers entries with additionalProperties: false, so the entry carries only fields from its stdio and HTTP schemas. There is no per-server timeout or tool allowlist to manage, and --mcp-tool-timeout does not apply.
  • An HTTP token is referenced as ${env:NAME} rather than ${input:NAME}, which keeps the entry forwardable to Agent Host — VS Code does not forward servers that require interactive input.

Guidance goes to ${COPILOT_HOME:-~/.copilot}/instructions/zvec-grep.instructions.md, the harness-agnostic user instructions folder that VS Code, Agent Host, and Copilot CLI all read, with the applyTo: '**' frontmatter that makes VS Code apply it automatically. Uninstall removes the managed block and deletes the file when nothing else remains in it.

The two targets deliberately keep separate guidance files rather than sharing one: Copilot CLI applies copilot-instructions.md on every turn, while a modular .instructions.md file is path-scoped. Collapsing them would have weakened the CLI case. The overlap is documented in docs/01-agents.md.

Supporting change

The JSONC MCP helpers now take a container key, so VS Code's servers object reuses the comment-preserving machinery that Qoder uses for mcpServers. The default is unchanged, so existing call sites are untouched.

Tests

14 new tests in test/install.test.mjs covering, for each target: the stdio entry and guidance with unrelated configuration preserved, idempotent re-install, the HTTP entry, --mcp-toolset propagation, --force against an unmanaged entry, uninstall leaving an unmanaged entry alone, auto-detection, and help output. VS Code additionally covers JSONC comment preservation, VSCODE_PORTABLE resolution, frontmatter ordering, user-authored guidance surviving uninstall, and a field-set assertion pinning each entry to the schema VS Code accepts.

Auto-target tests now pin VSCODE_USER_DIR at a missing path, so a VS Code profile on a contributor's own machine cannot be detected by them.

Verification

npm run check passes end to end — lint, format, typecheck, coverage thresholds, and package tests, exit 0 with no failures.

The generated mcp.json was validated against the JSON schema VS Code itself uses for MCP configuration, in both stdio and HTTP form, with a negative control confirming the schema was actually being enforced. That is schema conformance rather than a live editor session; loading the server in a real VS Code window is worth confirming by hand before release.

Out of scope: the Copilot cloud coding agent and Copilot code review, which use separate repository-level MCP configuration. zvec-grep indexes a local workspace, so those hosted surfaces cannot reach it.

Add `copilot` as a `zg install` / `zg uninstall` target so GitHub Copilot
joins Codex, Claude Code, Qwen Code, Qoder, OpenCode, and Cursor as a
supported agent.

The installer manages the user-level Copilot configuration under
`${COPILOT_HOME:-~/.copilot}`:

- `mcp-config.json` receives a managed `zvec_grep` entry under `mcpServers`,
  using `type: "local"` (`zg server --stdio`) for stdio transport and
  `type: "http"` with an optional `Authorization` header for HTTP transport.
- `copilot-instructions.md` receives the existing zvec-grep search-routing
  guidance inside the standard `ZVEC_GREP_START`/`ZVEC_GREP_END` markers,
  so Copilot learns when to prefer semantic search over native grep.

The managed entry sets `tools` to `["*"]` so the server-side `--mcp-toolset`
selection stays the single place that decides which tools are exposed;
pinning a tool list here would silently truncate `--mcp-toolset full`.

Both files reuse the existing JSON MCP and marked-block machinery, so
unrelated MCP servers, unrelated instructions, and unmanaged `zvec_grep`
entries are preserved, `--force` is still required to replace an unmanaged
entry, and uninstall removes only zvec-grep-owned state.

Copilot CLI and the VS Code agent host both read this user-level
`mcp-config.json`, so one install covers both. The Copilot cloud coding
agent and code review use separate repository-level configuration and are
out of scope here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YPkQZNkK72inHZobHhrCrd
Add `vscode` as a `zg install` / `zg uninstall` target. The `copilot` target
covers Copilot CLI and Agent Host, which read `~/.copilot/mcp-config.json`
natively, but not VS Code's own agent mode: VS Code keeps its servers in
`mcp.json` and forwards them to Agent Host.

The installer manages the `mcp.json` of the default VS Code user profile, so
the server is available across every workspace. The profile directory is
resolved the way VS Code resolves it: `VSCODE_PORTABLE`, then `VSCODE_APPDATA`,
then the per-platform application data directory, with `VSCODE_USER_DIR`
available to point at another profile, Insiders, or a derivative.

VS Code validates `servers` entries with `additionalProperties: false`, so the
managed entry carries only fields from its stdio and HTTP schemas — no timeout
or tool allowlist, unlike the Copilot CLI entry. An HTTP token is referenced as
`${env:NAME}` rather than `${input:NAME}` so the entry stays forwardable to
Agent Host, which drops servers that need interactive input.

Search guidance goes to
`${COPILOT_HOME:-~/.copilot}/instructions/zvec-grep.instructions.md`, the
harness-agnostic user instructions folder VS Code and Agent Host both read,
with the `applyTo: '**'` frontmatter that makes VS Code apply it automatically.
Uninstall removes the managed block and deletes the file when nothing else
remains in it.

The JSONC MCP helpers now take a container key so VS Code's `servers` object
reuses the comment-preserving machinery that Qoder uses for `mcpServers`.
Auto-target tests pin `VSCODE_USER_DIR` at a missing path so a VS Code profile
on the developer's own machine cannot be detected by them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YPkQZNkK72inHZobHhrCrd

@egolearner egolearner left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superseded by the English inline review. The original inline comments were deleted.

@egolearner egolearner left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline findings from a static review. No local build or tests were run.

Comment thread src/cli/install.ts
id: "vscode",
aliases: ["vs-code", "code"],
label: "VS Code",
executables: ["code", "code-insiders"],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Auto-detection treats code-insiders as the vscode target, but the default path resolver always writes Code/User/mcp.json. On a machine with only Insiders installed, zg install --yes reports success while creating a Stable-profile configuration that Insiders never reads. Please carry the detected product/channel into path resolution (or stop auto-detecting Insiders as the default Stable target) and add an auto-install test with only code-insiders available.

Comment thread src/cli/install.ts
const configPath = resolveVsCodeMcpConfigPath();
const guidancePath = resolveVsCodeGuidancePath();

await updateJsoncMcpSettings({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] The reused parseJsoncSettings() still parses with allowTrailingComma: false, whereas VS Code's mcp.json schema explicitly allows trailing commas. An existing, valid mcp.json with a trailing comma therefore makes both install and uninstall fail with Invalid VS Code configuration, instead of preserving the user's JSONC. Please allow trailing commas for the VS Code path without changing other hosts' parsing semantics, and cover install/uninstall with comments and trailing commas. See the VS Code schema.

Comment thread src/cli/install.ts
options: InstallAgentOptions,
): Promise<InstallAgentResult> {
const configPath = resolveVsCodeMcpConfigPath();
const guidancePath = resolveVsCodeGuidancePath();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] This ~/.copilot/instructions file is also read by standalone Copilot CLI, while the vscode target configures only VS Code's mcp.json. Installing vscode alone, or uninstalling copilot while keeping vscode, leaves the CLI with instructions to call zvec_grep_search but no corresponding CLI MCP entry. Please handle this cross-target lifecycle so the guidance only directs a host to use a tool it actually exposes, and add tests for those install/uninstall sequences.

Comment thread src/cli/install.ts
// VS Code reads user instructions from the harness-agnostic
// `~/.copilot/instructions` folder, so the guidance needs the frontmatter
// header before the managed block rather than a bare markdown file.
if (!(await readTextFileIfExists(guidancePath)).trim()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] The required applyTo: '**' frontmatter is written only when the guidance file is empty. If zvec-grep.instructions.md already contains user text, no frontmatter, or a narrower applyTo, the managed block is appended but VS Code does not automatically apply the search guidance to all files. Please ensure the required frontmatter while preserving user content (or fail explicitly if that cannot be done safely), and test installation over a non-empty existing file.

Comment thread src/cli/install.ts
type: "local",
command: "zg",
args: stdioArgs(options.mcpToolset),
tools: COPILOT_MCP_TOOLS,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Copilot CLI supports a per-server timeout for local MCP entries and defaults to 30,000 ms, but this entry ignores the advertised --mcp-tool-timeout option (which defaults to 600 seconds in the installer). Slow tool discovery or calls can still time out after 30 seconds even when the user requests a longer limit. Please emit timeout: options.mcpToolTimeoutSeconds * 1000 and test that the option reaches the generated configuration. See the CLI MCP field reference.

Comment thread src/cli/install.ts

async function removeVsCodeGuidanceFileIfEmpty(path: string): Promise<void> {
const remaining = await readTextFileIfExists(path);
if (!remaining.trim()) return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] After uninstall removes the managed block, an otherwise blank guidance file returns here before unlink, leaving an empty zvec-grep.instructions.md behind. The current test only covers the case where the exact generated frontmatter remains. Please also delete a file that becomes blank (while keeping a missing file as a no-op) and cover an existing or edited guidance file on uninstall.

Comment thread test/install.test.mjs
);

test("GitHub Copilot installer manages a user-level stdio MCP server", async (t) => {
const temporaryDirectory = await mkdtemp(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] Duplicated Code: these new cases repeatedly copy mkdtemp, t.after(...rm...), COPILOT_HOME / VSCODE_USER_DIR setup, and target invocation across the Copilot and VS Code tests. A shared temporary-directory fixture plus small target-specific install helpers would leave each case focused on inputs and behavior assertions, and avoid synchronized edits in more than a dozen tests when setup changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants