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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +47 -333
- package/apps/architecture-review/app.json +1 -1
- package/apps/architecture-review-fast/app.json +1 -1
- package/apps/architecture-review-fast/workflow.js +2 -0
- package/apps/end-to-end-golden-path/app.json +1 -1
- package/apps/pr-review-fix-ci/app.json +1 -1
- package/apps/release-cut/app.json +1 -1
- package/apps/research-synthesis/app.json +1 -1
- package/dist/capability-core.js +131 -3
- package/dist/capability-registry.js +84 -50
- package/dist/cli/command-surface.js +3 -0
- package/dist/doctor.js +37 -1
- package/dist/mcp/tool-call.js +428 -0
- package/dist/mcp/tool-definitions.js +1027 -0
- package/dist/mcp-surface.js +5 -1442
- package/dist/onramp.js +421 -0
- package/dist/orchestrator.js +20 -15
- package/dist/version.js +1 -1
- package/dist/workbench-host.js +1 -8
- package/docs/agent-delegation-drive.7.md +21 -2
- package/docs/cli-mcp-parity.7.md +29 -4
- package/docs/contract-migration-tooling.7.md +2 -0
- package/docs/control-plane-scheduling.7.md +2 -0
- package/docs/dogfood-one-real-repo.7.md +9 -1
- package/docs/durable-state-and-locking.7.md +2 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
- package/docs/execution-backends.7.md +2 -0
- package/docs/getting-started.md +40 -2
- package/docs/index.md +51 -38
- package/docs/multi-agent-cli-mcp-surface.7.md +2 -0
- package/docs/multi-agent-eval-replay-harness.7.md +2 -0
- package/docs/multi-agent-operator-ux.7.md +2 -0
- package/docs/node-snapshot-diff-replay.7.md +2 -0
- package/docs/observability-cost-accounting.7.md +2 -0
- package/docs/project-index.md +18 -7
- package/docs/real-execution-backends.7.md +2 -0
- package/docs/release-and-migration.7.md +3 -1
- package/docs/release-history.md +342 -0
- package/docs/release-tooling.7.md +16 -0
- package/docs/run-registry-control-plane.7.md +2 -0
- package/docs/run-retention-reclamation.7.md +2 -0
- package/docs/state-explosion-management.7.md +2 -0
- package/docs/team-collaboration.7.md +2 -0
- package/docs/web-desktop-workbench.7.md +2 -0
- package/manifest/plugin.manifest.json +1 -1
- package/manifest/source-context-profiles.json +1 -1
- package/package.json +2 -1
- package/scripts/architecture-review-fast.js +44 -2
- package/scripts/canonical-apps.js +4 -4
- package/scripts/dogfood-release.js +55 -133
- package/scripts/golden-path.js +4 -4
- package/scripts/onramp-check.js +47 -0
- package/scripts/parity-check.js +740 -2
- package/scripts/release-check.js +2 -0
- package/scripts/source-context.js +31 -5
- 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
|
+
}
|
package/dist/orchestrator.js
CHANGED
|
@@ -178,7 +178,7 @@ class CoolWorkflowRunner {
|
|
|
178
178
|
return {
|
|
179
179
|
valid: false,
|
|
180
180
|
appId: target,
|
|
181
|
-
appPath:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 ||
|
|
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 ||
|
|
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 ||
|
|
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 ||
|
|
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 =
|
|
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
|
|
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.
|
|
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;
|
package/dist/workbench-host.js
CHANGED
|
@@ -99,14 +99,7 @@ class WorkbenchHost {
|
|
|
99
99
|
}
|
|
100
100
|
const runMatch = /^\/api\/run\/([^/]+)$/.exec(route);
|
|
101
101
|
if (runMatch) {
|
|
102
|
-
|
|
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,
|
|
176
|
-
|
|
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
|