cool-workflow 0.1.85 → 0.1.87
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 +53 -332
- 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 +108 -50
- package/dist/cli/command-surface.js +83 -10
- package/dist/cli.js +2 -1
- package/dist/doctor.js +50 -5
- package/dist/execution-backend.js +8 -0
- 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/operator-ux/format.js +21 -15
- package/dist/operator-ux.js +2 -1
- package/dist/orchestrator.js +190 -87
- package/dist/term.js +93 -0
- package/dist/version.js +1 -1
- package/dist/workbench-host.js +1 -8
- package/docs/agent-delegation-drive.7.md +45 -8
- package/docs/cli-mcp-parity.7.md +41 -6
- package/docs/contract-migration-tooling.7.md +6 -0
- package/docs/control-plane-scheduling.7.md +6 -0
- package/docs/dogfood-one-real-repo.7.md +9 -1
- package/docs/durable-state-and-locking.7.md +6 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +6 -0
- package/docs/execution-backends.7.md +6 -0
- package/docs/getting-started.md +40 -2
- package/docs/index.md +51 -38
- package/docs/multi-agent-cli-mcp-surface.7.md +6 -0
- package/docs/multi-agent-eval-replay-harness.7.md +6 -0
- package/docs/multi-agent-operator-ux.7.md +6 -0
- package/docs/node-snapshot-diff-replay.7.md +6 -0
- package/docs/observability-cost-accounting.7.md +6 -0
- package/docs/project-index.md +24 -7
- package/docs/real-execution-backends.7.md +6 -0
- package/docs/release-and-migration.7.md +7 -1
- package/docs/release-history.md +352 -0
- package/docs/release-tooling.7.md +28 -0
- package/docs/run-registry-control-plane.7.md +6 -0
- package/docs/run-retention-reclamation.7.md +6 -0
- package/docs/state-explosion-management.7.md +6 -0
- package/docs/team-collaboration.7.md +6 -0
- package/docs/web-desktop-workbench.7.md +6 -0
- package/manifest/plugin.manifest.json +1 -1
- package/manifest/source-context-profiles.json +1 -1
- package/package.json +2 -1
- package/scripts/agents/agent-adapter-core.js +180 -0
- package/scripts/agents/builtin-templates.json +4 -1
- package/scripts/agents/codex-agent.js +134 -0
- package/scripts/agents/gemini-agent.js +115 -0
- package/scripts/agents/opencode-agent.js +119 -0
- 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 +741 -2
- package/scripts/release-check.js +5 -3
- package/scripts/release-gate.sh +1 -1
- 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
|
// ---------------------------------------------------------------------------
|
|
@@ -59,6 +61,30 @@ const BUILTIN_CAPABILITIES = [
|
|
|
59
61
|
cli: { path: ["list"], jsonMode: "default" },
|
|
60
62
|
mcp: { tool: "cw_list" }
|
|
61
63
|
},
|
|
64
|
+
{
|
|
65
|
+
capability: "info",
|
|
66
|
+
summary: "Show what a workflow app does: title, description, inputs, sandbox, phases, and a runnable example.",
|
|
67
|
+
entry: "showApp",
|
|
68
|
+
surface: "cli-only",
|
|
69
|
+
cli: { path: ["info"], jsonMode: "flag" },
|
|
70
|
+
reason: "Human-focused workflow discovery tool (like Homebrew's `brew info`). MCP agents discover workflows via cw_list and cw_app_show tools."
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
capability: "search",
|
|
74
|
+
summary: "Search workflow apps by keyword (title, description, id).",
|
|
75
|
+
entry: "listApps",
|
|
76
|
+
surface: "cli-only",
|
|
77
|
+
cli: { path: ["search"], jsonMode: "flag" },
|
|
78
|
+
reason: "Human-focused workflow discovery (like Homebrew's `brew search`). MCP agents discover workflows via cw_list tool."
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
capability: "man",
|
|
82
|
+
summary: "Show a man page from docs/ (e.g. cw man release-tooling).",
|
|
83
|
+
entry: "n/a",
|
|
84
|
+
surface: "cli-only",
|
|
85
|
+
cli: { path: ["man"], jsonMode: "human" },
|
|
86
|
+
reason: "Human documentation viewer. MCP agents read docs/ directly via file tools."
|
|
87
|
+
},
|
|
62
88
|
{
|
|
63
89
|
capability: "doctor",
|
|
64
90
|
summary: "Diagnose the host for setup problems (Node version, agent backend, agent binary on PATH, git, writable home/repo state) and print an actionable fix per check.",
|
|
@@ -351,7 +377,7 @@ const BUILTIN_CAPABILITIES = [
|
|
|
351
377
|
{ 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
378
|
{
|
|
353
379
|
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).",
|
|
380
|
+
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
381
|
entry: "quickstart",
|
|
356
382
|
surface: "cli-only",
|
|
357
383
|
cli: { path: ["quickstart"], caseTokens: ["quickstart", "audit-run"], jsonMode: "default" },
|
|
@@ -458,34 +484,63 @@ const RUN_PAYLOAD_PROBE_CAPABILITIES = [
|
|
|
458
484
|
"gc.plan",
|
|
459
485
|
"gc.verify"
|
|
460
486
|
];
|
|
487
|
+
const SCENARIO_PAYLOAD_PROBE_CAPABILITIES = [
|
|
488
|
+
"plan",
|
|
489
|
+
"app.show",
|
|
490
|
+
"app.validate",
|
|
491
|
+
"app.package",
|
|
492
|
+
"topology.show",
|
|
493
|
+
"topology.validate",
|
|
494
|
+
"topology.apply",
|
|
495
|
+
"topology.summary",
|
|
496
|
+
"topology.graph",
|
|
497
|
+
"summary.refresh",
|
|
498
|
+
"summary.show",
|
|
499
|
+
"sandbox.show",
|
|
500
|
+
"sandbox.validate",
|
|
501
|
+
"sandbox.choose",
|
|
502
|
+
"sandbox.resolve",
|
|
503
|
+
"approve",
|
|
504
|
+
"reject",
|
|
505
|
+
"comment.add",
|
|
506
|
+
"handoff",
|
|
507
|
+
"review.policy",
|
|
508
|
+
"worker.list",
|
|
509
|
+
"worker.show",
|
|
510
|
+
"worker.manifest",
|
|
511
|
+
"worker.output",
|
|
512
|
+
"worker.fail",
|
|
513
|
+
"worker.validate",
|
|
514
|
+
"candidate.list",
|
|
515
|
+
"candidate.show",
|
|
516
|
+
"candidate.register",
|
|
517
|
+
"candidate.score",
|
|
518
|
+
"candidate.rank",
|
|
519
|
+
"candidate.select",
|
|
520
|
+
"candidate.reject",
|
|
521
|
+
"feedback.list",
|
|
522
|
+
"feedback.show",
|
|
523
|
+
"feedback.collect",
|
|
524
|
+
"feedback.task",
|
|
525
|
+
"feedback.resolve",
|
|
526
|
+
"node.show",
|
|
527
|
+
"node.snapshot",
|
|
528
|
+
"node.diff",
|
|
529
|
+
"node.replay",
|
|
530
|
+
"node.replay.verify"
|
|
531
|
+
];
|
|
461
532
|
const PAYLOAD_PROBE_DEFERRED_GROUPS = [
|
|
462
533
|
{
|
|
463
534
|
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
535
|
capabilities: [
|
|
465
536
|
"init",
|
|
466
|
-
"plan",
|
|
467
537
|
"dispatch",
|
|
468
538
|
"result",
|
|
469
|
-
"app.show",
|
|
470
|
-
"app.validate",
|
|
471
539
|
"app.init",
|
|
472
|
-
"app.package",
|
|
473
540
|
"app.run",
|
|
474
|
-
"node.show",
|
|
475
|
-
"node.snapshot",
|
|
476
|
-
"node.diff",
|
|
477
|
-
"node.replay",
|
|
478
|
-
"node.replay.verify",
|
|
479
541
|
"migration.list",
|
|
480
542
|
"migration.check",
|
|
481
543
|
"migration.prove",
|
|
482
|
-
"topology.show",
|
|
483
|
-
"topology.validate",
|
|
484
|
-
"topology.apply",
|
|
485
|
-
"topology.summary",
|
|
486
|
-
"topology.graph",
|
|
487
|
-
"summary.refresh",
|
|
488
|
-
"summary.show",
|
|
489
544
|
"multi-agent.run",
|
|
490
545
|
"multi-agent.status",
|
|
491
546
|
"multi-agent.step",
|
|
@@ -542,30 +597,8 @@ const PAYLOAD_PROBE_DEFERRED_GROUPS = [
|
|
|
542
597
|
"audit.judge",
|
|
543
598
|
"audit.attest",
|
|
544
599
|
"audit.decision",
|
|
545
|
-
"sandbox.show",
|
|
546
|
-
"sandbox.validate",
|
|
547
|
-
"sandbox.choose",
|
|
548
|
-
"sandbox.resolve",
|
|
549
600
|
"backend.show",
|
|
550
601
|
"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
602
|
"schedule.create",
|
|
570
603
|
"schedule.list",
|
|
571
604
|
"schedule.delete",
|
|
@@ -607,12 +640,7 @@ const PAYLOAD_PROBE_DEFERRED_GROUPS = [
|
|
|
607
640
|
"sched.policy.show",
|
|
608
641
|
"sched.policy.set",
|
|
609
642
|
"telemetry.verify",
|
|
610
|
-
"history"
|
|
611
|
-
"approve",
|
|
612
|
-
"reject",
|
|
613
|
-
"comment.add",
|
|
614
|
-
"handoff",
|
|
615
|
-
"review.policy"
|
|
643
|
+
"history"
|
|
616
644
|
]
|
|
617
645
|
}
|
|
618
646
|
];
|
|
@@ -665,6 +693,24 @@ function declaredCliTokens() {
|
|
|
665
693
|
}
|
|
666
694
|
return [...tokens].sort();
|
|
667
695
|
}
|
|
696
|
+
/** The top-level CLI commands that should be visible in `cw help`. Subcommands
|
|
697
|
+
* are intentionally collapsed to their first token; aliases such as audit-run
|
|
698
|
+
* stay visible. */
|
|
699
|
+
function declaredCliHelpTokens() {
|
|
700
|
+
const tokens = new Set();
|
|
701
|
+
for (const cap of exports.CAPABILITY_REGISTRY) {
|
|
702
|
+
if (!cap.cli)
|
|
703
|
+
continue;
|
|
704
|
+
const subcommandTokens = new Set(cap.cli.path.slice(1));
|
|
705
|
+
tokens.add(cap.cli.path[0]);
|
|
706
|
+
for (const token of cap.cli.caseTokens || []) {
|
|
707
|
+
if (!subcommandTokens.has(token))
|
|
708
|
+
tokens.add(token);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
tokens.delete("help");
|
|
712
|
+
return [...tokens].sort();
|
|
713
|
+
}
|
|
668
714
|
/** Whether a descriptor MUST carry a reason (surface-specific or divergent). */
|
|
669
715
|
function requiresReason(cap) {
|
|
670
716
|
if (cap.surface !== "both")
|
|
@@ -699,27 +745,31 @@ function payloadIdenticalCapabilities() {
|
|
|
699
745
|
function payloadProbeTargets() {
|
|
700
746
|
return [
|
|
701
747
|
...GLOBAL_PAYLOAD_PROBE_CAPABILITIES.map((capability) => ({ capability, kind: "global" })),
|
|
702
|
-
...RUN_PAYLOAD_PROBE_CAPABILITIES.map((capability) => ({ capability, kind: "run" }))
|
|
748
|
+
...RUN_PAYLOAD_PROBE_CAPABILITIES.map((capability) => ({ capability, kind: "run" })),
|
|
749
|
+
...SCENARIO_PAYLOAD_PROBE_CAPABILITIES.map((capability) => ({ capability, kind: "scenario" }))
|
|
703
750
|
];
|
|
704
751
|
}
|
|
705
752
|
function deferredPayloadProbeCapabilities() {
|
|
706
753
|
return PAYLOAD_PROBE_DEFERRED_GROUPS.flatMap((group) => group.capabilities.map((capability) => ({ capability, reason: group.reason })));
|
|
707
754
|
}
|
|
708
|
-
function
|
|
755
|
+
function buildPayloadProbePlan(targets, deferred) {
|
|
709
756
|
const candidateIds = new Set(payloadIdenticalCapabilities().map((cap) => cap.capability));
|
|
710
757
|
const counts = new Map();
|
|
711
|
-
const classified = [...
|
|
758
|
+
const classified = [...targets.map((entry) => entry.capability), ...deferred.map((entry) => entry.capability)];
|
|
712
759
|
for (const capability of classified)
|
|
713
760
|
counts.set(capability, (counts.get(capability) || 0) + 1);
|
|
714
761
|
const classifiedIds = new Set(classified);
|
|
715
762
|
return {
|
|
716
|
-
targets
|
|
717
|
-
deferred
|
|
763
|
+
targets,
|
|
764
|
+
deferred,
|
|
718
765
|
unclassified: [...candidateIds].filter((capability) => !classifiedIds.has(capability)).sort(),
|
|
719
766
|
duplicateClassifications: [...counts.entries()].filter(([, count]) => count > 1).map(([capability]) => capability).sort(),
|
|
720
767
|
invalidClassifications: [...classifiedIds].filter((capability) => !candidateIds.has(capability)).sort()
|
|
721
768
|
};
|
|
722
769
|
}
|
|
770
|
+
function payloadProbePlan() {
|
|
771
|
+
return buildPayloadProbePlan(payloadProbeTargets(), deferredPayloadProbeCapabilities());
|
|
772
|
+
}
|
|
723
773
|
function lintRegistry() {
|
|
724
774
|
const issues = [];
|
|
725
775
|
const seenCaps = new Set();
|
|
@@ -755,10 +805,14 @@ function buildParityReport(input) {
|
|
|
755
805
|
const actualTools = new Set(input.mcpTools);
|
|
756
806
|
const declaredTokens = new Set(declaredCliTokens());
|
|
757
807
|
const actualTokens = new Set(input.cliTokens);
|
|
808
|
+
const declaredHelpTokens = new Set(declaredCliHelpTokens());
|
|
809
|
+
const actualHelpTokens = new Set(input.helpTokens || []);
|
|
758
810
|
const missingMcpTools = [...declaredTools].filter((tool) => !actualTools.has(tool)).sort();
|
|
759
811
|
const undeclaredMcpTools = [...actualTools].filter((tool) => !declaredTools.has(tool)).sort();
|
|
760
812
|
const missingCliTokens = [...declaredTokens].filter((token) => !actualTokens.has(token)).sort();
|
|
761
813
|
const undeclaredCliTokens = [...actualTokens].filter((token) => !declaredTokens.has(token)).sort();
|
|
814
|
+
const helpMissingCliTokens = input.helpTokens ? [...declaredHelpTokens].filter((token) => !actualHelpTokens.has(token)).sort() : [];
|
|
815
|
+
const helpUndeclaredCliTokens = input.helpTokens ? [...actualHelpTokens].filter((token) => !declaredHelpTokens.has(token)).sort() : [];
|
|
762
816
|
const reasonlessExceptions = exports.CAPABILITY_REGISTRY.filter((cap) => requiresReason(cap) && !(cap.reason && cap.reason.trim()))
|
|
763
817
|
.map((cap) => cap.capability)
|
|
764
818
|
.sort();
|
|
@@ -768,6 +822,8 @@ function buildParityReport(input) {
|
|
|
768
822
|
undeclaredMcpTools.length === 0 &&
|
|
769
823
|
missingCliTokens.length === 0 &&
|
|
770
824
|
undeclaredCliTokens.length === 0 &&
|
|
825
|
+
helpMissingCliTokens.length === 0 &&
|
|
826
|
+
helpUndeclaredCliTokens.length === 0 &&
|
|
771
827
|
reasonlessExceptions.length === 0 &&
|
|
772
828
|
payloadPlan.unclassified.length === 0 &&
|
|
773
829
|
payloadPlan.duplicateClassifications.length === 0 &&
|
|
@@ -780,6 +836,8 @@ function buildParityReport(input) {
|
|
|
780
836
|
undeclaredMcpTools,
|
|
781
837
|
missingCliTokens,
|
|
782
838
|
undeclaredCliTokens,
|
|
839
|
+
helpMissingCliTokens,
|
|
840
|
+
helpUndeclaredCliTokens,
|
|
783
841
|
reasonlessExceptions,
|
|
784
842
|
payloadProbeUnclassified: payloadPlan.unclassified,
|
|
785
843
|
payloadProbeDuplicateClassifications: payloadPlan.duplicateClassifications,
|
|
@@ -22,6 +22,8 @@ const multi_agent_eval_1 = require("../multi-agent-eval");
|
|
|
22
22
|
const state_explosion_1 = require("../state-explosion");
|
|
23
23
|
const evidence_reasoning_1 = require("../evidence-reasoning");
|
|
24
24
|
const doctor_1 = require("../doctor");
|
|
25
|
+
const orchestrator_2 = require("../orchestrator");
|
|
26
|
+
const term_1 = require("../term");
|
|
25
27
|
async function runCli(argv = process.argv.slice(2)) {
|
|
26
28
|
const args = (0, orchestrator_1.parseArgv)(argv);
|
|
27
29
|
const runner = new orchestrator_1.CoolWorkflowRunner({
|
|
@@ -37,10 +39,61 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
37
39
|
case "list":
|
|
38
40
|
printJson(runner.listWorkflows());
|
|
39
41
|
return;
|
|
42
|
+
case "search": {
|
|
43
|
+
const keyword = args.positionals.join(" ");
|
|
44
|
+
if (!keyword.trim())
|
|
45
|
+
throw new Error("Missing search keyword.\n Tip: cw search architecture to find workflows about architecture.");
|
|
46
|
+
const apps = runner.listApps();
|
|
47
|
+
const lower = keyword.toLowerCase();
|
|
48
|
+
const results = apps.filter((a) => a.title.toLowerCase().includes(lower) || a.summary.toLowerCase().includes(lower) || a.id.toLowerCase().includes(lower)).map((a) => ({ id: a.id, title: a.title, summary: a.summary }));
|
|
49
|
+
if (wantsJson(args.options))
|
|
50
|
+
printJson(results);
|
|
51
|
+
else
|
|
52
|
+
process.stdout.write(`${(0, orchestrator_2.formatSearchResults)(keyword, results)}\n`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
case "man": {
|
|
56
|
+
const [topic] = args.positionals;
|
|
57
|
+
if (!topic)
|
|
58
|
+
throw new Error("Missing topic.\n Tip: cw man release-tooling for the release tooling manual.");
|
|
59
|
+
const docsDir = node_path_1.default.resolve(runner.pluginRoot, "docs");
|
|
60
|
+
const candidates = [
|
|
61
|
+
node_path_1.default.join(docsDir, `${topic}.7.md`),
|
|
62
|
+
node_path_1.default.join(docsDir, `${topic}.md`),
|
|
63
|
+
node_path_1.default.join(docsDir, `${topic}`)
|
|
64
|
+
];
|
|
65
|
+
let found;
|
|
66
|
+
for (const c of candidates) {
|
|
67
|
+
try {
|
|
68
|
+
if (node_fs_1.default.statSync(c).isFile()) {
|
|
69
|
+
found = c;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch { /* keep looking */ }
|
|
74
|
+
}
|
|
75
|
+
if (!found)
|
|
76
|
+
throw new Error(`Man page not found: ${topic}.\n Tip: cw list for workflow topics, or browse docs/ for manuals.`);
|
|
77
|
+
process.stdout.write(node_fs_1.default.readFileSync(found, "utf8"));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
case "info": {
|
|
81
|
+
const [appId] = args.positionals;
|
|
82
|
+
if (!appId)
|
|
83
|
+
throw new Error("Missing workflow app id.\n Tip: list apps with \"cw list\", then \"cw info <id>\" for details");
|
|
84
|
+
const data = runner.showApp(appId);
|
|
85
|
+
if (wantsJson(args.options))
|
|
86
|
+
printJson(data);
|
|
87
|
+
else
|
|
88
|
+
process.stdout.write(`${(0, orchestrator_2.formatInfo)(appId, data)}\n`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
40
91
|
case "doctor": {
|
|
41
92
|
const report = (0, doctor_1.runDoctor)(args.options, process.env, String(args.options.cwd || process.cwd()));
|
|
42
93
|
if (wantsJson(args.options))
|
|
43
94
|
printJson(report);
|
|
95
|
+
else if (args.options.fix)
|
|
96
|
+
process.stdout.write(`${(0, doctor_1.formatDoctorFixes)(report)}\n`);
|
|
44
97
|
else
|
|
45
98
|
process.stdout.write(`${(0, doctor_1.formatDoctorReport)(report)}\n`);
|
|
46
99
|
if (!report.ok)
|
|
@@ -50,7 +103,7 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
50
103
|
case "init": {
|
|
51
104
|
const [workflowId] = args.positionals;
|
|
52
105
|
if (!workflowId)
|
|
53
|
-
throw new Error("Missing workflow id
|
|
106
|
+
throw new Error("Missing workflow id.\n Tip: create one with \"cw init my-workflow\" or list with \"cw list\"");
|
|
54
107
|
printJson(runner.init(workflowId, args.options));
|
|
55
108
|
return;
|
|
56
109
|
}
|
|
@@ -93,6 +146,18 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
93
146
|
const runId = optionalArg(args.options.run) || optionalArg(args.options.runId);
|
|
94
147
|
const qs = (0, capability_core_1.quickstart)(runner, { ...args.options, ...(appId ? { appId } : {}), ...(runId ? { runId } : {}) });
|
|
95
148
|
printJson(qs);
|
|
149
|
+
const qr = qs;
|
|
150
|
+
if (typeof qr.runId === "string" && typeof qr.reportPath === "string") {
|
|
151
|
+
(0, term_1.printSuccessSummary)({
|
|
152
|
+
runId: qr.runId,
|
|
153
|
+
reportPath: qr.reportPath,
|
|
154
|
+
status: String(qr.status || ""),
|
|
155
|
+
bundle: Boolean(args.options.bundle)
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
if (qs.mode === "check" && qs.ok === false) {
|
|
159
|
+
process.exitCode = 1;
|
|
160
|
+
}
|
|
96
161
|
// Fail closed: if --bundle produced an artifact that does not self-verify, exit
|
|
97
162
|
// non-zero so `cw quickstart ... --bundle && send-to-client` cannot ship a report
|
|
98
163
|
// whose bundle a client could not verify. Mirrors `report bundle`.
|
|
@@ -104,7 +169,7 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
104
169
|
case "plan": {
|
|
105
170
|
const [workflowId] = args.positionals;
|
|
106
171
|
if (!workflowId)
|
|
107
|
-
throw new Error("Missing workflow id
|
|
172
|
+
throw new Error("Missing workflow id.\n Tip: plan an architecture review with \"cw plan architecture-review\"");
|
|
108
173
|
printJson((0, capability_core_1.planSummary)(runner, workflowId, args.options));
|
|
109
174
|
return;
|
|
110
175
|
}
|
|
@@ -118,8 +183,10 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
118
183
|
}
|
|
119
184
|
else if (wantsJson(args.options))
|
|
120
185
|
printJson(runner.status(args.positionals[0]));
|
|
121
|
-
else
|
|
122
|
-
|
|
186
|
+
else {
|
|
187
|
+
const summary = runner.operatorStatus(args.positionals[0]);
|
|
188
|
+
process.stdout.write(`${(args.options.summary || args.options.brief ? (0, operator_ux_1.formatOperatorSummary)(summary) : (0, operator_ux_1.formatOperatorStatus)(summary))}\n`);
|
|
189
|
+
}
|
|
123
190
|
return;
|
|
124
191
|
case "next":
|
|
125
192
|
printJson(runner.next(required(args.positionals[0], "run id"), args.options));
|
|
@@ -201,8 +268,10 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
201
268
|
case "status":
|
|
202
269
|
if (wantsJson(args.options))
|
|
203
270
|
printJson(runner.operatorStatus(required(runId, "run id")));
|
|
204
|
-
else
|
|
205
|
-
|
|
271
|
+
else {
|
|
272
|
+
const summary = runner.operatorStatus(required(runId, "run id"));
|
|
273
|
+
process.stdout.write(`${(args.options.summary || args.options.brief ? (0, operator_ux_1.formatOperatorSummary)(summary) : (0, operator_ux_1.formatOperatorStatus)(summary))}\n`);
|
|
274
|
+
}
|
|
206
275
|
return;
|
|
207
276
|
case "report":
|
|
208
277
|
if (wantsJson(args.options))
|
|
@@ -1047,7 +1116,9 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
1047
1116
|
driveArgs.runId = runId;
|
|
1048
1117
|
else
|
|
1049
1118
|
driveArgs.appId = target;
|
|
1050
|
-
|
|
1119
|
+
const dr = (0, capability_core_1.runDrive)(runner, driveArgs);
|
|
1120
|
+
printJson(dr);
|
|
1121
|
+
(0, term_1.printSuccessSummary)({ runId: dr.runId, reportPath: dr.reportPath, status: dr.status });
|
|
1051
1122
|
return;
|
|
1052
1123
|
}
|
|
1053
1124
|
const registry = (0, capability_core_1.runRegistryFor)(args.options, runner);
|
|
@@ -1059,7 +1130,9 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
1059
1130
|
const driveArgs = { ...args.options };
|
|
1060
1131
|
if (id)
|
|
1061
1132
|
driveArgs.runId = id;
|
|
1062
|
-
|
|
1133
|
+
const dr = (0, capability_core_1.runDrive)(runner, driveArgs);
|
|
1134
|
+
printJson(dr);
|
|
1135
|
+
(0, term_1.printSuccessSummary)({ runId: dr.runId, reportPath: dr.reportPath, status: dr.status });
|
|
1063
1136
|
return;
|
|
1064
1137
|
}
|
|
1065
1138
|
printJson((0, capability_core_1.runDrivePreview)(runner, { ...args.options, runId: required(id, "run id") }));
|
|
@@ -1329,12 +1402,12 @@ async function runCli(argv = process.argv.slice(2)) {
|
|
|
1329
1402
|
}
|
|
1330
1403
|
}
|
|
1331
1404
|
default:
|
|
1332
|
-
throw new Error(`Unknown command: ${args.command}`);
|
|
1405
|
+
throw new Error(`Unknown command: ${args.command}${((0, orchestrator_1.suggestCommand)(String(args.command || "")) ? `. Did you mean: ${(0, orchestrator_1.suggestCommand)(String(args.command))}?` : "")}`);
|
|
1333
1406
|
}
|
|
1334
1407
|
}
|
|
1335
1408
|
function required(value, label) {
|
|
1336
1409
|
if (!value)
|
|
1337
|
-
throw new Error(`Missing ${label}
|
|
1410
|
+
throw new Error(`Missing ${label}.\n Tip: find run ids with "cw run list" or create one with "cw quickstart"`);
|
|
1338
1411
|
return value;
|
|
1339
1412
|
}
|
|
1340
1413
|
function optionalArg(value) {
|
package/dist/cli.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
const command_surface_1 = require("./cli/command-surface");
|
|
5
|
+
const term_1 = require("./term");
|
|
5
6
|
(0, command_surface_1.runCli)(process.argv.slice(2)).catch((error) => {
|
|
6
7
|
const message = error instanceof Error ? error.message : String(error);
|
|
7
|
-
process.stderr.write(
|
|
8
|
+
process.stderr.write(`${(0, term_1.bold)("cw:")} ${(0, term_1.red)(message)}\n`);
|
|
8
9
|
process.exitCode = 1;
|
|
9
10
|
});
|