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
@@ -51,6 +51,7 @@ const drive_1 = require("./drive");
51
51
  const commit_1 = require("./commit");
52
52
  const report_1 = require("./report");
53
53
  const operator_ux_1 = require("./operator-ux");
54
+ const agent_config_1 = require("./agent-config");
54
55
  function cwdFor(args) {
55
56
  return typeof args.cwd === "string" && args.cwd.trim() ? path.resolve(args.cwd) : process.cwd();
56
57
  }
@@ -60,6 +61,10 @@ function req(value, label) {
60
61
  throw new Error(`Missing ${label}`);
61
62
  return s;
62
63
  }
64
+ /** `--allow-unattested` (CLI: dashed key; MCP: allowUnattested). */
65
+ function allowUnattestedOption(args) {
66
+ return Boolean(args.allowUnattested ?? args["allow-unattested"]);
67
+ }
63
68
  function workerListCli(args) {
64
69
  const run = (0, run_store_1.loadRunFromCwd)(req(args.runId, "run id"), cwdFor(args));
65
70
  return (0, worker_isolation_1.listWorkerScopes)(run, { status: typeof args.status === "string" ? args.status : undefined });
@@ -72,13 +77,14 @@ function workerShowCli(args) {
72
77
  return scope;
73
78
  }
74
79
  function workerManifestCli(args) {
75
- const run = (0, run_store_1.loadRunFromCwd)(req(args.runId, "run id"), cwdFor(args));
76
- const scope = (0, worker_isolation_1.getWorkerScope)(run, req(args.workerId, "worker id"));
77
- if (!scope)
78
- throw new Error(`Unknown worker for run ${run.id}: ${args.workerId}`);
79
- const manifest = (0, worker_isolation_1.writeWorkerManifest)(run, scope);
80
- (0, run_store_1.saveCheckpoint)(run);
81
- return manifest;
80
+ return (0, run_store_1.withRunStateLock)(req(args.runId, "run id"), cwdFor(args), (run) => {
81
+ const scope = (0, worker_isolation_1.getWorkerScope)(run, req(args.workerId, "worker id"));
82
+ if (!scope)
83
+ throw new Error(`Unknown worker for run ${run.id}: ${args.workerId}`);
84
+ const manifest = (0, worker_isolation_1.writeWorkerManifest)(run, scope);
85
+ (0, run_store_1.saveCheckpoint)(run);
86
+ return manifest;
87
+ });
82
88
  }
83
89
  /** `cw worker output <run> <worker> <result>` — records the worker's result
84
90
  * and returns the full RunSummary, a byte-behavior port of the old build's
@@ -91,34 +97,40 @@ function workerManifestCli(args) {
91
97
  * tasks.completed, workers.byStatus, and loopStage. The drive loop does these
92
98
  * same steps itself around the bare accept, so it never routes through here. */
93
99
  function workerOutputCli(args) {
94
- const run = (0, run_store_1.loadRunFromCwd)(req(args.runId, "run id"), cwdFor(args));
95
- (0, worker_isolation_1.recordWorkerOutput)(run, req(args.workerId, "worker id"), req(args.resultPath, "result file"), {});
96
- run.loopStage = "observe";
97
- (0, dispatch_1.updatePhaseStatuses)(run);
98
- (0, drive_1.maybeExpandLoop)(run);
99
- (0, commit_1.commitState)(run, `worker:${req(args.workerId, "worker id")}:result`);
100
- (0, report_1.writeReport)(run);
101
- (0, run_store_1.saveCheckpoint)(run);
102
- return (0, operator_ux_1.summarizeRun)(run);
100
+ return (0, run_store_1.withRunStateLock)(req(args.runId, "run id"), cwdFor(args), (run) => {
101
+ (0, worker_isolation_1.recordWorkerOutput)(run, req(args.workerId, "worker id"), req(args.resultPath, "result file"), {
102
+ requireAttestedTelemetry: (0, agent_config_1.resolveAgentConfig)(args).requireAttestedTelemetry,
103
+ allowUnattested: allowUnattestedOption(args),
104
+ });
105
+ run.loopStage = "observe";
106
+ (0, dispatch_1.updatePhaseStatuses)(run);
107
+ (0, drive_1.maybeExpandLoop)(run);
108
+ (0, commit_1.commitState)(run, `worker:${req(args.workerId, "worker id")}:result`);
109
+ (0, report_1.writeReport)(run);
110
+ (0, run_store_1.saveCheckpoint)(run);
111
+ return (0, operator_ux_1.summarizeRun)(run);
112
+ });
103
113
  }
104
114
  function workerFailCli(args) {
105
- const run = (0, run_store_1.loadRunFromCwd)(req(args.runId, "run id"), cwdFor(args));
106
- const message = String(args.message || req(args.resultPath, "failure message"));
107
- const scope = (0, worker_isolation_1.recordWorkerFailure)(run, req(args.workerId, "worker id"), message, {
108
- code: typeof args.code === "string" ? args.code : undefined,
109
- path: typeof args.path === "string" ? args.path : undefined,
110
- retryable: args.retryable !== undefined ? Boolean(args.retryable) : undefined,
115
+ return (0, run_store_1.withRunStateLock)(req(args.runId, "run id"), cwdFor(args), (run) => {
116
+ const message = String(args.message || req(args.resultPath, "failure message"));
117
+ const scope = (0, worker_isolation_1.recordWorkerFailure)(run, req(args.workerId, "worker id"), message, {
118
+ code: typeof args.code === "string" ? args.code : undefined,
119
+ path: typeof args.path === "string" ? args.path : undefined,
120
+ retryable: args.retryable !== undefined ? Boolean(args.retryable) : undefined,
121
+ });
122
+ (0, run_store_1.saveCheckpoint)(run);
123
+ return scope;
111
124
  });
112
- (0, run_store_1.saveCheckpoint)(run);
113
- return scope;
114
125
  }
115
126
  /** validate returns the boundary violation (null when the write path is
116
127
  * allowed) and signals a violation through a non-zero exit code, not just
117
128
  * stdout — a validate verb must report an invalid verdict via its exit code. */
118
129
  function workerValidateCli(args) {
119
- const run = (0, run_store_1.loadRunFromCwd)(req(args.runId, "run id"), cwdFor(args));
120
- const target = args.path || args.resultPath;
121
- const violation = (0, worker_isolation_1.validateWorkerBoundary)(run, req(args.workerId, "worker id"), target ? { path: String(target) } : {});
122
- (0, run_store_1.saveCheckpoint)(run);
123
- return { violation, exitCode: violation ? 1 : undefined };
130
+ return (0, run_store_1.withRunStateLock)(req(args.runId, "run id"), cwdFor(args), (run) => {
131
+ const target = args.path || args.resultPath;
132
+ const violation = (0, worker_isolation_1.validateWorkerBoundary)(run, req(args.workerId, "worker id"), target ? { path: String(target) } : {});
133
+ (0, run_store_1.saveCheckpoint)(run);
134
+ return { violation, exitCode: violation ? 1 : undefined };
135
+ });
124
136
  }
@@ -106,6 +106,7 @@ function workerBlackboardManifest(run, task) {
106
106
  const verifier_1 = require("./verifier");
107
107
  const runner_1 = require("../core/pipeline/runner");
108
108
  const hash_1 = require("../core/hash");
109
+ const collate_1 = require("../core/util/collate");
109
110
  const telemetry_attestation_1 = require("../core/trust/telemetry-attestation");
110
111
  const telemetry_ledger_io_1 = require("./telemetry-ledger-io");
111
112
  exports.WORKER_ISOLATION_SCHEMA_VERSION = 1;
@@ -531,13 +532,37 @@ function recordWorkerOutput(run, workerId, resultPath, options = {}) {
531
532
  })
532
533
  : undefined;
533
534
  // Opt-in fail-closed gate (default off): when the operator requires
534
- // attested telemetry, a delegated hop whose verdict is not `attested`
535
- // is REJECTED here — BEFORE any accept-side state mutation — so the
536
- // drive parks it instead of recording unverifiable usage.
537
- if (options.requireAttestedTelemetry && telemetry && telemetry.status !== "attested") {
538
- const message = `Worker ${workerId} telemetry is ${telemetry.status} (${telemetry.reason || "unverified"}) and require-attested-telemetry is enabled refusing to accept a hop whose usage cannot be cryptographically verified`;
539
- recordWorkerFailure(run, workerId, message, { code: "telemetry-unattested-blocked", path: absoluteResultPath, retryable: false });
540
- throw new Error(message);
535
+ // attested telemetry, an accept whose usage cannot be verified is
536
+ // REJECTED here — BEFORE any accept-side state mutation — so the drive
537
+ // parks it instead of recording unverifiable usage. This fires on BOTH
538
+ // shapes: a delegation present but not attested (telemetry.status !==
539
+ // "attested"), and NO delegation metadata at all. The second shape is
540
+ // the gap a manual `cw worker output` / `cw result` accept used to slip
541
+ // through silently: options.agentDelegation was simply absent, so
542
+ // `telemetry` was undefined and the old `telemetry &&` condition
543
+ // short-circuited false — an unattested result could be laundered
544
+ // through the manual accept path even with the require flag on.
545
+ // --allow-unattested is the operator's explicit way past this: it never
546
+ // skips the gate silently, it records a telemetry.gate-override event.
547
+ if (options.requireAttestedTelemetry && (!telemetry || telemetry.status !== "attested")) {
548
+ if (options.allowUnattested) {
549
+ (0, trust_audit_1.recordTrustAuditEvent)(run, {
550
+ kind: "telemetry.gate-override",
551
+ decision: "allowed",
552
+ source: "operator",
553
+ workerId,
554
+ taskId: task.id,
555
+ metadata: { reason: "--allow-unattested", telemetryStatus: telemetry ? telemetry.status : "absent" },
556
+ });
557
+ }
558
+ else {
559
+ const code = telemetry ? "telemetry-unattested-blocked" : "telemetry-missing-blocked";
560
+ const message = telemetry
561
+ ? `Worker ${workerId} telemetry is ${telemetry.status} (${telemetry.reason || "unverified"}) and require-attested-telemetry is enabled — refusing to accept a hop whose usage cannot be cryptographically verified`
562
+ : `Worker ${workerId} carries no agent-delegation telemetry at all and require-attested-telemetry is enabled — refusing to accept an unattested manual result (pass --allow-unattested to record an audited override)`;
563
+ recordWorkerFailure(run, workerId, message, { code, path: absoluteResultPath, retryable: false });
564
+ throw new Error(message);
565
+ }
541
566
  }
542
567
  const agentDelegationMeta = delegation
543
568
  ? {
@@ -797,7 +822,7 @@ function listWorkerScopes(run, options = {}) {
797
822
  // silently drops workers whenever run.workers was reset.
798
823
  const merged = mergeScopes(run.workers || [], loadWorkerScopesFromDisk(run));
799
824
  run.workers = merged;
800
- const workers = merged.slice().sort((a, b) => a.id.localeCompare(b.id));
825
+ const workers = merged.slice().sort((a, b) => (0, collate_1.stableCompare)(a.id, b.id));
801
826
  return options.status ? workers.filter((w) => w.status === options.status) : workers;
802
827
  }
803
828
  function countByStatus(workers) {
@@ -824,7 +849,7 @@ function countBucket(values) {
824
849
  return counts;
825
850
  }
826
851
  function formatCountBucket(counts) {
827
- const entries = Object.entries(counts).sort(([a], [b]) => a.localeCompare(b));
852
+ const entries = Object.entries(counts).sort(([a], [b]) => (0, collate_1.stableCompare)(a, b));
828
853
  if (!entries.length)
829
854
  return "none";
830
855
  return entries.map(([k, v]) => `${k}=${v}`).join(", ");
@@ -75,6 +75,7 @@ const path = __importStar(require("node:path"));
75
75
  const app_schema_1 = require("../core/workflow-apps/app-schema");
76
76
  const sandbox_profile_1 = require("./sandbox-profile");
77
77
  const version_1 = require("../core/version");
78
+ const collate_1 = require("../core/util/collate");
78
79
  class WorkflowAppNotFoundError extends Error {
79
80
  constructor(appId) {
80
81
  super(`Workflow app not found: ${appId}`);
@@ -421,10 +422,10 @@ function listWorkflowAppRecords() {
421
422
  ...loadWorkflowFiles(workflowsDir).map((file) => loadWorkflowAppFromEntrypoint(file)),
422
423
  ...loadAppManifestFiles(appsDir).map((file) => loadWorkflowAppFromManifest(file)),
423
424
  ].sort((left, right) => {
424
- const byId = left.app.id.localeCompare(right.app.id);
425
+ const byId = (0, collate_1.stableCompare)(left.app.id, right.app.id);
425
426
  if (byId)
426
427
  return byId;
427
- return sourcePathOf(left).localeCompare(sourcePathOf(right));
428
+ return (0, collate_1.stableCompare)(sourcePathOf(left), sourcePathOf(right));
428
429
  });
429
430
  const seen = new Map();
430
431
  for (const record of records) {
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ // wiring/capability-table/basics.ts — MILESTONE 2's CLI bindings (version,
3
+ // list, status, sandbox.list). Split out of core/capability-table.ts,
4
+ // byte-for-byte (extracted with sed, not retyped).
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const registry_core_1 = require("./registry-core");
7
+ // ---------------------------------------------------------------------
8
+ // CLI bindings wired at THIS milestone (version, list, status,
9
+ // sandbox.list). `version` is cli-only per SPEC/mcp.md's declared
10
+ // one-surface list (`help` is handled directly by cli/entry.ts's
11
+ // top-level flag redirect, same as milestone 1 — it is not itself a
12
+ // dispatchable command row); `list`/`status`/`sandbox.list` reuse the mcp
13
+ // row's capability id and get a cli binding layered on top. Every handler
14
+ // below returns a `CliHandlerResult`; core/ never touches process.stdout
15
+ // or process.exitCode directly (see docs/rebuild/PLAN.md's core/shell split) —
16
+ // cli/dispatch.ts's generic executor performs the actual write.
17
+ // ---------------------------------------------------------------------
18
+ const version_1 = require("../../core/version");
19
+ const help_1 = require("../../core/format/help");
20
+ const completion_1 = require("../../core/format/completion");
21
+ const io_1 = require("../../cli/io");
22
+ // This whole module is required unconditionally at startup for EVERY
23
+ // command (see wiring/capability-table/index.ts) — a top-level import of
24
+ // `shell/workflow-app-loader` here means even `cw --version` pays its
25
+ // load cost, though only `search`'s handler ever calls it.
26
+ function loadWorkflowAppLoader() {
27
+ return require("../../shell/workflow-app-loader");
28
+ }
29
+ (0, registry_core_1.addCliOnlyCapability)("version", "Print the current cool-workflow version.", {
30
+ path: ["version"],
31
+ jsonMode: "default",
32
+ handler: () => ({ text: `${version_1.CURRENT_COOL_WORKFLOW_VERSION}\n` }),
33
+ }, "version is a local, no-run-state print; the old build never gave it an MCP peer.");
34
+ /** `cw completion <bash|zsh|fish>` — a static shell-completion script for
35
+ * the top-level command word list (core/format/completion.ts). CLI-only:
36
+ * a shell-integration script is meaningless as an MCP tool result. */
37
+ (0, registry_core_1.addCliOnlyCapability)("completion", "Print a shell-completion script for the top-level cw commands (bash, zsh, or fish).", {
38
+ path: ["completion"],
39
+ jsonMode: "human",
40
+ handler: (args) => {
41
+ const shell = (0, io_1.optionalArg)(args.positionals[0]);
42
+ if (!shell)
43
+ throw new Error('Missing shell name.\n Try: cw completion bash|zsh|fish');
44
+ return { text: (0, completion_1.formatCompletionScript)(shell) };
45
+ },
46
+ }, "shell-completion output is a script for the user's OWN shell to source, not data an MCP client could use — CLI-only, same reasoning as doctor/fix.");
47
+ /** `cw search <keyword>` — filters the SAME real app discovery `cw list`
48
+ * shows, by id/title/summary (byte-behavior port of cli/dispatch.ts's
49
+ * milestone-1 carry-over `search` arm, moved here so the dispatchLegacy
50
+ * switch shrinks per its file header's rule). The old v0.1.98 CLI DID
51
+ * have its own `cw help search` row (`docs/rebuild/SPEC/cli-help/
52
+ * search.txt`: "cw search Search workflow apps by keyword (title,
53
+ * description, id)."); `hiddenFromHelp` here was a rebuild regression
54
+ * that hid it, not a preserved old-build quirk — removed so the row
55
+ * shows again. The summary text below is intentionally NOT byte-matched
56
+ * to that old capture: the old wording says "description," but the
57
+ * filter below matches `summary` (there is no `description` field) — so
58
+ * the summary text stays accurate to current fields rather than
59
+ * reproducing the old capture's stale terminology. */
60
+ (0, registry_core_1.addCliOnlyCapability)("search", "Search bundled workflows by id/title/summary keyword.", {
61
+ path: ["search"],
62
+ jsonMode: "flag",
63
+ handler: (args) => {
64
+ const keyword = args.positionals.join(" ");
65
+ if (!keyword.trim()) {
66
+ throw new Error('Missing search keyword.\n Tip: cw search architecture to find workflows about architecture.');
67
+ }
68
+ const lower = keyword.toLowerCase();
69
+ const results = loadWorkflowAppLoader().listWorkflowApps()
70
+ .filter((a) => String(a.title).toLowerCase().includes(lower) ||
71
+ String(a.summary).toLowerCase().includes(lower) ||
72
+ String(a.id).toLowerCase().includes(lower))
73
+ .map((a) => ({ id: String(a.id), title: String(a.title), summary: String(a.summary) }));
74
+ return { json: results, text: (0, help_1.formatSearchResults)(keyword, results) };
75
+ },
76
+ }, "CLI-only discovery helper over the same real app data cw list shows; no MCP client needs a free-text search tool alongside cw_list's structured output.");
77
+ (0, registry_core_1.attachCliBinding)("list", {
78
+ path: ["list"],
79
+ jsonMode: "default",
80
+ handler: () => ({ json: (0, registry_core_1.listBundledWorkflows)() }),
81
+ });
82
+ (0, registry_core_1.attachCliBinding)("status", {
83
+ path: ["status"],
84
+ jsonMode: "flag",
85
+ handler: (args) => ({ json: (0, registry_core_1.statusPayload)(args.positionals[0]) }),
86
+ });
87
+ (0, registry_core_1.attachCliBinding)("sandbox.list", {
88
+ path: ["sandbox", "list"],
89
+ jsonMode: "default",
90
+ handler: () => ({ json: (0, registry_core_1.listBundledSandboxProfiles)() }),
91
+ });
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ // wiring/capability-table/exec-backend.ts — MILESTONE 5 (execution backend,
3
+ // agent spawn, sandbox) CLI bindings: sandbox.*, backend.*, app.run. Split
4
+ // out of core/capability-table.ts, byte-for-byte (extracted with sed, not
5
+ // retyped).
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const registry_core_1 = require("./registry-core");
8
+ // MILESTONE 5 (execution backend, agent spawn, sandbox) CLI bindings:
9
+ // sandbox.list|show|validate, backend.list|show|probe,
10
+ // backend.agent.config.show|set, doctor, fix. Handler BODIES live in
11
+ // shell/exec-backend-cli.ts / shell/doctor.ts (impure — env/fs reads);
12
+ // this table only wires argv shape -> handler call, per cli/dispatch.ts's
13
+ // generic executor contract. `sandbox.list`/`backend.list` are ALREADY
14
+ // declared MCP-only rows from milestone 2 (MCP_TOOL_DATA above) — this
15
+ // section layers a `cli` binding onto them (attachCliBinding) and replaces
16
+ // their milestone-2 placeholder `mcp.handler` with the real body, exactly
17
+ // as milestones 3/4 did for their own rows.
18
+ // ---------------------------------------------------------------------
19
+ const exec_backend_cli_1 = require("../../shell/exec-backend-cli");
20
+ const io_1 = require("../../cli/io");
21
+ // This slice is required unconditionally at startup for every command;
22
+ // load doctor/app-run-cli lazily so only doctor/fix/sandbox.choose/
23
+ // sandbox.resolve/app.run handlers pay their require cost.
24
+ function loadDoctor() {
25
+ return require("../../shell/doctor");
26
+ }
27
+ function loadAppRunCli() {
28
+ return require("../../shell/app-run-cli");
29
+ }
30
+ (0, registry_core_1.attachCliBinding)("sandbox.list", {
31
+ path: ["sandbox", "list"],
32
+ jsonMode: "default",
33
+ handler: (args) => ({ json: (0, exec_backend_cli_1.listSandboxProfilesCli)(args.options) }),
34
+ });
35
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("sandbox.list").mcp.handler = (args) => (0, exec_backend_cli_1.listSandboxProfilesCli)(args);
36
+ // GAP #24: cw_sandbox_choose / cw_sandbox_resolve + cw_app_run were declared
37
+ // MCP-only rows with the notYetImplemented placeholder handler. Wire them to
38
+ // the ported shell bodies (both are MCP-only in the old build — no CLI path).
39
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("sandbox.choose").mcp.handler = (args) => loadAppRunCli().sandboxChooseCli(args);
40
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("sandbox.resolve").mcp.handler = (args) => loadAppRunCli().sandboxChooseCli(args);
41
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("app.run").mcp.handler = (args) => loadAppRunCli().appRunCli(args);
42
+ (0, registry_core_1.attachCliBinding)("sandbox.show", {
43
+ path: ["sandbox", "show"],
44
+ jsonMode: "default",
45
+ handler: (args) => ({ json: (0, exec_backend_cli_1.showSandboxProfileCli)((0, io_1.required)(args.positionals[0], "profile id"), args.options) }),
46
+ });
47
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("sandbox.show").mcp.handler = (args) => (0, exec_backend_cli_1.showSandboxProfileCli)((0, io_1.required)((0, io_1.optionalArg)(args.profileId), "profile id"), args);
48
+ (0, registry_core_1.attachCliBinding)("sandbox.validate", {
49
+ path: ["sandbox", "validate"],
50
+ jsonMode: "default",
51
+ handler: (args) => {
52
+ const result = (0, exec_backend_cli_1.validateSandboxProfileCli)((0, io_1.required)(args.positionals[0], "profile file"), args.options);
53
+ return { json: result, exitCode: result.valid ? undefined : 1 };
54
+ },
55
+ });
56
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("sandbox.validate").mcp.handler = (args) => (0, exec_backend_cli_1.validateSandboxProfileCli)((0, io_1.required)((0, io_1.optionalArg)(args.profileFile), "profile file"), args);
57
+ // PARITY: `sandbox.choose`/`sandbox.resolve` are BOTH-surface capabilities
58
+ // per SPEC/mcp.md (old build cli.path ["sandbox","choose"]/["sandbox",
59
+ // "resolve"]) — they were left MCP-only at GAP #24 (see the comment
60
+ // above sandboxChooseCli's mcp.handler wiring). Attach the same, already-
61
+ // working shell body as the cli.handler too, so the CLI front door and
62
+ // the parity payload probe both reach it (no new business logic, same
63
+ // function both surfaces already call over MCP).
64
+ (0, registry_core_1.attachCliBinding)("sandbox.choose", {
65
+ path: ["sandbox", "choose"],
66
+ jsonMode: "default",
67
+ handler: (args) => ({ json: loadAppRunCli().sandboxChooseCli(args.options) }),
68
+ });
69
+ (0, registry_core_1.attachCliBinding)("sandbox.resolve", {
70
+ path: ["sandbox", "resolve"],
71
+ jsonMode: "default",
72
+ handler: (args) => ({ json: loadAppRunCli().sandboxChooseCli(args.options) }),
73
+ });
74
+ (0, registry_core_1.attachCliBinding)("backend.list", {
75
+ path: ["backend", "list"],
76
+ jsonMode: "default",
77
+ handler: () => ({ json: (0, exec_backend_cli_1.listBackendsCli)() }),
78
+ });
79
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.list").mcp.handler = () => (0, exec_backend_cli_1.listBackendsCli)();
80
+ (0, registry_core_1.attachCliBinding)("backend.show", {
81
+ path: ["backend", "show"],
82
+ jsonMode: "default",
83
+ handler: (args) => ({ json: (0, exec_backend_cli_1.showBackendCli)((0, io_1.required)(args.positionals[0], "backend id")) }),
84
+ });
85
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.show").mcp.handler = (args) => (0, exec_backend_cli_1.showBackendCli)((0, io_1.required)((0, io_1.optionalArg)(args.backendId), "backend id"));
86
+ (0, registry_core_1.attachCliBinding)("backend.probe", {
87
+ path: ["backend", "probe"],
88
+ jsonMode: "default",
89
+ handler: (args) => ({ json: (0, exec_backend_cli_1.probeBackendCli)(args.positionals[0], args.options) }),
90
+ });
91
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.probe").mcp.handler = (args) => (0, exec_backend_cli_1.probeBackendCli)((0, io_1.optionalArg)(args.backendId), args);
92
+ // `backend agent config [show]` = read-only; `backend agent config set
93
+ // ...` = mutating. CLI path is ["backend", "agent"] (2 tokens, matching
94
+ // dispatchTable's supported path lengths); the remaining positionals
95
+ // ("config", "show"/"set") are read inside the handler, byte-exact to the
96
+ // old build's handleBackend "agent" case (src/cli/handlers/
97
+ // operational.ts:52-62).
98
+ (0, registry_core_1.attachCliBinding)("backend.agent.config.show", {
99
+ path: ["backend", "agent"],
100
+ helpPath: ["backend", "agent", "config"],
101
+ jsonMode: "default",
102
+ handler: (args) => {
103
+ const action = args.positionals[1];
104
+ if (action === "set")
105
+ return { json: (0, exec_backend_cli_1.backendAgentConfigSet)(args.options) };
106
+ return { json: (0, exec_backend_cli_1.backendAgentConfigShow)(args.options) };
107
+ },
108
+ });
109
+ // `backend.agent.config.set` shares the SAME dispatch path/handler as
110
+ // `.show` above (dispatchTable only supports 2-token paths, and the
111
+ // show-vs-set branch lives inside that one handler on positionals[1] —
112
+ // byte-exact to the old build's handleBackend "agent" case). This second
113
+ // attachCliBinding call exists ONLY so `cw help backend` lists both rows
114
+ // (cliCommandHelpRows iterates cliCapabilities(), one row per capability),
115
+ // matching the old registry's two declared rows sharing one caseTokens
116
+ // group; dispatchTable itself never reaches this row a second time because
117
+ // `backend.agent.config.show`'s row is found first by
118
+ // findCapabilityByCliPath's linear scan and its handler already covers
119
+ // both actions.
120
+ (0, registry_core_1.attachCliBinding)("backend.agent.config.set", {
121
+ path: ["backend", "agent"],
122
+ helpPath: ["backend", "agent", "config"],
123
+ jsonMode: "default",
124
+ handler: (args) => {
125
+ const action = args.positionals[1];
126
+ if (action === "set")
127
+ return { json: (0, exec_backend_cli_1.backendAgentConfigSet)(args.options) };
128
+ return { json: (0, exec_backend_cli_1.backendAgentConfigShow)(args.options) };
129
+ },
130
+ });
131
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.agent.config.show").mcp.handler = (args) => (0, exec_backend_cli_1.backendAgentConfigShow)(args);
132
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.agent.config.set").mcp.handler = (args) => (0, exec_backend_cli_1.backendAgentConfigSet)(args);
133
+ // PARITY: `backend.agent.config.set` mutates $CW_HOME/agent-config.json
134
+ // (secret-stripped) before returning the effective config; both surfaces
135
+ // perform the same write, so it is a documented opt-out from the
136
+ // read-payload probe, not an undocumented divergence.
137
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.agent.config.set").payloadIdentical = false;
138
+ registry_core_1.REGISTRY_BY_CAPABILITY.get("backend.agent.config.set").reason =
139
+ "Mutating: persists $CW_HOME/agent-config.json (secret-stripped) before returning the effective config; both surfaces perform the same write — it is a surface-mutating verb, not a read probe.";
140
+ (0, registry_core_1.addCliOnlyCapability)("doctor", "Diagnose the host for setup problems (Node version, agent backend, agent binary on PATH, git, writable home/repo state) and print an actionable fix per check.", {
141
+ path: ["doctor"],
142
+ jsonMode: "flag",
143
+ handler: (args) => {
144
+ const doctor = loadDoctor();
145
+ const report = doctor.runDoctor(args.options, process.env, String(args.options.cwd || process.cwd()));
146
+ // Byte-exact port of src/cli/command-surface.ts:170-176: both text
147
+ // branches are written as `${formatX(report)}\n` UNCONDITIONALLY —
148
+ // formatDoctorFixes already ends in its own "\n" (its last joined
149
+ // element is ""), so its case needs one MORE explicit "\n" here to
150
+ // reproduce that unconditional append; cli/dispatch.ts's generic
151
+ // renderer only appends "\n" when the text does NOT already end in
152
+ // one, so a bare `formatDoctorFixes(report)` here would silently
153
+ // drop the old build's trailing blank line.
154
+ const text = (0, io_1.wantsJson)(args.options) ? undefined : args.options.fix ? `${doctor.formatDoctorFixes(report)}\n` : doctor.formatDoctorReport(report);
155
+ return { json: report, text, exitCode: report.ok ? undefined : 1 };
156
+ },
157
+ }, "Environment diagnostics are inherently local to the CLI host — Node version, $PATH, $CW_HOME/cwd writability. An MCP client diagnosing the server process's environment is not meaningful; agents already receive the same readiness facts in their typed results (e.g. status: blocked, agentConfigured). Inspired by `brew doctor`.");
158
+ (0, registry_core_1.addCliOnlyCapability)("fix", "Print consolidated fix commands for CW setup issues.", {
159
+ path: ["fix"],
160
+ jsonMode: "human",
161
+ handler: (args) => {
162
+ const doctor = loadDoctor();
163
+ const report = doctor.runDoctor(args.options, process.env, String(args.options.cwd || process.cwd()));
164
+ // See the "doctor" handler's comment above: formatDoctorFixes
165
+ // already ends in "\n", so one more explicit "\n" here reproduces
166
+ // src/cli/command-surface.ts:126-130's unconditional
167
+ // `${formatDoctorFixes(report)}\n` write.
168
+ return { text: `${doctor.formatDoctorFixes(report)}\n`, exitCode: report.ok ? undefined : 1 };
169
+ },
170
+ }, "Environment fix commands are local diagnostics, same reasoning as doctor.");
171
+ // ---------------------------------------------------------------------
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ // wiring/capability-table/index.ts — composes the capability table from
3
+ // registry-core (the shared machinery) plus every domain slice, in the
4
+ // EXACT original source order (REGISTRY array order is a pinned
5
+ // behavior: tools/list order, gen-parity-doc's byte-diff gate, cw help
6
+ // line order). Each slice is a plain module whose top-level
7
+ // attachCliBinding/addCliOnlyCapability/REGISTRY_BY_CAPABILITY calls run
8
+ // once, at first import — Node's module system guarantees that happens
9
+ // in exactly the order these imports are written below, the same
10
+ // guarantee the single original file relied on for its own top-to-bottom
11
+ // statement order.
12
+ //
13
+ // No slice imports another slice; every slice imports only from
14
+ // registry-core.ts, core/capability-data.ts, and shell/core as needed —
15
+ // so this file is the only place the full domain list is assembled, and
16
+ // there is no cross-slice circular dependency to reason about.
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
29
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
30
+ };
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ __exportStar(require("./registry-core"), exports);
33
+ require("./basics");
34
+ require("./state");
35
+ require("./exec-backend");
36
+ require("./pipeline");
37
+ require("./trust-ledger");
38
+ require("./multi-agent");
39
+ require("./scheduling-registry");
40
+ require("./reporting");
41
+ require("./workflow-apps");
42
+ __exportStar(require("./parity"), exports);