@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.
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.mcp.claude.json +1 -1
- package/.mcp.codex.json +1 -1
- package/MARKETPLACE.md +1 -1
- package/README.md +13 -5
- package/dist/help.js +2 -1
- package/dist/setup.js +126 -13
- package/dist/update.js +1 -1
- package/package.json +1 -1
- package/skills/apex-cli/SKILL.md +1 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Cantina agent plugins for security review workflows.",
|
|
9
|
-
"version": "0.1.
|
|
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.
|
|
17
|
+
"version": "0.1.10"
|
|
18
18
|
},
|
|
19
19
|
"description": "Run Apex security scans and review findings from Claude Code.",
|
|
20
|
-
"version": "0.1.
|
|
20
|
+
"version": "0.1.10",
|
|
21
21
|
"author": {
|
|
22
22
|
"name": "Cantina",
|
|
23
23
|
"email": "support@cantina.xyz"
|
package/.mcp.claude.json
CHANGED
package/.mcp.codex.json
CHANGED
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.
|
|
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
|
|
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
|
|
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.
|
|
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]
|
|
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
|
-
|
|
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" ? [
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
package/skills/apex-cli/SKILL.md
CHANGED
|
@@ -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
|
|