@heretyc/subagent-mcp 2.6.0 → 2.6.2
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/README.md +124 -124
- package/directives/carryover-claude.md +17 -17
- package/directives/carryover-codex.md +17 -17
- package/directives/off-turn-reminder.md +1 -1
- package/directives/orchestration-claude.md +21 -21
- package/directives/orchestration-codex.md +22 -22
- package/dist/advanced-ruleset.py +67 -67
- package/dist/doctor.js +6 -6
- package/dist/routing-table.json +3821 -3821
- package/dist/ruleset-scaffold.js +1 -1
- package/dist/setup.js +118 -36
- package/package.json +51 -51
- package/scripts/postinstall.mjs +102 -102
package/dist/ruleset-scaffold.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// GENERATED by scripts/gen-ruleset-scaffold.mjs from src/advanced-ruleset.py — DO NOT EDIT.
|
|
2
|
-
export const RULESET_SCAFFOLD = "#!/usr/bin/env python3\
|
|
2
|
+
export const RULESET_SCAFFOLD = "#!/usr/bin/env python3\n\"\"\"advanced-ruleset.py — final-authority model-routing override hook for subagent-mcp.\n\n(a) PERFORMANCE WARNING: this script runs synchronously inside EVERY launch_agent\n call. Slow rules slow every agent launch. Keep rules lean and low-latency —\n no network calls, no heavy imports at module top. This is YOUR responsibility;\n you have been warned.\n\n(b) OUTPUT CONTRACT (routing mode): print to stdout ONE JSON array — the modified\n candidate list (reorder / filter / replace allowed). Template:\n [\n {\"provider\": \"claude\", \"model\": \"sonnet\", \"effort\": \"high\", \"rank\": 1},\n {\"provider\": \"codex\", \"model\": \"gpt-5.5\", \"effort\": \"xhigh\", \"rank\": 2}\n ]\n Valid providers: claude, codex. Valid models: haiku, sonnet, opus, opus-4-8 (claude);\n gpt-5.5 (codex). Valid efforts: haiku -> \"none\" only; sonnet -> low|medium|high|xhigh|max;\n opus/opus-4-8 -> those plus ultracode; gpt-5.5 -> low|medium|high|xhigh.\n \"rank\" on output is ignored. An EMPTY array vetoes the launch. Anything else\n invalid fails the launch hard — the server validates strictly.\n\n(c) INPUT CONTRACT (routing mode, invoked as: <python> advanced-ruleset.py route):\n stdin receives one JSON object:\n { \"candidates\": [ {\"provider\",\"model\",\"effort\",\"rank\"} ... ], # rank 1..N best->worst\n \"context\": { \"task_category\": str, \"cwd\": str,\n \"selection_mode\": \"auto\"|\"provider\"|\"provider_model\"|\"explicit\",\n \"provider\": str|None, \"model\": str|None, \"effort\": str|None } }\n OS environment variables are visible natively (os.environ).\n\nENV-CHECK MODE (no arguments): prints {\"ready\": true|false, \"load-rules\": true|false}.\nRuns once per MCP server process. load-rules false => ruleset silently disabled\nfor the rest of the process. Set LOAD_RULES = True below to activate.\n\"\"\"\nimport json\nimport sys\n\nLOAD_RULES = False\n\n# --- Requirements stub (scaffold itself is stdlib-only) ----------------------\n# List third-party distributions your rules import, e.g.:\n# REQUIREMENTS = [\"requests\", \"pyyaml\"]\n# Install with: <python> -m pip install <name> ...\nREQUIREMENTS = []\n\ndef missing_requirements():\n \"\"\"pip-check helper: returns the REQUIREMENTS entries not importable here.\"\"\"\n import importlib.util\n return [r for r in REQUIREMENTS\n if importlib.util.find_spec(r.replace(\"-\", \"_\")) is None]\n\ndef env_check():\n missing = missing_requirements()\n json.dump({\"ready\": not missing, \"load-rules\": bool(LOAD_RULES)}, sys.stdout)\n\ndef apply_rules(candidates, context):\n \"\"\"YOUR RULES HERE. Default: passthrough (returns the list unchanged).\"\"\"\n return candidates\n\ndef route():\n payload = json.load(sys.stdin)\n out = apply_rules(payload.get(\"candidates\", []), payload.get(\"context\", {}))\n json.dump(out, sys.stdout)\n\nif __name__ == \"__main__\":\n if len(sys.argv) > 1 and sys.argv[1] == \"route\":\n route()\n else:\n env_check()\n";
|
package/dist/setup.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// - Every config file is backed up before its first edit.
|
|
17
17
|
// - Failures never abort the run: they are collected and reported at the end
|
|
18
18
|
// with a copy-paste repair prompt the user can hand to Claude/Codex.
|
|
19
|
-
import { existsSync, readFileSync, writeFileSync, copyFileSync, } from "node:fs";
|
|
19
|
+
import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync, } from "node:fs";
|
|
20
20
|
import { homedir } from "node:os";
|
|
21
21
|
import { join, dirname, resolve } from "node:path";
|
|
22
22
|
import { fileURLToPath } from "node:url";
|
|
@@ -104,17 +104,14 @@ export function reconcileClaudeJson(cj, serverPath) {
|
|
|
104
104
|
const cur = servers["subagent-mcp"];
|
|
105
105
|
if (cur) {
|
|
106
106
|
const args = cur.args;
|
|
107
|
-
const exact = cur.command === "
|
|
108
|
-
Array.isArray(args) &&
|
|
109
|
-
args.length === 1 &&
|
|
110
|
-
args[0] === serverPath;
|
|
107
|
+
const exact = cur.command === "subagent-mcp" && Array.isArray(args) && args.length === 0;
|
|
111
108
|
if (exact)
|
|
112
109
|
return { changed: false, status: "ok" };
|
|
113
110
|
}
|
|
114
111
|
servers["subagent-mcp"] = {
|
|
115
112
|
type: "stdio",
|
|
116
|
-
command: "
|
|
117
|
-
args: [
|
|
113
|
+
command: "subagent-mcp",
|
|
114
|
+
args: [],
|
|
118
115
|
env: {},
|
|
119
116
|
};
|
|
120
117
|
return { changed: true, status: cur ? "repaired" : "added" };
|
|
@@ -144,7 +141,7 @@ export function reconcileCodexToml(toml, serverPath) {
|
|
|
144
141
|
status: "added",
|
|
145
142
|
};
|
|
146
143
|
}
|
|
147
|
-
if (m[0].includes(`args = ["${serverPath}"]`)) {
|
|
144
|
+
if (m[0].includes(`command = "node"`) && m[0].includes(`args = ["${serverPath}"]`)) {
|
|
148
145
|
return { toml, changed: false, status: "ok" };
|
|
149
146
|
}
|
|
150
147
|
return {
|
|
@@ -242,26 +239,60 @@ function backup(file) {
|
|
|
242
239
|
}
|
|
243
240
|
}
|
|
244
241
|
function runCmd(cmd, cmdArgs) {
|
|
242
|
+
return runCmdCapture(cmd, cmdArgs).ok;
|
|
243
|
+
}
|
|
244
|
+
function quoteWinShellArg(arg) {
|
|
245
|
+
if (!/[ \t"]/.test(arg))
|
|
246
|
+
return arg;
|
|
247
|
+
return `"${arg.replace(/"/g, '\\"')}"`;
|
|
248
|
+
}
|
|
249
|
+
function quoteWinShellExe(exe) {
|
|
250
|
+
return `"${exe.replace(/"/g, '\\"')}"`;
|
|
251
|
+
}
|
|
252
|
+
function runCmdCapture(cmd, cmdArgs) {
|
|
245
253
|
console.log(` $ ${cmd} ${cmdArgs.join(" ")}`);
|
|
246
254
|
if (DRY_RUN) {
|
|
247
255
|
console.log(" (dry-run: skipped)");
|
|
248
|
-
return true;
|
|
256
|
+
return { ok: true, stdout: "" };
|
|
249
257
|
}
|
|
250
258
|
try {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
execSync(
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
execFileSync(cmd, cmdArgs, { stdio: "pipe" });
|
|
258
|
-
}
|
|
259
|
-
return true;
|
|
259
|
+
const exe = findOnPath(cmd) ?? cmd;
|
|
260
|
+
const isWinCmdShim = process.platform === "win32" && /\.(?:cmd|bat)$/i.test(exe);
|
|
261
|
+
const stdout = isWinCmdShim
|
|
262
|
+
? execSync([quoteWinShellExe(exe), ...cmdArgs.map(quoteWinShellArg)].join(" "), { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] })
|
|
263
|
+
: execFileSync(exe, cmdArgs, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
264
|
+
return { ok: true, stdout };
|
|
260
265
|
}
|
|
261
266
|
catch {
|
|
262
|
-
return false;
|
|
267
|
+
return { ok: false, stdout: "" };
|
|
263
268
|
}
|
|
264
269
|
}
|
|
270
|
+
export function claudeAddArgs() {
|
|
271
|
+
return ["mcp", "add", "subagent-mcp", "subagent-mcp", "-s", "user"];
|
|
272
|
+
}
|
|
273
|
+
export function claudeRemoveArgs() {
|
|
274
|
+
return ["mcp", "remove", "subagent-mcp", "-s", "user"];
|
|
275
|
+
}
|
|
276
|
+
export function codexAddArgs(serverPath) {
|
|
277
|
+
return ["mcp", "add", "subagent-mcp", "--", "node", serverPath];
|
|
278
|
+
}
|
|
279
|
+
export function codexRemoveArgs() {
|
|
280
|
+
return ["mcp", "remove", "subagent-mcp"];
|
|
281
|
+
}
|
|
282
|
+
function claudeRegisteredViaCli() {
|
|
283
|
+
const get = runCmdCapture("claude", ["mcp", "get", "subagent-mcp"]);
|
|
284
|
+
if (get.ok && get.stdout.includes("subagent-mcp"))
|
|
285
|
+
return true;
|
|
286
|
+
const list = runCmdCapture("claude", ["mcp", "list"]);
|
|
287
|
+
return list.ok && list.stdout.includes("subagent-mcp");
|
|
288
|
+
}
|
|
289
|
+
function codexRegisteredViaCli() {
|
|
290
|
+
const get = runCmdCapture("codex", ["mcp", "get", "subagent-mcp"]);
|
|
291
|
+
if (get.ok && get.stdout.includes("subagent-mcp"))
|
|
292
|
+
return true;
|
|
293
|
+
const list = runCmdCapture("codex", ["mcp", "list"]);
|
|
294
|
+
return list.ok && list.stdout.includes("subagent-mcp");
|
|
295
|
+
}
|
|
265
296
|
const issues = [];
|
|
266
297
|
function repairPromptFor(vendor, problem) {
|
|
267
298
|
const p = serverPaths();
|
|
@@ -269,7 +300,7 @@ function repairPromptFor(vendor, problem) {
|
|
|
269
300
|
return (`subagent-mcp setup hit a problem on my machine: ${problem}. ` +
|
|
270
301
|
`The install root is "${fwd(INSTALL_ROOT)}". Please repair my Claude Code wiring: ` +
|
|
271
302
|
`(1) register a user-scope MCP server named "subagent-mcp" running ` +
|
|
272
|
-
`
|
|
303
|
+
`the global bin shim "subagent-mcp" (use 'claude mcp add subagent-mcp subagent-mcp -s user' or edit the mcpServers ` +
|
|
273
304
|
`key in ~/.claude.json), and (2) ensure ~/.claude/settings.json has a ` +
|
|
274
305
|
`hooks.UserPromptSubmit entry {type:"command", command:"node", args:["${p.claudeHook}"]}. ` +
|
|
275
306
|
`Back up any file before editing it.`);
|
|
@@ -308,20 +339,27 @@ function wireClaude() {
|
|
|
308
339
|
const probe = JSON.parse(JSON.stringify(cj));
|
|
309
340
|
const { status } = reconcileClaudeJson(probe, p.server);
|
|
310
341
|
if (status === "ok") {
|
|
311
|
-
|
|
342
|
+
let registered = claudeRegisteredViaCli();
|
|
343
|
+
if (!registered) {
|
|
344
|
+
runCmd("claude", claudeAddArgs());
|
|
345
|
+
registered = claudeRegisteredViaCli();
|
|
346
|
+
}
|
|
347
|
+
if (registered)
|
|
348
|
+
describe("ok", "MCP server (user scope)");
|
|
349
|
+
else
|
|
350
|
+
fail("claude", "MCP server file shape is correct, but 'claude mcp add' failed to register it with the CLI");
|
|
312
351
|
}
|
|
313
352
|
else {
|
|
314
353
|
if (status === "repaired") {
|
|
315
354
|
console.log(" MCP server registration points at a stale path — re-registering.");
|
|
316
|
-
runCmd("claude",
|
|
355
|
+
runCmd("claude", claudeRemoveArgs());
|
|
317
356
|
}
|
|
318
|
-
const cliOk = runCmd("claude",
|
|
319
|
-
|
|
320
|
-
]);
|
|
357
|
+
const cliOk = runCmd("claude", claudeAddArgs());
|
|
358
|
+
const cliVerified = cliOk && claudeRegisteredViaCli();
|
|
321
359
|
// Read back; if the CLI failed or didn't take, write the entry directly.
|
|
322
360
|
const after = readJson(cjFile, {});
|
|
323
361
|
const verify = reconcileClaudeJson(after, p.server);
|
|
324
|
-
if (verify.status !== "ok" && !DRY_RUN) {
|
|
362
|
+
if (!cliVerified && verify.status !== "ok" && !DRY_RUN) {
|
|
325
363
|
if (!cliOk)
|
|
326
364
|
console.log(" 'claude mcp add' failed — writing ~/.claude.json directly.");
|
|
327
365
|
backup(cjFile);
|
|
@@ -359,7 +397,14 @@ function wireCodex() {
|
|
|
359
397
|
const cfg = join(codexDir, "config.toml");
|
|
360
398
|
const toml = existsSync(cfg) ? readFileSync(cfg, "utf8") : "";
|
|
361
399
|
const r = reconcileCodexToml(toml, p.server);
|
|
362
|
-
if (r.
|
|
400
|
+
if (r.status === "repaired") {
|
|
401
|
+
console.log(" MCP server registration points at a stale path — re-registering.");
|
|
402
|
+
runCmd("codex", codexRemoveArgs());
|
|
403
|
+
}
|
|
404
|
+
const cliOk = r.status === "ok" && codexRegisteredViaCli() ? true : runCmd("codex", codexAddArgs(p.server));
|
|
405
|
+
if (!cliOk && r.changed && !DRY_RUN) {
|
|
406
|
+
console.log(" 'codex mcp add' failed — writing ~/.codex/config.toml directly.");
|
|
407
|
+
mkdirSync(codexDir, { recursive: true });
|
|
363
408
|
backup(cfg);
|
|
364
409
|
writeFileSync(cfg, r.toml);
|
|
365
410
|
}
|
|
@@ -377,6 +422,7 @@ function wireCodex() {
|
|
|
377
422
|
const hookCmd = `node "${p.codexHook}"`;
|
|
378
423
|
const { changed, statuses } = reconcileCodexHooks(h, hookCmd);
|
|
379
424
|
if (changed && !DRY_RUN) {
|
|
425
|
+
mkdirSync(codexDir, { recursive: true });
|
|
380
426
|
backup(hfile);
|
|
381
427
|
writeFileSync(hfile, JSON.stringify(h, null, 2));
|
|
382
428
|
}
|
|
@@ -392,7 +438,7 @@ function wireCodex() {
|
|
|
392
438
|
fail("codex", `could not write hooks.json: ${e.message}`);
|
|
393
439
|
}
|
|
394
440
|
}
|
|
395
|
-
export function verifyWiring(root = INSTALL_ROOT) {
|
|
441
|
+
export function verifyWiring(root = INSTALL_ROOT, repair = false) {
|
|
396
442
|
const p = serverPaths(root);
|
|
397
443
|
const results = [];
|
|
398
444
|
const home = homedir();
|
|
@@ -403,7 +449,29 @@ export function verifyWiring(root = INSTALL_ROOT) {
|
|
|
403
449
|
detail: missing.length === 0 ? `all present under ${fwd(root)}` : `missing: ${missing.join(", ")}`,
|
|
404
450
|
});
|
|
405
451
|
const hasClaude = findOnPath("claude") !== null;
|
|
452
|
+
const hasClaudeConfig = existsSync(join(home, ".claude.json"));
|
|
406
453
|
if (hasClaude) {
|
|
454
|
+
const sj = readJson(join(home, ".claude", "settings.json"), {});
|
|
455
|
+
let registered = claudeRegisteredViaCli();
|
|
456
|
+
let repaired = false;
|
|
457
|
+
if (!registered && repair) {
|
|
458
|
+
runCmd("claude", claudeAddArgs());
|
|
459
|
+
repaired = true;
|
|
460
|
+
registered = claudeRegisteredViaCli();
|
|
461
|
+
}
|
|
462
|
+
const hk = reconcileClaudeSettings(JSON.parse(JSON.stringify(sj)), p.claudeHook);
|
|
463
|
+
results.push({
|
|
464
|
+
label: "claude: MCP server (user scope)",
|
|
465
|
+
ok: registered,
|
|
466
|
+
detail: registered ? (repaired ? "repaired" : "registered") : "not registered; CLI repair failed",
|
|
467
|
+
});
|
|
468
|
+
results.push({
|
|
469
|
+
label: "claude: UserPromptSubmit hook",
|
|
470
|
+
ok: hk.status === "ok",
|
|
471
|
+
detail: hk.status === "ok" ? "wired" : `${hk.status === "repaired" ? "stale path" : "not wired"} - run: subagent-mcp setup`,
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
else if (hasClaudeConfig) {
|
|
407
475
|
const cj = readJson(join(home, ".claude.json"), {});
|
|
408
476
|
const sj = readJson(join(home, ".claude", "settings.json"), {});
|
|
409
477
|
const srv = reconcileClaudeJson(JSON.parse(JSON.stringify(cj)), p.server);
|
|
@@ -411,34 +479,48 @@ export function verifyWiring(root = INSTALL_ROOT) {
|
|
|
411
479
|
results.push({
|
|
412
480
|
label: "claude: MCP server (user scope)",
|
|
413
481
|
ok: srv.status === "ok",
|
|
414
|
-
detail: srv.status === "ok" ? "registered
|
|
482
|
+
detail: srv.status === "ok" ? "registered (file fallback)" : "not registered; claude CLI not on PATH",
|
|
415
483
|
});
|
|
416
484
|
results.push({
|
|
417
485
|
label: "claude: UserPromptSubmit hook",
|
|
418
486
|
ok: hk.status === "ok",
|
|
419
|
-
detail: hk.status === "ok" ? "wired" : `${hk.status === "repaired" ? "stale path" : "not wired"}
|
|
487
|
+
detail: hk.status === "ok" ? "wired" : `${hk.status === "repaired" ? "stale path" : "not wired"} - run: subagent-mcp setup`,
|
|
420
488
|
});
|
|
421
489
|
}
|
|
422
|
-
const
|
|
490
|
+
const hasCodexCli = findOnPath("codex") !== null;
|
|
491
|
+
const hasCodex = hasCodexCli || existsSync(join(home, ".codex"));
|
|
423
492
|
if (hasCodex) {
|
|
424
493
|
const cfg = join(home, ".codex", "config.toml");
|
|
425
494
|
const toml = existsSync(cfg) ? readFileSync(cfg, "utf8") : "";
|
|
426
495
|
const tomlR = reconcileCodexToml(toml, p.server);
|
|
427
496
|
const hj = readJson(join(home, ".codex", "hooks.json"), { hooks: {} });
|
|
428
497
|
const hkR = reconcileCodexHooks(hj, `node "${p.codexHook}"`);
|
|
498
|
+
let registered = false;
|
|
499
|
+
let repaired = false;
|
|
500
|
+
if (hasCodexCli) {
|
|
501
|
+
registered = codexRegisteredViaCli();
|
|
502
|
+
if (!registered && repair) {
|
|
503
|
+
runCmd("codex", codexAddArgs(p.server));
|
|
504
|
+
repaired = true;
|
|
505
|
+
registered = codexRegisteredViaCli();
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
registered = tomlR.status === "ok";
|
|
510
|
+
}
|
|
429
511
|
results.push({
|
|
430
512
|
label: "codex: config.toml MCP server block",
|
|
431
|
-
ok:
|
|
432
|
-
detail:
|
|
513
|
+
ok: registered,
|
|
514
|
+
detail: registered ? (repaired ? "repaired" : "registered") : "not registered; CLI repair failed",
|
|
433
515
|
});
|
|
434
516
|
const allOk = Object.values(hkR.statuses).every((s) => s === "ok");
|
|
435
517
|
results.push({
|
|
436
518
|
label: "codex: SessionStart + UserPromptSubmit hooks",
|
|
437
519
|
ok: allOk,
|
|
438
|
-
detail: allOk ? "wired (trust via /hooks in Codex)" : "incomplete
|
|
520
|
+
detail: allOk ? "wired (trust via /hooks in Codex)" : "incomplete - run: subagent-mcp setup",
|
|
439
521
|
});
|
|
440
522
|
}
|
|
441
|
-
if (!hasClaude && !hasCodex) {
|
|
523
|
+
if (!hasClaude && !hasClaudeConfig && !hasCodex) {
|
|
442
524
|
results.push({
|
|
443
525
|
label: "vendors",
|
|
444
526
|
ok: false,
|
|
@@ -483,7 +565,7 @@ export async function runSetup() {
|
|
|
483
565
|
// Read-back verification: report what is ACTUALLY on disk now.
|
|
484
566
|
if (!DRY_RUN) {
|
|
485
567
|
console.log("\n--- Verification (read-back) ---");
|
|
486
|
-
for (const r of verifyWiring()) {
|
|
568
|
+
for (const r of verifyWiring(INSTALL_ROOT, false)) {
|
|
487
569
|
console.log(` ${r.ok ? "PASS" : "FAIL"} ${r.label} — ${r.detail}`);
|
|
488
570
|
}
|
|
489
571
|
}
|
package/package.json
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@heretyc/subagent-mcp",
|
|
3
|
-
"version": "2.6.
|
|
4
|
-
"description": "MCP server that launches and manages local Claude Code and Codex CLI sub-agents as child processes (no direct Anthropic/OpenAI API).",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"bin": {
|
|
8
|
-
"subagent-mcp": "dist/index.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"dist",
|
|
12
|
-
"directives",
|
|
13
|
-
"scripts/postinstall.mjs",
|
|
14
|
-
"LICENSE",
|
|
15
|
-
"NOTICE",
|
|
16
|
-
"README.md"
|
|
17
|
-
],
|
|
18
|
-
"scripts": {
|
|
19
|
-
"build": "node scripts/gen-ruleset-scaffold.mjs && tsc && node scripts/copy-provider.mjs",
|
|
20
|
-
"start": "node dist/index.js",
|
|
21
|
-
"postinstall": "node scripts/postinstall.mjs",
|
|
22
|
-
"prepare": "npm run build",
|
|
23
|
-
"prepublishOnly": "npm test",
|
|
24
|
-
"test": "node test/effort.test.mjs && node test/platform.test.mjs && node test/wait.test.mjs && node test/status.test.mjs && node test/output.test.mjs && node test/stream.test.mjs && node test/routing.test.mjs && node test/deadlock.test.mjs && node test/handler-validation.test.mjs && node test/index-handler.test.mjs && node test/ruleset.test.mjs && node test/ruleset-exec.test.mjs && node test/ruleset-handler.test.mjs && node test/failover.test.mjs && node test/orchestration-marker.test.mjs && node test/orchestration-hook-core.test.mjs && node test/orchestration-adapters.test.mjs && node test/orchestration-directives.test.mjs && node test/setup-repair.test.mjs && node scripts/validate_provider.mjs && node scripts/validate_seed_sites.mjs && node scripts/validate_routing_audit.mjs && node test/seed-sites.test.mjs && node test/mcp-compliance.test.mjs"
|
|
25
|
-
},
|
|
26
|
-
"author": "Lexi Blackburn",
|
|
27
|
-
"license": "Apache-2.0",
|
|
28
|
-
"repository": {
|
|
29
|
-
"type": "git",
|
|
30
|
-
"url": "git+https://github.com/Heretyc/subagent-mcp.git"
|
|
31
|
-
},
|
|
32
|
-
"homepage": "https://github.com/Heretyc/subagent-mcp#readme",
|
|
33
|
-
"bugs": {
|
|
34
|
-
"url": "https://github.com/Heretyc/subagent-mcp/issues"
|
|
35
|
-
},
|
|
36
|
-
"dependencies": {
|
|
37
|
-
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
38
|
-
"zod": "^3.0.0"
|
|
39
|
-
},
|
|
40
|
-
"devDependencies": {
|
|
41
|
-
"@types/node": "^20.0.0",
|
|
42
|
-
"typescript": "^5.0.0"
|
|
43
|
-
},
|
|
44
|
-
"engines": {
|
|
45
|
-
"node": ">=18"
|
|
46
|
-
},
|
|
47
|
-
"publishConfig": {
|
|
48
|
-
"registry": "https://npm.pkg.github.com",
|
|
49
|
-
"access": "public"
|
|
50
|
-
}
|
|
51
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@heretyc/subagent-mcp",
|
|
3
|
+
"version": "2.6.2",
|
|
4
|
+
"description": "MCP server that launches and manages local Claude Code and Codex CLI sub-agents as child processes (no direct Anthropic/OpenAI API).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"subagent-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"directives",
|
|
13
|
+
"scripts/postinstall.mjs",
|
|
14
|
+
"LICENSE",
|
|
15
|
+
"NOTICE",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "node scripts/gen-ruleset-scaffold.mjs && tsc && node scripts/copy-provider.mjs",
|
|
20
|
+
"start": "node dist/index.js",
|
|
21
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
22
|
+
"prepare": "npm run build",
|
|
23
|
+
"prepublishOnly": "npm test",
|
|
24
|
+
"test": "node test/effort.test.mjs && node test/platform.test.mjs && node test/wait.test.mjs && node test/status.test.mjs && node test/output.test.mjs && node test/stream.test.mjs && node test/routing.test.mjs && node test/deadlock.test.mjs && node test/handler-validation.test.mjs && node test/index-handler.test.mjs && node test/ruleset.test.mjs && node test/ruleset-exec.test.mjs && node test/ruleset-handler.test.mjs && node test/failover.test.mjs && node test/orchestration-marker.test.mjs && node test/orchestration-hook-core.test.mjs && node test/orchestration-adapters.test.mjs && node test/orchestration-directives.test.mjs && node test/setup-repair.test.mjs && node scripts/validate_provider.mjs && node scripts/validate_seed_sites.mjs && node scripts/validate_routing_audit.mjs && node test/seed-sites.test.mjs && node test/mcp-compliance.test.mjs"
|
|
25
|
+
},
|
|
26
|
+
"author": "Lexi Blackburn",
|
|
27
|
+
"license": "Apache-2.0",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/Heretyc/subagent-mcp.git"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/Heretyc/subagent-mcp#readme",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/Heretyc/subagent-mcp/issues"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
38
|
+
"zod": "^3.0.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^20.0.0",
|
|
42
|
+
"typescript": "^5.0.0"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"registry": "https://npm.pkg.github.com",
|
|
49
|
+
"access": "public"
|
|
50
|
+
}
|
|
51
|
+
}
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -1,102 +1,102 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Postinstall banner for subagent-mcp.
|
|
3
|
-
//
|
|
4
|
-
// `npm install -g @heretyc/subagent-mcp` ships only the MCP server + hook
|
|
5
|
-
// assets; it does NOT wire them into Claude Code / Codex. Without feedback the
|
|
6
|
-
// user has no idea an addon landed, let alone that a second step is required.
|
|
7
|
-
// This prints a clear "what installed / what to run next / how to verify"
|
|
8
|
-
// banner so the install is self-explanatory.
|
|
9
|
-
//
|
|
10
|
-
// Rules:
|
|
11
|
-
// - NEVER fail the install. Any error is swallowed; always exit 0.
|
|
12
|
-
// - Only speak for a real end-user install. In the dev checkout (src/ present)
|
|
13
|
-
// stay silent so `npm install` during development isn't noisy.
|
|
14
|
-
// - Print only — do not mutate vendor config. Wiring is the explicit,
|
|
15
|
-
// reversible `subagent-mcp setup` step.
|
|
16
|
-
|
|
17
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
18
|
-
import { homedir } from "node:os";
|
|
19
|
-
import { join, dirname, resolve } from "node:path";
|
|
20
|
-
import { fileURLToPath } from "node:url";
|
|
21
|
-
import { execSync } from "node:child_process";
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
// Install root: scripts/postinstall.mjs -> scripts/ -> <root>
|
|
25
|
-
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
26
|
-
|
|
27
|
-
// Dev checkout? (src/ only exists in the repo, never in the shipped tarball.)
|
|
28
|
-
// Stay silent there — the maintainer doesn't need the end-user banner.
|
|
29
|
-
if (existsSync(join(ROOT, "src"))) process.exit(0);
|
|
30
|
-
|
|
31
|
-
let version = "";
|
|
32
|
-
try {
|
|
33
|
-
version = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8")).version || "";
|
|
34
|
-
} catch { /* version is cosmetic */ }
|
|
35
|
-
|
|
36
|
-
// Best-effort vendor detection so the banner can say what WILL be wired.
|
|
37
|
-
// Pure read-only; failures just fall back to "not detected".
|
|
38
|
-
function has(cmd) {
|
|
39
|
-
try {
|
|
40
|
-
execSync(process.platform === "win32" ? `where ${cmd}` : `command -v ${cmd}`, {
|
|
41
|
-
stdio: "ignore",
|
|
42
|
-
});
|
|
43
|
-
return true;
|
|
44
|
-
} catch {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const hasClaude = has("claude");
|
|
49
|
-
const hasCodex = has("codex") || existsSync(join(homedir(), ".codex"));
|
|
50
|
-
|
|
51
|
-
const L = [];
|
|
52
|
-
const line = (s = "") => L.push(s);
|
|
53
|
-
const bar = "============================================================";
|
|
54
|
-
|
|
55
|
-
line();
|
|
56
|
-
line(bar);
|
|
57
|
-
line(` subagent-mcp installed${version ? ` (v${version})` : ""}`);
|
|
58
|
-
line(bar);
|
|
59
|
-
line();
|
|
60
|
-
line(" This is an MCP ADDON for Claude Code CLI and Codex CLI.");
|
|
61
|
-
line(" It is NOT active yet — one command wires it in.");
|
|
62
|
-
line();
|
|
63
|
-
line(" FINISH SETUP (auto-detects vendors, wires all present):");
|
|
64
|
-
line();
|
|
65
|
-
line(" subagent-mcp setup");
|
|
66
|
-
line();
|
|
67
|
-
line(" That registers the MCP server AND installs the per-turn");
|
|
68
|
-
line(" orchestration-mode hooks for every vendor it finds.");
|
|
69
|
-
line();
|
|
70
|
-
|
|
71
|
-
// Detected vendors — concrete, so the user knows what setup will touch.
|
|
72
|
-
if (hasClaude || hasCodex) {
|
|
73
|
-
line(" Detected on this machine:");
|
|
74
|
-
if (hasClaude) line(" - Claude Code CLI (will get MCP server + UserPromptSubmit hook)");
|
|
75
|
-
if (hasCodex) line(" - Codex CLI (will get MCP server + SessionStart/UserPromptSubmit hooks)");
|
|
76
|
-
} else {
|
|
77
|
-
line(" No Claude Code or Codex CLI detected yet. Install one,");
|
|
78
|
-
line(" then run: subagent-mcp setup");
|
|
79
|
-
}
|
|
80
|
-
line();
|
|
81
|
-
|
|
82
|
-
line(" AFTER setup — confirm it took effect:");
|
|
83
|
-
if (hasClaude || !hasCodex) {
|
|
84
|
-
line(" - Claude Code: restart the session, run /mcp");
|
|
85
|
-
line(" -> 'subagent-mcp' shows Connected.");
|
|
86
|
-
}
|
|
87
|
-
if (hasCodex || !hasClaude) {
|
|
88
|
-
line(" - Codex CLI: restart the session, run /hooks");
|
|
89
|
-
line(" -> TRUST the new subagent-mcp hook.");
|
|
90
|
-
}
|
|
91
|
-
line();
|
|
92
|
-
line(" Preview without changes: subagent-mcp setup --dry-run");
|
|
93
|
-
line(" Health check any time: subagent-mcp doctor");
|
|
94
|
-
line(" Docs: https://github.com/Heretyc/subagent-mcp#readme");
|
|
95
|
-
line(bar);
|
|
96
|
-
line();
|
|
97
|
-
|
|
98
|
-
process.stdout.write(L.join("\n") + "\n");
|
|
99
|
-
} catch {
|
|
100
|
-
// Never let a banner failure break the install.
|
|
101
|
-
}
|
|
102
|
-
process.exit(0);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Postinstall banner for subagent-mcp.
|
|
3
|
+
//
|
|
4
|
+
// `npm install -g @heretyc/subagent-mcp` ships only the MCP server + hook
|
|
5
|
+
// assets; it does NOT wire them into Claude Code / Codex. Without feedback the
|
|
6
|
+
// user has no idea an addon landed, let alone that a second step is required.
|
|
7
|
+
// This prints a clear "what installed / what to run next / how to verify"
|
|
8
|
+
// banner so the install is self-explanatory.
|
|
9
|
+
//
|
|
10
|
+
// Rules:
|
|
11
|
+
// - NEVER fail the install. Any error is swallowed; always exit 0.
|
|
12
|
+
// - Only speak for a real end-user install. In the dev checkout (src/ present)
|
|
13
|
+
// stay silent so `npm install` during development isn't noisy.
|
|
14
|
+
// - Print only — do not mutate vendor config. Wiring is the explicit,
|
|
15
|
+
// reversible `subagent-mcp setup` step.
|
|
16
|
+
|
|
17
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
18
|
+
import { homedir } from "node:os";
|
|
19
|
+
import { join, dirname, resolve } from "node:path";
|
|
20
|
+
import { fileURLToPath } from "node:url";
|
|
21
|
+
import { execSync } from "node:child_process";
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
// Install root: scripts/postinstall.mjs -> scripts/ -> <root>
|
|
25
|
+
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
26
|
+
|
|
27
|
+
// Dev checkout? (src/ only exists in the repo, never in the shipped tarball.)
|
|
28
|
+
// Stay silent there — the maintainer doesn't need the end-user banner.
|
|
29
|
+
if (existsSync(join(ROOT, "src"))) process.exit(0);
|
|
30
|
+
|
|
31
|
+
let version = "";
|
|
32
|
+
try {
|
|
33
|
+
version = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8")).version || "";
|
|
34
|
+
} catch { /* version is cosmetic */ }
|
|
35
|
+
|
|
36
|
+
// Best-effort vendor detection so the banner can say what WILL be wired.
|
|
37
|
+
// Pure read-only; failures just fall back to "not detected".
|
|
38
|
+
function has(cmd) {
|
|
39
|
+
try {
|
|
40
|
+
execSync(process.platform === "win32" ? `where ${cmd}` : `command -v ${cmd}`, {
|
|
41
|
+
stdio: "ignore",
|
|
42
|
+
});
|
|
43
|
+
return true;
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const hasClaude = has("claude");
|
|
49
|
+
const hasCodex = has("codex") || existsSync(join(homedir(), ".codex"));
|
|
50
|
+
|
|
51
|
+
const L = [];
|
|
52
|
+
const line = (s = "") => L.push(s);
|
|
53
|
+
const bar = "============================================================";
|
|
54
|
+
|
|
55
|
+
line();
|
|
56
|
+
line(bar);
|
|
57
|
+
line(` subagent-mcp installed${version ? ` (v${version})` : ""}`);
|
|
58
|
+
line(bar);
|
|
59
|
+
line();
|
|
60
|
+
line(" This is an MCP ADDON for Claude Code CLI and Codex CLI.");
|
|
61
|
+
line(" It is NOT active yet — one command wires it in.");
|
|
62
|
+
line();
|
|
63
|
+
line(" FINISH SETUP (auto-detects vendors, wires all present):");
|
|
64
|
+
line();
|
|
65
|
+
line(" subagent-mcp setup");
|
|
66
|
+
line();
|
|
67
|
+
line(" That registers the MCP server AND installs the per-turn");
|
|
68
|
+
line(" orchestration-mode hooks for every vendor it finds.");
|
|
69
|
+
line();
|
|
70
|
+
|
|
71
|
+
// Detected vendors — concrete, so the user knows what setup will touch.
|
|
72
|
+
if (hasClaude || hasCodex) {
|
|
73
|
+
line(" Detected on this machine:");
|
|
74
|
+
if (hasClaude) line(" - Claude Code CLI (will get MCP server + UserPromptSubmit hook)");
|
|
75
|
+
if (hasCodex) line(" - Codex CLI (will get MCP server + SessionStart/UserPromptSubmit hooks)");
|
|
76
|
+
} else {
|
|
77
|
+
line(" No Claude Code or Codex CLI detected yet. Install one,");
|
|
78
|
+
line(" then run: subagent-mcp setup");
|
|
79
|
+
}
|
|
80
|
+
line();
|
|
81
|
+
|
|
82
|
+
line(" AFTER setup — confirm it took effect:");
|
|
83
|
+
if (hasClaude || !hasCodex) {
|
|
84
|
+
line(" - Claude Code: restart the session, run /mcp");
|
|
85
|
+
line(" -> 'subagent-mcp' shows Connected.");
|
|
86
|
+
}
|
|
87
|
+
if (hasCodex || !hasClaude) {
|
|
88
|
+
line(" - Codex CLI: restart the session, run /hooks");
|
|
89
|
+
line(" -> TRUST the new subagent-mcp hook.");
|
|
90
|
+
}
|
|
91
|
+
line();
|
|
92
|
+
line(" Preview without changes: subagent-mcp setup --dry-run");
|
|
93
|
+
line(" Health check any time: subagent-mcp doctor");
|
|
94
|
+
line(" Docs: https://github.com/Heretyc/subagent-mcp#readme");
|
|
95
|
+
line(bar);
|
|
96
|
+
line();
|
|
97
|
+
|
|
98
|
+
process.stdout.write(L.join("\n") + "\n");
|
|
99
|
+
} catch {
|
|
100
|
+
// Never let a banner failure break the install.
|
|
101
|
+
}
|
|
102
|
+
process.exit(0);
|