feat(installer): GitHub Copilot targets — VS Code, Copilot CLI, JetBrains

Adds three new installer targets so `codegraph install` can wire the
MCP server into GitHub Copilot surfaces:

- copilot-vscode: .vscode/mcp.json (local) or the VS Code User-dir
  mcp.json (global), JSONC-surgical edits, `--path` pinned via
  ${workspaceFolder} for global installs
- copilot-cli: ~/.copilot/mcp-config.json
- copilot-jetbrains: github-copilot config dir (XDG / %LOCALAPPDATA%)

Detection, install, uninstall, and --print-config are covered for all
three in installer-targets.test.ts, including platform-specific path
resolution.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Colby McHenry
2026-07-23 17:29:38 -05:00
co-authored by Claude Fable 5
parent 572d22bfbe
commit 490791c07a
10 changed files with 1124 additions and 11 deletions
+2 -2
View File
@@ -2232,7 +2232,7 @@ program
*/
program
.command('install')
.description('Install codegraph MCP server into one or more agents (Claude Code, Cursor, Codex CLI, opencode, Hermes Agent)')
.description('Install codegraph MCP server into one or more agents (Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, GitHub Copilot)')
.option('-t, --target <ids>', 'Target agent(s): comma-separated ids, or "auto"|"all"|"none". Default: prompt')
.option('-l, --location <where>', 'Install location: "global" or "local". Default: prompt')
.option('-y, --yes', 'Non-interactive: defaults to --location=global --target=auto, auto-allow on')
@@ -2332,7 +2332,7 @@ program
*/
program
.command('uninstall')
.description('Remove codegraph from your agents (Claude Code, Cursor, Codex CLI, opencode, Hermes Agent)')
.description('Remove codegraph from your agents (Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, GitHub Copilot)')
.option('-t, --target <ids>', 'Target agent(s): comma-separated ids, or "all". Default: all')
.option('-l, --location <where>', 'Uninstall location: "global" or "local". Default: prompt')
.option('-y, --yes', 'Non-interactive: defaults to --location=global --target=all')
+4 -3
View File
@@ -3,7 +3,8 @@
*
* Multi-target: writes MCP server config + instructions for the
* agents the user picks (Claude Code, Cursor, Codex CLI, opencode,
* Hermes Agent, Gemini CLI, Antigravity IDE).
* Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, and GitHub
* Copilot in VS Code / the Copilot CLI / JetBrains IDEs).
* Defaults to the Claude-only behavior for backwards compatibility
* when no targets are explicitly chosen and nothing else is detected.
*
@@ -467,8 +468,8 @@ export async function runUninstaller(opts: RunUninstallerOptions): Promise<void>
const sel = await clack.select({
message: 'Remove CodeGraph from all your projects, or just this one?',
options: [
{ value: 'global' as const, label: 'All projects (global)', hint: '~/.claude, ~/.cursor, ~/.codex, ~/.config/opencode, ~/.hermes, ~/.gemini, ~/.kiro' },
{ value: 'local' as const, label: 'Just this project (local)', hint: './.claude, ./.cursor, ./opencode.jsonc, ./.gemini, ./.kiro' },
{ value: 'global' as const, label: 'All projects (global)', hint: '~/.claude, ~/.cursor, ~/.codex, ~/.config/opencode, ~/.hermes, ~/.gemini, ~/.kiro, ~/.copilot, ~/.config/github-copilot' },
{ value: 'local' as const, label: 'Just this project (local)', hint: './.claude, ./.cursor, ./.vscode, ./opencode.jsonc, ./.gemini, ./.kiro' },
],
initialValue: 'global' as const,
});
+166
View File
@@ -0,0 +1,166 @@
/**
* GitHub Copilot CLI target.
*
* - MCP server entry to `~/.copilot/mcp-config.json` under the
* `mcpServers` key (same wrapper as Claude/Cursor). Entry shape per
* the GitHub docs: `{ "type": "stdio", "command", "args", "tools" }`
* — `type` accepts `"local"` or `"stdio"`; we write `"stdio"` (the
* standard MCP name, recommended by the docs for cross-client
* compatibility). `"tools": ["*"]` mirrors the docs' example and is
* the documented default.
* - The config dir is `~/.copilot` unless the user moved it via
* `COPILOT_HOME` (documented override) — we honor it so install and
* detect follow the CLI's own resolution.
*
* Copilot CLI as of 2026-07 has no project-local MCP config — per-repo
* config (`.github/mcp.json`) is an open feature request
* (github/copilot-cli#2528). `supportsLocation('local')` returns false;
* the orchestrator skips this target for local installs with a clear
* message (same pattern as Codex).
*
* The file is machine-written by the CLI's own `/mcp add` flow, so it's
* plain JSON — no JSONC handling needed; surgical edits go through the
* shared read/mutate/write helpers (Cursor pattern), preserving sibling
* servers.
*
* No instructions file (MCP `initialize` instructions are the single
* source of truth, #529) and no permissions concept — `autoAllow` is
* silently ignored.
*/
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import {
AgentTarget,
DetectionResult,
InstallOptions,
Location,
WriteResult,
} from './types';
import {
getMcpServerConfig,
jsonDeepEqual,
readJsonFile,
writeJsonFile,
} from './shared';
function configDir(): string {
const override = process.env.COPILOT_HOME;
if (override && override.trim().length > 0) return override;
return path.join(os.homedir(), '.copilot');
}
function mcpConfigPath(): string {
return path.join(configDir(), 'mcp-config.json');
}
/**
* Best-effort check that the `copilot` binary is reachable on PATH.
* A plain fs scan (no shell-out) — cheap enough to run inside
* `detectAll()` for the multiselect prompt.
*/
function copilotOnPath(): boolean {
const pathVar = process.env.PATH || '';
const exts = process.platform === 'win32'
? ['.exe', '.cmd', '.bat', '.ps1']
: [''];
for (const dir of pathVar.split(path.delimiter)) {
if (!dir) continue;
for (const ext of exts) {
try {
if (fs.existsSync(path.join(dir, 'copilot' + ext))) return true;
} catch { /* ignore unreadable PATH entries */ }
}
}
return false;
}
function buildCopilotMcpConfig(): { type: string; command: string; args: string[]; tools: string[] } {
const base = getMcpServerConfig();
return { ...base, tools: ['*'] };
}
class CopilotCliTarget implements AgentTarget {
readonly id = 'copilot-cli' as const;
readonly displayName = 'GitHub Copilot CLI';
readonly docsUrl = 'https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers';
supportsLocation(loc: Location): boolean {
return loc === 'global';
}
detect(loc: Location): DetectionResult {
if (loc !== 'global') {
return { installed: false, alreadyConfigured: false };
}
const file = mcpConfigPath();
const config = readJsonFile(file);
const alreadyConfigured = !!config.mcpServers?.codegraph;
const installed = fs.existsSync(configDir()) || copilotOnPath();
return { installed, alreadyConfigured, configPath: file };
}
install(loc: Location, _opts: InstallOptions): WriteResult {
if (loc !== 'global') {
return {
files: [],
notes: ['Copilot CLI has no project-local config — re-run with --location=global to install.'],
};
}
return {
files: [writeMcpEntry()],
notes: ['Restart any running Copilot CLI session to pick up the MCP server.'],
};
}
uninstall(loc: Location): WriteResult {
if (loc !== 'global') return { files: [] };
const file = mcpConfigPath();
if (!fs.existsSync(file)) {
return { files: [{ path: file, action: 'not-found' }] };
}
const config = readJsonFile(file);
if (!config.mcpServers?.codegraph) {
return { files: [{ path: file, action: 'not-found' }] };
}
delete config.mcpServers.codegraph;
if (Object.keys(config.mcpServers).length === 0) {
delete config.mcpServers;
}
writeJsonFile(file, config);
return { files: [{ path: file, action: 'removed' }] };
}
printConfig(loc: Location): string {
if (loc !== 'global') {
return '# Copilot CLI has no project-local config — use --location=global.\n';
}
const snippet = JSON.stringify({ mcpServers: { codegraph: buildCopilotMcpConfig() } }, null, 2);
return `# Add to ${mcpConfigPath()}\n\n${snippet}\n`;
}
describePaths(loc: Location): string[] {
if (loc !== 'global') return [];
return [mcpConfigPath()];
}
}
function writeMcpEntry(): WriteResult['files'][number] {
const file = mcpConfigPath();
const existing = readJsonFile(file);
const before = existing.mcpServers?.codegraph;
const after = buildCopilotMcpConfig();
if (jsonDeepEqual(before, after)) {
return { path: file, action: 'unchanged' };
}
const existed = fs.existsSync(file);
if (!existing.mcpServers) existing.mcpServers = {};
existing.mcpServers.codegraph = after;
writeJsonFile(file, existing);
return { path: file, action: existed ? 'updated' : 'created' };
}
export const copilotCliTarget: AgentTarget = new CopilotCliTarget();
+230
View File
@@ -0,0 +1,230 @@
/**
* JetBrains IDEs (GitHub Copilot plugin) target.
*
* - MCP server entry to the plugin's user-level `mcp.json`, which
* lives under the shared `github-copilot` config dir (the same dir
* the Copilot ecosystem uses for `hosts.json`):
*
* macOS/Linux: $XDG_CONFIG_HOME|~/.config/github-copilot/intellij/mcp.json
* Windows: %LOCALAPPDATA%\github-copilot\intellij\mcp.json
*
* `$XDG_CONFIG_HOME` is honored on every platform when set —
* matching the plugin family's own resolution (copilot.vim /
* copilot-language-server check it before the OS default).
* - Shape is VS Code-compatible: `{ "servers": { "<name>": { "type":
* "stdio", "command", "args" } } }` — the plugin documents mcp.json
* parity with `.vscode/mcp.json`.
* - **Global-only.** The plugin reads exactly one user-level file; a
* project-level mcp.json is an open feature request
* (microsoft/copilot-intellij-feedback#701, still open 2026-07).
* `supportsLocation('local')` returns false so the orchestrator
* skips local installs with a clear message (Codex pattern).
* - No `--path` injection: the config is user-global and the plugin
* documents no `${workspaceFolder}`-style variable expansion for
* this file, so we ship the plain entry and let the MCP server
* resolve the project from the client's roots/cwd as with other
* global installs.
* - No instructions file (MCP `initialize` instructions are the
* single source of truth, #529) and no permissions concept —
* `autoAllow` is silently ignored.
*
* The IDE opens this file in a JSON editor for hand-editing (Settings →
* Tools → GitHub Copilot → MCP → Configure), so reads + writes go
* through `jsonc-parser` — surgical edits that preserve sibling
* servers, user comments, and formatting (same approach as the
* copilot-vscode target).
*
* The plugin only re-reads mcp.json on IDE restart
* (microsoft/copilot-intellij-feedback#1139) — hence the restart note.
*/
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import { parse as parseJsonc, modify, applyEdits } from 'jsonc-parser';
import {
AgentTarget,
DetectionResult,
InstallOptions,
Location,
WriteResult,
} from './types';
import {
atomicWriteFileSync,
getMcpServerConfig,
jsonDeepEqual,
} from './shared';
/**
* The `github-copilot` config root, resolved the way the Copilot
* plugin family resolves it: `$XDG_CONFIG_HOME` first on every
* platform, then `%LOCALAPPDATA%` on Windows, then `~/.config`.
*/
function copilotConfigRoot(): string {
const xdg = process.env.XDG_CONFIG_HOME;
if (xdg && xdg.trim().length > 0) {
return path.join(xdg, 'github-copilot');
}
if (process.platform === 'win32') {
const localAppData = process.env.LOCALAPPDATA && process.env.LOCALAPPDATA.trim().length > 0
? process.env.LOCALAPPDATA
: path.join(os.homedir(), 'AppData', 'Local');
return path.join(localAppData, 'github-copilot');
}
return path.join(os.homedir(), '.config', 'github-copilot');
}
function intellijDir(): string {
return path.join(copilotConfigRoot(), 'intellij');
}
function mcpJsonPath(): string {
return path.join(intellijDir(), 'mcp.json');
}
/**
* Best-effort "a JetBrains IDE exists here" heuristic for the
* multiselect default — the per-OS dir every JetBrains IDE creates on
* first launch. False positives (IDE without the Copilot plugin) are
* acceptable per the `DetectionResult` contract.
*/
function jetbrainsConfigDirExists(): boolean {
const home = os.homedir();
if (process.platform === 'darwin') {
return fs.existsSync(path.join(home, 'Library', 'Application Support', 'JetBrains'));
}
if (process.platform === 'win32') {
const appData = process.env.APPDATA && process.env.APPDATA.trim().length > 0
? process.env.APPDATA
: path.join(home, 'AppData', 'Roaming');
return fs.existsSync(path.join(appData, 'JetBrains'));
}
const xdg = process.env.XDG_CONFIG_HOME && process.env.XDG_CONFIG_HOME.trim().length > 0
? process.env.XDG_CONFIG_HOME
: path.join(home, '.config');
return fs.existsSync(path.join(xdg, 'JetBrains'));
}
function readConfigText(file: string): string {
if (!fs.existsSync(file)) return '';
return fs.readFileSync(file, 'utf-8');
}
function parseConfig(text: string): Record<string, any> {
if (!text.trim()) return {};
const errors: any[] = [];
const result = parseJsonc(text, errors, { allowTrailingComma: true });
if (result == null || typeof result !== 'object' || Array.isArray(result)) {
return {};
}
return result as Record<string, any>;
}
const FORMATTING = { tabSize: 2, insertSpaces: true, eol: '\n' };
class CopilotJetbrainsTarget implements AgentTarget {
readonly id = 'copilot-jetbrains' as const;
readonly displayName = 'JetBrains IDEs (Copilot plugin)';
readonly docsUrl = 'https://docs.github.com/en/copilot/how-tos/provide-context/use-mcp/extend-copilot-chat-with-mcp';
supportsLocation(loc: Location): boolean {
return loc === 'global';
}
detect(loc: Location): DetectionResult {
if (loc !== 'global') {
return { installed: false, alreadyConfigured: false };
}
const file = mcpJsonPath();
const config = parseConfig(readConfigText(file));
const alreadyConfigured = !!config.servers?.codegraph;
// The `intellij/` subdir is created by the Copilot plugin itself;
// fall back to "some JetBrains IDE is installed" for first-time
// plugin users.
const installed = fs.existsSync(intellijDir()) || jetbrainsConfigDirExists();
return { installed, alreadyConfigured, configPath: file };
}
install(loc: Location, _opts: InstallOptions): WriteResult {
if (loc !== 'global') {
return {
files: [],
notes: ['The JetBrains Copilot plugin has no project-local MCP config — re-run with --location=global to install.'],
};
}
return {
files: [writeMcpEntry()],
notes: ['Restart your JetBrains IDE — the Copilot plugin only reads mcp.json on startup.'],
};
}
uninstall(loc: Location): WriteResult {
if (loc !== 'global') return { files: [] };
return { files: [removeMcpEntry()] };
}
printConfig(loc: Location): string {
if (loc !== 'global') {
return '# The JetBrains Copilot plugin has no project-local MCP config — use --location=global.\n';
}
const snippet = JSON.stringify({ servers: { codegraph: getMcpServerConfig() } }, null, 2);
return `# Add to ${mcpJsonPath()}\n# (Settings → Tools → GitHub Copilot → Model Context Protocol → Configure)\n\n${snippet}\n`;
}
describePaths(loc: Location): string[] {
if (loc !== 'global') return [];
return [mcpJsonPath()];
}
}
function writeMcpEntry(): WriteResult['files'][number] {
const file = mcpJsonPath();
const existed = fs.existsSync(file);
let text = readConfigText(file);
if (!text.trim()) text = '{}\n';
const config = parseConfig(text);
const before = config.servers?.codegraph;
const after = getMcpServerConfig();
if (jsonDeepEqual(before, after)) {
return { path: file, action: 'unchanged' };
}
// Surgical edit — preserves comments, formatting, and sibling
// servers ("servers" is created when missing).
const edits = modify(text, ['servers', 'codegraph'], after, {
formattingOptions: FORMATTING,
});
const updated = applyEdits(text, edits);
atomicWriteFileSync(file, updated);
return { path: file, action: existed ? 'updated' : 'created' };
}
function removeMcpEntry(): WriteResult['files'][number] {
const file = mcpJsonPath();
if (!fs.existsSync(file)) return { path: file, action: 'not-found' };
const text = readConfigText(file);
const config = parseConfig(text);
if (!config.servers?.codegraph) return { path: file, action: 'not-found' };
let edits = modify(text, ['servers', 'codegraph'], undefined, {
formattingOptions: FORMATTING,
});
let updated = applyEdits(text, edits);
// Drop an emptied `servers` wrapper; the file itself is left in
// place — the plugin owns it and siblings may remain.
const afterParsed = parseConfig(updated);
if (afterParsed.servers && typeof afterParsed.servers === 'object' &&
Object.keys(afterParsed.servers).length === 0) {
edits = modify(updated, ['servers'], undefined, { formattingOptions: FORMATTING });
updated = applyEdits(updated, edits);
}
atomicWriteFileSync(file, updated);
return { path: file, action: 'removed' };
}
export const copilotJetbrainsTarget: AgentTarget = new CopilotJetbrainsTarget();
+202
View File
@@ -0,0 +1,202 @@
/**
* VS Code (GitHub Copilot Chat) target.
*
* - MCP server entry to `.vscode/mcp.json` (local, workspace-scoped)
* or the user-level `mcp.json` in the VS Code User dir (global):
*
* macOS: ~/Library/Application Support/Code/User/mcp.json
* Windows: %APPDATA%\Code\User\mcp.json
* Linux: $XDG_CONFIG_HOME|~/.config/Code/User/mcp.json
*
* VS Code moved MCP config out of settings.json into this dedicated
* `mcp.json` (v1.102, "MCP: Open User Configuration"). Shape is
* `{ "servers": { "<name>": { "type": "stdio", "command", "args" } } }`
* — note `servers`, not the `mcpServers` wrapper Claude/Cursor use.
* - No instructions file: Copilot Chat consumes the MCP `initialize`
* instructions, the single source of truth (#529).
* - No permissions concept — `autoAllow` is silently ignored.
*
* ## Why we inject `--path` (mirrors Cursor)
*
* VS Code's docs don't specify the working directory stdio MCP servers
* are launched with, and (like Cursor) we can't rely on it being the
* workspace root. Rather than depend on undocumented cwd behavior we
* pin the project explicitly:
*
* - `local` install: absolute path (known at install time).
* - `global` install: `${workspaceFolder}` — VS Code expands its
* standard variables inside mcp.json, giving per-workspace behavior
* from a single user-level config.
*
* ## JSONC
*
* VS Code parses its config files as JSONC (comments + trailing commas
* allowed), so reads + writes go through `jsonc-parser` — surgical
* edits that preserve sibling servers, user comments, and formatting
* across install / re-install / uninstall (same approach as opencode).
*/
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import { parse as parseJsonc, modify, applyEdits } from 'jsonc-parser';
import {
AgentTarget,
DetectionResult,
InstallOptions,
Location,
WriteResult,
} from './types';
import {
atomicWriteFileSync,
getMcpServerConfig,
jsonDeepEqual,
} from './shared';
function vscodeUserDir(): string {
const home = os.homedir();
if (process.platform === 'win32') {
const appData = process.env.APPDATA && process.env.APPDATA.trim().length > 0
? process.env.APPDATA
: path.join(home, 'AppData', 'Roaming');
return path.join(appData, 'Code', 'User');
}
if (process.platform === 'darwin') {
return path.join(home, 'Library', 'Application Support', 'Code', 'User');
}
const xdg = process.env.XDG_CONFIG_HOME && process.env.XDG_CONFIG_HOME.trim().length > 0
? process.env.XDG_CONFIG_HOME
: path.join(home, '.config');
return path.join(xdg, 'Code', 'User');
}
function mcpJsonPath(loc: Location): string {
return loc === 'global'
? path.join(vscodeUserDir(), 'mcp.json')
: path.join(process.cwd(), '.vscode', 'mcp.json');
}
/**
* Build the codegraph server entry for VS Code at the given location.
* Shared `{type, command, args}` shape plus the `--path` pin — see
* file header for why we don't trust VS Code's launch cwd.
*/
function buildVscodeServerEntry(loc: Location): { type: string; command: string; args: string[] } {
const base = getMcpServerConfig();
const pathArg = loc === 'local' ? process.cwd() : '${workspaceFolder}';
return { ...base, args: [...base.args, '--path', pathArg] };
}
function readConfigText(file: string): string {
if (!fs.existsSync(file)) return '';
return fs.readFileSync(file, 'utf-8');
}
function parseConfig(text: string): Record<string, any> {
if (!text.trim()) return {};
const errors: any[] = [];
const result = parseJsonc(text, errors, { allowTrailingComma: true });
if (result == null || typeof result !== 'object' || Array.isArray(result)) {
return {};
}
return result as Record<string, any>;
}
const FORMATTING = { tabSize: 2, insertSpaces: true, eol: '\n' };
class CopilotVscodeTarget implements AgentTarget {
readonly id = 'copilot-vscode' as const;
readonly displayName = 'VS Code (Copilot Chat)';
readonly docsUrl = 'https://code.visualstudio.com/docs/copilot/customization/mcp-servers';
supportsLocation(_loc: Location): boolean {
return true;
}
detect(loc: Location): DetectionResult {
const file = mcpJsonPath(loc);
const config = parseConfig(readConfigText(file));
const alreadyConfigured = !!config.servers?.codegraph;
// "Installed" heuristic: the VS Code User dir (created on first
// launch) or ~/.vscode (extensions dir) for global; an existing
// .vscode/ dir in the project for local.
const installed = loc === 'global'
? fs.existsSync(vscodeUserDir()) || fs.existsSync(path.join(os.homedir(), '.vscode'))
: fs.existsSync(path.join(process.cwd(), '.vscode'));
return { installed, alreadyConfigured, configPath: file };
}
install(loc: Location, _opts: InstallOptions): WriteResult {
return {
files: [writeMcpEntry(loc)],
notes: ['Restart VS Code for MCP changes to take effect.'],
};
}
uninstall(loc: Location): WriteResult {
return { files: [removeMcpEntry(loc)] };
}
printConfig(loc: Location): string {
const target = mcpJsonPath(loc);
const snippet = JSON.stringify({ servers: { codegraph: buildVscodeServerEntry(loc) } }, null, 2);
return `# Add to ${target}\n\n${snippet}\n`;
}
describePaths(loc: Location): string[] {
return [mcpJsonPath(loc)];
}
}
function writeMcpEntry(loc: Location): WriteResult['files'][number] {
const file = mcpJsonPath(loc);
const existed = fs.existsSync(file);
let text = readConfigText(file);
if (!text.trim()) text = '{}\n';
const config = parseConfig(text);
const before = config.servers?.codegraph;
const after = buildVscodeServerEntry(loc);
if (jsonDeepEqual(before, after)) {
return { path: file, action: 'unchanged' };
}
// Surgical edit — preserves comments, formatting, and sibling
// servers ("servers" is created when missing).
const edits = modify(text, ['servers', 'codegraph'], after, {
formattingOptions: FORMATTING,
});
const updated = applyEdits(text, edits);
atomicWriteFileSync(file, updated);
return { path: file, action: existed ? 'updated' : 'created' };
}
function removeMcpEntry(loc: Location): WriteResult['files'][number] {
const file = mcpJsonPath(loc);
if (!fs.existsSync(file)) return { path: file, action: 'not-found' };
const text = readConfigText(file);
const config = parseConfig(text);
if (!config.servers?.codegraph) return { path: file, action: 'not-found' };
let edits = modify(text, ['servers', 'codegraph'], undefined, {
formattingOptions: FORMATTING,
});
let updated = applyEdits(text, edits);
// Drop an emptied `servers` wrapper; the file itself is left in
// place — VS Code recreates/reads it and siblings like `inputs`
// may remain.
const afterParsed = parseConfig(updated);
if (afterParsed.servers && typeof afterParsed.servers === 'object' &&
Object.keys(afterParsed.servers).length === 0) {
edits = modify(updated, ['servers'], undefined, { formattingOptions: FORMATTING });
updated = applyEdits(updated, edits);
}
atomicWriteFileSync(file, updated);
return { path: file, action: 'removed' };
}
export const copilotVscodeTarget: AgentTarget = new CopilotVscodeTarget();
+6
View File
@@ -16,6 +16,9 @@ import { hermesTarget } from './hermes';
import { geminiTarget } from './gemini';
import { antigravityTarget } from './antigravity';
import { kiroTarget } from './kiro';
import { copilotVscodeTarget } from './copilot-vscode';
import { copilotCliTarget } from './copilot-cli';
import { copilotJetbrainsTarget } from './copilot-jetbrains';
export const ALL_TARGETS: readonly AgentTarget[] = Object.freeze([
claudeTarget,
@@ -26,6 +29,9 @@ export const ALL_TARGETS: readonly AgentTarget[] = Object.freeze([
geminiTarget,
antigravityTarget,
kiroTarget,
copilotVscodeTarget,
copilotCliTarget,
copilotJetbrainsTarget,
]);
export function getTarget(id: string): AgentTarget | undefined {
+1 -1
View File
@@ -19,7 +19,7 @@ export type Location = 'global' | 'local';
* lookup. New targets add a value here when they're added to the
* registry. Keep these short and lowercase.
*/
export type TargetId = 'claude' | 'cursor' | 'codex' | 'opencode' | 'hermes' | 'gemini' | 'antigravity' | 'kiro';
export type TargetId = 'claude' | 'cursor' | 'codex' | 'opencode' | 'hermes' | 'gemini' | 'antigravity' | 'kiro' | 'copilot-vscode' | 'copilot-cli' | 'copilot-jetbrains';
/**
* Result of `target.detect(location)`.