cool-workflow 0.1.78 → 0.1.80
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 +29 -3
- package/apps/architecture-review/app.json +1 -1
- package/apps/architecture-review-fast/app.json +64 -0
- package/apps/architecture-review-fast/workflow.js +153 -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 +71 -0
- package/dist/capability-registry.js +13 -8
- package/dist/cli.js +49 -1
- package/dist/drive.js +74 -1
- package/dist/evidence-reasoning.js +2 -2
- package/dist/execution-backend.js +6 -1
- package/dist/mcp-server.js +56 -13
- package/dist/orchestrator/lifecycle-operations.js +2 -1
- package/dist/orchestrator.js +1 -1
- package/dist/run-export.js +370 -25
- package/dist/run-registry.js +11 -4
- package/dist/state-explosion.js +100 -21
- package/dist/telemetry-demo.js +154 -0
- package/dist/version.js +1 -1
- package/docs/agent-delegation-drive.7.md +60 -0
- package/docs/canonical-workflow-apps.7.md +37 -0
- package/docs/cli-mcp-parity.7.md +14 -0
- package/docs/contract-migration-tooling.7.md +6 -0
- package/docs/control-plane-scheduling.7.md +6 -0
- package/docs/durable-state-and-locking.7.md +6 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +6 -0
- package/docs/execution-backends.7.md +6 -0
- package/docs/index.md +1 -0
- package/docs/launch/demo.tape +28 -0
- package/docs/launch/launch-kit.md +172 -0
- package/docs/launch/pre-launch-checklist.md +53 -0
- package/docs/multi-agent-cli-mcp-surface.7.md +6 -0
- package/docs/multi-agent-eval-replay-harness.7.md +6 -0
- package/docs/multi-agent-operator-ux.7.md +6 -0
- package/docs/node-snapshot-diff-replay.7.md +6 -0
- package/docs/observability-cost-accounting.7.md +6 -0
- package/docs/project-index.md +16 -6
- package/docs/real-execution-backends.7.md +6 -0
- package/docs/release-and-migration.7.md +6 -0
- package/docs/release-tooling.7.md +6 -0
- package/docs/routines.md +23 -0
- package/docs/run-registry-control-plane.7.md +44 -1
- package/docs/run-retention-reclamation.7.md +6 -0
- package/docs/source-context-profiles.7.md +119 -0
- package/docs/state-explosion-management.7.md +13 -0
- package/docs/team-collaboration.7.md +6 -0
- package/docs/unix-principles.md +49 -1
- package/docs/web-desktop-workbench.7.md +6 -0
- package/manifest/plugin.manifest.json +1 -1
- package/manifest/source-context-profiles.json +142 -0
- package/package.json +2 -1
- package/scripts/agents/claude-p-agent.js +129 -43
- package/scripts/architecture-review-fast.js +362 -0
- package/scripts/bump-version.js +1 -0
- package/scripts/canonical-apps.js +21 -4
- package/scripts/coverage-gate.js +211 -0
- package/scripts/dogfood-release.js +1 -1
- package/scripts/golden-path.js +4 -4
- package/scripts/source-context.js +291 -0
- package/scripts/version-sync-check.js +1 -0
- package/skills/ci-triage/SKILL.md +50 -0
- package/skills/ci-triage/agents/openai.yaml +4 -0
- package/skills/cool-workflow/SKILL.md +4 -1
- package/skills/deploy-check/SKILL.md +55 -0
- package/skills/deploy-check/agents/openai.yaml +4 -0
- package/skills/design-qa/SKILL.md +49 -0
- package/skills/design-qa/agents/openai.yaml +4 -0
- package/skills/pr-review/SKILL.md +45 -0
- package/skills/pr-review/agents/openai.yaml +4 -0
package/docs/unix-principles.md
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# Unix-Inspired Workflow Principles
|
|
2
2
|
|
|
3
3
|
CW borrows a small set of durable systems ideas and applies them to agent
|
|
4
|
-
workflow engineering. These are design principles, not platform claims
|
|
4
|
+
workflow engineering. These are design principles, not platform claims — but
|
|
5
|
+
they are not optional: this project strictly follows the FreeBSD programming
|
|
6
|
+
philosophy, and §7 below states the binding rules every change is reviewed
|
|
7
|
+
against (mirrored as hard constraints in the repository's `AGENTS.md`).
|
|
5
8
|
|
|
6
9
|
## 1. Everything Is State
|
|
7
10
|
|
|
@@ -190,3 +193,48 @@ Hosts enforce runtime sandbox policy.
|
|
|
190
193
|
```
|
|
191
194
|
|
|
192
195
|
This keeps CW small, inspectable, and extensible.
|
|
196
|
+
|
|
197
|
+
## 7. FreeBSD Discipline (Binding Rules)
|
|
198
|
+
|
|
199
|
+
The principles above descend from one tradition — the FreeBSD school of
|
|
200
|
+
systems engineering — and CW adheres to it strictly. Concretely:
|
|
201
|
+
|
|
202
|
+
**POLA — Principle of Least Astonishment.** An existing output, file layout,
|
|
203
|
+
exit code, or flag never changes meaning or bytes underneath an operator. New
|
|
204
|
+
behavior ships behind a new verb/flag or an env toggle, with the prior
|
|
205
|
+
behavior byte-identical by default. (Example: live drive output is additive —
|
|
206
|
+
stderr only, TTY-gated, `CW_NO_STREAM=1` opt-out; the stdout payload and
|
|
207
|
+
evidence digest are unchanged.)
|
|
208
|
+
|
|
209
|
+
**Mechanism, not policy.** The kernel provides mechanisms; policy is data in
|
|
210
|
+
userland. WHICH agent runs is config (`CW_AGENT_COMMAND` / agent-config), not
|
|
211
|
+
code; vendor-specific rendering lives in wrappers under `scripts/agents/`,
|
|
212
|
+
never in core. Core may forward a vendor's stream; it never parses one.
|
|
213
|
+
|
|
214
|
+
**Rule of Silence.** stdout is data, stderr is diagnostics, and a
|
|
215
|
+
non-interactive run is silent on success. Anything human-friendly is TTY-gated
|
|
216
|
+
and can be disabled; `--json` output is stable and undecorated so it composes
|
|
217
|
+
in pipes.
|
|
218
|
+
|
|
219
|
+
**Fail closed, conservative defaults.** Unconfigured backends probe as
|
|
220
|
+
`unverified`, unverifiable telemetry is surfaced loudly (or refused in strict
|
|
221
|
+
mode), invalid results park the hop. CW never fabricates a success and never
|
|
222
|
+
falls back silently. Boring correctness beats clever features.
|
|
223
|
+
|
|
224
|
+
**Tools, not frameworks.** Zero runtime dependencies is a red line. Verbs do
|
|
225
|
+
one thing; composition happens through durable files (`.cw/`) and pipes, not
|
|
226
|
+
hidden in-process coupling.
|
|
227
|
+
|
|
228
|
+
**Man pages are the contract.** Every shipped capability has a `docs/*.7.md`
|
|
229
|
+
page updated in the same change, and doc-drift guards in the test suite keep
|
|
230
|
+
the documented commands honest. Undocumented behavior is unfinished behavior.
|
|
231
|
+
|
|
232
|
+
**style(9) spirit.** One consistent style per layer; a diff matches the file
|
|
233
|
+
it touches and never reformats code it does not change.
|
|
234
|
+
|
|
235
|
+
**Release engineering.** Main is -CURRENT; a tag is -RELEASE: it exists only
|
|
236
|
+
after the deterministic gate and an independent review pass, and cadence never
|
|
237
|
+
overrides the gate.
|
|
238
|
+
|
|
239
|
+
A change that violates any rule in this section is rejected in review even if
|
|
240
|
+
the capability it ships is otherwise desirable.
|
|
@@ -213,3 +213,9 @@ Migration DAG with reversible edges (v0.1.45), capability auto-discovery (v0.1.4
|
|
|
213
213
|
0.1.77
|
|
214
214
|
|
|
215
215
|
0.1.78
|
|
216
|
+
|
|
217
|
+
0.1.79
|
|
218
|
+
|
|
219
|
+
## Fast Architecture Review (v0.1.80)
|
|
220
|
+
|
|
221
|
+
Adds the opt-in fast architecture-review lane: scoped JSONL source contexts, diff-aware exports, reusable Map and Assess results, measurable wrapper metrics, actionable background full-review handoff, and userland model policy flags for routing fast/strong workers without changing the full review contract.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"_comment": "SINGLE SOURCE OF TRUTH for every vendor manifest. Edit THIS file, then run `npm run gen:manifests`. Do NOT hand-edit the generated vendor manifests (.claude-plugin/, .codex-plugin/, .agents/, .mcp.json) — `npm run gen:manifests -- --check` (run by release:check) will fail if they drift from this source.",
|
|
3
3
|
"identity": {
|
|
4
4
|
"name": "cool-workflow",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.80",
|
|
6
6
|
"license": "BSD-2-Clause",
|
|
7
7
|
"homepage": "https://github.com/coo1white/cool-workflow",
|
|
8
8
|
"author": {
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"profiles": {
|
|
4
|
+
"core": {
|
|
5
|
+
"description": "Default AI source context: runtime source, apps, package metadata, and agent wrappers; generated artifacts, tests, docs, release records, and long logs are manifest-only.",
|
|
6
|
+
"maxLines": 50000,
|
|
7
|
+
"include": [
|
|
8
|
+
"plugins/cool-workflow/src/**",
|
|
9
|
+
"plugins/cool-workflow/apps/**",
|
|
10
|
+
"plugins/cool-workflow/package.json",
|
|
11
|
+
"plugins/cool-workflow/tsconfig.json",
|
|
12
|
+
"plugins/cool-workflow/scripts/cw.js",
|
|
13
|
+
"plugins/cool-workflow/scripts/mcp-server.js",
|
|
14
|
+
"plugins/cool-workflow/scripts/agents/**"
|
|
15
|
+
],
|
|
16
|
+
"exclude": [
|
|
17
|
+
"plugins/cool-workflow/dist/**",
|
|
18
|
+
"plugins/cool-workflow/test/**",
|
|
19
|
+
"plugins/cool-workflow/docs/**",
|
|
20
|
+
"docs/assets/**",
|
|
21
|
+
".cw-release/**",
|
|
22
|
+
"CHANGELOG.md",
|
|
23
|
+
"ITERATION_LOG.md"
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
"runtime": {
|
|
27
|
+
"description": "Runtime-kernel source context for state, orchestration, scheduling, execution, and shared types.",
|
|
28
|
+
"maxLines": 40000,
|
|
29
|
+
"include": [
|
|
30
|
+
"plugins/cool-workflow/src/**",
|
|
31
|
+
"plugins/cool-workflow/package.json",
|
|
32
|
+
"plugins/cool-workflow/tsconfig.json"
|
|
33
|
+
],
|
|
34
|
+
"exclude": [
|
|
35
|
+
"plugins/cool-workflow/dist/**",
|
|
36
|
+
"plugins/cool-workflow/test/**",
|
|
37
|
+
"plugins/cool-workflow/docs/**",
|
|
38
|
+
"plugins/cool-workflow/apps/**",
|
|
39
|
+
"plugins/cool-workflow/scripts/**",
|
|
40
|
+
"docs/assets/**",
|
|
41
|
+
".cw-release/**",
|
|
42
|
+
"CHANGELOG.md",
|
|
43
|
+
"ITERATION_LOG.md"
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"mcp": {
|
|
47
|
+
"description": "MCP and CLI surface context for capability registry, MCP server, CLI routing, and MCP launcher wrappers.",
|
|
48
|
+
"maxLines": 18000,
|
|
49
|
+
"include": [
|
|
50
|
+
"plugins/cool-workflow/src/capability-core.ts",
|
|
51
|
+
"plugins/cool-workflow/src/capability-registry.ts",
|
|
52
|
+
"plugins/cool-workflow/src/cli.ts",
|
|
53
|
+
"plugins/cool-workflow/src/mcp-server.ts",
|
|
54
|
+
"plugins/cool-workflow/src/types/**",
|
|
55
|
+
"plugins/cool-workflow/scripts/cw.js",
|
|
56
|
+
"plugins/cool-workflow/scripts/mcp-server.js",
|
|
57
|
+
"plugins/cool-workflow/package.json",
|
|
58
|
+
"plugins/cool-workflow/tsconfig.json"
|
|
59
|
+
],
|
|
60
|
+
"exclude": [
|
|
61
|
+
"plugins/cool-workflow/dist/**",
|
|
62
|
+
"plugins/cool-workflow/test/**",
|
|
63
|
+
"plugins/cool-workflow/docs/**",
|
|
64
|
+
"docs/assets/**",
|
|
65
|
+
".cw-release/**",
|
|
66
|
+
"CHANGELOG.md",
|
|
67
|
+
"ITERATION_LOG.md"
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
"workflow-apps": {
|
|
71
|
+
"description": "Workflow App framework and canonical app context without the full runtime kernel.",
|
|
72
|
+
"maxLines": 18000,
|
|
73
|
+
"include": [
|
|
74
|
+
"plugins/cool-workflow/apps/**",
|
|
75
|
+
"plugins/cool-workflow/src/workflow-app-framework.ts",
|
|
76
|
+
"plugins/cool-workflow/src/orchestrator.ts",
|
|
77
|
+
"plugins/cool-workflow/src/orchestrator/**",
|
|
78
|
+
"plugins/cool-workflow/src/types/workflow-app.ts",
|
|
79
|
+
"plugins/cool-workflow/src/types/run.ts",
|
|
80
|
+
"plugins/cool-workflow/scripts/canonical-apps.js",
|
|
81
|
+
"plugins/cool-workflow/package.json",
|
|
82
|
+
"plugins/cool-workflow/tsconfig.json"
|
|
83
|
+
],
|
|
84
|
+
"exclude": [
|
|
85
|
+
"plugins/cool-workflow/dist/**",
|
|
86
|
+
"plugins/cool-workflow/test/**",
|
|
87
|
+
"plugins/cool-workflow/docs/**",
|
|
88
|
+
"docs/assets/**",
|
|
89
|
+
".cw-release/**",
|
|
90
|
+
"CHANGELOG.md",
|
|
91
|
+
"ITERATION_LOG.md"
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
"release": {
|
|
95
|
+
"description": "Release-engineering context for gates, release flow, version sync, manifests, and package metadata.",
|
|
96
|
+
"maxLines": 12000,
|
|
97
|
+
"include": [
|
|
98
|
+
"AGENTS.md",
|
|
99
|
+
"plugins/cool-workflow/scripts/release-flow.js",
|
|
100
|
+
"plugins/cool-workflow/scripts/release-gate.sh",
|
|
101
|
+
"plugins/cool-workflow/scripts/dogfood-release.js",
|
|
102
|
+
"plugins/cool-workflow/scripts/gen-manifests.js",
|
|
103
|
+
"plugins/cool-workflow/scripts/version-sync-check.js",
|
|
104
|
+
"plugins/cool-workflow/scripts/bump-version.js",
|
|
105
|
+
"plugins/cool-workflow/package.json",
|
|
106
|
+
"plugins/cool-workflow/manifest/**",
|
|
107
|
+
"plugins/cool-workflow/docs/release-tooling.7.md"
|
|
108
|
+
],
|
|
109
|
+
"exclude": [
|
|
110
|
+
"plugins/cool-workflow/dist/**",
|
|
111
|
+
"plugins/cool-workflow/test/**",
|
|
112
|
+
"docs/assets/**",
|
|
113
|
+
".cw-release/**",
|
|
114
|
+
"CHANGELOG.md",
|
|
115
|
+
"ITERATION_LOG.md"
|
|
116
|
+
]
|
|
117
|
+
},
|
|
118
|
+
"agent-wrappers": {
|
|
119
|
+
"description": "External agent delegation wrappers and neutral backend configuration context.",
|
|
120
|
+
"maxLines": 14000,
|
|
121
|
+
"include": [
|
|
122
|
+
"plugins/cool-workflow/scripts/agents/**",
|
|
123
|
+
"plugins/cool-workflow/scripts/architecture-review-fast.js",
|
|
124
|
+
"plugins/cool-workflow/src/agent-config.ts",
|
|
125
|
+
"plugins/cool-workflow/src/execution-backend.ts",
|
|
126
|
+
"plugins/cool-workflow/src/drive.ts",
|
|
127
|
+
"plugins/cool-workflow/src/types/execution-backend.ts",
|
|
128
|
+
"plugins/cool-workflow/src/types/drive.ts",
|
|
129
|
+
"plugins/cool-workflow/package.json",
|
|
130
|
+
"plugins/cool-workflow/docs/agent-delegation-drive.7.md"
|
|
131
|
+
],
|
|
132
|
+
"exclude": [
|
|
133
|
+
"plugins/cool-workflow/dist/**",
|
|
134
|
+
"plugins/cool-workflow/test/**",
|
|
135
|
+
"docs/assets/**",
|
|
136
|
+
".cw-release/**",
|
|
137
|
+
"CHANGELOG.md",
|
|
138
|
+
"ITERATION_LOG.md"
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cool-workflow",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.80",
|
|
4
4
|
"bin": {
|
|
5
5
|
"cool-workflow": "scripts/cw.js",
|
|
6
6
|
"cw": "scripts/cw.js"
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"release:check": "node scripts/release-check.js",
|
|
57
57
|
"test": "node dist/cli.js list && node test/run-all.js",
|
|
58
58
|
"test:fast": "npm run build --if-present && node dist/cli.js list && node test/run-all.js --concurrency auto",
|
|
59
|
+
"test:coverage": "node dist/cli.js list && node scripts/coverage-gate.js",
|
|
59
60
|
"eval:replay": "tsc -p tsconfig.json && node test/multi-agent-eval-replay-harness-smoke.js",
|
|
60
61
|
"ci": "npm run build && npm run check && npm run test && npm run release:check",
|
|
61
62
|
"validate:schema": "node scripts/validate-run-state-schema.js"
|
|
@@ -5,27 +5,30 @@
|
|
|
5
5
|
//
|
|
6
6
|
// This is a CONFIG, NOT a CW dependency: CW spawns it out-of-process (argv-style,
|
|
7
7
|
// shell:false, cwd = the target repo) and records its attested output; the model
|
|
8
|
-
// runs in claude's process, never in CW.
|
|
9
|
-
// delegates here) so the documented onboarding path is portable (node-only repo
|
|
10
|
-
// convention, Windows included).
|
|
8
|
+
// runs in claude's process, never in CW.
|
|
11
9
|
//
|
|
12
10
|
// It fulfills ONE worker: read the worker's input.md ({{input}}), delegate the
|
|
13
11
|
// analysis to headless claude READ-ONLY, persist claude's final markdown to the
|
|
14
|
-
// worker's result.md ({{result}}), and forward claude's JSON
|
|
15
|
-
//
|
|
12
|
+
// worker's result.md ({{result}}), and forward claude's JSON on STDOUT so CW
|
|
13
|
+
// records the agent-reported provenance.
|
|
16
14
|
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
15
|
+
// LIVE OUTPUT (Unix discipline): default output is the legacy `--output-format
|
|
16
|
+
// json` contract, forwarded verbatim on stdout. Set CW_AGENT_STREAM=1 to opt in
|
|
17
|
+
// to claude `stream-json`; only then does this wrapper render a human-readable
|
|
18
|
+
// trace to stderr, and only when stderr is a TTY. Diagnostics stay off stdout,
|
|
19
|
+
// and vendor-specific stream parsing lives HERE in the wrapper (policy), not in
|
|
20
|
+
// CW's core (which only forwards, never parses).
|
|
21
21
|
//
|
|
22
|
-
//
|
|
22
|
+
// READ-ONLY by design: claude gets NO Write tool; the architecture-review app
|
|
23
|
+
// declares the `readonly` sandbox profile. This wrapper (the transport) writes
|
|
24
|
+
// the single result.md artifact itself, so the worker completes without granting
|
|
25
|
+
// the model file-write access.
|
|
26
|
+
//
|
|
27
|
+
// Point CW at it (from plugins/cool-workflow/), or use the `builtin:claude` alias:
|
|
23
28
|
// CW_AGENT_COMMAND="node $(pwd)/scripts/agents/claude-p-agent.js {{input}} {{result}}"
|
|
24
|
-
// or per-invocation:
|
|
25
|
-
// --agent-command "node $(pwd)/scripts/agents/claude-p-agent.js {{input}} {{result}}"
|
|
26
29
|
|
|
27
30
|
const fs = require("node:fs");
|
|
28
|
-
const { spawnSync } = require("node:child_process");
|
|
31
|
+
const { spawn, spawnSync } = require("node:child_process");
|
|
29
32
|
|
|
30
33
|
const inputPath = process.argv[2];
|
|
31
34
|
const resultPath = process.argv[3];
|
|
@@ -63,42 +66,125 @@ HARD RULES (the result is REJECTED otherwise):
|
|
|
63
66
|
- "classification", if present, MUST be one of: real, conditional, non-issue, unknown.
|
|
64
67
|
- Any finding with "severity" P0, P1, or P2 MUST include a NON-EMPTY "evidence" array.
|
|
65
68
|
- The top-level "evidence" array MUST be NON-EMPTY with REAL file:line locators from this repo.
|
|
66
|
-
- If you have no structured findings, use "findings": [] (empty) — never omit a finding's id
|
|
67
|
-
`;
|
|
69
|
+
- If you have no structured findings, use "findings": [] (empty) — never omit a finding's id.`;
|
|
68
70
|
|
|
69
71
|
const prompt = `${fs.readFileSync(inputPath, "utf8")}\n${CONTRACT}`;
|
|
72
|
+
const streamEnabled = process.env.CW_AGENT_STREAM === "1" && process.env.CW_NO_STREAM !== "1";
|
|
73
|
+
const traceEnabled = streamEnabled && Boolean(process.stderr.isTTY);
|
|
70
74
|
|
|
71
|
-
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
75
|
+
if (!streamEnabled) {
|
|
76
|
+
// Legacy default: --output-format json and verbatim stdout forwarding. This is
|
|
77
|
+
// the public wrapper contract existing users already scripted against.
|
|
78
|
+
const child = spawnSync("claude", ["-p", prompt, "--output-format", "json", "--allowedTools", "Read,Grep,Glob,Bash"], {
|
|
79
|
+
encoding: "utf8",
|
|
80
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
81
|
+
shell: false
|
|
82
|
+
});
|
|
83
|
+
if (child.error) {
|
|
84
|
+
process.stderr.write(`claude spawn failed: ${child.error.message}\n`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
if (child.status !== 0) {
|
|
88
|
+
process.stderr.write(String(child.stderr || `claude exited ${child.status}`));
|
|
89
|
+
process.exit(child.status === null ? 1 : child.status);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const out = String(child.stdout || "");
|
|
93
|
+
let parsed;
|
|
94
|
+
try {
|
|
95
|
+
parsed = JSON.parse(out);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
process.stderr.write(`claude output was not JSON: ${error.message}\n`);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
fs.writeFileSync(resultPath, String(parsed.result || ""), "utf8");
|
|
102
|
+
process.stdout.write(out);
|
|
103
|
+
process.exit(0);
|
|
83
104
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
105
|
+
|
|
106
|
+
// Live trace → stderr only. Concise; one line per meaningful event.
|
|
107
|
+
function trace(line) {
|
|
108
|
+
if (!traceEnabled) return;
|
|
109
|
+
process.stderr.write(`${line}\n`);
|
|
110
|
+
}
|
|
111
|
+
function shortInput(tool, input) {
|
|
112
|
+
if (!input || typeof input !== "object") return "";
|
|
113
|
+
const v = input.file_path || input.path || input.pattern || input.command || input.query || input.url || "";
|
|
114
|
+
const s = String(v).replace(/\s+/g, " ").trim();
|
|
115
|
+
return s ? ` ${s.length > 80 ? s.slice(0, 77) + "…" : s}` : "";
|
|
87
116
|
}
|
|
88
117
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
118
|
+
// stream-json so claude emits incremental NDJSON events we can render live, while
|
|
119
|
+
// we reconstruct the single {model, usage, result} object CW consumes on stdout.
|
|
120
|
+
const child = spawn(
|
|
121
|
+
"claude",
|
|
122
|
+
["-p", prompt, "--output-format", "stream-json", "--verbose", "--allowedTools", "Read,Grep,Glob,Bash"],
|
|
123
|
+
{ stdio: ["ignore", "pipe", "inherit"] } // claude's own stderr → straight through
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
let model;
|
|
127
|
+
let usage;
|
|
128
|
+
let resultText;
|
|
129
|
+
let buf = "";
|
|
130
|
+
|
|
131
|
+
trace("● claude: reading the repo (read-only)…");
|
|
132
|
+
|
|
133
|
+
child.stdout.setEncoding("utf8");
|
|
134
|
+
child.stdout.on("data", (chunk) => {
|
|
135
|
+
buf += chunk;
|
|
136
|
+
let nl;
|
|
137
|
+
while ((nl = buf.indexOf("\n")) >= 0) {
|
|
138
|
+
const line = buf.slice(0, nl).trim();
|
|
139
|
+
buf = buf.slice(nl + 1);
|
|
140
|
+
if (!line) continue;
|
|
141
|
+
let ev;
|
|
142
|
+
try {
|
|
143
|
+
ev = JSON.parse(line);
|
|
144
|
+
} catch {
|
|
145
|
+
continue; // not a complete JSON line; ignore (defensive)
|
|
146
|
+
}
|
|
147
|
+
renderEvent(ev);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
function renderEvent(ev) {
|
|
152
|
+
if (ev.type === "assistant" && ev.message) {
|
|
153
|
+
if (!model && typeof ev.message.model === "string") model = ev.message.model;
|
|
154
|
+
for (const part of ev.message.content || []) {
|
|
155
|
+
if (part.type === "text" && part.text && part.text.trim()) {
|
|
156
|
+
trace(` ${part.text.trim().replace(/\n+/g, "\n ")}`);
|
|
157
|
+
} else if (part.type === "tool_use") {
|
|
158
|
+
trace(` → ${part.name}${shortInput(part.name, part.input)}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} else if (ev.type === "system" && ev.subtype === "post_turn_summary" && ev.status_detail) {
|
|
162
|
+
trace(` · ${ev.status_detail}`);
|
|
163
|
+
} else if (ev.type === "result") {
|
|
164
|
+
if (typeof ev.result === "string") resultText = ev.result;
|
|
165
|
+
if (ev.usage && typeof ev.usage === "object") usage = ev.usage;
|
|
166
|
+
if (ev.is_error) trace(" ✗ claude reported an error result");
|
|
167
|
+
}
|
|
97
168
|
}
|
|
98
169
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
170
|
+
child.on("error", (err) => {
|
|
171
|
+
process.stderr.write(`claude spawn failed: ${err.message}\n`);
|
|
172
|
+
process.exit(1);
|
|
173
|
+
});
|
|
102
174
|
|
|
103
|
-
|
|
104
|
-
|
|
175
|
+
child.on("close", (code) => {
|
|
176
|
+
if (code !== 0) {
|
|
177
|
+
process.stderr.write(`claude exited ${code === null ? "(timeout/killed)" : code}\n`);
|
|
178
|
+
process.exit(code === null ? 1 : code);
|
|
179
|
+
}
|
|
180
|
+
if (typeof resultText !== "string") {
|
|
181
|
+
// Fail closed: no result event ⇒ no result.md ⇒ CW records a failed hop.
|
|
182
|
+
process.stderr.write("claude produced no result event — refusing to fabricate a result\n");
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
// Persist the AGENT's final markdown to the worker's result.md (CW is transport).
|
|
186
|
+
fs.writeFileSync(resultPath, resultText, "utf8");
|
|
187
|
+
trace("● done — result captured");
|
|
188
|
+
// The single JSON CW consumes on STDOUT (data channel): model + usage + result.
|
|
189
|
+
process.stdout.write(JSON.stringify({ model, usage, result: resultText }));
|
|
190
|
+
});
|