cool-workflow 0.1.85 → 0.1.86
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/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +47 -333
- package/apps/architecture-review/app.json +1 -1
- package/apps/architecture-review-fast/app.json +1 -1
- package/apps/architecture-review-fast/workflow.js +2 -0
- package/apps/end-to-end-golden-path/app.json +1 -1
- package/apps/pr-review-fix-ci/app.json +1 -1
- package/apps/release-cut/app.json +1 -1
- package/apps/research-synthesis/app.json +1 -1
- package/dist/capability-core.js +131 -3
- package/dist/capability-registry.js +84 -50
- package/dist/cli/command-surface.js +3 -0
- package/dist/doctor.js +37 -1
- package/dist/mcp/tool-call.js +428 -0
- package/dist/mcp/tool-definitions.js +1027 -0
- package/dist/mcp-surface.js +5 -1442
- package/dist/onramp.js +421 -0
- package/dist/orchestrator.js +20 -15
- package/dist/version.js +1 -1
- package/dist/workbench-host.js +1 -8
- package/docs/agent-delegation-drive.7.md +21 -2
- package/docs/cli-mcp-parity.7.md +29 -4
- package/docs/contract-migration-tooling.7.md +2 -0
- package/docs/control-plane-scheduling.7.md +2 -0
- package/docs/dogfood-one-real-repo.7.md +9 -1
- package/docs/durable-state-and-locking.7.md +2 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
- package/docs/execution-backends.7.md +2 -0
- package/docs/getting-started.md +40 -2
- package/docs/index.md +51 -38
- package/docs/multi-agent-cli-mcp-surface.7.md +2 -0
- package/docs/multi-agent-eval-replay-harness.7.md +2 -0
- package/docs/multi-agent-operator-ux.7.md +2 -0
- package/docs/node-snapshot-diff-replay.7.md +2 -0
- package/docs/observability-cost-accounting.7.md +2 -0
- package/docs/project-index.md +18 -7
- package/docs/real-execution-backends.7.md +2 -0
- package/docs/release-and-migration.7.md +3 -1
- package/docs/release-history.md +342 -0
- package/docs/release-tooling.7.md +16 -0
- package/docs/run-registry-control-plane.7.md +2 -0
- package/docs/run-retention-reclamation.7.md +2 -0
- package/docs/state-explosion-management.7.md +2 -0
- package/docs/team-collaboration.7.md +2 -0
- package/docs/web-desktop-workbench.7.md +2 -0
- package/manifest/plugin.manifest.json +1 -1
- package/manifest/source-context-profiles.json +1 -1
- package/package.json +2 -1
- package/scripts/architecture-review-fast.js +44 -2
- package/scripts/canonical-apps.js +4 -4
- package/scripts/dogfood-release.js +55 -133
- package/scripts/golden-path.js +4 -4
- package/scripts/onramp-check.js +47 -0
- package/scripts/parity-check.js +740 -2
- package/scripts/release-check.js +2 -0
- package/scripts/source-context.js +31 -5
- package/scripts/version-sync-check.js +4 -2
package/dist/capability-core.js
CHANGED
|
@@ -179,8 +179,8 @@ function compactOperatorStatus(status) {
|
|
|
179
179
|
// functions so `cw <cmd> --json` is byte-identical to `cw_<tool>`. Each accepts
|
|
180
180
|
// the raw CLI options OR the raw MCP arguments and normalizes them identically,
|
|
181
181
|
// then calls the single RunRegistry method. The registry is constructed from the
|
|
182
|
-
// same resolved cwd on both surfaces (CLI: --cwd|process.cwd(); MCP
|
|
183
|
-
//
|
|
182
|
+
// same resolved cwd on both surfaces (CLI: --cwd|process.cwd(); MCP passes a
|
|
183
|
+
// resolved cwd and scopes the runner), so repo/home roots line up.
|
|
184
184
|
function runRegistryFor(args, planner) {
|
|
185
185
|
return new run_registry_1.RunRegistry(String(args.cwd || process.cwd()), planner);
|
|
186
186
|
}
|
|
@@ -545,6 +545,8 @@ exports.QUICKSTART_DEFAULT_APP = "architecture-review";
|
|
|
545
545
|
function quickstart(runner, args) {
|
|
546
546
|
const appId = String(args.appId || args.app || args.workflowId || exports.QUICKSTART_DEFAULT_APP);
|
|
547
547
|
const agentConfigured = Boolean((0, agent_config_1.resolveAgentConfig)(args).command || (0, agent_config_1.resolveAgentConfig)(args).endpoint);
|
|
548
|
+
if (isTrue(args.check))
|
|
549
|
+
return quickstartCheck(runner, appId, args, agentConfigured);
|
|
548
550
|
// `--resume`: a discoverability flag over the existing continuation. With no
|
|
549
551
|
// `--run`, advance exactly ONE step (reuse the `--once` path) and print a
|
|
550
552
|
// copy-pasteable continue line; with `--run <id>`, continue that run to
|
|
@@ -623,7 +625,7 @@ function quickstart(runner, args) {
|
|
|
623
625
|
}
|
|
624
626
|
else if (result.status === "in-progress") {
|
|
625
627
|
hint = resume
|
|
626
|
-
? `one step advanced — continue: cw quickstart ${appId} --run ${result.runId} --resume`
|
|
628
|
+
? `one step advanced — continue: cw quickstart ${appId} --run ${result.runId} --resume${wantsBundle ? " --bundle" : ""}`
|
|
627
629
|
: `one step advanced (--once) — continue: cw quickstart ${appId} --run ${result.runId} --once`;
|
|
628
630
|
}
|
|
629
631
|
// --bundle on a run that didn't complete is a NO-OP, not silence: tell the operator
|
|
@@ -655,6 +657,132 @@ function quickstart(runner, args) {
|
|
|
655
657
|
...(bundle ? { bundle } : {})
|
|
656
658
|
};
|
|
657
659
|
}
|
|
660
|
+
function quickstartCheck(runner, appId, args, agentConfigured) {
|
|
661
|
+
const base = invocationCwd(args);
|
|
662
|
+
const repoArg = optionalString(args.repo) || base;
|
|
663
|
+
const repo = node_path_1.default.resolve(base, repoArg);
|
|
664
|
+
const checks = [];
|
|
665
|
+
try {
|
|
666
|
+
runner.showApp(appId);
|
|
667
|
+
checks.push({ name: "app", status: "ok", detail: `Workflow app ${appId} is available.` });
|
|
668
|
+
}
|
|
669
|
+
catch (error) {
|
|
670
|
+
checks.push({
|
|
671
|
+
name: "app",
|
|
672
|
+
status: "blocked",
|
|
673
|
+
detail: `Workflow app ${appId} is not available.`,
|
|
674
|
+
fix: "Run `cw app list` and choose one of the listed app ids."
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
let repoReadable = false;
|
|
678
|
+
let repoStateWritable = false;
|
|
679
|
+
try {
|
|
680
|
+
const stat = node_fs_1.default.statSync(repo);
|
|
681
|
+
repoReadable = stat.isDirectory();
|
|
682
|
+
if (!repoReadable)
|
|
683
|
+
throw new Error("not a directory");
|
|
684
|
+
node_fs_1.default.accessSync(repo, node_fs_1.default.constants.R_OK);
|
|
685
|
+
checks.push({ name: "repo", status: "ok", detail: `Repository path is readable (${repo}).` });
|
|
686
|
+
}
|
|
687
|
+
catch (error) {
|
|
688
|
+
checks.push({
|
|
689
|
+
name: "repo",
|
|
690
|
+
status: "blocked",
|
|
691
|
+
detail: `Repository path is not readable (${repo}).`,
|
|
692
|
+
fix: "Pass --repo PATH for a readable repository directory."
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
try {
|
|
696
|
+
const cwDir = node_path_1.default.join(repo, ".cw");
|
|
697
|
+
node_fs_1.default.accessSync(node_fs_1.default.existsSync(cwDir) ? cwDir : repo, node_fs_1.default.constants.W_OK);
|
|
698
|
+
repoStateWritable = repoReadable;
|
|
699
|
+
checks.push({ name: "repo-state", status: "ok", detail: "Run state location is writable." });
|
|
700
|
+
}
|
|
701
|
+
catch (error) {
|
|
702
|
+
checks.push({
|
|
703
|
+
name: "repo-state",
|
|
704
|
+
status: "blocked",
|
|
705
|
+
detail: "Run state location is not writable.",
|
|
706
|
+
fix: "Use a writable repo, fix directory permissions, or pass --repo to a writable checkout."
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
if (optionalString(args.question)) {
|
|
710
|
+
checks.push({ name: "question", status: "ok", detail: "Question is set." });
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
checks.push({
|
|
714
|
+
name: "question",
|
|
715
|
+
status: "blocked",
|
|
716
|
+
detail: "Question is missing.",
|
|
717
|
+
fix: "Pass --question TEXT."
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
if (agentConfigured) {
|
|
721
|
+
checks.push({ name: "agent", status: "ok", detail: "Agent backend is configured." });
|
|
722
|
+
}
|
|
723
|
+
else {
|
|
724
|
+
checks.push({
|
|
725
|
+
name: "agent",
|
|
726
|
+
status: "blocked",
|
|
727
|
+
detail: "No agent backend is configured.",
|
|
728
|
+
fix: "Pass --agent-command \"claude -p\", set $CW_AGENT_COMMAND, or use --agent-command builtin:claude."
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
if (flag(args.bundle) === true) {
|
|
732
|
+
const trustKey = optionalString(args["with-trust-key"] || args.withTrustKey || args.trustKey || args.pubkey) || process.env.CW_AGENT_ATTEST_PUBKEY;
|
|
733
|
+
if (trustKey) {
|
|
734
|
+
checks.push({ name: "bundle-trust-key", status: "ok", detail: "Bundle trust public key is configured." });
|
|
735
|
+
}
|
|
736
|
+
else if (Boolean(args["strict-signatures"] || args.strictSignatures || args.strictSigs)) {
|
|
737
|
+
checks.push({
|
|
738
|
+
name: "bundle-trust-key",
|
|
739
|
+
status: "blocked",
|
|
740
|
+
detail: "Strict signature verification needs a public trust key.",
|
|
741
|
+
fix: "Pass --with-trust-key PATH or set $CW_AGENT_ATTEST_PUBKEY."
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
checks.push({
|
|
746
|
+
name: "bundle-trust-key",
|
|
747
|
+
status: "warn",
|
|
748
|
+
detail: "No public trust key is configured; unsigned or unkeyed bundles may verify with reduced signature proof.",
|
|
749
|
+
fix: "Pass --with-trust-key PATH to embed the public key."
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
const ok = checks.every((check) => check.status !== "blocked") && repoStateWritable;
|
|
754
|
+
return {
|
|
755
|
+
schemaVersion: 1,
|
|
756
|
+
mode: "check",
|
|
757
|
+
ok,
|
|
758
|
+
appId,
|
|
759
|
+
repo,
|
|
760
|
+
checks,
|
|
761
|
+
nextCommand: quickstartNextCommand(appId, repo, args)
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
function quickstartNextCommand(appId, repo, args) {
|
|
765
|
+
const parts = ["cw", "quickstart", shellWord(appId), "--repo", shellWord(repo)];
|
|
766
|
+
const question = optionalString(args.question);
|
|
767
|
+
if (question)
|
|
768
|
+
parts.push("--question", shellWord(question));
|
|
769
|
+
const command = optionalString(args.agentCommand || args["agent-command"]);
|
|
770
|
+
if (command)
|
|
771
|
+
parts.push("--agent-command", shellWord(command));
|
|
772
|
+
if (flag(args.bundle) === true)
|
|
773
|
+
parts.push("--bundle");
|
|
774
|
+
const trustKey = optionalString(args["with-trust-key"] || args.withTrustKey || args.trustKey);
|
|
775
|
+
if (trustKey)
|
|
776
|
+
parts.push("--with-trust-key", shellWord(trustKey));
|
|
777
|
+
if (Boolean(args["strict-signatures"] || args.strictSignatures || args.strictSigs))
|
|
778
|
+
parts.push("--strict-signatures");
|
|
779
|
+
return parts.join(" ");
|
|
780
|
+
}
|
|
781
|
+
function shellWord(value) {
|
|
782
|
+
if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value))
|
|
783
|
+
return value;
|
|
784
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
785
|
+
}
|
|
658
786
|
/** Read-only, deterministic projection of the effective agent config (secret-stripped). */
|
|
659
787
|
function backendAgentConfigShow(args) {
|
|
660
788
|
return (0, agent_config_1.agentConfigShow)(args);
|
|
@@ -31,11 +31,13 @@ exports.mcpCapabilityForId = mcpCapabilityForId;
|
|
|
31
31
|
exports.mcpRequiredArgsForTool = mcpRequiredArgsForTool;
|
|
32
32
|
exports.mcpToolDefinition = mcpToolDefinition;
|
|
33
33
|
exports.declaredCliTokens = declaredCliTokens;
|
|
34
|
+
exports.declaredCliHelpTokens = declaredCliHelpTokens;
|
|
34
35
|
exports.requiresReason = requiresReason;
|
|
35
36
|
exports.isPayloadProbeOptOut = isPayloadProbeOptOut;
|
|
36
37
|
exports.payloadIdenticalCapabilities = payloadIdenticalCapabilities;
|
|
37
38
|
exports.payloadProbeTargets = payloadProbeTargets;
|
|
38
39
|
exports.deferredPayloadProbeCapabilities = deferredPayloadProbeCapabilities;
|
|
40
|
+
exports.buildPayloadProbePlan = buildPayloadProbePlan;
|
|
39
41
|
exports.payloadProbePlan = payloadProbePlan;
|
|
40
42
|
exports.buildParityReport = buildParityReport;
|
|
41
43
|
// ---------------------------------------------------------------------------
|
|
@@ -351,7 +353,7 @@ const BUILTIN_CAPABILITIES = [
|
|
|
351
353
|
{ capability: "run.drive.step", summary: "Drive a run by delegating each worker to the agent backend (plan->dispatch->fulfill->accept->commit; --once for one step).", entry: "runDrive", surface: "both", cli: { path: ["run", "drive"], caseTokens: ["run", "drive"], jsonMode: "default" }, mcp: { tool: "cw_run_drive_step" }, payloadIdentical: false, reason: "Mutating: advances the run by spawning the external agent per worker and recording attested output — not a read probe. CLI (--drive/--step) and MCP route through the same drive() core." },
|
|
352
354
|
{
|
|
353
355
|
capability: "quickstart",
|
|
354
|
-
summary: "ONE-COMMAND quickstart: plan(app, default architecture-review) -> run --drive -> report in a single invocation (--preview for a read-only dry run; --bundle [--with-trust-key K] seals a completed run into a self-verified portable bundle).",
|
|
356
|
+
summary: "ONE-COMMAND quickstart: --check preflights without writes; otherwise plan(app, default architecture-review) -> run --drive -> report in a single invocation (--preview for a read-only dry run; --bundle [--with-trust-key K] seals a completed run into a self-verified portable bundle).",
|
|
355
357
|
entry: "quickstart",
|
|
356
358
|
surface: "cli-only",
|
|
357
359
|
cli: { path: ["quickstart"], caseTokens: ["quickstart", "audit-run"], jsonMode: "default" },
|
|
@@ -458,34 +460,63 @@ const RUN_PAYLOAD_PROBE_CAPABILITIES = [
|
|
|
458
460
|
"gc.plan",
|
|
459
461
|
"gc.verify"
|
|
460
462
|
];
|
|
463
|
+
const SCENARIO_PAYLOAD_PROBE_CAPABILITIES = [
|
|
464
|
+
"plan",
|
|
465
|
+
"app.show",
|
|
466
|
+
"app.validate",
|
|
467
|
+
"app.package",
|
|
468
|
+
"topology.show",
|
|
469
|
+
"topology.validate",
|
|
470
|
+
"topology.apply",
|
|
471
|
+
"topology.summary",
|
|
472
|
+
"topology.graph",
|
|
473
|
+
"summary.refresh",
|
|
474
|
+
"summary.show",
|
|
475
|
+
"sandbox.show",
|
|
476
|
+
"sandbox.validate",
|
|
477
|
+
"sandbox.choose",
|
|
478
|
+
"sandbox.resolve",
|
|
479
|
+
"approve",
|
|
480
|
+
"reject",
|
|
481
|
+
"comment.add",
|
|
482
|
+
"handoff",
|
|
483
|
+
"review.policy",
|
|
484
|
+
"worker.list",
|
|
485
|
+
"worker.show",
|
|
486
|
+
"worker.manifest",
|
|
487
|
+
"worker.output",
|
|
488
|
+
"worker.fail",
|
|
489
|
+
"worker.validate",
|
|
490
|
+
"candidate.list",
|
|
491
|
+
"candidate.show",
|
|
492
|
+
"candidate.register",
|
|
493
|
+
"candidate.score",
|
|
494
|
+
"candidate.rank",
|
|
495
|
+
"candidate.select",
|
|
496
|
+
"candidate.reject",
|
|
497
|
+
"feedback.list",
|
|
498
|
+
"feedback.show",
|
|
499
|
+
"feedback.collect",
|
|
500
|
+
"feedback.task",
|
|
501
|
+
"feedback.resolve",
|
|
502
|
+
"node.show",
|
|
503
|
+
"node.snapshot",
|
|
504
|
+
"node.diff",
|
|
505
|
+
"node.replay",
|
|
506
|
+
"node.replay.verify"
|
|
507
|
+
];
|
|
461
508
|
const PAYLOAD_PROBE_DEFERRED_GROUPS = [
|
|
462
509
|
{
|
|
463
510
|
reason: "Not safe for the deterministic bootstrap parity probe yet: this capability needs extra target ids/files, mutates durable state, depends on external state, or needs a dedicated fixture beyond cwd/runId.",
|
|
464
511
|
capabilities: [
|
|
465
512
|
"init",
|
|
466
|
-
"plan",
|
|
467
513
|
"dispatch",
|
|
468
514
|
"result",
|
|
469
|
-
"app.show",
|
|
470
|
-
"app.validate",
|
|
471
515
|
"app.init",
|
|
472
|
-
"app.package",
|
|
473
516
|
"app.run",
|
|
474
|
-
"node.show",
|
|
475
|
-
"node.snapshot",
|
|
476
|
-
"node.diff",
|
|
477
|
-
"node.replay",
|
|
478
|
-
"node.replay.verify",
|
|
479
517
|
"migration.list",
|
|
480
518
|
"migration.check",
|
|
481
519
|
"migration.prove",
|
|
482
|
-
"topology.show",
|
|
483
|
-
"topology.validate",
|
|
484
|
-
"topology.apply",
|
|
485
|
-
"topology.summary",
|
|
486
|
-
"topology.graph",
|
|
487
|
-
"summary.refresh",
|
|
488
|
-
"summary.show",
|
|
489
520
|
"multi-agent.run",
|
|
490
521
|
"multi-agent.status",
|
|
491
522
|
"multi-agent.step",
|
|
@@ -542,30 +573,8 @@ const PAYLOAD_PROBE_DEFERRED_GROUPS = [
|
|
|
542
573
|
"audit.judge",
|
|
543
574
|
"audit.attest",
|
|
544
575
|
"audit.decision",
|
|
545
|
-
"sandbox.show",
|
|
546
|
-
"sandbox.validate",
|
|
547
|
-
"sandbox.choose",
|
|
548
|
-
"sandbox.resolve",
|
|
549
576
|
"backend.show",
|
|
550
577
|
"backend.probe",
|
|
551
|
-
"worker.list",
|
|
552
|
-
"worker.show",
|
|
553
|
-
"worker.manifest",
|
|
554
|
-
"worker.output",
|
|
555
|
-
"worker.fail",
|
|
556
|
-
"worker.validate",
|
|
557
|
-
"candidate.list",
|
|
558
|
-
"candidate.show",
|
|
559
|
-
"candidate.register",
|
|
560
|
-
"candidate.score",
|
|
561
|
-
"candidate.rank",
|
|
562
|
-
"candidate.select",
|
|
563
|
-
"candidate.reject",
|
|
564
|
-
"feedback.list",
|
|
565
|
-
"feedback.show",
|
|
566
|
-
"feedback.collect",
|
|
567
|
-
"feedback.task",
|
|
568
|
-
"feedback.resolve",
|
|
569
578
|
"schedule.create",
|
|
570
579
|
"schedule.list",
|
|
571
580
|
"schedule.delete",
|
|
@@ -607,12 +616,7 @@ const PAYLOAD_PROBE_DEFERRED_GROUPS = [
|
|
|
607
616
|
"sched.policy.show",
|
|
608
617
|
"sched.policy.set",
|
|
609
618
|
"telemetry.verify",
|
|
610
|
-
"history"
|
|
611
|
-
"approve",
|
|
612
|
-
"reject",
|
|
613
|
-
"comment.add",
|
|
614
|
-
"handoff",
|
|
615
|
-
"review.policy"
|
|
619
|
+
"history"
|
|
616
620
|
]
|
|
617
621
|
}
|
|
618
622
|
];
|
|
@@ -665,6 +669,24 @@ function declaredCliTokens() {
|
|
|
665
669
|
}
|
|
666
670
|
return [...tokens].sort();
|
|
667
671
|
}
|
|
672
|
+
/** The top-level CLI commands that should be visible in `cw help`. Subcommands
|
|
673
|
+
* are intentionally collapsed to their first token; aliases such as audit-run
|
|
674
|
+
* stay visible. */
|
|
675
|
+
function declaredCliHelpTokens() {
|
|
676
|
+
const tokens = new Set();
|
|
677
|
+
for (const cap of exports.CAPABILITY_REGISTRY) {
|
|
678
|
+
if (!cap.cli)
|
|
679
|
+
continue;
|
|
680
|
+
const subcommandTokens = new Set(cap.cli.path.slice(1));
|
|
681
|
+
tokens.add(cap.cli.path[0]);
|
|
682
|
+
for (const token of cap.cli.caseTokens || []) {
|
|
683
|
+
if (!subcommandTokens.has(token))
|
|
684
|
+
tokens.add(token);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
tokens.delete("help");
|
|
688
|
+
return [...tokens].sort();
|
|
689
|
+
}
|
|
668
690
|
/** Whether a descriptor MUST carry a reason (surface-specific or divergent). */
|
|
669
691
|
function requiresReason(cap) {
|
|
670
692
|
if (cap.surface !== "both")
|
|
@@ -699,27 +721,31 @@ function payloadIdenticalCapabilities() {
|
|
|
699
721
|
function payloadProbeTargets() {
|
|
700
722
|
return [
|
|
701
723
|
...GLOBAL_PAYLOAD_PROBE_CAPABILITIES.map((capability) => ({ capability, kind: "global" })),
|
|
702
|
-
...RUN_PAYLOAD_PROBE_CAPABILITIES.map((capability) => ({ capability, kind: "run" }))
|
|
724
|
+
...RUN_PAYLOAD_PROBE_CAPABILITIES.map((capability) => ({ capability, kind: "run" })),
|
|
725
|
+
...SCENARIO_PAYLOAD_PROBE_CAPABILITIES.map((capability) => ({ capability, kind: "scenario" }))
|
|
703
726
|
];
|
|
704
727
|
}
|
|
705
728
|
function deferredPayloadProbeCapabilities() {
|
|
706
729
|
return PAYLOAD_PROBE_DEFERRED_GROUPS.flatMap((group) => group.capabilities.map((capability) => ({ capability, reason: group.reason })));
|
|
707
730
|
}
|
|
708
|
-
function
|
|
731
|
+
function buildPayloadProbePlan(targets, deferred) {
|
|
709
732
|
const candidateIds = new Set(payloadIdenticalCapabilities().map((cap) => cap.capability));
|
|
710
733
|
const counts = new Map();
|
|
711
|
-
const classified = [...
|
|
734
|
+
const classified = [...targets.map((entry) => entry.capability), ...deferred.map((entry) => entry.capability)];
|
|
712
735
|
for (const capability of classified)
|
|
713
736
|
counts.set(capability, (counts.get(capability) || 0) + 1);
|
|
714
737
|
const classifiedIds = new Set(classified);
|
|
715
738
|
return {
|
|
716
|
-
targets
|
|
717
|
-
deferred
|
|
739
|
+
targets,
|
|
740
|
+
deferred,
|
|
718
741
|
unclassified: [...candidateIds].filter((capability) => !classifiedIds.has(capability)).sort(),
|
|
719
742
|
duplicateClassifications: [...counts.entries()].filter(([, count]) => count > 1).map(([capability]) => capability).sort(),
|
|
720
743
|
invalidClassifications: [...classifiedIds].filter((capability) => !candidateIds.has(capability)).sort()
|
|
721
744
|
};
|
|
722
745
|
}
|
|
746
|
+
function payloadProbePlan() {
|
|
747
|
+
return buildPayloadProbePlan(payloadProbeTargets(), deferredPayloadProbeCapabilities());
|
|
748
|
+
}
|
|
723
749
|
function lintRegistry() {
|
|
724
750
|
const issues = [];
|
|
725
751
|
const seenCaps = new Set();
|
|
@@ -755,10 +781,14 @@ function buildParityReport(input) {
|
|
|
755
781
|
const actualTools = new Set(input.mcpTools);
|
|
756
782
|
const declaredTokens = new Set(declaredCliTokens());
|
|
757
783
|
const actualTokens = new Set(input.cliTokens);
|
|
784
|
+
const declaredHelpTokens = new Set(declaredCliHelpTokens());
|
|
785
|
+
const actualHelpTokens = new Set(input.helpTokens || []);
|
|
758
786
|
const missingMcpTools = [...declaredTools].filter((tool) => !actualTools.has(tool)).sort();
|
|
759
787
|
const undeclaredMcpTools = [...actualTools].filter((tool) => !declaredTools.has(tool)).sort();
|
|
760
788
|
const missingCliTokens = [...declaredTokens].filter((token) => !actualTokens.has(token)).sort();
|
|
761
789
|
const undeclaredCliTokens = [...actualTokens].filter((token) => !declaredTokens.has(token)).sort();
|
|
790
|
+
const helpMissingCliTokens = input.helpTokens ? [...declaredHelpTokens].filter((token) => !actualHelpTokens.has(token)).sort() : [];
|
|
791
|
+
const helpUndeclaredCliTokens = input.helpTokens ? [...actualHelpTokens].filter((token) => !declaredHelpTokens.has(token)).sort() : [];
|
|
762
792
|
const reasonlessExceptions = exports.CAPABILITY_REGISTRY.filter((cap) => requiresReason(cap) && !(cap.reason && cap.reason.trim()))
|
|
763
793
|
.map((cap) => cap.capability)
|
|
764
794
|
.sort();
|
|
@@ -768,6 +798,8 @@ function buildParityReport(input) {
|
|
|
768
798
|
undeclaredMcpTools.length === 0 &&
|
|
769
799
|
missingCliTokens.length === 0 &&
|
|
770
800
|
undeclaredCliTokens.length === 0 &&
|
|
801
|
+
helpMissingCliTokens.length === 0 &&
|
|
802
|
+
helpUndeclaredCliTokens.length === 0 &&
|
|
771
803
|
reasonlessExceptions.length === 0 &&
|
|
772
804
|
payloadPlan.unclassified.length === 0 &&
|
|
773
805
|
payloadPlan.duplicateClassifications.length === 0 &&
|
|
@@ -780,6 +812,8 @@ function buildParityReport(input) {
|
|
|
780
812
|
undeclaredMcpTools,
|
|
781
813
|
missingCliTokens,
|
|
782
814
|
undeclaredCliTokens,
|
|
815
|
+
helpMissingCliTokens,
|
|
816
|
+
helpUndeclaredCliTokens,
|
|
783
817
|
reasonlessExceptions,
|
|
784
818
|
payloadProbeUnclassified: payloadPlan.unclassified,
|
|
785
819
|
payloadProbeDuplicateClassifications: payloadPlan.duplicateClassifications,
|
|
@@ -93,6 +93,9 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
93
93
|
const runId = optionalArg(args.options.run) || optionalArg(args.options.runId);
|
|
94
94
|
const qs = (0, capability_core_1.quickstart)(runner, { ...args.options, ...(appId ? { appId } : {}), ...(runId ? { runId } : {}) });
|
|
95
95
|
printJson(qs);
|
|
96
|
+
if (qs.mode === "check" && qs.ok === false) {
|
|
97
|
+
process.exitCode = 1;
|
|
98
|
+
}
|
|
96
99
|
// Fail closed: if --bundle produced an artifact that does not self-verify, exit
|
|
97
100
|
// non-zero so `cw quickstart ... --bundle && send-to-client` cannot ship a report
|
|
98
101
|
// whose bundle a client could not verify. Mirrors `report bundle`.
|
package/dist/doctor.js
CHANGED
|
@@ -25,6 +25,7 @@ const node_os_1 = __importDefault(require("node:os"));
|
|
|
25
25
|
const node_path_1 = __importDefault(require("node:path"));
|
|
26
26
|
const node_child_process_1 = require("node:child_process");
|
|
27
27
|
const agent_config_1 = require("./agent-config");
|
|
28
|
+
const onramp_1 = require("./onramp");
|
|
28
29
|
/** Resolve a bare binary name against $PATH (or accept an explicit path). Returns
|
|
29
30
|
* the resolved path, or undefined when not found. No spawning. */
|
|
30
31
|
function whichBinary(bin, env) {
|
|
@@ -136,7 +137,15 @@ function runDoctor(args = {}, env = process.env, cwd = process.cwd()) {
|
|
|
136
137
|
? "ready — all checks passed"
|
|
137
138
|
: `ready, with ${warns} warning${warns === 1 ? "" : "s"}`
|
|
138
139
|
: `${fails} blocking problem${fails === 1 ? "" : "s"} found`;
|
|
139
|
-
return {
|
|
140
|
+
return {
|
|
141
|
+
schemaVersion: 1,
|
|
142
|
+
ok,
|
|
143
|
+
checks,
|
|
144
|
+
summary,
|
|
145
|
+
...((0, onramp_1.optionEnabled)(args.onramp)
|
|
146
|
+
? { onramp: (0, onramp_1.buildDoctorOnramp)({ cwd, env, changedFrom: typeof args["changed-from"] === "string" ? args["changed-from"] : undefined }) }
|
|
147
|
+
: {})
|
|
148
|
+
};
|
|
140
149
|
}
|
|
141
150
|
/** Human rendering (TTY/default). `--json` callers use the report object directly. */
|
|
142
151
|
function formatDoctorReport(report) {
|
|
@@ -149,5 +158,32 @@ function formatDoctorReport(report) {
|
|
|
149
158
|
}
|
|
150
159
|
lines.push("");
|
|
151
160
|
lines.push(`${report.ok ? "✓" : "✗"} ${report.summary}`);
|
|
161
|
+
if (report.onramp) {
|
|
162
|
+
lines.push("");
|
|
163
|
+
lines.push("Onramp");
|
|
164
|
+
lines.push(` ${report.onramp.summary}`);
|
|
165
|
+
if (report.onramp.recommendedChecks) {
|
|
166
|
+
lines.push("");
|
|
167
|
+
lines.push(" Recommended Checks");
|
|
168
|
+
for (const command of report.onramp.recommendedChecks.commands)
|
|
169
|
+
lines.push(` - ${command}`);
|
|
170
|
+
}
|
|
171
|
+
if (report.onramp.contract && !report.onramp.contract.ok) {
|
|
172
|
+
lines.push("");
|
|
173
|
+
lines.push(" Contract Issues");
|
|
174
|
+
for (const issue of report.onramp.contract.issues) {
|
|
175
|
+
lines.push(` - ${issue.code}: ${issue.detail}`);
|
|
176
|
+
lines.push(` fix: ${issue.fix}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
for (const section of report.onramp.sections) {
|
|
180
|
+
lines.push("");
|
|
181
|
+
lines.push(` ${section.title}: ${section.summary}`);
|
|
182
|
+
for (const action of section.actions) {
|
|
183
|
+
lines.push(` - ${action.command}`);
|
|
184
|
+
lines.push(` ${action.reason}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
152
188
|
return lines.join("\n");
|
|
153
189
|
}
|