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.
Files changed (69) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +53 -332
  4. package/apps/architecture-review/app.json +1 -1
  5. package/apps/architecture-review-fast/app.json +1 -1
  6. package/apps/architecture-review-fast/workflow.js +2 -0
  7. package/apps/end-to-end-golden-path/app.json +1 -1
  8. package/apps/pr-review-fix-ci/app.json +1 -1
  9. package/apps/release-cut/app.json +1 -1
  10. package/apps/research-synthesis/app.json +1 -1
  11. package/dist/capability-core.js +131 -3
  12. package/dist/capability-registry.js +108 -50
  13. package/dist/cli/command-surface.js +83 -10
  14. package/dist/cli.js +2 -1
  15. package/dist/doctor.js +50 -5
  16. package/dist/execution-backend.js +8 -0
  17. package/dist/mcp/tool-call.js +428 -0
  18. package/dist/mcp/tool-definitions.js +1027 -0
  19. package/dist/mcp-surface.js +5 -1442
  20. package/dist/onramp.js +421 -0
  21. package/dist/operator-ux/format.js +21 -15
  22. package/dist/operator-ux.js +2 -1
  23. package/dist/orchestrator.js +190 -87
  24. package/dist/term.js +93 -0
  25. package/dist/version.js +1 -1
  26. package/dist/workbench-host.js +1 -8
  27. package/docs/agent-delegation-drive.7.md +45 -8
  28. package/docs/cli-mcp-parity.7.md +41 -6
  29. package/docs/contract-migration-tooling.7.md +6 -0
  30. package/docs/control-plane-scheduling.7.md +6 -0
  31. package/docs/dogfood-one-real-repo.7.md +9 -1
  32. package/docs/durable-state-and-locking.7.md +6 -0
  33. package/docs/evidence-adoption-reasoning-chain.7.md +6 -0
  34. package/docs/execution-backends.7.md +6 -0
  35. package/docs/getting-started.md +40 -2
  36. package/docs/index.md +51 -38
  37. package/docs/multi-agent-cli-mcp-surface.7.md +6 -0
  38. package/docs/multi-agent-eval-replay-harness.7.md +6 -0
  39. package/docs/multi-agent-operator-ux.7.md +6 -0
  40. package/docs/node-snapshot-diff-replay.7.md +6 -0
  41. package/docs/observability-cost-accounting.7.md +6 -0
  42. package/docs/project-index.md +24 -7
  43. package/docs/real-execution-backends.7.md +6 -0
  44. package/docs/release-and-migration.7.md +7 -1
  45. package/docs/release-history.md +352 -0
  46. package/docs/release-tooling.7.md +28 -0
  47. package/docs/run-registry-control-plane.7.md +6 -0
  48. package/docs/run-retention-reclamation.7.md +6 -0
  49. package/docs/state-explosion-management.7.md +6 -0
  50. package/docs/team-collaboration.7.md +6 -0
  51. package/docs/web-desktop-workbench.7.md +6 -0
  52. package/manifest/plugin.manifest.json +1 -1
  53. package/manifest/source-context-profiles.json +1 -1
  54. package/package.json +2 -1
  55. package/scripts/agents/agent-adapter-core.js +180 -0
  56. package/scripts/agents/builtin-templates.json +4 -1
  57. package/scripts/agents/codex-agent.js +134 -0
  58. package/scripts/agents/gemini-agent.js +115 -0
  59. package/scripts/agents/opencode-agent.js +119 -0
  60. package/scripts/architecture-review-fast.js +44 -2
  61. package/scripts/canonical-apps.js +4 -4
  62. package/scripts/dogfood-release.js +55 -133
  63. package/scripts/golden-path.js +4 -4
  64. package/scripts/onramp-check.js +47 -0
  65. package/scripts/parity-check.js +741 -2
  66. package/scripts/release-check.js +5 -3
  67. package/scripts/release-gate.sh +1 -1
  68. package/scripts/source-context.js +31 -5
  69. package/scripts/version-sync-check.js +4 -2
@@ -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 chdir'd to
183
- // args.cwd), so repo/home roots line up.
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 payloadProbePlan() {
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 = [...payloadProbeTargets().map((entry) => entry.capability), ...deferredPayloadProbeCapabilities().map((entry) => entry.capability)];
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: payloadProbeTargets(),
717
- deferred: deferredPayloadProbeCapabilities(),
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. Example: cw.js init my-workflow");
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. Example: cw.js plan architecture-review");
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
- process.stdout.write(`${(0, operator_ux_1.formatOperatorStatus)(runner.operatorStatus(args.positionals[0]))}\n`);
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
- process.stdout.write(`${(0, operator_ux_1.formatOperatorStatus)(runner.operatorStatus(required(runId, "run id")))}\n`);
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
- printJson((0, capability_core_1.runDrive)(runner, driveArgs));
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
- printJson((0, capability_core_1.runDrive)(runner, driveArgs));
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}. Run "cw.js help" for usage.`);
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(`cw: ${message}\n`);
8
+ process.stderr.write(`${(0, term_1.bold)("cw:")} ${(0, term_1.red)(message)}\n`);
8
9
  process.exitCode = 1;
9
10
  });