cc-devflow 4.5.11 → 4.5.12
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/skills/cc-act/CHANGELOG.md +18 -0
- package/.claude/skills/cc-act/PLAYBOOK.md +17 -269
- package/.claude/skills/cc-act/SKILL.md +38 -425
- package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_INDEX_TEMPLATE.md +2 -13
- package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_TEMPLATE.md +1 -9
- package/.claude/skills/cc-act/assets/PR_BRIEF_TEMPLATE.md +21 -177
- package/.claude/skills/cc-act/references/closure-contract.md +12 -63
- package/.claude/skills/cc-act/references/git-commit-guidelines.md +5 -5
- package/.claude/skills/cc-act/scripts/cc-act-common.sh +5 -322
- package/.claude/skills/cc-act/scripts/detect-ship-target.sh +11 -2
- package/.claude/skills/cc-act/scripts/inspect-git-index.sh +58 -0
- package/.claude/skills/cc-act/scripts/render-pr-brief.sh +40 -440
- package/.claude/skills/cc-act/scripts/verify-act-gate.sh +10 -50
- package/.claude/skills/cc-check/CHANGELOG.md +18 -0
- package/.claude/skills/cc-check/PLAYBOOK.md +19 -273
- package/.claude/skills/cc-check/SKILL.md +33 -456
- package/.claude/skills/cc-check/references/review-contract.md +12 -147
- package/.claude/skills/cc-dev/CHANGELOG.md +15 -0
- package/.claude/skills/cc-dev/PLAYBOOK.md +1 -1
- package/.claude/skills/cc-dev/SKILL.md +52 -137
- package/.claude/skills/cc-dev/scripts/resolve-cc-devflow.sh +181 -0
- package/.claude/skills/cc-do/CHANGELOG.md +11 -0
- package/.claude/skills/cc-do/PLAYBOOK.md +19 -113
- package/.claude/skills/cc-do/SKILL.md +39 -245
- package/.claude/skills/cc-do/references/execution-recovery.md +15 -109
- package/.claude/skills/cc-do/scripts/cc-do-common.sh +5 -57
- package/.claude/skills/cc-do/scripts/check-task-status.sh +35 -65
- package/.claude/skills/cc-do/scripts/mark-task-complete.sh +9 -46
- package/.claude/skills/cc-do/scripts/select-ready-tasks.sh +29 -97
- package/.claude/skills/cc-investigate/CHANGELOG.md +16 -0
- package/.claude/skills/cc-investigate/PLAYBOOK.md +20 -180
- package/.claude/skills/cc-investigate/SKILL.md +64 -246
- package/.claude/skills/cc-investigate/assets/TASKS_TEMPLATE.md +48 -98
- package/.claude/skills/cc-investigate/references/investigation-contract.md +14 -218
- package/.claude/skills/cc-next/CHANGELOG.md +6 -0
- package/.claude/skills/cc-next/PLAYBOOK.md +12 -8
- package/.claude/skills/cc-next/SKILL.md +34 -140
- package/.claude/skills/cc-plan/CHANGELOG.md +16 -0
- package/.claude/skills/cc-plan/PLAYBOOK.md +22 -161
- package/.claude/skills/cc-plan/SKILL.md +45 -295
- package/.claude/skills/cc-plan/assets/TASKS_TEMPLATE.md +30 -228
- package/.claude/skills/cc-plan/references/planning-contract.md +24 -161
- package/.claude/skills/cc-plan/scripts/next-change-key.sh +8 -44
- package/.claude/skills/cc-plan/scripts/parse-task-dependencies.js +2 -2
- package/.claude/skills/cc-plan/scripts/validate-scope.sh +1 -1
- package/.claude/skills/cc-pr-land/SKILL.md +14 -114
- package/.claude/skills/cc-pr-review/CHANGELOG.md +4 -0
- package/.claude/skills/cc-pr-review/SKILL.md +20 -103
- package/.claude/skills/cc-review/CHANGELOG.md +17 -0
- package/.claude/skills/cc-review/PLAYBOOK.md +13 -86
- package/.claude/skills/cc-review/SKILL.md +53 -241
- package/.claude/skills/cc-review/references/e2e-and-plugin-verification.md +2 -2
- package/.claude/skills/cc-review/references/implementation-review-branch.md +7 -147
- package/.claude/skills/cc-review/references/plan-review-branch.md +5 -147
- package/.claude/skills/cc-review/references/review-methods.md +10 -218
- package/.claude/skills/cc-review/scripts/collect-review-context.sh +4 -63
- package/.claude/skills/cc-roadmap/PLAYBOOK.md +1 -1
- package/.claude/skills/cc-roadmap/SKILL.md +3 -3
- package/.claude/skills/cc-simplify/CHANGELOG.md +7 -0
- package/.claude/skills/cc-simplify/SKILL.md +26 -21
- package/.claude/skills/cc-spec-init/PLAYBOOK.md +12 -48
- package/.claude/skills/cc-spec-init/SKILL.md +29 -132
- package/.claude/skills/cc-spec-init/references/spec-contract.md +8 -17
- package/CHANGELOG.md +13 -0
- package/bin/cc-devflow-cli.js +20 -260
- package/bin/cc-devflow.js +44 -7
- package/docs/commands/README.md +1 -1
- package/docs/commands/README.zh-CN.md +1 -1
- package/docs/examples/README.md +1 -1
- package/docs/examples/START-HERE.md +14 -15
- package/docs/examples/example-bindings.json +11 -11
- package/docs/examples/full-design-blocked/README.md +4 -6
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/{planning/tasks.md → task.md} +20 -15
- package/docs/examples/local-handoff/README.md +8 -11
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/pr-brief.md +31 -0
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/{planning/tasks.md → task.md} +18 -13
- package/docs/examples/pdca-loop/README.md +6 -9
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/pr-brief.md +9 -11
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/{planning/tasks.md → task.md} +18 -13
- package/docs/examples/scripts/check-example-bindings.sh +11 -62
- package/docs/guides/artifact-contract.md +10 -40
- package/docs/guides/getting-started.md +8 -8
- package/docs/guides/getting-started.zh-CN.md +8 -8
- package/docs/guides/minimize-artifacts.md +16 -130
- package/docs/guides/project-postmortem.md +14 -71
- package/lib/compiler/__tests__/skills-registry.test.js +9 -8
- package/lib/compiler/resource-copier.js +29 -0
- package/lib/skill-runtime/__tests__/archive-change.test.js +2 -2
- package/lib/skill-runtime/__tests__/benchmark-skills.test.js +3 -3
- package/lib/skill-runtime/__tests__/cli-bootstrap.integration.test.js +14 -4
- package/lib/skill-runtime/errors.js +3 -3
- package/lib/skill-runtime/index.js +5 -23
- package/lib/skill-runtime/paths.js +5 -52
- package/lib/skill-runtime/query-registry.js +4 -4
- package/lib/skill-runtime/query.js +89 -201
- package/lib/skill-runtime/store.js +4 -40
- package/lib/skill-runtime/trace.js +2 -2
- package/package.json +2 -5
- package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_PRINCIPLES_TEMPLATE.md +0 -29
- package/.claude/skills/cc-act/assets/RELEASE_NOTE_TEMPLATE.md +0 -54
- package/.claude/skills/cc-act/scripts/generate-status-report.sh +0 -92
- package/.claude/skills/cc-act/scripts/sync-act-docs.sh +0 -355
- package/.claude/skills/cc-check/assets/REPORT_CARD_TEMPLATE.json +0 -234
- package/.claude/skills/cc-check/scripts/render-report-card.js +0 -438
- package/.claude/skills/cc-check/scripts/verify-gate.sh +0 -85
- package/.claude/skills/cc-do/scripts/build-task-context.sh +0 -175
- package/.claude/skills/cc-do/scripts/record-review-decision.sh +0 -88
- package/.claude/skills/cc-do/scripts/recover-workflow.sh +0 -82
- package/.claude/skills/cc-do/scripts/run-problem-analysis.sh +0 -70
- package/.claude/skills/cc-do/scripts/verify-task-gates.sh +0 -109
- package/.claude/skills/cc-do/scripts/write-task-checkpoint.sh +0 -92
- package/.claude/skills/cc-investigate/assets/TASK_MANIFEST_TEMPLATE.json +0 -224
- package/.claude/skills/cc-plan/assets/TASK_MANIFEST_TEMPLATE.json +0 -178
- package/.claude/skills/cc-spec-init/assets/CHANGE_META_TEMPLATE.json +0 -28
- package/.claude/skills/cc-spec-init/scripts/validate-spec-links.sh +0 -45
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/design.md +0 -234
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/task-manifest.json +0 -488
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/review/report-card.json +0 -189
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/resume-index.md +0 -39
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/status.md +0 -29
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/design.md +0 -123
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/task-manifest.json +0 -292
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/review/report-card.json +0 -136
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/status.md +0 -29
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/design.md +0 -124
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/task-manifest.json +0 -292
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/review/report-card.json +0 -136
- package/docs/get-shit-done-strategy-audit.md +0 -518
- package/docs/skill-runtime-migration.md +0 -46
- package/lib/skill-runtime/__tests__/approve.test.js +0 -92
- package/lib/skill-runtime/__tests__/autopilot.test.js +0 -253
- package/lib/skill-runtime/__tests__/benchmark-artifacts.test.js +0 -165
- package/lib/skill-runtime/__tests__/delegation.test.js +0 -97
- package/lib/skill-runtime/__tests__/dispatch.test.js +0 -237
- package/lib/skill-runtime/__tests__/intent.test.js +0 -203
- package/lib/skill-runtime/__tests__/lifecycle.test.js +0 -169
- package/lib/skill-runtime/__tests__/planner.tdd.test.js +0 -331
- package/lib/skill-runtime/__tests__/prepare-pr.test.js +0 -126
- package/lib/skill-runtime/__tests__/query.test.js +0 -860
- package/lib/skill-runtime/__tests__/readiness.test.js +0 -53
- package/lib/skill-runtime/__tests__/release.test.js +0 -85
- package/lib/skill-runtime/__tests__/review-check-integration.test.js +0 -148
- package/lib/skill-runtime/__tests__/review-records.test.js +0 -619
- package/lib/skill-runtime/__tests__/runtime.integration.test.js +0 -351
- package/lib/skill-runtime/__tests__/schemas.test.js +0 -337
- package/lib/skill-runtime/__tests__/task-contract-migrate.test.js +0 -137
- package/lib/skill-runtime/__tests__/task-contract.test.js +0 -874
- package/lib/skill-runtime/__tests__/team-state.test.js +0 -51
- package/lib/skill-runtime/__tests__/verify-artifacts.test.js +0 -203
- package/lib/skill-runtime/__tests__/worker-run.test.js +0 -275
- package/lib/skill-runtime/__tests__/worker.test.js +0 -56
- package/lib/skill-runtime/__tests__/workflow-context-legacy-fallback.test.js +0 -31
- package/lib/skill-runtime/__tests__/workflow-context.test.js +0 -98
- package/lib/skill-runtime/artifacts.js +0 -88
- package/lib/skill-runtime/context-index.js +0 -545
- package/lib/skill-runtime/delegation.js +0 -533
- package/lib/skill-runtime/intent.js +0 -309
- package/lib/skill-runtime/lifecycle.js +0 -294
- package/lib/skill-runtime/operations/CLAUDE.md +0 -19
- package/lib/skill-runtime/operations/approve.js +0 -81
- package/lib/skill-runtime/operations/autopilot-core.js +0 -337
- package/lib/skill-runtime/operations/autopilot-execution.js +0 -307
- package/lib/skill-runtime/operations/autopilot-shared.js +0 -48
- package/lib/skill-runtime/operations/autopilot.js +0 -163
- package/lib/skill-runtime/operations/dispatch.js +0 -416
- package/lib/skill-runtime/operations/init.js +0 -60
- package/lib/skill-runtime/operations/janitor.js +0 -61
- package/lib/skill-runtime/operations/plan.js +0 -59
- package/lib/skill-runtime/operations/prepare-pr.js +0 -25
- package/lib/skill-runtime/operations/release.js +0 -99
- package/lib/skill-runtime/operations/resume.js +0 -126
- package/lib/skill-runtime/operations/review-records.js +0 -265
- package/lib/skill-runtime/operations/snapshot.js +0 -45
- package/lib/skill-runtime/operations/task-contract.js +0 -593
- package/lib/skill-runtime/operations/verify.js +0 -170
- package/lib/skill-runtime/operations/worker-run.js +0 -531
- package/lib/skill-runtime/operations/worker.js +0 -33
- package/lib/skill-runtime/planner.js +0 -539
- package/lib/skill-runtime/readiness.js +0 -84
- package/lib/skill-runtime/review-records.js +0 -123
- package/lib/skill-runtime/review.js +0 -855
- package/lib/skill-runtime/schemas.js +0 -746
- package/lib/skill-runtime/task-contract.js +0 -188
- package/lib/skill-runtime/team-state.js +0 -122
- package/lib/skill-runtime/workflow-context.js +0 -748
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* [INPUT]: 依赖 manifest 与 package scripts,依赖 shell gates(lint/typecheck/test/audit/review)。
|
|
3
|
-
* [OUTPUT]: 生成 report-card.json,给出 quick/strict/review 门禁结论。
|
|
4
|
-
* [POS]: skill runtime 质量门禁入口,供内部验证链路复用。
|
|
5
|
-
* [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const {
|
|
9
|
-
nowIso,
|
|
10
|
-
getPackageScripts,
|
|
11
|
-
readJson,
|
|
12
|
-
writeJson,
|
|
13
|
-
runCommand,
|
|
14
|
-
getTaskManifestPath,
|
|
15
|
-
getRuntimeStatePath,
|
|
16
|
-
getReportCardPath
|
|
17
|
-
} = require('../store');
|
|
18
|
-
const { parseManifest, parseReportCard } = require('../schemas');
|
|
19
|
-
const { runReviewSuite } = require('../review');
|
|
20
|
-
|
|
21
|
-
const path = require('path');
|
|
22
|
-
const fs = require('fs');
|
|
23
|
-
const os = require('os');
|
|
24
|
-
|
|
25
|
-
const REQ_CHECK_DIR = path.join(__dirname, '..', '..', '..', '.claude', 'skills', 'cc-check');
|
|
26
|
-
const RUN_GATES_SCRIPT = path.join(REQ_CHECK_DIR, 'scripts', 'run-quality-gates.sh');
|
|
27
|
-
const RENDER_REPORT_SCRIPT = path.join(REQ_CHECK_DIR, 'scripts', 'render-report-card.js');
|
|
28
|
-
|
|
29
|
-
function shellEscape(value) {
|
|
30
|
-
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async function runGateCollection({ repoRoot, definitions }) {
|
|
34
|
-
if (!definitions || definitions.length === 0) {
|
|
35
|
-
return [];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const args = definitions
|
|
39
|
-
.map((item) => `--gate ${shellEscape(`${item.name}::${item.command}`)}`)
|
|
40
|
-
.join(' ');
|
|
41
|
-
const result = await runCommand(`bash ${shellEscape(RUN_GATES_SCRIPT)} ${args}`, {
|
|
42
|
-
cwd: repoRoot,
|
|
43
|
-
timeoutMs: 20 * 60 * 1000
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
if (result.code !== 0) {
|
|
47
|
-
throw new Error(result.stderr || result.stdout || 'run-quality-gates.sh failed');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return JSON.parse(result.stdout || '[]');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async function renderReportCard({ repoRoot, changeId, manifestPath, quickGates, strictGates, review }) {
|
|
54
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-cc-check-'));
|
|
55
|
-
const quickPath = path.join(tempDir, 'quick-gates.json');
|
|
56
|
-
const strictPath = path.join(tempDir, 'strict-gates.json');
|
|
57
|
-
const reviewPath = path.join(tempDir, 'review.json');
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
fs.writeFileSync(quickPath, `${JSON.stringify(quickGates, null, 2)}\n`);
|
|
61
|
-
fs.writeFileSync(strictPath, `${JSON.stringify(strictGates, null, 2)}\n`);
|
|
62
|
-
fs.writeFileSync(reviewPath, `${JSON.stringify(review, null, 2)}\n`);
|
|
63
|
-
|
|
64
|
-
const command = [
|
|
65
|
-
'node',
|
|
66
|
-
shellEscape(RENDER_REPORT_SCRIPT),
|
|
67
|
-
'--change-id',
|
|
68
|
-
shellEscape(changeId),
|
|
69
|
-
'--manifest',
|
|
70
|
-
shellEscape(manifestPath),
|
|
71
|
-
'--quick',
|
|
72
|
-
shellEscape(quickPath),
|
|
73
|
-
'--strict',
|
|
74
|
-
shellEscape(strictPath),
|
|
75
|
-
'--review',
|
|
76
|
-
shellEscape(reviewPath),
|
|
77
|
-
'--timestamp',
|
|
78
|
-
shellEscape(nowIso())
|
|
79
|
-
].join(' ');
|
|
80
|
-
|
|
81
|
-
const result = await runCommand(command, { cwd: repoRoot, timeoutMs: 20 * 60 * 1000 });
|
|
82
|
-
if (result.code !== 0) {
|
|
83
|
-
throw new Error(result.stderr || result.stdout || 'render-report-card.js failed');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return JSON.parse(result.stdout);
|
|
87
|
-
} finally {
|
|
88
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async function runVerify({ repoRoot, changeId, strict = false, skipReview = false }) {
|
|
93
|
-
const manifestPath = getTaskManifestPath(repoRoot, changeId);
|
|
94
|
-
const manifest = parseManifest(await readJson(manifestPath));
|
|
95
|
-
const scripts = await getPackageScripts(repoRoot);
|
|
96
|
-
|
|
97
|
-
const quickGates = await runGateCollection({
|
|
98
|
-
repoRoot,
|
|
99
|
-
definitions: ['lint', 'typecheck', 'test'].map((name) => ({
|
|
100
|
-
name,
|
|
101
|
-
command: scripts[name]
|
|
102
|
-
? `npm run ${name}`
|
|
103
|
-
: `printf '__CC_DEVFLOW_SKIP__ Script ${name} is not defined\n'`
|
|
104
|
-
}))
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
const strictGates = strict
|
|
108
|
-
? await runGateCollection({
|
|
109
|
-
repoRoot,
|
|
110
|
-
definitions: [
|
|
111
|
-
{
|
|
112
|
-
name: 'test:integration',
|
|
113
|
-
command: scripts['test:integration']
|
|
114
|
-
? 'npm run test:integration'
|
|
115
|
-
: "printf '__CC_DEVFLOW_SKIP__ Script test:integration is not defined\n'"
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
name: 'audit',
|
|
119
|
-
command: 'npm audit --audit-level=high'
|
|
120
|
-
}
|
|
121
|
-
]
|
|
122
|
-
})
|
|
123
|
-
: [];
|
|
124
|
-
|
|
125
|
-
const review = await runReviewSuite({
|
|
126
|
-
repoRoot,
|
|
127
|
-
changeId,
|
|
128
|
-
manifest,
|
|
129
|
-
strict,
|
|
130
|
-
skipReview
|
|
131
|
-
});
|
|
132
|
-
const report = parseReportCard(
|
|
133
|
-
await renderReportCard({
|
|
134
|
-
repoRoot,
|
|
135
|
-
changeId,
|
|
136
|
-
manifestPath,
|
|
137
|
-
quickGates,
|
|
138
|
-
strictGates,
|
|
139
|
-
review
|
|
140
|
-
})
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
const outputPath = getReportCardPath(repoRoot, changeId);
|
|
144
|
-
await writeJson(outputPath, report);
|
|
145
|
-
|
|
146
|
-
// Update change-state.json with verifiedAt timestamp if passed
|
|
147
|
-
if (report.verdict === 'pass') {
|
|
148
|
-
const statePath = getRuntimeStatePath(repoRoot, changeId);
|
|
149
|
-
const stateExists = require('fs').existsSync(statePath);
|
|
150
|
-
if (stateExists) {
|
|
151
|
-
const state = await readJson(statePath);
|
|
152
|
-
state.status = 'verified';
|
|
153
|
-
state.verifiedAt = nowIso();
|
|
154
|
-
state.updatedAt = nowIso();
|
|
155
|
-
await writeJson(statePath, state);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return {
|
|
160
|
-
changeId,
|
|
161
|
-
outputPath,
|
|
162
|
-
overall: report.overall,
|
|
163
|
-
verdict: report.verdict || (report.overall === 'pass' ? 'pass' : 'fail'),
|
|
164
|
-
blockingFindings: report.blockingFindings
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
module.exports = {
|
|
169
|
-
runVerify
|
|
170
|
-
};
|
|
@@ -1,531 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* [INPUT]: 依赖 delegation/store 提供的 handoff、workspace 与 shell 执行能力,接收 changeId/workerId/taskId/command/provider 以运行本地 worker。
|
|
3
|
-
* [OUTPUT]: 对外提供 worker-run 执行结果,并写入 session.log、state、journal、message bus、目标 assignment 状态与 provider session prompt。
|
|
4
|
-
* [POS]: skill runtime 的本地 worker 执行壳,把 Markdown handoff bundle 接到真实本地命令执行,并以最薄 provider launcher 连接 codex/claude。
|
|
5
|
-
* [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const {
|
|
10
|
-
buildWorkerHandoff,
|
|
11
|
-
appendWorkerJournal,
|
|
12
|
-
appendMessageBus,
|
|
13
|
-
updateWorkerState,
|
|
14
|
-
updateAssignmentStatus,
|
|
15
|
-
ensureWorkerWorkspace,
|
|
16
|
-
getWorkerSessionLogPath
|
|
17
|
-
} = require('../delegation');
|
|
18
|
-
const {
|
|
19
|
-
readText,
|
|
20
|
-
readJson,
|
|
21
|
-
writeText,
|
|
22
|
-
nowIso,
|
|
23
|
-
writeJson,
|
|
24
|
-
appendJsonl,
|
|
25
|
-
runCommand,
|
|
26
|
-
getEventsPath,
|
|
27
|
-
getTaskManifestPath,
|
|
28
|
-
getRuntimeStatePath
|
|
29
|
-
} = require('../store');
|
|
30
|
-
const { parseManifest, parseRuntimeState } = require('../schemas');
|
|
31
|
-
const { applyManifestExecutionState } = require('../planner');
|
|
32
|
-
const { syncIntentMemory } = require('../intent');
|
|
33
|
-
const { namedError } = require('../errors');
|
|
34
|
-
|
|
35
|
-
function quoteShellArg(value) {
|
|
36
|
-
return `'${String(value).replace(/'/g, `'\"'\"'`)}'`;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function summarizeOutput(result) {
|
|
40
|
-
const combined = [result.stdout, result.stderr]
|
|
41
|
-
.filter(Boolean)
|
|
42
|
-
.join('\n')
|
|
43
|
-
.replace(/\s+/g, ' ')
|
|
44
|
-
.trim();
|
|
45
|
-
|
|
46
|
-
if (!combined) {
|
|
47
|
-
return result.code === 0 ? 'command finished without output' : 'command failed without output';
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return combined.slice(0, 240);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function quoteCommandArg(value) {
|
|
54
|
-
return `'${String(value).replace(/'/g, `'\"'\"'`)}'`;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async function appendSessionLog(logPath, lines) {
|
|
58
|
-
const previous = await readText(logPath, '');
|
|
59
|
-
const chunk = `${lines.join('\n')}\n`;
|
|
60
|
-
await writeText(logPath, `${previous}${chunk}`);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function writeWorkerEvent(repoRoot, changeId, taskId, event, debug = false) {
|
|
64
|
-
if (!debug && !String(event.type || '').includes('failed')) {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
await appendJsonl(getEventsPath(repoRoot, changeId, taskId), event);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async function updateAssignments(repoRoot, changeId, handoff, fields) {
|
|
71
|
-
for (const taskId of handoff.taskIds) {
|
|
72
|
-
await updateAssignmentStatus(repoRoot, changeId, taskId, fields(taskId));
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function pickTaskId(handoff, requestedTaskId) {
|
|
77
|
-
if (!requestedTaskId) {
|
|
78
|
-
return handoff.taskIds[0];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (!handoff.taskIds.includes(requestedTaskId)) {
|
|
82
|
-
throw new Error(`Worker ${handoff.workerId} is not assigned to task ${requestedTaskId}`);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return requestedTaskId;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function getProviderPromptPath(handoff, taskId) {
|
|
89
|
-
return path.join(handoff.runtimeDir, `session-${taskId}.md`);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function getProviderLastMessagePath(handoff, taskId) {
|
|
93
|
-
return path.join(handoff.runtimeDir, `session-${taskId}.last-message.md`);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function getProviderTranscriptPath(handoff, taskId, provider) {
|
|
97
|
-
return path.join(handoff.runtimeDir, `session-${taskId}.${provider}.jsonl`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function formatList(items, empty = '- None') {
|
|
101
|
-
if (!items || items.length === 0) {
|
|
102
|
-
return empty;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return items.map((item) => `- ${item}`).join('\n');
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async function buildTaskBrief(repoRoot, changeId, taskId, planVersion) {
|
|
109
|
-
const manifest = parseManifest(await readJson(getTaskManifestPath(repoRoot, changeId)));
|
|
110
|
-
const task = manifest.tasks.find((item) => item.id === taskId);
|
|
111
|
-
|
|
112
|
-
if (!task) {
|
|
113
|
-
throw new Error(`Task ${taskId} not found in manifest`);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return [
|
|
117
|
-
`# Task Brief: ${task.id}`,
|
|
118
|
-
'',
|
|
119
|
-
`- Plan Version: ${planVersion || manifest.metadata?.planVersion || 1}`,
|
|
120
|
-
`- Type: \`${task.type}\``,
|
|
121
|
-
`- Title: ${task.title}`,
|
|
122
|
-
'',
|
|
123
|
-
'## Dependencies',
|
|
124
|
-
'',
|
|
125
|
-
formatList(task.dependsOn.map((item) => `\`${item}\``), '- None'),
|
|
126
|
-
'',
|
|
127
|
-
'## Touched Files',
|
|
128
|
-
'',
|
|
129
|
-
formatList(task.touches.map((item) => `\`${item}\``), '- None'),
|
|
130
|
-
'',
|
|
131
|
-
'## Commands',
|
|
132
|
-
'',
|
|
133
|
-
formatList([...task.run, ...task.checks].map((item) => `\`${item}\``))
|
|
134
|
-
].join('\n');
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async function assertWorkerPlanFresh(repoRoot, changeId, handoff) {
|
|
138
|
-
const manifestPath = getTaskManifestPath(repoRoot, changeId);
|
|
139
|
-
const manifest = parseManifest(await readJson(manifestPath));
|
|
140
|
-
const currentPlanVersion = manifest.metadata?.planVersion || 1;
|
|
141
|
-
|
|
142
|
-
if (currentPlanVersion === handoff.planVersion) {
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
throw namedError(
|
|
147
|
-
'StalePlanVersionError',
|
|
148
|
-
`Worker ${handoff.workerId} was assigned to planVersion ${handoff.planVersion}, but current planVersion is ${currentPlanVersion}`,
|
|
149
|
-
{
|
|
150
|
-
artifactRefs: [
|
|
151
|
-
`devflow/changes/<change>/planning/task-manifest.json`,
|
|
152
|
-
handoff.assignmentPath
|
|
153
|
-
],
|
|
154
|
-
rescueAction: 'rerun delegation sync for current planVersion before worker-run',
|
|
155
|
-
details: {
|
|
156
|
-
workerId: handoff.workerId,
|
|
157
|
-
assignedPlanVersion: handoff.planVersion,
|
|
158
|
-
currentPlanVersion,
|
|
159
|
-
manifestPath
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async function buildProviderPrompt(repoRootOrHandoff, handoffOrTaskId, maybeTaskId) {
|
|
166
|
-
const repoRoot = typeof repoRootOrHandoff === 'string' ? repoRootOrHandoff : process.cwd();
|
|
167
|
-
const handoff = typeof repoRootOrHandoff === 'string' ? handoffOrTaskId : repoRootOrHandoff;
|
|
168
|
-
const taskId = typeof repoRootOrHandoff === 'string' ? maybeTaskId : handoffOrTaskId;
|
|
169
|
-
const [assignment, taskBrief] = await Promise.all([
|
|
170
|
-
readText(handoff.assignmentPath, ''),
|
|
171
|
-
buildTaskBrief(repoRoot, handoff.changeId, taskId, handoff.planVersion)
|
|
172
|
-
]);
|
|
173
|
-
|
|
174
|
-
return [
|
|
175
|
-
`# Worker Session`,
|
|
176
|
-
'',
|
|
177
|
-
`- Change: \`${handoff.changeId}\``,
|
|
178
|
-
`- Worker: \`${handoff.workerId}\``,
|
|
179
|
-
`- Role: \`${handoff.role}\``,
|
|
180
|
-
`- Task: \`${taskId}\``,
|
|
181
|
-
`- Plan Version: ${handoff.planVersion}`,
|
|
182
|
-
'',
|
|
183
|
-
'## Operating Rules',
|
|
184
|
-
'',
|
|
185
|
-
'- 先阅读下面的 assignment 和 task brief,再执行当前任务。',
|
|
186
|
-
'- 仅处理当前 task,不要擅自结算同 worker 的其他任务。',
|
|
187
|
-
'- 所有关键结论先落到仓库工件,再结束会话。',
|
|
188
|
-
'- 如果发现 plan_version 漂移,停止并返回阻塞说明。',
|
|
189
|
-
'',
|
|
190
|
-
'## Worker Assignment',
|
|
191
|
-
'',
|
|
192
|
-
assignment.trim(),
|
|
193
|
-
'',
|
|
194
|
-
'## Selected Task Brief',
|
|
195
|
-
'',
|
|
196
|
-
taskBrief.trim(),
|
|
197
|
-
''
|
|
198
|
-
].join('\n');
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function buildProviderCommand({
|
|
202
|
-
provider,
|
|
203
|
-
providerPromptPath,
|
|
204
|
-
providerLastMessagePath,
|
|
205
|
-
providerTranscriptPath,
|
|
206
|
-
workspace,
|
|
207
|
-
providerArgs
|
|
208
|
-
}) {
|
|
209
|
-
const extraArgs = providerArgs ? ` ${providerArgs.trim()}` : '';
|
|
210
|
-
const stdinPromptArg = provider === 'codex' ? ' -' : '';
|
|
211
|
-
|
|
212
|
-
if (provider === 'codex') {
|
|
213
|
-
return `cat ${quoteCommandArg(providerPromptPath)} | codex exec --json --dangerously-bypass-approvals-and-sandbox -C ${quoteCommandArg(workspace)} -o ${quoteCommandArg(providerLastMessagePath)}${stdinPromptArg}${extraArgs} | tee ${quoteCommandArg(providerTranscriptPath)}`;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (provider === 'claude') {
|
|
217
|
-
return `cat ${quoteCommandArg(providerPromptPath)} | claude -p --dangerously-skip-permissions --add-dir ${quoteCommandArg(workspace)}${extraArgs}`;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
throw new Error(`Unsupported provider: ${provider}`);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function parseCodexTranscript(stdout = '') {
|
|
224
|
-
const lines = stdout
|
|
225
|
-
.split('\n')
|
|
226
|
-
.map((line) => line.trim())
|
|
227
|
-
.filter(Boolean);
|
|
228
|
-
|
|
229
|
-
const summary = {
|
|
230
|
-
threadId: null,
|
|
231
|
-
agentMessages: [],
|
|
232
|
-
commandExecutions: [],
|
|
233
|
-
fileChanges: [],
|
|
234
|
-
toolCalls: [],
|
|
235
|
-
errors: []
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
for (const line of lines) {
|
|
239
|
-
let event;
|
|
240
|
-
try {
|
|
241
|
-
event = JSON.parse(line);
|
|
242
|
-
} catch {
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (event.type === 'thread.started' && event.thread_id) {
|
|
247
|
-
summary.threadId = event.thread_id;
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (event.type !== 'item.completed' || !event.item) {
|
|
252
|
-
continue;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (event.item.type === 'agent_message' && event.item.text) {
|
|
256
|
-
summary.agentMessages.push(event.item.text);
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if (event.item.type === 'command_execution') {
|
|
261
|
-
summary.commandExecutions.push({
|
|
262
|
-
command: event.item.command,
|
|
263
|
-
status: event.item.status,
|
|
264
|
-
exitCode: event.item.exit_code
|
|
265
|
-
});
|
|
266
|
-
continue;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (event.item.type === 'file_change') {
|
|
270
|
-
summary.fileChanges.push(...(event.item.changes || []).map((change) => `${change.kind}:${change.path}`));
|
|
271
|
-
continue;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (event.item.type === 'mcp_tool_call') {
|
|
275
|
-
summary.toolCalls.push(`${event.item.server}:${event.item.tool}:${event.item.status}`);
|
|
276
|
-
continue;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
if (event.item.type === 'error' && event.item.message) {
|
|
280
|
-
summary.errors.push(event.item.message);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
return summary;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
async function updateManifestTaskState(repoRoot, changeId, taskId, fields) {
|
|
288
|
-
const manifestPath = getTaskManifestPath(repoRoot, changeId);
|
|
289
|
-
const manifest = parseManifest(await readJson(manifestPath));
|
|
290
|
-
const task = manifest.tasks.find((item) => item.id === taskId);
|
|
291
|
-
|
|
292
|
-
if (!task) {
|
|
293
|
-
throw new Error(`Task ${taskId} not found in manifest`);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (fields.status) {
|
|
297
|
-
task.status = fields.status;
|
|
298
|
-
}
|
|
299
|
-
if (Number.isInteger(fields.attempts)) {
|
|
300
|
-
task.attempts = fields.attempts;
|
|
301
|
-
}
|
|
302
|
-
if (Object.prototype.hasOwnProperty.call(fields, 'lastError')) {
|
|
303
|
-
task.lastError = fields.lastError || undefined;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
applyManifestExecutionState(manifest, nowIso());
|
|
307
|
-
await writeJson(manifestPath, manifest);
|
|
308
|
-
return manifest;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
async function markRuntimeInProgress(repoRoot, changeId) {
|
|
312
|
-
const statePath = getRuntimeStatePath(repoRoot, changeId);
|
|
313
|
-
const state = await readJson(statePath, null);
|
|
314
|
-
if (!state) {
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const parsed = parseRuntimeState(state);
|
|
319
|
-
if (parsed.status === 'planned' || parsed.status === 'initialized') {
|
|
320
|
-
parsed.status = 'in_progress';
|
|
321
|
-
}
|
|
322
|
-
parsed.updatedAt = nowIso();
|
|
323
|
-
await writeJson(statePath, parsed);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
async function runWorkerCommand({
|
|
327
|
-
repoRoot,
|
|
328
|
-
changeId,
|
|
329
|
-
workerId,
|
|
330
|
-
taskId,
|
|
331
|
-
command,
|
|
332
|
-
provider,
|
|
333
|
-
providerArgs,
|
|
334
|
-
debug = false,
|
|
335
|
-
timeoutMs = 15 * 60 * 1000
|
|
336
|
-
}) {
|
|
337
|
-
if (!command && !provider) {
|
|
338
|
-
throw new Error('Missing execution mode: provide --command "<local agent command>" or --provider <codex|claude>');
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (command && provider) {
|
|
342
|
-
throw new Error('Use either --command or --provider, not both');
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
const handoff = await buildWorkerHandoff(repoRoot, changeId, workerId);
|
|
346
|
-
await assertWorkerPlanFresh(repoRoot, changeId, handoff);
|
|
347
|
-
const primaryTaskId = pickTaskId(handoff, taskId);
|
|
348
|
-
const assignment = {
|
|
349
|
-
workerId,
|
|
350
|
-
role: handoff.role
|
|
351
|
-
};
|
|
352
|
-
const workspace = await ensureWorkerWorkspace(repoRoot, changeId, assignment);
|
|
353
|
-
const sessionId = `${workerId}-${Date.now()}`;
|
|
354
|
-
const startedAt = nowIso();
|
|
355
|
-
const sessionLogPath = handoff.sessionLogPath || getWorkerSessionLogPath(repoRoot, changeId, workerId);
|
|
356
|
-
const providerPromptPath = getProviderPromptPath(handoff, primaryTaskId);
|
|
357
|
-
const providerLastMessagePath = getProviderLastMessagePath(handoff, primaryTaskId);
|
|
358
|
-
const providerTranscriptPath = provider ? getProviderTranscriptPath(handoff, primaryTaskId, provider) : null;
|
|
359
|
-
await markRuntimeInProgress(repoRoot, changeId);
|
|
360
|
-
const providerPrompt = provider ? await buildProviderPrompt(repoRoot, handoff, primaryTaskId) : null;
|
|
361
|
-
if (providerPrompt) {
|
|
362
|
-
await writeText(providerPromptPath, `${providerPrompt}\n`);
|
|
363
|
-
}
|
|
364
|
-
const resolvedCommand = command || buildProviderCommand({
|
|
365
|
-
provider,
|
|
366
|
-
providerPromptPath,
|
|
367
|
-
providerLastMessagePath,
|
|
368
|
-
providerTranscriptPath,
|
|
369
|
-
workspace: workspace.workspacePath,
|
|
370
|
-
providerArgs
|
|
371
|
-
});
|
|
372
|
-
const wrappedCommand = [
|
|
373
|
-
`export CC_DEVFLOW_CHANGE_ID=${quoteShellArg(changeId)}`,
|
|
374
|
-
`export CC_DEVFLOW_WORKER_ID=${quoteShellArg(workerId)}`,
|
|
375
|
-
`export CC_DEVFLOW_WORKER_ROLE=${quoteShellArg(handoff.role)}`,
|
|
376
|
-
`export CC_DEVFLOW_TASK_ID=${quoteShellArg(primaryTaskId)}`,
|
|
377
|
-
`export CC_DEVFLOW_WORKER_STATE=${quoteShellArg(handoff.statePath)}`,
|
|
378
|
-
`export CC_DEVFLOW_WORKER_JOURNAL=${quoteShellArg(handoff.journalPath)}`,
|
|
379
|
-
`export CC_DEVFLOW_WORKER_SESSION_LOG=${quoteShellArg(sessionLogPath)}`,
|
|
380
|
-
providerPrompt ? `export CC_DEVFLOW_PROVIDER_PROMPT=${quoteShellArg(providerPromptPath)}` : '',
|
|
381
|
-
providerPrompt ? `export CC_DEVFLOW_PROVIDER_LAST_MESSAGE=${quoteShellArg(providerLastMessagePath)}` : '',
|
|
382
|
-
providerTranscriptPath ? `export CC_DEVFLOW_PROVIDER_TRANSCRIPT=${quoteShellArg(providerTranscriptPath)}` : '',
|
|
383
|
-
provider ? `export CC_DEVFLOW_PROVIDER=${quoteShellArg(provider)}` : '',
|
|
384
|
-
resolvedCommand
|
|
385
|
-
].join('\n');
|
|
386
|
-
|
|
387
|
-
await updateWorkerState(repoRoot, changeId, workerId, {
|
|
388
|
-
role: handoff.role,
|
|
389
|
-
planVersion: handoff.planVersion,
|
|
390
|
-
status: 'running',
|
|
391
|
-
currentTask: primaryTaskId,
|
|
392
|
-
branch: workspace.mode === 'worktree' ? 'detached-worktree' : 'controller',
|
|
393
|
-
workspace: workspace.workspacePath
|
|
394
|
-
});
|
|
395
|
-
await updateManifestTaskState(repoRoot, changeId, primaryTaskId, {
|
|
396
|
-
status: 'running',
|
|
397
|
-
attempts: 1,
|
|
398
|
-
lastError: undefined
|
|
399
|
-
});
|
|
400
|
-
await updateAssignments(repoRoot, changeId, handoff, (taskId) => ({
|
|
401
|
-
status: taskId === primaryTaskId ? 'running' : undefined,
|
|
402
|
-
workspace: taskId === primaryTaskId ? workspace.workspacePath : undefined,
|
|
403
|
-
sessionId: taskId === primaryTaskId ? sessionId : undefined,
|
|
404
|
-
summary: taskId === primaryTaskId
|
|
405
|
-
? `worker ${workerId} started ${provider || 'custom'} execution`
|
|
406
|
-
: undefined
|
|
407
|
-
}));
|
|
408
|
-
await appendWorkerJournal(
|
|
409
|
-
repoRoot,
|
|
410
|
-
changeId,
|
|
411
|
-
workerId,
|
|
412
|
-
`${startedAt} started ${sessionId} in ${workspace.mode} workspace for ${primaryTaskId} via ${provider || 'custom-command'}`
|
|
413
|
-
);
|
|
414
|
-
await appendMessageBus(
|
|
415
|
-
repoRoot,
|
|
416
|
-
changeId,
|
|
417
|
-
`${workerId} started ${primaryTaskId} via ${provider || 'custom-command'} in ${workspace.mode} workspace`
|
|
418
|
-
);
|
|
419
|
-
await writeWorkerEvent(repoRoot, changeId, primaryTaskId, {
|
|
420
|
-
type: 'worker_run_started',
|
|
421
|
-
changeId,
|
|
422
|
-
taskId: primaryTaskId,
|
|
423
|
-
sessionId,
|
|
424
|
-
provider: provider || 'custom',
|
|
425
|
-
workerId,
|
|
426
|
-
timestamp: startedAt
|
|
427
|
-
}, debug);
|
|
428
|
-
await appendSessionLog(sessionLogPath, [
|
|
429
|
-
`# Session ${sessionId}`,
|
|
430
|
-
`started_at: ${startedAt}`,
|
|
431
|
-
`worker: ${workerId}`,
|
|
432
|
-
`role: ${handoff.role}`,
|
|
433
|
-
`workspace: ${workspace.workspacePath}`,
|
|
434
|
-
`workspace_mode: ${workspace.mode}`,
|
|
435
|
-
`workspace_reason: ${workspace.reason}`,
|
|
436
|
-
`provider: ${provider || 'custom'}`,
|
|
437
|
-
providerPrompt ? `provider_prompt: ${providerPromptPath}` : '',
|
|
438
|
-
`command: ${resolvedCommand}`,
|
|
439
|
-
'',
|
|
440
|
-
'## output'
|
|
441
|
-
].filter(Boolean));
|
|
442
|
-
|
|
443
|
-
const result = await runCommand(wrappedCommand, {
|
|
444
|
-
cwd: workspace.cwd,
|
|
445
|
-
timeoutMs
|
|
446
|
-
});
|
|
447
|
-
const finishedAt = nowIso();
|
|
448
|
-
const finalStatus = result.code === 0 ? 'completed' : 'failed';
|
|
449
|
-
const summary = summarizeOutput(result);
|
|
450
|
-
const codexTranscript = provider === 'codex' ? parseCodexTranscript(result.stdout) : null;
|
|
451
|
-
|
|
452
|
-
await appendSessionLog(sessionLogPath, [
|
|
453
|
-
result.stdout ? result.stdout.trimEnd() : '',
|
|
454
|
-
result.stderr ? `\n## stderr\n${result.stderr.trimEnd()}` : '',
|
|
455
|
-
'',
|
|
456
|
-
`exit_code: ${result.code}`,
|
|
457
|
-
`finished_at: ${finishedAt}`,
|
|
458
|
-
`duration_ms: ${result.durationMs}`,
|
|
459
|
-
`timed_out: ${result.killedByTimeout ? 'true' : 'false'}`,
|
|
460
|
-
''
|
|
461
|
-
].filter(Boolean));
|
|
462
|
-
|
|
463
|
-
await updateManifestTaskState(repoRoot, changeId, primaryTaskId, {
|
|
464
|
-
status: result.code === 0 ? 'passed' : 'failed',
|
|
465
|
-
attempts: 1,
|
|
466
|
-
lastError: result.code === 0 ? undefined : (result.stderr || result.stdout || '').trim()
|
|
467
|
-
});
|
|
468
|
-
await writeWorkerEvent(repoRoot, changeId, primaryTaskId, {
|
|
469
|
-
type: result.code === 0 ? 'worker_run_passed' : 'worker_run_failed',
|
|
470
|
-
changeId,
|
|
471
|
-
taskId: primaryTaskId,
|
|
472
|
-
sessionId,
|
|
473
|
-
provider: provider || 'custom',
|
|
474
|
-
workerId,
|
|
475
|
-
timestamp: finishedAt,
|
|
476
|
-
error: result.code === 0 ? undefined : (result.stderr || result.stdout || '').trim()
|
|
477
|
-
}, debug);
|
|
478
|
-
await syncIntentMemory(repoRoot, changeId, {
|
|
479
|
-
event: result.code === 0 ? 'worker_run_completed' : 'worker_run_failed',
|
|
480
|
-
reason: `${workerId} ${result.code === 0 ? 'completed' : 'failed'} ${primaryTaskId} via ${provider || 'custom-command'}`
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
await updateWorkerState(repoRoot, changeId, workerId, {
|
|
484
|
-
role: handoff.role,
|
|
485
|
-
planVersion: handoff.planVersion,
|
|
486
|
-
status: finalStatus,
|
|
487
|
-
currentTask: result.code === 0 ? 'none' : primaryTaskId,
|
|
488
|
-
branch: workspace.mode === 'worktree' ? 'detached-worktree' : 'controller',
|
|
489
|
-
workspace: workspace.workspacePath
|
|
490
|
-
});
|
|
491
|
-
await updateAssignments(repoRoot, changeId, handoff, (taskId) => ({
|
|
492
|
-
status: taskId === primaryTaskId ? finalStatus : undefined,
|
|
493
|
-
workspace: taskId === primaryTaskId ? workspace.workspacePath : undefined,
|
|
494
|
-
sessionId: taskId === primaryTaskId ? sessionId : undefined,
|
|
495
|
-
summary: taskId === primaryTaskId ? `${workerId} ${finalStatus}: ${summary}` : undefined
|
|
496
|
-
}));
|
|
497
|
-
await appendWorkerJournal(
|
|
498
|
-
repoRoot,
|
|
499
|
-
changeId,
|
|
500
|
-
workerId,
|
|
501
|
-
`${finishedAt} ${sessionId} ${finalStatus} (exit ${result.code})`
|
|
502
|
-
);
|
|
503
|
-
await appendMessageBus(
|
|
504
|
-
repoRoot,
|
|
505
|
-
changeId,
|
|
506
|
-
`${workerId} ${finalStatus} ${primaryTaskId}: ${summary}`
|
|
507
|
-
);
|
|
508
|
-
|
|
509
|
-
return {
|
|
510
|
-
changeId,
|
|
511
|
-
workerId,
|
|
512
|
-
role: handoff.role,
|
|
513
|
-
sessionId,
|
|
514
|
-
status: finalStatus,
|
|
515
|
-
taskId: primaryTaskId,
|
|
516
|
-
workspace,
|
|
517
|
-
provider: provider || null,
|
|
518
|
-
providerPromptPath: providerPrompt ? providerPromptPath : null,
|
|
519
|
-
providerLastMessagePath: providerPrompt ? providerLastMessagePath : null,
|
|
520
|
-
providerTranscriptPath,
|
|
521
|
-
command: resolvedCommand,
|
|
522
|
-
sessionLogPath,
|
|
523
|
-
result
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
module.exports = {
|
|
528
|
-
buildProviderCommand,
|
|
529
|
-
buildProviderPrompt,
|
|
530
|
-
runWorkerCommand
|
|
531
|
-
};
|