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,524 +0,0 @@
1
- /**
2
- * [INPUT]: 接收 repoRoot/changeId/changeKey,读取 planning/tasks.md。
3
- * [OUTPUT]: 对外提供 task-contract compile / validate / migrate,生成、检查、迁移最小 workflow artifacts。
4
- * [POS]: REQ-003 task-contract CLI operation 层。
5
- * [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
6
- */
7
-
8
- const { createTaskManifest } = require('../planner');
9
- const fsp = require('fs/promises');
10
- const path = require('path');
11
- const { getChangePaths } = require('../paths');
12
- const {
13
- extractTasksContractSummary,
14
- extractTasksRootCauseContract
15
- } = require('../task-contract');
16
- const {
17
- exists,
18
- ensureDir,
19
- getTaskManifestPath,
20
- getTasksMarkdownPath,
21
- readText,
22
- readJson,
23
- writeText,
24
- writeJson
25
- } = require('../store');
26
-
27
- const GENERATED_BY = 'cc-devflow task-contract';
28
- const ACCEPTED_GENERATORS = new Set(['cc-devflow task-contract', 'skill:cc-plan']);
29
- const PROFILE_BUDGETS = {
30
- tiny: 1200,
31
- standard: 2500,
32
- deep: 5000
33
- };
34
-
35
- function listField(value) {
36
- if (Array.isArray(value)) return value;
37
- return value ? [value] : [];
38
- }
39
-
40
- function buildChangeMeta(change, fields) {
41
- const contractChange = fields.change || change.changeKey;
42
- return {
43
- changeId: contractChange,
44
- requirementId: contractChange,
45
- goal: listField(fields.goal),
46
- acceptance: listField(fields.acceptance),
47
- specReference: {
48
- source: 'planning/tasks.md#contract-summary',
49
- change: contractChange,
50
- mode: fields.mode || '',
51
- profile: fields.profile || '',
52
- approval: fields.approval || ''
53
- },
54
- _meta: {
55
- generatedBy: GENERATED_BY,
56
- generatedAt: new Date().toISOString()
57
- }
58
- };
59
- }
60
-
61
- async function relativeExists(change, relativePath) {
62
- return exists(path.join(change.changeDir, relativePath));
63
- }
64
-
65
- async function readJsonSafe(filePath) {
66
- try {
67
- return await readJson(filePath, null);
68
- } catch {
69
- return null;
70
- }
71
- }
72
-
73
- function estimateTokens(text) {
74
- return Math.ceil(String(text || '').length / 4);
75
- }
76
-
77
- function contractProfile(tasksText) {
78
- const contract = extractTasksContractSummary(tasksText);
79
- if (contract.found) return contract.fields.profile || 'standard';
80
- const rootCause = extractTasksRootCauseContract(tasksText);
81
- if (rootCause.found) return rootCause.fields.profile || 'standard';
82
- return 'standard';
83
- }
84
-
85
- function hasAnyContract(tasksText) {
86
- return extractTasksContractSummary(tasksText).found || extractTasksRootCauseContract(tasksText).found;
87
- }
88
-
89
- function countTracerSlices(tasksText) {
90
- const matches = [...String(tasksText || '').matchAll(/Vertical slice:\s*([^\n]+)/gi)];
91
- if (matches.length > 0) {
92
- return new Set(matches.map((match) => match[1].trim())).size;
93
- }
94
- return [...String(tasksText || '').matchAll(/^- \[[ xX]\]\s+T\d{3}\b/gm)].length;
95
- }
96
-
97
- function quoteLegacyMarkdown(text) {
98
- return String(text || '')
99
- .replace(/\r\n/g, '\n')
100
- .replace(/\n*$/, '')
101
- .split('\n')
102
- .map((line) => (line.length > 0 ? `> ${line}` : '>'))
103
- .join('\n');
104
- }
105
-
106
- function appendToSection(tasksText, heading, addition) {
107
- const lines = String(tasksText || '').replace(/\r\n/g, '\n').split('\n');
108
- const start = lines.findIndex((line) => line.trimEnd() === heading);
109
- if (start === -1) return '';
110
- let end = start + 1;
111
- while (end < lines.length && !lines[end].startsWith('## ')) end += 1;
112
- return [...lines.slice(0, end), '', addition, '', ...lines.slice(end)].join('\n').replace(/\n*$/, '\n');
113
- }
114
-
115
- function insertAfterTitle(tasksText, section) {
116
- const lines = String(tasksText || '').replace(/\r\n/g, '\n').split('\n');
117
- if (!lines[0]?.startsWith('# ')) {
118
- return `${section}\n\n${String(tasksText || '').replace(/\n*$/, '\n')}`;
119
- }
120
- let tailStart = 1;
121
- while (lines[tailStart] === '') tailStart += 1;
122
- return [lines[0], '', section, '', ...lines.slice(tailStart)].join('\n').replace(/\n*$/, '\n');
123
- }
124
-
125
- function upsertContractSection(tasksText, heading, section, addition) {
126
- return appendToSection(tasksText, heading, addition) || insertAfterTitle(tasksText, section);
127
- }
128
-
129
- function legacyBlock(label, content) {
130
- return [`${label}:`, quoteLegacyMarkdown(content)].join('\n');
131
- }
132
-
133
- function designContractSection(change, content) {
134
- const block = legacyBlock('Legacy Design', content);
135
- return [
136
- '## Contract Summary',
137
- '',
138
- `Change: ${change.changeKey}`,
139
- 'Mode: migrated',
140
- 'Profile: standard',
141
- 'Approval: migrated',
142
- '',
143
- 'Goal:',
144
- '- Preserve the legacy planning contract inside tasks.md.',
145
- '',
146
- 'Do Not Do:',
147
- '- Do not auto-run this migration during compile.',
148
- '',
149
- 'Approved Direction:',
150
- '- Use tasks.md as the canonical human-authored contract after migration.',
151
- '',
152
- 'Acceptance:',
153
- '- Legacy planning/design.md content is folded into this section.',
154
- '- planning/design.md is archived to assets/legacy/design.md.bak.',
155
- '',
156
- 'Verification:',
157
- '',
158
- '```bash',
159
- 'npm test -- -t "task-contract migrate"',
160
- '```',
161
- '',
162
- 'Risk / Escalate If:',
163
- '- Review migrated content before deleting any legacy references.',
164
- '',
165
- block
166
- ].join('\n');
167
- }
168
-
169
- function analysisContractSection(change, content) {
170
- const block = legacyBlock('Legacy Analysis', content);
171
- return [
172
- '## Root Cause Contract',
173
- '',
174
- `Change: ${change.changeKey}`,
175
- 'Mode: migrated',
176
- 'Profile: standard',
177
- 'Diagnosis: Migrated from legacy planning/analysis.md.',
178
- '',
179
- 'Symptom:',
180
- '- Legacy investigation contract lived outside tasks.md.',
181
- '',
182
- 'Reproduction:',
183
- '- Run task-contract migrate with --fold-analysis.',
184
- '',
185
- 'Expected:',
186
- '- tasks.md owns the root-cause contract.',
187
- '',
188
- 'Actual:',
189
- '- planning/analysis.md was the legacy contract file.',
190
- '',
191
- 'Root Cause:',
192
- '- The pre-minimization workflow used a separate analysis.md artifact.',
193
- '',
194
- 'Evidence Chain:',
195
- '- Migrated content is quoted below.',
196
- '',
197
- 'Repair Boundary:',
198
- '- Archive only the requested legacy analysis file.',
199
- '',
200
- 'Prevention:',
201
- '- Keep future root-cause contracts in tasks.md.',
202
- '',
203
- 'Risk / Escalate If:',
204
- '- Review migrated content before deleting any legacy references.',
205
- '',
206
- block
207
- ].join('\n');
208
- }
209
-
210
- function firstViolation(violations) {
211
- if (violations.length === 0) {
212
- return {
213
- code: 0,
214
- ok: true,
215
- violations: [],
216
- stderr: ''
217
- };
218
- }
219
- const code = violations[0].exitCode;
220
- return {
221
- code,
222
- ok: false,
223
- violations,
224
- stderr: violations.map((violation) => `${violation.ruleId}: ${violation.message}`).join('\n')
225
- };
226
- }
227
-
228
- const VALIDATE_RULES = [
229
- {
230
- id: 'C1',
231
- exitCode: 2,
232
- check: async ({ change }) => (await relativeExists(change, 'planning/design.md'))
233
- ? 'planning/design.md is a legacy default artifact'
234
- : ''
235
- },
236
- {
237
- id: 'C2',
238
- exitCode: 2,
239
- check: async ({ change }) => (await relativeExists(change, 'planning/analysis.md'))
240
- ? 'planning/analysis.md is a legacy default artifact'
241
- : ''
242
- },
243
- {
244
- id: 'C3',
245
- exitCode: 2,
246
- check: async ({ change }) => {
247
- const legacy = [];
248
- if (await relativeExists(change, 'review/cc-review-report.md')) legacy.push('review/cc-review-report.md');
249
- if (await relativeExists(change, 'review/cc-review-plan.md')) legacy.push('review/cc-review-plan.md');
250
- return legacy.length > 0 ? `legacy review Markdown exists: ${legacy.join(', ')}` : '';
251
- }
252
- },
253
- {
254
- id: 'C4',
255
- exitCode: 3,
256
- check: async ({ tasksText }) => hasAnyContract(tasksText)
257
- ? ''
258
- : 'planning/tasks.md is missing ## Contract Summary or ## Root Cause Contract'
259
- },
260
- {
261
- id: 'C5',
262
- exitCode: 3,
263
- check: async ({ manifestPath }) => {
264
- const manifest = await readJsonSafe(manifestPath);
265
- const generator = manifest?.metadata?.generatedBy || '';
266
- return ACCEPTED_GENERATORS.has(generator) ? '' : `task-manifest.json generatedBy is unknown: ${generator || '<missing>'}`;
267
- }
268
- },
269
- {
270
- id: 'C6',
271
- exitCode: 3,
272
- check: async ({ changeMetaPath }) => {
273
- const changeMeta = await readJsonSafe(changeMetaPath);
274
- const generator = changeMeta?._meta?.generatedBy || '';
275
- return ACCEPTED_GENERATORS.has(generator) ? '' : `change-meta.json _meta.generatedBy is unknown: ${generator || '<missing>'}`;
276
- }
277
- },
278
- {
279
- id: 'C7',
280
- exitCode: 4,
281
- check: async ({ tasksText }) => {
282
- const profile = contractProfile(tasksText);
283
- const slices = countTracerSlices(tasksText);
284
- return profile === 'tiny' && slices > 1 ? `tiny profile has ${slices} tracer slices` : '';
285
- }
286
- },
287
- {
288
- id: 'C8',
289
- exitCode: 5,
290
- check: async ({ change }) => {
291
- const ledgerPath = path.join(change.changeDir, 'review', 'review-ledger.jsonl');
292
- if (!(await exists(ledgerPath))) return '';
293
- const lines = (await readText(ledgerPath)).split(/\r?\n/).filter(Boolean);
294
- const missing = lines.some((line) => {
295
- try {
296
- return !JSON.parse(line).createdBy;
297
- } catch {
298
- return true;
299
- }
300
- });
301
- return missing ? 'review-ledger.jsonl has a row without createdBy' : '';
302
- }
303
- },
304
- {
305
- id: 'C9',
306
- exitCode: 5,
307
- check: async ({ change }) => {
308
- const reportPath = path.join(change.changeDir, 'review', 'report-card.json');
309
- if (!(await exists(reportPath))) return '';
310
- const report = await readJsonSafe(reportPath);
311
- const generator = report?._meta?.generatedBy || '';
312
- return ACCEPTED_GENERATORS.has(generator) ? '' : `report-card.json generatedBy is unknown: ${generator || '<missing>'}`;
313
- }
314
- },
315
- {
316
- id: 'C10',
317
- exitCode: 6,
318
- check: async ({ tasksText }) => {
319
- const profile = contractProfile(tasksText);
320
- const budget = PROFILE_BUDGETS[profile] || PROFILE_BUDGETS.standard;
321
- const tokens = estimateTokens(tasksText);
322
- return tokens > budget ? `tasks.md token estimate ${tokens} exceeds ${profile} budget ${budget}` : '';
323
- }
324
- }
325
- ];
326
-
327
- async function runCompile({
328
- repoRoot,
329
- changeId,
330
- changeKey,
331
- goal,
332
- overwrite = true
333
- }) {
334
- const pathOptions = changeKey ? { changeKey } : {};
335
- const change = getChangePaths(repoRoot, changeId, pathOptions);
336
- const tasksPath = getTasksMarkdownPath(repoRoot, changeId, { changeKey: change.changeKey });
337
-
338
- if (!(await exists(tasksPath))) {
339
- return {
340
- code: 3,
341
- changeId,
342
- changeKey: change.changeKey,
343
- error: `Missing planning/tasks.md: ${tasksPath}`
344
- };
345
- }
346
-
347
- const contract = extractTasksContractSummary(await readText(tasksPath));
348
- if (!contract.found) {
349
- return {
350
- code: 3,
351
- changeId,
352
- changeKey: change.changeKey,
353
- error: `Missing ## Contract Summary in planning/tasks.md: ${tasksPath}`
354
- };
355
- }
356
-
357
- await createTaskManifest({
358
- repoRoot,
359
- changeId,
360
- changeKey: change.changeKey,
361
- goal,
362
- overwrite
363
- });
364
-
365
- const manifestPath = getTaskManifestPath(repoRoot, changeId, { changeKey: change.changeKey });
366
- const changeMetaPath = path.join(change.changeDir, 'change-meta.json');
367
- const manifest = await readJson(manifestPath);
368
- const compiled = {
369
- ...manifest,
370
- metadata: {
371
- ...manifest.metadata,
372
- source: 'tasks.md',
373
- generatedBy: GENERATED_BY
374
- }
375
- };
376
-
377
- await writeJson(manifestPath, compiled);
378
- await writeJson(changeMetaPath, buildChangeMeta(change, contract.fields));
379
-
380
- return {
381
- code: 0,
382
- changeId,
383
- changeKey: change.changeKey,
384
- manifestPath,
385
- changeMetaPath,
386
- metadata: compiled.metadata
387
- };
388
- }
389
-
390
- async function runValidate({ repoRoot, changeId, changeKey }) {
391
- const pathOptions = changeKey ? { changeKey } : {};
392
- const change = getChangePaths(repoRoot, changeId, pathOptions);
393
- const tasksPath = getTasksMarkdownPath(repoRoot, changeId, { changeKey: change.changeKey });
394
- const manifestPath = getTaskManifestPath(repoRoot, changeId, { changeKey: change.changeKey });
395
- const changeMetaPath = path.join(change.changeDir, 'change-meta.json');
396
- const tasksText = await readText(tasksPath);
397
- const context = { change, tasksPath, manifestPath, changeMetaPath, tasksText };
398
- const violations = [];
399
-
400
- for (const rule of VALIDATE_RULES) {
401
- const message = await rule.check(context);
402
- if (message) {
403
- violations.push({ ruleId: rule.id, exitCode: rule.exitCode, message });
404
- }
405
- }
406
-
407
- return {
408
- ...firstViolation(violations),
409
- changeId,
410
- changeKey: change.changeKey
411
- };
412
- }
413
-
414
- async function prepareLegacyFold(change, tasksText, config) {
415
- const sourcePath = path.join(change.changeDir, 'planning', config.fileName);
416
- if (!(await exists(sourcePath))) {
417
- return { code: 3, error: `Missing planning/${config.fileName}: ${sourcePath}` };
418
- }
419
-
420
- let legacyText;
421
- try {
422
- legacyText = await readText(sourcePath);
423
- } catch (error) {
424
- return { code: 4, error: `Unable to read planning/${config.fileName}: ${error.message}` };
425
- }
426
-
427
- const archivePath = path.join(change.changeDir, 'assets', 'legacy', `${config.fileName}.bak`);
428
- if (await exists(archivePath)) {
429
- return { code: 4, error: `Legacy archive already exists: ${archivePath}` };
430
- }
431
-
432
- return {
433
- code: 0,
434
- fileName: config.fileName,
435
- sourcePath,
436
- archivePath,
437
- tasksText: upsertContractSection(
438
- tasksText,
439
- config.heading,
440
- config.section(change, legacyText),
441
- legacyBlock(config.label, legacyText)
442
- )
443
- };
444
- }
445
-
446
- async function runMigrate({
447
- repoRoot,
448
- changeId,
449
- changeKey,
450
- foldDesign = false,
451
- foldAnalysis = false
452
- }) {
453
- if (!foldDesign && !foldAnalysis) {
454
- return {
455
- code: 3,
456
- changeId,
457
- changeKey,
458
- error: 'task-contract migrate requires --fold-design and/or --fold-analysis'
459
- };
460
- }
461
-
462
- const pathOptions = changeKey ? { changeKey } : {};
463
- const change = getChangePaths(repoRoot, changeId, pathOptions);
464
- const tasksPath = getTasksMarkdownPath(repoRoot, changeId, { changeKey: change.changeKey });
465
- if (!(await exists(tasksPath))) {
466
- return {
467
- code: 3,
468
- changeId,
469
- changeKey: change.changeKey,
470
- error: `Missing planning/tasks.md: ${tasksPath}`
471
- };
472
- }
473
-
474
- let tasksText = await readText(tasksPath);
475
- const folds = [];
476
- if (foldDesign) {
477
- folds.push({
478
- fileName: 'design.md',
479
- heading: '## Contract Summary',
480
- label: 'Legacy Design',
481
- section: designContractSection
482
- });
483
- }
484
- if (foldAnalysis) {
485
- folds.push({
486
- fileName: 'analysis.md',
487
- heading: '## Root Cause Contract',
488
- label: 'Legacy Analysis',
489
- section: analysisContractSection
490
- });
491
- }
492
-
493
- const prepared = [];
494
- for (const fold of folds) {
495
- const result = await prepareLegacyFold(change, tasksText, fold);
496
- if (result.code !== 0) {
497
- return { ...result, changeId, changeKey: change.changeKey };
498
- }
499
- tasksText = result.tasksText;
500
- prepared.push(result);
501
- }
502
-
503
- await writeText(tasksPath, tasksText);
504
- for (const fold of prepared) {
505
- await ensureDir(path.dirname(fold.archivePath));
506
- await fsp.rename(fold.sourcePath, fold.archivePath);
507
- }
508
-
509
- return {
510
- code: 0,
511
- changeId,
512
- changeKey: change.changeKey,
513
- tasksPath,
514
- migrated: prepared.map((fold) => fold.fileName),
515
- archives: prepared.map((fold) => fold.archivePath)
516
- };
517
- }
518
-
519
- module.exports = {
520
- GENERATED_BY,
521
- runCompile,
522
- runValidate,
523
- runMigrate
524
- };
@@ -1,170 +0,0 @@
1
- /**
2
- * [INPUT]: 依赖 manifest 与 package scripts,依赖 shell gates(lint/typecheck/test/audit/review)。
3
- * [OUTPUT]: 生成 report-card.json,给出 quick/strict/review 门禁结论。
4
- * [POS]: skill runtime 质量门禁入口,供内部验证链路复用。
5
- * [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
6
- */
7
-
8
- const {
9
- nowIso,
10
- getPackageScripts,
11
- readJson,
12
- writeJson,
13
- runCommand,
14
- getTaskManifestPath,
15
- getRuntimeStatePath,
16
- getReportCardPath
17
- } = require('../store');
18
- const { parseManifest, parseReportCard } = require('../schemas');
19
- const { runReviewSuite } = require('../review');
20
-
21
- const path = require('path');
22
- const fs = require('fs');
23
- const os = require('os');
24
-
25
- const REQ_CHECK_DIR = path.join(__dirname, '..', '..', '..', '.claude', 'skills', 'cc-check');
26
- const RUN_GATES_SCRIPT = path.join(REQ_CHECK_DIR, 'scripts', 'run-quality-gates.sh');
27
- const RENDER_REPORT_SCRIPT = path.join(REQ_CHECK_DIR, 'scripts', 'render-report-card.js');
28
-
29
- function shellEscape(value) {
30
- return `'${String(value).replace(/'/g, `'\\''`)}'`;
31
- }
32
-
33
- async function runGateCollection({ repoRoot, definitions }) {
34
- if (!definitions || definitions.length === 0) {
35
- return [];
36
- }
37
-
38
- const args = definitions
39
- .map((item) => `--gate ${shellEscape(`${item.name}::${item.command}`)}`)
40
- .join(' ');
41
- const result = await runCommand(`bash ${shellEscape(RUN_GATES_SCRIPT)} ${args}`, {
42
- cwd: repoRoot,
43
- timeoutMs: 20 * 60 * 1000
44
- });
45
-
46
- if (result.code !== 0) {
47
- throw new Error(result.stderr || result.stdout || 'run-quality-gates.sh failed');
48
- }
49
-
50
- return JSON.parse(result.stdout || '[]');
51
- }
52
-
53
- async function renderReportCard({ repoRoot, changeId, manifestPath, quickGates, strictGates, review }) {
54
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-cc-check-'));
55
- const quickPath = path.join(tempDir, 'quick-gates.json');
56
- const strictPath = path.join(tempDir, 'strict-gates.json');
57
- const reviewPath = path.join(tempDir, 'review.json');
58
-
59
- try {
60
- fs.writeFileSync(quickPath, `${JSON.stringify(quickGates, null, 2)}\n`);
61
- fs.writeFileSync(strictPath, `${JSON.stringify(strictGates, null, 2)}\n`);
62
- fs.writeFileSync(reviewPath, `${JSON.stringify(review, null, 2)}\n`);
63
-
64
- const command = [
65
- 'node',
66
- shellEscape(RENDER_REPORT_SCRIPT),
67
- '--change-id',
68
- shellEscape(changeId),
69
- '--manifest',
70
- shellEscape(manifestPath),
71
- '--quick',
72
- shellEscape(quickPath),
73
- '--strict',
74
- shellEscape(strictPath),
75
- '--review',
76
- shellEscape(reviewPath),
77
- '--timestamp',
78
- shellEscape(nowIso())
79
- ].join(' ');
80
-
81
- const result = await runCommand(command, { cwd: repoRoot, timeoutMs: 20 * 60 * 1000 });
82
- if (result.code !== 0) {
83
- throw new Error(result.stderr || result.stdout || 'render-report-card.js failed');
84
- }
85
-
86
- return JSON.parse(result.stdout);
87
- } finally {
88
- fs.rmSync(tempDir, { recursive: true, force: true });
89
- }
90
- }
91
-
92
- async function runVerify({ repoRoot, changeId, strict = false, skipReview = false }) {
93
- const manifestPath = getTaskManifestPath(repoRoot, changeId);
94
- const manifest = parseManifest(await readJson(manifestPath));
95
- const scripts = await getPackageScripts(repoRoot);
96
-
97
- const quickGates = await runGateCollection({
98
- repoRoot,
99
- definitions: ['lint', 'typecheck', 'test'].map((name) => ({
100
- name,
101
- command: scripts[name]
102
- ? `npm run ${name}`
103
- : `printf '__CC_DEVFLOW_SKIP__ Script ${name} is not defined\n'`
104
- }))
105
- });
106
-
107
- const strictGates = strict
108
- ? await runGateCollection({
109
- repoRoot,
110
- definitions: [
111
- {
112
- name: 'test:integration',
113
- command: scripts['test:integration']
114
- ? 'npm run test:integration'
115
- : "printf '__CC_DEVFLOW_SKIP__ Script test:integration is not defined\n'"
116
- },
117
- {
118
- name: 'audit',
119
- command: 'npm audit --audit-level=high'
120
- }
121
- ]
122
- })
123
- : [];
124
-
125
- const review = await runReviewSuite({
126
- repoRoot,
127
- changeId,
128
- manifest,
129
- strict,
130
- skipReview
131
- });
132
- const report = parseReportCard(
133
- await renderReportCard({
134
- repoRoot,
135
- changeId,
136
- manifestPath,
137
- quickGates,
138
- strictGates,
139
- review
140
- })
141
- );
142
-
143
- const outputPath = getReportCardPath(repoRoot, changeId);
144
- await writeJson(outputPath, report);
145
-
146
- // Update change-state.json with verifiedAt timestamp if passed
147
- if (report.verdict === 'pass') {
148
- const statePath = getRuntimeStatePath(repoRoot, changeId);
149
- const stateExists = require('fs').existsSync(statePath);
150
- if (stateExists) {
151
- const state = await readJson(statePath);
152
- state.status = 'verified';
153
- state.verifiedAt = nowIso();
154
- state.updatedAt = nowIso();
155
- await writeJson(statePath, state);
156
- }
157
- }
158
-
159
- return {
160
- changeId,
161
- outputPath,
162
- overall: report.overall,
163
- verdict: report.verdict || (report.overall === 'pass' ? 'pass' : 'fail'),
164
- blockingFindings: report.blockingFindings
165
- };
166
- }
167
-
168
- module.exports = {
169
- runVerify
170
- };