sillyspec 3.20.2 → 3.20.3

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 (208) hide show
  1. package/.claude/skills/sillyspec-archive/SKILL.md +1 -5
  2. package/.claude/skills/sillyspec-auto/SKILL.md +2 -8
  3. package/.claude/skills/sillyspec-brainstorm/SKILL.md +1 -28
  4. package/.claude/skills/sillyspec-commit/SKILL.md +3 -4
  5. package/.claude/skills/sillyspec-continue/SKILL.md +4 -5
  6. package/.claude/skills/sillyspec-doctor/SKILL.md +3 -12
  7. package/.claude/skills/sillyspec-execute/SKILL.md +2 -15
  8. package/.claude/skills/sillyspec-explore/SKILL.md +1 -14
  9. package/.claude/skills/sillyspec-plan/SKILL.md +36 -5
  10. package/.claude/skills/sillyspec-propose/SKILL.md +0 -4
  11. package/.claude/skills/sillyspec-quick/SKILL.md +1 -5
  12. package/.claude/skills/sillyspec-resume/SKILL.md +66 -23
  13. package/.claude/skills/sillyspec-scan/SKILL.md +1 -5
  14. package/.claude/skills/sillyspec-state/SKILL.md +8 -8
  15. package/.claude/skills/sillyspec-status/SKILL.md +1 -5
  16. package/.claude/skills/sillyspec-verify/SKILL.md +1 -5
  17. package/.claude/skills/sillyspec-workspace/SKILL.md +3 -11
  18. package/.sillyspec/changes/archive/2026-04-08-derive-state/design.md +97 -0
  19. package/.sillyspec/changes/archive/2026-04-08-derive-state/plan.md +51 -0
  20. package/.sillyspec/changes/archive/2026-04-08-derive-state/proposal.md +29 -0
  21. package/.sillyspec/changes/archive/2026-04-08-derive-state/requirements.md +34 -0
  22. package/.sillyspec/changes/archive/2026-04-08-derive-state/tasks.md +13 -0
  23. package/.sillyspec/changes/archive/2026-04-08-derive-state/verify-result.md +43 -0
  24. package/.sillyspec/changes/auto-mode/design.md +50 -0
  25. package/.sillyspec/changes/auto-mode/proposal.md +19 -0
  26. package/.sillyspec/changes/auto-mode/requirements.md +21 -0
  27. package/.sillyspec/changes/auto-mode/tasks.md +7 -0
  28. package/.sillyspec/changes/brainstorm-archive/2026-04-05-dashboard-design.md +206 -0
  29. package/.sillyspec/changes/brainstorm-archive/2026-04-05-unified-docs-design.md +199 -0
  30. package/.sillyspec/changes/dashboard/design.md +219 -0
  31. package/.sillyspec/changes/dashboard/design.md.braindraft +206 -0
  32. package/.sillyspec/changes/run-command-design/design.md +1230 -0
  33. package/.sillyspec/changes/unified-docs-design/design.md +199 -0
  34. package/.sillyspec/docs/sillyspec/scan/.gitkeep +0 -0
  35. package/.sillyspec/knowledge/INDEX.md +8 -0
  36. package/.sillyspec/knowledge/uncategorized.md +3 -0
  37. package/.sillyspec/plans/2026-04-05-dashboard.md +737 -0
  38. package/.sillyspec/projects/sillyspec.yaml +3 -0
  39. package/README.md +11 -13
  40. package/SKILL.md +40 -44
  41. package/dist/steps/brainstorm/01-load-context.md +30 -0
  42. package/dist/steps/brainstorm/02-reuse-check.md +6 -0
  43. package/dist/steps/brainstorm/03-prototype-analysis.md +11 -0
  44. package/dist/steps/brainstorm/04-module-split.md +23 -0
  45. package/dist/steps/brainstorm/05-dialog-explore.md +8 -0
  46. package/dist/steps/brainstorm/06-propose-approaches.md +3 -0
  47. package/dist/steps/brainstorm/07-present-design.md +3 -0
  48. package/dist/steps/brainstorm/08-write-design.md +21 -0
  49. package/dist/steps/brainstorm/09-self-review.md +15 -0
  50. package/dist/steps/brainstorm/10-user-confirm.md +3 -0
  51. package/dist/steps/brainstorm/11-output-spec.md +7 -0
  52. package/dist/steps/brainstorm/manifest.yaml +26 -0
  53. package/dist/steps/execute/01-load-context.md +41 -0
  54. package/dist/steps/execute/02-scan-conventions.md +47 -0
  55. package/dist/steps/execute/03-skill-mcp.md +19 -0
  56. package/dist/steps/execute/04-assign-task.md +22 -0
  57. package/dist/steps/execute/04b-prompt-template.md +54 -0
  58. package/dist/steps/execute/05-write-test.md +7 -0
  59. package/dist/steps/execute/06-write-code.md +8 -0
  60. package/dist/steps/execute/07-run-test.md +26 -0
  61. package/dist/steps/execute/08-fix-issues.md +28 -0
  62. package/dist/steps/execute/09-next-task.md +33 -0
  63. package/dist/steps/execute/manifest.yaml +28 -0
  64. package/dist/steps/plan/01-load-context.md +22 -0
  65. package/dist/steps/plan/02-anchor-confirm.md +1 -0
  66. package/dist/steps/plan/03-expand-tasks.md +33 -0
  67. package/dist/steps/plan/04-mark-order.md +15 -0
  68. package/dist/steps/plan/05-e2e-planning.md +17 -0
  69. package/dist/steps/plan/06-self-check.md +16 -0
  70. package/dist/steps/plan/07-save.md +1 -0
  71. package/dist/steps/plan/manifest.yaml +18 -0
  72. package/dist/steps/scan/01-env-detect.md +51 -0
  73. package/dist/steps/scan/02-tech-stack.md +16 -0
  74. package/dist/steps/scan/03-conventions.md +16 -0
  75. package/dist/steps/scan/04-structure.md +19 -0
  76. package/dist/steps/scan/05-quality.md +18 -0
  77. package/dist/steps/scan/06-complete.md +49 -0
  78. package/dist/steps/scan/manifest.yaml +16 -0
  79. package/dist/steps/verify/01-load-specs.md +28 -0
  80. package/dist/steps/verify/02-check-tasks.md +1 -0
  81. package/dist/steps/verify/03-check-design.md +6 -0
  82. package/dist/steps/verify/04-run-tests.md +7 -0
  83. package/dist/steps/verify/05-e2e-tests.md +27 -0
  84. package/dist/steps/verify/05b-e2e-fix.md +33 -0
  85. package/dist/steps/verify/06-code-quality.md +25 -0
  86. package/dist/steps/verify/07-lint-check.md +27 -0
  87. package/dist/steps/verify/08-output-report.md +14 -0
  88. package/dist/steps/verify/manifest.yaml +22 -0
  89. package/package.json +1 -7
  90. package/packages/dashboard/dist/assets/{index-Bq_Z2hne.js → index-D1EVTLmc.js} +1264 -1264
  91. package/packages/dashboard/dist/assets/index-DGe8CqeP.css +1 -0
  92. package/packages/dashboard/dist/index.html +2 -2
  93. package/packages/dashboard/package-lock.json +6 -6
  94. package/packages/dashboard/server/executor.js +1 -1
  95. package/packages/dashboard/server/index.js +18 -98
  96. package/packages/dashboard/server/parser.js +71 -139
  97. package/packages/dashboard/server/watcher.js +6 -14
  98. package/packages/dashboard/src/App.vue +185 -418
  99. package/packages/dashboard/src/components/ActionBar.vue +1 -10
  100. package/packages/dashboard/src/components/CommandPalette.vue +1 -5
  101. package/packages/dashboard/src/components/DocPreview.vue +8 -105
  102. package/packages/dashboard/src/components/DocTree.vue +19 -75
  103. package/packages/dashboard/src/components/PipelineStage.vue +2 -22
  104. package/packages/dashboard/src/components/PipelineView.vue +5 -32
  105. package/packages/dashboard/src/components/ProjectOverview.vue +139 -113
  106. package/packages/dashboard/src/components/StageBadge.vue +3 -17
  107. package/packages/dashboard/src/components/StepCard.vue +2 -7
  108. package/packages/dashboard/src/composables/useDashboard.js +0 -28
  109. package/src/derive.js +147 -0
  110. package/src/index.js +40 -688
  111. package/src/init.js +63 -119
  112. package/src/migrate.js +7 -7
  113. package/src/progress.js +248 -1474
  114. package/src/run.js +302 -3008
  115. package/src/setup.js +64 -2
  116. package/src/stages/archive.js +17 -123
  117. package/src/stages/brainstorm.js +48 -454
  118. package/src/stages/doctor.js +46 -99
  119. package/src/stages/execute.js +59 -420
  120. package/src/stages/index.js +18 -12
  121. package/src/stages/plan.js +189 -492
  122. package/src/stages/propose.js +11 -70
  123. package/src/stages/quick.js +13 -52
  124. package/src/stages/scan.js +68 -485
  125. package/src/stages/status.js +1 -1
  126. package/src/stages/verify.js +16 -203
  127. package/src/step.js +543 -0
  128. package/.claude/skills/sillyspec-knowledge/SKILL.md +0 -270
  129. package/.husky/pre-push +0 -13
  130. package/CLAUDE.md +0 -18
  131. package/docs/brainstorm-plan-contract.md +0 -64
  132. package/docs/plan-execute-contract.md +0 -123
  133. package/docs/platform-scan-protocol.md +0 -298
  134. package/docs/revision-mode.md +0 -115
  135. package/docs/sillyspec/file-lifecycle/known-implementation-gaps.md +0 -99
  136. package/docs/sillyspec/file-lifecycle/platform-workflows-sync.md +0 -218
  137. package/docs/sillyspec/file-lifecycle/stage-artifacts.md +0 -167
  138. package/docs/sillyspec/file-lifecycle/storage-and-state.md +0 -148
  139. package/docs/sillyspec/file-lifecycle/worktree-and-guard.md +0 -193
  140. package/docs/sillyspec/file-lifecycle.md +0 -125
  141. package/docs/workflow-contract-regression.md +0 -106
  142. package/docs/worktree-isolation.md +0 -252
  143. package/packages/dashboard/dist/assets/index-O2W5RV4z.css +0 -1
  144. package/packages/dashboard/dist/prototype-dashboard.html +0 -836
  145. package/packages/dashboard/dist/prototype-overview.html +0 -256
  146. package/packages/dashboard/public/prototype-dashboard.html +0 -836
  147. package/packages/dashboard/public/prototype-overview.html +0 -256
  148. package/packages/dashboard/src/components/HResizeHandle.vue +0 -48
  149. package/packages/dashboard/src/components/ProjectCard.vue +0 -187
  150. package/packages/dashboard/src/components/VResizeHandle.vue +0 -61
  151. package/packages/dashboard/src/composables/useLayout.js +0 -131
  152. package/src/brainstorm-postcheck.js +0 -158
  153. package/src/change-list.js +0 -52
  154. package/src/change-risk-profile.js +0 -352
  155. package/src/classify-change.js +0 -73
  156. package/src/constants.js +0 -70
  157. package/src/contract-matrix.js +0 -278
  158. package/src/db.js +0 -201
  159. package/src/endpoint-extractor.js +0 -315
  160. package/src/hooks/claude-pre-tool-use.cjs +0 -125
  161. package/src/hooks/worktree-guard.js +0 -761
  162. package/src/knowledge-match.js +0 -130
  163. package/src/modules.js +0 -482
  164. package/src/scan-postcheck.js +0 -383
  165. package/src/stage-contract.js +0 -700
  166. package/src/stages/brainstorm-auto.js +0 -229
  167. package/src/stages/explore.js +0 -34
  168. package/src/stages/knowledge.js +0 -498
  169. package/src/stages/plan-postcheck.js +0 -513
  170. package/src/sync.js +0 -497
  171. package/src/task-review.js +0 -346
  172. package/src/workflow.js +0 -785
  173. package/src/worktree-apply.js +0 -549
  174. package/src/worktree.js +0 -932
  175. package/templates/workflows/archive-impact.yaml +0 -79
  176. package/templates/workflows/scan-docs.yaml +0 -132
  177. package/test/brainstorm-plan-contract.test.mjs +0 -273
  178. package/test/check-syntax.mjs +0 -26
  179. package/test/cli-top-level-aliases.test.mjs +0 -174
  180. package/test/contract-artifacts.test.mjs +0 -323
  181. package/test/decision-supersede.test.mjs +0 -277
  182. package/test/knowledge-match.test.mjs +0 -231
  183. package/test/plan-execute-contract.test.mjs +0 -330
  184. package/test/plan-optimization.test.mjs +0 -572
  185. package/test/platform-artifacts.test.mjs +0 -190
  186. package/test/platform-failure-samples.test.mjs +0 -199
  187. package/test/platform-recovery-chain.test.mjs +0 -179
  188. package/test/platform-recovery.test.mjs +0 -167
  189. package/test/platform-scan-p0.test.mjs +0 -175
  190. package/test/revision-v1.test.mjs +0 -1145
  191. package/test/run-sanitize-project-name.test.mjs +0 -51
  192. package/test/run-scan-postcheck-fail.test.mjs +0 -64
  193. package/test/run-scan-project-parse.test.mjs +0 -200
  194. package/test/run-tests.mjs +0 -48
  195. package/test/scan-docs-yaml-placeholders.test.mjs +0 -84
  196. package/test/scan-knowledge.test.mjs +0 -175
  197. package/test/scan-paths.test.mjs +0 -68
  198. package/test/scan-postcheck-project-priority.test.mjs +0 -85
  199. package/test/scan-postcheck.test.mjs +0 -197
  200. package/test/scan-workflow-anyfailed-block.test.mjs +0 -52
  201. package/test/spec-dir.test.mjs +0 -206
  202. package/test/stage-contract-failed-post-check.test.mjs +0 -102
  203. package/test/stage-contract.test.mjs +0 -299
  204. package/test/stage-definitions.test.mjs +0 -39
  205. package/test/wait-gates.test.mjs +0 -501
  206. package/test/workflow-spec-base.test.mjs +0 -142
  207. package/test/worktree-guard.test.mjs +0 -136
  208. package/test/worktree-native-overlay.test.mjs +0 -188
@@ -1,346 +0,0 @@
1
- /**
2
- * SillySpec Task Review Gate — execute 阶段任务级评审校验
3
- *
4
- * execute 阶段每个 task 完成后,controller 必须写入 review.json。
5
- * execute --done 时 CLI 硬校验:缺失 review 或 verdict 不通过则阻断。
6
- *
7
- * 目录结构:
8
- * .sillyspec/.runtime/execute-runs/<runId>/tasks/<taskId>/review.json
9
- */
10
-
11
- import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from 'fs'
12
- import { join, resolve } from 'path'
13
-
14
- // ── review.json schema version ──
15
- export const REVIEW_SCHEMA_VERSION = 1
16
-
17
- // ── 合法 verdict 枚举 ──
18
- export const VALID_VERDICTS = ['pass', 'fail', 'cannot_verify']
19
-
20
- /**
21
- * 解析 plan.md 中的 task 列表
22
- * @param {string} planContent - plan.md 文件内容
23
- * @returns {string[]} task id 列表,如 ['task-01', 'task-02']
24
- */
25
- export function parseTaskIdsFromPlan(planContent) {
26
- if (!planContent) return []
27
- const ids = new Set()
28
- const re = /^\s*[-*]\s*\[[ x]\]\s*task-(\d+)/gim
29
- for (const m of planContent.matchAll(re)) {
30
- ids.add(`task-${m[1].padStart(2, '0')}`)
31
- }
32
- return [...ids].sort()
33
- }
34
-
35
- /**
36
- * 校验单个 review.json 文件
37
- * @param {object} review - 解析后的 JSON 对象
38
- * @returns {{ ok: boolean, errors: string[] }}
39
- */
40
- export function validateReviewSchema(review) {
41
- const errors = []
42
- if (!review || typeof review !== 'object') {
43
- errors.push('review.json 不是有效 JSON 对象')
44
- return { ok: false, errors }
45
- }
46
-
47
- if (review.schemaVersion !== REVIEW_SCHEMA_VERSION) {
48
- errors.push(`schemaVersion 应为 ${REVIEW_SCHEMA_VERSION},实际为 ${review.schemaVersion}`)
49
- }
50
-
51
- if (!review.task || typeof review.task !== 'string') {
52
- errors.push('缺少 task 字段(应为 "task-XX" 格式)')
53
- }
54
-
55
- if (!VALID_VERDICTS.includes(review.specVerdict)) {
56
- errors.push(`specVerdict 无效:${review.specVerdict}(应为 ${VALID_VERDICTS.join('/')})`)
57
- }
58
-
59
- if (!VALID_VERDICTS.includes(review.qualityVerdict)) {
60
- errors.push(`qualityVerdict 无效:${review.qualityVerdict}(应为 ${VALID_VERDICTS.join('/')})`)
61
- }
62
-
63
- // cannot_verify 必须提供 requiredEvidence
64
- if (review.specVerdict === 'cannot_verify' || review.qualityVerdict === 'cannot_verify') {
65
- if (!Array.isArray(review.requiredEvidence) || review.requiredEvidence.length === 0) {
66
- errors.push('cannot_verify 的 verdict 必须提供非空的 requiredEvidence 数组')
67
- }
68
- }
69
-
70
- // base/head 非空检查
71
- if (!review.base || typeof review.base !== 'string') {
72
- errors.push('缺少 base 字段(git commit hash)')
73
- }
74
- if (!review.head || typeof review.head !== 'string') {
75
- errors.push('缺少 head 字段(git commit hash)')
76
- }
77
-
78
- return { ok: errors.length === 0, errors }
79
- }
80
-
81
- /**
82
- * 读取单个 task 的 review.json
83
- * @param {string} reviewPath - review.json 文件路径
84
- * @returns {{ ok: boolean, review: object|null, errors: string[] }}
85
- */
86
- export function readReview(reviewPath) {
87
- if (!existsSync(reviewPath)) {
88
- return { ok: false, review: null, errors: ['review.json 不存在'] }
89
- }
90
-
91
- let raw
92
- try {
93
- raw = readFileSync(reviewPath, 'utf8')
94
- } catch (e) {
95
- return { ok: false, review: null, parseError: true, errors: [`review.json 读取失败: ${e.message}`] }
96
- }
97
-
98
- let parsed
99
- try {
100
- parsed = JSON.parse(raw)
101
- } catch (e) {
102
- // 文件存在但 JSON 非法:review 设为 null 但标记 parseError=true
103
- return { ok: false, review: null, parseError: true, errors: [`review.json 解析失败: ${e.message}`] }
104
- }
105
-
106
- const schemaResult = validateReviewSchema(parsed)
107
- if (!schemaResult.ok) {
108
- return { ok: false, review: parsed, schemaError: true, errors: schemaResult.errors }
109
- }
110
-
111
- return { ok: true, review: parsed, errors: [] }
112
- }
113
-
114
- /**
115
- * execute --done 时的 task review 总校验
116
- *
117
- * 规则:
118
- * - 每个 plan task 必须有 review.json
119
- * - specVerdict 或 qualityVerdict 为 fail → 整体 fail
120
- * - specVerdict 或 qualityVerdict 为 cannot_verify → warning(requiredEvidence 非空)
121
- * - cannot_verify + requiredEvidence 为空 → fail(agent 逃避判断)
122
- * - cannot_verify 的 requiredEvidence 汇总到 requiredEvidence 字段,供 verify 阶段消费
123
- *
124
- * @param {object} opts
125
- * @param {string} opts.planContent - plan.md 内容
126
- * @param {string} opts.runtimeRoot - .sillyspec/.runtime 的绝对路径
127
- * @param {string} opts.executeRunId - execute run id(如 'exec-2026-06-23-131400')
128
- * @param {boolean} [opts.allowCannotVerify=true] - 是否允许 cannot_verify(默认允许,给 warning)
129
- * @returns {{ ok: boolean, errors: string[], warnings: string[], requiredEvidence: Array<{task: string, verdict: string, evidence: string[]}> }}
130
- */
131
- export function validateTaskReviews(opts) {
132
- const { planContent, runtimeRoot, executeRunId, allowCannotVerify = true } = opts
133
-
134
- const taskIds = parseTaskIdsFromPlan(planContent)
135
-
136
- // 如果 plan 里没有 task,跳过校验(向后兼容)
137
- if (taskIds.length === 0) {
138
- return { ok: true, errors: [], warnings: [], requiredEvidence: [] }
139
- }
140
-
141
- const errors = []
142
- const warnings = []
143
- const requiredEvidence = []
144
-
145
- for (const taskId of taskIds) {
146
- const reviewDir = join(runtimeRoot, 'execute-runs', executeRunId, 'tasks', taskId)
147
- const reviewPath = join(reviewDir, 'review.json')
148
-
149
- const result = readReview(reviewPath)
150
-
151
- if (!result.ok) {
152
- if (result.parseError) {
153
- // review.json 存在但 JSON 非法
154
- errors.push(`${taskId}: review.json 解析失败 — ${result.errors.join('; ')}`)
155
- } else if (result.schemaError) {
156
- // review.json 存在且 JSON 合法,但 schema 校验失败
157
- errors.push(`${taskId}: review.json 校验失败 — ${result.errors.join('; ')}`)
158
- } else {
159
- // review.json 不存在
160
- errors.push(`${taskId}: 缺少 review.json — task 未经过评审`)
161
- }
162
- continue
163
- }
164
-
165
- const review = result.review
166
-
167
- // 检查 review.task 是否与 plan 中的 taskId 一致
168
- if (review.task && review.task !== taskId) {
169
- errors.push(`${taskId}: review.json 中的 task 字段为 "${review.task}",与 plan 不一致(应为 "${taskId}")— agent 可能复制模板未修改`)
170
- continue
171
- }
172
-
173
- // 检查 fail verdict
174
- if (review.specVerdict === 'fail' || review.qualityVerdict === 'fail') {
175
- errors.push(`${taskId}: review 未通过 — spec: ${review.specVerdict}, quality: ${review.qualityVerdict}`)
176
- if (review.reviewerNotes) {
177
- errors.push(`${taskId}: ${review.reviewerNotes}`)
178
- }
179
- continue
180
- }
181
-
182
- // 检查 cannot_verify
183
- if (review.specVerdict === 'cannot_verify' || review.qualityVerdict === 'cannot_verify') {
184
- if (!allowCannotVerify) {
185
- errors.push(`${taskId}: cannot_verify 不被允许 — 必须提供评审结果`)
186
- continue
187
- }
188
-
189
- if (review.requiredEvidence && review.requiredEvidence.length > 0) {
190
- const verdicts = []
191
- if (review.specVerdict === 'cannot_verify') verdicts.push('spec')
192
- if (review.qualityVerdict === 'cannot_verify') verdicts.push('quality')
193
- warnings.push(`${taskId}: ${verdicts.join('+')}=cannot_verify,requiredEvidence 必须在 verify 阶段满足`)
194
- requiredEvidence.push({
195
- task: taskId,
196
- verdict: verdicts.join('+'),
197
- evidence: review.requiredEvidence,
198
- })
199
- } else {
200
- // cannot_verify + 空 requiredEvidence = agent 逃避判断
201
- errors.push(`${taskId}: cannot_verify 但 requiredEvidence 为空 — 这是无效评审`)
202
- }
203
- }
204
- }
205
-
206
- // 额外检查:扫描 execute-runs/<runId>/tasks/ 下是否有 plan 里没有的 task review
207
- // (agent 可能写错了 task id)
208
- try {
209
- const tasksDir = join(runtimeRoot, 'execute-runs', executeRunId, 'tasks')
210
- if (existsSync(tasksDir)) {
211
- const taskDirs = readdirSync(tasksDir, { withFileTypes: true })
212
- .filter(e => e.isDirectory())
213
- .map(e => e.name)
214
- const taskIdSet = new Set(taskIds)
215
- for (const dirName of taskDirs) {
216
- if (!taskIdSet.has(dirName) && existsSync(join(tasksDir, dirName, 'review.json'))) {
217
- warnings.push(`${dirName}: 存在 review.json 但不在 plan.md 的 task 列表中(可能是多余文件)`)
218
- }
219
- }
220
- }
221
- } catch (e) {
222
- warnings.push(`task review extra-check 异常: ${e.message}`)
223
- }
224
-
225
- return {
226
- ok: errors.length === 0,
227
- errors,
228
- warnings,
229
- requiredEvidence,
230
- }
231
- }
232
-
233
- /**
234
- * 将 cannot_verify 的 requiredEvidence 写入 change 目录
235
- * 供 verify 阶段消费
236
- *
237
- * @param {string} changeDir - 变更目录(.sillyspec/changes/<name>)
238
- * @param {Array<{task: string, verdict: string, evidence: string[]}>} requiredEvidence
239
- * @returns {string|null} 写入的文件路径,null 表示无需写入
240
- */
241
- export function writeVerifyRequiredEvidence(changeDir, requiredEvidence) {
242
- if (!requiredEvidence || requiredEvidence.length === 0) return null
243
-
244
- const filePath = join(changeDir, 'verify-required-evidence.json')
245
- const data = {
246
- generatedAt: new Date().toISOString(),
247
- schemaVersion: 1,
248
- items: requiredEvidence,
249
- }
250
-
251
- mkdirSync(changeDir, { recursive: true })
252
- writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n')
253
-
254
- return filePath
255
- }
256
-
257
- /**
258
- * 生成 execute run id
259
- * @returns {string} 如 'exec-2026-06-23-131400'
260
- */
261
- export function generateExecuteRunId() {
262
- const now = new Date()
263
- const pad = (n) => String(n).padStart(2, '0')
264
- return `exec-${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`
265
- }
266
-
267
- /**
268
- * 获取当前(或最新)execute run id
269
- * 从 runtime 目录下查找 execute-runs/ 子目录
270
- *
271
- * @param {string} runtimeRoot - .sillyspec/.runtime 路径
272
- * @returns {string|null} 最新 run id,null 表示无任何 run
273
- */
274
- /**
275
- * 获取当前 execute run id
276
- * 优先从 current-execute-run-id 标记文件读取(execute 阶段启动时写入),
277
- * fallback 到 execute-runs/ 下最新的 exec- 前缀目录。
278
- *
279
- * @param {string} runtimeRoot - .sillyspec/.runtime 路径
280
- * @returns {string|null} 当前 run id,null 表示无任何 run
281
- */
282
- export function getLatestExecuteRunId(runtimeRoot) {
283
- // 优先读标记文件(execute 阶段启动时由 run.js 写入,生命周期内不变)
284
- const markerPath = join(runtimeRoot, 'current-execute-run-id')
285
- try {
286
- if (existsSync(markerPath)) {
287
- const content = readFileSync(markerPath, 'utf8').trim()
288
- if (content) return content
289
- }
290
- } catch {}
291
-
292
- // fallback:扫描 execute-runs/ 目录
293
- const runsDir = join(runtimeRoot, 'execute-runs')
294
- if (!existsSync(runsDir)) return null
295
-
296
- try {
297
- const entries = readdirSync(runsDir, { withFileTypes: true })
298
- .filter(e => e.isDirectory() && e.name.startsWith('exec-'))
299
- .map(e => e.name)
300
- .sort()
301
- .reverse()
302
- return entries[0] || null
303
- } catch {
304
- return null
305
- }
306
- }
307
-
308
- /**
309
- * 确保 task review 目录存在
310
- * @param {string} runtimeRoot
311
- * @param {string} executeRunId
312
- * @param {string} taskId
313
- * @returns {string} task review 目录路径
314
- */
315
- export function ensureTaskReviewDir(runtimeRoot, executeRunId, taskId) {
316
- const dir = join(runtimeRoot, 'execute-runs', executeRunId, 'tasks', taskId)
317
- mkdirSync(dir, { recursive: true })
318
- return dir
319
- }
320
-
321
- /**
322
- * 打印校验结果
323
- * @param {{ ok: boolean, errors: string[], warnings: string[], requiredEvidence: Array }} result
324
- */
325
- export function printReviewResult(result) {
326
- if (result.ok && result.warnings.length === 0) {
327
- console.log('\n✅ Task Review Gate — 所有任务评审通过')
328
- return
329
- }
330
-
331
- if (result.errors.length > 0) {
332
- console.error('\n🚫 Task Review Gate — FAILED')
333
- for (const err of result.errors) {
334
- console.error(` - ${err}`)
335
- }
336
- console.error('\n 提示:为缺失/失败的任务补充 review.json,然后重新 --done')
337
- }
338
-
339
- if (result.warnings.length > 0) {
340
- console.warn('\n⚠️ Task Review Gate — WARNING')
341
- for (const w of result.warnings) {
342
- console.warn(` - ${w}`)
343
- }
344
- console.warn('\n cannot_verify 的 requiredEvidence 将在 verify 阶段校验')
345
- }
346
- }