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.
Files changed (187) hide show
  1. package/.claude/skills/cc-act/CHANGELOG.md +23 -0
  2. package/.claude/skills/cc-act/PLAYBOOK.md +17 -269
  3. package/.claude/skills/cc-act/SKILL.md +38 -418
  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 +24 -0
  15. package/.claude/skills/cc-check/PLAYBOOK.md +19 -273
  16. package/.claude/skills/cc-check/SKILL.md +33 -454
  17. package/.claude/skills/cc-check/references/review-contract.md +12 -147
  18. package/.claude/skills/cc-dev/CHANGELOG.md +20 -0
  19. package/.claude/skills/cc-dev/PLAYBOOK.md +1 -1
  20. package/.claude/skills/cc-dev/SKILL.md +52 -130
  21. package/.claude/skills/cc-dev/scripts/resolve-cc-devflow.sh +181 -0
  22. package/.claude/skills/cc-do/CHANGELOG.md +17 -0
  23. package/.claude/skills/cc-do/PLAYBOOK.md +19 -113
  24. package/.claude/skills/cc-do/SKILL.md +39 -236
  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 +23 -0
  31. package/.claude/skills/cc-investigate/PLAYBOOK.md +20 -180
  32. package/.claude/skills/cc-investigate/SKILL.md +65 -513
  33. package/.claude/skills/cc-investigate/assets/TASKS_TEMPLATE.md +48 -95
  34. package/.claude/skills/cc-investigate/references/investigation-contract.md +14 -217
  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 +29 -0
  39. package/.claude/skills/cc-plan/PLAYBOOK.md +22 -161
  40. package/.claude/skills/cc-plan/SKILL.md +47 -640
  41. package/.claude/skills/cc-plan/assets/TASKS_TEMPLATE.md +30 -225
  42. package/.claude/skills/cc-plan/references/planning-contract.md +24 -160
  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 +17 -0
  50. package/.claude/skills/cc-review/PLAYBOOK.md +13 -86
  51. package/.claude/skills/cc-review/SKILL.md +53 -241
  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 +27 -0
  65. package/README.md +5 -3
  66. package/README.zh-CN.md +5 -3
  67. package/bin/cc-devflow-cli.js +20 -260
  68. package/bin/cc-devflow.js +44 -7
  69. package/docs/commands/README.md +1 -1
  70. package/docs/commands/README.zh-CN.md +1 -1
  71. package/docs/examples/README.md +1 -1
  72. package/docs/examples/START-HERE.md +14 -14
  73. package/docs/examples/example-bindings.json +11 -11
  74. package/docs/examples/full-design-blocked/README.md +4 -6
  75. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/{planning/tasks.md → task.md} +20 -15
  76. package/docs/examples/local-handoff/README.md +8 -11
  77. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/pr-brief.md +31 -0
  78. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/{planning/tasks.md → task.md} +18 -13
  79. package/docs/examples/pdca-loop/README.md +6 -9
  80. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/pr-brief.md +9 -11
  81. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/{planning/tasks.md → task.md} +18 -13
  82. package/docs/examples/scripts/check-example-bindings.sh +11 -62
  83. package/docs/guides/artifact-contract.md +10 -36
  84. package/docs/guides/getting-started.md +8 -7
  85. package/docs/guides/getting-started.zh-CN.md +8 -7
  86. package/docs/guides/minimize-artifacts.md +16 -116
  87. package/docs/guides/project-postmortem.md +14 -71
  88. package/lib/compiler/__tests__/skills-registry.test.js +9 -8
  89. package/lib/compiler/resource-copier.js +29 -0
  90. package/lib/skill-runtime/__tests__/archive-change.test.js +2 -2
  91. package/lib/skill-runtime/__tests__/benchmark-skills.test.js +109 -0
  92. package/lib/skill-runtime/__tests__/cli-bootstrap.integration.test.js +14 -4
  93. package/lib/skill-runtime/errors.js +3 -3
  94. package/lib/skill-runtime/index.js +5 -23
  95. package/lib/skill-runtime/paths.js +5 -52
  96. package/lib/skill-runtime/query-registry.js +4 -4
  97. package/lib/skill-runtime/query.js +89 -201
  98. package/lib/skill-runtime/store.js +4 -40
  99. package/lib/skill-runtime/trace.js +2 -2
  100. package/package.json +5 -7
  101. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_PRINCIPLES_TEMPLATE.md +0 -29
  102. package/.claude/skills/cc-act/assets/RELEASE_NOTE_TEMPLATE.md +0 -54
  103. package/.claude/skills/cc-act/scripts/generate-status-report.sh +0 -92
  104. package/.claude/skills/cc-act/scripts/sync-act-docs.sh +0 -355
  105. package/.claude/skills/cc-check/assets/REPORT_CARD_TEMPLATE.json +0 -234
  106. package/.claude/skills/cc-check/scripts/render-report-card.js +0 -438
  107. package/.claude/skills/cc-check/scripts/verify-gate.sh +0 -85
  108. package/.claude/skills/cc-do/scripts/build-task-context.sh +0 -175
  109. package/.claude/skills/cc-do/scripts/record-review-decision.sh +0 -88
  110. package/.claude/skills/cc-do/scripts/recover-workflow.sh +0 -82
  111. package/.claude/skills/cc-do/scripts/run-problem-analysis.sh +0 -70
  112. package/.claude/skills/cc-do/scripts/verify-task-gates.sh +0 -109
  113. package/.claude/skills/cc-do/scripts/write-task-checkpoint.sh +0 -92
  114. package/.claude/skills/cc-investigate/assets/TASK_MANIFEST_TEMPLATE.json +0 -225
  115. package/.claude/skills/cc-plan/assets/TASK_MANIFEST_TEMPLATE.json +0 -179
  116. package/.claude/skills/cc-spec-init/assets/CHANGE_META_TEMPLATE.json +0 -28
  117. package/.claude/skills/cc-spec-init/scripts/validate-spec-links.sh +0 -45
  118. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/design.md +0 -234
  119. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/task-manifest.json +0 -488
  120. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/review/report-card.json +0 -189
  121. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/resume-index.md +0 -39
  122. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/status.md +0 -29
  123. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/design.md +0 -123
  124. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/task-manifest.json +0 -292
  125. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/review/report-card.json +0 -136
  126. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/status.md +0 -29
  127. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/design.md +0 -124
  128. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/task-manifest.json +0 -292
  129. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/review/report-card.json +0 -136
  130. package/docs/get-shit-done-strategy-audit.md +0 -518
  131. package/docs/skill-runtime-migration.md +0 -46
  132. package/lib/skill-runtime/__tests__/approve.test.js +0 -92
  133. package/lib/skill-runtime/__tests__/autopilot.test.js +0 -253
  134. package/lib/skill-runtime/__tests__/benchmark-artifacts.test.js +0 -165
  135. package/lib/skill-runtime/__tests__/delegation.test.js +0 -97
  136. package/lib/skill-runtime/__tests__/dispatch.test.js +0 -237
  137. package/lib/skill-runtime/__tests__/intent.test.js +0 -203
  138. package/lib/skill-runtime/__tests__/lifecycle.test.js +0 -169
  139. package/lib/skill-runtime/__tests__/planner.tdd.test.js +0 -331
  140. package/lib/skill-runtime/__tests__/prepare-pr.test.js +0 -126
  141. package/lib/skill-runtime/__tests__/query.test.js +0 -860
  142. package/lib/skill-runtime/__tests__/readiness.test.js +0 -53
  143. package/lib/skill-runtime/__tests__/release.test.js +0 -85
  144. package/lib/skill-runtime/__tests__/review-check-integration.test.js +0 -148
  145. package/lib/skill-runtime/__tests__/review-records.test.js +0 -619
  146. package/lib/skill-runtime/__tests__/runtime.integration.test.js +0 -351
  147. package/lib/skill-runtime/__tests__/schemas.test.js +0 -337
  148. package/lib/skill-runtime/__tests__/task-contract-migrate.test.js +0 -137
  149. package/lib/skill-runtime/__tests__/task-contract.test.js +0 -783
  150. package/lib/skill-runtime/__tests__/team-state.test.js +0 -51
  151. package/lib/skill-runtime/__tests__/verify-artifacts.test.js +0 -203
  152. package/lib/skill-runtime/__tests__/worker-run.test.js +0 -275
  153. package/lib/skill-runtime/__tests__/worker.test.js +0 -56
  154. package/lib/skill-runtime/__tests__/workflow-context-legacy-fallback.test.js +0 -31
  155. package/lib/skill-runtime/__tests__/workflow-context.test.js +0 -98
  156. package/lib/skill-runtime/artifacts.js +0 -88
  157. package/lib/skill-runtime/context-index.js +0 -545
  158. package/lib/skill-runtime/delegation.js +0 -533
  159. package/lib/skill-runtime/intent.js +0 -309
  160. package/lib/skill-runtime/lifecycle.js +0 -294
  161. package/lib/skill-runtime/operations/CLAUDE.md +0 -19
  162. package/lib/skill-runtime/operations/approve.js +0 -81
  163. package/lib/skill-runtime/operations/autopilot-core.js +0 -337
  164. package/lib/skill-runtime/operations/autopilot-execution.js +0 -307
  165. package/lib/skill-runtime/operations/autopilot-shared.js +0 -48
  166. package/lib/skill-runtime/operations/autopilot.js +0 -163
  167. package/lib/skill-runtime/operations/dispatch.js +0 -416
  168. package/lib/skill-runtime/operations/init.js +0 -60
  169. package/lib/skill-runtime/operations/janitor.js +0 -61
  170. package/lib/skill-runtime/operations/plan.js +0 -59
  171. package/lib/skill-runtime/operations/prepare-pr.js +0 -25
  172. package/lib/skill-runtime/operations/release.js +0 -99
  173. package/lib/skill-runtime/operations/resume.js +0 -126
  174. package/lib/skill-runtime/operations/review-records.js +0 -265
  175. package/lib/skill-runtime/operations/snapshot.js +0 -45
  176. package/lib/skill-runtime/operations/task-contract.js +0 -524
  177. package/lib/skill-runtime/operations/verify.js +0 -170
  178. package/lib/skill-runtime/operations/worker-run.js +0 -531
  179. package/lib/skill-runtime/operations/worker.js +0 -33
  180. package/lib/skill-runtime/planner.js +0 -539
  181. package/lib/skill-runtime/readiness.js +0 -84
  182. package/lib/skill-runtime/review-records.js +0 -123
  183. package/lib/skill-runtime/review.js +0 -855
  184. package/lib/skill-runtime/schemas.js +0 -746
  185. package/lib/skill-runtime/task-contract.js +0 -187
  186. package/lib/skill-runtime/team-state.js +0 -122
  187. package/lib/skill-runtime/workflow-context.js +0 -748
@@ -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
- };