cool-workflow 0.1.85 → 0.1.86

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 (58) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +47 -333
  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 +2 -0
  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 +131 -3
  12. package/dist/capability-registry.js +84 -50
  13. package/dist/cli/command-surface.js +3 -0
  14. package/dist/doctor.js +37 -1
  15. package/dist/mcp/tool-call.js +428 -0
  16. package/dist/mcp/tool-definitions.js +1027 -0
  17. package/dist/mcp-surface.js +5 -1442
  18. package/dist/onramp.js +421 -0
  19. package/dist/orchestrator.js +20 -15
  20. package/dist/version.js +1 -1
  21. package/dist/workbench-host.js +1 -8
  22. package/docs/agent-delegation-drive.7.md +21 -2
  23. package/docs/cli-mcp-parity.7.md +29 -4
  24. package/docs/contract-migration-tooling.7.md +2 -0
  25. package/docs/control-plane-scheduling.7.md +2 -0
  26. package/docs/dogfood-one-real-repo.7.md +9 -1
  27. package/docs/durable-state-and-locking.7.md +2 -0
  28. package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
  29. package/docs/execution-backends.7.md +2 -0
  30. package/docs/getting-started.md +40 -2
  31. package/docs/index.md +51 -38
  32. package/docs/multi-agent-cli-mcp-surface.7.md +2 -0
  33. package/docs/multi-agent-eval-replay-harness.7.md +2 -0
  34. package/docs/multi-agent-operator-ux.7.md +2 -0
  35. package/docs/node-snapshot-diff-replay.7.md +2 -0
  36. package/docs/observability-cost-accounting.7.md +2 -0
  37. package/docs/project-index.md +18 -7
  38. package/docs/real-execution-backends.7.md +2 -0
  39. package/docs/release-and-migration.7.md +3 -1
  40. package/docs/release-history.md +342 -0
  41. package/docs/release-tooling.7.md +16 -0
  42. package/docs/run-registry-control-plane.7.md +2 -0
  43. package/docs/run-retention-reclamation.7.md +2 -0
  44. package/docs/state-explosion-management.7.md +2 -0
  45. package/docs/team-collaboration.7.md +2 -0
  46. package/docs/web-desktop-workbench.7.md +2 -0
  47. package/manifest/plugin.manifest.json +1 -1
  48. package/manifest/source-context-profiles.json +1 -1
  49. package/package.json +2 -1
  50. package/scripts/architecture-review-fast.js +44 -2
  51. package/scripts/canonical-apps.js +4 -4
  52. package/scripts/dogfood-release.js +55 -133
  53. package/scripts/golden-path.js +4 -4
  54. package/scripts/onramp-check.js +47 -0
  55. package/scripts/parity-check.js +740 -2
  56. package/scripts/release-check.js +2 -0
  57. package/scripts/source-context.js +31 -5
  58. package/scripts/version-sync-check.js +4 -2
package/dist/onramp.js ADDED
@@ -0,0 +1,421 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.optionEnabled = optionEnabled;
7
+ exports.npmCommand = npmCommand;
8
+ exports.nodeSmokeCommand = nodeSmokeCommand;
9
+ exports.detectSourceCheckout = detectSourceCheckout;
10
+ exports.shellQuote = shellQuote;
11
+ exports.buildDoctorOnramp = buildDoctorOnramp;
12
+ exports.resolveChangedFiles = resolveChangedFiles;
13
+ exports.evaluateOnrampContract = evaluateOnrampContract;
14
+ exports.recommendSmokeTests = recommendSmokeTests;
15
+ const node_fs_1 = __importDefault(require("node:fs"));
16
+ const node_path_1 = __importDefault(require("node:path"));
17
+ const node_child_process_1 = require("node:child_process");
18
+ const CURATED_SMOKE_MAP = [
19
+ {
20
+ patterns: ["src/doctor.ts", "src/onramp.ts", "scripts/onramp-check.js"],
21
+ smokes: ["doctor-smoke.js", "onramp-check-smoke.js"]
22
+ },
23
+ {
24
+ patterns: ["README.md", "docs/getting-started.md", "docs/index.md"],
25
+ smokes: ["quickstart-readme-path-smoke.js", "doctor-smoke.js"]
26
+ },
27
+ {
28
+ patterns: ["src/cli.ts", "src/cli/"],
29
+ smokes: ["cli-command-surface-smoke.js", "cli-jsonmode-parity-smoke.js", "cli-mcp-parity-smoke.js"]
30
+ },
31
+ {
32
+ patterns: ["src/orchestrator.ts"],
33
+ smokes: ["cli-mcp-parity-smoke.js"]
34
+ },
35
+ {
36
+ patterns: ["src/capability-registry.ts", "scripts/parity-check.js"],
37
+ smokes: ["cli-mcp-parity-smoke.js", "cli-jsonmode-parity-smoke.js", "parity-doc-sync-smoke.js"]
38
+ },
39
+ {
40
+ patterns: ["src/mcp-server.ts", "src/mcp-surface.ts"],
41
+ smokes: ["mcp-surface-registry-smoke.js", "mcp-app-surface-smoke.js", "cli-mcp-parity-smoke.js"]
42
+ },
43
+ {
44
+ patterns: ["src/run-export.ts", "src/types/run.ts"],
45
+ smokes: ["run-export-import-smoke.js", "run-export-restore-resume-smoke.js", "run-inspect-archive-smoke.js"]
46
+ },
47
+ {
48
+ patterns: ["src/capability-core.ts", "src/drive.ts", "src/agent-config.ts"],
49
+ smokes: ["quickstart-smoke.js", "quickstart-check-smoke.js", "agent-delegation-drive-smoke.js"]
50
+ },
51
+ {
52
+ patterns: ["src/telemetry", "src/worker-accept/telemetry"],
53
+ smokes: ["telemetry-ledger-smoke.js", "telemetry-attestation-smoke.js", "telemetry-verify-signatures-smoke.js"]
54
+ },
55
+ {
56
+ patterns: ["src/workbench", "ui/workbench/"],
57
+ smokes: ["web-desktop-workbench-smoke.js"]
58
+ },
59
+ {
60
+ patterns: ["src/scheduler.ts", "src/scheduling.ts", "src/daemon.ts", "src/triggers.ts"],
61
+ smokes: ["schedule-routine-daemon-smoke.js", "sched-policy-validation-smoke.js"]
62
+ },
63
+ {
64
+ patterns: ["src/multi-agent", "src/topology.ts", "src/coordinator", "src/evidence-reasoning.ts", "src/state-explosion"],
65
+ smokes: [
66
+ "multi-agent-runtime-core-smoke.js",
67
+ "multi-agent-topologies-map-reduce-smoke.js",
68
+ "multi-agent-topologies-debate-smoke.js",
69
+ "multi-agent-topologies-judge-panel-smoke.js",
70
+ "multi-agent-cli-mcp-surface-smoke.js",
71
+ "blackboard-state-explosion-management-smoke.js"
72
+ ]
73
+ }
74
+ ];
75
+ function optionEnabled(value) {
76
+ if (value === undefined || value === false)
77
+ return false;
78
+ if (typeof value === "string" && ["", "0", "false", "no"].includes(value.toLowerCase()))
79
+ return false;
80
+ return true;
81
+ }
82
+ function npmCommand(cwd, script) {
83
+ const source = detectSourceCheckout(cwd);
84
+ const base = `npm run ${script}`;
85
+ return source ? `${source.chdir}${base}` : base;
86
+ }
87
+ function nodeSmokeCommand(cwd, smoke) {
88
+ const source = detectSourceCheckout(cwd);
89
+ const base = `node test/${smoke}`;
90
+ return source ? `${source.chdir}${base}` : base;
91
+ }
92
+ function detectSourceCheckout(cwd) {
93
+ const resolved = node_path_1.default.resolve(cwd);
94
+ const candidates = [resolved, node_path_1.default.join(resolved, "plugins", "cool-workflow")];
95
+ for (const candidate of candidates) {
96
+ try {
97
+ const pkg = JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(candidate, "package.json"), "utf8"));
98
+ if (pkg.name === "cool-workflow") {
99
+ return {
100
+ packageDir: candidate,
101
+ chdir: candidate === resolved ? "" : `cd ${shellQuote(node_path_1.default.relative(resolved, candidate) || ".")} && `
102
+ };
103
+ }
104
+ }
105
+ catch {
106
+ /* keep looking */
107
+ }
108
+ }
109
+ return undefined;
110
+ }
111
+ function shellQuote(value) {
112
+ if (/^[A-Za-z0-9_./:-]+$/.test(value))
113
+ return value;
114
+ return `'${value.replace(/'/g, "'\\''")}'`;
115
+ }
116
+ function buildDoctorOnramp(options = {}) {
117
+ const cwd = node_path_1.default.resolve(options.cwd || process.cwd());
118
+ const agentCommand = "--agent-command builtin:claude";
119
+ const onramp = {
120
+ schemaVersion: 1,
121
+ summary: "start small, run the short gate while changing code, then run the full gate before release",
122
+ sections: [
123
+ {
124
+ id: "first-run",
125
+ title: "First Run",
126
+ summary: "Prove the tool, check the setup, then make one report.",
127
+ actions: [
128
+ {
129
+ id: "demo",
130
+ title: "Prove tamper checks",
131
+ command: "cw demo tamper",
132
+ reason: "Shows the core trust check without an agent or a repo."
133
+ },
134
+ {
135
+ id: "setup",
136
+ title: "Check setup",
137
+ command: "cw doctor --onramp",
138
+ reason: "Names local setup trouble before a run is made."
139
+ },
140
+ {
141
+ id: "dry-run",
142
+ title: "Check a real run",
143
+ command: `cw quickstart architecture-review --check --repo /path/to/repo --question "What are the main risks?" ${agentCommand}`,
144
+ reason: "Does no writes and no agent call; it checks inputs first."
145
+ },
146
+ {
147
+ id: "report",
148
+ title: "Make the report",
149
+ command: `cw quickstart architecture-review --repo /path/to/repo --question "What are the main risks?" ${agentCommand}`,
150
+ reason: "Runs the short user path: ask, run, verify, report."
151
+ },
152
+ {
153
+ id: "bundle",
154
+ title: "Make a bundle",
155
+ command: `cw quickstart architecture-review --repo /path/to/repo --question "What are the main risks?" ${agentCommand} --bundle`,
156
+ reason: "Seals a completed report into a portable file the receiver can check offline."
157
+ },
158
+ {
159
+ id: "verify-bundle",
160
+ title: "Check the bundle",
161
+ command: "cw report verify-bundle report.cwrun.json",
162
+ reason: "Checks the report bundle without the source repo or a .cw tree."
163
+ }
164
+ ]
165
+ },
166
+ {
167
+ id: "change-loop",
168
+ title: "Change Loop",
169
+ summary: "Use the small checks while changing code; save the slow full gate for the end.",
170
+ actions: [
171
+ {
172
+ id: "build",
173
+ title: "Type check the change",
174
+ command: npmCommand(cwd, "build"),
175
+ reason: "Fast first check for TypeScript errors."
176
+ },
177
+ {
178
+ id: "target-smoke",
179
+ title: "Run the closest smoke",
180
+ command: nodeSmokeCommand(cwd, "doctor-smoke.js"),
181
+ reason: "Replace this smoke name with the one that covers your changed path."
182
+ },
183
+ {
184
+ id: "fast-suite",
185
+ title: "Run the parallel suite",
186
+ command: npmCommand(cwd, "test:fast"),
187
+ reason: "Runs all smokes with isolated state and parallel workers."
188
+ }
189
+ ]
190
+ },
191
+ {
192
+ id: "surface-guard",
193
+ title: "Surface Guard",
194
+ summary: "Keep the wide runner, CLI, and MCP faces tied to one source.",
195
+ actions: [
196
+ {
197
+ id: "registry",
198
+ title: "Declare each new verb once",
199
+ command: npmCommand(cwd, "parity:check"),
200
+ reason: "Fails if CLI and MCP drift from the capability registry."
201
+ },
202
+ {
203
+ id: "manifest",
204
+ title: "Check generated faces",
205
+ command: npmCommand(cwd, "gen:manifests -- --check"),
206
+ reason: "Fails if plugin manifests drift from source."
207
+ }
208
+ ]
209
+ },
210
+ {
211
+ id: "release-gate",
212
+ title: "Release Gate",
213
+ summary: "Run the full gate only when the batch is ready.",
214
+ actions: [
215
+ {
216
+ id: "release-check",
217
+ title: "Dry-run the release gate",
218
+ command: npmCommand(cwd, "release:check"),
219
+ reason: "Builds, checks docs and generated files, runs the parallel suite, and makes no tag."
220
+ }
221
+ ]
222
+ }
223
+ ]
224
+ };
225
+ if (options.changedFrom) {
226
+ const changed = resolveChangedFiles({ cwd, changedFrom: options.changedFrom, env: options.env });
227
+ const contract = evaluateOnrampContract(changed.files, { cwd });
228
+ onramp.changedFiles = changed;
229
+ onramp.contract = contract;
230
+ onramp.recommendedChecks = {
231
+ summary: contract.recommendedSmokeTests.length
232
+ ? "run the closest smoke tests first, then the fast suite and release gate"
233
+ : "no changed files were found; use the normal short checks",
234
+ smokeTests: contract.recommendedSmokeTests,
235
+ commands: contract.recommendedCommands
236
+ };
237
+ }
238
+ return onramp;
239
+ }
240
+ function resolveChangedFiles(options = {}) {
241
+ const cwd = node_path_1.default.resolve(options.cwd || process.cwd());
242
+ const root = gitRoot(cwd);
243
+ const baseRef = resolveBaseRef(root, options.changedFrom, options.env || process.env);
244
+ const files = new Set();
245
+ for (const file of gitLines(root, ["diff", "--name-only", baseRef, "--"]))
246
+ files.add(normalizeChangedPath(file));
247
+ for (const file of gitLines(root, ["ls-files", "--others", "--exclude-standard"]))
248
+ files.add(normalizeChangedPath(file));
249
+ return { baseRef, files: [...files].filter(Boolean).sort() };
250
+ }
251
+ function evaluateOnrampContract(files, options = {}) {
252
+ const cwd = node_path_1.default.resolve(options.cwd || process.cwd());
253
+ const normalized = [...new Set(files.map((file) => normalizeChangedPath(file)).filter(Boolean))].sort();
254
+ const issues = [];
255
+ const runtimeFiles = normalized.filter(isRuntimeSource);
256
+ const appFiles = normalized.filter((file) => file.startsWith("plugins/cool-workflow/apps/"));
257
+ const typeFiles = normalized.filter((file) => file.startsWith("plugins/cool-workflow/src/types/") && file.endsWith(".ts"));
258
+ const scriptFiles = normalized.filter((file) => file.startsWith("plugins/cool-workflow/scripts/"));
259
+ const surfaceFiles = normalized.filter(isSurfaceFile);
260
+ const smokeFiles = normalized.filter((file) => /^plugins\/cool-workflow\/test\/.+-smoke\.js$/.test(file));
261
+ const docFiles = normalized.filter(isDocFile);
262
+ const iterationFiles = normalized.filter((file) => file === "ITERATION_LOG.md");
263
+ const sourceAppOrScript = runtimeFiles.length > 0 || typeFiles.length > 0 || appFiles.length > 0 || scriptFiles.length > 0;
264
+ if ((runtimeFiles.length > 0 || appFiles.length > 0) && smokeFiles.length === 0) {
265
+ issues.push({
266
+ code: "runtime-smoke-required",
267
+ detail: "Runtime or app changes must include at least one smoke test change.",
268
+ fix: "Add or update a focused test/*-smoke.js file for the changed behavior.",
269
+ files: [...runtimeFiles, ...appFiles]
270
+ });
271
+ }
272
+ if (typeFiles.length > 0 && runtimeFiles.length === 0 && appFiles.length === 0) {
273
+ issues.push({
274
+ code: "types-without-runtime",
275
+ detail: "Type-only source changes are not a valid cycle.",
276
+ fix: "Add the runtime behavior that reads the type, or remove the type-only change.",
277
+ files: typeFiles
278
+ });
279
+ }
280
+ if (surfaceFiles.length > 0 && docFiles.length === 0) {
281
+ issues.push({
282
+ code: "surface-docs-required",
283
+ detail: "CLI, MCP, or capability surface changes must update public docs.",
284
+ fix: "Update README.md or plugins/cool-workflow/docs/*.md with the changed surface.",
285
+ files: surfaceFiles
286
+ });
287
+ }
288
+ if (sourceAppOrScript && iterationFiles.length === 0) {
289
+ issues.push({
290
+ code: "iteration-log-required",
291
+ detail: "Source, app, or script changes must be recorded in ITERATION_LOG.md.",
292
+ fix: "Append one cycle row with goal, files, tests, gate, and tag decision.",
293
+ files: [...runtimeFiles, ...typeFiles, ...appFiles, ...scriptFiles]
294
+ });
295
+ }
296
+ const recommendedSmokeTests = recommendSmokeTests(normalized, cwd);
297
+ return {
298
+ ok: issues.length === 0,
299
+ changedFiles: normalized,
300
+ recommendedSmokeTests,
301
+ recommendedCommands: recommendedCommands(normalized, recommendedSmokeTests, cwd),
302
+ issues
303
+ };
304
+ }
305
+ function recommendSmokeTests(files, cwd = process.cwd()) {
306
+ const normalized = files.map((file) => normalizeChangedPath(file));
307
+ const smokes = new Set();
308
+ const curatedFiles = new Set();
309
+ for (const file of normalized) {
310
+ const pluginPath = stripPluginPrefix(file);
311
+ for (const entry of CURATED_SMOKE_MAP) {
312
+ const matched = entry.patterns.some((pattern) => pluginPath === pattern || pluginPath.startsWith(pattern));
313
+ if (matched) {
314
+ curatedFiles.add(file);
315
+ for (const smoke of entry.smokes)
316
+ smokes.add(smoke);
317
+ }
318
+ }
319
+ }
320
+ const available = availableSmokeTests(cwd);
321
+ for (const file of normalized) {
322
+ if (curatedFiles.has(file))
323
+ continue;
324
+ const pluginPath = stripPluginPrefix(file);
325
+ if (!pluginPath.startsWith("src/"))
326
+ continue;
327
+ const base = node_path_1.default.basename(pluginPath, ".ts");
328
+ const direct = `${base}-smoke.js`;
329
+ if (available.includes(direct))
330
+ smokes.add(direct);
331
+ const tokens = base.split(/[^a-zA-Z0-9]+/).filter((token) => token.length >= 3);
332
+ for (const smoke of available) {
333
+ if (tokens.some((token) => smoke.includes(token)))
334
+ smokes.add(smoke);
335
+ }
336
+ }
337
+ return [...smokes].sort();
338
+ }
339
+ function recommendedCommands(files, smokes, cwd) {
340
+ const commands = new Set();
341
+ commands.add(npmCommand(cwd, "build"));
342
+ for (const smoke of smokes)
343
+ commands.add(nodeSmokeCommand(cwd, smoke));
344
+ commands.add(npmCommand(cwd, "test:fast"));
345
+ if (files.some(isSurfaceFile))
346
+ commands.add(npmCommand(cwd, "parity:check"));
347
+ if (files.some(isSurfaceFile))
348
+ commands.add(npmCommand(cwd, "gen:manifests -- --check"));
349
+ commands.add(npmCommand(cwd, "release:check"));
350
+ return [...commands];
351
+ }
352
+ function resolveBaseRef(root, changedFrom, env) {
353
+ if (changedFrom)
354
+ return verifyRef(root, changedFrom);
355
+ if (env.CW_ONRAMP_BASE)
356
+ return verifyRef(root, env.CW_ONRAMP_BASE);
357
+ const baseBranch = env.GITHUB_BASE_REF ? `origin/${env.GITHUB_BASE_REF}` : "origin/main";
358
+ const mergeBase = gitOne(root, ["merge-base", "HEAD", baseBranch]);
359
+ if (mergeBase)
360
+ return mergeBase;
361
+ return verifyRef(root, "HEAD");
362
+ }
363
+ function verifyRef(root, ref) {
364
+ const resolved = gitOne(root, ["rev-parse", "--verify", `${ref}^{commit}`]);
365
+ if (!resolved)
366
+ throw new Error(`Unknown onramp base ref: ${ref}`);
367
+ return ref;
368
+ }
369
+ function gitRoot(cwd) {
370
+ return gitOne(node_path_1.default.resolve(cwd), ["rev-parse", "--show-toplevel"]) || node_path_1.default.resolve(cwd);
371
+ }
372
+ function gitLines(cwd, args) {
373
+ const result = (0, node_child_process_1.spawnSync)("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
374
+ if (result.status !== 0)
375
+ return [];
376
+ return String(result.stdout || "").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
377
+ }
378
+ function gitOne(cwd, args) {
379
+ return gitLines(cwd, args)[0] || "";
380
+ }
381
+ function availableSmokeTests(cwd) {
382
+ const source = detectSourceCheckout(cwd);
383
+ const testDir = source ? node_path_1.default.join(source.packageDir, "test") : node_path_1.default.join(node_path_1.default.resolve(cwd), "test");
384
+ try {
385
+ return node_fs_1.default.readdirSync(testDir).filter((file) => file.endsWith("-smoke.js")).sort();
386
+ }
387
+ catch {
388
+ return [];
389
+ }
390
+ }
391
+ function normalizeChangedPath(file) {
392
+ const normalized = file.replace(/\\/g, "/").replace(/^\.\//, "");
393
+ if (/^(src|apps|scripts|test|docs|dist|manifest|ui|workflows)\//.test(normalized)) {
394
+ return `plugins/cool-workflow/${normalized}`;
395
+ }
396
+ return normalized;
397
+ }
398
+ function stripPluginPrefix(file) {
399
+ return file.startsWith("plugins/cool-workflow/") ? file.slice("plugins/cool-workflow/".length) : file;
400
+ }
401
+ function isRuntimeSource(file) {
402
+ return file.startsWith("plugins/cool-workflow/src/") && file.endsWith(".ts") && !file.startsWith("plugins/cool-workflow/src/types/");
403
+ }
404
+ function isSurfaceFile(file) {
405
+ const pluginPath = stripPluginPrefix(file);
406
+ return (pluginPath === "src/cli.ts" ||
407
+ pluginPath.startsWith("src/cli/") ||
408
+ pluginPath === "src/capability-registry.ts" ||
409
+ pluginPath === "src/mcp-server.ts" ||
410
+ pluginPath === "src/mcp-surface.ts" ||
411
+ pluginPath === "src/orchestrator.ts" ||
412
+ pluginPath === "scripts/parity-check.js");
413
+ }
414
+ function isDocFile(file) {
415
+ return (file === "README.md" ||
416
+ file === "CHANGELOG.md" ||
417
+ file === "RELEASE.md" ||
418
+ file.startsWith("docs/") ||
419
+ file === "plugins/cool-workflow/README.md" ||
420
+ file.startsWith("plugins/cool-workflow/docs/"));
421
+ }
@@ -178,7 +178,7 @@ class CoolWorkflowRunner {
178
178
  return {
179
179
  valid: false,
180
180
  appId: target,
181
- appPath: node_path_1.default.resolve(target),
181
+ appPath: this.resolveFromBase(target),
182
182
  issues
183
183
  };
184
184
  }
@@ -188,7 +188,7 @@ class CoolWorkflowRunner {
188
188
  if (!id)
189
189
  throw new Error("App id must include at least one letter or digit");
190
190
  const title = String(options.title || titleize(id));
191
- const destinationDir = node_path_1.default.resolve(String(options.directory || options.output || node_path_1.default.join(this.appsDir, id)));
191
+ const destinationDir = this.resolveFromBase(String(options.directory || options.output || node_path_1.default.join(this.appsDir, id)));
192
192
  const manifestPath = node_path_1.default.join(destinationDir, "app.json");
193
193
  const entrypointPath = node_path_1.default.join(destinationDir, "workflow.js");
194
194
  if (!options.force && (node_fs_1.default.existsSync(manifestPath) || node_fs_1.default.existsSync(entrypointPath))) {
@@ -205,8 +205,7 @@ class CoolWorkflowRunner {
205
205
  }
206
206
  packageApp(appId, options = {}) {
207
207
  const record = this.loadWorkflowAppById(appId);
208
- const destination = node_path_1.default.resolve(String(options.output ||
209
- node_path_1.default.join(process.cwd(), ".cw", "packages", `${record.app.id}-${record.app.version}.cwapp.json`)));
208
+ const destination = this.resolveFromBase(String(options.output || node_path_1.default.join(".cw", "packages", `${record.app.id}-${record.app.version}.cwapp.json`)));
210
209
  node_fs_1.default.mkdirSync(node_path_1.default.dirname(destination), { recursive: true });
211
210
  (0, state_1.writeJson)(destination, {
212
211
  schemaVersion: 1,
@@ -221,7 +220,7 @@ class CoolWorkflowRunner {
221
220
  if (!id)
222
221
  throw new Error("Workflow id must include at least one letter or digit");
223
222
  const title = String(options.title || titleize(id));
224
- const destination = node_path_1.default.resolve(String(options.output || node_path_1.default.join(this.workflowsDir, `${id}.workflow.js`)));
223
+ const destination = this.resolveFromBase(String(options.output || node_path_1.default.join(this.workflowsDir, `${id}.workflow.js`)));
225
224
  if (node_fs_1.default.existsSync(destination) && !options.force) {
226
225
  throw new Error(`Refusing to overwrite existing workflow: ${destination}`);
227
226
  }
@@ -248,7 +247,7 @@ class CoolWorkflowRunner {
248
247
  return lifecycleOps.dispatch(this.loadRun(runId), options);
249
248
  }
250
249
  recordResult(runId, taskId, resultPath, options = {}) {
251
- return lifecycleOps.recordResult(this.loadRun(runId), taskId, resultPath, options);
250
+ return lifecycleOps.recordResult(this.loadRun(runId), taskId, this.resolveFromBase(resultPath), options);
252
251
  }
253
252
  listWorkers(runId, options = {}) {
254
253
  return (0, worker_isolation_1.listWorkerScopes)(this.loadRun(runId), {
@@ -272,13 +271,13 @@ class CoolWorkflowRunner {
272
271
  return (0, worker_isolation_1.writeWorkerManifest)(run, worker);
273
272
  }
274
273
  recordWorkerOutput(runId, workerId, resultPath, options = {}) {
275
- return lifecycleOps.recordWorkerOutput(this.loadRun(runId), workerId, resultPath, options);
274
+ return lifecycleOps.recordWorkerOutput(this.loadRun(runId), workerId, this.resolveFromBase(resultPath), options);
276
275
  }
277
276
  recordWorkerFailure(runId, workerId, message, options = {}) {
278
277
  return lifecycleOps.recordWorkerFailure(this.loadRun(runId), workerId, message, options);
279
278
  }
280
279
  validateWorker(runId, workerId, targetPath) {
281
- return (0, worker_isolation_1.validateWorkerBoundary)(this.loadRun(runId), workerId, targetPath ? { path: targetPath } : {});
280
+ return (0, worker_isolation_1.validateWorkerBoundary)(this.loadRun(runId), workerId, targetPath ? { path: this.resolveFromBase(targetPath) } : {});
282
281
  }
283
282
  // Audit domain — delegated to ./orchestrator/audit-operations (v0.1.40 P3
284
283
  // router pattern). The runner stays the routing surface; the logic lives in the
@@ -314,13 +313,13 @@ class CoolWorkflowRunner {
314
313
  return auditOps.recordAuditDecision(this.loadRun(runId), workerId, options);
315
314
  }
316
315
  listSandboxProfiles(options = {}) {
317
- return (0, sandbox_profile_1.listBundledSandboxProfiles)((0, sandbox_profile_1.sandboxContextForValidation)(String(options.cwd || process.cwd())));
316
+ return (0, sandbox_profile_1.listBundledSandboxProfiles)((0, sandbox_profile_1.sandboxContextForValidation)(String(options.cwd || this.invocationCwd())));
318
317
  }
319
318
  showSandboxProfile(profileId, options = {}) {
320
- return (0, sandbox_profile_1.showBundledSandboxProfile)(profileId, (0, sandbox_profile_1.sandboxContextForValidation)(String(options.cwd || process.cwd())));
319
+ return (0, sandbox_profile_1.showBundledSandboxProfile)(profileId, (0, sandbox_profile_1.sandboxContextForValidation)(String(options.cwd || this.invocationCwd())));
321
320
  }
322
321
  validateSandboxProfile(profileFile, options = {}) {
323
- return (0, sandbox_profile_1.validateSandboxProfileFile)(profileFile, (0, sandbox_profile_1.sandboxContextForValidation)(String(options.cwd || process.cwd())));
322
+ return (0, sandbox_profile_1.validateSandboxProfileFile)(this.resolveFromBase(profileFile), (0, sandbox_profile_1.sandboxContextForValidation)(String(options.cwd || this.invocationCwd())));
324
323
  }
325
324
  listBackends(options = {}) {
326
325
  void options;
@@ -331,7 +330,7 @@ class CoolWorkflowRunner {
331
330
  return (0, execution_backend_1.backendShowPayload)(backendId);
332
331
  }
333
332
  probeBackend(backendId, options = {}) {
334
- return (0, execution_backend_1.backendProbePayload)(backendId, { cwd: String(options.cwd || process.cwd()) });
333
+ return (0, execution_backend_1.backendProbePayload)(backendId, { cwd: String(options.cwd || this.invocationCwd()) });
335
334
  }
336
335
  // Candidate domain — delegated to ./orchestrator/candidate-operations.
337
336
  listCandidates(runId, options = {}) {
@@ -716,6 +715,12 @@ class CoolWorkflowRunner {
716
715
  loadRun(runId) {
717
716
  return (0, state_1.loadRunFromCwd)(runId, this.baseDir);
718
717
  }
718
+ invocationCwd() {
719
+ return this.baseDir || process.cwd();
720
+ }
721
+ resolveFromBase(target) {
722
+ return node_path_1.default.resolve(this.invocationCwd(), target);
723
+ }
719
724
  loadWorkflowAppById(appId) {
720
725
  const record = this.loadWorkflowApps().find((candidate) => candidate.app.id === appId);
721
726
  if (!record)
@@ -725,7 +730,7 @@ class CoolWorkflowRunner {
725
730
  loadWorkflowAppTarget(target) {
726
731
  if (!target)
727
732
  throw new Error("Missing workflow app path or id");
728
- const resolved = node_path_1.default.resolve(target);
733
+ const resolved = this.resolveFromBase(target);
729
734
  if (node_fs_1.default.existsSync(resolved)) {
730
735
  const stat = node_fs_1.default.statSync(resolved);
731
736
  if (stat.isDirectory())
@@ -821,9 +826,9 @@ function formatHelp() {
821
826
  "",
822
827
  "Commands:",
823
828
  " list",
824
- " doctor [--json] (check your setup: Node, agent backend, git, writable state)",
829
+ " doctor [--json] [--onramp] [--changed-from REF] (check setup and show the shortest safe next steps)",
825
830
  " init <workflow-id> [--title TEXT] [--output PATH]",
826
- " quickstart [app-id] [--repo PATH] [--question TEXT] [--agent-command CMD] [--once] [--preview]",
831
+ " quickstart [app-id] [--repo PATH] [--question TEXT] [--agent-command CMD] [--check] [--once] [--preview]",
827
832
  " plan <workflow-id> [--repo PATH] [--question TEXT] [--invariant TEXT]",
828
833
  " status <run-id> [--json|--format json]",
829
834
  " next <run-id> [--limit N]",
package/dist/version.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MIN_SUPPORTED_RUN_STATE_SCHEMA_VERSION = exports.LEGACY_RUN_STATE_SCHEMA_VERSION = exports.CURRENT_RUN_STATE_SCHEMA_VERSION = exports.WORKFLOW_APP_SCHEMA_VERSION = exports.CURRENT_COOL_WORKFLOW_VERSION = void 0;
4
- exports.CURRENT_COOL_WORKFLOW_VERSION = "0.1.85";
4
+ exports.CURRENT_COOL_WORKFLOW_VERSION = "0.1.86";
5
5
  exports.WORKFLOW_APP_SCHEMA_VERSION = 1;
6
6
  exports.CURRENT_RUN_STATE_SCHEMA_VERSION = 1;
7
7
  exports.LEGACY_RUN_STATE_SCHEMA_VERSION = 0;
@@ -99,14 +99,7 @@ class WorkbenchHost {
99
99
  }
100
100
  const runMatch = /^\/api\/run\/([^/]+)$/.exec(route);
101
101
  if (runMatch) {
102
- const previousCwd = process.cwd();
103
- process.chdir(this.cwd);
104
- try {
105
- return this.send(res, 200, (0, workbench_1.buildWorkbenchRunView)(this.runner, runMatch[1]));
106
- }
107
- finally {
108
- process.chdir(previousCwd);
109
- }
102
+ return this.send(res, 200, (0, workbench_1.buildWorkbenchRunView)(this.runner.withBaseDir(this.cwd), runMatch[1]));
110
103
  }
111
104
  this.send(res, 404, { error: `no such read-only view: ${route}` });
112
105
  }
@@ -128,6 +128,7 @@ node dist/cli.js backend agent config # show the effective config (se
128
128
  node dist/cli.js backend probe agent --json # ready iff configured, else unverified
129
129
 
130
130
  # drive a real repo end-to-end (zero hand-written result.md)
131
+ node dist/cli.js quickstart architecture-review --check --repo /path/to/repo --question "Is the design sound?" --agent-command "node $(pwd)/scripts/agents/claude-p-agent.js {{input}} {{result}}"
131
132
  node dist/cli.js run architecture-review --drive --repo /path/to/repo --question "Is the design sound?"
132
133
  node dist/cli.js run architecture-review --drive --once --repo /path/to/repo --question "..." # one step
133
134
  node dist/cli.js run drive <run-id> --json # read-only preview of the next step
@@ -135,12 +136,22 @@ node dist/cli.js run drive <run-id> --json # read-only preview of the next
135
136
  # quickstart --resume: a guided stop-then-resume a newcomer can WITNESS in <5 min
136
137
  node dist/cli.js quickstart --resume --repo /path/to/repo --question "..." # advances ONE step, prints a continue line
137
138
  node dist/cli.js quickstart --run <run-id> --resume # continues that run to completion
139
+ node dist/cli.js quickstart --run <run-id> --resume --bundle # continues, then seals a completed run
138
140
  ```
139
141
 
142
+ `quickstart --check` is a zero-write preflight. It does not make a run, write
143
+ `.cw/`, call the agent, write a report, or commit. It checks the app id, repo,
144
+ question, agent config, and (with `--bundle`) the trust-key shape, then gives the
145
+ next command to run. A blocked check exits non-zero, so scripts may use it as a
146
+ gate before a real run.
147
+
140
148
  `quickstart --resume` with no `--run` drives a single step and prints a
141
149
  copy-pasteable `cw quickstart --run <id> --resume` continue line; run it again with
142
150
  the `--run <id>` to finish. The continuing invocation echoes `resumedFrom: <id>`.
143
151
  Bare `quickstart` (no `--resume`) is unchanged — it drives straight to the end.
152
+ When `--bundle` is present on the fresh resume step, no bundle is sealed until
153
+ the run is complete; the continue line keeps `--bundle` so the second command
154
+ finishes and seals the report.
144
155
 
145
156
  For faster first results, use the opt-in fast app in place of changing the full
146
157
  review contract:
@@ -171,9 +182,15 @@ an Assess cache hit. A cache hit still goes through `recordWorkerOutput`
171
182
  validation; a corrupt cached result parks/fails closed rather than spawning a
172
183
  quiet fallback.
173
184
 
185
+ Verify and Verdict also get the source-context instruction so they do not have to
186
+ start by scanning the repo again. They are not result-cached; they still have to
187
+ cite evidence and make the final check from the accepted Map and Assess work.
188
+
174
189
  `--metrics` is diagnostic and opt-in. It adds elapsed milliseconds, step counts,
175
- agent-spawn counts, and `result-cache` hit counts to the wrapper JSON payload;
176
- without it, the wrapper's default output shape stays unchanged.
190
+ agent-spawn counts, `result-cache` hit counts, source-context bytes/digest, and
191
+ one row per driven task with phase, task id, elapsed time, and spawn/cache state
192
+ to the wrapper JSON payload; without it, the wrapper's default output shape stays
193
+ unchanged.
177
194
 
178
195
  `{{manifest}}`, `{{input}}`, `{{result}}`, `{{workerDir}}`, `{{model}}`, and
179
196
  `{{prompt}}` are put into DISCRETE argv elements (never a shell-interpreted
@@ -272,3 +289,5 @@ Loaders fail closed on corrupt state; store writes are made safe under more than
272
289
  No other change to this page in v0.1.84.
273
290
 
274
291
  0.1.85
292
+
293
+ 0.1.86