cool-workflow 0.1.92 → 0.1.94

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 +104 -129
  4. package/apps/architecture-review/app.json +1 -1
  5. package/apps/architecture-review-fast/app.json +1 -1
  6. package/apps/architecture-review-fast/workflow.js +15 -2
  7. package/apps/end-to-end-golden-path/app.json +1 -1
  8. package/apps/pr-review-fix-ci/app.json +1 -1
  9. package/apps/release-cut/app.json +1 -1
  10. package/apps/research-synthesis/app.json +1 -1
  11. package/dist/capability-core.js +47 -0
  12. package/dist/capability-registry.js +2 -0
  13. package/dist/cli/command-surface.js +165 -1352
  14. package/dist/cli/format.js +56 -0
  15. package/dist/cli/handlers/audit.js +82 -0
  16. package/dist/cli/handlers/blackboard.js +81 -0
  17. package/dist/cli/handlers/candidate.js +40 -0
  18. package/dist/cli/handlers/clones.js +34 -0
  19. package/dist/cli/handlers/collaboration.js +61 -0
  20. package/dist/cli/handlers/eval.js +40 -0
  21. package/dist/cli/handlers/maintenance.js +107 -0
  22. package/dist/cli/handlers/multi-agent.js +165 -0
  23. package/dist/cli/handlers/node.js +41 -0
  24. package/dist/cli/handlers/operational.js +155 -0
  25. package/dist/cli/handlers/operator.js +146 -0
  26. package/dist/cli/handlers/registry.js +68 -0
  27. package/dist/cli/handlers/run.js +153 -0
  28. package/dist/cli/handlers/scheduling.js +126 -0
  29. package/dist/cli/handlers/workbench.js +41 -0
  30. package/dist/cli/handlers/worker.js +45 -0
  31. package/dist/cli/io.js +27 -0
  32. package/dist/cli/run-summary.js +45 -0
  33. package/dist/commit.js +0 -5
  34. package/dist/execution-backend.js +0 -11
  35. package/dist/mcp/tool-call.js +2 -0
  36. package/dist/mcp/tool-definitions.js +8 -0
  37. package/dist/orchestrator/app-operations.js +205 -0
  38. package/dist/orchestrator.js +41 -153
  39. package/dist/state-explosion.js +0 -7
  40. package/dist/term.js +0 -18
  41. package/dist/validation.js +0 -21
  42. package/dist/version.js +1 -1
  43. package/docs/agent-delegation-drive.7.md +13 -7
  44. package/docs/cli-mcp-parity.7.md +19 -2
  45. package/docs/contract-migration-tooling.7.md +3 -1
  46. package/docs/control-plane-scheduling.7.md +3 -1
  47. package/docs/durable-state-and-locking.7.md +3 -1
  48. package/docs/evidence-adoption-reasoning-chain.7.md +3 -1
  49. package/docs/execution-backends.7.md +3 -1
  50. package/docs/mcp-app-surface.7.md +12 -0
  51. package/docs/multi-agent-cli-mcp-surface.7.md +3 -1
  52. package/docs/multi-agent-eval-replay-harness.7.md +3 -1
  53. package/docs/multi-agent-operator-ux.7.md +3 -1
  54. package/docs/node-snapshot-diff-replay.7.md +3 -1
  55. package/docs/observability-cost-accounting.7.md +3 -1
  56. package/docs/project-index.md +16 -3
  57. package/docs/real-execution-backends.7.md +3 -1
  58. package/docs/release-and-migration.7.md +3 -1
  59. package/docs/release-tooling.7.md +3 -1
  60. package/docs/run-registry-control-plane.7.md +27 -3
  61. package/docs/run-retention-reclamation.7.md +3 -1
  62. package/docs/state-explosion-management.7.md +3 -1
  63. package/docs/team-collaboration.7.md +3 -1
  64. package/docs/web-desktop-workbench.7.md +3 -1
  65. package/manifest/plugin.manifest.json +1 -1
  66. package/package.json +1 -1
  67. package/scripts/agents/builtin-templates.json +3 -2
  68. package/scripts/agents/claude-p-agent.js +6 -6
  69. package/scripts/agents/codex-agent.js +17 -3
  70. package/scripts/agents/deepseek-agent.js +23 -0
  71. package/scripts/agents/gemini-opencode-agent.js +25 -0
  72. package/scripts/agents/opencode-agent.js +57 -8
  73. package/scripts/architecture-review-fast.js +19 -5
  74. package/scripts/bump-version.js +16 -0
  75. package/scripts/canonical-apps.js +4 -4
  76. package/scripts/dogfood-release.js +1 -1
  77. package/scripts/golden-path.js +4 -4
  78. package/scripts/parity-check.js +9 -1
  79. package/scripts/release-flow.js +35 -0
  80. package/scripts/vendor-preflight.js +127 -0
  81. package/scripts/version-sync-check.js +5 -0
@@ -83,7 +83,7 @@ const canonicalApps = [
83
83
  "--source",
84
84
  "plugins/cool-workflow/docs/workflow-app-framework.7.md",
85
85
  "--scope",
86
- "Cool Workflow v0.1.92",
86
+ "Cool Workflow v0.1.94",
87
87
  "--freshness",
88
88
  "as of release preparation"
89
89
  ]
@@ -117,14 +117,14 @@ function main() {
117
117
  assert.ok(summary, `${app.id} must appear in app list`);
118
118
  assert.equal(summary.sourceKind, "app-directory");
119
119
  assert.equal(summary.legacy, false);
120
- assert.equal(summary.version, "0.1.92");
120
+ assert.equal(summary.version, "0.1.94");
121
121
 
122
122
  const validation = runJson(["app", "validate", manifestPath]);
123
123
  assert.equal(validation.valid, true, `${app.id} manifest must validate`);
124
124
 
125
125
  const shown = runJson(["app", "show", app.id]);
126
126
  assert.equal(shown.app.id, app.id);
127
- assert.equal(shown.app.version, "0.1.92");
127
+ assert.equal(shown.app.version, "0.1.94");
128
128
  assert.ok(shown.app.metadata.canonical, `${app.id} must be marked canonical`);
129
129
  assert.ok(shown.app.sandboxProfiles.length > 0, `${app.id} must declare sandbox profiles`);
130
130
  assertTaskIdsUnique(shown);
@@ -135,7 +135,7 @@ function main() {
135
135
  const plan = runJson(["plan", app.id, ...app.args(workspace)]);
136
136
  const state = JSON.parse(fs.readFileSync(plan.statePath, "utf8"));
137
137
  assert.equal(state.workflow.app.id, app.id);
138
- assert.equal(state.workflow.app.version, "0.1.92");
138
+ assert.equal(state.workflow.app.version, "0.1.94");
139
139
  assert.equal(state.workflow.app.metadata.canonical, true);
140
140
  assert.ok(state.tasks.some((task) => task.requiresEvidence), `${app.id} plan must include evidence gates`);
141
141
  assert.ok(state.tasks.every((task) => task.sandboxProfileId), `${app.id} plan must include sandbox hints`);
@@ -6,7 +6,7 @@ const fs = require("node:fs");
6
6
  const path = require("node:path");
7
7
  const { CoolWorkflowRunner } = require("../dist/orchestrator.js");
8
8
 
9
- const TARGET_VERSION = "0.1.92";
9
+ const TARGET_VERSION = "0.1.94";
10
10
  const PREVIOUS_VERSION = "0.1.31";
11
11
  const pluginRoot = path.resolve(__dirname, "..");
12
12
  const repoRoot = path.resolve(pluginRoot, "..", "..");
@@ -33,7 +33,7 @@ function main() {
33
33
  const appValidation = runJson(["app", "validate", "end-to-end-golden-path"], pluginRoot);
34
34
  assert.equal(appValidation.valid, true);
35
35
  assert.equal(appValidation.summary.id, "end-to-end-golden-path");
36
- assert.equal(appValidation.summary.version, "0.1.92");
36
+ assert.equal(appValidation.summary.version, "0.1.94");
37
37
 
38
38
  const plan = runJson(
39
39
  [
@@ -42,7 +42,7 @@ function main() {
42
42
  "--repo",
43
43
  tmp,
44
44
  "--question",
45
- "Prove the deterministic v0.1.92 end-to-end golden path."
45
+ "Prove the deterministic v0.1.94 end-to-end golden path."
46
46
  ],
47
47
  pluginRoot
48
48
  );
@@ -52,7 +52,7 @@ function main() {
52
52
 
53
53
  let state = readJson(plan.statePath);
54
54
  assert.equal(state.workflow.app.id, "end-to-end-golden-path");
55
- assert.equal(state.workflow.app.version, "0.1.92");
55
+ assert.equal(state.workflow.app.version, "0.1.94");
56
56
  assert.equal(state.loopStage, "interpret");
57
57
 
58
58
  const dispatch = runJson(["dispatch", plan.runId, "--limit", "1", "--sandbox", "readonly"], tmp);
@@ -195,7 +195,7 @@ function main() {
195
195
  assert.equal(reportPath, plan.reportPath);
196
196
  assert.ok(fs.existsSync(reportPath));
197
197
  const report = fs.readFileSync(reportPath, "utf8");
198
- assert.match(report, /Workflow App: end-to-end-golden-path@0\.1\.92/);
198
+ assert.match(report, /Workflow App: end-to-end-golden-path@0\.1\.94/);
199
199
  assert.match(report, /## Candidates/);
200
200
  assert.match(report, /## Trust Audit/);
201
201
  assert.match(report, /## Acceptance Rationale/);
@@ -69,7 +69,15 @@ function cliDispatchTokens() {
69
69
  }
70
70
 
71
71
  function cliDispatchSources() {
72
- return [cli, path.join(pluginRoot, "dist", "cli", "command-surface.js")].filter((file) => fs.existsSync(file));
72
+ // The dispatcher + every per-command handler module carved out of it (the
73
+ // command-surface god-object decomposition). A verb's subcommand `case`s may
74
+ // live in dist/cli/handlers/<group>.js, so scan those too or their tokens read
75
+ // as "missing from dist/cli.js".
76
+ const handlersDir = path.join(pluginRoot, "dist", "cli", "handlers");
77
+ const handlerFiles = fs.existsSync(handlersDir)
78
+ ? fs.readdirSync(handlersDir).filter((name) => name.endsWith(".js")).map((name) => path.join(handlersDir, name))
79
+ : [];
80
+ return [cli, path.join(pluginRoot, "dist", "cli", "command-surface.js"), ...handlerFiles].filter((file) => fs.existsSync(file));
73
81
  }
74
82
 
75
83
  function cliHelpTokens() {
@@ -119,6 +119,40 @@ function runGate() {
119
119
  if (r.status !== 0) die("deterministic gate FAILED — fix findings in normal cycles, do not retry the release here.");
120
120
  }
121
121
 
122
+ // ---- 1b. vendor liveness preflight (cut only) ------------------------------
123
+ // CW promises claude/codex/gemini/deepseek all work; this proves it before a cut.
124
+ // LIVE: spends real tokens on each vendor, so it runs on --cut (release machine
125
+ // has the keys), not on --check. HARD-BLOCK: any vendor not green stops the cut.
126
+ // Escape hatch CW_SKIP_VENDOR_PREFLIGHT=1 (emergencies/offline). Test seam
127
+ // CW_RELEASE_FLOW_PREFLIGHT_CMD overrides the command (smoke stubs it).
128
+ function runVendorPreflight() {
129
+ if (!MODE_CUT) {
130
+ say("[1b/3] vendor preflight — skipped (check mode; live vendor calls run on --cut)");
131
+ return;
132
+ }
133
+ if (process.env.CW_SKIP_VENDOR_PREFLIGHT === "1") {
134
+ say("[1b/3] vendor preflight — SKIPPED via CW_SKIP_VENDOR_PREFLIGHT=1 (operator override)");
135
+ return;
136
+ }
137
+ const vendors = (process.env.CW_PREFLIGHT_VENDORS || "claude,codex,gemini,deepseek").trim();
138
+ const override = (process.env.CW_RELEASE_FLOW_PREFLIGHT_CMD || "").trim();
139
+ say(`[1b/3] vendor preflight — live liveness check: ${vendors}`);
140
+ let r;
141
+ if (override) {
142
+ // Test/override seam — shell:true, smoke/operator only, never the delegated agent.
143
+ r = spawnSync(override, { cwd: repoRoot, encoding: "utf8", shell: true, stdio: "inherit" });
144
+ } else {
145
+ r = spawnSync(process.execPath, [path.join(scriptsDir, "vendor-preflight.js"), "--vendors", vendors], {
146
+ cwd: repoRoot,
147
+ encoding: "utf8",
148
+ stdio: "inherit"
149
+ });
150
+ }
151
+ if (r.status !== 0) {
152
+ die("vendor preflight FAILED — a promised vendor is not live (missing CLI/key or broken). Users must not be limited to one vendor; fix before cutting, or set CW_SKIP_VENDOR_PREFLIGHT=1 to override.");
153
+ }
154
+ }
155
+
122
156
  // ---- 2. independent reviewer, delegated to the configured agent -------------
123
157
  function reviewerPromptBody() {
124
158
  // Reuse the committed reviewer spec as the prompt; strip YAML frontmatter.
@@ -476,6 +510,7 @@ function main() {
476
510
  const inputPath = path.join(markerDir, `review-input-${HEAD}.md`);
477
511
 
478
512
  runGate();
513
+ runVendorPreflight();
479
514
  fs.writeFileSync(inputPath, buildReviewerInput(resultPath));
480
515
  delegateReview(resultPath, inputPath);
481
516
  const capability = verifyVerdict(resultPath);
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // vendor-preflight.js — pre-release liveness gate for ALL agent vendors.
5
+ //
6
+ // CW promises that claude, codex, gemini, and deepseek all work. This gate
7
+ // proves it before a release: it runs EACH builtin wrapper against a tiny
8
+ // throwaway git repo with a trivial question and checks the wrapper returns a
9
+ // real, non-empty result. It is the enforcement behind that promise.
10
+ //
11
+ // HARD-BLOCK policy (operator chose this): a vendor that does not return a valid
12
+ // result — missing CLI, auth error, empty/garbage output — counts as FAIL and
13
+ // the gate exits nonzero. There is no "skip"; an unconfigured vendor blocks.
14
+ //
15
+ // This is a LIVE gate: it spends real tokens on each configured vendor. It is
16
+ // meant for the release machine (where all keys/logins exist), NOT the offline
17
+ // CI test suite. The offline smoke (test/vendor-preflight-smoke.js) exercises
18
+ // this script's LOGIC with PATH shims, no keys.
19
+ //
20
+ // Zero runtime dependency: node + git only. stdout is data (the matrix), stderr
21
+ // is diagnostics — same discipline as the rest of CW.
22
+ //
23
+ // Usage:
24
+ // node vendor-preflight.js [--vendors claude,codex,deepseek] [--question "..."]
25
+ // [--timeout-ms 180000] [--json] [--keep]
26
+ //
27
+ // Test seam (smoke/operator only):
28
+ // CW_PREFLIGHT_AGENTS_DIR override where wrapper scripts + builtin-templates.json
29
+ // are read from (the smoke points this at a shim dir).
30
+
31
+ const fs = require("node:fs");
32
+ const os = require("node:os");
33
+ const path = require("node:path");
34
+ const { spawnSync } = require("node:child_process");
35
+
36
+ function argValue(flag, fallback) {
37
+ const i = process.argv.indexOf(flag);
38
+ return i >= 0 && i + 1 < process.argv.length ? process.argv[i + 1] : fallback;
39
+ }
40
+ const hasFlag = (flag) => process.argv.includes(flag);
41
+
42
+ const agentsDir = process.env.CW_PREFLIGHT_AGENTS_DIR || path.join(__dirname, "agents");
43
+ const manifestPath = path.join(agentsDir, "builtin-templates.json");
44
+ const question = argValue("--question", "In one sentence, what does this repo do? Keep it short.");
45
+ const timeoutMs = Number(argValue("--timeout-ms", "180000")) || 180000;
46
+ const asJson = hasFlag("--json");
47
+ const keep = hasFlag("--keep");
48
+
49
+ function die(msg) {
50
+ process.stderr.write(msg + "\n");
51
+ process.exit(2);
52
+ }
53
+
54
+ let templates;
55
+ try {
56
+ templates = JSON.parse(fs.readFileSync(manifestPath, "utf8")).templates || {};
57
+ } catch (e) {
58
+ die(`vendor-preflight: cannot read ${manifestPath}: ${e.message}`);
59
+ }
60
+
61
+ const onlyRaw = argValue("--vendors", "");
62
+ const only = onlyRaw ? onlyRaw.split(",").map((s) => s.trim()).filter(Boolean) : null;
63
+ const vendors = Object.keys(templates).filter((v) => !only || only.includes(v));
64
+ if (only) {
65
+ for (const v of only) if (!templates[v]) die(`vendor-preflight: unknown vendor "${v}" — known: ${Object.keys(templates).join(", ")}`);
66
+ }
67
+ if (!vendors.length) die("vendor-preflight: no vendors to check");
68
+
69
+ // One throwaway git repo + worker input, reused read-only across vendors.
70
+ const work = fs.mkdtempSync(path.join(os.tmpdir(), "cw-preflight-"));
71
+ const repo = path.join(work, "repo");
72
+ fs.mkdirSync(repo, { recursive: true });
73
+ spawnSync("git", ["init", "-q"], { cwd: repo });
74
+ fs.writeFileSync(path.join(repo, "README.md"), "# Tiny App\n\nA one-file demo. main.js reads input and prints it.\n");
75
+ const inputPath = path.join(repo, "input.md");
76
+ fs.writeFileSync(inputPath, `# Worker preflight\n\n- Result: ${path.join(repo, "result.md")}\n\n## Task\n\n${question}\n`);
77
+
78
+ function checkVendor(name) {
79
+ const script = templates[name];
80
+ const wrapper = path.join(agentsDir, script);
81
+ const resultPath = path.join(repo, `result-${name}.md`);
82
+ try { fs.rmSync(resultPath, { force: true }); } catch { /* ignore */ }
83
+ const started = process.hrtime.bigint();
84
+ const r = spawnSync(process.execPath, [wrapper, inputPath, resultPath], {
85
+ cwd: repo,
86
+ encoding: "utf8",
87
+ timeout: timeoutMs,
88
+ env: { ...process.env, CW_AGENT_STREAM: "0" }
89
+ });
90
+ const ms = Number((process.hrtime.bigint() - started) / 1000000n);
91
+ const result = (() => { try { return fs.readFileSync(resultPath, "utf8"); } catch { return ""; } })();
92
+ let model;
93
+ try { model = JSON.parse(r.stdout || "{}").model; } catch { /* provenance optional */ }
94
+
95
+ let status = "PASS";
96
+ let reason = "";
97
+ if (r.error && r.error.code === "ETIMEDOUT") {
98
+ status = "FAIL"; reason = `timed out after ${timeoutMs}ms`;
99
+ } else if (r.status !== 0) {
100
+ status = "FAIL"; reason = (r.stderr || "").trim().split("\n").pop() || `exit ${r.status}`;
101
+ } else if (!result.trim()) {
102
+ status = "FAIL"; reason = "exit 0 but empty result";
103
+ }
104
+ return { vendor: name, status, reason, ms, model, hasContract: /```cw:result/.test(result) };
105
+ }
106
+
107
+ const rows = vendors.map(checkVendor);
108
+ if (!keep) { try { fs.rmSync(work, { recursive: true, force: true }); } catch { /* ignore */ } }
109
+
110
+ const failed = rows.filter((r) => r.status !== "PASS");
111
+
112
+ if (asJson) {
113
+ process.stdout.write(JSON.stringify({ ok: failed.length === 0, vendors: rows }, null, 2) + "\n");
114
+ } else {
115
+ for (const r of rows) {
116
+ const mark = r.status === "PASS" ? "✓" : "✗";
117
+ const extra = r.status === "PASS"
118
+ ? `${r.ms}ms${r.model ? `, ${r.model}` : ""}${r.hasContract ? "" : ", no cw:result block"}`
119
+ : r.reason;
120
+ process.stdout.write(` ${mark} ${r.vendor.padEnd(10)} ${r.status} (${extra})\n`);
121
+ }
122
+ process.stdout.write(failed.length === 0
123
+ ? `vendor-preflight: ok — ${rows.length}/${rows.length} vendors live\n`
124
+ : `vendor-preflight: BLOCKED — ${failed.length}/${rows.length} vendor(s) not live: ${failed.map((r) => r.vendor).join(", ")}\n`);
125
+ }
126
+
127
+ process.exit(failed.length === 0 ? 0 : 1);
@@ -196,6 +196,11 @@ function main() {
196
196
  checkIncludes("plugins/cool-workflow/docs/release-and-migration.7.md", VERSION, checks);
197
197
  checkIncludes("CHANGELOG.md", `## ${VERSION}`, checks);
198
198
  checkIncludes("RELEASE.md", VERSION, checks);
199
+ // Homebrew formula (repo root): git-tag formula. Homebrew scans the version
200
+ // from the tag, so the tag is the single version surface to gate (an explicit
201
+ // `version` line would be redundant and brew audit rejects it). bump-version.js
202
+ // moves this tag.
203
+ checkIncludes("Formula/cool-workflow.rb", `tag: "v${VERSION}"`, checks);
199
204
  checkIncludes("plugins/cool-workflow/skills/cool-workflow/SKILL.md", "release:check", checks);
200
205
 
201
206
  process.stdout.write(