@cantinasecurity/apex-cli 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Cantina agent plugins for security review workflows.",
9
- "version": "0.1.9"
9
+ "version": "0.1.10"
10
10
  },
11
11
  "plugins": [
12
12
  {
@@ -14,10 +14,10 @@
14
14
  "source": {
15
15
  "source": "npm",
16
16
  "package": "@cantinasecurity/apex-cli",
17
- "version": "0.1.9"
17
+ "version": "0.1.10"
18
18
  },
19
19
  "description": "Run Apex security scans and review findings from Claude Code.",
20
- "version": "0.1.9",
20
+ "version": "0.1.10",
21
21
  "author": {
22
22
  "name": "Cantina",
23
23
  "email": "support@cantina.xyz"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apex-cli",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Run Apex security scans and review findings from Claude Code.",
5
5
  "author": {
6
6
  "name": "Cantina",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apex-cli",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Run Apex security scans and review findings from Codex.",
5
5
  "author": {
6
6
  "name": "Cantina",
package/.mcp.claude.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "args": [
6
6
  "-y",
7
7
  "-p",
8
- "@cantinasecurity/apex-cli@0.1.9",
8
+ "@cantinasecurity/apex-cli@0.1.10",
9
9
  "apex-mcp"
10
10
  ]
11
11
  }
package/.mcp.codex.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "args": [
5
5
  "-y",
6
6
  "-p",
7
- "@cantinasecurity/apex-cli@0.1.9",
7
+ "@cantinasecurity/apex-cli@0.1.10",
8
8
  "apex-mcp"
9
9
  ]
10
10
  }
package/MARKETPLACE.md CHANGED
@@ -13,7 +13,7 @@ This package is prepared as both a Claude Code plugin and a Codex plugin. The pu
13
13
  The plugin MCP configs launch the pinned npm CLI package with:
14
14
 
15
15
  ```bash
16
- npx -y -p @cantinasecurity/apex-cli@0.1.9 apex-mcp
16
+ npx -y -p @cantinasecurity/apex-cli@0.1.10 apex-mcp
17
17
  ```
18
18
 
19
19
  That keeps marketplace installs independent of a user's global `apex` install.
package/README.md CHANGED
@@ -19,15 +19,17 @@ apex setup
19
19
 
20
20
  `apex setup` is the lowest-friction path for agent clients. It:
21
21
 
22
- - registers Apex as an MCP server in any installed Codex and Claude Code CLIs
22
+ - registers Apex as an MCP server in any installed Codex CLI, Claude Code, and GitHub Copilot CLI clients
23
23
  - installs the Codex skill into `$CODEX_HOME/skills/apex-cli`
24
24
  - installs the Claude project skill into `.claude/skills/apex-cli` in the current repository
25
+ - installs the GitHub Copilot CLI skill into `$COPILOT_HOME/skills/apex-cli` or `~/.copilot/skills/apex-cli`
25
26
 
26
27
  If you only want one client, run:
27
28
 
28
29
  ```bash
29
30
  apex setup codex
30
31
  apex setup claude
32
+ apex setup copilot
31
33
  ```
32
34
 
33
35
  If one client is not installed yet, `apex setup` skips it automatically. If you target a client explicitly, its CLI must already be installed.
@@ -146,7 +148,7 @@ Supported shell commands:
146
148
  - `apex doctor`
147
149
  - `apex login`
148
150
  - `apex logout`
149
- - `apex setup [all|codex|claude]`
151
+ - `apex setup [all|codex|claude|copilot]`
150
152
  - `apex update`
151
153
  - `apex connect github`
152
154
  - `apex connect gitlab`
@@ -219,7 +221,7 @@ If Apex is installed globally, prefer:
219
221
  apex setup
220
222
  ```
221
223
 
222
- That registers Apex for installed Codex and Claude Code clients automatically.
224
+ That registers Apex for installed Codex CLI, Claude Code, and GitHub Copilot CLI clients automatically.
223
225
 
224
226
  If you want to wire clients manually instead, Apex ships a stable `apex-mcp` binary. For Codex:
225
227
 
@@ -233,6 +235,12 @@ For Claude Code:
233
235
  claude mcp add --scope user apex -- apex-mcp
234
236
  ```
235
237
 
238
+ For GitHub Copilot CLI:
239
+
240
+ ```bash
241
+ copilot mcp add apex --type stdio --tools "*" -- apex-mcp
242
+ ```
243
+
236
244
  For any other MCP client, configure it to launch:
237
245
 
238
246
  ```json
@@ -285,7 +293,7 @@ The MCP server exposes Apex-specific tools for:
285
293
 
286
294
  For repository-scoped operations, pass `cwd` explicitly so the server can resolve the right `.apex/workspace.json` binding and repository roots.
287
295
 
288
- For Codex-style clients, the packaged skill can be installed with `apex setup codex`. The repo-local source lives at `skills/apex-cli/SKILL.md`.
296
+ For Codex-style clients, the packaged skill can be installed with `apex setup codex`. For GitHub Copilot CLI, the same skill is installed into `~/.copilot/skills/apex-cli` with `apex setup copilot`. The repo-local source lives at `skills/apex-cli/SKILL.md`.
289
297
 
290
298
  For Claude Code, the packaged project skill can be installed into the current repository with `apex setup claude`. The repo-local source lives at `.claude/skills/apex-cli/SKILL.md`. Anthropic documents project skills as filesystem directories under `.claude/skills/<name>/SKILL.md`, and the Claude Agent SDK uses the same location when the `Skill` tool is enabled.
291
299
 
@@ -297,7 +305,7 @@ The npm package also includes marketplace-ready plugin artifacts:
297
305
  - `.claude-plugin/plugin.json` and `.mcp.claude.json` for Claude Code plugin installs
298
306
  - `.claude-plugin/marketplace.json` for a Claude marketplace entry backed by the public npm package
299
307
 
300
- These plugin installs launch the pinned npm package with `npx -y -p @cantinasecurity/apex-cli@0.1.9 apex-mcp`, so users do not need to install `apex` globally before enabling the plugin.
308
+ These plugin installs launch the pinned npm package with `npx -y -p @cantinasecurity/apex-cli@0.1.10 apex-mcp`, so users do not need to install `apex` globally before enabling the plugin.
301
309
 
302
310
  The repository also includes `.agents/plugins/marketplace.json` for local Codex marketplace testing from a checkout.
303
311
 
package/dist/help.js CHANGED
@@ -21,7 +21,8 @@ export const CLI_HELP_TEXT = `Usage:
21
21
  apex login Sign in to Apex
22
22
  apex logout Sign out locally
23
23
  apex mcp Start the Apex MCP server over stdio
24
- apex setup [all|codex|claude] Configure Apex for Codex and Claude Code
24
+ apex setup [all|codex|claude|copilot]
25
+ Configure Apex for Codex, Claude Code, and GitHub Copilot CLI
25
26
  apex update Update the local Apex CLI install
26
27
  apex connect github Open the GitHub connection flow
27
28
  apex connect gitlab Open the GitLab connection flow
package/dist/setup.js CHANGED
@@ -10,6 +10,7 @@ const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)),
10
10
  const CODEX_SKILL_NAME = "apex-cli";
11
11
  const MCP_SERVER_NAME = "apex";
12
12
  const execFile = promisify(execFileCallback);
13
+ const SETUP_CLIENTS = ["codex", "claude", "copilot"];
13
14
  function quoteShellArg(value) {
14
15
  return /[^A-Za-z0-9_./:-]/.test(value)
15
16
  ? `'${value.replace(/'/g, `'\\''`)}'`
@@ -73,9 +74,28 @@ function getCodexHome() {
73
74
  }
74
75
  return path.join(os.homedir(), ".codex");
75
76
  }
77
+ function getCopilotHome() {
78
+ const configured = process.env.COPILOT_HOME?.trim();
79
+ if (configured) {
80
+ return configured;
81
+ }
82
+ return path.join(os.homedir(), ".copilot");
83
+ }
76
84
  function normalizeArgs(value) {
77
85
  return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
78
86
  }
87
+ function normalizeTools(value) {
88
+ if (Array.isArray(value)) {
89
+ return value.filter((item) => typeof item === "string");
90
+ }
91
+ if (typeof value === "string") {
92
+ return value
93
+ .split(",")
94
+ .map((item) => item.trim())
95
+ .filter(Boolean);
96
+ }
97
+ return [];
98
+ }
79
99
  function codexConfigMatches(existing, launch) {
80
100
  if (!existing?.transport) {
81
101
  return false;
@@ -92,6 +112,17 @@ function claudeConfigMatches(existing, launch) {
92
112
  JSON.stringify(normalizeArgs(existing.args)) === JSON.stringify(launch.args) &&
93
113
  normalizedEnv === 0);
94
114
  }
115
+ function copilotConfigMatches(existing, launch) {
116
+ const env = existing?.env;
117
+ const normalizedEnv = env && typeof env === "object" && !Array.isArray(env) ? Object.keys(env).length : 0;
118
+ const normalizedTools = normalizeTools(existing?.tools);
119
+ return ((existing?.type === "stdio" || existing?.type === "local") &&
120
+ existing.command === launch.command &&
121
+ JSON.stringify(normalizeArgs(existing.args)) === JSON.stringify(launch.args) &&
122
+ normalizedEnv === 0 &&
123
+ normalizedTools.length === 1 &&
124
+ normalizedTools[0] === "*");
125
+ }
95
126
  async function writeManagedFile(filePath, content) {
96
127
  const current = await readTextFile(filePath);
97
128
  if (current === content) {
@@ -127,6 +158,20 @@ async function readClaudeUserMcpConfig() {
127
158
  return null;
128
159
  }
129
160
  }
161
+ async function readCopilotUserMcpConfig() {
162
+ const configPath = path.join(getCopilotHome(), "mcp-config.json");
163
+ const raw = await readTextFile(configPath);
164
+ if (!raw) {
165
+ return null;
166
+ }
167
+ try {
168
+ const parsed = JSON.parse(raw);
169
+ return parsed.mcpServers?.[MCP_SERVER_NAME] ?? null;
170
+ }
171
+ catch {
172
+ return null;
173
+ }
174
+ }
130
175
  async function configureCodex(launch) {
131
176
  const existing = await readCodexMcpConfig();
132
177
  const mcpStatus = existing === null
@@ -197,6 +242,49 @@ async function configureClaude(cwd, launch) {
197
242
  },
198
243
  ];
199
244
  }
245
+ async function configureCopilot(launch) {
246
+ const existing = await readCopilotUserMcpConfig();
247
+ const mcpStatus = existing === null
248
+ ? "installed"
249
+ : copilotConfigMatches(existing, launch)
250
+ ? "unchanged"
251
+ : "updated";
252
+ if (existing) {
253
+ await execText("copilot", ["mcp", "remove", MCP_SERVER_NAME]);
254
+ }
255
+ await execText("copilot", [
256
+ "mcp",
257
+ "add",
258
+ MCP_SERVER_NAME,
259
+ "--type",
260
+ "stdio",
261
+ "--tools",
262
+ "*",
263
+ "--",
264
+ launch.command,
265
+ ...launch.args,
266
+ ]);
267
+ const skillSource = path.join(PACKAGE_ROOT, "skills", CODEX_SKILL_NAME, "SKILL.md");
268
+ const skillTarget = path.join(getCopilotHome(), "skills", CODEX_SKILL_NAME, "SKILL.md");
269
+ const skillContent = await readFile(skillSource, "utf8");
270
+ const skillStatus = await writeManagedFile(skillTarget, skillContent);
271
+ return [
272
+ {
273
+ client: "copilot",
274
+ kind: "mcp",
275
+ status: mcpStatus,
276
+ path: path.join(getCopilotHome(), "mcp-config.json"),
277
+ detail: `Registered ${MCP_SERVER_NAME} -> ${launch.label} in GitHub Copilot CLI user config`,
278
+ },
279
+ {
280
+ client: "copilot",
281
+ kind: "skill",
282
+ status: skillStatus,
283
+ path: skillTarget,
284
+ detail: "Installed the Apex GitHub Copilot CLI skill",
285
+ },
286
+ ];
287
+ }
200
288
  function summarizeStep(step) {
201
289
  const prefix = `${step.client} ${step.kind}`;
202
290
  if (step.path) {
@@ -204,29 +292,51 @@ function summarizeStep(step) {
204
292
  }
205
293
  return `${prefix}: ${step.status}`;
206
294
  }
295
+ function isSetupClient(value) {
296
+ return SETUP_CLIENTS.includes(value);
297
+ }
298
+ function displayClientName(client) {
299
+ switch (client) {
300
+ case "codex":
301
+ return "Codex";
302
+ case "claude":
303
+ return "Claude Code";
304
+ case "copilot":
305
+ return "GitHub Copilot CLI";
306
+ }
307
+ }
308
+ function isMissingClientError(error) {
309
+ return error instanceof Error && /spawn (codex|claude|copilot) ENOENT/i.test(error.message);
310
+ }
311
+ async function configureClient(client, cwd, launch) {
312
+ switch (client) {
313
+ case "codex":
314
+ return configureCodex(launch);
315
+ case "claude":
316
+ return configureClaude(cwd, launch);
317
+ case "copilot":
318
+ return configureCopilot(launch);
319
+ }
320
+ }
207
321
  export async function commandSetup(cwd, flags, requestedTarget, packageRoot = PACKAGE_ROOT) {
208
- if (requestedTarget !== null &&
209
- requestedTarget !== "all" &&
210
- requestedTarget !== "codex" &&
211
- requestedTarget !== "claude") {
212
- throw new Error("Usage: apex setup [all|codex|claude]");
322
+ if (requestedTarget !== null && requestedTarget !== "all" && !isSetupClient(requestedTarget)) {
323
+ throw new Error("Usage: apex setup [all|codex|claude|copilot]");
213
324
  }
214
325
  const target = requestedTarget ?? "all";
215
326
  const launch = await resolveMcpLaunchSpec(packageRoot);
216
327
  const steps = [];
217
- const requestedClients = target === "all" ? ["codex", "claude"] : [target];
328
+ const requestedClients = target === "all" ? [...SETUP_CLIENTS] : [target];
218
329
  let configuredClients = 0;
219
330
  for (const client of requestedClients) {
220
331
  try {
221
- const clientSteps = client === "codex" ? await configureCodex(launch) : await configureClaude(cwd, launch);
332
+ const clientSteps = await configureClient(client, cwd, launch);
222
333
  steps.push(...clientSteps);
223
334
  configuredClients += 1;
224
335
  }
225
336
  catch (error) {
226
- const missingClient = error instanceof Error &&
227
- /spawn (codex|claude) ENOENT/i.test(error.message);
337
+ const missingClient = isMissingClientError(error);
228
338
  if (missingClient && target !== "all") {
229
- throw new Error(`${client === "codex" ? "Codex" : "Claude Code"} is not installed on this machine. Install it first, then re-run \`apex setup ${client}\`.`);
339
+ throw new Error(`${displayClientName(client)} is not installed on this machine. Install it first, then re-run \`apex setup ${client}\`.`);
230
340
  }
231
341
  if (!missingClient) {
232
342
  throw error;
@@ -236,19 +346,19 @@ export async function commandSetup(cwd, flags, requestedTarget, packageRoot = PA
236
346
  kind: "mcp",
237
347
  status: "skipped",
238
348
  path: null,
239
- detail: `${client} is not installed on this machine`,
349
+ detail: `${displayClientName(client)} is not installed on this machine`,
240
350
  });
241
351
  steps.push({
242
352
  client,
243
353
  kind: "skill",
244
354
  status: "skipped",
245
355
  path: null,
246
- detail: `${client} is not installed on this machine`,
356
+ detail: `${displayClientName(client)} is not installed on this machine`,
247
357
  });
248
358
  }
249
359
  }
250
360
  if (configuredClients === 0) {
251
- throw new Error("Neither Codex nor Claude Code is installed. Install one of them first, then re-run `apex setup`.");
361
+ throw new Error("Neither Codex, Claude Code, nor GitHub Copilot CLI is installed. Install one of them first, then re-run `apex setup`.");
252
362
  }
253
363
  const payload = {
254
364
  target,
@@ -271,5 +381,8 @@ export async function commandSetup(cwd, flags, requestedTarget, packageRoot = PA
271
381
  if (requestedClients.includes("claude")) {
272
382
  logLine("Re-run `apex setup claude` in each repository where you want the Claude project skill.", flags);
273
383
  }
384
+ if (requestedClients.includes("copilot")) {
385
+ logLine("Restart GitHub Copilot CLI or run `/skills reload` to pick up a newly installed or updated skill.", flags);
386
+ }
274
387
  return payload;
275
388
  }
package/dist/update.js CHANGED
@@ -456,7 +456,7 @@ export async function commandUpdate(flags, packageRoot = PACKAGE_ROOT) {
456
456
  printJson(payload);
457
457
  }
458
458
  else {
459
- logLine("Apex CLI updated. Re-run `apex` to use the latest version. Re-run `apex setup` to refresh copied Codex or Claude skill files.", flags);
459
+ logLine("Apex CLI updated. Re-run `apex` to use the latest version. Re-run `apex setup` to refresh copied Codex, Claude Code, or GitHub Copilot CLI skill files.", flags);
460
460
  }
461
461
  return payload;
462
462
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cantinasecurity/apex-cli",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Standalone CLI and MCP server for Apex.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -5,7 +5,7 @@ description: Use when a user wants to start Apex scans, inspect findings, bind w
5
5
 
6
6
  # Apex CLI
7
7
 
8
- This skill is bundled with Apex CLI and can be installed into Codex with `apex setup codex`.
8
+ This skill is bundled with Apex CLI and can be installed into Codex with `apex setup codex` or GitHub Copilot CLI with `apex setup copilot`.
9
9
 
10
10
  Prefer the Apex MCP tools over running `apex` in the shell when the server is available.
11
11