@heretyc/subagent-mcp 2.6.2 → 2.7.0
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/dist/setup.js +192 -91
- package/package.json +2 -2
package/dist/setup.js
CHANGED
|
@@ -23,6 +23,7 @@ import { fileURLToPath } from "node:url";
|
|
|
23
23
|
import { execFileSync, execSync } from "node:child_process";
|
|
24
24
|
const cliArgs = process.argv.slice(3); // argv[2]='setup', flags start at [3]
|
|
25
25
|
const DRY_RUN = cliArgs.includes("--dry-run");
|
|
26
|
+
export const SERVER_NAME = "subagent-mcp";
|
|
26
27
|
// Install root: dist/setup.js -> dist/ -> <install-root>
|
|
27
28
|
const INSTALL_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
28
29
|
export function fwd(p) {
|
|
@@ -241,13 +242,36 @@ function backup(file) {
|
|
|
241
242
|
function runCmd(cmd, cmdArgs) {
|
|
242
243
|
return runCmdCapture(cmd, cmdArgs).ok;
|
|
243
244
|
}
|
|
244
|
-
|
|
245
|
-
|
|
245
|
+
/**
|
|
246
|
+
* Parse an npm cmd-shim (.cmd) for its dp0-relative node script.
|
|
247
|
+
* Matches both modern `"%dp0%\node_modules\...\cli.js"` and legacy
|
|
248
|
+
* `"%~dp0\..."` forms. Returns the absolute JS path or null.
|
|
249
|
+
*/
|
|
250
|
+
export function resolveCmdShimNodeScript(cmdPath) {
|
|
251
|
+
try {
|
|
252
|
+
const text = readFileSync(cmdPath, "utf8");
|
|
253
|
+
const m = text.match(/"%(?:~dp0|dp0%)\\([^"]+\.(?:js|cjs|mjs))"/i);
|
|
254
|
+
if (!m)
|
|
255
|
+
return null;
|
|
256
|
+
const js = join(dirname(cmdPath), m[1]);
|
|
257
|
+
return existsSync(js) ? js : null;
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// Conservative safe-charset: quote on ANYTHING else, including the cmd.exe
|
|
264
|
+
// metachars & | < > ^ ( ) % ! plus space, tab, and ".
|
|
265
|
+
const WIN_SAFE_ARG = /^[A-Za-z0-9_.,:=@+\/\\-]+$/;
|
|
266
|
+
export function quoteWinShellArg(arg) {
|
|
267
|
+
if (arg !== "" && WIN_SAFE_ARG.test(arg))
|
|
246
268
|
return arg;
|
|
247
|
-
|
|
269
|
+
// "" doubling: quote-state-safe at the cmd parse stage, a literal " at the
|
|
270
|
+
// final MSVCRT argv stage (\" would close the quote and expose metachars).
|
|
271
|
+
return `"${arg.replace(/"/g, '""')}"`;
|
|
248
272
|
}
|
|
249
|
-
function quoteWinShellExe(exe) {
|
|
250
|
-
return `"${exe.replace(/"/g, '
|
|
273
|
+
export function quoteWinShellExe(exe) {
|
|
274
|
+
return `"${exe.replace(/"/g, '""')}"`;
|
|
251
275
|
}
|
|
252
276
|
function runCmdCapture(cmd, cmdArgs) {
|
|
253
277
|
console.log(` $ ${cmd} ${cmdArgs.join(" ")}`);
|
|
@@ -258,9 +282,30 @@ function runCmdCapture(cmd, cmdArgs) {
|
|
|
258
282
|
try {
|
|
259
283
|
const exe = findOnPath(cmd) ?? cmd;
|
|
260
284
|
const isWinCmdShim = process.platform === "win32" && /\.(?:cmd|bat)$/i.test(exe);
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
285
|
+
let stdout;
|
|
286
|
+
if (!isWinCmdShim) {
|
|
287
|
+
stdout = execFileSync(exe, cmdArgs, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
// Primary: bypass cmd.exe by invoking node on the shim's JS entry —
|
|
291
|
+
// execFileSync without a shell does correct argv quoting, so cmd.exe
|
|
292
|
+
// metachar/percent expansion never applies.
|
|
293
|
+
const js = resolveCmdShimNodeScript(exe);
|
|
294
|
+
if (js) {
|
|
295
|
+
stdout = execFileSync(process.execPath, [js, ...cmdArgs], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
// Fallback for non-npm shims: hardened cmd.exe quoting. '%' is the one
|
|
299
|
+
// channel cmd cannot neutralize on a /c line — warn, don't reject.
|
|
300
|
+
if ([exe, ...cmdArgs].some((a) => a.includes("%"))) {
|
|
301
|
+
console.log(" note: an argument contains '%' — cmd.exe may expand it as an env var; if this command fails, check for name collisions.");
|
|
302
|
+
}
|
|
303
|
+
stdout = execSync([quoteWinShellExe(exe), ...cmdArgs.map(quoteWinShellArg)].join(" "), {
|
|
304
|
+
encoding: "utf8",
|
|
305
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
264
309
|
return { ok: true, stdout };
|
|
265
310
|
}
|
|
266
311
|
catch {
|
|
@@ -279,19 +324,28 @@ export function codexAddArgs(serverPath) {
|
|
|
279
324
|
export function codexRemoveArgs() {
|
|
280
325
|
return ["mcp", "remove", "subagent-mcp"];
|
|
281
326
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
327
|
+
/**
|
|
328
|
+
* True iff CLI output mentions `name` as a standalone token: the chars
|
|
329
|
+
* immediately around it must not be server-name chars [A-Za-z0-9._-].
|
|
330
|
+
* Robust to `claude mcp list` / `codex mcp list` / `mcp get` formats
|
|
331
|
+
* ("name: cmd - ✓ Connected", table rows, "Name: x" detail views), and
|
|
332
|
+
* rejects sibling names like `subagent-mcp-dev` / `my-subagent-mcp`.
|
|
333
|
+
*/
|
|
334
|
+
export function outputListsServer(stdout, name = SERVER_NAME) {
|
|
335
|
+
const esc = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
336
|
+
return new RegExp(`(^|[^A-Za-z0-9._-])${esc}($|[^A-Za-z0-9._-])`, "m").test(stdout);
|
|
288
337
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
338
|
+
const defaultExecDeps = {
|
|
339
|
+
run: runCmd,
|
|
340
|
+
capture: runCmdCapture,
|
|
341
|
+
dryRun: DRY_RUN,
|
|
342
|
+
};
|
|
343
|
+
function registeredViaCli(cli, deps = defaultExecDeps) {
|
|
344
|
+
const get = deps.capture(cli, ["mcp", "get", SERVER_NAME]);
|
|
345
|
+
if (get.ok && outputListsServer(get.stdout))
|
|
292
346
|
return true;
|
|
293
|
-
const list =
|
|
294
|
-
return list.ok && list.stdout
|
|
347
|
+
const list = deps.capture(cli, ["mcp", "list"]);
|
|
348
|
+
return list.ok && outputListsServer(list.stdout);
|
|
295
349
|
}
|
|
296
350
|
const issues = [];
|
|
297
351
|
function repairPromptFor(vendor, problem) {
|
|
@@ -328,45 +382,91 @@ function describe(status, what) {
|
|
|
328
382
|
else
|
|
329
383
|
console.log(` ${what}: pointed at a stale path — repaired.`);
|
|
330
384
|
}
|
|
385
|
+
export function vendorWireSpecs(p = serverPaths(), home = homedir()) {
|
|
386
|
+
const claudeFile = join(home, ".claude.json");
|
|
387
|
+
const codexDir = join(home, ".codex");
|
|
388
|
+
const codexFile = join(codexDir, "config.toml");
|
|
389
|
+
return {
|
|
390
|
+
claude: {
|
|
391
|
+
vendor: "claude",
|
|
392
|
+
cli: "claude",
|
|
393
|
+
configFile: claudeFile,
|
|
394
|
+
addArgs: claudeAddArgs(),
|
|
395
|
+
removeArgs: claudeRemoveArgs(),
|
|
396
|
+
read: () => readJson(claudeFile, {}),
|
|
397
|
+
reconcile: (cfg) => {
|
|
398
|
+
const r = reconcileClaudeJson(cfg, p.server);
|
|
399
|
+
return { status: r.status, changed: r.changed, out: cfg };
|
|
400
|
+
},
|
|
401
|
+
serialize: (out) => JSON.stringify(out, null, 2),
|
|
402
|
+
cliFailMsg: "MCP server file shape is correct, but 'claude mcp add' failed to register it with the CLI",
|
|
403
|
+
},
|
|
404
|
+
codex: {
|
|
405
|
+
vendor: "codex",
|
|
406
|
+
cli: "codex",
|
|
407
|
+
configFile: codexFile,
|
|
408
|
+
addArgs: codexAddArgs(p.server),
|
|
409
|
+
removeArgs: codexRemoveArgs(),
|
|
410
|
+
read: () => (existsSync(codexFile) ? readFileSync(codexFile, "utf8") : ""),
|
|
411
|
+
reconcile: (cfg) => {
|
|
412
|
+
const r = reconcileCodexToml(cfg, p.server);
|
|
413
|
+
return { status: r.status, changed: r.changed, out: r.toml };
|
|
414
|
+
},
|
|
415
|
+
serialize: (out) => out,
|
|
416
|
+
ensureDir: () => mkdirSync(codexDir, { recursive: true }),
|
|
417
|
+
cliFailMsg: "MCP server file shape is correct, but 'codex mcp add' failed to register it with the CLI",
|
|
418
|
+
},
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Single wiring driver for both vendors. Policy: CLI-first -> read-back ->
|
|
423
|
+
* reconcile -> unconditional canonical write on divergence -> fail only when
|
|
424
|
+
* neither the CLI registration nor the file fallback took.
|
|
425
|
+
*/
|
|
426
|
+
export function wireMcpServer(spec, deps = defaultExecDeps) {
|
|
427
|
+
const initial = spec.reconcile(spec.read());
|
|
428
|
+
if (initial.status === "repaired") {
|
|
429
|
+
console.log(" MCP server registration points at a stale path — re-registering.");
|
|
430
|
+
deps.run(spec.cli, spec.removeArgs);
|
|
431
|
+
}
|
|
432
|
+
let registered = initial.status === "ok" && registeredViaCli(spec.cli, deps);
|
|
433
|
+
if (!registered) {
|
|
434
|
+
deps.run(spec.cli, spec.addArgs);
|
|
435
|
+
registered = registeredViaCli(spec.cli, deps);
|
|
436
|
+
}
|
|
437
|
+
if (deps.dryRun) {
|
|
438
|
+
// Never verify/fail/write in dry-run: capture() returns empty stdout, so
|
|
439
|
+
// any verification below would fail spuriously.
|
|
440
|
+
if (initial.changed)
|
|
441
|
+
console.log(" (dry-run: not written)");
|
|
442
|
+
return { status: initial.status, registered: true, wroteFile: false, failure: null };
|
|
443
|
+
}
|
|
444
|
+
const after = spec.reconcile(spec.read()); // read-back: what is ACTUALLY on disk now
|
|
445
|
+
let wroteFile = false;
|
|
446
|
+
if (after.status !== "ok") {
|
|
447
|
+
spec.ensureDir?.();
|
|
448
|
+
backup(spec.configFile);
|
|
449
|
+
writeFileSync(spec.configFile, spec.serialize(after.out));
|
|
450
|
+
wroteFile = true;
|
|
451
|
+
console.log(registered
|
|
452
|
+
? ` NOTE: ${spec.cli} CLI registration diverged from the canonical config — rewrote ${spec.configFile} to the canonical form.`
|
|
453
|
+
: ` '${spec.cli} mcp add' failed — writing ${spec.configFile} directly.`);
|
|
454
|
+
}
|
|
455
|
+
const failure = registered || wroteFile ? null : spec.cliFailMsg;
|
|
456
|
+
return { status: initial.status, registered, wroteFile, failure };
|
|
457
|
+
}
|
|
331
458
|
function wireClaude() {
|
|
332
459
|
console.log("\n--- Claude Code CLI ---");
|
|
333
460
|
const p = serverPaths();
|
|
334
|
-
const
|
|
335
|
-
// 1) MCP server (user scope).
|
|
336
|
-
//
|
|
461
|
+
const specs = vendorWireSpecs(p);
|
|
462
|
+
// 1) MCP server (user scope). CLI-first, read-back verified, with a direct
|
|
463
|
+
// (schema-identical) ~/.claude.json write whenever the file diverges.
|
|
337
464
|
try {
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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");
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
if (status === "repaired") {
|
|
354
|
-
console.log(" MCP server registration points at a stale path — re-registering.");
|
|
355
|
-
runCmd("claude", claudeRemoveArgs());
|
|
356
|
-
}
|
|
357
|
-
const cliOk = runCmd("claude", claudeAddArgs());
|
|
358
|
-
const cliVerified = cliOk && claudeRegisteredViaCli();
|
|
359
|
-
// Read back; if the CLI failed or didn't take, write the entry directly.
|
|
360
|
-
const after = readJson(cjFile, {});
|
|
361
|
-
const verify = reconcileClaudeJson(after, p.server);
|
|
362
|
-
if (!cliVerified && verify.status !== "ok" && !DRY_RUN) {
|
|
363
|
-
if (!cliOk)
|
|
364
|
-
console.log(" 'claude mcp add' failed — writing ~/.claude.json directly.");
|
|
365
|
-
backup(cjFile);
|
|
366
|
-
writeFileSync(cjFile, JSON.stringify(after, null, 2));
|
|
367
|
-
}
|
|
368
|
-
describe(status, "MCP server (user scope)");
|
|
369
|
-
}
|
|
465
|
+
const r = wireMcpServer(specs.claude);
|
|
466
|
+
if (r.failure)
|
|
467
|
+
fail("claude", r.failure);
|
|
468
|
+
else
|
|
469
|
+
describe(r.status, "MCP server (user scope)");
|
|
370
470
|
}
|
|
371
471
|
catch (e) {
|
|
372
472
|
fail("claude", `could not register the MCP server: ${e.message}`);
|
|
@@ -392,25 +492,15 @@ function wireCodex() {
|
|
|
392
492
|
console.log("\n--- Codex CLI ---");
|
|
393
493
|
const p = serverPaths();
|
|
394
494
|
const codexDir = join(homedir(), ".codex");
|
|
495
|
+
const specs = vendorWireSpecs(p);
|
|
395
496
|
// 1) config.toml — MCP server block (created if the file is missing).
|
|
396
497
|
try {
|
|
397
|
-
const
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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 });
|
|
408
|
-
backup(cfg);
|
|
409
|
-
writeFileSync(cfg, r.toml);
|
|
410
|
-
}
|
|
411
|
-
describe(r.status, toml === "" ? "config.toml (created) MCP server block" : "config.toml MCP server block");
|
|
412
|
-
if (r.changed && DRY_RUN)
|
|
413
|
-
console.log(" (dry-run: not written)");
|
|
498
|
+
const existed = existsSync(specs.codex.configFile);
|
|
499
|
+
const r = wireMcpServer(specs.codex);
|
|
500
|
+
if (r.failure)
|
|
501
|
+
fail("codex", r.failure);
|
|
502
|
+
else
|
|
503
|
+
describe(r.status, existed ? "config.toml MCP server block" : "config.toml (created) MCP server block");
|
|
414
504
|
}
|
|
415
505
|
catch (e) {
|
|
416
506
|
fail("codex", `could not write config.toml: ${e.message}`);
|
|
@@ -438,6 +528,25 @@ function wireCodex() {
|
|
|
438
528
|
fail("codex", `could not write hooks.json: ${e.message}`);
|
|
439
529
|
}
|
|
440
530
|
}
|
|
531
|
+
/** Detail string for a CLI registration check; "CLI repair failed" only when a
|
|
532
|
+
* repair was actually attempted. */
|
|
533
|
+
export function registrationDetail(registered, attemptedRepair) {
|
|
534
|
+
if (registered)
|
|
535
|
+
return attemptedRepair ? "repaired" : "registered";
|
|
536
|
+
return attemptedRepair
|
|
537
|
+
? "not registered; CLI repair failed"
|
|
538
|
+
: "not registered — run: subagent-mcp doctor";
|
|
539
|
+
}
|
|
540
|
+
function checkCliRegistration(cli, addArgs, repair, deps = defaultExecDeps) {
|
|
541
|
+
let registered = registeredViaCli(cli, deps);
|
|
542
|
+
let attemptedRepair = false;
|
|
543
|
+
if (!registered && repair) {
|
|
544
|
+
deps.run(cli, addArgs);
|
|
545
|
+
attemptedRepair = true;
|
|
546
|
+
registered = registeredViaCli(cli, deps);
|
|
547
|
+
}
|
|
548
|
+
return { registered, attemptedRepair };
|
|
549
|
+
}
|
|
441
550
|
export function verifyWiring(root = INSTALL_ROOT, repair = false) {
|
|
442
551
|
const p = serverPaths(root);
|
|
443
552
|
const results = [];
|
|
@@ -452,18 +561,12 @@ export function verifyWiring(root = INSTALL_ROOT, repair = false) {
|
|
|
452
561
|
const hasClaudeConfig = existsSync(join(home, ".claude.json"));
|
|
453
562
|
if (hasClaude) {
|
|
454
563
|
const sj = readJson(join(home, ".claude", "settings.json"), {});
|
|
455
|
-
|
|
456
|
-
|
|
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);
|
|
564
|
+
const { registered, attemptedRepair } = checkCliRegistration("claude", claudeAddArgs(), repair);
|
|
565
|
+
const hk = reconcileClaudeSettings(sj, p.claudeHook);
|
|
463
566
|
results.push({
|
|
464
567
|
label: "claude: MCP server (user scope)",
|
|
465
568
|
ok: registered,
|
|
466
|
-
detail: registered
|
|
569
|
+
detail: registrationDetail(registered, attemptedRepair),
|
|
467
570
|
});
|
|
468
571
|
results.push({
|
|
469
572
|
label: "claude: UserPromptSubmit hook",
|
|
@@ -474,12 +577,12 @@ export function verifyWiring(root = INSTALL_ROOT, repair = false) {
|
|
|
474
577
|
else if (hasClaudeConfig) {
|
|
475
578
|
const cj = readJson(join(home, ".claude.json"), {});
|
|
476
579
|
const sj = readJson(join(home, ".claude", "settings.json"), {});
|
|
477
|
-
const srv = reconcileClaudeJson(
|
|
478
|
-
const hk = reconcileClaudeSettings(
|
|
580
|
+
const srv = reconcileClaudeJson(cj, p.server);
|
|
581
|
+
const hk = reconcileClaudeSettings(sj, p.claudeHook);
|
|
479
582
|
results.push({
|
|
480
583
|
label: "claude: MCP server (user scope)",
|
|
481
584
|
ok: srv.status === "ok",
|
|
482
|
-
detail: srv.status === "ok" ? "registered (file fallback)" : "
|
|
585
|
+
detail: srv.status === "ok" ? "registered (file fallback)" : "config stale — run: subagent-mcp setup",
|
|
483
586
|
});
|
|
484
587
|
results.push({
|
|
485
588
|
label: "claude: UserPromptSubmit hook",
|
|
@@ -496,22 +599,20 @@ export function verifyWiring(root = INSTALL_ROOT, repair = false) {
|
|
|
496
599
|
const hj = readJson(join(home, ".codex", "hooks.json"), { hooks: {} });
|
|
497
600
|
const hkR = reconcileCodexHooks(hj, `node "${p.codexHook}"`);
|
|
498
601
|
let registered = false;
|
|
499
|
-
let
|
|
602
|
+
let detail;
|
|
500
603
|
if (hasCodexCli) {
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
repaired = true;
|
|
505
|
-
registered = codexRegisteredViaCli();
|
|
506
|
-
}
|
|
604
|
+
const r = checkCliRegistration("codex", codexAddArgs(p.server), repair);
|
|
605
|
+
registered = r.registered;
|
|
606
|
+
detail = registrationDetail(r.registered, r.attemptedRepair);
|
|
507
607
|
}
|
|
508
608
|
else {
|
|
509
609
|
registered = tomlR.status === "ok";
|
|
610
|
+
detail = registered ? "registered" : "config stale — run: subagent-mcp setup";
|
|
510
611
|
}
|
|
511
612
|
results.push({
|
|
512
613
|
label: "codex: config.toml MCP server block",
|
|
513
614
|
ok: registered,
|
|
514
|
-
detail
|
|
615
|
+
detail,
|
|
515
616
|
});
|
|
516
617
|
const allOk = Object.values(hkR.statuses).every((s) => s === "ok");
|
|
517
618
|
results.push({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heretyc/subagent-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
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
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"postinstall": "node scripts/postinstall.mjs",
|
|
22
22
|
"prepare": "npm run build",
|
|
23
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"
|
|
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 test/setup-quoting.test.mjs && node test/setup-wire.test.mjs && node test/setup-cli-integration.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
25
|
},
|
|
26
26
|
"author": "Lexi Blackburn",
|
|
27
27
|
"license": "Apache-2.0",
|