cc-devflow 4.5.11 → 4.5.13

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.
Files changed (185) hide show
  1. package/.claude/skills/cc-act/CHANGELOG.md +18 -0
  2. package/.claude/skills/cc-act/PLAYBOOK.md +17 -269
  3. package/.claude/skills/cc-act/SKILL.md +38 -425
  4. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_INDEX_TEMPLATE.md +2 -13
  5. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_TEMPLATE.md +1 -9
  6. package/.claude/skills/cc-act/assets/PR_BRIEF_TEMPLATE.md +21 -177
  7. package/.claude/skills/cc-act/references/closure-contract.md +12 -63
  8. package/.claude/skills/cc-act/references/git-commit-guidelines.md +5 -5
  9. package/.claude/skills/cc-act/scripts/cc-act-common.sh +5 -322
  10. package/.claude/skills/cc-act/scripts/detect-ship-target.sh +11 -2
  11. package/.claude/skills/cc-act/scripts/inspect-git-index.sh +58 -0
  12. package/.claude/skills/cc-act/scripts/render-pr-brief.sh +40 -440
  13. package/.claude/skills/cc-act/scripts/verify-act-gate.sh +10 -50
  14. package/.claude/skills/cc-check/CHANGELOG.md +18 -0
  15. package/.claude/skills/cc-check/PLAYBOOK.md +19 -273
  16. package/.claude/skills/cc-check/SKILL.md +33 -456
  17. package/.claude/skills/cc-check/references/review-contract.md +12 -147
  18. package/.claude/skills/cc-dev/CHANGELOG.md +15 -0
  19. package/.claude/skills/cc-dev/PLAYBOOK.md +1 -1
  20. package/.claude/skills/cc-dev/SKILL.md +52 -137
  21. package/.claude/skills/cc-dev/scripts/resolve-cc-devflow.sh +181 -0
  22. package/.claude/skills/cc-do/CHANGELOG.md +11 -0
  23. package/.claude/skills/cc-do/PLAYBOOK.md +19 -113
  24. package/.claude/skills/cc-do/SKILL.md +39 -245
  25. package/.claude/skills/cc-do/references/execution-recovery.md +15 -109
  26. package/.claude/skills/cc-do/scripts/cc-do-common.sh +5 -57
  27. package/.claude/skills/cc-do/scripts/check-task-status.sh +35 -65
  28. package/.claude/skills/cc-do/scripts/mark-task-complete.sh +9 -46
  29. package/.claude/skills/cc-do/scripts/select-ready-tasks.sh +29 -97
  30. package/.claude/skills/cc-investigate/CHANGELOG.md +16 -0
  31. package/.claude/skills/cc-investigate/PLAYBOOK.md +20 -180
  32. package/.claude/skills/cc-investigate/SKILL.md +64 -246
  33. package/.claude/skills/cc-investigate/assets/TASKS_TEMPLATE.md +48 -98
  34. package/.claude/skills/cc-investigate/references/investigation-contract.md +14 -218
  35. package/.claude/skills/cc-next/CHANGELOG.md +6 -0
  36. package/.claude/skills/cc-next/PLAYBOOK.md +12 -8
  37. package/.claude/skills/cc-next/SKILL.md +34 -140
  38. package/.claude/skills/cc-plan/CHANGELOG.md +16 -0
  39. package/.claude/skills/cc-plan/PLAYBOOK.md +22 -161
  40. package/.claude/skills/cc-plan/SKILL.md +45 -295
  41. package/.claude/skills/cc-plan/assets/TASKS_TEMPLATE.md +30 -228
  42. package/.claude/skills/cc-plan/references/planning-contract.md +24 -161
  43. package/.claude/skills/cc-plan/scripts/next-change-key.sh +8 -44
  44. package/.claude/skills/cc-plan/scripts/parse-task-dependencies.js +2 -2
  45. package/.claude/skills/cc-plan/scripts/validate-scope.sh +1 -1
  46. package/.claude/skills/cc-pr-land/SKILL.md +14 -114
  47. package/.claude/skills/cc-pr-review/CHANGELOG.md +4 -0
  48. package/.claude/skills/cc-pr-review/SKILL.md +20 -103
  49. package/.claude/skills/cc-review/CHANGELOG.md +23 -0
  50. package/.claude/skills/cc-review/PLAYBOOK.md +13 -86
  51. package/.claude/skills/cc-review/SKILL.md +67 -238
  52. package/.claude/skills/cc-review/references/e2e-and-plugin-verification.md +2 -2
  53. package/.claude/skills/cc-review/references/implementation-review-branch.md +7 -147
  54. package/.claude/skills/cc-review/references/plan-review-branch.md +5 -147
  55. package/.claude/skills/cc-review/references/review-methods.md +10 -218
  56. package/.claude/skills/cc-review/scripts/collect-review-context.sh +4 -63
  57. package/.claude/skills/cc-roadmap/PLAYBOOK.md +1 -1
  58. package/.claude/skills/cc-roadmap/SKILL.md +3 -3
  59. package/.claude/skills/cc-simplify/CHANGELOG.md +7 -0
  60. package/.claude/skills/cc-simplify/SKILL.md +26 -21
  61. package/.claude/skills/cc-spec-init/PLAYBOOK.md +12 -48
  62. package/.claude/skills/cc-spec-init/SKILL.md +29 -132
  63. package/.claude/skills/cc-spec-init/references/spec-contract.md +8 -17
  64. package/CHANGELOG.md +21 -0
  65. package/bin/cc-devflow-cli.js +20 -260
  66. package/bin/cc-devflow.js +44 -7
  67. package/docs/commands/README.md +1 -1
  68. package/docs/commands/README.zh-CN.md +1 -1
  69. package/docs/examples/README.md +1 -1
  70. package/docs/examples/START-HERE.md +14 -15
  71. package/docs/examples/example-bindings.json +11 -11
  72. package/docs/examples/full-design-blocked/README.md +4 -6
  73. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/{planning/tasks.md → task.md} +20 -15
  74. package/docs/examples/local-handoff/README.md +8 -11
  75. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/pr-brief.md +31 -0
  76. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/{planning/tasks.md → task.md} +18 -13
  77. package/docs/examples/pdca-loop/README.md +6 -9
  78. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/pr-brief.md +9 -11
  79. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/{planning/tasks.md → task.md} +18 -13
  80. package/docs/examples/scripts/check-example-bindings.sh +11 -62
  81. package/docs/guides/artifact-contract.md +10 -40
  82. package/docs/guides/getting-started.md +8 -8
  83. package/docs/guides/getting-started.zh-CN.md +8 -8
  84. package/docs/guides/minimize-artifacts.md +16 -130
  85. package/docs/guides/project-postmortem.md +14 -71
  86. package/lib/compiler/__tests__/skills-registry.test.js +9 -8
  87. package/lib/compiler/resource-copier.js +29 -0
  88. package/lib/skill-runtime/__tests__/archive-change.test.js +2 -2
  89. package/lib/skill-runtime/__tests__/benchmark-skills.test.js +3 -3
  90. package/lib/skill-runtime/__tests__/cli-bootstrap.integration.test.js +14 -4
  91. package/lib/skill-runtime/errors.js +3 -3
  92. package/lib/skill-runtime/index.js +5 -23
  93. package/lib/skill-runtime/paths.js +5 -52
  94. package/lib/skill-runtime/query-registry.js +4 -4
  95. package/lib/skill-runtime/query.js +89 -201
  96. package/lib/skill-runtime/store.js +4 -40
  97. package/lib/skill-runtime/trace.js +2 -2
  98. package/package.json +2 -5
  99. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_PRINCIPLES_TEMPLATE.md +0 -29
  100. package/.claude/skills/cc-act/assets/RELEASE_NOTE_TEMPLATE.md +0 -54
  101. package/.claude/skills/cc-act/scripts/generate-status-report.sh +0 -92
  102. package/.claude/skills/cc-act/scripts/sync-act-docs.sh +0 -355
  103. package/.claude/skills/cc-check/assets/REPORT_CARD_TEMPLATE.json +0 -234
  104. package/.claude/skills/cc-check/scripts/render-report-card.js +0 -438
  105. package/.claude/skills/cc-check/scripts/verify-gate.sh +0 -85
  106. package/.claude/skills/cc-do/scripts/build-task-context.sh +0 -175
  107. package/.claude/skills/cc-do/scripts/record-review-decision.sh +0 -88
  108. package/.claude/skills/cc-do/scripts/recover-workflow.sh +0 -82
  109. package/.claude/skills/cc-do/scripts/run-problem-analysis.sh +0 -70
  110. package/.claude/skills/cc-do/scripts/verify-task-gates.sh +0 -109
  111. package/.claude/skills/cc-do/scripts/write-task-checkpoint.sh +0 -92
  112. package/.claude/skills/cc-investigate/assets/TASK_MANIFEST_TEMPLATE.json +0 -224
  113. package/.claude/skills/cc-plan/assets/TASK_MANIFEST_TEMPLATE.json +0 -178
  114. package/.claude/skills/cc-spec-init/assets/CHANGE_META_TEMPLATE.json +0 -28
  115. package/.claude/skills/cc-spec-init/scripts/validate-spec-links.sh +0 -45
  116. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/design.md +0 -234
  117. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/task-manifest.json +0 -488
  118. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/review/report-card.json +0 -189
  119. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/resume-index.md +0 -39
  120. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/status.md +0 -29
  121. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/design.md +0 -123
  122. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/task-manifest.json +0 -292
  123. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/review/report-card.json +0 -136
  124. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/status.md +0 -29
  125. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/design.md +0 -124
  126. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/task-manifest.json +0 -292
  127. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/review/report-card.json +0 -136
  128. package/docs/get-shit-done-strategy-audit.md +0 -518
  129. package/docs/skill-runtime-migration.md +0 -46
  130. package/lib/skill-runtime/__tests__/approve.test.js +0 -92
  131. package/lib/skill-runtime/__tests__/autopilot.test.js +0 -253
  132. package/lib/skill-runtime/__tests__/benchmark-artifacts.test.js +0 -165
  133. package/lib/skill-runtime/__tests__/delegation.test.js +0 -97
  134. package/lib/skill-runtime/__tests__/dispatch.test.js +0 -237
  135. package/lib/skill-runtime/__tests__/intent.test.js +0 -203
  136. package/lib/skill-runtime/__tests__/lifecycle.test.js +0 -169
  137. package/lib/skill-runtime/__tests__/planner.tdd.test.js +0 -331
  138. package/lib/skill-runtime/__tests__/prepare-pr.test.js +0 -126
  139. package/lib/skill-runtime/__tests__/query.test.js +0 -860
  140. package/lib/skill-runtime/__tests__/readiness.test.js +0 -53
  141. package/lib/skill-runtime/__tests__/release.test.js +0 -85
  142. package/lib/skill-runtime/__tests__/review-check-integration.test.js +0 -148
  143. package/lib/skill-runtime/__tests__/review-records.test.js +0 -619
  144. package/lib/skill-runtime/__tests__/runtime.integration.test.js +0 -351
  145. package/lib/skill-runtime/__tests__/schemas.test.js +0 -337
  146. package/lib/skill-runtime/__tests__/task-contract-migrate.test.js +0 -137
  147. package/lib/skill-runtime/__tests__/task-contract.test.js +0 -874
  148. package/lib/skill-runtime/__tests__/team-state.test.js +0 -51
  149. package/lib/skill-runtime/__tests__/verify-artifacts.test.js +0 -203
  150. package/lib/skill-runtime/__tests__/worker-run.test.js +0 -275
  151. package/lib/skill-runtime/__tests__/worker.test.js +0 -56
  152. package/lib/skill-runtime/__tests__/workflow-context-legacy-fallback.test.js +0 -31
  153. package/lib/skill-runtime/__tests__/workflow-context.test.js +0 -98
  154. package/lib/skill-runtime/artifacts.js +0 -88
  155. package/lib/skill-runtime/context-index.js +0 -545
  156. package/lib/skill-runtime/delegation.js +0 -533
  157. package/lib/skill-runtime/intent.js +0 -309
  158. package/lib/skill-runtime/lifecycle.js +0 -294
  159. package/lib/skill-runtime/operations/CLAUDE.md +0 -19
  160. package/lib/skill-runtime/operations/approve.js +0 -81
  161. package/lib/skill-runtime/operations/autopilot-core.js +0 -337
  162. package/lib/skill-runtime/operations/autopilot-execution.js +0 -307
  163. package/lib/skill-runtime/operations/autopilot-shared.js +0 -48
  164. package/lib/skill-runtime/operations/autopilot.js +0 -163
  165. package/lib/skill-runtime/operations/dispatch.js +0 -416
  166. package/lib/skill-runtime/operations/init.js +0 -60
  167. package/lib/skill-runtime/operations/janitor.js +0 -61
  168. package/lib/skill-runtime/operations/plan.js +0 -59
  169. package/lib/skill-runtime/operations/prepare-pr.js +0 -25
  170. package/lib/skill-runtime/operations/release.js +0 -99
  171. package/lib/skill-runtime/operations/resume.js +0 -126
  172. package/lib/skill-runtime/operations/review-records.js +0 -265
  173. package/lib/skill-runtime/operations/snapshot.js +0 -45
  174. package/lib/skill-runtime/operations/task-contract.js +0 -593
  175. package/lib/skill-runtime/operations/verify.js +0 -170
  176. package/lib/skill-runtime/operations/worker-run.js +0 -531
  177. package/lib/skill-runtime/operations/worker.js +0 -33
  178. package/lib/skill-runtime/planner.js +0 -539
  179. package/lib/skill-runtime/readiness.js +0 -84
  180. package/lib/skill-runtime/review-records.js +0 -123
  181. package/lib/skill-runtime/review.js +0 -855
  182. package/lib/skill-runtime/schemas.js +0 -746
  183. package/lib/skill-runtime/task-contract.js +0 -188
  184. package/lib/skill-runtime/team-state.js +0 -122
  185. package/lib/skill-runtime/workflow-context.js +0 -748
@@ -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
- };
@@ -1,539 +0,0 @@
1
- /**
2
- * [INPUT]: 依赖 store 读取 tasks.md 与输出路径,接收 changeId/changeKey,依赖 schemas 校验 manifest。
3
- * [OUTPUT]: 对外提供 tasks.md → task-manifest.json 的解析与生成能力。
4
- * [POS]: skill runtime 计划编排层,被 operations/plan 直接调用。
5
- * [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
6
- */
7
-
8
- const {
9
- nowIso,
10
- readText,
11
- readJson,
12
- writeJson,
13
- exists,
14
- getTaskManifestPath,
15
- getTasksMarkdownPath
16
- } = require('./store');
17
- const path = require('path');
18
- const { parseManifest } = require('./schemas');
19
- const { isTaskCompletedStatus } = require('./lifecycle');
20
-
21
- const TASK_LINE = /^- \[( |x|X)\]\s+(T\d{3})\s*(.*)$/;
22
- const TRAILING_PATHS = /\(([^)]+)\)\s*$/;
23
- const DEPENDS_TAG = /dependsOn:([A-Za-z0-9_,-]+)/i;
24
- const PHASE_HEADING = /^##\s+Phase\s+(\d+)\s*:/i;
25
- const FIELD_LINE = /^\s{2,}([A-Za-z ]+):\s*(.*)$/;
26
-
27
- function normalizeTitle(rawTitle) {
28
- return rawTitle
29
- .replace(/\[P\]/g, '')
30
- .replace(DEPENDS_TAG, '')
31
- .replace(/`[^`]+`/g, '')
32
- .trim();
33
- }
34
-
35
- function parseTouches(rawTail) {
36
- const match = rawTail.match(TRAILING_PATHS);
37
- if (!match) {
38
- return [];
39
- }
40
-
41
- return match[1]
42
- .split(',')
43
- .map((item) => item.trim())
44
- .filter(Boolean);
45
- }
46
-
47
- function parseInlineCodeRefs(rawText) {
48
- const refs = [];
49
- const pattern = /`([^`]+)`/g;
50
- let match;
51
-
52
- while ((match = pattern.exec(rawText)) !== null) {
53
- const value = match[1].trim();
54
- if (!value || value.includes('&&') || value.includes('--') || value.includes(' ')) {
55
- continue;
56
- }
57
- refs.push(value);
58
- }
59
-
60
- return refs;
61
- }
62
-
63
- function parseCsvLike(rawText) {
64
- return String(rawText || '')
65
- .split(',')
66
- .map((item) => item.trim())
67
- .filter(Boolean);
68
- }
69
-
70
- function quoteShellArg(value) {
71
- return `'${String(value).replace(/'/g, `'\"'\"'`)}'`;
72
- }
73
-
74
- function buildTaskRunCommand(taskId, title) {
75
- return `printf '%s\\n' ${quoteShellArg(`[TASK ${taskId}] ${title}`)}`;
76
- }
77
-
78
- function parseFieldValues(rawValue) {
79
- const inlineRefs = parseInlineCodeRefs(rawValue);
80
- if (inlineRefs.length > 0) {
81
- return inlineRefs;
82
- }
83
-
84
- return parseCsvLike(
85
- String(rawValue || '')
86
- .replace(/`/g, '')
87
- .replace(/\s*\+\s*/g, ', ')
88
- );
89
- }
90
-
91
- function applyTaskField(task, label, value) {
92
- const normalizedLabel = label.trim().toLowerCase();
93
-
94
- if (normalizedLabel === 'goal') {
95
- const goal = value.trim();
96
- if (goal) {
97
- task.acceptance.push(goal);
98
- task.context.notes.push(goal);
99
- }
100
- return;
101
- }
102
-
103
- if (normalizedLabel === 'files') {
104
- const files = parseFieldValues(value);
105
- task.files.push(...files);
106
- task.touches.push(...files);
107
- return;
108
- }
109
-
110
- if (normalizedLabel === 'read first') {
111
- task.context.readFiles.push(...parseFieldValues(value));
112
- return;
113
- }
114
-
115
- if (normalizedLabel === 'verification') {
116
- const verification = value.trim();
117
- if (verification) {
118
- task.verification.push(verification);
119
- task.checks.push(verification);
120
- task.context.commands.push(verification);
121
- }
122
- return;
123
- }
124
-
125
- if (normalizedLabel === 'evidence') {
126
- task.evidence.push(...parseCsvLike(value));
127
- }
128
- }
129
-
130
- function dedupeList(values) {
131
- return [...new Set(values.filter(Boolean))];
132
- }
133
-
134
- function pushMissing(target, values) {
135
- for (const value of values) {
136
- if (value && !target.includes(value)) {
137
- target.push(value);
138
- }
139
- }
140
- }
141
-
142
- function pickPrimaryTestTarget(task) {
143
- return [...(task.files || []), ...(task.touches || [])].find((item) => /\.test\./i.test(item)) || '';
144
- }
145
-
146
- function enrichTaskMetadata(task, options = {}) {
147
- const primaryTestTarget = pickPrimaryTestTarget(task);
148
- const defaultReadFiles = options.defaultReadFiles || ['design.md'];
149
-
150
- if (task.acceptance.length === 0) {
151
- if (task.type === 'TEST') {
152
- task.acceptance.push(`Prove ${extractFeatureName(task.title) || task.title} fails before implementation`);
153
- } else if (task.type === 'IMPL') {
154
- task.acceptance.push(`Make ${extractFeatureName(task.title) || task.title} pass with the smallest implementation`);
155
- }
156
- }
157
-
158
- if (task.verification.length === 0) {
159
- if (primaryTestTarget) {
160
- task.verification.push(`npm test -- ${primaryTestTarget}`);
161
- } else if (task.type !== 'OTHER') {
162
- task.verification.push(`echo "verify ${task.id}"`);
163
- }
164
- }
165
-
166
- if (task.evidence.length === 0) {
167
- if (task.type === 'TEST') {
168
- task.evidence.push('failing test output');
169
- } else if (task.type === 'IMPL') {
170
- task.evidence.push('passing test output');
171
- }
172
- }
173
-
174
- if (task.context.readFiles.length === 0) {
175
- pushMissing(task.context.readFiles, defaultReadFiles);
176
- if (task.type === 'TEST') {
177
- pushMissing(task.context.readFiles, ['tasks.md']);
178
- }
179
- if (primaryTestTarget && !task.context.readFiles.includes(primaryTestTarget)) {
180
- task.context.readFiles.push(primaryTestTarget);
181
- }
182
- }
183
-
184
- if (task.context.commands.length === 0) {
185
- task.context.commands.push(...task.verification);
186
- }
187
- }
188
-
189
- function parseDependsOn(rawTail, fallbackDependsOn, isParallel) {
190
- const explicit = rawTail.match(DEPENDS_TAG);
191
- if (explicit) {
192
- return explicit[1]
193
- .split(',')
194
- .map((item) => item.trim())
195
- .filter(Boolean);
196
- }
197
-
198
- if (isParallel || !fallbackDependsOn) {
199
- return [];
200
- }
201
-
202
- return [fallbackDependsOn];
203
- }
204
-
205
- function parseTasksMarkdown(content, options = {}) {
206
- const lines = content.split(/\r?\n/);
207
- const tasks = [];
208
- let previousTaskId = null;
209
- let currentPhase = 1;
210
- let currentTask = null;
211
-
212
- function finalizeTask(task) {
213
- if (!task) {
214
- return;
215
- }
216
-
217
- task.touches = dedupeList(task.touches);
218
- task.files = dedupeList(task.files);
219
- task.checks = dedupeList(task.checks);
220
- task.acceptance = dedupeList(task.acceptance);
221
- task.verification = dedupeList(task.verification);
222
- task.evidence = dedupeList(task.evidence);
223
- task.context.readFiles = dedupeList(task.context.readFiles);
224
- task.context.commands = dedupeList(task.context.commands);
225
- task.context.notes = dedupeList(task.context.notes);
226
- enrichTaskMetadata(task, options);
227
- task.acceptance = dedupeList(task.acceptance);
228
- task.verification = dedupeList(task.verification);
229
- task.evidence = dedupeList(task.evidence);
230
- task.context.readFiles = dedupeList(task.context.readFiles);
231
- task.context.commands = dedupeList(task.context.commands);
232
- tasks.push(task);
233
- previousTaskId = task.id;
234
- }
235
-
236
- for (const line of lines) {
237
- const phaseMatch = line.match(PHASE_HEADING);
238
- if (phaseMatch) {
239
- currentPhase = Number(phaseMatch[1]);
240
- continue;
241
- }
242
-
243
- const match = line.match(TASK_LINE);
244
- if (match) {
245
- finalizeTask(currentTask);
246
-
247
- const [, doneMark, taskId, tail] = match;
248
- const isParallel = /\[P\]/.test(tail);
249
- const inlineFiles = parseInlineCodeRefs(tail);
250
- const touches = [...parseTouches(tail), ...inlineFiles];
251
- const dependsOn = parseDependsOn(tail, previousTaskId, isParallel);
252
- const title = normalizeTitle(tail).replace(TRAILING_PATHS, '').trim() || `Task ${taskId}`;
253
-
254
- // 提取任务类型 [TEST] 或 [IMPL]
255
- const taskType = extractTaskType(title);
256
-
257
- currentTask = {
258
- id: taskId,
259
- title,
260
- type: taskType,
261
- phase: currentPhase,
262
- parallel: isParallel,
263
- dependsOn,
264
- touches,
265
- files: inlineFiles,
266
- run: [buildTaskRunCommand(taskId, title)],
267
- checks: [],
268
- acceptance: [],
269
- verification: [],
270
- evidence: [],
271
- context: {
272
- readFiles: [],
273
- commands: [],
274
- notes: []
275
- },
276
- reviews: {
277
- spec: 'pending',
278
- code: 'pending'
279
- },
280
- status: doneMark.toLowerCase() === 'x' ? 'passed' : 'pending',
281
- attempts: 0,
282
- maxRetries: 1
283
- };
284
- continue;
285
- }
286
-
287
- if (!currentTask) {
288
- continue;
289
- }
290
-
291
- const fieldMatch = line.match(FIELD_LINE);
292
- if (fieldMatch) {
293
- applyTaskField(currentTask, fieldMatch[1], fieldMatch[2]);
294
- continue;
295
- }
296
- }
297
-
298
- finalizeTask(currentTask);
299
-
300
- // TDD 顺序验证 (Constitution Article VI)
301
- validateTDDOrder(tasks);
302
-
303
- return tasks;
304
- }
305
-
306
- function extractTaskType(title) {
307
- if (/\[TEST\]/i.test(title)) {
308
- return 'TEST';
309
- }
310
- if (/\[IMPL\]/i.test(title)) {
311
- return 'IMPL';
312
- }
313
- return 'OTHER';
314
- }
315
-
316
- /**
317
- * TDD 顺序验证 (Constitution Article VI)
318
- *
319
- * 规则:
320
- * 1. 每个 [IMPL] 任务必须有对应的 [TEST] 任务
321
- * 2. [IMPL] 任务必须依赖对应的 [TEST] 任务 (通过 dependsOn)
322
- * 3. [TEST] 任务不能依赖 [IMPL] 任务
323
- *
324
- * @param {Array} tasks - 任务列表
325
- * @throws {Error} - TDD 顺序违规时抛出错误
326
- */
327
- function validateTDDOrder(tasks) {
328
- const violations = [];
329
- const testTasks = tasks.filter(t => t.type === 'TEST');
330
- const implTasks = tasks.filter(t => t.type === 'IMPL');
331
-
332
- // 为每个 IMPL 任务查找对应的 TEST 任务
333
- for (const implTask of implTasks) {
334
- // 提取功能名称 (去除 [IMPL] 标记后的主要描述)
335
- const implFeature = extractFeatureName(implTask.title);
336
-
337
- // 查找匹配的 TEST 任务 (使用模糊匹配)
338
- const matchingTest = testTasks.find(testTask => {
339
- const testFeature = extractFeatureName(testTask.title);
340
- // 模糊匹配:检查是否包含相同的核心关键词
341
- return isSimilarFeature(testFeature, implFeature);
342
- });
343
-
344
- if (!matchingTest) {
345
- violations.push(
346
- `Task ${implTask.id} (${implTask.title}) missing corresponding TEST task. ` +
347
- `Constitution Article VI requires: NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST.`
348
- );
349
- continue;
350
- }
351
-
352
- // 检查依赖关系
353
- if (!implTask.dependsOn.includes(matchingTest.id)) {
354
- violations.push(
355
- `Task ${implTask.id} (${implTask.title}) must depend on ${matchingTest.id} (${matchingTest.title}). ` +
356
- `TDD violation: Implementation must come AFTER test.`
357
- );
358
- }
359
- }
360
-
361
- // 检查 TEST 任务不能依赖 IMPL 任务
362
- for (const testTask of testTasks) {
363
- const implDeps = testTask.dependsOn.filter(depId => {
364
- const depTask = tasks.find(t => t.id === depId);
365
- return depTask && depTask.type === 'IMPL';
366
- });
367
-
368
- if (implDeps.length > 0) {
369
- violations.push(
370
- `Task ${testTask.id} (${testTask.title}) depends on IMPL tasks: ${implDeps.join(', ')}. ` +
371
- `TDD violation: Tests must be written BEFORE implementation.`
372
- );
373
- }
374
- }
375
-
376
- if (violations.length > 0) {
377
- throw new Error(
378
- `TDD Order Validation Failed (Constitution Article VI):\n\n` +
379
- violations.map((v, i) => `${i + 1}. ${v}`).join('\n\n') +
380
- `\n\nFix tasks.md to follow TDD sequence: [TEST] tasks BEFORE [IMPL] tasks.`
381
- );
382
- }
383
- }
384
-
385
- /**
386
- * 从任务标题中提取功能名称
387
- * 例如: "[TEST] 用户登录功能" -> "用户登录功能"
388
- * "[IMPL] 用户登录功能" -> "用户登录功能"
389
- */
390
- function extractFeatureName(title) {
391
- return title
392
- .replace(/\[TEST\]/gi, '')
393
- .replace(/\[IMPL\]/gi, '')
394
- .replace(/\[P\]/gi, '')
395
- .replace(/\(dependsOn:[^)]*\)/gi, '')
396
- .replace(/\([^)]*\)/g, '')
397
- .trim();
398
- }
399
-
400
- /**
401
- * 判断两个功能名称是否相似
402
- * 使用核心关键词匹配策略
403
- */
404
- function isSimilarFeature(feature1, feature2) {
405
- // 提取核心关键词 (去除常见后缀如"测试"、"实现"、"功能"等)
406
- const normalize = (str) => str
407
- .replace(/测试$/g, '')
408
- .replace(/实现$/g, '')
409
- .replace(/功能$/g, '')
410
- .replace(/开发$/g, '')
411
- .replace(/编写$/g, '')
412
- .trim();
413
-
414
- const core1 = normalize(feature1);
415
- const core2 = normalize(feature2);
416
-
417
- // 精确匹配
418
- if (core1 === core2) {
419
- return true;
420
- }
421
-
422
- // 包含匹配 (较长的包含较短的)
423
- if (core1.length > core2.length) {
424
- return core1.includes(core2);
425
- } else {
426
- return core2.includes(core1);
427
- }
428
- }
429
-
430
- function buildDefaultTasks(changeId) {
431
- return [
432
- {
433
- id: 'T001',
434
- title: `Bootstrap ${changeId}`,
435
- type: 'OTHER',
436
- phase: 1,
437
- parallel: false,
438
- dependsOn: [],
439
- touches: [],
440
- files: [],
441
- run: [buildTaskRunCommand('T001', `Bootstrap ${changeId}`)],
442
- checks: [],
443
- acceptance: ['Bootstrap the requirement workspace'],
444
- verification: ['echo "[TASK T001] Bootstrap complete"'],
445
- evidence: ['bootstrap output'],
446
- context: {
447
- readFiles: ['tasks.md', 'design.md'],
448
- commands: ['echo "[TASK T001] Bootstrap complete"'],
449
- notes: ['Default bootstrap task created because tasks.md was missing']
450
- },
451
- reviews: {
452
- spec: 'pending',
453
- code: 'pending'
454
- },
455
- status: 'pending',
456
- attempts: 0,
457
- maxRetries: 1
458
- }
459
- ];
460
- }
461
-
462
- function deriveManifestExecutionState(tasks) {
463
- const unfinished = tasks.filter((task) => !isTaskCompletedStatus(task.status));
464
-
465
- if (unfinished.length === 0) {
466
- return {
467
- currentTaskId: null,
468
- activePhase: null
469
- };
470
- }
471
-
472
- const activePhase = Math.min(...unfinished.map((task) => task.phase || 1));
473
- const completedIds = new Set(
474
- tasks.filter((task) => isTaskCompletedStatus(task.status)).map((task) => task.id)
475
- );
476
- const currentTask = unfinished.find((task) => {
477
- if ((task.phase || 1) !== activePhase) {
478
- return false;
479
- }
480
-
481
- return (task.dependsOn || []).every((depId) => completedIds.has(depId));
482
- });
483
-
484
- return {
485
- currentTaskId: currentTask ? currentTask.id : null,
486
- activePhase
487
- };
488
- }
489
-
490
- function applyManifestExecutionState(manifest, updatedAt = nowIso()) {
491
- const executionState = deriveManifestExecutionState(manifest.tasks || []);
492
- manifest.currentTaskId = executionState.currentTaskId;
493
- delete manifest.activePhase;
494
- manifest.updatedAt = updatedAt;
495
- return manifest;
496
- }
497
-
498
- async function createTaskManifest({ repoRoot, changeId, changeKey, goal, overwrite = false }) {
499
- const pathOptions = changeKey ? { changeKey } : {};
500
- const manifestPath = getTaskManifestPath(repoRoot, changeId, pathOptions);
501
- const previous = await readJson(manifestPath, null);
502
-
503
- if (!overwrite && (await exists(manifestPath))) {
504
- return parseManifest(previous);
505
- }
506
-
507
- const tasksPath = getTasksMarkdownPath(repoRoot, changeId, pathOptions);
508
- const hasTasksFile = await exists(tasksPath);
509
- const legacyDesignPath = path.join(path.dirname(tasksPath), 'design.md');
510
- const defaultReadFiles = await exists(legacyDesignPath) ? ['design.md'] : ['tasks.md'];
511
- const rawTasks = hasTasksFile ? parseTasksMarkdown(await readText(tasksPath), { defaultReadFiles }) : [];
512
- const tasks = rawTasks.length > 0 ? rawTasks : buildDefaultTasks(changeId);
513
- const previousPlanVersion = previous?.metadata?.planVersion || 0;
514
- const manifest = applyManifestExecutionState({
515
- changeId,
516
- goal: goal || `Deliver ${changeId} safely with task-state truth.`,
517
- createdAt: previous?.createdAt || nowIso(),
518
- updatedAt: nowIso(),
519
- currentTaskId: null,
520
- tasks,
521
- metadata: {
522
- source: hasTasksFile ? 'tasks.md' : 'default',
523
- generatedBy: 'skill:cc-plan',
524
- planVersion: previousPlanVersion > 0 ? previousPlanVersion + 1 : 1
525
- }
526
- });
527
-
528
- const parsedManifest = parseManifest(manifest);
529
-
530
- await writeJson(manifestPath, parsedManifest);
531
- return parsedManifest;
532
- }
533
-
534
- module.exports = {
535
- parseTasksMarkdown,
536
- deriveManifestExecutionState,
537
- applyManifestExecutionState,
538
- createTaskManifest
539
- };
@@ -1,84 +0,0 @@
1
- /**
2
- * [INPUT]: 接收 report-card 对象与 report artifact path。
3
- * [OUTPUT]: 派生 ship-readiness 结果,并在未 ready 时抛出 named error。
4
- * [POS]: skill runtime 的交付就绪单一真相源,被 query/release 共享。
5
- * [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
6
- */
7
-
8
- const { namedError } = require('./errors');
9
-
10
- function deriveVerdict(report) {
11
- return report.verdict || (report.overall === 'pass' ? 'pass' : 'fail');
12
- }
13
-
14
- function collectShipReadinessBlockers(report) {
15
- const verdict = deriveVerdict(report);
16
- const blockers = [];
17
-
18
- if (report.overall !== 'pass') {
19
- blockers.push('report-card overall is not pass');
20
- }
21
-
22
- if (verdict !== 'pass') {
23
- blockers.push(`verdict is ${verdict}`);
24
- }
25
-
26
- if ((report.reroute || 'none') !== 'none') {
27
- blockers.push(`reroute is ${report.reroute}`);
28
- }
29
-
30
- if (report.specSyncReady !== true) {
31
- blockers.push('specSyncReady is not true');
32
- }
33
-
34
- blockers.push(...(report.blockingFindings || []));
35
- blockers.push(...(report.gaps || []));
36
- return blockers;
37
- }
38
-
39
- function deriveShipReadiness(report, { reportPath = '' } = {}) {
40
- const verdict = deriveVerdict(report);
41
- const reroute = report.reroute || 'none';
42
- const specSyncReady = report.specSyncReady === true;
43
- const blockers = collectShipReadinessBlockers(report);
44
-
45
- return {
46
- ready: blockers.length === 0,
47
- verdict,
48
- reroute,
49
- specSyncReady,
50
- blockers,
51
- reportPath,
52
- timestamp: report.timestamp || ''
53
- };
54
- }
55
-
56
- function assertShipReady(report, {
57
- reportPath = '',
58
- errorName = 'ShipReadinessError',
59
- rescueAction = 'run cc-check until ship-readiness is ready'
60
- } = {}) {
61
- const readiness = deriveShipReadiness(report, { reportPath });
62
-
63
- if (readiness.ready) {
64
- return readiness;
65
- }
66
-
67
- throw namedError(
68
- errorName,
69
- `Ship readiness blocked: ${readiness.blockers.join('; ')}`,
70
- {
71
- artifactRefs: reportPath ? [reportPath] : [],
72
- rescueAction,
73
- details: {
74
- blockers: readiness.blockers
75
- }
76
- }
77
- );
78
- }
79
-
80
- module.exports = {
81
- collectShipReadinessBlockers,
82
- deriveShipReadiness,
83
- assertShipReady
84
- };