cool-workflow 0.2.1 → 0.2.3

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 (156) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/apps/architecture-review/app.json +1 -1
  4. package/apps/architecture-review-fast/app.json +1 -1
  5. package/apps/end-to-end-golden-path/app.json +1 -1
  6. package/apps/pr-review-fix-ci/app.json +1 -1
  7. package/apps/release-cut/app.json +1 -1
  8. package/apps/research-synthesis/app.json +1 -1
  9. package/dist/cli/dispatch.js +29 -91
  10. package/dist/cli/entry.js +41 -1
  11. package/dist/cli/io.js +6 -2
  12. package/dist/cli/parseargv.js +6 -2
  13. package/dist/core/capability-data.js +271 -0
  14. package/dist/core/capability-table.js +16 -3504
  15. package/dist/core/format/completion.js +68 -0
  16. package/dist/core/format/help.js +50 -8
  17. package/dist/core/format/safe-json.js +73 -0
  18. package/dist/core/format/state-explosion-text.js +1 -1
  19. package/dist/core/multi-agent/candidate-scoring.js +5 -1
  20. package/dist/core/multi-agent/collaboration.js +6 -5
  21. package/dist/core/multi-agent/coordinator.js +9 -8
  22. package/dist/core/multi-agent/runtime.js +4 -4
  23. package/dist/core/multi-agent/topology.js +3 -3
  24. package/dist/core/pipeline/commit-gate.js +2 -1
  25. package/dist/core/pipeline/dispatch.js +18 -4
  26. package/dist/core/pipeline/drive-decide.js +2 -1
  27. package/dist/core/state/migrations.js +16 -1
  28. package/dist/core/state/state-explosion/digest.js +16 -15
  29. package/dist/core/state/state-explosion/graph.js +26 -19
  30. package/dist/core/state/state-explosion/helpers.js +2 -1
  31. package/dist/core/state/state-explosion/report.js +6 -6
  32. package/dist/core/trust/ledger.js +2 -1
  33. package/dist/core/types/execution-backend.js +18 -0
  34. package/dist/core/types/observability.js +7 -0
  35. package/dist/core/util/collate.js +23 -0
  36. package/dist/core/util/numeric-flag.js +40 -0
  37. package/dist/core/version.js +1 -1
  38. package/dist/mcp/server.js +99 -11
  39. package/dist/shell/audit-cli.js +99 -23
  40. package/dist/shell/commit-summary.js +2 -1
  41. package/dist/shell/coordinator-io.js +73 -13
  42. package/dist/shell/dispatch.js +1 -1
  43. package/dist/shell/doctor.js +80 -1
  44. package/dist/shell/drive.js +269 -59
  45. package/dist/shell/eval-text.js +2 -2
  46. package/dist/shell/evidence-reasoning.js +8 -7
  47. package/dist/shell/execution-backend/agent.js +20 -1
  48. package/dist/shell/execution-backend/container.js +4 -1
  49. package/dist/shell/execution-backend/local.js +4 -1
  50. package/dist/shell/execution-backend/registry.js +2 -1
  51. package/dist/shell/execution-backend/types.js +0 -9
  52. package/dist/shell/feedback-cli.js +6 -6
  53. package/dist/shell/fs-atomic.js +232 -30
  54. package/dist/shell/man-cli.js +6 -0
  55. package/dist/shell/metrics-cli.js +2 -1
  56. package/dist/shell/multi-agent-cli.js +367 -323
  57. package/dist/shell/multi-agent-host.js +9 -9
  58. package/dist/shell/multi-agent-operator-ux.js +82 -39
  59. package/dist/shell/node-store.js +10 -4
  60. package/dist/shell/observability.js +13 -12
  61. package/dist/shell/onramp.js +17 -1
  62. package/dist/shell/operator-ux-text.js +24 -23
  63. package/dist/shell/operator-ux.js +22 -20
  64. package/dist/shell/orchestrator.js +49 -38
  65. package/dist/shell/pipeline-cli.js +168 -111
  66. package/dist/shell/reclamation-io.js +16 -5
  67. package/dist/shell/registry-cli.js +19 -17
  68. package/dist/shell/remote-source.js +13 -8
  69. package/dist/shell/report-cli.js +45 -0
  70. package/dist/shell/report.js +4 -2
  71. package/dist/shell/run-registry-io.js +77 -19
  72. package/dist/shell/run-store.js +91 -2
  73. package/dist/shell/scheduling-io.js +12 -13
  74. package/dist/shell/state-cli.js +2 -7
  75. package/dist/shell/state-explosion-cli.js +19 -10
  76. package/dist/shell/topology-io.js +38 -6
  77. package/dist/shell/trust-audit.js +264 -23
  78. package/dist/shell/trust-policy-io.js +1 -1
  79. package/dist/shell/worker-cli.js +41 -29
  80. package/dist/shell/worker-isolation.js +34 -9
  81. package/dist/shell/workflow-app-loader.js +3 -2
  82. package/dist/wiring/capability-table/basics.js +91 -0
  83. package/dist/wiring/capability-table/exec-backend.js +171 -0
  84. package/dist/wiring/capability-table/index.js +42 -0
  85. package/dist/wiring/capability-table/multi-agent.js +630 -0
  86. package/dist/wiring/capability-table/parity.js +467 -0
  87. package/dist/wiring/capability-table/pipeline.js +292 -0
  88. package/dist/wiring/capability-table/registry-core.js +208 -0
  89. package/dist/wiring/capability-table/reporting.js +435 -0
  90. package/dist/wiring/capability-table/scheduling-registry.js +592 -0
  91. package/dist/wiring/capability-table/state.js +181 -0
  92. package/dist/wiring/capability-table/trust-ledger.js +158 -0
  93. package/dist/wiring/capability-table/workflow-apps.js +381 -0
  94. package/docs/agent-delegation-drive.7.md +4 -0
  95. package/docs/candidate-scoring.7.md +1 -1
  96. package/docs/canonical-workflow-apps.7.md +7 -7
  97. package/docs/cli-mcp-parity.7.md +13 -3
  98. package/docs/contract-migration-tooling.7.md +4 -0
  99. package/docs/control-plane-scheduling.7.md +4 -0
  100. package/docs/coordinator-blackboard.7.md +17 -17
  101. package/docs/dogfood-one-real-repo.7.md +11 -11
  102. package/docs/durable-state-and-locking.7.md +24 -0
  103. package/docs/end-to-end-golden-path.7.md +14 -14
  104. package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
  105. package/docs/execution-backends.7.md +4 -0
  106. package/docs/getting-started.md +37 -37
  107. package/docs/index.md +1 -0
  108. package/docs/multi-agent-cli-mcp-surface.7.md +21 -17
  109. package/docs/multi-agent-eval-replay-harness.7.md +17 -13
  110. package/docs/multi-agent-operator-ux.7.md +23 -19
  111. package/docs/multi-agent-runtime-core.7.md +22 -22
  112. package/docs/multi-agent-topologies.7.md +6 -6
  113. package/docs/multi-agent-trust-policy-audit.7.md +11 -11
  114. package/docs/node-snapshot-diff-replay.7.md +4 -0
  115. package/docs/observability-cost-accounting.7.md +4 -0
  116. package/docs/operator-ux.7.md +34 -34
  117. package/docs/pipeline-runner.7.md +4 -4
  118. package/docs/project-index.md +48 -5
  119. package/docs/real-execution-backends.7.md +4 -0
  120. package/docs/release-and-migration.7.md +5 -1
  121. package/docs/release-tooling.7.md +4 -0
  122. package/docs/routines.md +4 -4
  123. package/docs/run-registry-control-plane.7.md +23 -19
  124. package/docs/run-retention-reclamation.7.md +4 -0
  125. package/docs/scheduled-tasks.md +14 -14
  126. package/docs/security-trust-hardening.7.md +43 -13
  127. package/docs/state-explosion-management.7.md +14 -10
  128. package/docs/team-collaboration.7.md +4 -0
  129. package/docs/trust-audit-anchor.7.md +71 -0
  130. package/docs/unix-principles.md +3 -1
  131. package/docs/verifier-gated-commit.7.md +1 -1
  132. package/docs/web-desktop-workbench.7.md +4 -0
  133. package/docs/workflow-app-framework.7.md +13 -13
  134. package/manifest/plugin.manifest.json +1 -1
  135. package/package.json +4 -2
  136. package/scripts/agents/claude-p-agent.js +2 -2
  137. package/scripts/block-unapproved-tag.sh +23 -2
  138. package/scripts/bump-version.js +24 -2
  139. package/scripts/canonical-apps.js +4 -4
  140. package/scripts/children/batch-delegate-child.js +52 -2
  141. package/scripts/dogfood-release.js +1 -1
  142. package/scripts/fake-date-for-reproduction.js +44 -0
  143. package/scripts/golden-path.js +4 -4
  144. package/scripts/purity-baseline.json +71 -0
  145. package/scripts/purity-gate.js +239 -0
  146. package/scripts/release-check.js +8 -1
  147. package/scripts/release-flow.js +57 -1
  148. package/scripts/verdict-keygen.js +83 -0
  149. package/scripts/verify-bump-reproduction.sh +148 -0
  150. package/scripts/verify-verdict-signature.js +61 -0
  151. package/scripts/version-sync-check.js +33 -12
  152. package/skills/cool-workflow/SKILL.md +9 -9
  153. package/skills/cool-workflow/references/commands.md +89 -88
  154. package/ui/workbench/app.css +37 -1
  155. package/ui/workbench/app.js +124 -6
  156. package/workflows/README.md +19 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cool-workflow",
3
3
  "description": "A workflow control plane and run-time you are able to check: it sends out jobs in TypeScript, makes certain of work against facts before it goes through, puts state into fixed records, orders jobs by time, runs jobs again and again, gets a group of agents to do their parts together, and talks MCP. It gives the doing of the work to outside agents — it never runs the models itself.",
4
- "version": "0.2.1",
4
+ "version": "0.2.3",
5
5
  "author": {
6
6
  "name": "COOLWHITE LLC"
7
7
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cool-workflow",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "A workflow control plane and run-time you are able to check: it sends out jobs in TypeScript, makes certain of work against facts before it goes through, puts state into fixed records, orders jobs by time, runs jobs again and again, gets a group of agents to do their parts together, and talks MCP. It gives the doing of the work to outside agents — it never runs the models itself.",
5
5
  "author": {
6
6
  "name": "COOLWHITE LLC"
@@ -3,7 +3,7 @@
3
3
  "id": "architecture-review",
4
4
  "title": "Architecture Review",
5
5
  "summary": "Map a repository architecture, assess risks, verify important findings, and synthesize an evidence-backed verdict.",
6
- "version": "0.2.1",
6
+ "version": "0.2.3",
7
7
  "author": "COOLWHITE LLC",
8
8
  "inputs": [
9
9
  {
@@ -3,7 +3,7 @@
3
3
  "id": "architecture-review-fast",
4
4
  "title": "Architecture Review Fast",
5
5
  "summary": "Run a shorter architecture review with parallel map and assess phases for faster first results.",
6
- "version": "0.2.1",
6
+ "version": "0.2.3",
7
7
  "author": "COOLWHITE LLC",
8
8
  "inputs": [
9
9
  {
@@ -3,7 +3,7 @@
3
3
  "id": "end-to-end-golden-path",
4
4
  "title": "End-to-End Golden Path",
5
5
  "summary": "Deterministic one-worker workflow app for proving the CW integration chain.",
6
- "version": "0.2.1",
6
+ "version": "0.2.3",
7
7
  "author": "COOLWHITE LLC",
8
8
  "inputs": [
9
9
  {
@@ -3,7 +3,7 @@
3
3
  "id": "pr-review-fix-ci",
4
4
  "title": "PR Review Fix CI",
5
5
  "summary": "Review a pull request or branch, inspect CI failures, diagnose actionable issues, optionally patch, verify, and summarize with evidence.",
6
- "version": "0.2.1",
6
+ "version": "0.2.3",
7
7
  "author": "COOLWHITE LLC",
8
8
  "inputs": [
9
9
  {
@@ -3,7 +3,7 @@
3
3
  "id": "release-cut",
4
4
  "title": "Release Cut",
5
5
  "summary": "Prepare a release with checklist discipline: version checks, changelog, tests, packaging, release notes, and final verification.",
6
- "version": "0.2.1",
6
+ "version": "0.2.3",
7
7
  "author": "COOLWHITE LLC",
8
8
  "inputs": [
9
9
  {
@@ -3,7 +3,7 @@
3
3
  "id": "research-synthesis",
4
4
  "title": "Research Synthesis",
5
5
  "summary": "Split a research question into claims, investigate sources, cross-check evidence, verify claims, and synthesize a concise answer.",
6
- "version": "0.2.1",
6
+ "version": "0.2.3",
7
7
  "author": "COOLWHITE LLC",
8
8
  "inputs": [
9
9
  {
@@ -27,7 +27,6 @@ const capability_table_1 = require("../core/capability-table");
27
27
  const parseargv_1 = require("./parseargv");
28
28
  Object.defineProperty(exports, "KNOWN_COMMANDS", { enumerable: true, get: function () { return parseargv_1.KNOWN_COMMANDS; } });
29
29
  const io_1 = require("./io");
30
- const workflow_app_loader_1 = require("../shell/workflow-app-loader");
31
30
  function firstPositional(args, index = 0) {
32
31
  return args.positionals[index];
33
32
  }
@@ -55,8 +54,14 @@ function renderCliResult(result, jsonMode, options) {
55
54
  /** Tries the capability table for `args`. Matches the row whose
56
55
  * `cli.path` is `[command]` or `[command, positionals[0]]` (the only two
57
56
  * path lengths any milestone-2 row uses); returns true when a table row
58
- * handled the command. */
59
- function dispatchTable(args) {
57
+ * handled the command. `await`ing the handler's result is a no-op for
58
+ * the ~199 rows whose handler returns a plain CliHandlerResult (an
59
+ * `await` on a non-Promise value just resolves on the next microtask,
60
+ * invisible to any caller that itself awaits dispatch()) -- it only
61
+ * matters for the live drive rows, which return a real Promise so their
62
+ * round loop can actually stay interruptible (see shell/drive.ts's
63
+ * driveAsync). */
64
+ async function dispatchTable(args) {
60
65
  const candidates = [[args.command]];
61
66
  if (args.positionals.length > 0)
62
67
  candidates.push([args.command, args.positionals[0]]);
@@ -69,32 +74,19 @@ function dispatchTable(args) {
69
74
  positionals: args.positionals.slice(consumed),
70
75
  options: args.options,
71
76
  };
72
- const result = row.cli.handler(cliArgs);
77
+ const result = await row.cli.handler(cliArgs);
73
78
  renderCliResult(result, row.cli.jsonMode, args.options);
74
79
  return true;
75
80
  }
76
81
  return false;
77
82
  }
78
- // MILESTONE 12 (workflow-apps) — `search` filters the SAME real app
79
- // discovery `cw app list`/`cw list` use (shell/workflow-app-loader.ts),
80
- // by id/title/summary, matching `listApps` in the old build.
81
- function formatSearchResults(keyword, results) {
82
- if (results.length === 0) {
83
- return `No workflows matched "${keyword}".\n Tip: cw list for all available workflows.`;
84
- }
85
- const lines = [`${results.length} workflow${results.length === 1 ? "" : "s"} matching "${keyword}"`];
86
- for (const r of results) {
87
- lines.push(` ${r.id} — ${r.title}`);
88
- const cut = r.summary.length > 120 ? `${r.summary.slice(0, 119)}…` : r.summary;
89
- lines.push(` ${cut}`);
90
- }
91
- lines.push("");
92
- lines.push("Use cw info <id> for full details.");
93
- return lines.join("\n");
94
- }
95
83
  /** The milestone-1 carry-over switch. See file header: never extended
96
84
  * again — each arm here is replaced by a capability-table row when its
97
- * own build-order milestone lands, not edited in place. */
85
+ * own build-order milestone lands, not edited in place. Only entry-level
86
+ * concerns live here now (bare help, and the two verbs a real
87
+ * capability-table row can never fully own): every other verb this
88
+ * switch used to carry is now a table row (see the NOTEs below) and
89
+ * dispatchTable() above always matches it first. */
98
90
  function dispatchLegacy(args) {
99
91
  switch (args.command) {
100
92
  case "": {
@@ -114,38 +106,15 @@ function dispatchLegacy(args) {
114
106
  // NOTE: "version" is not an arm here — it is a real capability-table
115
107
  // row (core/capability-table.ts) that dispatchTable() above always
116
108
  // matches first, per the Revision note's "table rows, never a new
117
- // switch arm" rule. Same for "list", "status", and "sandbox list".
118
- // `search` filters the real app discovery by title/summary/id (see
119
- // note above); MILESTONE 12.
120
- case "search": {
121
- const keyword = args.positionals.join(" ");
122
- if (!keyword.trim()) {
123
- throw new Error('Missing search keyword.\n Tip: cw search architecture to find workflows about architecture.');
124
- }
125
- const lower = keyword.toLowerCase();
126
- const results = (0, workflow_app_loader_1.listWorkflowApps)()
127
- .filter((a) => String(a.title).toLowerCase().includes(lower) ||
128
- String(a.summary).toLowerCase().includes(lower) ||
129
- String(a.id).toLowerCase().includes(lower))
130
- .map((a) => ({ id: String(a.id), title: String(a.title), summary: String(a.summary) }));
131
- if ((0, io_1.wantsJson)(args.options)) {
132
- (0, io_1.printJson)(results);
133
- }
134
- else {
135
- process.stdout.write(`${formatSearchResults(keyword, results)}\n`);
136
- }
137
- return;
138
- }
109
+ // switch arm" rule. Same for "list", "status", "sandbox list", and
110
+ // "search" (a cli-only row, hiddenFromHelp, so `cw help search` keeps
111
+ // its existing "Unknown command" text).
139
112
  // NOTE: "plan" and "quickstart" are not arms here any more — both are
140
113
  // now real capability-table rows (core/capability-table.ts, milestone
141
114
  // 6+7) that dispatchTable() above always matches first.
142
- // PLACEHOLDER (milestone 3/6, state kernel + pipeline) — real `next`
143
- // loads run state and returns dispatchable tasks; this milestone only
144
- // reproduces the io.required missing-run-id refusal.
145
- case "next": {
146
- const runId = (0, io_1.required)((0, io_1.optionalArg)(firstPositional(args)), "run id");
147
- throw new Error(`next is not implemented in this milestone (runId=${runId})`);
148
- }
115
+ // NOTE: "next" is not an arm here any more it is a real
116
+ // capability-table row (core/capability-table.ts) that dispatchTable()
117
+ // above always matches first.
149
118
  // MILESTONE 8 — `ledger propose|review|verify|apply|list` are now
150
119
  // real capability-table rows (core/capability-table.ts) that
151
120
  // dispatchTable() above always matches first; this arm is reached
@@ -157,30 +126,9 @@ function dispatchLegacy(args) {
157
126
  case "ledger": {
158
127
  throw new Error("Usage: cw ledger propose|review|verify|apply|list [options]");
159
128
  }
160
- // PLACEHOLDER (milestone 10, scheduling/gc)real `gc verify` checks
161
- // whether a run's disk footprint was actually reclaimed; a run that
162
- // was never reclaimed is not a failure (exit 0), which is exactly the
163
- // shape this stub reproduces for an unresolvable run id.
164
- case "gc": {
165
- const sub = firstPositional(args);
166
- if (sub === "verify") {
167
- const runId = (0, io_1.optionalArg)(firstPositional(args, 1));
168
- const payload = {
169
- schemaVersion: 1,
170
- runId: runId ?? null,
171
- reclaimed: false,
172
- verified: false,
173
- tier: "live",
174
- capability: "re-runnable",
175
- chainLength: 0,
176
- checks: [{ name: "located", pass: false, code: "not-reclaimed", detail: "run source not found" }],
177
- nextAction: "node scripts/cw.js registry refresh --scope home",
178
- };
179
- (0, io_1.printJson)(payload);
180
- return;
181
- }
182
- throw new Error(`gc ${sub ?? ""} is not implemented in this milestone`);
183
- }
129
+ // NOTE: "gc" is not an arm here any more — gc.plan/gc.run/gc.verify
130
+ // are now real capability-table rows (core/capability-table.ts) that
131
+ // dispatchTable() above always matches first.
184
132
  // NOTE: "run" is not an arm here any more — run.drive.step/run.drive
185
133
  // are now real capability-table rows (core/capability-table.ts,
186
134
  // milestone 6+7) that dispatchTable() above always matches first
@@ -196,20 +144,10 @@ function dispatchLegacy(args) {
196
144
  // (core/capability-table.ts, milestone 9) that dispatchTable() above
197
145
  // always matches first, per the Revision note's "table rows, never a
198
146
  // new switch arm" rule.
199
- // PLACEHOLDER (milestone 3/4, state kernel + contract-migration)
200
- // real `migration check`/`prove` resolve a run id or file target; the
201
- // missing-target refusal is the only shape this milestone reproduces.
202
- case "migration": {
203
- const sub = firstPositional(args);
204
- if (sub === "check" || sub === "prove") {
205
- const target = (0, io_1.optionalArg)(firstPositional(args, 1));
206
- if (!target) {
207
- throw new Error('Missing target (run-id or state/app file).\n Tip: find run ids with "cw run list" or create one with "cw quickstart"');
208
- }
209
- throw new Error(`migration ${sub} is not implemented in this milestone`);
210
- }
211
- throw new Error(`migration ${sub ?? ""} is not implemented in this milestone`);
212
- }
147
+ // NOTE: "migration" is not an arm here any more migration.list/
148
+ // check/prove are now real capability-table rows
149
+ // (core/capability-table.ts) that dispatchTable() above always
150
+ // matches first.
213
151
  // NOTE: "report" is not an arm here any more — report/report.bundle/
214
152
  // report.verify-bundle are now real capability-table rows
215
153
  // (core/capability-table.ts, milestones 8/11) that dispatchTable()
@@ -229,8 +167,8 @@ function dispatchLegacy(args) {
229
167
  *
230
168
  * Tries the capability table FIRST (real rows always win), then falls
231
169
  * back to the milestone-1 legacy switch for verbs not yet migrated. */
232
- function dispatch(args) {
233
- if (dispatchTable(args))
170
+ async function dispatch(args) {
171
+ if (await dispatchTable(args))
234
172
  return;
235
173
  dispatchLegacy(args);
236
174
  }
package/dist/cli/entry.js CHANGED
@@ -33,6 +33,9 @@ const io_1 = require("./io");
33
33
  * comes from core/format/help.ts either way. */
34
34
  function printVersion() {
35
35
  const row = (0, capability_table_1.findCapability)("version");
36
+ // `version`'s own handler is a pure, synchronous projection (never one of
37
+ // the live drive rows) -- safe to read `.text` straight off it rather than
38
+ // route through the generic (possibly-async) dispatch path.
36
39
  const result = row?.cli?.handler({ positionals: [], options: {} });
37
40
  process.stdout.write(result?.text ?? "");
38
41
  }
@@ -88,6 +91,21 @@ async function runCli(argv = process.argv.slice(2)) {
88
91
  process.env.CW_NO_COLOR = "1";
89
92
  if (args.options.full)
90
93
  process.env.CW_OUTPUT = "full";
94
+ // --quiet is a documented CLI spelling of the existing CW_DRIVE_PROGRESS=0
95
+ // env var (shell/drive.ts's emitProgress) — it only silences the terse
96
+ // "[drive] " progress lines, not the end-of-run summary or any other
97
+ // Rule of Silence gate point (SPEC/reporting-ux.md's 3 gate points are
98
+ // each independent; --verbose/--full don't touch them either). It also
99
+ // does NOT reach two OTHER TTY-gated narration channels a user might
100
+ // reasonably expect it to quiet: live raw agent stderr streaming
101
+ // (shell/execution-backend/agent.ts's shouldStreamAgentStderr, gated by
102
+ // CW_AGENT_STREAM/CW_NO_STREAM/isTTY) and the local backend's
103
+ // "● Running…"/"✓ Done" lines (shell/execution-backend/local.ts, gated
104
+ // by isTTY with no env override at all). Folding those in is a larger,
105
+ // separate change — this flag's help text says "not agent output" on
106
+ // purpose so the scope is clear without reading this comment.
107
+ if (args.options.quiet)
108
+ process.env.CW_DRIVE_PROGRESS = "0";
91
109
  // `cw <verb> --help` / `-h` -> per-command help
92
110
  // (src/cli/command-surface.ts:80-83).
93
111
  if ((args.options.help || args.options.h) && args.command && !args.command.startsWith("-")) {
@@ -103,13 +121,35 @@ async function runCli(argv = process.argv.slice(2)) {
103
121
  else if (!args.command && typeof args.options.question === "string") {
104
122
  args.command = "quickstart";
105
123
  }
106
- (0, dispatch_1.dispatch)(args);
124
+ await (0, dispatch_1.dispatch)(args);
125
+ }
126
+ /** Broken pipe (`cw ... --json | head`): when the reader at the other end
127
+ * of a pipe goes away early, a write to stdout gives an async 'error'
128
+ * event that no promise .catch can see — without a listener node comes
129
+ * down hard with a stack trace. Raw writes are all over the CLI (help,
130
+ * version, printJson, human text), so ONE process-level listener here
131
+ * covers every write point: EPIPE says "the reader has all it needs",
132
+ * so stop quietly with code 0. Any other stream error is thrown again
133
+ * and still comes up as an uncaughtException, same as before. */
134
+ function exitQuietOnEpipe(stream) {
135
+ stream.on("error", (error) => {
136
+ if (error && error.code === "EPIPE")
137
+ process.exit(0);
138
+ throw error;
139
+ });
107
140
  }
108
141
  /** Top-level run wrapper matching src/cli.ts's catch shape byte-for-byte:
109
142
  * `cw: <message>\n` then, only when a hint matches, ` Try: <hint>\n` on
110
143
  * stderr; `process.exitCode = 1` (never a hard `process.exit`). */
111
144
  function main(argv = process.argv.slice(2)) {
145
+ exitQuietOnEpipe(process.stdout);
146
+ exitQuietOnEpipe(process.stderr);
112
147
  return runCli(argv).catch((error) => {
148
+ // On some platforms a broken-pipe write throws EPIPE in the write call
149
+ // itself (not as a stream 'error' event) — same broken pipe, same quiet
150
+ // exit 0, never a `cw: write EPIPE` line.
151
+ if (error?.code === "EPIPE")
152
+ return;
113
153
  const message = error instanceof Error ? error.message : String(error);
114
154
  process.stderr.write(`${(0, term_1.bold)("cw:", process.stderr)} ${(0, term_1.red)(message, process.stderr)}\n`);
115
155
  const hint = recoveryHint(message);
package/dist/cli/io.js CHANGED
@@ -16,6 +16,7 @@ exports.optionalArg = optionalArg;
16
16
  exports.printJson = printJson;
17
17
  exports.wantsJson = wantsJson;
18
18
  const help_1 = require("../core/format/help");
19
+ const safe_json_1 = require("../core/format/safe-json");
19
20
  const term_1 = require("../shell/term");
20
21
  /** Bold ONLY the fixed "Cool Workflow" header line of `formatHelp()`'s
21
22
  * plain text. */
@@ -34,9 +35,12 @@ function required(value, label) {
34
35
  function optionalArg(value) {
35
36
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
36
37
  }
37
- /** Machine payload to stdout (stdout = data; never colored, never chrome). */
38
+ /** Machine payload to stdout (stdout = data; never colored, never chrome).
39
+ * Byte-capped via safeJsonStringify — an aggregate result too large to be
40
+ * useful (or large enough to blow V8's string limit) prints a small
41
+ * overflow notice instead of hundreds of MB. */
38
42
  function printJson(value) {
39
- process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
43
+ process.stdout.write(`${(0, safe_json_1.safeJsonStringify)(value)}\n`);
40
44
  }
41
45
  /** True when the caller asked for JSON output (`--json` or `--format json`). */
42
46
  function wantsJson(options) {
@@ -103,7 +103,10 @@ function parseArgv(argv) {
103
103
  /** src/orchestrator.ts:842-851 — every top-level command name, used for
104
104
  * "did you mean". NOTE: this deliberately does NOT include "ledger" even
105
105
  * though the dispatcher handles it and formatHelp lists it — a known,
106
- * intentionally-preserved wart (see docs/rebuild/PLAN.md "Kept byte-for-byte"). */
106
+ * intentionally-preserved wart (see docs/rebuild/PLAN.md "Kept byte-for-byte").
107
+ * NOTE: "update" is gone from the old capture's list on purpose: no code
108
+ * was behind the verb in this build, so having it here made `cw update`
109
+ * say "Did you mean: update?" — a hint that points at itself. */
107
110
  exports.KNOWN_COMMANDS = new Set([
108
111
  "help", "list", "doctor", "info", "search", "man", "init", "quickstart",
109
112
  "plan", "status", "next", "dispatch", "result", "state", "commit", "report",
@@ -112,7 +115,8 @@ exports.KNOWN_COMMANDS = new Set([
112
115
  "run", "queue", "clones", "orphans", "history", "audit-run", "multi-agent",
113
116
  "topology", "summary", "blackboard", "coordinator", "metrics", "operator",
114
117
  "sched", "gc", "telemetry", "migration", "demo", "workbench", "approve",
115
- "reject", "comment", "handoff", "graph", "eval", "version", "update", "fix",
118
+ "reject", "comment", "handoff", "graph", "eval", "version", "fix",
119
+ "completion",
116
120
  ]);
117
121
  /** Levenshtein edit distance between two strings. */
118
122
  function levenshtein(a, b) {