cool-workflow 0.1.86 → 0.1.88

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 (81) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +111 -68
  4. package/apps/architecture-review/app.json +1 -1
  5. package/apps/architecture-review-fast/app.json +1 -1
  6. package/apps/end-to-end-golden-path/app.json +1 -1
  7. package/apps/pr-review-fix-ci/app.json +1 -1
  8. package/apps/release-cut/app.json +1 -1
  9. package/apps/research-synthesis/app.json +1 -1
  10. package/dist/agent-config.js +42 -1
  11. package/dist/capability-core.js +9 -4
  12. package/dist/capability-registry.js +48 -0
  13. package/dist/cli/command-surface.js +182 -11
  14. package/dist/cli.js +2 -1
  15. package/dist/doctor.js +27 -5
  16. package/dist/drive.js +222 -16
  17. package/dist/execution-backend.js +12 -4
  18. package/dist/loop-expansion.js +60 -0
  19. package/dist/onramp.js +25 -0
  20. package/dist/operator-ux/format.js +21 -15
  21. package/dist/operator-ux.js +2 -1
  22. package/dist/orchestrator/lifecycle-operations.js +134 -3
  23. package/dist/orchestrator.js +122 -76
  24. package/dist/run-export.js +106 -2
  25. package/dist/state-node.js +13 -3
  26. package/dist/state.js +21 -0
  27. package/dist/telemetry-attestation.js +30 -6
  28. package/dist/telemetry-demo.js +29 -1
  29. package/dist/telemetry-ledger.js +6 -0
  30. package/dist/term.js +93 -0
  31. package/dist/version.js +1 -1
  32. package/dist/worker-accept/telemetry-ledger.js +12 -2
  33. package/dist/workflow-api.js +33 -0
  34. package/dist/workflow-app-framework.js +20 -0
  35. package/docs/agent-delegation-drive.7.md +28 -6
  36. package/docs/capability-topology-registry.7.md +69 -46
  37. package/docs/cli-mcp-parity.7.md +22 -2
  38. package/docs/contract-migration-tooling.7.md +8 -0
  39. package/docs/control-plane-scheduling.7.md +8 -0
  40. package/docs/durable-state-and-locking.7.md +8 -0
  41. package/docs/evidence-adoption-reasoning-chain.7.md +8 -0
  42. package/docs/execution-backends.7.md +8 -0
  43. package/docs/launch/launch-kit.md +9 -9
  44. package/docs/multi-agent-cli-mcp-surface.7.md +8 -0
  45. package/docs/multi-agent-eval-replay-harness.7.md +8 -0
  46. package/docs/multi-agent-operator-ux.7.md +8 -0
  47. package/docs/node-snapshot-diff-replay.7.md +8 -0
  48. package/docs/observability-cost-accounting.7.md +8 -0
  49. package/docs/project-index.md +26 -5
  50. package/docs/readme-v0.1.87-full.md +301 -0
  51. package/docs/real-execution-backends.7.md +8 -0
  52. package/docs/release-and-migration.7.md +8 -0
  53. package/docs/release-history.md +10 -0
  54. package/docs/release-tooling.7.md +16 -0
  55. package/docs/report-verifiable-bundle.7.md +34 -2
  56. package/docs/run-registry-control-plane.7.md +18 -0
  57. package/docs/run-retention-reclamation.7.md +8 -0
  58. package/docs/state-explosion-management.7.md +8 -0
  59. package/docs/team-collaboration.7.md +8 -0
  60. package/docs/trust-model.md +6 -4
  61. package/docs/web-desktop-workbench.7.md +8 -0
  62. package/manifest/plugin.manifest.json +1 -1
  63. package/manifest/source-context-profiles.json +1 -1
  64. package/package.json +8 -5
  65. package/scripts/agents/agent-adapter-core.js +180 -0
  66. package/scripts/agents/builtin-templates.json +5 -1
  67. package/scripts/agents/claude-p-agent.js +7 -33
  68. package/scripts/agents/codex-agent.js +134 -0
  69. package/scripts/agents/cw-attest-wrap.js +9 -1
  70. package/scripts/agents/gemini-agent.js +115 -0
  71. package/scripts/agents/opencode-agent.js +119 -0
  72. package/scripts/canonical-apps.js +4 -4
  73. package/scripts/coverage-gate.js +15 -1
  74. package/scripts/cw.js +0 -0
  75. package/scripts/dogfood-release.js +1 -1
  76. package/scripts/golden-path.js +4 -4
  77. package/scripts/parity-check.js +6 -5
  78. package/scripts/release-check.js +3 -3
  79. package/scripts/release-flow.js +49 -2
  80. package/scripts/release-gate.sh +1 -1
  81. package/tsconfig.json +3 -1
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
@@ -6,6 +39,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
39
  exports.runCli = runCli;
7
40
  const node_fs_1 = __importDefault(require("node:fs"));
8
41
  const node_path_1 = __importDefault(require("node:path"));
42
+ const node_child_process_1 = require("node:child_process");
43
+ const readline = __importStar(require("node:readline"));
9
44
  const orchestrator_1 = require("../orchestrator");
10
45
  const capability_core_1 = require("../capability-core");
11
46
  const observability_1 = require("../observability");
@@ -22,8 +57,38 @@ const multi_agent_eval_1 = require("../multi-agent-eval");
22
57
  const state_explosion_1 = require("../state-explosion");
23
58
  const evidence_reasoning_1 = require("../evidence-reasoning");
24
59
  const doctor_1 = require("../doctor");
60
+ const orchestrator_2 = require("../orchestrator");
61
+ const term_1 = require("../term");
62
+ const version_1 = require("../version");
25
63
  async function runCli(argv = process.argv.slice(2)) {
26
64
  const args = (0, orchestrator_1.parseArgv)(argv);
65
+ // Top-level flags: accept --version / -v / --help / -h before command lookup.
66
+ if (args.command?.startsWith("-") || !args.command) {
67
+ if (args.command === "--version" || args.command === "-v" || args.options.v || args.options.version) {
68
+ process.stdout.write(`${version_1.CURRENT_COOL_WORKFLOW_VERSION}\n`);
69
+ return;
70
+ }
71
+ if (!args.command || args.command === "--help" || args.command === "-h" || args.options.h || args.options.help) {
72
+ process.stdout.write((0, orchestrator_1.formatHelp)());
73
+ return;
74
+ }
75
+ }
76
+ // Map vendor shorthand flags (-claude, -codex, -deepseek) to --agent-command.
77
+ if (args.options.claude)
78
+ args.options["agent-command"] = "builtin:claude";
79
+ if (args.options.codex)
80
+ args.options["agent-command"] = "builtin:codex";
81
+ if (args.options.deepseek)
82
+ args.options["agent-command"] = "builtin:deepseek";
83
+ // Bare -q / --question -> redirect to quickstart (auto-detect repo/agent/app).
84
+ if (args.command === "-q" || args.command === "--question") {
85
+ if (!args.options.question && args.positionals[0])
86
+ args.options.question = args.positionals[0];
87
+ args.command = "quickstart";
88
+ }
89
+ else if (!args.command && typeof args.options.question === "string") {
90
+ args.command = "quickstart";
91
+ }
27
92
  const runner = new orchestrator_1.CoolWorkflowRunner({
28
93
  pluginRoot: node_path_1.default.resolve(__dirname, "../..")
29
94
  });
@@ -34,13 +99,87 @@ async function runCli(argv = process.argv.slice(2)) {
34
99
  case undefined:
35
100
  process.stdout.write((0, orchestrator_1.formatHelp)());
36
101
  return;
102
+ case "version":
103
+ process.stdout.write(`${version_1.CURRENT_COOL_WORKFLOW_VERSION}\n`);
104
+ return;
105
+ case "update": {
106
+ process.stderr.write("Updating cool-workflow...\n");
107
+ const npm = (0, node_child_process_1.spawnSync)("npm", ["update", "-g", "cool-workflow"], { encoding: "utf8", stdio: "inherit" });
108
+ if (npm.status !== 0) {
109
+ process.stderr.write("Update failed, trying install...\n");
110
+ const install = (0, node_child_process_1.spawnSync)("npm", ["install", "-g", "cool-workflow@latest"], { encoding: "utf8", stdio: "inherit" });
111
+ if (install.status !== 0) {
112
+ process.stderr.write("Install failed. Check npm and try again.\n");
113
+ process.exitCode = 1;
114
+ }
115
+ }
116
+ return;
117
+ }
118
+ case "fix": {
119
+ const report = (0, doctor_1.runDoctor)(args.options, process.env, String(args.options.cwd || process.cwd()));
120
+ process.stdout.write(`${(0, doctor_1.formatDoctorFixes)(report)}\n`);
121
+ if (!report.ok)
122
+ process.exitCode = 1;
123
+ return;
124
+ }
37
125
  case "list":
38
126
  printJson(runner.listWorkflows());
39
127
  return;
128
+ case "search": {
129
+ const keyword = args.positionals.join(" ");
130
+ if (!keyword.trim())
131
+ throw new Error("Missing search keyword.\n Tip: cw search architecture to find workflows about architecture.");
132
+ const apps = runner.listApps();
133
+ const lower = keyword.toLowerCase();
134
+ 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 }));
135
+ if (wantsJson(args.options))
136
+ printJson(results);
137
+ else
138
+ process.stdout.write(`${(0, orchestrator_2.formatSearchResults)(keyword, results)}\n`);
139
+ return;
140
+ }
141
+ case "man": {
142
+ const [topic] = args.positionals;
143
+ if (!topic)
144
+ throw new Error("Missing topic.\n Tip: cw man release-tooling for the release tooling manual.");
145
+ const docsDir = node_path_1.default.resolve(runner.pluginRoot, "docs");
146
+ const candidates = [
147
+ node_path_1.default.join(docsDir, `${topic}.7.md`),
148
+ node_path_1.default.join(docsDir, `${topic}.md`),
149
+ node_path_1.default.join(docsDir, `${topic}`)
150
+ ];
151
+ let found;
152
+ for (const c of candidates) {
153
+ try {
154
+ if (node_fs_1.default.statSync(c).isFile()) {
155
+ found = c;
156
+ break;
157
+ }
158
+ }
159
+ catch { /* keep looking */ }
160
+ }
161
+ if (!found)
162
+ throw new Error(`Man page not found: ${topic}.\n Tip: cw list for workflow topics, or browse docs/ for manuals.`);
163
+ process.stdout.write(node_fs_1.default.readFileSync(found, "utf8"));
164
+ return;
165
+ }
166
+ case "info": {
167
+ const [appId] = args.positionals;
168
+ if (!appId)
169
+ throw new Error("Missing workflow app id.\n Tip: list apps with \"cw list\", then \"cw info <id>\" for details");
170
+ const data = runner.showApp(appId);
171
+ if (wantsJson(args.options))
172
+ printJson(data);
173
+ else
174
+ process.stdout.write(`${(0, orchestrator_2.formatInfo)(appId, data)}\n`);
175
+ return;
176
+ }
40
177
  case "doctor": {
41
178
  const report = (0, doctor_1.runDoctor)(args.options, process.env, String(args.options.cwd || process.cwd()));
42
179
  if (wantsJson(args.options))
43
180
  printJson(report);
181
+ else if (args.options.fix)
182
+ process.stdout.write(`${(0, doctor_1.formatDoctorFixes)(report)}\n`);
44
183
  else
45
184
  process.stdout.write(`${(0, doctor_1.formatDoctorReport)(report)}\n`);
46
185
  if (!report.ok)
@@ -50,7 +189,7 @@ async function runCli(argv = process.argv.slice(2)) {
50
189
  case "init": {
51
190
  const [workflowId] = args.positionals;
52
191
  if (!workflowId)
53
- throw new Error("Missing workflow id. Example: cw.js init my-workflow");
192
+ throw new Error("Missing workflow id.\n Tip: create one with \"cw init my-workflow\" or list with \"cw list\"");
54
193
  printJson(runner.init(workflowId, args.options));
55
194
  return;
56
195
  }
@@ -91,8 +230,18 @@ async function runCli(argv = process.argv.slice(2)) {
91
230
  // fails closed (status=blocked) when none is set. No new executor/scheduler.
92
231
  const [appId] = args.positionals;
93
232
  const runId = optionalArg(args.options.run) || optionalArg(args.options.runId);
233
+ await promptQuestion(args.options);
94
234
  const qs = (0, capability_core_1.quickstart)(runner, { ...args.options, ...(appId ? { appId } : {}), ...(runId ? { runId } : {}) });
95
235
  printJson(qs);
236
+ const qr = qs;
237
+ if (typeof qr.runId === "string" && typeof qr.reportPath === "string") {
238
+ (0, term_1.printSuccessSummary)({
239
+ runId: qr.runId,
240
+ reportPath: qr.reportPath,
241
+ status: String(qr.status || ""),
242
+ bundle: Boolean(args.options.bundle)
243
+ });
244
+ }
96
245
  if (qs.mode === "check" && qs.ok === false) {
97
246
  process.exitCode = 1;
98
247
  }
@@ -107,7 +256,7 @@ async function runCli(argv = process.argv.slice(2)) {
107
256
  case "plan": {
108
257
  const [workflowId] = args.positionals;
109
258
  if (!workflowId)
110
- throw new Error("Missing workflow id. Example: cw.js plan architecture-review");
259
+ throw new Error("Missing workflow id.\n Tip: plan an architecture review with \"cw plan architecture-review\"");
111
260
  printJson((0, capability_core_1.planSummary)(runner, workflowId, args.options));
112
261
  return;
113
262
  }
@@ -121,8 +270,10 @@ async function runCli(argv = process.argv.slice(2)) {
121
270
  }
122
271
  else if (wantsJson(args.options))
123
272
  printJson(runner.status(args.positionals[0]));
124
- else
125
- process.stdout.write(`${(0, operator_ux_1.formatOperatorStatus)(runner.operatorStatus(args.positionals[0]))}\n`);
273
+ else {
274
+ const summary = runner.operatorStatus(args.positionals[0]);
275
+ process.stdout.write(`${(args.options.summary || args.options.brief ? (0, operator_ux_1.formatOperatorSummary)(summary) : (0, operator_ux_1.formatOperatorStatus)(summary))}\n`);
276
+ }
126
277
  return;
127
278
  case "next":
128
279
  printJson(runner.next(required(args.positionals[0], "run id"), args.options));
@@ -204,8 +355,10 @@ async function runCli(argv = process.argv.slice(2)) {
204
355
  case "status":
205
356
  if (wantsJson(args.options))
206
357
  printJson(runner.operatorStatus(required(runId, "run id")));
207
- else
208
- process.stdout.write(`${(0, operator_ux_1.formatOperatorStatus)(runner.operatorStatus(required(runId, "run id")))}\n`);
358
+ else {
359
+ const summary = runner.operatorStatus(required(runId, "run id"));
360
+ process.stdout.write(`${(args.options.summary || args.options.brief ? (0, operator_ux_1.formatOperatorSummary)(summary) : (0, operator_ux_1.formatOperatorStatus)(summary))}\n`);
361
+ }
209
362
  return;
210
363
  case "report":
211
364
  if (wantsJson(args.options))
@@ -1050,7 +1203,9 @@ async function runCli(argv = process.argv.slice(2)) {
1050
1203
  driveArgs.runId = runId;
1051
1204
  else
1052
1205
  driveArgs.appId = target;
1053
- printJson((0, capability_core_1.runDrive)(runner, driveArgs));
1206
+ const dr = (0, capability_core_1.runDrive)(runner, driveArgs);
1207
+ printJson(dr);
1208
+ (0, term_1.printSuccessSummary)({ runId: dr.runId, reportPath: dr.reportPath, status: dr.status });
1054
1209
  return;
1055
1210
  }
1056
1211
  const registry = (0, capability_core_1.runRegistryFor)(args.options, runner);
@@ -1062,7 +1217,9 @@ async function runCli(argv = process.argv.slice(2)) {
1062
1217
  const driveArgs = { ...args.options };
1063
1218
  if (id)
1064
1219
  driveArgs.runId = id;
1065
- printJson((0, capability_core_1.runDrive)(runner, driveArgs));
1220
+ const dr = (0, capability_core_1.runDrive)(runner, driveArgs);
1221
+ printJson(dr);
1222
+ (0, term_1.printSuccessSummary)({ runId: dr.runId, reportPath: dr.reportPath, status: dr.status });
1066
1223
  return;
1067
1224
  }
1068
1225
  printJson((0, capability_core_1.runDrivePreview)(runner, { ...args.options, runId: required(id, "run id") }));
@@ -1132,7 +1289,7 @@ async function runCli(argv = process.argv.slice(2)) {
1132
1289
  return;
1133
1290
  }
1134
1291
  default:
1135
- 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] [--repo R --question Q]");
1292
+ 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]");
1136
1293
  }
1137
1294
  }
1138
1295
  case "queue": {
@@ -1332,12 +1489,12 @@ async function runCli(argv = process.argv.slice(2)) {
1332
1489
  }
1333
1490
  }
1334
1491
  default:
1335
- throw new Error(`Unknown command: ${args.command}`);
1492
+ throw new Error(`Unknown command: ${args.command}${((0, orchestrator_1.suggestCommand)(String(args.command || "")) ? `. Did you mean: ${(0, orchestrator_1.suggestCommand)(String(args.command))}?` : "")}`);
1336
1493
  }
1337
1494
  }
1338
1495
  function required(value, label) {
1339
1496
  if (!value)
1340
- throw new Error(`Missing ${label}. Run "cw.js help" for usage.`);
1497
+ throw new Error(`Missing ${label}.\n Tip: find run ids with "cw run list" or create one with "cw quickstart"`);
1341
1498
  return value;
1342
1499
  }
1343
1500
  function optionalArg(value) {
@@ -1349,6 +1506,20 @@ function printJson(value) {
1349
1506
  function wantsJson(options) {
1350
1507
  return Boolean(options.json || options.format === "json");
1351
1508
  }
1509
+ /** Prompt the user for a question interactively when --question is missing on a TTY. */
1510
+ async function promptQuestion(options) {
1511
+ if (options.question || !process.stdin.isTTY)
1512
+ return;
1513
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
1514
+ return new Promise((resolve) => {
1515
+ rl.question("Question: ", (answer) => {
1516
+ rl.close();
1517
+ if (answer.trim())
1518
+ options.question = answer.trim();
1519
+ resolve();
1520
+ });
1521
+ });
1522
+ }
1352
1523
  function formatWorkbenchView(view) {
1353
1524
  const lines = [
1354
1525
  `Workbench view ${view.runId} (${view.resolved ? "resolved" : "UNRESOLVED"})`,
package/dist/cli.js CHANGED
@@ -2,8 +2,9 @@
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const command_surface_1 = require("./cli/command-surface");
5
+ const term_1 = require("./term");
5
6
  (0, command_surface_1.runCli)(process.argv.slice(2)).catch((error) => {
6
7
  const message = error instanceof Error ? error.message : String(error);
7
- process.stderr.write(`cw: ${message}\n`);
8
+ process.stderr.write(`${(0, term_1.bold)("cw:")} ${(0, term_1.red)(message)}\n`);
8
9
  process.exitCode = 1;
9
10
  });
package/dist/doctor.js CHANGED
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.runDoctor = runDoctor;
7
7
  exports.formatDoctorReport = formatDoctorReport;
8
+ exports.formatDoctorFixes = formatDoctorFixes;
8
9
  // `cw doctor` — environment diagnostics, in the spirit of `brew doctor`.
9
10
  //
10
11
  // Homebrew's `doctor` turned "something is subtly wrong with your setup" into a
@@ -26,6 +27,7 @@ const node_path_1 = __importDefault(require("node:path"));
26
27
  const node_child_process_1 = require("node:child_process");
27
28
  const agent_config_1 = require("./agent-config");
28
29
  const onramp_1 = require("./onramp");
30
+ const term_1 = require("./term");
29
31
  /** Resolve a bare binary name against $PATH (or accept an explicit path). Returns
30
32
  * the resolved path, or undefined when not found. No spawning. */
31
33
  function whichBinary(bin, env) {
@@ -89,7 +91,15 @@ function runDoctor(args = {}, env = process.env, cwd = process.cwd()) {
89
91
  : { name: "node", status: "fail", detail: `Node ${process.version} is below the required v18.`, fix: "Install Node.js 18+ (e.g. `brew install node`, or https://nodejs.org)." });
90
92
  // 2. Agent backend — CW delegates execution; without one, real runs park.
91
93
  const cfg = (0, agent_config_1.resolveAgentConfig)(args, env);
92
- if (cfg.source === "none") {
94
+ if (cfg.source === "auto") {
95
+ const vendor = (cfg.model && cfg.model.startsWith("builtin:")) ? cfg.model.slice("builtin:".length) : "auto";
96
+ checks.push({
97
+ name: "agent",
98
+ status: "ok",
99
+ detail: `Agent auto-detected: ${vendor}. Set CW_AGENT_COMMAND or --agent-command to override.`
100
+ });
101
+ }
102
+ else if (cfg.source === "none") {
93
103
  checks.push({
94
104
  name: "agent",
95
105
  status: "warn",
@@ -149,16 +159,21 @@ function runDoctor(args = {}, env = process.env, cwd = process.cwd()) {
149
159
  }
150
160
  /** Human rendering (TTY/default). `--json` callers use the report object directly. */
151
161
  function formatDoctorReport(report) {
152
- const glyph = { ok: "✓", warn: "!", fail: "✗" };
153
- const lines = ["cw doctor"];
162
+ const lines = [(0, term_1.bold)("cw doctor")];
154
163
  for (const check of report.checks) {
155
- lines.push(` ${glyph[check.status]} ${check.name}: ${check.detail}`);
164
+ lines.push(` ${(0, term_1.doctorGlyph)(check.status)} ${check.name}: ${check.detail}`);
156
165
  if (check.fix && check.status !== "ok")
157
166
  lines.push(` fix: ${check.fix}`);
158
167
  }
159
168
  lines.push("");
160
- lines.push(`${report.ok ? "✓" : "✗"} ${report.summary}`);
169
+ const summaryGlyph = report.ok ? (0, term_1.green)("✓") : (0, term_1.red)("✗");
170
+ lines.push(`${summaryGlyph} ${report.summary}`);
161
171
  if (report.onramp) {
172
+ lines.push("");
173
+ lines.push("Quick start (3 steps):");
174
+ lines.push(" 1. cw demo tamper — prove trust checks work (30s)");
175
+ lines.push(" 2. cw demo bundle — prove portable bundles (30s)");
176
+ lines.push(' 3. cw -q "what risks?" — your first real report (needs an agent)');
162
177
  lines.push("");
163
178
  lines.push("Onramp");
164
179
  lines.push(` ${report.onramp.summary}`);
@@ -187,3 +202,10 @@ function formatDoctorReport(report) {
187
202
  }
188
203
  return lines.join("\n");
189
204
  }
205
+ /** `--fix` rendering: consolidates all fix strings into an actionable block. */
206
+ function formatDoctorFixes(report) {
207
+ const fixes = report.checks.filter((c) => c.fix && c.status !== "ok").map((c) => c.fix);
208
+ if (!fixes.length)
209
+ return "No fixes needed.";
210
+ return [(0, term_1.bold)("Fix Commands"), ...fixes.map((f, i) => ` ${i + 1}. ${f}`), ""].join("\n");
211
+ }