sillyspec 3.27.2 → 3.27.5

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 (221) hide show
  1. package/.claude/skills/sillyspec-execute/SKILL.md +8 -0
  2. package/.claude/skills/sillyspec-quick/SKILL.md +2 -1
  3. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-archive/SKILL.md +87 -0
  4. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-auto/SKILL.md +93 -0
  5. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -0
  6. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-commit/SKILL.md +67 -0
  7. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-continue/SKILL.md +47 -0
  8. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-doctor/SKILL.md +76 -0
  9. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-execute/SKILL.md +214 -0
  10. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-explore/SKILL.md +102 -0
  11. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-export/SKILL.md +57 -0
  12. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-init/SKILL.md +177 -0
  13. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-knowledge/SKILL.md +270 -0
  14. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-plan/SKILL.md +156 -0
  15. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-quick/SKILL.md +116 -0
  16. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-resume/SKILL.md +70 -0
  17. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-scan/SKILL.md +79 -0
  18. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-state/SKILL.md +64 -0
  19. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-status/SKILL.md +54 -0
  20. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-verify/SKILL.md +87 -0
  21. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-workspace/SKILL.md +175 -0
  22. package/.claude/worktrees/sillyspec-data-model-research/.gitattributes +2 -0
  23. package/.claude/worktrees/sillyspec-data-model-research/.husky/pre-push +20 -0
  24. package/.claude/worktrees/sillyspec-data-model-research/README.md +203 -0
  25. package/.claude/worktrees/sillyspec-data-model-research/SKILL.md +91 -0
  26. package/.claude/worktrees/sillyspec-data-model-research/bin/sillyspec.js +2 -0
  27. package/.claude/worktrees/sillyspec-data-model-research/logo.jpg +0 -0
  28. package/.claude/worktrees/sillyspec-data-model-research/package-lock.json +977 -0
  29. package/.claude/worktrees/sillyspec-data-model-research/package.json +39 -0
  30. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -0
  31. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -0
  32. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/favicon.jpg +0 -0
  33. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/index.html +16 -0
  34. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/logo.jpg +0 -0
  35. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/prototype-dashboard.html +836 -0
  36. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/prototype-overview.html +256 -0
  37. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/package.json +25 -0
  38. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/executor.js +95 -0
  39. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/index.js +679 -0
  40. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/parser.js +526 -0
  41. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/watcher.js +357 -0
  42. package/.claude/worktrees/sillyspec-data-model-research/src/agent-session-log.js +803 -0
  43. package/.claude/worktrees/sillyspec-data-model-research/src/change-list.js +290 -0
  44. package/.claude/worktrees/sillyspec-data-model-research/src/change-risk-profile.js +270 -0
  45. package/.claude/worktrees/sillyspec-data-model-research/src/check-primitives.js +65 -0
  46. package/.claude/worktrees/sillyspec-data-model-research/src/classify-change.js +109 -0
  47. package/.claude/worktrees/sillyspec-data-model-research/src/commit-suggest.js +202 -0
  48. package/.claude/worktrees/sillyspec-data-model-research/src/config-cat.js +88 -0
  49. package/.claude/worktrees/sillyspec-data-model-research/src/config-schema.js +328 -0
  50. package/.claude/worktrees/sillyspec-data-model-research/src/constants.js +97 -0
  51. package/.claude/worktrees/sillyspec-data-model-research/src/contract-matrix.js +480 -0
  52. package/.claude/worktrees/sillyspec-data-model-research/src/datetime.js +20 -0
  53. package/.claude/worktrees/sillyspec-data-model-research/src/db-engine.js +55 -0
  54. package/.claude/worktrees/sillyspec-data-model-research/src/db.js +381 -0
  55. package/.claude/worktrees/sillyspec-data-model-research/src/decision-distill.js +517 -0
  56. package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/backends/local-agent.js +155 -0
  57. package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/backends/sillyhub-mcp.js +245 -0
  58. package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/probe.js +246 -0
  59. package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/strategy.js +121 -0
  60. package/.claude/worktrees/sillyspec-data-model-research/src/docs-check.js +825 -0
  61. package/.claude/worktrees/sillyspec-data-model-research/src/docs-debt.js +393 -0
  62. package/.claude/worktrees/sillyspec-data-model-research/src/docs-gate.js +107 -0
  63. package/.claude/worktrees/sillyspec-data-model-research/src/doctor-diagnostics.js +1099 -0
  64. package/.claude/worktrees/sillyspec-data-model-research/src/endpoint-extractor.js +420 -0
  65. package/.claude/worktrees/sillyspec-data-model-research/src/foreign-declared.js +78 -0
  66. package/.claude/worktrees/sillyspec-data-model-research/src/fs-atomic.js +73 -0
  67. package/.claude/worktrees/sillyspec-data-model-research/src/git-helper.js +150 -0
  68. package/.claude/worktrees/sillyspec-data-model-research/src/hooks/claude-pre-tool-use.cjs +130 -0
  69. package/.claude/worktrees/sillyspec-data-model-research/src/hooks/worktree-guard.js +892 -0
  70. package/.claude/worktrees/sillyspec-data-model-research/src/index.js +2798 -0
  71. package/.claude/worktrees/sillyspec-data-model-research/src/init.js +626 -0
  72. package/.claude/worktrees/sillyspec-data-model-research/src/knowledge-match.js +231 -0
  73. package/.claude/worktrees/sillyspec-data-model-research/src/local-detect.js +128 -0
  74. package/.claude/worktrees/sillyspec-data-model-research/src/local-register.js +104 -0
  75. package/.claude/worktrees/sillyspec-data-model-research/src/machine-interface.js +543 -0
  76. package/.claude/worktrees/sillyspec-data-model-research/src/migrate.js +127 -0
  77. package/.claude/worktrees/sillyspec-data-model-research/src/module-impact.js +166 -0
  78. package/.claude/worktrees/sillyspec-data-model-research/src/module-resolve.js +222 -0
  79. package/.claude/worktrees/sillyspec-data-model-research/src/modules.js +580 -0
  80. package/.claude/worktrees/sillyspec-data-model-research/src/progress/change-registry.js +336 -0
  81. package/.claude/worktrees/sillyspec-data-model-research/src/progress/consistency-doctor.js +355 -0
  82. package/.claude/worktrees/sillyspec-data-model-research/src/progress/shared.js +36 -0
  83. package/.claude/worktrees/sillyspec-data-model-research/src/progress/stage-machine.js +594 -0
  84. package/.claude/worktrees/sillyspec-data-model-research/src/progress/step-store.js +265 -0
  85. package/.claude/worktrees/sillyspec-data-model-research/src/progress.js +1304 -0
  86. package/.claude/worktrees/sillyspec-data-model-research/src/quick-recommend.js +115 -0
  87. package/.claude/worktrees/sillyspec-data-model-research/src/quicklog.js +852 -0
  88. package/.claude/worktrees/sillyspec-data-model-research/src/review-tier.js +77 -0
  89. package/.claude/worktrees/sillyspec-data-model-research/src/run/command.js +1595 -0
  90. package/.claude/worktrees/sillyspec-data-model-research/src/run/complete-handlers.js +1552 -0
  91. package/.claude/worktrees/sillyspec-data-model-research/src/run/complete.js +1259 -0
  92. package/.claude/worktrees/sillyspec-data-model-research/src/run/concurrent-detect.js +156 -0
  93. package/.claude/worktrees/sillyspec-data-model-research/src/run/gates.js +1084 -0
  94. package/.claude/worktrees/sillyspec-data-model-research/src/run/multi-repo-context.js +208 -0
  95. package/.claude/worktrees/sillyspec-data-model-research/src/run/next.js +128 -0
  96. package/.claude/worktrees/sillyspec-data-model-research/src/run/prompt.js +843 -0
  97. package/.claude/worktrees/sillyspec-data-model-research/src/run/quick-audit.js +186 -0
  98. package/.claude/worktrees/sillyspec-data-model-research/src/run/scan-profile.js +269 -0
  99. package/.claude/worktrees/sillyspec-data-model-research/src/run/shared.js +1562 -0
  100. package/.claude/worktrees/sillyspec-data-model-research/src/run/stage.js +584 -0
  101. package/.claude/worktrees/sillyspec-data-model-research/src/run.js +23 -0
  102. package/.claude/worktrees/sillyspec-data-model-research/src/scan-diff.js +388 -0
  103. package/.claude/worktrees/sillyspec-data-model-research/src/scan-postcheck.js +512 -0
  104. package/.claude/worktrees/sillyspec-data-model-research/src/scan-staleness.js +95 -0
  105. package/.claude/worktrees/sillyspec-data-model-research/src/setup.js +414 -0
  106. package/.claude/worktrees/sillyspec-data-model-research/src/sillyhub-mcp/client.js +597 -0
  107. package/.claude/worktrees/sillyspec-data-model-research/src/sillyhub-mcp/config.js +62 -0
  108. package/.claude/worktrees/sillyspec-data-model-research/src/spec-dir-typo.js +50 -0
  109. package/.claude/worktrees/sillyspec-data-model-research/src/spec-sync.js +358 -0
  110. package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract-engine.js +171 -0
  111. package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract-spec.js +550 -0
  112. package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract.js +889 -0
  113. package/.claude/worktrees/sillyspec-data-model-research/src/stage-review.js +597 -0
  114. package/.claude/worktrees/sillyspec-data-model-research/src/stages/archive.js +199 -0
  115. package/.claude/worktrees/sillyspec-data-model-research/src/stages/brainstorm-auto.js +236 -0
  116. package/.claude/worktrees/sillyspec-data-model-research/src/stages/brainstorm.js +617 -0
  117. package/.claude/worktrees/sillyspec-data-model-research/src/stages/cmd-existence.js +145 -0
  118. package/.claude/worktrees/sillyspec-data-model-research/src/stages/doctor.js +540 -0
  119. package/.claude/worktrees/sillyspec-data-model-research/src/stages/execute.js +1275 -0
  120. package/.claude/worktrees/sillyspec-data-model-research/src/stages/explore.js +43 -0
  121. package/.claude/worktrees/sillyspec-data-model-research/src/stages/index.js +29 -0
  122. package/.claude/worktrees/sillyspec-data-model-research/src/stages/knowledge.js +514 -0
  123. package/.claude/worktrees/sillyspec-data-model-research/src/stages/plan-postcheck.js +1344 -0
  124. package/.claude/worktrees/sillyspec-data-model-research/src/stages/plan.js +650 -0
  125. package/.claude/worktrees/sillyspec-data-model-research/src/stages/quick.js +167 -0
  126. package/.claude/worktrees/sillyspec-data-model-research/src/stages/scan.js +608 -0
  127. package/.claude/worktrees/sillyspec-data-model-research/src/stages/status.js +64 -0
  128. package/.claude/worktrees/sillyspec-data-model-research/src/stages/verify.js +246 -0
  129. package/.claude/worktrees/sillyspec-data-model-research/src/sync.js +1470 -0
  130. package/.claude/worktrees/sillyspec-data-model-research/src/task-review.js +1498 -0
  131. package/.claude/worktrees/sillyspec-data-model-research/src/taskcard.js +210 -0
  132. package/.claude/worktrees/sillyspec-data-model-research/src/verify-postcheck.js +1718 -0
  133. package/.claude/worktrees/sillyspec-data-model-research/src/verify-probes.js +277 -0
  134. package/.claude/worktrees/sillyspec-data-model-research/src/version.js +19 -0
  135. package/.claude/worktrees/sillyspec-data-model-research/src/workflow.js +600 -0
  136. package/.claude/worktrees/sillyspec-data-model-research/src/workspace.js +125 -0
  137. package/.claude/worktrees/sillyspec-data-model-research/src/worktree-apply.js +1446 -0
  138. package/.claude/worktrees/sillyspec-data-model-research/src/worktree-deps.js +576 -0
  139. package/.claude/worktrees/sillyspec-data-model-research/src/worktree.js +1715 -0
  140. package/.claude/worktrees/sillyspec-data-model-research/templates/claude-instruction.md +23 -0
  141. package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/taskcard-rules.md +28 -0
  142. package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/testcase-design.md +12 -0
  143. package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/verify-probes.md +32 -0
  144. package/.claude/worktrees/sillyspec-data-model-research/templates/workflows/archive-impact.yaml +79 -0
  145. package/.claude/worktrees/sillyspec-data-model-research/templates/workflows/scan-docs.yaml +132 -0
  146. package/eval/README.md +86 -0
  147. package/eval/eval.config.json +28 -0
  148. package/eval/lib/adapters/cli.mjs +63 -0
  149. package/eval/lib/adapters/dryrun.mjs +27 -0
  150. package/eval/lib/config.mjs +82 -0
  151. package/eval/lib/db.mjs +76 -0
  152. package/eval/lib/report.mjs +120 -0
  153. package/eval/lib/runner.mjs +157 -0
  154. package/eval/lib/tasks.mjs +73 -0
  155. package/eval/lib/util.mjs +107 -0
  156. package/eval/lib/workdir.mjs +30 -0
  157. package/eval/run.mjs +83 -0
  158. package/eval/tasks/001-slugify/files/package.json +6 -0
  159. package/eval/tasks/001-slugify/solution/src/slugify.mjs +26 -0
  160. package/eval/tasks/001-slugify/task.json +6 -0
  161. package/eval/tasks/001-slugify/verify.mjs +28 -0
  162. package/eval/tasks/002-each-day/files/package.json +6 -0
  163. package/eval/tasks/002-each-day/files/src/dates.mjs +4 -0
  164. package/eval/tasks/002-each-day/solution/src/dates.mjs +30 -0
  165. package/eval/tasks/002-each-day/task.json +6 -0
  166. package/eval/tasks/002-each-day/verify.mjs +31 -0
  167. package/eval/tasks/003-word-report/files/data/notes.txt +5 -0
  168. package/eval/tasks/003-word-report/files/package.json +6 -0
  169. package/eval/tasks/003-word-report/solution/bin/report.mjs +36 -0
  170. package/eval/tasks/003-word-report/task.json +6 -0
  171. package/eval/tasks/003-word-report/verify.mjs +38 -0
  172. package/package.json +4 -2
  173. package/src/agent-session-log.js +45 -5
  174. package/src/change-risk-profile.js +83 -14
  175. package/src/config-schema.js +18 -3
  176. package/src/contract-matrix.js +90 -29
  177. package/src/datetime.js +20 -0
  178. package/src/decision-distill.js +517 -0
  179. package/src/dispatch/backends/local-agent.js +4 -4
  180. package/src/docs-check.js +227 -0
  181. package/src/docs-debt.js +187 -24
  182. package/src/doctor-diagnostics.js +102 -2
  183. package/src/foreign-declared.js +78 -0
  184. package/src/index.js +89 -9
  185. package/src/init.js +9 -7
  186. package/src/knowledge-match.js +107 -6
  187. package/src/local-register.js +7 -1
  188. package/src/module-resolve.js +12 -1
  189. package/src/modules.js +2 -1
  190. package/src/plan-adopt-waves.js +126 -0
  191. package/src/quicklog.js +50 -4
  192. package/src/run/command.js +122 -43
  193. package/src/run/complete-handlers.js +48 -19
  194. package/src/run/complete.js +5 -2
  195. package/src/run/gates.js +87 -22
  196. package/src/run/prompt.js +84 -42
  197. package/src/run/quick-audit.js +11 -0
  198. package/src/run/shared.js +152 -10
  199. package/src/run.js +2 -2
  200. package/src/scan-postcheck.js +30 -17
  201. package/src/stage-contract-spec.js +2 -1
  202. package/src/stage-contract.js +68 -4
  203. package/src/stages/archive.js +53 -12
  204. package/src/stages/brainstorm-auto.js +7 -0
  205. package/src/stages/brainstorm.js +11 -0
  206. package/src/stages/doctor.js +157 -1
  207. package/src/stages/execute.js +32 -6
  208. package/src/stages/plan-postcheck.js +76 -17
  209. package/src/stages/plan.js +1 -0
  210. package/src/stages/quick.js +6 -1
  211. package/src/stages/verify.js +31 -14
  212. package/src/sync.js +41 -3
  213. package/src/task-review.js +2 -2
  214. package/src/taskcard-placeholders.js +21 -0
  215. package/src/taskcard.js +10 -1
  216. package/src/verify-postcheck.js +315 -13
  217. package/src/verify-probes.js +46 -4
  218. package/src/worktree-apply.js +184 -7
  219. package/src/worktree-deps.js +57 -0
  220. package/src/worktree.js +201 -58
  221. package/templates/prompts/taskcard-rules.md +1 -0
@@ -0,0 +1,277 @@
1
+ /**
2
+ * verify-probes.js — `sillyspec verify-probes` 的机械探针实现 + verify-result.md 骨架生成
3
+ * (2026-08-21 agent-手工产出审计第三批 H1/H3/H5/H7/F9)。
4
+ *
5
+ * verify-probes.md 模板定义六个探针,agent 此前逐条手跑 grep/递归查找/git 对账再手工拼表格。
6
+ * 本模块把纯机械的四个探针命令化(语义判断的留 agent,输出里显式标注):
7
+ * 探针1 未实现标记扫描:design §6 清单的具体文件逐行 grep TODO/FIXME/尚未实现 等
8
+ * 探针3 测试覆盖:逐 task 按 allowed_paths 定位模块目录,递归找测试文件(co-located tests/ 陷阱)
9
+ * 探针5 API 契约对账:复用 contract-matrix.verifyApiParity(endpoints.json × 前端调用)+ 表格渲染
10
+ * 探针6 删除对账:git diff --name-status HEAD 的 D/R × design 声明操作三态判定
11
+ * 探针2(关键词提取半语义)/探针3.4 集成盲区/3.5 断言抽查/探针4(决策追踪语义)留 agent。
12
+ *
13
+ * verify-result.md 骨架:七章节固定结构 + 探针结果机械预填 + 其余章节 <!--TODO--> 占位。
14
+ * 结论章节留「待填」——extractVerifyConclusion 找不到 PASS/FAIL 关键词即判不过,骨架不能
15
+ * 直接过门(与 symbol-impact 骨架同款防偷懒语义)。
16
+ */
17
+ import { existsSync, readFileSync, readdirSync, writeFileSync } from 'fs'
18
+ import { join, dirname, basename } from 'path'
19
+ import { gitQuiet } from './git-helper.js'
20
+ import { parseFileChangeListDetailed } from './change-list.js'
21
+ import { parseAllowedPaths } from './stages/plan-postcheck.js'
22
+ import { verifyApiParity } from './contract-matrix.js'
23
+ import { splitOwnVsForeignDiffFiles } from './foreign-declared.js'
24
+ import { resolveSpecDir, resolveRuntimeRoot } from './run/shared.js'
25
+
26
+ const TODO_MARKER_RE = /尚未实现|TODO|FIXME|HACK|XXX/
27
+ const TEST_FILE_RE = /test|spec/i
28
+ const PROBE1_MAX_MATCHES = 200
29
+
30
+ function listTaskIds(tasksPath) {
31
+ if (!existsSync(tasksPath)) return []
32
+ const ids = []
33
+ for (const line of readFileSync(tasksPath, 'utf8').split('\n')) {
34
+ const m = line.match(/^[-*]\s*\[[ xX]\]\s*(task-\d+)\b/)
35
+ if (m) ids.push(m[1])
36
+ }
37
+ return ids
38
+ }
39
+
40
+ /** 递归找测试文件(排除 node_modules/.git/dist 等;返回 posix 相对 cwd 路径,封顶展示) */
41
+ function findTestFiles(rootDir, cwd, cap = 10) {
42
+ const found = []
43
+ const skip = new Set(['node_modules', '.git', '.gradle', '__pycache__', '.venv', 'dist', 'build', 'target', 'out'])
44
+ const walk = (d) => {
45
+ let entries
46
+ try {
47
+ entries = readdirSync(d, { withFileTypes: true })
48
+ } catch {
49
+ return
50
+ }
51
+ for (const e of entries) {
52
+ const full = join(d, e.name)
53
+ if (e.isDirectory()) {
54
+ if (!skip.has(e.name)) walk(full)
55
+ } else if (e.isFile() && TEST_FILE_RE.test(basename(e.name))) {
56
+ found.push(full.split('\\').join('/').replace(cwd.split('\\').join('/').replace(/\/$/, '') + '/', ''))
57
+ if (found.length >= cap) return
58
+ }
59
+ }
60
+ }
61
+ if (existsSync(rootDir)) walk(rootDir)
62
+ return found
63
+ }
64
+
65
+ /**
66
+ * 跑四个机械探针。
67
+ * @param {{ cwd: string, changeName: string, specDir?: string|null }} opts
68
+ * @returns {{ probe1: object, probe3: object, probe5: object, probe6: object }}
69
+ */
70
+ export function runVerifyProbes({ cwd, changeName, specDir = null }) {
71
+ const specBase = resolveSpecDir(cwd, { specDir })
72
+ const changeDir = join(specBase, 'changes', changeName)
73
+ const designPath = join(changeDir, 'design.md')
74
+ const detailed = existsSync(designPath) ? parseFileChangeListDetailed(designPath) : []
75
+ const designOps = new Map(detailed.map(e => [e.path, e.operation]))
76
+
77
+ // ── 探针 1:未实现标记扫描(design 清单具体文件;glob 项列出让 agent 展开)──
78
+ const probe1 = { matches: [], globEntries: [], skippedFiles: [] }
79
+ for (const e of detailed) {
80
+ if (e.path.startsWith('.sillyspec/')) continue
81
+ if (/[*?[\]]/.test(e.path)) {
82
+ probe1.globEntries.push(e.path)
83
+ continue
84
+ }
85
+ const abs = join(cwd, e.path)
86
+ if (!existsSync(abs)) {
87
+ probe1.skippedFiles.push(e.path)
88
+ continue
89
+ }
90
+ try {
91
+ const lines = readFileSync(abs, 'utf8').split('\n')
92
+ lines.forEach((line, i) => {
93
+ if (probe1.matches.length >= PROBE1_MAX_MATCHES) return
94
+ if (TODO_MARKER_RE.test(line)) probe1.matches.push({ file: e.path, line: i + 1, content: line.trim().slice(0, 160) })
95
+ })
96
+ } catch {
97
+ probe1.skippedFiles.push(e.path)
98
+ }
99
+ }
100
+
101
+ // ── 探针 3:验收标准测试覆盖(task 卡 allowed_paths → 模块目录递归找测试文件)──
102
+ const tasksPath = join(changeDir, 'tasks.md')
103
+ const probe3 = { tasks: [], note: '集成盲区(路由/跨模块装配)与断言有效性抽查是语义判断,留给 agent 逐 task 标注 ⚠️' }
104
+ for (const taskId of listTaskIds(tasksPath)) {
105
+ const cardPath = join(changeDir, 'tasks', `${taskId}.md`)
106
+ let allowed = []
107
+ if (existsSync(cardPath)) allowed = parseAllowedPaths(readFileSync(cardPath, 'utf8'))
108
+ const moduleDirs = [...new Set(allowed.map(p => dirname(p.split('\\').join('/'))).filter(d => d && d !== '.'))]
109
+ const testFiles = []
110
+ for (const d of moduleDirs) {
111
+ for (const f of findTestFiles(join(cwd, d), cwd)) {
112
+ if (!testFiles.includes(f)) testFiles.push(f)
113
+ }
114
+ }
115
+ probe3.tasks.push({
116
+ task: taskId,
117
+ moduleDirs,
118
+ testFiles: testFiles.slice(0, 10),
119
+ testFileCount: testFiles.length,
120
+ hasTest: testFiles.length > 0,
121
+ located: moduleDirs.length > 0,
122
+ })
123
+ }
124
+
125
+ // ── 探针 5:API 契约对账(复用 verifyApiParity:endpoints.json × 前端调用)──
126
+ const runtimeRoot = resolveRuntimeRoot({ specRoot: specDir }, specBase)
127
+ const probe5 = verifyApiParity(specBase, cwd, runtimeRoot, changeName)
128
+
129
+ // ── 探针 6:代码删除对账(git diff --name-status HEAD 的 D/R × design 声明三态)──
130
+ const probe6 = { deletions: [], note: '以 git 事实为准(真实 > 声明);是否 FAIL blocker 由 agent 诚实判定' }
131
+ // 他者声明归属过滤(坑 verify-reconcile-foreign-wip):并行会话在途删除/改动不进本变更对账
132
+ const nsOut = gitQuiet(cwd, ['diff', '--name-status', 'HEAD']) || ''
133
+ let nsRows = nsOut.split('\n').filter(Boolean)
134
+ {
135
+ const rawTargets = nsRows.map(row => ((row.split('\t')[1]) || ''))
136
+ .filter(Boolean).map(t => t.split('\\').join('/'))
137
+ const { foreign } = splitOwnVsForeignDiffFiles(cwd, changeName, rawTargets)
138
+ if (foreign.length > 0) {
139
+ const foreignSet = new Set(foreign.map(x => x.file))
140
+ nsRows = nsRows.filter(row => !foreignSet.has((row.split('\t')[1] || '').split('\\').join('/')))
141
+ console.warn(`⚠️ 探针6 已排除 ${foreign.length} 个并行会话声明的删除/改动(${foreign.slice(0, 5).map(x => x.file).join(', ')}${foreign.length > 5 ? ' 等' : ''})`)
142
+ }
143
+ }
144
+ for (const row of nsRows) {
145
+ const [status, ...paths] = row.split('\t')
146
+ const st = (status || '').trim().toUpperCase()
147
+ if (!/^[DRC]/.test(st)) continue
148
+ // R/C 的旧路径等价删除(paths[0]);D 的唯一路径
149
+ const target = (st.startsWith('D') ? paths[0] : paths[0]) || ''
150
+ const posix = target.split('\\').join('/')
151
+ if (!posix || posix.startsWith('.sillyspec/') || posix === 'meta.json' || basename(posix) === 'meta.json') continue
152
+ const op = designOps.get(posix)
153
+ let verdict
154
+ if (op && /删除|delete|del/i.test(op)) verdict = '✅ 合规(design 声明删除)'
155
+ else if (op) verdict = `❌ 高风险(design 声明「${op}」却整文件删除)`
156
+ else verdict = '⚠️ 未声明删除(design 清单未列出)'
157
+ probe6.deletions.push({ path: posix, status: st, designOp: op || null, verdict })
158
+ }
159
+
160
+ return { probe1, probe3, probe5, probe6 }
161
+ }
162
+
163
+ /**
164
+ * 渲染探针结果为 markdown(可直接粘进 verify-result.md「探针结果」章节)。
165
+ */
166
+ export function renderVerifyProbesReport(result) {
167
+ const L = []
168
+ const { probe1, probe3, probe5, probe6 } = result
169
+
170
+ L.push('#### 探针 1:未实现标记扫描(design 清单文件)')
171
+ if (probe1.matches.length === 0) {
172
+ L.push('- ✅ 无 TODO/FIXME/尚未实现 标记命中')
173
+ } else {
174
+ for (const m of probe1.matches) L.push(`- ⚠️ \`${m.file}:${m.line}\` ${m.content}`)
175
+ }
176
+ if (probe1.globEntries.length > 0) L.push(`- ℹ️ glob 项未展开(agent 手动展开扫描):${probe1.globEntries.join('、')}`)
177
+ if (probe1.skippedFiles.length > 0) L.push(`- ℹ️ 清单文件不存在(跳过):${probe1.skippedFiles.join('、')}`)
178
+ L.push('')
179
+
180
+ L.push('#### 探针 2:设计关键词覆盖')
181
+ L.push('<!--TODO: 半语义探针——从 design 提取能力关键词逐个 grep 确认实现(agent 执行)-->')
182
+ L.push('')
183
+
184
+ L.push('#### 探针 3:验收标准测试覆盖')
185
+ if (probe3.tasks.length === 0) {
186
+ L.push('- ℹ️ tasks.md 无 checkbox 任务')
187
+ } else {
188
+ for (const t of probe3.tasks) {
189
+ if (!t.located) {
190
+ L.push(`- ⚠️ ${t.task}: 无 task 卡/allowed_paths,无法定位模块目录(agent 手查)`)
191
+ } else if (t.hasTest) {
192
+ L.push(`- ✅ ${t.task}: 模块目录(${t.moduleDirs.join('、')})找到 ${t.testFileCount} 个测试文件(${t.testFiles.slice(0, 5).join('、')}${t.testFileCount > 5 ? ' …' : ''})`)
193
+ } else {
194
+ L.push(`- ⚠️ ${t.task}: 模块目录(${t.moduleDirs.join('、')})递归未找到测试文件(含 co-located tests/)`)
195
+ }
196
+ }
197
+ }
198
+ L.push(`- ℹ️ ${probe3.note}`)
199
+ L.push('')
200
+
201
+ L.push('#### 探针 4:决策追踪覆盖')
202
+ L.push('<!--TODO: 语义探针——D-xxx@vN → FR-xxx → plan/task 引用 → 证据回指闭环(agent 执行)-->')
203
+ L.push('')
204
+
205
+ L.push('#### 探针 5:API Contract Parity')
206
+ L.push(`- ${probe5.summary || `backend ${probe5.backendCount ?? 0} 端点 / frontend ${probe5.frontendCount ?? 0} 调用`}`)
207
+ if ((probe5.missingBackend || []).length > 0) {
208
+ L.push('')
209
+ L.push('| 状态 | 前端调用 | 后端端点 | 文件 |')
210
+ L.push('|---|---|---|---|')
211
+ for (const m of probe5.missingBackend) {
212
+ L.push(`| ❌ missing | ${m.method} ${m.path} | — | ${m.consumerFile || '?'}${m.consumerLine ? ':' + m.consumerLine : ''} |`)
213
+ }
214
+ L.push('')
215
+ L.push('- ❌ contract gap 是真实集成缺陷——诚实判 FAIL 并回 execute 补端点(CLI 仅 advisory 不硬阻断)')
216
+ }
217
+ if ((probe5.unusedBackend || []).length > 0) {
218
+ L.push(`- ⚠️ ${probe5.unusedBackend.length} 个后端端点前端未调用(warning 不阻断):${probe5.unusedBackend.slice(0, 5).map(u => `${u.method} ${u.path}`).join('、')}${probe5.unusedBackend.length > 5 ? ' …' : ''}`)
219
+ }
220
+ L.push('')
221
+
222
+ L.push('#### 探针 6:代码删除对账')
223
+ if (probe6.deletions.length === 0) {
224
+ L.push('- ✅ git diff 无整文件删除(D/R/C)记录')
225
+ } else {
226
+ for (const d of probe6.deletions) {
227
+ L.push(`- ${d.verdict} \`${d.path}\`(git 状态 ${d.status})`)
228
+ }
229
+ }
230
+ L.push(`- ℹ️ ${probe6.note}`)
231
+ return L.join('\n')
232
+ }
233
+
234
+ /**
235
+ * 生成 verify-result.md 骨架(七章节;探针结果机械预填,语义章节 <!--TODO--> 占位)。
236
+ * 结论章节留「待填」——extractVerifyConclusion 无 PASS/FAIL 关键词即判不过,骨架不能直接过门。
237
+ * @returns {string|null} 骨架全文;无 design.md/tasks.md(非完整流程变更)→ null
238
+ */
239
+ export function generateVerifyResultSkeleton(result) {
240
+ const L = [
241
+ '# 验证报告(骨架由 `sillyspec verify-probes --change <变更名> --init` 生成)',
242
+ '',
243
+ '> 探针结果已机械预填;其余章节把 `<!--TODO-->` 替换为真实内容。**结论必须写明 PASS / FAIL**——',
244
+ '> 留「待填」会被 gate 判不过(fail-closed)。',
245
+ '',
246
+ '## 结论:<待填:PASS 或 FAIL(+一句话理由)>',
247
+ '',
248
+ '## 任务完成度',
249
+ '<!--TODO: 逐 task 对照 tasks.md 勾选与验收标准,完成/未完成/存疑三态-->',
250
+ '',
251
+ '## 设计一致性',
252
+ '<!--TODO: 实现与 design.md 的偏差(无偏差也显式写「一致」)-->',
253
+ '',
254
+ '## 探针结果(CLI 机械预填)',
255
+ renderVerifyProbesReport(result),
256
+ '',
257
+ '## 测试结果',
258
+ '<!--TODO: 测试命令 + 结果(通过数/失败数;known_failures 豁免逐条注明)-->',
259
+ '',
260
+ '## 决策追踪矩阵(如存在 decisions.md;无则删本节)',
261
+ '<!--TODO: | 决策 ID | FR | Task | Evidence | 状态 |(D-xxx@vN → FR-xxx → task → 证据回指闭环)-->',
262
+ '',
263
+ '## 技术债务',
264
+ '<!--TODO: TODO/FIXME/HACK 统计(探针 1 的命中已预填在上方探针结果)-->',
265
+ '',
266
+ '## 变更风险等级',
267
+ '<!--TODO: doc-only / unit-sufficient / contract-required / integration-critical / deployment-critical;若 design.md frontmatter 有 risk_level 显式声明,写明「显式声明 = <等级>」+ 理由(豁免可审计)-->',
268
+ '',
269
+ '## Runtime Evidence',
270
+ '<!--TODO: 关键命令输出/时间戳/commit hash 证据链;integration/deployment-critical 必填,按实际触碰的运行时组件写(启动命令/端点/请求响应/日志片段/生命周期终态断言/失败模式排除),未涉及的行写「不涉及」-->',
271
+ '',
272
+ '## 代码审查',
273
+ '<!--TODO: 问题列表 + 总体评价-->',
274
+ '',
275
+ ]
276
+ return L.join('\n')
277
+ }
@@ -0,0 +1,19 @@
1
+ import { readFileSync } from 'fs';
2
+ import { resolve, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+
7
+ /**
8
+ * 读取 package.json 版本号。
9
+ * 独立轻量模块:只依赖 fs/path/url,不拖入 init.js 的 @inquirer/prompts 等重型交互库。
10
+ * 供 index.js 顶部静态 import——--version 等高频路径不再为 getVersion 付 init.js 加载税。
11
+ */
12
+ export function getVersion() {
13
+ try {
14
+ const pkg = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf8'));
15
+ return pkg.version;
16
+ } catch {
17
+ return '?.?.?';
18
+ }
19
+ }