cool-workflow 0.1.92 → 0.1.93

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 (73) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +104 -129
  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 +15 -2
  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 +47 -0
  12. package/dist/capability-registry.js +2 -0
  13. package/dist/cli/command-surface.js +165 -1352
  14. package/dist/cli/format.js +56 -0
  15. package/dist/cli/handlers/audit.js +82 -0
  16. package/dist/cli/handlers/blackboard.js +81 -0
  17. package/dist/cli/handlers/candidate.js +40 -0
  18. package/dist/cli/handlers/clones.js +34 -0
  19. package/dist/cli/handlers/collaboration.js +61 -0
  20. package/dist/cli/handlers/eval.js +40 -0
  21. package/dist/cli/handlers/maintenance.js +107 -0
  22. package/dist/cli/handlers/multi-agent.js +165 -0
  23. package/dist/cli/handlers/node.js +41 -0
  24. package/dist/cli/handlers/operational.js +155 -0
  25. package/dist/cli/handlers/operator.js +146 -0
  26. package/dist/cli/handlers/registry.js +68 -0
  27. package/dist/cli/handlers/run.js +153 -0
  28. package/dist/cli/handlers/scheduling.js +126 -0
  29. package/dist/cli/handlers/workbench.js +41 -0
  30. package/dist/cli/handlers/worker.js +45 -0
  31. package/dist/cli/io.js +27 -0
  32. package/dist/cli/run-summary.js +45 -0
  33. package/dist/commit.js +0 -5
  34. package/dist/execution-backend.js +0 -11
  35. package/dist/mcp/tool-call.js +2 -0
  36. package/dist/mcp/tool-definitions.js +8 -0
  37. package/dist/orchestrator/app-operations.js +205 -0
  38. package/dist/orchestrator.js +41 -153
  39. package/dist/state-explosion.js +0 -7
  40. package/dist/term.js +0 -18
  41. package/dist/validation.js +0 -21
  42. package/dist/version.js +1 -1
  43. package/docs/agent-delegation-drive.7.md +1 -1
  44. package/docs/cli-mcp-parity.7.md +17 -2
  45. package/docs/contract-migration-tooling.7.md +1 -1
  46. package/docs/control-plane-scheduling.7.md +1 -1
  47. package/docs/durable-state-and-locking.7.md +1 -1
  48. package/docs/evidence-adoption-reasoning-chain.7.md +1 -1
  49. package/docs/execution-backends.7.md +1 -1
  50. package/docs/mcp-app-surface.7.md +12 -0
  51. package/docs/multi-agent-cli-mcp-surface.7.md +1 -1
  52. package/docs/multi-agent-eval-replay-harness.7.md +1 -1
  53. package/docs/multi-agent-operator-ux.7.md +1 -1
  54. package/docs/node-snapshot-diff-replay.7.md +1 -1
  55. package/docs/observability-cost-accounting.7.md +1 -1
  56. package/docs/project-index.md +13 -3
  57. package/docs/real-execution-backends.7.md +1 -1
  58. package/docs/release-and-migration.7.md +1 -1
  59. package/docs/release-tooling.7.md +1 -1
  60. package/docs/run-registry-control-plane.7.md +25 -3
  61. package/docs/run-retention-reclamation.7.md +1 -1
  62. package/docs/state-explosion-management.7.md +1 -1
  63. package/docs/team-collaboration.7.md +1 -1
  64. package/docs/web-desktop-workbench.7.md +1 -1
  65. package/manifest/plugin.manifest.json +1 -1
  66. package/package.json +1 -1
  67. package/scripts/architecture-review-fast.js +19 -5
  68. package/scripts/bump-version.js +16 -0
  69. package/scripts/canonical-apps.js +4 -4
  70. package/scripts/dogfood-release.js +1 -1
  71. package/scripts/golden-path.js +4 -4
  72. package/scripts/parity-check.js +9 -1
  73. package/scripts/version-sync-check.js +5 -0
@@ -43,22 +43,29 @@ const node_child_process_1 = require("node:child_process");
43
43
  const readline = __importStar(require("node:readline"));
44
44
  const orchestrator_1 = require("../orchestrator");
45
45
  const capability_core_1 = require("../capability-core");
46
- const observability_1 = require("../observability");
47
- const telemetry_demo_1 = require("../telemetry-demo");
48
- const run_registry_1 = require("../run-registry");
49
- const daemon_1 = require("../daemon");
50
46
  const scheduler_1 = require("../scheduler");
51
47
  const triggers_1 = require("../triggers");
52
- const workbench_1 = require("../workbench");
53
- const workbench_host_1 = require("../workbench-host");
48
+ const io_1 = require("./io");
49
+ const run_summary_1 = require("./run-summary");
50
+ const audit_1 = require("./handlers/audit");
51
+ const candidate_1 = require("./handlers/candidate");
52
+ const operator_1 = require("./handlers/operator");
53
+ const registry_1 = require("./handlers/registry");
54
+ const multi_agent_1 = require("./handlers/multi-agent");
55
+ const run_1 = require("./handlers/run");
56
+ const collaboration_1 = require("./handlers/collaboration");
57
+ const blackboard_1 = require("./handlers/blackboard");
58
+ const eval_1 = require("./handlers/eval");
59
+ const node_1 = require("./handlers/node");
60
+ const maintenance_1 = require("./handlers/maintenance");
61
+ const operational_1 = require("./handlers/operational");
62
+ const scheduling_1 = require("./handlers/scheduling");
63
+ const worker_1 = require("./handlers/worker");
64
+ const clones_1 = require("./handlers/clones");
65
+ const workbench_1 = require("./handlers/workbench");
54
66
  const operator_ux_1 = require("../operator-ux");
55
- const multi_agent_operator_ux_1 = require("../multi-agent-operator-ux");
56
- const multi_agent_eval_1 = require("../multi-agent-eval");
57
- const state_explosion_1 = require("../state-explosion");
58
- const evidence_reasoning_1 = require("../evidence-reasoning");
59
67
  const doctor_1 = require("../doctor");
60
68
  const orchestrator_2 = require("../orchestrator");
61
- const reporter_1 = require("../reporter");
62
69
  const version_1 = require("../version");
63
70
  async function runCli(argv = process.argv.slice(2)) {
64
71
  const args = (0, orchestrator_1.parseArgv)(argv);
@@ -96,6 +103,13 @@ async function runCli(argv = process.argv.slice(2)) {
96
103
  process.env.CW_NO_COLOR = "1";
97
104
  if (args.options.full)
98
105
  process.env.CW_OUTPUT = "full";
106
+ // `cw <verb> --help` / `-h` -> per-command help (the verb's subcommands +
107
+ // one-line summaries), derived from the capability registry. Additive: the
108
+ // bare `cw` / `cw --help` top-level help is handled above.
109
+ if ((args.options.help || args.options.h) && args.command && !args.command.startsWith("-")) {
110
+ process.stdout.write((0, orchestrator_1.formatCommandHelp)(args.command) + "\n");
111
+ return;
112
+ }
99
113
  // Bare -q / --question -> redirect to quickstart (auto-detect repo/agent/app).
100
114
  // CONSUME the positional (shift) so the question never survives as positionals[0]
101
115
  // — otherwise the quickstart handler reads it as the app id ("Workflow app not found").
@@ -113,7 +127,11 @@ async function runCli(argv = process.argv.slice(2)) {
113
127
  const scheduler = new scheduler_1.Scheduler(String(args.options.cwd || process.cwd()));
114
128
  const triggers = new triggers_1.RoutineTriggerBridge(String(args.options.cwd || process.cwd()));
115
129
  switch (args.command) {
116
- case "help":
130
+ case "help": {
131
+ const [topic] = args.positionals;
132
+ process.stdout.write((topic ? (0, orchestrator_1.formatCommandHelp)(topic) : (0, orchestrator_1.formatHelp)()) + "\n");
133
+ return;
134
+ }
117
135
  case undefined:
118
136
  process.stdout.write((0, orchestrator_1.formatHelp)() + "\n");
119
137
  return;
@@ -141,7 +159,7 @@ async function runCli(argv = process.argv.slice(2)) {
141
159
  return;
142
160
  }
143
161
  case "list":
144
- printJson(runner.listWorkflows());
162
+ (0, io_1.printJson)(runner.listWorkflows());
145
163
  return;
146
164
  case "search": {
147
165
  const keyword = args.positionals.join(" ");
@@ -150,8 +168,8 @@ async function runCli(argv = process.argv.slice(2)) {
150
168
  const apps = runner.listApps();
151
169
  const lower = keyword.toLowerCase();
152
170
  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 }));
153
- if (wantsJson(args.options))
154
- printJson(results);
171
+ if ((0, io_1.wantsJson)(args.options))
172
+ (0, io_1.printJson)(results);
155
173
  else
156
174
  process.stdout.write(`${(0, orchestrator_2.formatSearchResults)(keyword, results)}\n`);
157
175
  return;
@@ -186,16 +204,16 @@ async function runCli(argv = process.argv.slice(2)) {
186
204
  if (!appId)
187
205
  throw new Error("Missing workflow app id.\n Tip: list apps with \"cw list\", then \"cw info <id>\" for details");
188
206
  const data = runner.showApp(appId);
189
- if (wantsJson(args.options))
190
- printJson(data);
207
+ if ((0, io_1.wantsJson)(args.options))
208
+ (0, io_1.printJson)(data);
191
209
  else
192
210
  process.stdout.write(`${(0, orchestrator_2.formatInfo)(appId, data)}\n`);
193
211
  return;
194
212
  }
195
213
  case "doctor": {
196
214
  const report = (0, doctor_1.runDoctor)(args.options, process.env, String(args.options.cwd || process.cwd()));
197
- if (wantsJson(args.options))
198
- printJson(report);
215
+ if ((0, io_1.wantsJson)(args.options))
216
+ (0, io_1.printJson)(report);
199
217
  else if (args.options.fix)
200
218
  process.stdout.write(`${(0, doctor_1.formatDoctorFixes)(report)}\n`);
201
219
  else
@@ -208,33 +226,33 @@ async function runCli(argv = process.argv.slice(2)) {
208
226
  const [workflowId] = args.positionals;
209
227
  if (!workflowId)
210
228
  throw new Error("Missing workflow id.\n Tip: create one with \"cw init my-workflow\" or list with \"cw list\"");
211
- printJson(runner.init(workflowId, args.options));
229
+ (0, io_1.printJson)(runner.init(workflowId, args.options));
212
230
  return;
213
231
  }
214
232
  case "app": {
215
233
  const [subcommand, appIdOrPath] = args.positionals;
216
234
  switch (subcommand) {
217
235
  case "list":
218
- printJson(runner.listApps());
236
+ (0, io_1.printJson)(runner.listApps());
219
237
  return;
220
238
  case "show":
221
- printJson(runner.showApp(required(appIdOrPath, "app id")));
239
+ (0, io_1.printJson)(runner.showApp((0, io_1.required)(appIdOrPath, "app id")));
222
240
  return;
223
241
  case "validate": {
224
- const result = runner.validateApp(required(appIdOrPath, "app path or id"));
225
- printJson(result);
242
+ const result = runner.validateApp((0, io_1.required)(appIdOrPath, "app path or id"));
243
+ (0, io_1.printJson)(result);
226
244
  if (!result.valid)
227
245
  process.exitCode = 1;
228
246
  return;
229
247
  }
230
248
  case "init":
231
- printJson(runner.initApp(required(appIdOrPath, "app id"), args.options));
249
+ (0, io_1.printJson)(runner.initApp((0, io_1.required)(appIdOrPath, "app id"), args.options));
232
250
  return;
233
251
  case "package":
234
- printJson(runner.packageApp(required(appIdOrPath, "app id"), args.options));
252
+ (0, io_1.printJson)(runner.packageApp((0, io_1.required)(appIdOrPath, "app id"), args.options));
235
253
  return;
236
254
  case "run":
237
- printJson((0, capability_core_1.appRun)(runner, { ...args.options, appId: required(appIdOrPath, "app id") }));
255
+ (0, io_1.printJson)((0, capability_core_1.appRun)(runner, { ...args.options, appId: (0, io_1.required)(appIdOrPath, "app id") }));
238
256
  return;
239
257
  default:
240
258
  throw new Error("Usage: cw.js app list|show|validate|init|package|run [app-id|path]");
@@ -247,18 +265,18 @@ async function runCli(argv = process.argv.slice(2)) {
247
265
  // DELEGATES worker execution to the operator's configured agent backend and
248
266
  // fails closed (status=blocked) when none is set. No new executor/scheduler.
249
267
  const [appId] = args.positionals;
250
- const runId = optionalArg(args.options.run) || optionalArg(args.options.runId);
268
+ const runId = (0, io_1.optionalArg)(args.options.run) || (0, io_1.optionalArg)(args.options.runId);
251
269
  await promptQuestion(args.options);
252
270
  const qs = (0, capability_core_1.quickstart)(runner, { ...args.options, ...(appId ? { appId } : {}), ...(runId ? { runId } : {}) });
253
- printJson(qs);
271
+ (0, io_1.printJson)(qs);
254
272
  const qr = qs;
255
273
  // Clean human summary on stderr (TTY-gated, inside the reporter). Suppressed under --json so
256
274
  // machine mode emits ONLY the stdout payload — no stderr chrome to parse around. The type
257
275
  // guard also skips --check/--preview results (no reportPath of their own). The summary is the
258
276
  // COMPACT findings table (re-parsed from each completed worker's cw:result), the report path,
259
277
  // and where the per-worker transcripts live — NOT the full prose (that's report.md/--full).
260
- if (!wantsJson(args.options) && typeof qr.runId === "string" && typeof qr.reportPath === "string") {
261
- emitRunSummary(runner, args.options, {
278
+ if (!(0, io_1.wantsJson)(args.options) && typeof qr.runId === "string" && typeof qr.reportPath === "string") {
279
+ (0, run_summary_1.emitRunSummary)(runner, args.options, {
262
280
  runId: qr.runId,
263
281
  reportPath: qr.reportPath,
264
282
  status: String(qr.status || ""),
@@ -283,41 +301,41 @@ async function runCli(argv = process.argv.slice(2)) {
283
301
  const [workflowId] = args.positionals;
284
302
  if (!workflowId)
285
303
  throw new Error("Missing workflow id.\n Tip: plan an architecture review with \"cw plan architecture-review\"");
286
- printJson((0, capability_core_1.planSummary)(runner, workflowId, args.options));
304
+ (0, io_1.printJson)((0, capability_core_1.planSummary)(runner, workflowId, args.options));
287
305
  return;
288
306
  }
289
307
  case "status":
290
308
  if (!args.positionals[0]) {
291
309
  const nextActions = (0, operator_ux_1.adviseNoRun)();
292
- if (wantsJson(args.options))
293
- printJson({ runId: null, nextActions });
310
+ if ((0, io_1.wantsJson)(args.options))
311
+ (0, io_1.printJson)({ runId: null, nextActions });
294
312
  else
295
313
  process.stdout.write(`No run selected\n\nNext Action\n${nextActions.map((action) => ` ${action.command}\n reason: ${action.reason}`).join("\n")}\n`);
296
314
  }
297
- else if (wantsJson(args.options))
298
- printJson(runner.status(args.positionals[0]));
315
+ else if ((0, io_1.wantsJson)(args.options))
316
+ (0, io_1.printJson)(runner.status(args.positionals[0]));
299
317
  else {
300
318
  const summary = runner.operatorStatus(args.positionals[0]);
301
319
  process.stdout.write(`${(args.options.summary || args.options.brief ? (0, operator_ux_1.formatOperatorSummary)(summary) : (0, operator_ux_1.formatOperatorStatus)(summary))}\n`);
302
320
  }
303
321
  return;
304
322
  case "next":
305
- printJson(runner.next(required(args.positionals[0], "run id"), args.options));
323
+ (0, io_1.printJson)(runner.next((0, io_1.required)(args.positionals[0], "run id"), args.options));
306
324
  return;
307
325
  case "dispatch":
308
- printJson(runner.dispatch(required(args.positionals[0], "run id"), args.options));
326
+ (0, io_1.printJson)(runner.dispatch((0, io_1.required)(args.positionals[0], "run id"), args.options));
309
327
  return;
310
328
  case "result": {
311
329
  const [runId, taskId, resultPath] = args.positionals;
312
- printJson(runner.recordResult(required(runId, "run id"), required(taskId, "task id"), required(resultPath, "result file"), args.options));
330
+ (0, io_1.printJson)(runner.recordResult((0, io_1.required)(runId, "run id"), (0, io_1.required)(taskId, "task id"), (0, io_1.required)(resultPath, "result file"), args.options));
313
331
  return;
314
332
  }
315
333
  case "state": {
316
334
  const [subcommand, runId] = args.positionals;
317
335
  switch (subcommand) {
318
336
  case "check": {
319
- const report = runner.checkState(required(runId, "run id"), args.options);
320
- printJson(report);
337
+ const report = runner.checkState((0, io_1.required)(runId, "run id"), args.options);
338
+ (0, io_1.printJson)(report);
321
339
  if (report.status === "unsupported")
322
340
  process.exitCode = 1;
323
341
  return;
@@ -328,1323 +346,132 @@ async function runCli(argv = process.argv.slice(2)) {
328
346
  }
329
347
  case "commit":
330
348
  if (args.positionals[0] === "summary") {
331
- const summary = runner.summarizeCommitRecords(required(args.positionals[1], "run id"));
332
- if (wantsJson(args.options))
333
- printJson(summary);
349
+ const summary = runner.summarizeCommitRecords((0, io_1.required)(args.positionals[1], "run id"));
350
+ if ((0, io_1.wantsJson)(args.options))
351
+ (0, io_1.printJson)(summary);
334
352
  else
335
353
  process.stdout.write(`${(0, operator_ux_1.formatCommitSummary)(summary)}\n`);
336
354
  return;
337
355
  }
338
- printJson(runner.commit(required(args.positionals[0], "run id"), args.options));
356
+ (0, io_1.printJson)(runner.commit((0, io_1.required)(args.positionals[0], "run id"), args.options));
339
357
  return;
340
- case "report": {
341
- // `report verify-bundle <path>` is the offline self-contained bundle verifier;
342
- // `report bundle <run-id>` exports a sealed bundle and self-verifies it;
343
- // every other `report <run-id>` form prints/inspects a local run's report.
344
- if (args.positionals[0] === "verify-bundle") {
345
- const result = (0, capability_core_1.runVerifyReportBundle)(runner, { ...args.options, archive: args.positionals[1] || args.options.archive || args.options.path || args.options.file || args.options.bundle });
346
- printJson(result);
347
- // Fail closed: a forged/edited/corrupt bundle verifies false — surface it
348
- // through the exit code so `cw report verify-bundle <file> && ship` cannot
349
- // pass on a lie. Mirrors run inspect-archive / telemetry verify.
350
- if (!result.ok)
351
- process.exitCode = 1;
352
- return;
353
- }
354
- if (args.positionals[0] === "bundle") {
355
- const result = (0, capability_core_1.reportBundle)(runner, required(args.positionals[1] || optionalArg(args.options.runId || args.options.run), "run id"), args.options);
356
- printJson(result);
357
- // Fail closed: never report a "bundle made" success if the artifact does not
358
- // self-verify — so `cw report bundle <run> && send-to-client` cannot ship an
359
- // unverifiable report (e.g. no trust key under --strict-signatures).
360
- if (!result.ok)
361
- process.exitCode = 1;
362
- return;
363
- }
364
- const runId = required(args.positionals[0], "run id");
365
- const report = runner.report(runId);
366
- if (wantsJson(args.options)) {
367
- printJson(report);
368
- }
369
- else if (args.options.show || args.options.summary) {
370
- process.stdout.write(`${(0, operator_ux_1.formatOperatorReport)(runner.operatorReport(runId))}\n`);
371
- process.stdout.write(`\n${(0, state_explosion_1.formatStateExplosionReport)(runner.stateExplosionReport(runId))}\n`);
372
- }
373
- else {
374
- process.stdout.write(`${report.path}\n`);
375
- }
358
+ case "report":
359
+ (0, operator_1.handleReport)(args, runner);
376
360
  return;
377
- }
378
- case "operator": {
379
- const [subcommand, runId] = args.positionals;
380
- switch (subcommand) {
381
- case "status":
382
- if (wantsJson(args.options))
383
- printJson(runner.operatorStatus(required(runId, "run id")));
384
- else {
385
- const summary = runner.operatorStatus(required(runId, "run id"));
386
- process.stdout.write(`${(args.options.summary || args.options.brief ? (0, operator_ux_1.formatOperatorSummary)(summary) : (0, operator_ux_1.formatOperatorStatus)(summary))}\n`);
387
- }
388
- return;
389
- case "report":
390
- if (wantsJson(args.options))
391
- printJson(runner.operatorReport(required(runId, "run id")));
392
- else
393
- process.stdout.write(`${(0, operator_ux_1.formatOperatorReport)(runner.operatorReport(required(runId, "run id")))}\n`);
394
- return;
395
- default:
396
- throw new Error("Usage: cw.js operator status|report <run-id> [--json]");
397
- }
398
- }
399
- case "graph": {
400
- const graph = runner.operatorGraph(required(args.positionals[0], "run id"));
401
- if (wantsJson(args.options))
402
- printJson(graph);
403
- else
404
- process.stdout.write(`${(0, operator_ux_1.formatOperatorGraph)(graph)}\n`);
361
+ case "operator":
362
+ (0, operator_1.handleOperator)(args, runner);
405
363
  return;
406
- }
407
- case "topology": {
408
- const [subcommand, first, second] = args.positionals;
409
- switch (subcommand) {
410
- case "list":
411
- printJson(runner.listTopologies());
412
- return;
413
- case "show":
414
- if (second)
415
- printJson(runner.showTopologyRun(required(first, "run id"), second));
416
- else
417
- printJson(runner.showTopology(required(first, "topology id")));
418
- return;
419
- case "validate": {
420
- const result = runner.validateTopology(required(first, "topology id"));
421
- printJson(result);
422
- if (!result.valid)
423
- process.exitCode = 1;
424
- return;
425
- }
426
- case "apply":
427
- printJson(runner.applyTopology(required(first, "run id"), required(second, "topology id"), args.options));
428
- return;
429
- case "summary": {
430
- const summary = runner.topologySummary(required(first, "run id"));
431
- if (wantsJson(args.options))
432
- printJson(summary);
433
- else
434
- process.stdout.write(`${(0, operator_ux_1.formatTopologySummary)(summary)}\n`);
435
- return;
436
- }
437
- case "graph": {
438
- const graph = runner.topologyGraph(required(first, "run id"));
439
- if (wantsJson(args.options))
440
- printJson(graph);
441
- else
442
- process.stdout.write(`${(0, operator_ux_1.formatOperatorGraph)({ runId: required(first, "run id"), nodes: graph.nodes, edges: graph.edges })}\n`);
443
- return;
444
- }
445
- default:
446
- throw new Error("Usage: cw.js topology list|show <topology-id>|show <run-id> <topology-run-id>|validate <topology-id>|apply <run-id> <topology-id>|summary <run-id>|graph <run-id>");
447
- }
448
- }
449
- case "summary": {
450
- const [subcommand, runId] = args.positionals;
451
- switch (subcommand) {
452
- case "refresh": {
453
- const index = runner.summaryRefresh(required(runId, "run id"), args.options);
454
- if (wantsJson(args.options))
455
- printJson(index);
456
- else
457
- process.stdout.write(`${(0, state_explosion_1.formatStateExplosionReport)(runner.summaryShow(required(runId, "run id")))}\n`);
458
- return;
459
- }
460
- case "show": {
461
- const report = runner.summaryShow(required(runId, "run id"));
462
- if (wantsJson(args.options))
463
- printJson(report);
464
- else
465
- process.stdout.write(`${(0, state_explosion_1.formatStateExplosionReport)(report)}\n`);
466
- return;
467
- }
468
- default:
469
- throw new Error("Usage: cw.js summary refresh|show <run-id> [--json]");
470
- }
471
- }
472
- case "multi-agent": {
473
- const [subcommand, runId, id] = args.positionals;
474
- switch (subcommand) {
475
- case "status":
476
- if (wantsJson(args.options))
477
- printJson(runner.hostMultiAgentStatus(required(runId, "run id")));
478
- else
479
- process.stdout.write(`${(0, multi_agent_operator_ux_1.formatMultiAgentOperatorStatus)(runner.multiAgentOperatorStatus(required(runId, "run id")))}\n`);
480
- return;
481
- case "step":
482
- printJson(runner.hostMultiAgentStep(required(runId, "run id"), args.options));
483
- return;
484
- case "blackboard":
485
- printJson(runner.hostMultiAgentBlackboard(required(runId, "run id"), id, args.options));
486
- return;
487
- case "score":
488
- printJson(runner.hostMultiAgentScore(required(runId, "run id"), { ...args.options, candidate: args.options.candidate || args.options.candidateId || id }));
489
- return;
490
- case "select":
491
- printJson(runner.hostMultiAgentSelect(required(runId, "run id"), { ...args.options, candidate: args.options.candidate || args.options.candidateId || id }));
492
- return;
493
- case "summary": {
494
- const summary = runner.multiAgentSummary(required(runId, "run id"));
495
- if (wantsJson(args.options))
496
- printJson(summary);
497
- else
498
- process.stdout.write(`${(0, operator_ux_1.formatMultiAgentSummary)(summary)}\n`);
499
- return;
500
- }
501
- case "summarize": {
502
- const report = runner.multiAgentSummarize(required(runId, "run id"));
503
- if (wantsJson(args.options))
504
- printJson(report);
505
- else
506
- process.stdout.write(`${(0, state_explosion_1.formatStateExplosionReport)(report)}\n`);
507
- return;
508
- }
509
- case "graph": {
510
- const wantsView = args.options.view || args.options.focus || args.options.depth;
511
- if (wantsView) {
512
- const graph = runner.multiAgentGraphView(required(runId, "run id"), args.options);
513
- if (wantsJson(args.options))
514
- printJson(graph);
515
- else
516
- process.stdout.write(`${(0, state_explosion_1.formatCompactGraph)(graph)}\n`);
517
- return;
518
- }
519
- const graph = runner.multiAgentOperatorGraph(required(runId, "run id"));
520
- if (wantsJson(args.options))
521
- printJson(graph);
522
- else
523
- process.stdout.write(`${(0, operator_ux_1.formatOperatorGraph)({ runId: required(runId, "run id"), nodes: graph.nodes, edges: graph.edges })}\n`);
524
- return;
525
- }
526
- case "dependencies": {
527
- const rows = runner.multiAgentDependencies(required(runId, "run id"));
528
- if (wantsJson(args.options))
529
- printJson(rows);
530
- else
531
- process.stdout.write(`${(0, multi_agent_operator_ux_1.formatMultiAgentDependencies)(rows)}\n`);
532
- return;
533
- }
534
- case "failures": {
535
- const rows = runner.multiAgentFailures(required(runId, "run id"));
536
- if (wantsJson(args.options))
537
- printJson(rows);
538
- else
539
- process.stdout.write(`${(0, multi_agent_operator_ux_1.formatMultiAgentFailures)(rows)}\n`);
540
- return;
541
- }
542
- case "evidence": {
543
- const rows = runner.multiAgentEvidence(required(runId, "run id"));
544
- if (wantsJson(args.options))
545
- printJson(rows);
546
- else
547
- process.stdout.write(`${(0, multi_agent_operator_ux_1.formatMultiAgentEvidence)(rows)}\n`);
548
- return;
549
- }
550
- case "reasoning": {
551
- if (args.options.refresh && !args.options.evidence && !args.options.evidenceId) {
552
- const index = runner.multiAgentReasoningRefresh(required(runId, "run id"));
553
- printJson(index);
554
- return;
555
- }
556
- const report = runner.multiAgentReasoning(required(runId, "run id"), { ...args.options, evidence: args.options.evidence || args.options.evidenceId || id });
557
- if (wantsJson(args.options))
558
- printJson(report);
559
- else
560
- process.stdout.write(`${(0, evidence_reasoning_1.formatEvidenceReasoningReport)(report)}\n`);
561
- return;
562
- }
563
- case "run":
564
- if (!runId ||
565
- args.options.topology ||
566
- args.options.topologyId ||
567
- args.options.app ||
568
- args.options.appId ||
569
- args.options.workflow ||
570
- args.options.workflowId) {
571
- printJson(runner.hostMultiAgentRun(runId, args.options));
572
- return;
573
- }
574
- if (id && !args.options.id && !args.options.status)
575
- printJson(runner.showMultiAgentRun(required(runId, "run id"), id));
576
- else if (id && args.options.status)
577
- printJson(runner.transitionMultiAgentRun(required(runId, "run id"), id, args.options));
578
- else
579
- printJson(runner.createMultiAgentRun(required(runId, "run id"), args.options));
580
- return;
581
- case "show":
582
- printJson(runner.showMultiAgentRun(required(runId, "run id"), required(id, "multi-agent run id")));
583
- return;
584
- case "role":
585
- if (id && !args.options.id && !args.options["multi-agent-run"] && !args.options.multiAgentRun && !args.options.multiAgentRunId) {
586
- printJson(runner.showAgentRole(required(runId, "run id"), id));
587
- }
588
- else {
589
- printJson(runner.createAgentRole(required(runId, "run id"), { ...args.options, id: args.options.id || id }));
590
- }
591
- return;
592
- case "group":
593
- if (id && !args.options.id && !args.options["multi-agent-run"] && !args.options.multiAgentRun && !args.options.multiAgentRunId) {
594
- printJson(runner.showAgentGroup(required(runId, "run id"), id));
595
- }
596
- else {
597
- printJson(runner.createAgentGroup(required(runId, "run id"), { ...args.options, id: args.options.id || id }));
598
- }
599
- return;
600
- case "membership":
601
- if (id && !args.options.id && !args.options.group && !args.options.groupId && !args.options["multi-agent-group"]) {
602
- printJson(runner.showAgentMembership(required(runId, "run id"), id));
603
- }
604
- else {
605
- printJson(runner.assignAgentMembership(required(runId, "run id"), { ...args.options, id: args.options.id || id }));
606
- }
607
- return;
608
- case "fanout":
609
- if (id && !args.options.id && !args.options.group && !args.options.groupId && !args.options["multi-agent-group"]) {
610
- printJson(runner.showAgentFanout(required(runId, "run id"), id));
611
- }
612
- else {
613
- printJson(runner.createAgentFanout(required(runId, "run id"), { ...args.options, id: args.options.id || id }));
614
- }
615
- return;
616
- case "fanin":
617
- if (id && !args.options.id && !args.options.group && !args.options.groupId && !args.options["multi-agent-group"] && !args.options.fanout) {
618
- printJson(runner.showAgentFanin(required(runId, "run id"), id));
619
- }
620
- else {
621
- printJson(runner.collectAgentFanin(required(runId, "run id"), { ...args.options, id: args.options.id || id }));
622
- }
623
- return;
624
- default:
625
- throw new Error("Usage: cw.js multi-agent run|status|step|blackboard|score|select|summary|summarize|graph|dependencies|failures|evidence|reasoning|show|role|group|membership|fanout|fanin <run-id> [id]");
626
- }
627
- }
628
- case "eval": {
629
- const [subcommand, first, second] = args.positionals;
630
- let result;
631
- switch (subcommand) {
632
- case "snapshot":
633
- result = runner.evalSnapshot(required(first, "run id"), args.options);
634
- break;
635
- case "replay":
636
- result = runner.evalReplay(required(first, "snapshot id or path"), args.options);
637
- break;
638
- case "compare":
639
- result = runner.evalCompare(required(first, "baseline id or path"), required(second, "replay id or path"));
640
- break;
641
- case "score":
642
- result = runner.evalScore(required(first, "replay id or path"));
643
- break;
644
- case "gate":
645
- result = runner.evalGate(required(first, "suite id or path"));
646
- if (!wantsJson(args.options) && result.status === "fail")
647
- process.exitCode = 1;
648
- break;
649
- case "report":
650
- result = runner.evalReport(required(first, "replay id or path"));
651
- break;
652
- default:
653
- throw new Error("Usage: cw.js eval snapshot <run-id> --id <snapshot-id> | replay <snapshot-id-or-path> | compare <baseline-id-or-path> <replay-id-or-path> | score <replay-id-or-path> | gate <suite-id-or-path> | report <replay-id-or-path>");
654
- }
655
- if (wantsJson(args.options))
656
- printJson(result);
657
- else
658
- process.stdout.write(`${(0, multi_agent_eval_1.formatMultiAgentEval)(result)}\n`);
659
- if (subcommand === "gate" && result.status === "fail")
660
- process.exitCode = 1;
364
+ case "graph":
365
+ (0, operator_1.handleGraph)(args, runner);
366
+ return;
367
+ case "topology":
368
+ (0, operator_1.handleTopology)(args, runner);
369
+ return;
370
+ case "summary":
371
+ (0, operator_1.handleSummary)(args, runner);
372
+ return;
373
+ case "multi-agent":
374
+ (0, multi_agent_1.handleMultiAgent)(args, runner);
375
+ return;
376
+ case "eval":
377
+ (0, eval_1.handleEval)(args, runner);
378
+ return;
379
+ case "blackboard":
380
+ (0, blackboard_1.handleBlackboard)(args, runner);
381
+ return;
382
+ case "coordinator":
383
+ (0, blackboard_1.handleCoordinator)(args, runner);
384
+ return;
385
+ case "sandbox":
386
+ (0, operational_1.handleSandbox)(args, runner);
387
+ return;
388
+ case "backend":
389
+ (0, operational_1.handleBackend)(args, runner);
390
+ return;
391
+ case "contract":
392
+ (0, operational_1.handleContract)(args, runner);
393
+ return;
394
+ case "node":
395
+ (0, node_1.handleNode)(args, runner);
396
+ return;
397
+ case "migration":
398
+ (0, operational_1.handleMigration)(args, runner);
399
+ return;
400
+ case "feedback":
401
+ (0, operational_1.handleFeedback)(args, runner);
402
+ return;
403
+ case "worker":
404
+ (0, worker_1.handleWorker)(args, runner);
405
+ return;
406
+ case "audit":
407
+ (0, audit_1.handleAudit)(args, runner);
408
+ return;
409
+ case "candidate":
410
+ (0, candidate_1.handleCandidate)(args, runner);
661
411
  return;
662
- }
663
- case "blackboard": {
664
- const [subcommand, action, runId] = args.positionals;
665
- switch (subcommand) {
666
- case "summary":
667
- printJson(runner.blackboardSummary(required(action, "run id"), args.options));
668
- return;
669
- case "summarize": {
670
- const digest = runner.blackboardSummarize(required(action, "run id"), args.options);
671
- if (wantsJson(args.options))
672
- printJson(digest);
673
- else
674
- process.stdout.write(`${(0, state_explosion_1.formatBlackboardDigest)(digest)}\n`);
675
- return;
676
- }
677
- case "graph":
678
- printJson(runner.blackboardGraph(required(action, "run id")));
679
- return;
680
- case "resolve":
681
- printJson(runner.resolveRunBlackboard(required(action, "run id"), args.options));
682
- return;
683
- case "topic":
684
- if (action === "create") {
685
- printJson(runner.createBlackboardTopic(required(runId, "run id"), args.options));
686
- return;
687
- }
688
- break;
689
- case "message":
690
- if (action === "post") {
691
- printJson(runner.postBlackboardMessage(required(runId, "run id"), args.options));
692
- return;
693
- }
694
- if (action === "list") {
695
- printJson(runner.listBlackboardMessages(required(runId, "run id"), args.options));
696
- return;
697
- }
698
- break;
699
- case "context":
700
- if (action === "put") {
701
- printJson(runner.putBlackboardContext(required(runId, "run id"), args.options));
702
- return;
703
- }
704
- break;
705
- case "artifact":
706
- if (action === "add") {
707
- printJson(runner.addBlackboardArtifact(required(runId, "run id"), args.options));
708
- return;
709
- }
710
- if (action === "list") {
711
- printJson(runner.listBlackboardArtifacts(required(runId, "run id"), args.options));
712
- return;
713
- }
714
- break;
715
- case "snapshot":
716
- printJson(runner.snapshotBlackboard(required(action, "run id"), args.options));
717
- return;
718
- default:
719
- break;
720
- }
721
- throw new Error("Usage: cw.js blackboard summary|summarize|graph|resolve <run-id> | topic create <run-id> | message post|list <run-id> | context put <run-id> | artifact add|list <run-id> | snapshot <run-id>");
722
- }
723
- case "coordinator": {
724
- const [subcommand, runId] = args.positionals;
725
- switch (subcommand) {
726
- case "summary":
727
- printJson(runner.coordinatorSummary(required(runId, "run id"), args.options));
728
- return;
729
- case "decision":
730
- printJson(runner.recordCoordinatorDecision(required(runId, "run id"), args.options));
731
- return;
732
- default:
733
- throw new Error("Usage: cw.js coordinator summary <run-id> | coordinator decision <run-id> --kind <kind> --outcome <outcome> --reason TEXT");
734
- }
735
- }
736
- case "sandbox": {
737
- const [subcommand, profileIdOrFile] = args.positionals;
738
- switch (subcommand) {
739
- case "list":
740
- printJson(runner.listSandboxProfiles(args.options));
741
- return;
742
- case "show":
743
- printJson(runner.showSandboxProfile(required(profileIdOrFile, "profile id"), args.options));
744
- return;
745
- case "validate": {
746
- const result = runner.validateSandboxProfile(required(profileIdOrFile, "profile file"), args.options);
747
- printJson(result);
748
- if (!result.valid)
749
- process.exitCode = 1;
750
- return;
751
- }
752
- case "choose":
753
- case "resolve":
754
- printJson((0, capability_core_1.sandboxChoose)(runner, { ...args.options, profileId: profileIdOrFile || args.options.profileId }));
755
- return;
756
- default:
757
- throw new Error("Usage: cw.js sandbox list|show|validate|choose|resolve [profile-id|profile-file]");
758
- }
759
- }
760
- case "backend": {
761
- const [subcommand, backendId] = args.positionals;
762
- switch (subcommand) {
763
- case "list":
764
- printJson(runner.listBackends(args.options));
765
- return;
766
- case "show":
767
- printJson(runner.showBackend(required(backendId, "backend id"), args.options));
768
- return;
769
- case "probe":
770
- printJson(runner.probeBackend(backendId, args.options));
771
- return;
772
- case "agent": {
773
- // `backend agent config [show]` = read-only; `backend agent config set ...` = mutating.
774
- const [, , action] = args.positionals;
775
- if (action === "set") {
776
- printJson((0, capability_core_1.backendAgentConfigSet)(args.options));
777
- return;
778
- }
779
- printJson((0, capability_core_1.backendAgentConfigShow)(args.options));
780
- return;
781
- }
782
- default:
783
- throw new Error("Usage: cw.js backend list|show|probe [backend-id] | cw.js backend agent config [show|set] [--agent-command ... --agent-endpoint ... --agent-model ...]");
784
- }
785
- }
786
- case "contract": {
787
- const [subcommand, runId, contractId] = args.positionals;
788
- switch (subcommand) {
789
- case "show":
790
- printJson(runner.showContract(required(runId, "run id"), contractId));
791
- return;
792
- default:
793
- throw new Error("Usage: cw.js contract show <run-id> [contract-id]");
794
- }
795
- }
796
- case "node": {
797
- const [subcommand, runId, nodeId] = args.positionals;
798
- switch (subcommand) {
799
- case "list":
800
- printJson(runner.listNodes(required(runId, "run id")));
801
- return;
802
- case "show":
803
- printJson(runner.showNode(required(runId, "run id"), required(nodeId, "node id")));
804
- return;
805
- case "graph":
806
- if (wantsJson(args.options))
807
- printJson(runner.graphNodes(required(runId, "run id")));
808
- else
809
- process.stdout.write(`${(0, operator_ux_1.formatOperatorGraph)(runner.operatorGraph(required(runId, "run id")))}\n`);
810
- return;
811
- case "snapshot":
812
- printJson(runner.nodeSnapshot(required(runId, "run id"), required(nodeId, "node id")));
813
- return;
814
- case "diff":
815
- printJson(runner.nodeDiff(required(runId, "run id"), required(nodeId, "baseline snapshot id"), required(args.positionals[3], "candidate snapshot id")));
816
- return;
817
- case "replay":
818
- printJson(runner.nodeReplay(required(runId, "run id"), required(nodeId, "snapshot id")));
819
- return;
820
- case "verify": {
821
- const verdict = runner.nodeReplayVerify(required(runId, "run id"), required(nodeId, "replay id"));
822
- printJson(verdict);
823
- if (!verdict.pass)
824
- process.exitCode = 1;
825
- return;
826
- }
827
- default:
828
- throw new Error("Usage: cw.js node list|show|graph|snapshot|diff|replay|verify <run-id> [node-id|snapshot-id|replay-id]");
829
- }
830
- }
831
- case "migration": {
832
- const [subcommand, target] = args.positionals;
833
- switch (subcommand) {
834
- case "list":
835
- printJson(runner.migrationList());
836
- return;
837
- case "check": {
838
- const report = runner.migrationCheck(required(target, "target (run-id or state/app file)"), args.options);
839
- printJson(report);
840
- if (report.status === "unsupported")
841
- process.exitCode = 1;
842
- return;
843
- }
844
- case "prove": {
845
- const proof = runner.migrationProve(required(target, "target (run-id or state/app file)"), args.options);
846
- printJson(proof);
847
- if (!proof.pass)
848
- process.exitCode = 1;
849
- return;
850
- }
851
- default:
852
- throw new Error("Usage: cw.js migration list|check|prove [target] [--contract run-state|workflow-app]");
853
- }
854
- }
855
- case "feedback": {
856
- const [subcommand, runId, feedbackId] = args.positionals;
857
- switch (subcommand) {
858
- case "list":
859
- printJson(runner.listFeedback(required(runId, "run id"), args.options));
860
- return;
861
- case "show":
862
- printJson(runner.showFeedback(required(runId, "run id"), required(feedbackId, "feedback id")));
863
- return;
864
- case "collect":
865
- printJson(runner.collectFeedback(required(runId, "run id")));
866
- return;
867
- case "summary": {
868
- const summary = runner.summarizeFeedbackRecords(required(runId, "run id"));
869
- if (wantsJson(args.options))
870
- printJson(summary);
871
- else
872
- process.stdout.write(`${(0, operator_ux_1.formatFeedbackSummary)(summary)}\n`);
873
- return;
874
- }
875
- case "task":
876
- printJson(runner.createFeedbackTask(required(runId, "run id"), required(feedbackId, "feedback id"), args.options));
877
- return;
878
- case "resolve":
879
- printJson(runner.resolveFeedback(required(runId, "run id"), required(feedbackId, "feedback id"), args.options));
880
- return;
881
- default:
882
- throw new Error("Usage: cw.js feedback list|show|summary|collect|task|resolve <run-id> [feedback-id]");
883
- }
884
- }
885
- case "worker": {
886
- const [subcommand, runId, workerId, resultPath] = args.positionals;
887
- switch (subcommand) {
888
- case "list":
889
- printJson(runner.listWorkers(required(runId, "run id"), args.options));
890
- return;
891
- case "summary": {
892
- const summary = runner.summarizeWorkerRecords(required(runId, "run id"));
893
- if (wantsJson(args.options))
894
- printJson(summary);
895
- else
896
- process.stdout.write(`${(0, operator_ux_1.formatWorkerSummary)(summary)}\n`);
897
- return;
898
- }
899
- case "show":
900
- printJson(runner.showWorker(required(runId, "run id"), required(workerId, "worker id")));
901
- return;
902
- case "manifest":
903
- printJson(runner.showWorkerManifest(required(runId, "run id"), required(workerId, "worker id")));
904
- return;
905
- case "output":
906
- printJson(runner.recordWorkerOutput(required(runId, "run id"), required(workerId, "worker id"), required(resultPath, "result file"), args.options));
907
- return;
908
- case "fail":
909
- printJson(runner.recordWorkerFailure(required(runId, "run id"), required(workerId, "worker id"), String(args.options.message || required(resultPath, "failure message")), args.options));
910
- return;
911
- case "validate": {
912
- // Non-null = a boundary violation: a validate verb must report an invalid
913
- // verdict through its exit code, not just print it and exit 0.
914
- const violation = runner.validateWorker(required(runId, "run id"), required(workerId, "worker id"), resultPath);
915
- printJson(violation);
916
- if (violation)
917
- process.exitCode = 1;
918
- return;
919
- }
920
- default:
921
- throw new Error("Usage: cw.js worker list|summary|show|manifest|output|fail|validate <run-id> [worker-id] [result-file]");
922
- }
923
- }
924
- case "audit": {
925
- const [subcommand, runId, id] = args.positionals;
926
- switch (subcommand) {
927
- case "summary":
928
- printJson(runner.auditSummary(required(runId, "run id")));
929
- return;
930
- case "verify": {
931
- const result = (0, capability_core_1.auditVerify)(runner, { ...args.options, runId: required(runId, "run id") });
932
- printJson(result);
933
- // Fail-closed: any unverified chain exits non-zero so `cw audit verify
934
- // <run> && deploy` stops — mirrors the telemetry-verify guard. verifyTrustAudit
935
- // returns verified:true for a truly absent/empty chain (nothing to prove),
936
- // so this stays exit 0 there; a FULLY-corrupt log reports present:false but
937
- // verified:false (corruptLines>0) and must NOT be conflated with absent — the
938
- // earlier `present && ...` guard let that severe tamper escape (exit 0).
939
- if (!result.verified)
940
- process.exitCode = 1;
941
- return;
942
- }
943
- case "worker":
944
- printJson(runner.workerAudit(required(runId, "run id"), required(id, "worker id")));
945
- return;
946
- case "provenance":
947
- printJson(runner.evidenceProvenance(required(runId, "run id"), args.options));
948
- return;
949
- case "multi-agent": {
950
- const view = runner.auditMultiAgent(required(runId, "run id"));
951
- if (wantsJson(args.options))
952
- printJson(view);
953
- else
954
- process.stdout.write(`${(0, operator_ux_1.formatMultiAgentTrustAudit)(view)}\n`);
955
- return;
956
- }
957
- case "policy": {
958
- const view = runner.auditPolicy(required(runId, "run id"));
959
- if (wantsJson(args.options))
960
- printJson(view);
961
- else
962
- process.stdout.write(`${(0, operator_ux_1.formatMultiAgentTrustAudit)(view)}\n`);
963
- return;
964
- }
965
- case "role": {
966
- const view = runner.auditRole(required(runId, "run id"), required(id, "role id"));
967
- if (wantsJson(args.options))
968
- printJson(view);
969
- else
970
- process.stdout.write(`${(0, operator_ux_1.formatMultiAgentTrustAudit)(view)}\n`);
971
- return;
972
- }
973
- case "blackboard": {
974
- const view = runner.auditBlackboard(required(runId, "run id"));
975
- if (wantsJson(args.options))
976
- printJson(view);
977
- else
978
- process.stdout.write(`${(0, operator_ux_1.formatMultiAgentTrustAudit)(view)}\n`);
979
- return;
980
- }
981
- case "judge": {
982
- const view = runner.auditJudge(required(runId, "run id"));
983
- if (wantsJson(args.options))
984
- printJson(view);
985
- else
986
- process.stdout.write(`${(0, operator_ux_1.formatMultiAgentTrustAudit)(view)}\n`);
987
- return;
988
- }
989
- case "attest":
990
- printJson(runner.recordAuditAttestation(required(runId, "run id"), args.options));
991
- return;
992
- case "decision":
993
- printJson(runner.recordAuditDecision(required(runId, "run id"), required(id, "worker id"), args.options));
994
- return;
995
- default:
996
- throw new Error("Usage: cw.js audit summary|worker|provenance|multi-agent|policy|role|blackboard|judge|attest|decision <run-id> [worker-id|role-id]");
997
- }
998
- }
999
- case "candidate": {
1000
- const [subcommand, runId, candidateId, reason] = args.positionals;
1001
- switch (subcommand) {
1002
- case "list":
1003
- printJson(runner.listCandidates(required(runId, "run id"), args.options));
1004
- return;
1005
- case "show":
1006
- printJson(runner.showCandidate(required(runId, "run id"), required(candidateId, "candidate id")));
1007
- return;
1008
- case "register":
1009
- printJson(runner.registerCandidate(required(runId, "run id"), args.options));
1010
- return;
1011
- case "score":
1012
- printJson(runner.scoreCandidate(required(runId, "run id"), required(candidateId, "candidate id"), args.options));
1013
- return;
1014
- case "rank":
1015
- printJson(runner.rankCandidates(required(runId, "run id"), args.options));
1016
- return;
1017
- case "select":
1018
- printJson(runner.selectCandidate(required(runId, "run id"), required(candidateId, "candidate id"), args.options));
1019
- return;
1020
- case "reject":
1021
- printJson(runner.rejectCandidate(required(runId, "run id"), required(candidateId, "candidate id"), String(args.options.reason || args.options.message || reason || "rejected")));
1022
- return;
1023
- case "summary":
1024
- if (wantsJson(args.options))
1025
- printJson(runner.summarizeCandidateOperatorRecords(required(runId, "run id")));
1026
- else
1027
- process.stdout.write(`${(0, operator_ux_1.formatCandidateSummary)(runner.summarizeCandidateOperatorRecords(required(runId, "run id")))}\n`);
1028
- return;
1029
- default:
1030
- throw new Error("Usage: cw.js candidate list|show|register|score|rank|select|reject|summary <run-id> [candidate-id]");
1031
- }
1032
- }
1033
412
  // ---- Team Collaboration (v0.1.32) ------------------------------------
1034
- case "approve": {
1035
- const [targetKind, runId, targetId] = args.positionals;
1036
- printJson(runner.collaborationApprove(required(runId, "run id"), required(targetKind, "target kind (candidate|commit|selection|run|task|node)"), required(targetId, "target id"), args.options));
413
+ case "approve":
414
+ (0, collaboration_1.handleApprove)(args, runner);
1037
415
  return;
1038
- }
1039
- case "reject": {
1040
- const [targetKind, runId, targetId] = args.positionals;
1041
- printJson(runner.collaborationReject(required(runId, "run id"), required(targetKind, "target kind (candidate|commit|selection|run|task|node)"), required(targetId, "target id"), args.options));
416
+ case "reject":
417
+ (0, collaboration_1.handleReject)(args, runner);
1042
418
  return;
1043
- }
1044
- case "comment": {
1045
- const [subcommand, ...rest] = args.positionals;
1046
- if (subcommand === "add") {
1047
- const [targetKind, runId, targetId] = rest;
1048
- printJson(runner.collaborationComment(required(runId, "run id"), required(targetKind, "target kind"), required(targetId, "target id"), args.options));
1049
- return;
1050
- }
1051
- if (subcommand === "list") {
1052
- const result = runner.collaborationCommentList(required(rest[0], "run id"), args.options);
1053
- if (wantsJson(args.options))
1054
- printJson(result);
1055
- else
1056
- process.stdout.write(`${runner.formatCommentList(result.comments)}\n`);
1057
- return;
1058
- }
1059
- throw new Error("Usage: cw.js comment add <kind> <run-id> <target-id> --body <text> | comment list <run-id> [--json]");
1060
- }
1061
- case "handoff": {
1062
- const [targetKind, runId, targetIdRaw] = args.positionals;
1063
- const kind = required(targetKind, "target kind (run|task|candidate|commit|node)");
1064
- const rid = required(runId, "run id");
1065
- const targetId = targetIdRaw || (kind === "run" ? rid : undefined);
1066
- printJson(runner.collaborationHandoff(rid, kind, required(targetId, "target id"), args.options));
419
+ case "comment":
420
+ (0, collaboration_1.handleComment)(args, runner);
421
+ return;
422
+ case "handoff":
423
+ (0, collaboration_1.handleHandoff)(args, runner);
424
+ return;
425
+ case "review":
426
+ (0, collaboration_1.handleReview)(args, runner);
1067
427
  return;
1068
- }
1069
- case "review": {
1070
- const [subcommand, runId] = args.positionals;
1071
- if (subcommand === "status") {
1072
- const report = runner.reviewStatus(required(runId, "run id"), args.options);
1073
- if (wantsJson(args.options))
1074
- printJson(report);
1075
- else
1076
- process.stdout.write(`${runner.formatReviewStatus(report)}\n`);
1077
- return;
1078
- }
1079
- if (subcommand === "policy") {
1080
- printJson(runner.reviewPolicy(required(runId, "run id"), args.options));
1081
- return;
1082
- }
1083
- throw new Error("Usage: cw.js review status <run-id> [--json] | review policy <run-id> --required-approvals N --authorized-roles a,b --applies-to commit,selection");
1084
- }
1085
428
  case "loop": {
1086
- printJson(scheduler.create({ ...args.options, kind: "loop" }));
429
+ (0, io_1.printJson)(scheduler.create({ ...args.options, kind: "loop" }));
1087
430
  return;
1088
431
  }
1089
- case "schedule": {
1090
- const [subcommand, id] = args.positionals;
1091
- switch (subcommand) {
1092
- case "create":
1093
- printJson(scheduler.create(args.options));
1094
- return;
1095
- case "list":
1096
- printJson(scheduler.list(args.options.status ? String(args.options.status) : undefined));
1097
- return;
1098
- case "delete":
1099
- printJson(scheduler.delete(required(id, "schedule id")));
1100
- return;
1101
- case "due":
1102
- printJson(scheduler.due());
1103
- return;
1104
- case "complete":
1105
- printJson(scheduler.complete(required(id, "schedule id"), args.options));
1106
- return;
1107
- case "pause":
1108
- printJson(scheduler.pause(required(id, "schedule id")));
1109
- return;
1110
- case "resume":
1111
- printJson(scheduler.resume(required(id, "schedule id")));
1112
- return;
1113
- case "run-now":
1114
- printJson(scheduler.runNow(required(id, "schedule id")));
1115
- return;
1116
- case "history":
1117
- printJson(scheduler.history(id));
1118
- return;
1119
- case "daemon": {
1120
- const daemon = new daemon_1.DesktopSchedulerDaemon({
1121
- cwd: String(args.options.cwd || process.cwd()),
1122
- intervalSeconds: Number(args.options.intervalSeconds || args.options.interval || 60)
1123
- });
1124
- if (args.options.once) {
1125
- printJson(daemon.tick());
1126
- return;
1127
- }
1128
- await daemon.run();
1129
- return;
1130
- }
1131
- default:
1132
- throw new Error("Usage: cw.js schedule create|list|delete|due|complete|pause|resume|run-now|history|daemon");
1133
- }
1134
- }
1135
- case "routine": {
1136
- const [subcommand, idOrKind, payloadPath] = args.positionals;
1137
- switch (subcommand) {
1138
- case "create":
1139
- printJson(triggers.create(args.options));
1140
- return;
1141
- case "list":
1142
- printJson(triggers.list(args.options.kind ? String(args.options.kind) : undefined));
1143
- return;
1144
- case "delete":
1145
- printJson(triggers.delete(required(idOrKind, "trigger id")));
1146
- return;
1147
- case "fire": {
1148
- const kind = required(idOrKind, "trigger kind");
1149
- const payload = payloadPath ? JSON.parse(node_fs_1.default.readFileSync(payloadPath, "utf8")) : args.options;
1150
- printJson(triggers.fire(kind, payload));
1151
- return;
1152
- }
1153
- case "events":
1154
- printJson(triggers.events(idOrKind));
1155
- return;
1156
- default:
1157
- throw new Error("Usage: cw.js routine create|list|delete|fire|events");
1158
- }
1159
- }
1160
- case "registry": {
1161
- const registry = (0, capability_core_1.runRegistryFor)(args.options, runner);
1162
- const [subcommand] = args.positionals;
1163
- switch (subcommand) {
1164
- case "refresh": {
1165
- const report = (0, capability_core_1.runRegistryRefresh)(registry, args.options);
1166
- if (wantsJson(args.options))
1167
- printJson(report);
1168
- else
1169
- process.stdout.write(`${(0, run_registry_1.formatRegistryReport)(report)}\n`);
1170
- return;
1171
- }
1172
- case "show": {
1173
- const report = (0, capability_core_1.runRegistryShow)(registry, args.options);
1174
- if (wantsJson(args.options))
1175
- printJson(report);
1176
- else
1177
- process.stdout.write(`${(0, run_registry_1.formatRegistryReport)(report)}\n`);
1178
- return;
1179
- }
1180
- default:
1181
- throw new Error("Usage: cw.js registry refresh|show [--scope repo|home] [--json]");
1182
- }
1183
- }
1184
- case "metrics": {
1185
- const [subcommand, runId] = args.positionals;
1186
- switch (subcommand) {
1187
- case "show": {
1188
- const report = runner.metricsShow(required(runId, "run id"), args.options);
1189
- if (wantsJson(args.options))
1190
- printJson(report);
1191
- else
1192
- process.stdout.write(`${(0, observability_1.formatMetricsReport)(report)}\n`);
1193
- return;
1194
- }
1195
- case "summary": {
1196
- const report = (0, capability_core_1.metricsSummary)((0, capability_core_1.runRegistryFor)(args.options, runner), runner, args.options);
1197
- if (wantsJson(args.options))
1198
- printJson(report);
1199
- else
1200
- process.stdout.write(`${(0, observability_1.formatMetricsSummary)(report)}\n`);
1201
- return;
1202
- }
1203
- default:
1204
- throw new Error("Usage: cw.js metrics show <run-id> | metrics summary [--scope repo|home] [--pricing <path>|default] [--json]");
1205
- }
1206
- }
1207
- case "run": {
1208
- // Agent Delegation Drive (v0.1.38): `cw run <app> --drive [--once]` drives a
1209
- // run end-to-end by delegating each worker to the agent backend. Distinct from
1210
- // the run-REGISTRY verbs below. `--preview` (or the `run drive <run-id>` form)
1211
- // is the read-only, deterministic next-step preview.
1212
- //
1213
- // A run-REGISTRY subcommand keyword (resume/show/...) must NOT be intercepted
1214
- // here just because it carries a --drive flag of its own — e.g.
1215
- // `run resume <id> --drive` is the resume verb's opt-in continuation, not
1216
- // `run <app=resume> --drive`. Fall through to the switch for those keywords.
1217
- const runRegistrySubcommand = new Set([
1218
- "drive", "search", "list", "show", "resume", "archive", "rerun", "export", "import", "verify-import", "inspect-archive"
1219
- ]);
1220
- if (args.options.drive && !runRegistrySubcommand.has(String(args.positionals[0] || ""))) {
1221
- const target = args.positionals[0];
1222
- const runId = optionalArg(args.options.run) || optionalArg(args.options.runId);
1223
- if (args.options.preview) {
1224
- printJson((0, capability_core_1.runDrivePreview)(runner, { ...args.options, runId: runId || target }));
1225
- return;
1226
- }
1227
- const driveArgs = { ...args.options };
1228
- if (runId)
1229
- driveArgs.runId = runId;
1230
- else
1231
- driveArgs.appId = target;
1232
- const dr = (0, capability_core_1.runDrive)(runner, driveArgs);
1233
- printJson(dr);
1234
- if (!wantsJson(args.options)) {
1235
- emitRunSummary(runner, args.options, {
1236
- runId: dr.runId,
1237
- reportPath: dr.reportPath,
1238
- status: dr.status,
1239
- statePath: dr.statePath,
1240
- completedWorkers: dr.completedWorkers,
1241
- plannedWorkers: dr.plannedWorkers,
1242
- agentConfigured: dr.agentConfigured
1243
- });
1244
- }
1245
- return;
1246
- }
1247
- const registry = (0, capability_core_1.runRegistryFor)(args.options, runner);
1248
- const [subcommand, id] = args.positionals;
1249
- switch (subcommand) {
1250
- case "drive": {
1251
- // `run drive <run-id>` = read-only preview; `--step [--once]` = mutating drive.
1252
- if (args.options.step) {
1253
- const driveArgs = { ...args.options };
1254
- if (id)
1255
- driveArgs.runId = id;
1256
- const dr = (0, capability_core_1.runDrive)(runner, driveArgs);
1257
- printJson(dr);
1258
- if (!wantsJson(args.options)) {
1259
- emitRunSummary(runner, args.options, {
1260
- runId: dr.runId,
1261
- reportPath: dr.reportPath,
1262
- status: dr.status,
1263
- statePath: dr.statePath,
1264
- completedWorkers: dr.completedWorkers,
1265
- plannedWorkers: dr.plannedWorkers,
1266
- agentConfigured: dr.agentConfigured
1267
- });
1268
- }
1269
- return;
1270
- }
1271
- printJson((0, capability_core_1.runDrivePreview)(runner, { ...args.options, runId: required(id, "run id") }));
1272
- return;
1273
- }
1274
- case "search": {
1275
- const result = (0, capability_core_1.runSearch)(registry, args.options);
1276
- if (wantsJson(args.options))
1277
- printJson(result);
1278
- else
1279
- process.stdout.write(`${(0, run_registry_1.formatRunSearch)(result)}\n`);
1280
- return;
1281
- }
1282
- case "list": {
1283
- const result = (0, capability_core_1.runList)(registry, args.options);
1284
- if (wantsJson(args.options))
1285
- printJson(result);
1286
- else
1287
- process.stdout.write(`${(0, run_registry_1.formatRunSearch)(result)}\n`);
1288
- return;
1289
- }
1290
- case "show": {
1291
- const result = (0, capability_core_1.runShow)(registry, required(id, "run id"), args.options);
1292
- if (wantsJson(args.options))
1293
- printJson(result);
1294
- else
1295
- process.stdout.write(`${(0, run_registry_1.formatRunShow)(result)}\n`);
1296
- return;
1297
- }
1298
- case "resume": {
1299
- const result = (0, capability_core_1.runResume)(registry, runner, required(id, "run id"), args.options);
1300
- if (wantsJson(args.options))
1301
- printJson(result);
1302
- else
1303
- process.stdout.write(`${(0, run_registry_1.formatResume)(result)}\n`);
1304
- return;
1305
- }
1306
- case "archive":
1307
- printJson((0, capability_core_1.runArchive)(registry, id, args.options));
1308
- return;
1309
- case "rerun":
1310
- printJson((0, capability_core_1.runRerun)(registry, required(id, "run id"), args.options));
1311
- return;
1312
- case "export":
1313
- printJson((0, capability_core_1.runExportArchive)(runner, required(id || optionalArg(args.options.runId || args.options.run), "run id"), args.options));
1314
- return;
1315
- case "import":
1316
- printJson((0, capability_core_1.runImportArchive)(runner, { ...args.options, archive: id || args.options.archive || args.options.path }));
1317
- return;
1318
- case "verify-import": {
1319
- const result = (0, capability_core_1.runVerifyImport)(runner, required(id || optionalArg(args.options.runId || args.options.run), "run id"), args.options);
1320
- printJson(result);
1321
- // Fail-closed ONLY behind --strict, so the default exit stays 0
1322
- // (byte-identical). With --strict, any failed restore check — including
1323
- // the new trust-audit row — exits 1 for `verify-import && restore`.
1324
- if (Boolean(args.options.strict) && !result.ok)
1325
- process.exitCode = 1;
1326
- return;
1327
- }
1328
- case "inspect-archive": {
1329
- const result = (0, capability_core_1.runInspectArchive)(runner, { ...args.options, archive: id || args.options.archive || args.options.path });
1330
- printJson(result);
1331
- // Read-only diagnostic: exit 1 when the archive fails any integrity check,
1332
- // so `cw run inspect-archive <path> && restore` stops on a bad archive.
1333
- if (!result.ok)
1334
- process.exitCode = 1;
1335
- return;
1336
- }
1337
- default:
1338
- throw new Error("Usage: cw.js run search|list|show|resume|archive|rerun|drive|export|import|verify-import|inspect-archive [run-id|archive] [--scope repo|home] [--json] | cw.js run <app> --drive [--once] [--incremental] [--repo R --question Q]");
1339
- }
1340
- }
1341
- case "queue": {
1342
- const registry = (0, capability_core_1.runRegistryFor)(args.options, runner);
1343
- const [subcommand, id] = args.positionals;
1344
- switch (subcommand) {
1345
- case "add":
1346
- printJson((0, capability_core_1.queueAdd)(registry, args.options));
1347
- return;
1348
- case "list": {
1349
- const result = (0, capability_core_1.queueList)(registry, args.options);
1350
- if (wantsJson(args.options))
1351
- printJson(result);
1352
- else
1353
- process.stdout.write(`${(0, run_registry_1.formatQueueList)(result)}\n`);
1354
- return;
1355
- }
1356
- case "drain":
1357
- printJson((0, capability_core_1.queueDrain)(registry, args.options));
1358
- return;
1359
- case "show":
1360
- printJson((0, capability_core_1.queueShow)(registry, required(id, "queue id")));
1361
- return;
1362
- default:
1363
- throw new Error("Usage: cw.js queue add|list|drain|show [queue-id] [--repo PATH] [--priority N]");
1364
- }
1365
- }
1366
- case "sched": {
1367
- const registry = (0, capability_core_1.runRegistryFor)(args.options, runner);
1368
- const [subcommand, idArg] = args.positionals;
1369
- switch (subcommand) {
1370
- case "plan":
1371
- printJson((0, capability_core_1.schedPlan)(registry, args.options));
1372
- return;
1373
- case "lease":
1374
- printJson((0, capability_core_1.schedLease)(registry, args.options));
1375
- return;
1376
- case "release":
1377
- printJson((0, capability_core_1.schedRelease)(registry, { ...args.options, leaseId: args.options.leaseId || idArg }));
1378
- return;
1379
- case "complete":
1380
- printJson((0, capability_core_1.schedComplete)(registry, { ...args.options, leaseId: args.options.leaseId || idArg }));
1381
- return;
1382
- case "reclaim":
1383
- printJson((0, capability_core_1.schedReclaim)(registry, args.options));
1384
- return;
1385
- case "reset":
1386
- printJson((0, capability_core_1.schedReset)(registry, { ...args.options, id: args.options.id || idArg }));
1387
- return;
1388
- case "policy": {
1389
- const [, action] = args.positionals;
1390
- if (action === "set") {
1391
- printJson((0, capability_core_1.schedPolicySet)(registry, args.options));
1392
- return;
1393
- }
1394
- printJson((0, capability_core_1.schedPolicyShow)(registry));
1395
- return;
1396
- }
1397
- default:
1398
- throw new Error("Usage: cw.js sched plan|lease|release|complete|reclaim|reset|policy [show|set] [id] [--maxConcurrent N --maxAttempts N ...]");
1399
- }
1400
- }
1401
- case "clones": {
1402
- // Remote-source clone cache (v0.1.91): `list` inspects the ~/.local/state/cool-workflow
1403
- // /clones checkouts that `--link`/URL reviews populate; `gc` reclaims them (a TTL sweep,
1404
- // or --all). Pure filesystem work — no network, no run registry.
1405
- const [subcommand] = args.positionals;
1406
- switch (subcommand) {
1407
- case "list": {
1408
- const result = (0, capability_core_1.listClones)(args.options);
1409
- if (wantsJson(args.options))
1410
- printJson(result);
1411
- else
1412
- process.stdout.write(`${formatClonesList(result)}\n`);
1413
- return;
1414
- }
1415
- case "gc": {
1416
- const result = (0, capability_core_1.gcClones)(args.options);
1417
- if (wantsJson(args.options))
1418
- printJson(result);
1419
- else
1420
- process.stdout.write(`${formatClonesGc(result)}\n`);
1421
- return;
1422
- }
1423
- default:
1424
- throw new Error("Usage: cw.js clones list [--json] | clones gc [--older-than-days N] [--all] [--json]");
1425
- }
1426
- }
1427
- case "gc": {
1428
- // Run Retention & Provable Reclamation (v0.1.39). `plan` is a pure dry-run
1429
- // (frees nothing); `run` executes the write-ahead reclamation transaction;
1430
- // `verify` re-proves a reclaimed run. CW never reclaims by default.
1431
- const registry = (0, capability_core_1.runRegistryFor)(args.options, runner);
1432
- const [subcommand, id] = args.positionals;
1433
- switch (subcommand) {
1434
- case "plan": {
1435
- const result = (0, capability_core_1.gcPlan)(registry, id, args.options);
1436
- if (wantsJson(args.options))
1437
- printJson(result);
1438
- else
1439
- process.stdout.write(`${(0, run_registry_1.formatGcPlan)(result)}\n`);
1440
- return;
1441
- }
1442
- case "run": {
1443
- const result = (0, capability_core_1.gcRun)(registry, id, args.options);
1444
- if (wantsJson(args.options))
1445
- printJson(result);
1446
- else
1447
- process.stdout.write(`${(0, run_registry_1.formatGcRun)(result)}\n`);
1448
- return;
1449
- }
1450
- case "verify": {
1451
- const result = (0, capability_core_1.gcVerify)(registry, required(id, "run id"), args.options);
1452
- if (wantsJson(args.options))
1453
- printJson(result);
1454
- else
1455
- process.stdout.write(`${(0, run_registry_1.formatGcVerify)(result)}\n`);
1456
- // Fail closed ONLY on a real integrity failure: a run that WAS reclaimed
1457
- // but no longer re-proves. A not-reclaimed run has nothing to verify
1458
- // (reclaimed:false/verified:false) and must not be treated as a failure.
1459
- // LIMIT (honest): a DELETED reclaimed.json reads as reclaimed:false, so
1460
- // proof-deletion is indistinguishable from never-reclaimed here without
1461
- // an independent witness (e.g. a trust-audit reclamation event) — a
1462
- // follow-up. This guard is still strictly better than the prior exit-0.
1463
- if (result.reclaimed && !result.verified)
1464
- process.exitCode = 1;
1465
- return;
1466
- }
1467
- default:
1468
- throw new Error("Usage: cw.js gc plan|run|verify [run-id] [--reclaimAfterArchiveDays N] [--keep-scratch] [--keep-snapshots] [--limit N] [--json]");
1469
- }
1470
- }
1471
- case "history": {
1472
- const registry = (0, capability_core_1.runRegistryFor)(args.options, runner);
1473
- const result = (0, capability_core_1.runHistory)(registry, args.options);
1474
- if (wantsJson(args.options))
1475
- printJson(result);
1476
- else
1477
- process.stdout.write(`${(0, run_registry_1.formatHistory)(result)}\n`);
432
+ case "schedule":
433
+ await (0, scheduling_1.handleSchedule)(args, scheduler);
434
+ return;
435
+ case "routine":
436
+ (0, scheduling_1.handleRoutine)(args, triggers);
437
+ return;
438
+ case "registry":
439
+ (0, registry_1.handleRegistry)(args, runner);
440
+ return;
441
+ case "metrics":
442
+ (0, operational_1.handleMetrics)(args, runner);
443
+ return;
444
+ case "run":
445
+ (0, run_1.handleRun)(args, runner);
446
+ return;
447
+ case "queue":
448
+ (0, registry_1.handleQueue)(args, runner);
449
+ return;
450
+ case "sched":
451
+ (0, scheduling_1.handleSched)(args, runner);
452
+ return;
453
+ case "clones":
454
+ (0, clones_1.handleClones)(args);
455
+ return;
456
+ case "gc":
457
+ (0, maintenance_1.handleGc)(args, runner);
458
+ return;
459
+ case "history":
460
+ (0, registry_1.handleHistory)(args, runner);
461
+ return;
462
+ case "telemetry":
463
+ (0, maintenance_1.handleTelemetry)(args, runner);
464
+ return;
465
+ case "demo":
466
+ (0, maintenance_1.handleDemo)(args, runner);
467
+ return;
468
+ case "workbench":
469
+ await (0, workbench_1.handleWorkbench)(args, runner);
1478
470
  return;
1479
- }
1480
- case "telemetry": {
1481
- const [subcommand, id] = args.positionals;
1482
- switch (subcommand) {
1483
- case "verify": {
1484
- const result = (0, capability_core_1.telemetryVerify)(runner, { ...args.options, runId: id || args.options.runId || args.options.run });
1485
- if (wantsJson(args.options))
1486
- printJson(result);
1487
- else
1488
- process.stdout.write(`${(0, telemetry_demo_1.formatTelemetryVerify)(result)}\n`);
1489
- // Fail closed: a forged/edited/corrupt ledger verifies false — report it
1490
- // through the exit code so `cw telemetry verify <run> && deploy` cannot
1491
- // pass on a lie. (Absent ledger = present:false/verified:true -> exit 0.)
1492
- if (!result.verified)
1493
- process.exitCode = 1;
1494
- return;
1495
- }
1496
- default:
1497
- throw new Error("Usage: cw.js telemetry verify <run-id> [--pubkey <pem-or-path>] [--json]");
1498
- }
1499
- }
1500
- case "demo": {
1501
- const [subcommand] = args.positionals;
1502
- switch (subcommand) {
1503
- case "tamper": {
1504
- const result = (0, capability_core_1.demoTamper)(runner, args.options);
1505
- if (wantsJson(args.options))
1506
- printJson(result);
1507
- else
1508
- process.stdout.write(`${(0, telemetry_demo_1.formatTamperDemo)(result)}\n`);
1509
- // Fail closed: if the proof did not hold (a tamper went undetected),
1510
- // exit nonzero so the demo can never green a broken guarantee.
1511
- if (!result.proven)
1512
- process.exitCode = 1;
1513
- return;
1514
- }
1515
- case "bundle": {
1516
- const result = (0, capability_core_1.demoBundle)(runner, args.options);
1517
- if (wantsJson(args.options))
1518
- printJson(result);
1519
- else
1520
- process.stdout.write(`${(0, telemetry_demo_1.formatBundleDemo)(result)}\n`);
1521
- // Fail closed: a forged bundle that verified would be a regression in the
1522
- // bundle guarantee — exit nonzero so the demo can never green it.
1523
- if (!result.proven)
1524
- process.exitCode = 1;
1525
- return;
1526
- }
1527
- default:
1528
- throw new Error("Usage: cw.js demo tamper|bundle [--json]");
1529
- }
1530
- }
1531
- case "workbench": {
1532
- const [subcommand, runId] = args.positionals;
1533
- switch (subcommand) {
1534
- case "view": {
1535
- // Read-only five-panel view of one run. Same core entry as cw_workbench_view.
1536
- const view = (0, workbench_1.buildWorkbenchRunView)(runner, required(runId, "run id"));
1537
- if (wantsJson(args.options))
1538
- printJson(view);
1539
- else
1540
- process.stdout.write(`${formatWorkbenchView(view)}\n`);
1541
- return;
1542
- }
1543
- case "serve": {
1544
- // The OPTIONAL localhost host. `--once`/`--json` emit the descriptor only
1545
- // (no server); the default starts the read-only, localhost-only host.
1546
- if (args.options.once || wantsJson(args.options)) {
1547
- printJson((0, workbench_1.buildWorkbenchServeDescriptor)(runner, { ...args.options, once: true }));
1548
- return;
1549
- }
1550
- const host = new workbench_host_1.WorkbenchHost({
1551
- runner,
1552
- cwd: String(args.options.cwd || process.cwd()),
1553
- port: Number(args.options.port) || undefined,
1554
- scope: args.options.scope === "repo" ? "repo" : "home"
1555
- });
1556
- await host.run();
1557
- return;
1558
- }
1559
- default:
1560
- throw new Error("Usage: cw.js workbench serve [--port N] [--once] | view <run-id> [--json]");
1561
- }
1562
- }
1563
471
  default:
1564
472
  throw new Error(`Unknown command: ${args.command}${((0, orchestrator_1.suggestCommand)(String(args.command || "")) ? `. Did you mean: ${(0, orchestrator_1.suggestCommand)(String(args.command))}?` : "")}`);
1565
473
  }
1566
474
  }
1567
- function required(value, label) {
1568
- if (!value)
1569
- throw new Error(`Missing ${label}.\n Tip: find run ids with "cw run list" or create one with "cw quickstart"`);
1570
- return value;
1571
- }
1572
- function optionalArg(value) {
1573
- return typeof value === "string" && value.trim() ? value.trim() : undefined;
1574
- }
1575
- /** Emit the calm end-of-run summary (stderr, TTY-gated inside the reporter): the COMPACT findings
1576
- * table re-parsed from each completed worker's `cw:result`, the report path, where the per-worker
1577
- * transcripts live, and — under `--full` — the report inline. Stderr/human-side ONLY: stdout (the
1578
- * `--json` payload printed just before this) stays byte-exact. Shared by the quickstart and the
1579
- * two `run --drive` paths so all three render an identical summary. */
1580
- function emitRunSummary(runner, options, fields) {
1581
- // Anchor run reads to the run's OWN repo (a drive/quickstart may run cross-directory): the run
1582
- // dir is <repo>/.cw/runs/<id>/, holding each worker's transcript.md next to its result.md.
1583
- const runDir = typeof fields.statePath === "string" ? node_path_1.default.dirname(fields.statePath) : undefined;
1584
- const baseDir = runDir ? node_path_1.default.resolve(runDir, "..", "..", "..") : undefined;
1585
- const findings = (0, capability_core_1.collectRunFindings)(runner, fields.runId, baseDir);
1586
- // --full ALSO prints the report inline at run end (the compact table stays the default summary).
1587
- let fullReport;
1588
- if (options.full && fields.reportPath && node_fs_1.default.existsSync(fields.reportPath)) {
1589
- try {
1590
- fullReport = node_fs_1.default.readFileSync(fields.reportPath, "utf8");
1591
- }
1592
- catch { /* best-effort inline */ }
1593
- }
1594
- reporter_1.reporter.runSummary({
1595
- runId: fields.runId,
1596
- reportPath: fields.reportPath,
1597
- status: fields.status,
1598
- completedWorkers: fields.completedWorkers,
1599
- plannedWorkers: fields.plannedWorkers,
1600
- agentConfigured: fields.agentConfigured,
1601
- findings,
1602
- runDir,
1603
- fullReport
1604
- });
1605
- }
1606
- function printJson(value) {
1607
- process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
1608
- }
1609
- function humanBytes(n) {
1610
- if (n < 1024)
1611
- return `${n}B`;
1612
- const units = ["KiB", "MiB", "GiB"];
1613
- let v = n / 1024;
1614
- let i = 0;
1615
- while (v >= 1024 && i < units.length - 1) {
1616
- v /= 1024;
1617
- i += 1;
1618
- }
1619
- return `${v.toFixed(1)}${units[i]}`;
1620
- }
1621
- function formatClonesList(result) {
1622
- if (result.count === 0)
1623
- return `No cached remote checkouts in ${result.clonesDir}.`;
1624
- const rows = result.entries.map((e) => {
1625
- const when = e.fetchedAt ? e.fetchedAt.replace("T", " ").replace(/\..*$/, "Z") : "unknown";
1626
- return ` ${e.kind.padEnd(7)} ${humanBytes(e.bytes).padStart(8)} ${when} ${e.url}${e.ref ? `@${e.ref}` : ""}`;
1627
- });
1628
- return [
1629
- `${result.count} cached checkout${result.count === 1 ? "" : "s"} — ${humanBytes(result.totalBytes)} in ${result.clonesDir}`,
1630
- " KIND SIZE FETCHED SOURCE",
1631
- ...rows,
1632
- `\nReclaim with: cw clones gc --older-than-days 30 (or --all)`
1633
- ].join("\n");
1634
- }
1635
- function formatClonesGc(result) {
1636
- const scope = result.all ? "all entries" : `entries older than ${result.olderThanDays} day(s)`;
1637
- if (result.removed.length === 0)
1638
- return `Nothing to reclaim (${scope}); ${result.keptCount} kept in ${result.clonesDir}.`;
1639
- const rows = result.removed.map((r) => ` ${humanBytes(r.bytes).padStart(8)} ${r.url}`);
1640
- return [
1641
- `Reclaimed ${result.removed.length} checkout${result.removed.length === 1 ? "" : "s"} (${scope}) — freed ${humanBytes(result.freedBytes)}; ${result.keptCount} kept`,
1642
- ...rows
1643
- ].join("\n");
1644
- }
1645
- function wantsJson(options) {
1646
- return Boolean(options.json || options.format === "json");
1647
- }
1648
475
  /** Prompt the user for a question interactively when --question is missing on a TTY. */
1649
476
  async function promptQuestion(options) {
1650
477
  if (options.question || !process.stdin.isTTY)
@@ -1659,17 +486,3 @@ async function promptQuestion(options) {
1659
486
  });
1660
487
  });
1661
488
  }
1662
- function formatWorkbenchView(view) {
1663
- const lines = [
1664
- `Workbench view ${view.runId} (${view.resolved ? "resolved" : "UNRESOLVED"})`,
1665
- view.error ? ` error: ${view.error}` : ""
1666
- ].filter(Boolean);
1667
- for (const [group, panels] of Object.entries(view.panels)) {
1668
- lines.push(` ${group}:`);
1669
- for (const [name, panel] of Object.entries(panels)) {
1670
- const note = panel.status === "present" ? panel.capability : `absent (${panel.error || "unreadable"})`;
1671
- lines.push(` ${name}: ${panel.status} — ${note}`);
1672
- }
1673
- }
1674
- return lines.join("\n");
1675
- }