cc-devflow 4.5.10 → 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 +23 -0
- package/.claude/skills/cc-act/PLAYBOOK.md +17 -269
- package/.claude/skills/cc-act/SKILL.md +38 -418
- 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 +24 -0
- package/.claude/skills/cc-check/PLAYBOOK.md +19 -273
- package/.claude/skills/cc-check/SKILL.md +33 -454
- package/.claude/skills/cc-check/references/review-contract.md +12 -147
- package/.claude/skills/cc-dev/CHANGELOG.md +20 -0
- package/.claude/skills/cc-dev/PLAYBOOK.md +1 -1
- package/.claude/skills/cc-dev/SKILL.md +52 -130
- package/.claude/skills/cc-dev/scripts/resolve-cc-devflow.sh +181 -0
- package/.claude/skills/cc-do/CHANGELOG.md +17 -0
- package/.claude/skills/cc-do/PLAYBOOK.md +19 -113
- package/.claude/skills/cc-do/SKILL.md +39 -236
- 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 +23 -0
- package/.claude/skills/cc-investigate/PLAYBOOK.md +20 -180
- package/.claude/skills/cc-investigate/SKILL.md +65 -513
- package/.claude/skills/cc-investigate/assets/TASKS_TEMPLATE.md +48 -95
- package/.claude/skills/cc-investigate/references/investigation-contract.md +14 -217
- 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 +29 -0
- package/.claude/skills/cc-plan/PLAYBOOK.md +22 -161
- package/.claude/skills/cc-plan/SKILL.md +47 -640
- package/.claude/skills/cc-plan/assets/TASKS_TEMPLATE.md +30 -225
- package/.claude/skills/cc-plan/references/planning-contract.md +24 -160
- 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 +27 -0
- package/README.md +5 -3
- package/README.zh-CN.md +5 -3
- 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 -14
- 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 -36
- package/docs/guides/getting-started.md +8 -7
- package/docs/guides/getting-started.zh-CN.md +8 -7
- package/docs/guides/minimize-artifacts.md +16 -116
- 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 +109 -0
- 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 +5 -7
- 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 -225
- package/.claude/skills/cc-plan/assets/TASK_MANIFEST_TEMPLATE.json +0 -179
- 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 -783
- 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 -524
- 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 -187
- package/lib/skill-runtime/team-state.js +0 -122
- package/lib/skill-runtime/workflow-context.js +0 -748
|
@@ -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
|
-
};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* [INPUT]: 依赖 delegation 层提供 worker handoff 与 journal/state 工件,接收 changeId/workerId 以准备本地 worker 会话。
|
|
3
|
-
* [OUTPUT]: 返回 worker launch/prompt/state/assignment 路径,并写入 handoff 准备日志。
|
|
4
|
-
* [POS]: skill runtime 的 worker 会话入口,为未来本地 subagent/worker shell 对接提供稳定接口。
|
|
5
|
-
* [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const {
|
|
9
|
-
buildWorkerHandoff,
|
|
10
|
-
appendWorkerJournal,
|
|
11
|
-
appendMessageBus,
|
|
12
|
-
updateWorkerState
|
|
13
|
-
} = require('../delegation');
|
|
14
|
-
|
|
15
|
-
async function runWorkerSession({ repoRoot, changeId, workerId }) {
|
|
16
|
-
const handoff = await buildWorkerHandoff(repoRoot, changeId, workerId);
|
|
17
|
-
|
|
18
|
-
await updateWorkerState(repoRoot, changeId, workerId, {
|
|
19
|
-
role: handoff.role,
|
|
20
|
-
planVersion: handoff.planVersion,
|
|
21
|
-
status: 'handoff_ready',
|
|
22
|
-
currentTask: handoff.currentTask || 'assigned',
|
|
23
|
-
workspace: 'pending'
|
|
24
|
-
});
|
|
25
|
-
await appendWorkerJournal(repoRoot, changeId, workerId, 'handoff prepared for local worker session');
|
|
26
|
-
await appendMessageBus(repoRoot, changeId, `${workerId} handoff prepared`);
|
|
27
|
-
|
|
28
|
-
return handoff;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
module.exports = {
|
|
32
|
-
runWorkerSession
|
|
33
|
-
};
|