cool-workflow 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/apps/architecture-review/app.json +1 -1
  4. package/apps/architecture-review-fast/app.json +1 -1
  5. package/apps/end-to-end-golden-path/app.json +1 -1
  6. package/apps/pr-review-fix-ci/app.json +1 -1
  7. package/apps/release-cut/app.json +1 -1
  8. package/apps/research-synthesis/app.json +1 -1
  9. package/dist/cli/dispatch.js +29 -91
  10. package/dist/cli/entry.js +41 -1
  11. package/dist/cli/io.js +6 -2
  12. package/dist/cli/parseargv.js +6 -2
  13. package/dist/core/capability-data.js +271 -0
  14. package/dist/core/capability-table.js +16 -3504
  15. package/dist/core/format/completion.js +68 -0
  16. package/dist/core/format/help.js +50 -8
  17. package/dist/core/format/safe-json.js +73 -0
  18. package/dist/core/format/state-explosion-text.js +1 -1
  19. package/dist/core/multi-agent/candidate-scoring.js +5 -1
  20. package/dist/core/multi-agent/collaboration.js +6 -5
  21. package/dist/core/multi-agent/coordinator.js +9 -8
  22. package/dist/core/multi-agent/runtime.js +4 -4
  23. package/dist/core/multi-agent/topology.js +3 -3
  24. package/dist/core/pipeline/commit-gate.js +2 -1
  25. package/dist/core/pipeline/dispatch.js +18 -4
  26. package/dist/core/pipeline/drive-decide.js +2 -1
  27. package/dist/core/state/migrations.js +16 -1
  28. package/dist/core/state/state-explosion/digest.js +16 -15
  29. package/dist/core/state/state-explosion/graph.js +26 -19
  30. package/dist/core/state/state-explosion/helpers.js +2 -1
  31. package/dist/core/state/state-explosion/report.js +6 -6
  32. package/dist/core/trust/ledger.js +2 -1
  33. package/dist/core/types/execution-backend.js +18 -0
  34. package/dist/core/types/observability.js +7 -0
  35. package/dist/core/util/collate.js +23 -0
  36. package/dist/core/util/numeric-flag.js +40 -0
  37. package/dist/core/version.js +1 -1
  38. package/dist/mcp/server.js +99 -11
  39. package/dist/shell/audit-cli.js +99 -23
  40. package/dist/shell/commit-summary.js +2 -1
  41. package/dist/shell/coordinator-io.js +73 -13
  42. package/dist/shell/dispatch.js +1 -1
  43. package/dist/shell/doctor.js +80 -1
  44. package/dist/shell/drive.js +269 -59
  45. package/dist/shell/eval-text.js +2 -2
  46. package/dist/shell/evidence-reasoning.js +8 -7
  47. package/dist/shell/execution-backend/agent.js +20 -1
  48. package/dist/shell/execution-backend/container.js +4 -1
  49. package/dist/shell/execution-backend/local.js +4 -1
  50. package/dist/shell/execution-backend/registry.js +2 -1
  51. package/dist/shell/execution-backend/types.js +0 -9
  52. package/dist/shell/feedback-cli.js +6 -6
  53. package/dist/shell/fs-atomic.js +232 -30
  54. package/dist/shell/man-cli.js +6 -0
  55. package/dist/shell/metrics-cli.js +2 -1
  56. package/dist/shell/multi-agent-cli.js +367 -323
  57. package/dist/shell/multi-agent-host.js +9 -9
  58. package/dist/shell/multi-agent-operator-ux.js +82 -39
  59. package/dist/shell/node-store.js +10 -4
  60. package/dist/shell/observability.js +13 -12
  61. package/dist/shell/onramp.js +17 -1
  62. package/dist/shell/operator-ux-text.js +24 -23
  63. package/dist/shell/operator-ux.js +22 -20
  64. package/dist/shell/orchestrator.js +49 -38
  65. package/dist/shell/pipeline-cli.js +168 -111
  66. package/dist/shell/reclamation-io.js +16 -5
  67. package/dist/shell/registry-cli.js +19 -17
  68. package/dist/shell/remote-source.js +13 -8
  69. package/dist/shell/report-cli.js +45 -0
  70. package/dist/shell/report.js +4 -2
  71. package/dist/shell/run-registry-io.js +77 -19
  72. package/dist/shell/run-store.js +91 -2
  73. package/dist/shell/scheduling-io.js +12 -13
  74. package/dist/shell/state-cli.js +2 -7
  75. package/dist/shell/state-explosion-cli.js +19 -10
  76. package/dist/shell/topology-io.js +38 -6
  77. package/dist/shell/trust-audit.js +264 -23
  78. package/dist/shell/trust-policy-io.js +1 -1
  79. package/dist/shell/worker-cli.js +41 -29
  80. package/dist/shell/worker-isolation.js +34 -9
  81. package/dist/shell/workflow-app-loader.js +3 -2
  82. package/dist/wiring/capability-table/basics.js +91 -0
  83. package/dist/wiring/capability-table/exec-backend.js +171 -0
  84. package/dist/wiring/capability-table/index.js +42 -0
  85. package/dist/wiring/capability-table/multi-agent.js +630 -0
  86. package/dist/wiring/capability-table/parity.js +467 -0
  87. package/dist/wiring/capability-table/pipeline.js +292 -0
  88. package/dist/wiring/capability-table/registry-core.js +208 -0
  89. package/dist/wiring/capability-table/reporting.js +435 -0
  90. package/dist/wiring/capability-table/scheduling-registry.js +592 -0
  91. package/dist/wiring/capability-table/state.js +181 -0
  92. package/dist/wiring/capability-table/trust-ledger.js +158 -0
  93. package/dist/wiring/capability-table/workflow-apps.js +381 -0
  94. package/docs/agent-delegation-drive.7.md +4 -0
  95. package/docs/candidate-scoring.7.md +1 -1
  96. package/docs/canonical-workflow-apps.7.md +7 -7
  97. package/docs/cli-mcp-parity.7.md +13 -3
  98. package/docs/contract-migration-tooling.7.md +4 -0
  99. package/docs/control-plane-scheduling.7.md +4 -0
  100. package/docs/coordinator-blackboard.7.md +17 -17
  101. package/docs/dogfood-one-real-repo.7.md +11 -11
  102. package/docs/durable-state-and-locking.7.md +24 -0
  103. package/docs/end-to-end-golden-path.7.md +14 -14
  104. package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
  105. package/docs/execution-backends.7.md +4 -0
  106. package/docs/getting-started.md +37 -37
  107. package/docs/index.md +1 -0
  108. package/docs/multi-agent-cli-mcp-surface.7.md +21 -17
  109. package/docs/multi-agent-eval-replay-harness.7.md +17 -13
  110. package/docs/multi-agent-operator-ux.7.md +23 -19
  111. package/docs/multi-agent-runtime-core.7.md +22 -22
  112. package/docs/multi-agent-topologies.7.md +6 -6
  113. package/docs/multi-agent-trust-policy-audit.7.md +11 -11
  114. package/docs/node-snapshot-diff-replay.7.md +4 -0
  115. package/docs/observability-cost-accounting.7.md +4 -0
  116. package/docs/operator-ux.7.md +34 -34
  117. package/docs/pipeline-runner.7.md +4 -4
  118. package/docs/project-index.md +48 -5
  119. package/docs/real-execution-backends.7.md +4 -0
  120. package/docs/release-and-migration.7.md +5 -1
  121. package/docs/release-tooling.7.md +4 -0
  122. package/docs/routines.md +4 -4
  123. package/docs/run-registry-control-plane.7.md +23 -19
  124. package/docs/run-retention-reclamation.7.md +4 -0
  125. package/docs/scheduled-tasks.md +14 -14
  126. package/docs/security-trust-hardening.7.md +43 -13
  127. package/docs/state-explosion-management.7.md +14 -10
  128. package/docs/team-collaboration.7.md +4 -0
  129. package/docs/trust-audit-anchor.7.md +71 -0
  130. package/docs/unix-principles.md +3 -1
  131. package/docs/verifier-gated-commit.7.md +1 -1
  132. package/docs/web-desktop-workbench.7.md +4 -0
  133. package/docs/workflow-app-framework.7.md +13 -13
  134. package/manifest/plugin.manifest.json +1 -1
  135. package/package.json +4 -2
  136. package/scripts/agents/claude-p-agent.js +2 -2
  137. package/scripts/block-unapproved-tag.sh +23 -2
  138. package/scripts/bump-version.js +24 -2
  139. package/scripts/canonical-apps.js +4 -4
  140. package/scripts/children/batch-delegate-child.js +52 -2
  141. package/scripts/dogfood-release.js +1 -1
  142. package/scripts/fake-date-for-reproduction.js +44 -0
  143. package/scripts/golden-path.js +4 -4
  144. package/scripts/purity-baseline.json +71 -0
  145. package/scripts/purity-gate.js +239 -0
  146. package/scripts/release-check.js +8 -1
  147. package/scripts/release-flow.js +57 -1
  148. package/scripts/verdict-keygen.js +83 -0
  149. package/scripts/verify-bump-reproduction.sh +148 -0
  150. package/scripts/verify-verdict-signature.js +61 -0
  151. package/scripts/version-sync-check.js +33 -12
  152. package/skills/cool-workflow/SKILL.md +9 -9
  153. package/skills/cool-workflow/references/commands.md +89 -88
  154. package/ui/workbench/app.css +37 -1
  155. package/ui/workbench/app.js +124 -6
  156. package/workflows/README.md +19 -0
@@ -0,0 +1,292 @@
1
+ "use strict";
2
+ // wiring/capability-table/pipeline.ts — MILESTONE 6+7 (plan, run.drive*,
3
+ // dispatch, result, commit, commit.summary) + MILESTONE 11's run.export/
4
+ // import/verify-import/inspect-archive/restore CLI bindings. Split out of
5
+ // core/capability-table.ts, byte-for-byte (extracted with sed, not
6
+ // retyped).
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ const registry_core_1 = require("./registry-core");
9
+ const io_1 = require("../../cli/io");
10
+ const io_2 = require("../../cli/io");
11
+ // MILESTONE 6+7 (combined; see docs/rebuild/PLAN.md Open risk 10) CLI bindings:
12
+ // plan, quickstart, run --drive, run drive (preview), dispatch, result,
13
+ // commit. Handler BODIES live in shell/pipeline-cli.ts (impure — they
14
+ // plan/drive/dispatch/commit real run state on disk); this table only
15
+ // wires argv shape -> handler call, per cli/dispatch.ts's generic
16
+ // executor contract.
17
+ // ---------------------------------------------------------------------
18
+ // This file is required unconditionally at startup for every command, so
19
+ // a top-level import of pipeline-cli/commit-summary would cost every
20
+ // invocation, not just plan/run/dispatch/result/commit/commit.summary ones.
21
+ function loadPipelineCli() {
22
+ return require("../../shell/pipeline-cli");
23
+ }
24
+ function loadCommitSummary() {
25
+ return require("../../shell/commit-summary");
26
+ }
27
+ (0, registry_core_1.attachCliBinding)("plan", {
28
+ path: ["plan"],
29
+ jsonMode: "default",
30
+ handler: (args) => {
31
+ const workflowId = (0, io_2.optionalArg)(args.positionals[0]);
32
+ if (!workflowId) {
33
+ throw new Error('Missing workflow id.\n Tip: plan an architecture review with "cw plan architecture-review"');
34
+ }
35
+ return { json: loadPipelineCli().planRun({ ...args.options, workflowId }) };
36
+ },
37
+ });
38
+ // `cw run <app> --drive [--once]` and `cw run drive <run-id> [--step]`
39
+ // share the single `run` dispatch path (byte-exact to the old build's
40
+ // handleRun: a run-REGISTRY subcommand keyword is never hijacked by the
41
+ // bare `--drive` intercept just because it carries its own --drive/--step
42
+ // flag). Both rows below dispatch on `["run"]`; the FIRST one registered
43
+ // (run.drive.step) is found first by findCapabilityByCliPath's linear
44
+ // scan, so its handler carries the full branch — the second row exists
45
+ // only so `cw help run` lists both capabilities.
46
+ (0, registry_core_1.attachCliBinding)("run.drive.step", {
47
+ path: ["run"],
48
+ helpPath: ["run", "drive"],
49
+ jsonMode: "default",
50
+ handler: async (args) => {
51
+ const registrySubcommands = new Set(["drive", "search", "list", "show", "resume", "archive", "rerun", "export", "import", "verify-import", "inspect-archive", "restore"]);
52
+ const target = args.positionals[0];
53
+ if (args.options.drive && !registrySubcommands.has(String(target || ""))) {
54
+ const runId = (0, io_2.optionalArg)(args.options.run) || (0, io_2.optionalArg)(args.options.runId);
55
+ if (args.options.preview)
56
+ return { json: loadPipelineCli().runDrivePreview({ ...args.options, runId: runId || target }) };
57
+ const driveArgs = { ...args.options };
58
+ if (runId)
59
+ driveArgs.runId = runId;
60
+ else
61
+ driveArgs.appId = target;
62
+ return { json: await loadPipelineCli().runDriveStep(driveArgs) };
63
+ }
64
+ const [subcommand, id] = args.positionals;
65
+ if (subcommand === "drive") {
66
+ if (args.options.step) {
67
+ const driveArgs = { ...args.options };
68
+ if (id)
69
+ driveArgs.runId = id;
70
+ return { json: await loadPipelineCli().runDriveStep(driveArgs) };
71
+ }
72
+ return { json: loadPipelineCli().runDrivePreview({ ...args.options, runId: (0, io_1.required)(id, "run id") }) };
73
+ }
74
+ // MILESTONE 11 (reporting/run-export) — the archive family. Handler
75
+ // bodies live in shell/run-export-cli.ts; this arm only wires argv
76
+ // shape -> handler call.
77
+ if (subcommand === "export") {
78
+ const result = loadRunExportCli().runExportCli((0, io_1.required)(id, "run id"), args.options);
79
+ return { json: result };
80
+ }
81
+ if (subcommand === "import") {
82
+ const result = loadRunExportCli().runImportCli((0, io_1.required)(id, "archive path"), args.options);
83
+ return { json: result };
84
+ }
85
+ if (subcommand === "verify-import") {
86
+ const result = loadRunExportCli().runVerifyImportCli((0, io_1.required)(id, "run id"), args.options);
87
+ return { json: result, exitCode: args.options.strict && !result.ok ? 1 : undefined };
88
+ }
89
+ if (subcommand === "inspect-archive") {
90
+ const result = loadRunExportCli().runInspectArchiveCli((0, io_1.required)(id, "archive path"), args.options);
91
+ return { json: result, exitCode: result.ok ? undefined : 1 };
92
+ }
93
+ if (subcommand === "restore") {
94
+ const result = loadRunExportCli().runRestoreCli((0, io_1.required)(id, "archive path"), args.options);
95
+ return { json: result, exitCode: result.ok ? undefined : 1 };
96
+ }
97
+ throw new Error("Usage: cw run search|list|show|resume|archive|rerun|drive|export|import|verify-import|inspect-archive|restore [run-id|archive] [--scope repo|home] [--json] | cw run <app> --drive [--once] [--incremental] [--repo R --question Q]");
98
+ },
99
+ });
100
+ // PARITY: `run.drive` (the read-only MCP preview tool) now ALSO carries
101
+ // its own two-token `cli.path` ["run","drive"], same as the old build's
102
+ // registry row (cli.path ["run","drive"]) and the same reversed-
103
+ // candidate-order pattern already used for run.search/run.list/etc.
104
+ // (see those rows' own comment below): findCapabilityByCliPath tries the
105
+ // 2-token candidate BEFORE the 1-token ["run"] row, so this row — not
106
+ // run.drive.step's combined switch — now serves `cw run drive <run-id>`
107
+ // [--step]. Behavior is unchanged (same runDrivePreview/runDriveStep
108
+ // calls, same --step branch run.drive.step's switch already used); only
109
+ // WHICH row answers the dispatch changes, so run.drive becomes a real
110
+ // both-surface, dual-bound capability for the payload-identity probe.
111
+ (0, registry_core_1.attachCliBinding)("run.drive", {
112
+ path: ["run", "drive"],
113
+ jsonMode: "default",
114
+ handler: async (args) => {
115
+ const id = args.positionals[0];
116
+ if (args.options.step) {
117
+ const driveArgs = { ...args.options };
118
+ if (id)
119
+ driveArgs.runId = id;
120
+ return { json: await loadPipelineCli().runDriveStep(driveArgs) };
121
+ }
122
+ return { json: loadPipelineCli().runDrivePreview({ ...args.options, runId: (0, io_1.required)(id, "run id") }) };
123
+ },
124
+ });
125
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("run.drive").mcp.handler = (args) => loadPipelineCli().runDrivePreview(args);
126
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("run.drive.step").mcp.handler = (args) => loadPipelineCli().runDriveStep(args);
127
+ // PARITY: `run.drive.step` advances the run by spawning the external
128
+ // agent per worker and recording attested output — not a read probe.
129
+ // CLI (--drive/--step) and MCP route through the same drive() core; the
130
+ // opt-out is the documented divergence, not undocumented drift.
131
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("run.drive.step").payloadIdentical = false;
132
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("run.drive.step").reason =
133
+ "Mutating: advances the run by spawning the external agent per worker and recording attested output — not a read probe. CLI (--drive/--step) and MCP route through the same drive() core.";
134
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("plan").mcp.handler = (args) => loadPipelineCli().planRun(args);
135
+ // GAP #24: dispatchRun reads the sandbox profile from `args.sandbox` only
136
+ // (the CLI's --sandbox flag). The cw_dispatch MCP tool also accepts the
137
+ // `sandboxProfile`/`sandboxProfileId` aliases (its declared properties), so
138
+ // normalize them onto `sandbox` here — mirrors the old build's
139
+ // sandboxProfileIdFrom() alias set — before handing off.
140
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("dispatch").mcp.handler = (args) => loadPipelineCli().dispatchRun({ ...args, sandbox: args.sandbox ?? args.sandboxProfile ?? args.sandboxProfileId });
141
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("result").mcp.handler = (args) => loadPipelineCli().recordResultRun(args);
142
+ // `cw_commit` returns the FLAT commit envelope (verifierGated/checkpoint/
143
+ // selectionId/… at the top level, plus a nested `commit`), matching the old
144
+ // build's commitEnvelope. `commitRun` (CLI shape) returns `{ runId, commit }`;
145
+ // lift the commit's key fields to the top for the MCP surface.
146
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("commit").mcp.handler = (args) => {
147
+ const result = loadPipelineCli().commitRun(args);
148
+ const commit = result.commit || {};
149
+ return {
150
+ runId: result.runId,
151
+ commitId: commit.id,
152
+ verifierGated: commit.verifierGated,
153
+ checkpoint: commit.checkpoint,
154
+ verifierNodeId: commit.verifierNodeId,
155
+ candidateId: commit.candidateId,
156
+ selectionId: commit.selectionId,
157
+ evidenceCount: Array.isArray(commit.evidence) ? commit.evidence.length : 0,
158
+ snapshotPath: commit.snapshotPath,
159
+ commit,
160
+ };
161
+ };
162
+ // PARITY: `commit` is the one declared payload projection (byte-compat
163
+ // item 5 above). Both surfaces route through the single core entry
164
+ // runner.commit (commitRun); the CLI keeps the raw StateCommitResult for
165
+ // scripting while cw_commit lifts an operator-facing envelope on top (see
166
+ // the mcp.handler just above). Marked here, not silently let drift, so
167
+ // the parity payload probe (core/capability-table.ts's
168
+ // payloadIdenticalCapabilities) skips it with a paper trail instead of
169
+ // tripping on an undocumented divergence.
170
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("commit").payloadIdentical = false;
171
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("commit").reason =
172
+ "Both surfaces route through the single core entry runner.commit. The CLI emits the raw StateCommitResult for scripting (commit.id, commit.evidence, commit.gate); cw_commit emits the operator commit envelope (commitId, verifierGated, checkpoint, evidenceCount, snapshotPath, nextActions, plus the raw result under `commit`). Declared projection, not drift.";
173
+ // MILESTONE 11 — run.export/import/verify-import/inspect-archive/restore
174
+ // MCP handlers (the CLI side is served by run.drive.step's combined
175
+ // handler above; these tools are called directly by name over MCP, so
176
+ // each needs its own mcp.handler per byte-compat item 5's two-field
177
+ // row shape).
178
+ // Same lazy-load reasoning as loadPipelineCli/loadCommitSummary above, for
179
+ // the run.export/import/verify-import/inspect-archive/restore family.
180
+ function loadRunExportCli() {
181
+ return require("../../shell/run-export-cli");
182
+ }
183
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("run.export").mcp.handler = (args) => loadRunExportCli().runExportCli((0, io_1.required)((0, io_2.optionalArg)(args.runId), "run id"), args);
184
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("run.import").mcp.handler = (args) => loadRunExportCli().runImportCli((0, io_1.required)((0, io_2.optionalArg)(args.archive || args.path || args.file), "archive path"), args);
185
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("run.verify-import").mcp.handler = (args) => loadRunExportCli().runVerifyImportCli((0, io_1.required)((0, io_2.optionalArg)(args.runId), "run id"), args);
186
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("run.inspect-archive").mcp.handler = (args) => loadRunExportCli().runInspectArchiveCli((0, io_1.required)((0, io_2.optionalArg)(args.archive || args.path || args.file), "archive path"), args);
187
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("run.restore").mcp.handler = (args) => loadRunExportCli().runRestoreCli((0, io_1.required)((0, io_2.optionalArg)(args.archive || args.path || args.file), "archive path"), args);
188
+ // `run export|import|verify-import|inspect-archive|restore` each carry their
189
+ // own two-token cli.path (found before the ["run"] run.drive.step catch-all
190
+ // per the reversed candidate order), calling the same shell fns with the
191
+ // same [subcommand, id] positional mapping and exit-code shape the catch-all
192
+ // switch already used. `hiddenFromHelp` keeps the byte-pinned `cw help run`
193
+ // fixture's rows coming from the single literal COMMAND_HELP_ROWS.run block.
194
+ (0, registry_core_1.attachCliBinding)("run.export", {
195
+ path: ["run", "export"],
196
+ jsonMode: "default",
197
+ hiddenFromHelp: true,
198
+ handler: (args) => ({ json: loadRunExportCli().runExportCli((0, io_1.required)(args.positionals[0], "run id"), args.options) }),
199
+ });
200
+ (0, registry_core_1.attachCliBinding)("run.import", {
201
+ path: ["run", "import"],
202
+ jsonMode: "default",
203
+ hiddenFromHelp: true,
204
+ handler: (args) => ({ json: loadRunExportCli().runImportCli((0, io_1.required)(args.positionals[0], "archive path"), args.options) }),
205
+ });
206
+ (0, registry_core_1.attachCliBinding)("run.verify-import", {
207
+ path: ["run", "verify-import"],
208
+ jsonMode: "default",
209
+ hiddenFromHelp: true,
210
+ handler: (args) => {
211
+ const result = loadRunExportCli().runVerifyImportCli((0, io_1.required)(args.positionals[0], "run id"), args.options);
212
+ return { json: result, exitCode: args.options.strict && !result.ok ? 1 : undefined };
213
+ },
214
+ });
215
+ (0, registry_core_1.attachCliBinding)("run.inspect-archive", {
216
+ path: ["run", "inspect-archive"],
217
+ jsonMode: "default",
218
+ hiddenFromHelp: true,
219
+ handler: (args) => {
220
+ const result = loadRunExportCli().runInspectArchiveCli((0, io_1.required)(args.positionals[0], "archive path"), args.options);
221
+ return { json: result, exitCode: result.ok ? undefined : 1 };
222
+ },
223
+ });
224
+ (0, registry_core_1.attachCliBinding)("run.restore", {
225
+ path: ["run", "restore"],
226
+ jsonMode: "default",
227
+ hiddenFromHelp: true,
228
+ handler: (args) => {
229
+ const result = loadRunExportCli().runRestoreCli((0, io_1.required)(args.positionals[0], "archive path"), args.options);
230
+ return { json: result, exitCode: result.ok ? undefined : 1 };
231
+ },
232
+ });
233
+ (0, registry_core_1.addCliOnlyCapability)("quickstart", "ONE-COMMAND quickstart: --check preflights without writes; otherwise plan(app, default architecture-review) -> run --drive -> report in a single invocation (--preview for a read-only dry run; --bundle [--with-trust-key K] seals a completed run into a self-verified portable bundle).", {
234
+ path: ["quickstart"],
235
+ // `audit-run` is a CLI-only alias that dispatches to the same quickstart
236
+ // wrapper (byte-behavior port of the old build's caseTokens).
237
+ caseTokens: ["quickstart", "audit-run"],
238
+ jsonMode: "default",
239
+ handler: async (args) => {
240
+ const appId = (0, io_2.optionalArg)(args.positionals[0]);
241
+ const result = (await loadPipelineCli().quickstartRun({ ...args.options, appId }));
242
+ // Fail closed on both known bad outcomes: a --check preflight that
243
+ // found a blocking gap, OR a --bundle that did not self-verify.
244
+ const bundle = result.bundle;
245
+ const bundleFailed = Boolean(bundle && bundle.ok === false);
246
+ const exitCode = (result.mode === "check" && result.ok === false) || bundleFailed ? 1 : undefined;
247
+ return { json: result, exitCode };
248
+ },
249
+ }, "quickstart composes plan/runDrive/report; SPEC/mcp.md's declared cli-only list names it explicitly (no MCP peer). `audit-run` is a CLI-only alias of the same wrapper.", "quickstart");
250
+ (0, registry_core_1.attachCliBinding)("dispatch", {
251
+ path: ["dispatch"],
252
+ jsonMode: "default",
253
+ handler: (args) => {
254
+ const runId = (0, io_1.required)((0, io_2.optionalArg)(args.positionals[0]), "run id");
255
+ return { json: loadPipelineCli().dispatchRun({ ...args.options, runId }) };
256
+ },
257
+ });
258
+ (0, registry_core_1.attachCliBinding)("result", {
259
+ path: ["result"],
260
+ jsonMode: "default",
261
+ handler: (args) => {
262
+ const runId = (0, io_1.required)((0, io_2.optionalArg)(args.positionals[0]), "run id");
263
+ const taskId = (0, io_1.required)((0, io_2.optionalArg)(args.positionals[1]), "task id");
264
+ const resultPath = (0, io_1.required)((0, io_2.optionalArg)(args.positionals[2]), "result file path");
265
+ return { json: loadPipelineCli().recordResultRun({ ...args.options, runId, taskId, resultPath }) };
266
+ },
267
+ });
268
+ (0, registry_core_1.attachCliBinding)("commit", {
269
+ path: ["commit"],
270
+ jsonMode: "default",
271
+ handler: (args) => {
272
+ const runId = (0, io_1.required)((0, io_2.optionalArg)(args.positionals[0]), "run id");
273
+ return { json: loadPipelineCli().commitRun({ ...args.options, runId }) };
274
+ },
275
+ });
276
+ // GAP #26: restore `cw commit summary <run-id>` (CLI + help row). The old
277
+ // build had commit.summary with cli path ["commit","summary"] surface "both"
278
+ // (capability-registry.ts:260-266); v2 kept only the cw_commit_summary MCP
279
+ // tool and dropped both the CLI binding and the COMMAND_HELP_ROWS entry, so
280
+ // `cw commit summary` mis-read "summary" as the run id. Path ["commit","summary"]
281
+ // is 2 tokens; dispatch consumes 1, so positionals[0] is the run id.
282
+ (0, registry_core_1.attachCliBinding)("commit.summary", {
283
+ path: ["commit", "summary"],
284
+ jsonMode: "flag",
285
+ handler: (args) => {
286
+ const commitSummary = loadCommitSummary();
287
+ const summary = commitSummary.commitSummaryCli({ ...args.options, runId: (0, io_1.required)(args.positionals[0], "run id") });
288
+ return { json: summary, text: `${commitSummary.formatCommitSummaryText(summary)}\n` };
289
+ },
290
+ });
291
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("commit.summary").mcp.handler = (args) => loadCommitSummary().commitSummaryCli(args);
292
+ // ---------------------------------------------------------------------
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ // wiring/capability-table/registry-core.ts — the shared machinery every
3
+ // domain slice registers into: REGISTRY, REGISTRY_BY_CAPABILITY,
4
+ // attachCliBinding, addCliOnlyCapability, and the read-only query
5
+ // functions (findCapability*, cliCapabilities, mcpToolDefinitions,
6
+ // declaredMcpTools). Also owns the small set of capability BODIES that
7
+ // must be in scope when MCP_TOOL_DATA.map() builds REGISTRY at module
8
+ // load (MCP_REAL_HANDLERS below) — kept here, not in a domain slice, to
9
+ // avoid a circular import (a slice needing attachCliBinding from this
10
+ // file, while this file would need a handler body FROM that slice).
11
+ //
12
+ // No slice file imports another slice file; every slice imports ONLY
13
+ // from this file, core/capability-data.ts, and shell/core as needed.
14
+ // index.ts imports this file plus every slice and composes them in the
15
+ // exact original source order (REGISTRY order is a pinned behavior —
16
+ // tools/list order, gen-parity-doc's byte-diff gate, cw help line order).
17
+ //
18
+ // Split out of core/capability-table.ts's "Public table-derived API"
19
+ // section, byte-for-byte (this file's body is the ORIGINAL file's own
20
+ // text, extracted with sed line ranges, not retyped).
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.REGISTRY_BY_CAPABILITY = exports.REGISTRY = void 0;
23
+ exports.listBundledWorkflows = listBundledWorkflows;
24
+ exports.listBundledSandboxProfiles = listBundledSandboxProfiles;
25
+ exports.statusPayload = statusPayload;
26
+ exports.attachCliBinding = attachCliBinding;
27
+ exports.addCliOnlyCapability = addCliOnlyCapability;
28
+ exports.findCapability = findCapability;
29
+ exports.findCapabilityByCliPath = findCapabilityByCliPath;
30
+ exports.cliCapabilities = cliCapabilities;
31
+ exports.mcpToolDefinitions = mcpToolDefinitions;
32
+ exports.declaredMcpTools = declaredMcpTools;
33
+ exports.findCapabilityByMcpTool = findCapabilityByMcpTool;
34
+ const capability_data_1 = require("../../core/capability-data");
35
+ const io_1 = require("../../cli/io");
36
+ // Every capability-table module (this file plus each domain slice) is
37
+ // required unconditionally at CLI/MCP startup, for every single command
38
+ // (index.ts's whole point is to populate REGISTRY before dispatch can
39
+ // look anything up) — so a top-level `import` of a shell module here
40
+ // means EVERY invocation pays that module's full load cost, even the
41
+ // 99% of commands that never touch `status`/`summary.refresh`/`list`.
42
+ // Requiring these 4 lazily (inside the handler that actually uses them)
43
+ // measured live: this file alone was ~30-45ms of a ~75-100ms `cw --version`
44
+ // require chain, the single largest slice. The 8 domain slices have the
45
+ // same shape and are a natural follow-up, not attempted here.
46
+ function loadRunStore() {
47
+ return require("../../shell/run-store");
48
+ }
49
+ function loadOperatorUx() {
50
+ return require("../../shell/operator-ux");
51
+ }
52
+ function loadWorkflowAppLoader() {
53
+ return require("../../shell/workflow-app-loader");
54
+ }
55
+ function loadStateExplosionCli() {
56
+ return require("../../shell/state-explosion-cli");
57
+ }
58
+ /** Real handlers implemented at THIS milestone, keyed by capability id.
59
+ * Every tool row not listed here gets `notYetImplemented`. Kept as a
60
+ * small side table (rather than inlined into MCP_TOOL_DATA above) so the
61
+ * 196-row literal above stays a pure, mechanically-checkable transcript
62
+ * of the spec table — handler wiring is a separate, obviously-later-
63
+ * editable concern. */
64
+ const MCP_REAL_HANDLERS = {
65
+ list: () => listBundledWorkflows(),
66
+ "sandbox.list": () => listBundledSandboxProfiles(),
67
+ status: (args) => statusPayload(optionalString(args.runId)),
68
+ "summary.refresh": (args) => loadStateExplosionCli().summaryRefreshCli((0, io_1.required)(optionalString(args.runId), "run id"), args),
69
+ "summary.show": (args) => loadStateExplosionCli().summaryShowCli((0, io_1.required)(optionalString(args.runId), "run id"), args),
70
+ };
71
+ function optionalString(value) {
72
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
73
+ }
74
+ /** `cw list` / `cw_list` (MILESTONE 12) — the real discovery over every
75
+ * `apps/*\/app.json` + legacy `workflows/*.workflow.js` on disk, per
76
+ * `listWorkflowsShallow` (shell/workflow-app-loader.ts). */
77
+ function listBundledWorkflows() {
78
+ return loadWorkflowAppLoader().listWorkflowsShallow();
79
+ }
80
+ /** PLACEHOLDER (milestone 5, execution-backend/sandbox) — the real
81
+ * `sandbox.list` resolves and stamps each of the 4 bundled profiles
82
+ * (default/readonly/workspace-write/locked-down) with real path lists
83
+ * via `resolveSandboxProfile` (SPEC/execution-backend.md). This
84
+ * milestone reproduces only the id/title/schemaVersion subset that
85
+ * mcp-basic.case.js checks. */
86
+ function listBundledSandboxProfiles() {
87
+ return [
88
+ { schemaVersion: 1, id: "default", title: "Default Worker Boundary" },
89
+ { schemaVersion: 1, id: "readonly", title: "Readonly Workspace" },
90
+ { schemaVersion: 1, id: "workspace-write", title: "Workspace Write" },
91
+ { schemaVersion: 1, id: "locked-down", title: "Locked Down" },
92
+ ];
93
+ }
94
+ /** `cw status` / `cw_status` — SPEC/cli-surface.md pins the no-id JSON
95
+ * shape exactly (`{runId:null, nextActions}`); a real run id resolves to
96
+ * `summarizeRun`'s payload (MILESTONE 11, reporting/observability). */
97
+ function statusPayload(runId, cwd) {
98
+ const operatorUx = loadOperatorUx();
99
+ if (!runId) {
100
+ return { runId: null, nextActions: operatorUx.adviseNoRun() };
101
+ }
102
+ const run = loadRunStore().loadRunFromCwd(runId, cwd || process.cwd());
103
+ return operatorUx.summarizeRun(run);
104
+ }
105
+ // ---------------------------------------------------------------------
106
+ // Public table-derived API
107
+ // ---------------------------------------------------------------------
108
+ function buildMcpBinding(row) {
109
+ const handler = MCP_REAL_HANDLERS[row.capability] ?? (0, capability_data_1.notYetImplemented)(row.capability);
110
+ // A transcript entry like "runId, workerId" is TWO AND-required args (the
111
+ // spec table's comma form), while "topicId|id" is one OR-group. mcp/dispatch's
112
+ // validator only splits on `|`, so expand each comma-joined transcript entry
113
+ // into its separate AND-groups here (the one place the row shape is turned
114
+ // into the runtime McpBinding.requiredArgs contract).
115
+ const requiredArgs = row.requiredArgs.flatMap((group) => group.split(",").map((entry) => entry.trim()).filter(Boolean));
116
+ return {
117
+ tool: row.tool,
118
+ requiredArgs: requiredArgs.length ? requiredArgs : undefined,
119
+ properties: row.properties,
120
+ description: row.description,
121
+ handler,
122
+ };
123
+ }
124
+ /** The full capability table: one row per MCP tool (196, per SPEC/mcp.md),
125
+ * in the exact source order `tools/list` must report. CLI bindings are
126
+ * layered on top for the small set of capabilities this milestone also
127
+ * exposes on the CLI front door (see `CLI_ROWS` below); every other row
128
+ * is MCP-only AT THIS MILESTONE (not a permanent `mcp-only` declaration —
129
+ * just not yet CLI-wired; later milestones add the `cli` binding without
130
+ * touching this array's mcp side). */
131
+ exports.REGISTRY = capability_data_1.MCP_TOOL_DATA.map((row) => ({
132
+ capability: row.capability,
133
+ summary: row.description,
134
+ surface: "both",
135
+ mcp: buildMcpBinding(row),
136
+ }));
137
+ exports.REGISTRY_BY_CAPABILITY = new Map(exports.REGISTRY.map((row) => [row.capability, row]));
138
+ /** Attach (or replace) a CLI binding for an already-declared MCP capability.
139
+ * Used once below to wire `list`/`status`/`sandbox.list` onto the CLI
140
+ * front door too, without duplicating their row data. */
141
+ function attachCliBinding(capability, cli) {
142
+ const row = exports.REGISTRY_BY_CAPABILITY.get(capability);
143
+ if (!row)
144
+ throw new Error(`capability-table: cannot attach cli binding to undeclared capability ${capability}`);
145
+ row.cli = cli;
146
+ }
147
+ /** Declare a capability that is CLI-only at this milestone (`help`,
148
+ * `version` — both are permanently `cli-only` per SPEC/mcp.md's
149
+ * declared one-surface list, so no mcp row is created for them). */
150
+ function addCliOnlyCapability(capability, summary, cli, reason, entry) {
151
+ const row = { capability, summary, surface: "cli-only", cli, reason, ...(entry ? { entry } : {}) };
152
+ exports.REGISTRY.push(row);
153
+ exports.REGISTRY_BY_CAPABILITY.set(capability, row);
154
+ }
155
+ /** Returns the declared row for a capability id, or undefined. */
156
+ function findCapability(capability) {
157
+ return exports.REGISTRY_BY_CAPABILITY.get(capability);
158
+ }
159
+ /** Returns the declared row whose `cli.path` matches `path` exactly
160
+ * (path[0] is the verb). Used by cli/dispatch.ts's generic executor.
161
+ * A single-token command also matches a row's `caseTokens` alias list, so
162
+ * an alias (e.g. `audit-run`) dispatches to the same handler as its verb. */
163
+ function findCapabilityByCliPath(path) {
164
+ for (const row of exports.REGISTRY) {
165
+ if (row.cli && row.cli.path.length === path.length && row.cli.path.every((p, i) => p === path[i])) {
166
+ return row;
167
+ }
168
+ }
169
+ if (path.length === 1) {
170
+ for (const row of exports.REGISTRY) {
171
+ if (row.cli && row.cli.caseTokens && row.cli.caseTokens.includes(path[0]))
172
+ return row;
173
+ }
174
+ }
175
+ return undefined;
176
+ }
177
+ /** Every capability row that declares a `cli` binding, in registry order.
178
+ * Used to derive `formatCommandHelp`'s per-verb subcommand rows. */
179
+ function cliCapabilities() {
180
+ return exports.REGISTRY.filter((row) => Boolean(row.cli));
181
+ }
182
+ /** `tools/list`'s exact array, in the pinned source order. */
183
+ function mcpToolDefinitions() {
184
+ const definitions = [];
185
+ for (const row of exports.REGISTRY) {
186
+ if (!row.mcp)
187
+ continue;
188
+ const overrides = capability_data_1.PROPERTY_OVERRIDES[row.mcp.tool] ?? {};
189
+ const properties = {};
190
+ for (const propName of row.mcp.properties) {
191
+ properties[propName] = overrides[propName] ?? (0, capability_data_1.stringProperty)(propName);
192
+ }
193
+ definitions.push({
194
+ name: row.mcp.tool,
195
+ description: row.mcp.description,
196
+ inputSchema: { type: "object", properties, additionalProperties: true },
197
+ });
198
+ }
199
+ return definitions;
200
+ }
201
+ /** Every declared MCP tool name, in `tools/list` order. */
202
+ function declaredMcpTools() {
203
+ return exports.REGISTRY.filter((row) => row.mcp).map((row) => row.mcp.tool);
204
+ }
205
+ /** Look up a capability row by its MCP tool name. */
206
+ function findCapabilityByMcpTool(tool) {
207
+ return exports.REGISTRY.find((row) => row.mcp && row.mcp.tool === tool);
208
+ }