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,533 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* [INPUT]: 依赖 manifest 与 canonical devflow 路径,依赖 store 的文件读写能力,接收 repoRoot/changeId 以生成本地委派骨架。
|
|
3
|
-
* [OUTPUT]: 对外提供 workspace scratch runtime 与 team-state 真相源能力。
|
|
4
|
-
* [POS]: skill runtime 的本地委派桥接层,把 delegate/team 语义收口到 team-state + workspace scratch。
|
|
5
|
-
* [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const {
|
|
10
|
-
ensureDir,
|
|
11
|
-
writeText,
|
|
12
|
-
exists,
|
|
13
|
-
readText,
|
|
14
|
-
runCommand,
|
|
15
|
-
nowIso
|
|
16
|
-
} = require('./store');
|
|
17
|
-
const { classifyDelegationMode } = require('./lifecycle');
|
|
18
|
-
const {
|
|
19
|
-
readTeamState,
|
|
20
|
-
writeTeamState,
|
|
21
|
-
buildInitialTeamState,
|
|
22
|
-
updateTeamState
|
|
23
|
-
} = require('./team-state');
|
|
24
|
-
const {
|
|
25
|
-
getChangePaths,
|
|
26
|
-
getWorkerPaths
|
|
27
|
-
} = require('./paths');
|
|
28
|
-
|
|
29
|
-
function getAssignmentsDir(repoRoot, changeId) {
|
|
30
|
-
return getChangePaths(repoRoot, changeId).workspacesDir;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function getSharedRuntimeDir(repoRoot, changeId) {
|
|
34
|
-
return path.join(getChangePaths(repoRoot, changeId).workspacesDir, '.runtime');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function getTeamMemoryPath(repoRoot, changeId) {
|
|
38
|
-
return path.join(getSharedRuntimeDir(repoRoot, changeId), 'team-memory.md');
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function getMessageBusPath(repoRoot, changeId) {
|
|
42
|
-
return path.join(getSharedRuntimeDir(repoRoot, changeId), 'message-bus.md');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function getWorkerWorkspacePath(repoRoot, changeId, workerId) {
|
|
46
|
-
return getWorkerPaths(repoRoot, changeId, workerId).workspacePath;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function getWorkersDir(repoRoot, changeId) {
|
|
50
|
-
return getChangePaths(repoRoot, changeId).workspacesDir;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function getWorkerDir(repoRoot, changeId, workerId) {
|
|
54
|
-
return getWorkerPaths(repoRoot, changeId, workerId).workerDir;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function getWorkerAssignmentPath(repoRoot, changeId, workerId) {
|
|
58
|
-
return getWorkerPaths(repoRoot, changeId, workerId).assignmentPath;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function getWorkerStatePath(repoRoot, changeId, workerId) {
|
|
62
|
-
return getWorkerPaths(repoRoot, changeId, workerId).statePath;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function getWorkerPromptPath(repoRoot, changeId, workerId) {
|
|
66
|
-
return getWorkerPaths(repoRoot, changeId, workerId).promptPath;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function getWorkerJournalPath(repoRoot, changeId, workerId) {
|
|
70
|
-
return getWorkerPaths(repoRoot, changeId, workerId).journalPath;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function getWorkerLaunchPath(repoRoot, changeId, workerId) {
|
|
74
|
-
return getWorkerPaths(repoRoot, changeId, workerId).launchPath;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function getWorkerSessionLogPath(repoRoot, changeId, workerId) {
|
|
78
|
-
return getWorkerPaths(repoRoot, changeId, workerId).sessionLogPath;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function guessWorkerRole(task) {
|
|
82
|
-
if (task.type === 'TEST') {
|
|
83
|
-
return 'qa-reviewer';
|
|
84
|
-
}
|
|
85
|
-
if (task.type === 'IMPL') {
|
|
86
|
-
return 'dev-implementer';
|
|
87
|
-
}
|
|
88
|
-
return 'operator';
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function buildOwnershipKey(task, state) {
|
|
92
|
-
return JSON.stringify([
|
|
93
|
-
classifyDelegationMode(task, state || {}),
|
|
94
|
-
guessWorkerRole(task),
|
|
95
|
-
[...(task.touches || [])].sort()
|
|
96
|
-
]);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function allocateWorkers(tasks, state) {
|
|
100
|
-
const workerMap = new Map();
|
|
101
|
-
const assignments = [];
|
|
102
|
-
let sequence = 1;
|
|
103
|
-
|
|
104
|
-
for (const task of tasks) {
|
|
105
|
-
const route = classifyDelegationMode(task, state || {});
|
|
106
|
-
if (route === 'direct') {
|
|
107
|
-
assignments.push({
|
|
108
|
-
taskId: task.id,
|
|
109
|
-
route,
|
|
110
|
-
workerId: 'main-agent',
|
|
111
|
-
role: 'controller'
|
|
112
|
-
});
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const key = buildOwnershipKey(task, state);
|
|
117
|
-
if (!workerMap.has(key)) {
|
|
118
|
-
const workerId = `${route}-${String(sequence).padStart(2, '0')}`;
|
|
119
|
-
workerMap.set(key, {
|
|
120
|
-
workerId,
|
|
121
|
-
role: guessWorkerRole(task)
|
|
122
|
-
});
|
|
123
|
-
sequence += 1;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
assignments.push({
|
|
127
|
-
taskId: task.id,
|
|
128
|
-
route,
|
|
129
|
-
...workerMap.get(key)
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return assignments;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async function detectCurrentBranch(repoRoot) {
|
|
137
|
-
const branch = await runCommand('git rev-parse --abbrev-ref HEAD', { cwd: repoRoot });
|
|
138
|
-
return branch.code === 0 ? branch.stdout.trim() : 'unknown';
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
async function ensureDelegationScaffold(repoRoot, changeId) {
|
|
142
|
-
const change = getChangePaths(repoRoot, changeId);
|
|
143
|
-
await ensureDir(change.executionDir);
|
|
144
|
-
await ensureDir(change.workspacesDir);
|
|
145
|
-
await ensureDir(getSharedRuntimeDir(repoRoot, changeId));
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async function syncMessageBus(repoRoot, changeId, assignments, planVersion) {
|
|
149
|
-
const busPath = getMessageBusPath(repoRoot, changeId);
|
|
150
|
-
const existing = await readText(busPath, '');
|
|
151
|
-
const header = existing.trim().length > 0 ? '' : '# Message Bus\n\n';
|
|
152
|
-
const lines = [
|
|
153
|
-
`## plan_version ${planVersion}`,
|
|
154
|
-
'',
|
|
155
|
-
...assignments
|
|
156
|
-
.filter((item) => item.route !== 'direct')
|
|
157
|
-
.map((item) => `- ${item.workerId} queued for ${item.taskId} (${item.route})`)
|
|
158
|
-
];
|
|
159
|
-
|
|
160
|
-
await writeText(busPath, `${existing}${header}${lines.join('\n')}\n\n`);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
async function syncTeamMemory(repoRoot, changeId, manifest, assignments) {
|
|
164
|
-
const teamMemoryPath = getTeamMemoryPath(repoRoot, changeId);
|
|
165
|
-
const delegateAssignments = assignments.filter((item) => item.route !== 'direct');
|
|
166
|
-
const content = [
|
|
167
|
-
`# Team Memory: ${changeId}`,
|
|
168
|
-
'',
|
|
169
|
-
`- Plan Version: ${manifest.metadata?.planVersion || 1}`,
|
|
170
|
-
`- Worker Count: ${new Set(delegateAssignments.map((item) => item.workerId)).size}`,
|
|
171
|
-
'',
|
|
172
|
-
'## Shared Constraints',
|
|
173
|
-
'',
|
|
174
|
-
'- change-state.json、task-manifest.json、team-state.json 是 durable truth。',
|
|
175
|
-
'- workspace scratch 只保留运行态,不视为事实源。',
|
|
176
|
-
'',
|
|
177
|
-
'## Active Worker Ownership',
|
|
178
|
-
'',
|
|
179
|
-
...(delegateAssignments.length > 0
|
|
180
|
-
? delegateAssignments.map((item) => `- ${item.workerId} (${item.role}) owns ${item.taskId}`)
|
|
181
|
-
: ['- No delegate/team workers in current plan'])
|
|
182
|
-
].join('\n');
|
|
183
|
-
|
|
184
|
-
await writeText(teamMemoryPath, `${content}\n`);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function groupAssignmentsByWorker(assignments) {
|
|
188
|
-
return assignments
|
|
189
|
-
.filter((item) => item.route !== 'direct')
|
|
190
|
-
.reduce((map, item) => {
|
|
191
|
-
if (!map.has(item.workerId)) {
|
|
192
|
-
map.set(item.workerId, []);
|
|
193
|
-
}
|
|
194
|
-
map.get(item.workerId).push(item);
|
|
195
|
-
return map;
|
|
196
|
-
}, new Map());
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
async function syncAssignmentFiles(repoRoot, changeId, manifest, assignments, branch) {
|
|
200
|
-
const grouped = groupAssignmentsByWorker(assignments);
|
|
201
|
-
|
|
202
|
-
for (const [workerId, owned] of grouped.entries()) {
|
|
203
|
-
const assignmentPath = getWorkerAssignmentPath(repoRoot, changeId, workerId);
|
|
204
|
-
const role = owned[0].role;
|
|
205
|
-
const workspace = getWorkerWorkspacePath(repoRoot, changeId, workerId);
|
|
206
|
-
const lines = [
|
|
207
|
-
`# Assignment: ${workerId}`,
|
|
208
|
-
'',
|
|
209
|
-
`- Worker: \`${workerId}\``,
|
|
210
|
-
`- Role: \`${role}\``,
|
|
211
|
-
`- Plan Version: ${manifest.metadata?.planVersion || 1}`,
|
|
212
|
-
`- Branch: ${branch}`,
|
|
213
|
-
`- Workspace: \`${workspace}\``,
|
|
214
|
-
'',
|
|
215
|
-
'## Owned Tasks',
|
|
216
|
-
'',
|
|
217
|
-
...owned.map((assignment) => {
|
|
218
|
-
const task = manifest.tasks.find((item) => item.id === assignment.taskId);
|
|
219
|
-
return `- \`${assignment.taskId}\` ${task.title}`;
|
|
220
|
-
}),
|
|
221
|
-
'',
|
|
222
|
-
'## Activity Log',
|
|
223
|
-
'',
|
|
224
|
-
'- queued'
|
|
225
|
-
];
|
|
226
|
-
|
|
227
|
-
await writeText(assignmentPath, `${lines.join('\n')}\n`);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
async function syncWorkerFiles(repoRoot, changeId, manifest, assignments, branch) {
|
|
232
|
-
const grouped = groupAssignmentsByWorker(assignments);
|
|
233
|
-
|
|
234
|
-
for (const [workerId, owned] of grouped.entries()) {
|
|
235
|
-
const role = owned[0].role;
|
|
236
|
-
const workerDir = getWorkerDir(repoRoot, changeId, workerId);
|
|
237
|
-
const workerPaths = getWorkerPaths(repoRoot, changeId, workerId);
|
|
238
|
-
await ensureDir(workerDir);
|
|
239
|
-
|
|
240
|
-
const state = [
|
|
241
|
-
`# Worker State: ${workerId}`,
|
|
242
|
-
'',
|
|
243
|
-
`- Role: \`${role}\``,
|
|
244
|
-
`- Plan Version: ${manifest.metadata?.planVersion || 1}`,
|
|
245
|
-
'- Status: `idle`',
|
|
246
|
-
'- Current Task: `none`',
|
|
247
|
-
`- Branch: ${branch}`,
|
|
248
|
-
`- Workspace: \`${workerPaths.workspacePath}\``
|
|
249
|
-
].join('\n');
|
|
250
|
-
|
|
251
|
-
const journal = [
|
|
252
|
-
`# Worker Journal: ${workerId}`,
|
|
253
|
-
'',
|
|
254
|
-
'## Initialized',
|
|
255
|
-
'',
|
|
256
|
-
`- Plan Version: ${manifest.metadata?.planVersion || 1}`,
|
|
257
|
-
`- Assigned Tasks: ${owned.map((item) => item.taskId).join(', ')}`
|
|
258
|
-
].join('\n');
|
|
259
|
-
|
|
260
|
-
await writeText(workerPaths.statePath, `${state}\n`);
|
|
261
|
-
await writeText(workerPaths.journalPath, `${journal}\n`);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
async function syncTeamStateArtifacts(repoRoot, changeId, manifest, assignments, branch) {
|
|
266
|
-
const delegateAssignments = assignments.filter((item) => item.route !== 'direct');
|
|
267
|
-
if (delegateAssignments.length === 0) {
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
await writeTeamState(repoRoot, changeId, buildInitialTeamState(changeId, manifest, assignments, branch));
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
async function syncDelegationRuntime(repoRoot, changeId, manifest, state) {
|
|
275
|
-
await ensureDelegationScaffold(repoRoot, changeId);
|
|
276
|
-
|
|
277
|
-
const assignments = allocateWorkers(manifest.tasks || [], state);
|
|
278
|
-
const branch = await detectCurrentBranch(repoRoot);
|
|
279
|
-
const planVersion = manifest.metadata?.planVersion || 1;
|
|
280
|
-
|
|
281
|
-
await syncWorkerFiles(repoRoot, changeId, manifest, assignments, branch);
|
|
282
|
-
await syncAssignmentFiles(repoRoot, changeId, manifest, assignments, branch);
|
|
283
|
-
await syncTeamMemory(repoRoot, changeId, manifest, assignments);
|
|
284
|
-
await syncMessageBus(repoRoot, changeId, assignments, planVersion);
|
|
285
|
-
await syncTeamStateArtifacts(repoRoot, changeId, manifest, assignments, branch);
|
|
286
|
-
|
|
287
|
-
return {
|
|
288
|
-
planVersion,
|
|
289
|
-
assignments,
|
|
290
|
-
delegatedCount: assignments.filter((item) => item.route !== 'direct').length,
|
|
291
|
-
teamCount: assignments.filter((item) => item.route === 'team').length
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
async function findAssignment(repoRoot, changeId, taskId) {
|
|
296
|
-
const teamState = await readTeamState(repoRoot, changeId);
|
|
297
|
-
if (!teamState?.team?.taskAssignments?.[taskId]) {
|
|
298
|
-
return null;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const workerId = teamState.team.taskAssignments[taskId];
|
|
302
|
-
const teammate = (teamState.team.teammates || []).find((item) => item.id === workerId);
|
|
303
|
-
return {
|
|
304
|
-
taskId,
|
|
305
|
-
workerId,
|
|
306
|
-
role: teammate?.role || 'developer',
|
|
307
|
-
route: teamState.team.mode === 'parallel' ? 'team' : 'delegate'
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
async function replaceSection(content, header, lines) {
|
|
312
|
-
const next = lines.join('\n');
|
|
313
|
-
const pattern = new RegExp(`## ${header}[\\s\\S]*?(?=\\n## |$)`, 'm');
|
|
314
|
-
const section = `## ${header}\n\n${next}`;
|
|
315
|
-
|
|
316
|
-
if (pattern.test(content)) {
|
|
317
|
-
return content.replace(pattern, section);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
return `${content.trimEnd()}\n\n${section}\n`;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
async function updateAssignmentStatus(repoRoot, changeId, taskId, fields) {
|
|
324
|
-
const assignment = await findAssignment(repoRoot, changeId, taskId);
|
|
325
|
-
if (!assignment || assignment.workerId === 'main-agent') {
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const assignmentPath = getWorkerAssignmentPath(repoRoot, changeId, assignment.workerId);
|
|
330
|
-
if (!(await exists(assignmentPath))) {
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
const existing = await readText(assignmentPath, '');
|
|
335
|
-
const logLines = existing.includes('## Activity Log')
|
|
336
|
-
? existing
|
|
337
|
-
: await replaceSection(existing, 'Activity Log', ['- queued']);
|
|
338
|
-
const message = [
|
|
339
|
-
`- ${nowIso()} \`${taskId}\``,
|
|
340
|
-
fields.status ? `status=\`${fields.status}\`` : '',
|
|
341
|
-
fields.workspace ? `workspace=\`${fields.workspace}\`` : '',
|
|
342
|
-
fields.sessionId ? `session=\`${fields.sessionId}\`` : '',
|
|
343
|
-
fields.summary ? `summary=${fields.summary}` : ''
|
|
344
|
-
].filter(Boolean).join(' ');
|
|
345
|
-
|
|
346
|
-
await writeText(assignmentPath, `${logLines.trimEnd()}\n${message}\n`);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
async function updateWorkerState(repoRoot, changeId, workerId, fields) {
|
|
350
|
-
const statePath = getWorkerStatePath(repoRoot, changeId, workerId);
|
|
351
|
-
if (!(await exists(statePath))) {
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
const content = [
|
|
356
|
-
`# Worker State: ${workerId}`,
|
|
357
|
-
'',
|
|
358
|
-
`- Role: \`${fields.role || 'developer'}\``,
|
|
359
|
-
`- Plan Version: ${fields.planVersion || 1}`,
|
|
360
|
-
`- Status: \`${fields.status || 'idle'}\``,
|
|
361
|
-
`- Current Task: \`${fields.currentTask || 'none'}\``,
|
|
362
|
-
`- Branch: ${fields.branch || 'unknown'}`,
|
|
363
|
-
`- Workspace: \`${fields.workspace || 'pending'}\``
|
|
364
|
-
].join('\n');
|
|
365
|
-
await writeText(statePath, `${content}\n`);
|
|
366
|
-
|
|
367
|
-
await updateTeamState(repoRoot, changeId, (current) => {
|
|
368
|
-
const teammates = [...(current.team?.teammates || [])];
|
|
369
|
-
const nextIndex = teammates.findIndex((item) => item.id === workerId);
|
|
370
|
-
if (nextIndex === -1) {
|
|
371
|
-
return current;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
teammates[nextIndex] = {
|
|
375
|
-
...teammates[nextIndex],
|
|
376
|
-
status: fields.status || teammates[nextIndex].status,
|
|
377
|
-
currentTask: fields.currentTask === 'none' ? null : (fields.currentTask || teammates[nextIndex].currentTask),
|
|
378
|
-
lastActiveAt: nowIso()
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
return {
|
|
382
|
-
...current,
|
|
383
|
-
updatedAt: nowIso(),
|
|
384
|
-
team: {
|
|
385
|
-
...current.team,
|
|
386
|
-
teammates,
|
|
387
|
-
updatedAt: nowIso()
|
|
388
|
-
}
|
|
389
|
-
};
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
async function appendWorkerJournal(repoRoot, changeId, workerId, message) {
|
|
394
|
-
const journalPath = getWorkerJournalPath(repoRoot, changeId, workerId);
|
|
395
|
-
const existing = await readText(journalPath, '');
|
|
396
|
-
const header = existing.trim().length > 0 ? '' : `# Worker Journal: ${workerId}\n\n`;
|
|
397
|
-
await writeText(journalPath, `${existing}${header}- ${message}\n`);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
async function listWorkers(repoRoot, changeId) {
|
|
401
|
-
const teamState = await readTeamState(repoRoot, changeId);
|
|
402
|
-
if (!teamState?.team?.teammates) {
|
|
403
|
-
return [];
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
return teamState.team.teammates.map((item) => ({
|
|
407
|
-
workerId: item.id,
|
|
408
|
-
role: item.role,
|
|
409
|
-
status: item.status,
|
|
410
|
-
currentTask: item.currentTask
|
|
411
|
-
}));
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
async function buildWorkerHandoff(repoRoot, changeId, workerId) {
|
|
415
|
-
const teamState = await readTeamState(repoRoot, changeId);
|
|
416
|
-
if (!teamState?.team?.taskAssignments) {
|
|
417
|
-
throw new Error(`No delegation runtime found for ${changeId}`);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
const tasks = Object.entries(teamState.team.taskAssignments)
|
|
421
|
-
.filter(([, assignedWorkerId]) => assignedWorkerId === workerId)
|
|
422
|
-
.map(([taskId]) => taskId);
|
|
423
|
-
|
|
424
|
-
if (tasks.length === 0) {
|
|
425
|
-
throw new Error(`Worker ${workerId} has no assigned tasks`);
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
const teammate = (teamState.team.teammates || []).find((item) => item.id === workerId);
|
|
429
|
-
const workerPaths = getWorkerPaths(repoRoot, changeId, workerId);
|
|
430
|
-
|
|
431
|
-
return {
|
|
432
|
-
changeId,
|
|
433
|
-
workerId,
|
|
434
|
-
role: teammate?.role || 'developer',
|
|
435
|
-
status: teammate?.status || 'idle',
|
|
436
|
-
planVersion: teamState.planVersion || 1,
|
|
437
|
-
currentTask: teammate?.currentTask || null,
|
|
438
|
-
taskIds: tasks,
|
|
439
|
-
runtimeDir: workerPaths.workerDir,
|
|
440
|
-
statePath: workerPaths.statePath,
|
|
441
|
-
journalPath: workerPaths.journalPath,
|
|
442
|
-
sessionLogPath: workerPaths.sessionLogPath,
|
|
443
|
-
assignmentPath: workerPaths.assignmentPath,
|
|
444
|
-
workspacePath: workerPaths.workspacePath
|
|
445
|
-
};
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
async function appendMessageBus(repoRoot, changeId, message) {
|
|
449
|
-
const busPath = getMessageBusPath(repoRoot, changeId);
|
|
450
|
-
const existing = await readText(busPath, '');
|
|
451
|
-
const header = existing.trim().length > 0 ? '' : '# Message Bus\n\n';
|
|
452
|
-
await writeText(busPath, `${existing}${header}- ${message}\n`);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
async function ensureWorkerWorkspace(repoRoot, changeId, assignment) {
|
|
456
|
-
const fallback = {
|
|
457
|
-
cwd: repoRoot,
|
|
458
|
-
mode: 'fallback',
|
|
459
|
-
workspacePath: repoRoot,
|
|
460
|
-
reason: 'direct-or-no-worktree'
|
|
461
|
-
};
|
|
462
|
-
|
|
463
|
-
if (!assignment || assignment.workerId === 'main-agent') {
|
|
464
|
-
return fallback;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
const workspacePath = getWorkerWorkspacePath(repoRoot, changeId, assignment.workerId);
|
|
468
|
-
if (await exists(workspacePath)) {
|
|
469
|
-
return {
|
|
470
|
-
cwd: workspacePath,
|
|
471
|
-
mode: 'worktree',
|
|
472
|
-
workspacePath,
|
|
473
|
-
reason: 'reused'
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
const hasGit = await runCommand('git rev-parse --show-toplevel', { cwd: repoRoot });
|
|
478
|
-
if (hasGit.code !== 0) {
|
|
479
|
-
return {
|
|
480
|
-
cwd: repoRoot,
|
|
481
|
-
mode: 'fallback',
|
|
482
|
-
workspacePath: repoRoot,
|
|
483
|
-
reason: 'git-unavailable'
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
await ensureDir(path.dirname(workspacePath));
|
|
488
|
-
const create = await runCommand(`git worktree add --detach "${workspacePath}" HEAD`, {
|
|
489
|
-
cwd: repoRoot,
|
|
490
|
-
timeoutMs: 60 * 1000
|
|
491
|
-
});
|
|
492
|
-
if (create.code !== 0) {
|
|
493
|
-
return {
|
|
494
|
-
cwd: repoRoot,
|
|
495
|
-
mode: 'fallback',
|
|
496
|
-
workspacePath: repoRoot,
|
|
497
|
-
reason: (create.stderr || create.stdout || 'worktree add failed').trim().slice(0, 200)
|
|
498
|
-
};
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
return {
|
|
502
|
-
cwd: workspacePath,
|
|
503
|
-
mode: 'worktree',
|
|
504
|
-
workspacePath,
|
|
505
|
-
reason: 'created'
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
module.exports = {
|
|
510
|
-
getAssignmentsDir,
|
|
511
|
-
getSharedRuntimeDir,
|
|
512
|
-
getTeamMemoryPath,
|
|
513
|
-
getMessageBusPath,
|
|
514
|
-
getWorkerWorkspacePath,
|
|
515
|
-
getWorkersDir,
|
|
516
|
-
getWorkerDir,
|
|
517
|
-
getWorkerAssignmentPath,
|
|
518
|
-
getWorkerStatePath,
|
|
519
|
-
getWorkerPromptPath,
|
|
520
|
-
getWorkerJournalPath,
|
|
521
|
-
getWorkerLaunchPath,
|
|
522
|
-
getWorkerSessionLogPath,
|
|
523
|
-
allocateWorkers,
|
|
524
|
-
syncDelegationRuntime,
|
|
525
|
-
findAssignment,
|
|
526
|
-
updateAssignmentStatus,
|
|
527
|
-
updateWorkerState,
|
|
528
|
-
appendWorkerJournal,
|
|
529
|
-
listWorkers,
|
|
530
|
-
buildWorkerHandoff,
|
|
531
|
-
appendMessageBus,
|
|
532
|
-
ensureWorkerWorkspace
|
|
533
|
-
};
|