cool-workflow 0.1.91 → 0.1.93
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 +111 -434
- package/apps/architecture-review/app.json +1 -1
- package/apps/architecture-review-fast/app.json +1 -1
- package/apps/architecture-review-fast/workflow.js +15 -2
- 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/apps/research-synthesis/workflow.js +1 -1
- package/dist/capability-core.js +47 -0
- package/dist/capability-registry.js +2 -0
- package/dist/cli/command-surface.js +165 -1352
- package/dist/cli/format.js +56 -0
- package/dist/cli/handlers/audit.js +82 -0
- package/dist/cli/handlers/blackboard.js +81 -0
- package/dist/cli/handlers/candidate.js +40 -0
- package/dist/cli/handlers/clones.js +34 -0
- package/dist/cli/handlers/collaboration.js +61 -0
- package/dist/cli/handlers/eval.js +40 -0
- package/dist/cli/handlers/maintenance.js +107 -0
- package/dist/cli/handlers/multi-agent.js +165 -0
- package/dist/cli/handlers/node.js +41 -0
- package/dist/cli/handlers/operational.js +155 -0
- package/dist/cli/handlers/operator.js +146 -0
- package/dist/cli/handlers/registry.js +68 -0
- package/dist/cli/handlers/run.js +153 -0
- package/dist/cli/handlers/scheduling.js +126 -0
- package/dist/cli/handlers/workbench.js +41 -0
- package/dist/cli/handlers/worker.js +45 -0
- package/dist/cli/io.js +27 -0
- package/dist/cli/run-summary.js +45 -0
- package/dist/commit.js +0 -5
- package/dist/doctor.js +1 -0
- package/dist/execution-backend.js +0 -11
- package/dist/mcp/tool-call.js +2 -0
- package/dist/mcp/tool-definitions.js +8 -0
- package/dist/orchestrator/app-operations.js +205 -0
- package/dist/orchestrator/report.js +6 -1
- package/dist/orchestrator.js +41 -153
- package/dist/state-explosion.js +0 -7
- package/dist/term.js +0 -18
- package/dist/validation.js +0 -21
- package/dist/version.js +1 -1
- package/docs/agent-delegation-drive.7.md +2 -0
- package/docs/canonical-workflow-apps.7.md +12 -0
- package/docs/cli-mcp-parity.7.md +18 -1
- package/docs/contract-migration-tooling.7.md +2 -0
- package/docs/control-plane-scheduling.7.md +2 -0
- 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/mcp-app-surface.7.md +12 -0
- 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 +15 -3
- package/docs/real-execution-backends.7.md +2 -0
- package/docs/release-and-migration.7.md +2 -0
- package/docs/release-tooling.7.md +11 -0
- package/docs/run-registry-control-plane.7.md +26 -2
- 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/package.json +3 -1
- package/scripts/agents/agent-adapter-core.js +179 -28
- package/scripts/agents/claude-p-agent.js +24 -7
- package/scripts/agents/codex-agent.js +1 -1
- package/scripts/agents/gemini-agent.js +1 -1
- package/scripts/agents/opencode-agent.js +1 -1
- package/scripts/architecture-review-fast.js +19 -5
- package/scripts/bump-version.js +20 -1
- package/scripts/canonical-apps.js +4 -4
- package/scripts/dogfood-release.js +1 -1
- package/scripts/golden-path.js +4 -4
- package/scripts/parity-check.js +9 -1
- package/scripts/release-check.js +5 -1
- package/scripts/sync-readme.js +123 -0
- package/scripts/version-sync-check.js +10 -1
|
@@ -0,0 +1,205 @@
|
|
|
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.loadWorkflowFiles = loadWorkflowFiles;
|
|
7
|
+
exports.loadAppManifestFiles = loadAppManifestFiles;
|
|
8
|
+
exports.loadWorkflowApps = loadWorkflowApps;
|
|
9
|
+
exports.loadWorkflowAppById = loadWorkflowAppById;
|
|
10
|
+
exports.loadWorkflowAppTarget = loadWorkflowAppTarget;
|
|
11
|
+
exports.listWorkflows = listWorkflows;
|
|
12
|
+
exports.listApps = listApps;
|
|
13
|
+
exports.showApp = showApp;
|
|
14
|
+
exports.validateApp = validateApp;
|
|
15
|
+
exports.initApp = initApp;
|
|
16
|
+
exports.packageApp = packageApp;
|
|
17
|
+
// Workflow-app management domain operations (v0.1.40 self-audit router pattern).
|
|
18
|
+
// Carved out of CoolWorkflowRunner; pure functions (no instance state). Behavior
|
|
19
|
+
// is identical to the inline versions — the runner-owned calls (resolveFromBase,
|
|
20
|
+
// validateApp) are passed as callbacks so the bodies stay byte-identical.
|
|
21
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
22
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
23
|
+
const workflow_api_1 = require("../workflow-api");
|
|
24
|
+
const state_1 = require("../state");
|
|
25
|
+
const workflow_app_framework_1 = require("../workflow-app-framework");
|
|
26
|
+
const cli_options_1 = require("./cli-options");
|
|
27
|
+
function loadWorkflowFiles(workflowsDir) {
|
|
28
|
+
if (!node_fs_1.default.existsSync(workflowsDir))
|
|
29
|
+
return [];
|
|
30
|
+
return node_fs_1.default
|
|
31
|
+
.readdirSync(workflowsDir)
|
|
32
|
+
.filter((file) => file.endsWith(".workflow.js"))
|
|
33
|
+
.sort()
|
|
34
|
+
.map((file) => node_path_1.default.join(workflowsDir, file));
|
|
35
|
+
}
|
|
36
|
+
function loadAppManifestFiles(appsDir) {
|
|
37
|
+
if (!node_fs_1.default.existsSync(appsDir))
|
|
38
|
+
return [];
|
|
39
|
+
return node_fs_1.default
|
|
40
|
+
.readdirSync(appsDir, { withFileTypes: true })
|
|
41
|
+
.filter((entry) => entry.isDirectory())
|
|
42
|
+
.map((entry) => node_path_1.default.join(appsDir, entry.name, "app.json"))
|
|
43
|
+
.filter((file) => node_fs_1.default.existsSync(file))
|
|
44
|
+
.sort();
|
|
45
|
+
}
|
|
46
|
+
function loadWorkflowApps(workflowsDir, appsDir) {
|
|
47
|
+
const records = [
|
|
48
|
+
...loadWorkflowFiles(workflowsDir).map((file) => (0, workflow_app_framework_1.loadWorkflowAppFromEntrypoint)(file)),
|
|
49
|
+
...loadAppManifestFiles(appsDir).map((file) => (0, workflow_app_framework_1.loadWorkflowAppFromManifest)(file))
|
|
50
|
+
].sort((left, right) => {
|
|
51
|
+
const byId = left.app.id.localeCompare(right.app.id);
|
|
52
|
+
if (byId)
|
|
53
|
+
return byId;
|
|
54
|
+
return (left.source.manifestPath || left.source.entrypointPath || left.source.path)
|
|
55
|
+
.localeCompare(right.source.manifestPath || right.source.entrypointPath || right.source.path);
|
|
56
|
+
});
|
|
57
|
+
const seen = new Map();
|
|
58
|
+
for (const record of records) {
|
|
59
|
+
const previous = seen.get(record.app.id);
|
|
60
|
+
if (previous) {
|
|
61
|
+
throw new Error(`Duplicate workflow app id ${record.app.id}: ${previous.source.manifestPath || previous.source.entrypointPath || previous.source.path} and ${record.source.manifestPath || record.source.entrypointPath || record.source.path}`);
|
|
62
|
+
}
|
|
63
|
+
seen.set(record.app.id, record);
|
|
64
|
+
}
|
|
65
|
+
return records;
|
|
66
|
+
}
|
|
67
|
+
function loadWorkflowAppById(workflowsDir, appsDir, appId) {
|
|
68
|
+
const record = loadWorkflowApps(workflowsDir, appsDir).find((candidate) => candidate.app.id === appId);
|
|
69
|
+
if (!record)
|
|
70
|
+
throw new Error(`Workflow app not found: ${appId}`);
|
|
71
|
+
return record;
|
|
72
|
+
}
|
|
73
|
+
// The runner pre-resolves `target` via resolveFromBase and passes the resolved
|
|
74
|
+
// path; the original `target` is still used for the id-fallback lookup.
|
|
75
|
+
function loadWorkflowAppTarget(workflowsDir, appsDir, resolved, target) {
|
|
76
|
+
if (!target)
|
|
77
|
+
throw new Error("Missing workflow app path or id");
|
|
78
|
+
if (node_fs_1.default.existsSync(resolved)) {
|
|
79
|
+
const stat = node_fs_1.default.statSync(resolved);
|
|
80
|
+
if (stat.isDirectory())
|
|
81
|
+
return (0, workflow_app_framework_1.loadWorkflowAppFromManifest)(node_path_1.default.join(resolved, "app.json"));
|
|
82
|
+
if (node_path_1.default.basename(resolved) === "app.json" || resolved.endsWith(".json"))
|
|
83
|
+
return (0, workflow_app_framework_1.loadWorkflowAppFromManifest)(resolved);
|
|
84
|
+
return (0, workflow_app_framework_1.loadWorkflowAppFromEntrypoint)(resolved);
|
|
85
|
+
}
|
|
86
|
+
return loadWorkflowAppById(workflowsDir, appsDir, target);
|
|
87
|
+
}
|
|
88
|
+
function listWorkflows(workflowsDir, appsDir) {
|
|
89
|
+
return loadWorkflowApps(workflowsDir, appsDir).map((record) => {
|
|
90
|
+
const summary = (0, workflow_app_framework_1.summarizeWorkflowApp)(record);
|
|
91
|
+
return {
|
|
92
|
+
id: summary.id,
|
|
93
|
+
title: summary.title,
|
|
94
|
+
summary: summary.summary,
|
|
95
|
+
file: summary.file
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function listApps(workflowsDir, appsDir) {
|
|
100
|
+
return loadWorkflowApps(workflowsDir, appsDir).map((record) => (0, workflow_app_framework_1.summarizeWorkflowApp)(record));
|
|
101
|
+
}
|
|
102
|
+
function showApp(workflowsDir, appsDir, appId) {
|
|
103
|
+
const record = loadWorkflowAppById(workflowsDir, appsDir, appId);
|
|
104
|
+
const summary = (0, workflow_app_framework_1.summarizeWorkflowApp)(record);
|
|
105
|
+
return {
|
|
106
|
+
...summary,
|
|
107
|
+
source: record.source,
|
|
108
|
+
app: {
|
|
109
|
+
schemaVersion: record.app.schemaVersion,
|
|
110
|
+
id: record.app.id,
|
|
111
|
+
title: record.app.title,
|
|
112
|
+
summary: record.app.summary || "",
|
|
113
|
+
version: record.app.version,
|
|
114
|
+
author: record.app.author,
|
|
115
|
+
inputs: record.app.inputs || record.app.workflow.inputs,
|
|
116
|
+
sandboxProfiles: record.app.sandboxProfiles || record.app.workflow.sandboxProfiles || [],
|
|
117
|
+
compatibility: record.app.compatibility,
|
|
118
|
+
metadata: record.app.metadata || {}
|
|
119
|
+
},
|
|
120
|
+
workflow: {
|
|
121
|
+
id: record.app.workflow.id,
|
|
122
|
+
title: record.app.workflow.title,
|
|
123
|
+
summary: record.app.workflow.summary || "",
|
|
124
|
+
limits: record.app.workflow.limits,
|
|
125
|
+
inputs: record.app.workflow.inputs,
|
|
126
|
+
sandboxProfiles: record.app.workflow.sandboxProfiles || [],
|
|
127
|
+
phases: record.app.workflow.phases.map((phase) => ({
|
|
128
|
+
id: phase.id,
|
|
129
|
+
name: phase.name,
|
|
130
|
+
status: phase.status,
|
|
131
|
+
tasks: phase.tasks.map((task) => ({
|
|
132
|
+
id: task.id,
|
|
133
|
+
kind: task.kind,
|
|
134
|
+
requiresEvidence: Boolean(task.requiresEvidence),
|
|
135
|
+
sandboxProfileId: task.sandboxProfileId
|
|
136
|
+
}))
|
|
137
|
+
}))
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
// The runner pre-resolves `target` via resolveFromBase and passes the resolved
|
|
142
|
+
// path; both the try-loader and the catch-branch appPath use resolvedTarget.
|
|
143
|
+
function validateApp(workflowsDir, appsDir, target, resolvedTarget) {
|
|
144
|
+
try {
|
|
145
|
+
const record = loadWorkflowAppTarget(workflowsDir, appsDir, resolvedTarget, target);
|
|
146
|
+
const result = (0, workflow_app_framework_1.validateWorkflowApp)(record.app, {
|
|
147
|
+
appPath: record.source.manifestPath || record.source.entrypointPath || record.source.path
|
|
148
|
+
});
|
|
149
|
+
return {
|
|
150
|
+
...result,
|
|
151
|
+
summary: (0, workflow_app_framework_1.summarizeWorkflowApp)(record)
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
const issues = (0, cli_options_1.validationIssuesFromError)(error);
|
|
156
|
+
return {
|
|
157
|
+
valid: false,
|
|
158
|
+
appId: target,
|
|
159
|
+
appPath: resolvedTarget,
|
|
160
|
+
issues
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// resolveFromBase and validateApp are passed as callbacks so the body stays
|
|
165
|
+
// byte-identical to the runner method (resolveFromBase(...) and validateApp(manifestPath)).
|
|
166
|
+
function initApp(appsDir, appId, options, resolveFromBase, validateApp) {
|
|
167
|
+
const id = (0, workflow_api_1.slugify)(appId);
|
|
168
|
+
if (!id)
|
|
169
|
+
throw new Error("App id must include at least one letter or digit");
|
|
170
|
+
const title = String(options.title || titleize(id));
|
|
171
|
+
const destinationDir = resolveFromBase(String(options.directory || options.output || node_path_1.default.join(appsDir, id)));
|
|
172
|
+
const manifestPath = node_path_1.default.join(destinationDir, "app.json");
|
|
173
|
+
const entrypointPath = node_path_1.default.join(destinationDir, "workflow.js");
|
|
174
|
+
if (!options.force && (node_fs_1.default.existsSync(manifestPath) || node_fs_1.default.existsSync(entrypointPath))) {
|
|
175
|
+
throw new Error(`Refusing to overwrite existing workflow app: ${destinationDir}`);
|
|
176
|
+
}
|
|
177
|
+
node_fs_1.default.mkdirSync(destinationDir, { recursive: true });
|
|
178
|
+
node_fs_1.default.writeFileSync(manifestPath, (0, workflow_app_framework_1.renderWorkflowAppManifestTemplate)(id, title), "utf8");
|
|
179
|
+
node_fs_1.default.writeFileSync(entrypointPath, (0, workflow_app_framework_1.renderWorkflowAppEntrypointTemplate)(id, title), "utf8");
|
|
180
|
+
const validation = validateApp(manifestPath);
|
|
181
|
+
if (!validation.valid) {
|
|
182
|
+
throw new workflow_app_framework_1.WorkflowAppValidationError("Generated workflow app is invalid", validation.issues);
|
|
183
|
+
}
|
|
184
|
+
return { id, manifestPath, entrypointPath };
|
|
185
|
+
}
|
|
186
|
+
// resolveFromBase is passed as a callback so the body stays byte-identical.
|
|
187
|
+
function packageApp(workflowsDir, appsDir, appId, options, resolveFromBase) {
|
|
188
|
+
const record = loadWorkflowAppById(workflowsDir, appsDir, appId);
|
|
189
|
+
const destination = resolveFromBase(String(options.output || node_path_1.default.join(".cw", "packages", `${record.app.id}-${record.app.version}.cwapp.json`)));
|
|
190
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(destination), { recursive: true });
|
|
191
|
+
(0, state_1.writeJson)(destination, {
|
|
192
|
+
schemaVersion: 1,
|
|
193
|
+
app: (0, workflow_app_framework_1.workflowAppRunMetadata)(record),
|
|
194
|
+
workflow: record.app.workflow,
|
|
195
|
+
packagedAt: new Date().toISOString()
|
|
196
|
+
});
|
|
197
|
+
return { id: record.app.id, version: record.app.version, path: destination };
|
|
198
|
+
}
|
|
199
|
+
function titleize(value) {
|
|
200
|
+
return value
|
|
201
|
+
.split("-")
|
|
202
|
+
.filter(Boolean)
|
|
203
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
204
|
+
.join(" ");
|
|
205
|
+
}
|
|
@@ -25,6 +25,11 @@ function writeReport(run) {
|
|
|
25
25
|
(0, dispatch_1.updatePhaseStatuses)(run);
|
|
26
26
|
const workerSummary = (0, worker_isolation_1.summarizeWorkers)(run);
|
|
27
27
|
const candidateSummary = (0, candidate_scoring_1.summarizeCandidates)(run);
|
|
28
|
+
// A research run reads a local folder of files, not a code repo — label its source line
|
|
29
|
+
// "Source". Skip the relabel when the run ALSO carries a remote-provenance "- Source: <url>"
|
|
30
|
+
// line below (run.inputs.sourceUrl set by a --link/URL), so a report never shows two
|
|
31
|
+
// "- Source:" lines. Every other app keeps the byte-identical "Repository:" (POLA).
|
|
32
|
+
const sourceLabel = run.workflow.app?.metadata?.domain === "research" && !run.inputs.sourceUrl ? "Source" : "Repository";
|
|
28
33
|
const report = [
|
|
29
34
|
`# ${run.workflow.title}`,
|
|
30
35
|
"",
|
|
@@ -38,7 +43,7 @@ function writeReport(run) {
|
|
|
38
43
|
: []),
|
|
39
44
|
`- Created: ${run.createdAt}`,
|
|
40
45
|
`- Updated: ${run.updatedAt}`,
|
|
41
|
-
`-
|
|
46
|
+
`- ${sourceLabel}: ${String(run.inputs.repo || run.cwd)}`,
|
|
42
47
|
// Remote provenance (v0.1.91): when the repo was materialized from a --link/URL, record
|
|
43
48
|
// the sanitized origin + resolved commit so the report itself says where the code came
|
|
44
49
|
// from. Conditional — absent for a local-repo run, so existing reports stay byte-identical.
|
package/dist/orchestrator.js
CHANGED
|
@@ -42,9 +42,11 @@ exports.suggestCommand = suggestCommand;
|
|
|
42
42
|
exports.formatSearchResults = formatSearchResults;
|
|
43
43
|
exports.formatInfo = formatInfo;
|
|
44
44
|
exports.formatHelp = formatHelp;
|
|
45
|
+
exports.formatCommandHelp = formatCommandHelp;
|
|
45
46
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
46
47
|
const node_path_1 = __importDefault(require("node:path"));
|
|
47
48
|
const workflow_api_1 = require("./workflow-api");
|
|
49
|
+
const capability_registry_1 = require("./capability-registry");
|
|
48
50
|
const workflow_app_framework_1 = require("./workflow-app-framework");
|
|
49
51
|
const dispatch_1 = require("./dispatch");
|
|
50
52
|
const state_1 = require("./state");
|
|
@@ -62,6 +64,7 @@ const state_explosion_1 = require("./state-explosion");
|
|
|
62
64
|
const evidence_reasoning_1 = require("./evidence-reasoning");
|
|
63
65
|
const report_1 = require("./orchestrator/report");
|
|
64
66
|
const cli_options_1 = require("./orchestrator/cli-options");
|
|
67
|
+
const appOps = __importStar(require("./orchestrator/app-operations"));
|
|
65
68
|
const auditOps = __importStar(require("./orchestrator/audit-operations"));
|
|
66
69
|
const candidateOps = __importStar(require("./orchestrator/candidate-operations"));
|
|
67
70
|
const collaborationOps = __importStar(require("./orchestrator/collaboration-operations"));
|
|
@@ -114,110 +117,22 @@ class CoolWorkflowRunner {
|
|
|
114
117
|
return new CoolWorkflowRunner({ pluginRoot: this.pluginRoot, baseDir: resolved });
|
|
115
118
|
}
|
|
116
119
|
listWorkflows() {
|
|
117
|
-
return
|
|
118
|
-
const summary = (0, workflow_app_framework_1.summarizeWorkflowApp)(record);
|
|
119
|
-
return {
|
|
120
|
-
id: summary.id,
|
|
121
|
-
title: summary.title,
|
|
122
|
-
summary: summary.summary,
|
|
123
|
-
file: summary.file
|
|
124
|
-
};
|
|
125
|
-
});
|
|
120
|
+
return appOps.listWorkflows(this.workflowsDir, this.appsDir);
|
|
126
121
|
}
|
|
127
122
|
listApps() {
|
|
128
|
-
return
|
|
123
|
+
return appOps.listApps(this.workflowsDir, this.appsDir);
|
|
129
124
|
}
|
|
130
125
|
showApp(appId) {
|
|
131
|
-
|
|
132
|
-
const summary = (0, workflow_app_framework_1.summarizeWorkflowApp)(record);
|
|
133
|
-
return {
|
|
134
|
-
...summary,
|
|
135
|
-
source: record.source,
|
|
136
|
-
app: {
|
|
137
|
-
schemaVersion: record.app.schemaVersion,
|
|
138
|
-
id: record.app.id,
|
|
139
|
-
title: record.app.title,
|
|
140
|
-
summary: record.app.summary || "",
|
|
141
|
-
version: record.app.version,
|
|
142
|
-
author: record.app.author,
|
|
143
|
-
inputs: record.app.inputs || record.app.workflow.inputs,
|
|
144
|
-
sandboxProfiles: record.app.sandboxProfiles || record.app.workflow.sandboxProfiles || [],
|
|
145
|
-
compatibility: record.app.compatibility,
|
|
146
|
-
metadata: record.app.metadata || {}
|
|
147
|
-
},
|
|
148
|
-
workflow: {
|
|
149
|
-
id: record.app.workflow.id,
|
|
150
|
-
title: record.app.workflow.title,
|
|
151
|
-
summary: record.app.workflow.summary || "",
|
|
152
|
-
limits: record.app.workflow.limits,
|
|
153
|
-
inputs: record.app.workflow.inputs,
|
|
154
|
-
sandboxProfiles: record.app.workflow.sandboxProfiles || [],
|
|
155
|
-
phases: record.app.workflow.phases.map((phase) => ({
|
|
156
|
-
id: phase.id,
|
|
157
|
-
name: phase.name,
|
|
158
|
-
status: phase.status,
|
|
159
|
-
tasks: phase.tasks.map((task) => ({
|
|
160
|
-
id: task.id,
|
|
161
|
-
kind: task.kind,
|
|
162
|
-
requiresEvidence: Boolean(task.requiresEvidence),
|
|
163
|
-
sandboxProfileId: task.sandboxProfileId
|
|
164
|
-
}))
|
|
165
|
-
}))
|
|
166
|
-
}
|
|
167
|
-
};
|
|
126
|
+
return appOps.showApp(this.workflowsDir, this.appsDir, appId);
|
|
168
127
|
}
|
|
169
128
|
validateApp(target) {
|
|
170
|
-
|
|
171
|
-
const record = this.loadWorkflowAppTarget(target);
|
|
172
|
-
const result = (0, workflow_app_framework_1.validateWorkflowApp)(record.app, {
|
|
173
|
-
appPath: record.source.manifestPath || record.source.entrypointPath || record.source.path
|
|
174
|
-
});
|
|
175
|
-
return {
|
|
176
|
-
...result,
|
|
177
|
-
summary: (0, workflow_app_framework_1.summarizeWorkflowApp)(record)
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
catch (error) {
|
|
181
|
-
const issues = (0, cli_options_1.validationIssuesFromError)(error);
|
|
182
|
-
return {
|
|
183
|
-
valid: false,
|
|
184
|
-
appId: target,
|
|
185
|
-
appPath: this.resolveFromBase(target),
|
|
186
|
-
issues
|
|
187
|
-
};
|
|
188
|
-
}
|
|
129
|
+
return appOps.validateApp(this.workflowsDir, this.appsDir, target, this.resolveFromBase(target));
|
|
189
130
|
}
|
|
190
131
|
initApp(appId, options) {
|
|
191
|
-
|
|
192
|
-
if (!id)
|
|
193
|
-
throw new Error("App id must include at least one letter or digit");
|
|
194
|
-
const title = String(options.title || titleize(id));
|
|
195
|
-
const destinationDir = this.resolveFromBase(String(options.directory || options.output || node_path_1.default.join(this.appsDir, id)));
|
|
196
|
-
const manifestPath = node_path_1.default.join(destinationDir, "app.json");
|
|
197
|
-
const entrypointPath = node_path_1.default.join(destinationDir, "workflow.js");
|
|
198
|
-
if (!options.force && (node_fs_1.default.existsSync(manifestPath) || node_fs_1.default.existsSync(entrypointPath))) {
|
|
199
|
-
throw new Error(`Refusing to overwrite existing workflow app: ${destinationDir}`);
|
|
200
|
-
}
|
|
201
|
-
node_fs_1.default.mkdirSync(destinationDir, { recursive: true });
|
|
202
|
-
node_fs_1.default.writeFileSync(manifestPath, (0, workflow_app_framework_1.renderWorkflowAppManifestTemplate)(id, title), "utf8");
|
|
203
|
-
node_fs_1.default.writeFileSync(entrypointPath, (0, workflow_app_framework_1.renderWorkflowAppEntrypointTemplate)(id, title), "utf8");
|
|
204
|
-
const validation = this.validateApp(manifestPath);
|
|
205
|
-
if (!validation.valid) {
|
|
206
|
-
throw new workflow_app_framework_1.WorkflowAppValidationError("Generated workflow app is invalid", validation.issues);
|
|
207
|
-
}
|
|
208
|
-
return { id, manifestPath, entrypointPath };
|
|
132
|
+
return appOps.initApp(this.appsDir, appId, options, (t) => this.resolveFromBase(t), (m) => this.validateApp(m));
|
|
209
133
|
}
|
|
210
134
|
packageApp(appId, options = {}) {
|
|
211
|
-
|
|
212
|
-
const destination = this.resolveFromBase(String(options.output || node_path_1.default.join(".cw", "packages", `${record.app.id}-${record.app.version}.cwapp.json`)));
|
|
213
|
-
node_fs_1.default.mkdirSync(node_path_1.default.dirname(destination), { recursive: true });
|
|
214
|
-
(0, state_1.writeJson)(destination, {
|
|
215
|
-
schemaVersion: 1,
|
|
216
|
-
app: (0, workflow_app_framework_1.workflowAppRunMetadata)(record),
|
|
217
|
-
workflow: record.app.workflow,
|
|
218
|
-
packagedAt: new Date().toISOString()
|
|
219
|
-
});
|
|
220
|
-
return { id: record.app.id, version: record.app.version, path: destination };
|
|
135
|
+
return appOps.packageApp(this.workflowsDir, this.appsDir, appId, options, (t) => this.resolveFromBase(t));
|
|
221
136
|
}
|
|
222
137
|
init(workflowId, options) {
|
|
223
138
|
const id = (0, workflow_api_1.slugify)(workflowId);
|
|
@@ -726,64 +641,7 @@ class CoolWorkflowRunner {
|
|
|
726
641
|
return node_path_1.default.resolve(this.invocationCwd(), target);
|
|
727
642
|
}
|
|
728
643
|
loadWorkflowAppById(appId) {
|
|
729
|
-
|
|
730
|
-
if (!record)
|
|
731
|
-
throw new Error(`Workflow app not found: ${appId}`);
|
|
732
|
-
return record;
|
|
733
|
-
}
|
|
734
|
-
loadWorkflowAppTarget(target) {
|
|
735
|
-
if (!target)
|
|
736
|
-
throw new Error("Missing workflow app path or id");
|
|
737
|
-
const resolved = this.resolveFromBase(target);
|
|
738
|
-
if (node_fs_1.default.existsSync(resolved)) {
|
|
739
|
-
const stat = node_fs_1.default.statSync(resolved);
|
|
740
|
-
if (stat.isDirectory())
|
|
741
|
-
return (0, workflow_app_framework_1.loadWorkflowAppFromManifest)(node_path_1.default.join(resolved, "app.json"));
|
|
742
|
-
if (node_path_1.default.basename(resolved) === "app.json" || resolved.endsWith(".json"))
|
|
743
|
-
return (0, workflow_app_framework_1.loadWorkflowAppFromManifest)(resolved);
|
|
744
|
-
return (0, workflow_app_framework_1.loadWorkflowAppFromEntrypoint)(resolved);
|
|
745
|
-
}
|
|
746
|
-
return this.loadWorkflowAppById(target);
|
|
747
|
-
}
|
|
748
|
-
loadWorkflowApps() {
|
|
749
|
-
const records = [
|
|
750
|
-
...this.loadWorkflowFiles().map((file) => (0, workflow_app_framework_1.loadWorkflowAppFromEntrypoint)(file)),
|
|
751
|
-
...this.loadAppManifestFiles().map((file) => (0, workflow_app_framework_1.loadWorkflowAppFromManifest)(file))
|
|
752
|
-
].sort((left, right) => {
|
|
753
|
-
const byId = left.app.id.localeCompare(right.app.id);
|
|
754
|
-
if (byId)
|
|
755
|
-
return byId;
|
|
756
|
-
return (left.source.manifestPath || left.source.entrypointPath || left.source.path)
|
|
757
|
-
.localeCompare(right.source.manifestPath || right.source.entrypointPath || right.source.path);
|
|
758
|
-
});
|
|
759
|
-
const seen = new Map();
|
|
760
|
-
for (const record of records) {
|
|
761
|
-
const previous = seen.get(record.app.id);
|
|
762
|
-
if (previous) {
|
|
763
|
-
throw new Error(`Duplicate workflow app id ${record.app.id}: ${previous.source.manifestPath || previous.source.entrypointPath || previous.source.path} and ${record.source.manifestPath || record.source.entrypointPath || record.source.path}`);
|
|
764
|
-
}
|
|
765
|
-
seen.set(record.app.id, record);
|
|
766
|
-
}
|
|
767
|
-
return records;
|
|
768
|
-
}
|
|
769
|
-
loadWorkflowFiles() {
|
|
770
|
-
if (!node_fs_1.default.existsSync(this.workflowsDir))
|
|
771
|
-
return [];
|
|
772
|
-
return node_fs_1.default
|
|
773
|
-
.readdirSync(this.workflowsDir)
|
|
774
|
-
.filter((file) => file.endsWith(".workflow.js"))
|
|
775
|
-
.sort()
|
|
776
|
-
.map((file) => node_path_1.default.join(this.workflowsDir, file));
|
|
777
|
-
}
|
|
778
|
-
loadAppManifestFiles() {
|
|
779
|
-
if (!node_fs_1.default.existsSync(this.appsDir))
|
|
780
|
-
return [];
|
|
781
|
-
return node_fs_1.default
|
|
782
|
-
.readdirSync(this.appsDir, { withFileTypes: true })
|
|
783
|
-
.filter((entry) => entry.isDirectory())
|
|
784
|
-
.map((entry) => node_path_1.default.join(this.appsDir, entry.name, "app.json"))
|
|
785
|
-
.filter((file) => node_fs_1.default.existsSync(file))
|
|
786
|
-
.sort();
|
|
644
|
+
return appOps.loadWorkflowAppById(this.workflowsDir, this.appsDir, appId);
|
|
787
645
|
}
|
|
788
646
|
}
|
|
789
647
|
exports.CoolWorkflowRunner = CoolWorkflowRunner;
|
|
@@ -973,7 +831,37 @@ function formatHelp() {
|
|
|
973
831
|
" --no-color Disable ANSI color (also honors NO_COLOR / FORCE_COLOR)",
|
|
974
832
|
"",
|
|
975
833
|
(0, term_1.bold)("More commands", out),
|
|
976
|
-
...wrapped
|
|
834
|
+
...wrapped,
|
|
835
|
+
"",
|
|
836
|
+
// 4-space indent on purpose: the CLI/MCP parity help-token check only parses
|
|
837
|
+
// 2-space command lines, so this note never registers as a bogus token.
|
|
838
|
+
" Run cw help <command> for one command's subcommands and descriptions."
|
|
839
|
+
].join("\n");
|
|
840
|
+
}
|
|
841
|
+
/** Per-command help: `cw help <verb>` (and `cw <verb> --help`) lists that verb's
|
|
842
|
+
* CLI subcommands with one-line summaries, derived from CAPABILITY_REGISTRY — the
|
|
843
|
+
* SAME table the dispatcher and the CLI/MCP parity check use, so there is no
|
|
844
|
+
* second source to drift. Additive: formatHelp()'s parity-checked token list is
|
|
845
|
+
* untouched. */
|
|
846
|
+
function formatCommandHelp(verb) {
|
|
847
|
+
const out = process.stdout;
|
|
848
|
+
const matches = capability_registry_1.CAPABILITY_REGISTRY.filter((cap) => cap.cli && cap.cli.path[0] === verb);
|
|
849
|
+
if (matches.length === 0) {
|
|
850
|
+
const lines = [`Unknown command: ${verb}`];
|
|
851
|
+
const hint = suggestCommand(verb);
|
|
852
|
+
if (hint)
|
|
853
|
+
lines.push(` Did you mean: cw ${hint}`);
|
|
854
|
+
lines.push(" Try: cw help (list all commands)");
|
|
855
|
+
return lines.join("\n");
|
|
856
|
+
}
|
|
857
|
+
const rows = matches
|
|
858
|
+
.map((cap) => ({ cmd: `cw ${cap.cli.path.join(" ")}`, summary: cap.summary }))
|
|
859
|
+
.sort((a, b) => (a.cmd < b.cmd ? -1 : a.cmd > b.cmd ? 1 : 0));
|
|
860
|
+
const width = Math.min(40, rows.reduce((max, row) => Math.max(max, row.cmd.length), 0));
|
|
861
|
+
return [
|
|
862
|
+
(0, term_1.bold)(`cw ${verb}`, out),
|
|
863
|
+
"",
|
|
864
|
+
...rows.map((row) => ` ${row.cmd.padEnd(width)} ${row.summary}`)
|
|
977
865
|
].join("\n");
|
|
978
866
|
}
|
|
979
867
|
function appendOption(options, key, value) {
|
package/dist/state-explosion.js
CHANGED
|
@@ -7,7 +7,6 @@ exports.stateExplosionReportLines = exports.formatBlackboardDigest = exports.for
|
|
|
7
7
|
exports.computeStateSize = computeStateSize;
|
|
8
8
|
exports.summarizeBlackboardDigest = summarizeBlackboardDigest;
|
|
9
9
|
exports.buildCompactGraph = buildCompactGraph;
|
|
10
|
-
exports.buildOperatorDigest = buildOperatorDigest;
|
|
11
10
|
exports.buildStateExplosionReport = buildStateExplosionReport;
|
|
12
11
|
exports.maybeCompactRun = maybeCompactRun;
|
|
13
12
|
exports.refreshStateExplosionSummaries = refreshStateExplosionSummaries;
|
|
@@ -732,12 +731,6 @@ function expansionCommandFor(run, view, key) {
|
|
|
732
731
|
return `node scripts/cw.js multi-agent graph ${run.id} --view full --json`;
|
|
733
732
|
return `node scripts/cw.js multi-agent graph ${run.id} --view full --focus ${key} --json`;
|
|
734
733
|
}
|
|
735
|
-
// ---------------------------------------------------------------------------
|
|
736
|
-
// Operator digest
|
|
737
|
-
// ---------------------------------------------------------------------------
|
|
738
|
-
function buildOperatorDigest(run, thresholds = exports.DEFAULT_STATE_EXPLOSION_THRESHOLDS) {
|
|
739
|
-
return buildOperatorDigestWithContext(run, thresholds, createStateExplosionBuildContext());
|
|
740
|
-
}
|
|
741
734
|
function buildOperatorDigestWithContext(run, thresholds, context) {
|
|
742
735
|
const stateSize = stateSizeFor(run, thresholds, context);
|
|
743
736
|
const operator = operatorFor(run, context);
|
package/dist/term.js
CHANGED
|
@@ -14,13 +14,11 @@ exports.yellow = yellow;
|
|
|
14
14
|
exports.red = red;
|
|
15
15
|
exports.cyan = cyan;
|
|
16
16
|
exports.doctorGlyph = doctorGlyph;
|
|
17
|
-
exports.cwLabel = cwLabel;
|
|
18
17
|
exports.indent = indent;
|
|
19
18
|
exports.nextHint = nextHint;
|
|
20
19
|
exports.tryHint = tryHint;
|
|
21
20
|
exports.sectionHeader = sectionHeader;
|
|
22
21
|
exports.phaseProgressLine = phaseProgressLine;
|
|
23
|
-
exports.formatDuration = formatDuration;
|
|
24
22
|
exports.printSuccessSummary = printSuccessSummary;
|
|
25
23
|
exports.stripAnsi = stripAnsi;
|
|
26
24
|
exports.visibleWidth = visibleWidth;
|
|
@@ -84,10 +82,6 @@ function doctorGlyph(status, stream) {
|
|
|
84
82
|
};
|
|
85
83
|
return color[status](`${glyph[status]}`, stream);
|
|
86
84
|
}
|
|
87
|
-
/** "cw:" prefix with bold and optional color. */
|
|
88
|
-
function cwLabel(stream) {
|
|
89
|
-
return bold("cw:", stream);
|
|
90
|
-
}
|
|
91
85
|
/** Render a multi-line block with consistent 2-space indentation. */
|
|
92
86
|
function indent(text, spaces = 2) {
|
|
93
87
|
const prefix = " ".repeat(spaces);
|
|
@@ -113,18 +107,6 @@ function phaseProgressLine(name, done, total, mode, stream) {
|
|
|
113
107
|
const count = total > 0 ? ` (${done}/${total})` : "";
|
|
114
108
|
return `${sectionHeader(name, stream)} ${glyph}${count}`;
|
|
115
109
|
}
|
|
116
|
-
/** Format a DURATION in ms as `850ms` / `5.2s` / `1m02s`. Pure (no clock) — the
|
|
117
|
-
* caller measures elapsed via process.hrtime, so this never reads wall-clock time. */
|
|
118
|
-
function formatDuration(ms) {
|
|
119
|
-
if (ms < 1000)
|
|
120
|
-
return `${Math.max(0, Math.round(ms))}ms`;
|
|
121
|
-
const s = Math.round(ms / 100) / 10;
|
|
122
|
-
if (s < 60)
|
|
123
|
-
return `${s}s`;
|
|
124
|
-
const m = Math.floor(s / 60);
|
|
125
|
-
const rem = Math.round(s % 60);
|
|
126
|
-
return `${m}m${String(rem).padStart(2, "0")}s`;
|
|
127
|
-
}
|
|
128
110
|
/** Print a success summary to stderr (TTY-gated). Shows the report path, a one-line
|
|
129
111
|
* status (with N/N worker counts when known), and a copy-pasteable next/recovery
|
|
130
112
|
* command. Pipe-friendly: silent when stderr is not a TTY, so it never pollutes
|
package/dist/validation.js
CHANGED
|
@@ -19,15 +19,11 @@
|
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
20
|
exports.RecordValidationError = void 0;
|
|
21
21
|
exports.validateWorkerScope = validateWorkerScope;
|
|
22
|
-
exports.tryValidateWorkerScope = tryValidateWorkerScope;
|
|
23
22
|
exports.validateNodeSnapshot = validateNodeSnapshot;
|
|
24
|
-
exports.tryValidateNodeSnapshot = tryValidateNodeSnapshot;
|
|
25
23
|
exports.validateNodeReplayRun = validateNodeReplayRun;
|
|
26
|
-
exports.tryValidateNodeReplayRun = tryValidateNodeReplayRun;
|
|
27
24
|
exports.validateCandidateScore = validateCandidateScore;
|
|
28
25
|
exports.tryValidateCandidateScore = tryValidateCandidateScore;
|
|
29
26
|
exports.validateCandidateRecord = validateCandidateRecord;
|
|
30
|
-
exports.tryValidateCandidateRecord = tryValidateCandidateRecord;
|
|
31
27
|
// ---------------------------------------------------------------------------
|
|
32
28
|
// Primitive predicates — small, total, never throw.
|
|
33
29
|
// ---------------------------------------------------------------------------
|
|
@@ -126,10 +122,6 @@ function validateWorkerScope(value) {
|
|
|
126
122
|
throw new RecordValidationError("WorkerScope", problem.reason, problem.field);
|
|
127
123
|
return value;
|
|
128
124
|
}
|
|
129
|
-
/** Best-effort variant: returns null on mismatch (caller skips the record). */
|
|
130
|
-
function tryValidateWorkerScope(value) {
|
|
131
|
-
return workerScopeReason(value) ? null : value;
|
|
132
|
-
}
|
|
133
125
|
// ---------------------------------------------------------------------------
|
|
134
126
|
// NodeSnapshotBody — shared by NodeSnapshot.body and NodeReplayRun.body.
|
|
135
127
|
// Required (per src/types/state-node.ts): id, kind, status, loopStage strings;
|
|
@@ -184,10 +176,6 @@ function validateNodeSnapshot(value) {
|
|
|
184
176
|
throw new RecordValidationError("NodeSnapshot", problem.reason, problem.field);
|
|
185
177
|
return value;
|
|
186
178
|
}
|
|
187
|
-
/** Best-effort variant: returns null on mismatch. */
|
|
188
|
-
function tryValidateNodeSnapshot(value) {
|
|
189
|
-
return nodeSnapshotReason(value) ? null : value;
|
|
190
|
-
}
|
|
191
179
|
// ---------------------------------------------------------------------------
|
|
192
180
|
// NodeReplayRun — node-snapshot.ts:133
|
|
193
181
|
// Required: schemaVersion===1, replayId, runId, nodeId, snapshotId, replayedAt,
|
|
@@ -226,10 +214,6 @@ function validateNodeReplayRun(value) {
|
|
|
226
214
|
throw new RecordValidationError("NodeReplayRun", problem.reason, problem.field);
|
|
227
215
|
return value;
|
|
228
216
|
}
|
|
229
|
-
/** Best-effort variant: returns null on mismatch. */
|
|
230
|
-
function tryValidateNodeReplayRun(value) {
|
|
231
|
-
return nodeReplayRunReason(value) ? null : value;
|
|
232
|
-
}
|
|
233
217
|
// ---------------------------------------------------------------------------
|
|
234
218
|
// CandidateScore — multi-agent-operator-ux.ts:502 / evidence-reasoning.ts:750
|
|
235
219
|
// Required (per src/types/candidate.ts): schemaVersion===1, id, candidateId,
|
|
@@ -321,8 +305,3 @@ function validateCandidateRecord(value) {
|
|
|
321
305
|
throw new RecordValidationError("CandidateRecord", problem.reason, problem.field);
|
|
322
306
|
return value;
|
|
323
307
|
}
|
|
324
|
-
/** Best-effort variant: returns null on mismatch (caller skips the record so the
|
|
325
|
-
* downstream selection gate fails closed on its absence). */
|
|
326
|
-
function tryValidateCandidateRecord(value) {
|
|
327
|
-
return candidateRecordReason(value) ? null : value;
|
|
328
|
-
}
|
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.93";
|
|
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;
|
|
@@ -98,6 +98,11 @@ node scripts/cw.js plan release-cut \
|
|
|
98
98
|
Break a research question into claims, look into sources, cross-check
|
|
99
99
|
the evidence, check the claims, and put together a short answer.
|
|
100
100
|
|
|
101
|
+
When you point it at a folder, it reads the local files there as primary
|
|
102
|
+
sources. So this app works over any corpus — your own docs, notes, or
|
|
103
|
+
papers — not only a git code repo. The working directory is the corpus,
|
|
104
|
+
so the agent can back its answer with your own files.
|
|
105
|
+
|
|
101
106
|
```bash
|
|
102
107
|
node scripts/cw.js plan research-synthesis \
|
|
103
108
|
--cwd /tmp/research-run \
|
|
@@ -107,6 +112,13 @@ node scripts/cw.js plan research-synthesis \
|
|
|
107
112
|
--freshness "as of today"
|
|
108
113
|
```
|
|
109
114
|
|
|
115
|
+
Over a local corpus folder, point `--repo` at it:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
cw quickstart research-synthesis --repo /path/to/papers \
|
|
119
|
+
--question "What do these papers conclude?"
|
|
120
|
+
```
|
|
121
|
+
|
|
110
122
|
## Validation Matrix
|
|
111
123
|
|
|
112
124
|
Run the canonical app matrix from the plugin root directory:
|