sillyspec 3.27.2 → 3.27.6

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 (398) 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/.worktrees/8d2d0392/.claude/skills/sillyspec-archive/SKILL.md +87 -0
  147. package/.worktrees/8d2d0392/.claude/skills/sillyspec-auto/SKILL.md +93 -0
  148. package/.worktrees/8d2d0392/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -0
  149. package/.worktrees/8d2d0392/.claude/skills/sillyspec-commit/SKILL.md +67 -0
  150. package/.worktrees/8d2d0392/.claude/skills/sillyspec-continue/SKILL.md +47 -0
  151. package/.worktrees/8d2d0392/.claude/skills/sillyspec-doctor/SKILL.md +76 -0
  152. package/.worktrees/8d2d0392/.claude/skills/sillyspec-execute/SKILL.md +214 -0
  153. package/.worktrees/8d2d0392/.claude/skills/sillyspec-explore/SKILL.md +102 -0
  154. package/.worktrees/8d2d0392/.claude/skills/sillyspec-export/SKILL.md +57 -0
  155. package/.worktrees/8d2d0392/.claude/skills/sillyspec-init/SKILL.md +177 -0
  156. package/.worktrees/8d2d0392/.claude/skills/sillyspec-knowledge/SKILL.md +270 -0
  157. package/.worktrees/8d2d0392/.claude/skills/sillyspec-plan/SKILL.md +156 -0
  158. package/.worktrees/8d2d0392/.claude/skills/sillyspec-propose/SKILL.md +87 -0
  159. package/.worktrees/8d2d0392/.claude/skills/sillyspec-quick/SKILL.md +116 -0
  160. package/.worktrees/8d2d0392/.claude/skills/sillyspec-resume/SKILL.md +70 -0
  161. package/.worktrees/8d2d0392/.claude/skills/sillyspec-scan/SKILL.md +79 -0
  162. package/.worktrees/8d2d0392/.claude/skills/sillyspec-state/SKILL.md +64 -0
  163. package/.worktrees/8d2d0392/.claude/skills/sillyspec-status/SKILL.md +54 -0
  164. package/.worktrees/8d2d0392/.claude/skills/sillyspec-verify/SKILL.md +87 -0
  165. package/.worktrees/8d2d0392/.claude/skills/sillyspec-workspace/SKILL.md +175 -0
  166. package/.worktrees/8d2d0392/.claude/skills/verify-per-user/SKILL.md +6 -0
  167. package/.worktrees/8d2d0392/.gitattributes +2 -0
  168. package/.worktrees/8d2d0392/.husky/pre-push +20 -0
  169. package/.worktrees/8d2d0392/README.md +203 -0
  170. package/.worktrees/8d2d0392/SKILL.md +91 -0
  171. package/.worktrees/8d2d0392/architecture-analysis.md +448 -0
  172. package/.worktrees/8d2d0392/bin/sillyspec.js +2 -0
  173. package/.worktrees/8d2d0392/eval/README.md +86 -0
  174. package/.worktrees/8d2d0392/eval/eval.config.json +28 -0
  175. package/.worktrees/8d2d0392/eval/lib/adapters/cli.mjs +63 -0
  176. package/.worktrees/8d2d0392/eval/lib/adapters/dryrun.mjs +27 -0
  177. package/.worktrees/8d2d0392/eval/lib/config.mjs +82 -0
  178. package/.worktrees/8d2d0392/eval/lib/db.mjs +76 -0
  179. package/.worktrees/8d2d0392/eval/lib/report.mjs +120 -0
  180. package/.worktrees/8d2d0392/eval/lib/runner.mjs +157 -0
  181. package/.worktrees/8d2d0392/eval/lib/tasks.mjs +73 -0
  182. package/.worktrees/8d2d0392/eval/lib/util.mjs +107 -0
  183. package/.worktrees/8d2d0392/eval/lib/workdir.mjs +30 -0
  184. package/.worktrees/8d2d0392/eval/run.mjs +83 -0
  185. package/.worktrees/8d2d0392/eval/tasks/001-slugify/files/package.json +6 -0
  186. package/.worktrees/8d2d0392/eval/tasks/001-slugify/solution/src/slugify.mjs +26 -0
  187. package/.worktrees/8d2d0392/eval/tasks/001-slugify/task.json +6 -0
  188. package/.worktrees/8d2d0392/eval/tasks/001-slugify/verify.mjs +28 -0
  189. package/.worktrees/8d2d0392/eval/tasks/002-each-day/files/package.json +6 -0
  190. package/.worktrees/8d2d0392/eval/tasks/002-each-day/files/src/dates.mjs +4 -0
  191. package/.worktrees/8d2d0392/eval/tasks/002-each-day/solution/src/dates.mjs +30 -0
  192. package/.worktrees/8d2d0392/eval/tasks/002-each-day/task.json +6 -0
  193. package/.worktrees/8d2d0392/eval/tasks/002-each-day/verify.mjs +31 -0
  194. package/.worktrees/8d2d0392/eval/tasks/003-word-report/files/data/notes.txt +5 -0
  195. package/.worktrees/8d2d0392/eval/tasks/003-word-report/files/package.json +6 -0
  196. package/.worktrees/8d2d0392/eval/tasks/003-word-report/solution/bin/report.mjs +36 -0
  197. package/.worktrees/8d2d0392/eval/tasks/003-word-report/task.json +6 -0
  198. package/.worktrees/8d2d0392/eval/tasks/003-word-report/verify.mjs +38 -0
  199. package/.worktrees/8d2d0392/logo.jpg +0 -0
  200. package/.worktrees/8d2d0392/package-lock.json +977 -0
  201. package/.worktrees/8d2d0392/package.json +41 -0
  202. package/.worktrees/8d2d0392/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -0
  203. package/.worktrees/8d2d0392/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -0
  204. package/.worktrees/8d2d0392/packages/dashboard/dist/favicon.jpg +0 -0
  205. package/.worktrees/8d2d0392/packages/dashboard/dist/index.html +16 -0
  206. package/.worktrees/8d2d0392/packages/dashboard/dist/logo.jpg +0 -0
  207. package/.worktrees/8d2d0392/packages/dashboard/dist/prototype-dashboard.html +836 -0
  208. package/.worktrees/8d2d0392/packages/dashboard/dist/prototype-overview.html +256 -0
  209. package/.worktrees/8d2d0392/packages/dashboard/package.json +25 -0
  210. package/.worktrees/8d2d0392/packages/dashboard/server/executor.js +95 -0
  211. package/.worktrees/8d2d0392/packages/dashboard/server/index.js +679 -0
  212. package/.worktrees/8d2d0392/packages/dashboard/server/parser.js +526 -0
  213. package/.worktrees/8d2d0392/packages/dashboard/server/watcher.js +357 -0
  214. package/.worktrees/8d2d0392/src/agent-session-log.js +803 -0
  215. package/.worktrees/8d2d0392/src/change-list.js +290 -0
  216. package/.worktrees/8d2d0392/src/change-risk-profile.js +339 -0
  217. package/.worktrees/8d2d0392/src/check-primitives.js +65 -0
  218. package/.worktrees/8d2d0392/src/classify-change.js +109 -0
  219. package/.worktrees/8d2d0392/src/commit-suggest.js +202 -0
  220. package/.worktrees/8d2d0392/src/config-cat.js +88 -0
  221. package/.worktrees/8d2d0392/src/config-schema.js +328 -0
  222. package/.worktrees/8d2d0392/src/constants.js +97 -0
  223. package/.worktrees/8d2d0392/src/contract-matrix.js +533 -0
  224. package/.worktrees/8d2d0392/src/datetime.js +20 -0
  225. package/.worktrees/8d2d0392/src/db-engine.js +55 -0
  226. package/.worktrees/8d2d0392/src/db.js +381 -0
  227. package/.worktrees/8d2d0392/src/decision-distill.js +517 -0
  228. package/.worktrees/8d2d0392/src/dispatch/backends/local-agent.js +155 -0
  229. package/.worktrees/8d2d0392/src/dispatch/backends/sillyhub-mcp.js +245 -0
  230. package/.worktrees/8d2d0392/src/dispatch/probe.js +246 -0
  231. package/.worktrees/8d2d0392/src/dispatch/strategy.js +121 -0
  232. package/.worktrees/8d2d0392/src/docs-check.js +825 -0
  233. package/.worktrees/8d2d0392/src/docs-debt.js +393 -0
  234. package/.worktrees/8d2d0392/src/docs-gate.js +107 -0
  235. package/.worktrees/8d2d0392/src/doctor-diagnostics.js +1099 -0
  236. package/.worktrees/8d2d0392/src/endpoint-extractor.js +409 -0
  237. package/.worktrees/8d2d0392/src/foreign-declared.js +78 -0
  238. package/.worktrees/8d2d0392/src/fs-atomic.js +73 -0
  239. package/.worktrees/8d2d0392/src/git-helper.js +150 -0
  240. package/.worktrees/8d2d0392/src/hooks/claude-pre-tool-use.cjs +130 -0
  241. package/.worktrees/8d2d0392/src/hooks/worktree-guard.js +892 -0
  242. package/.worktrees/8d2d0392/src/index.js +2854 -0
  243. package/.worktrees/8d2d0392/src/init.js +626 -0
  244. package/.worktrees/8d2d0392/src/knowledge-match.js +231 -0
  245. package/.worktrees/8d2d0392/src/local-detect.js +128 -0
  246. package/.worktrees/8d2d0392/src/local-register.js +104 -0
  247. package/.worktrees/8d2d0392/src/machine-interface.js +543 -0
  248. package/.worktrees/8d2d0392/src/migrate.js +127 -0
  249. package/.worktrees/8d2d0392/src/module-impact.js +166 -0
  250. package/.worktrees/8d2d0392/src/module-resolve.js +222 -0
  251. package/.worktrees/8d2d0392/src/modules.js +580 -0
  252. package/.worktrees/8d2d0392/src/plan-adopt-waves.js +126 -0
  253. package/.worktrees/8d2d0392/src/progress/change-registry.js +336 -0
  254. package/.worktrees/8d2d0392/src/progress/consistency-doctor.js +355 -0
  255. package/.worktrees/8d2d0392/src/progress/shared.js +36 -0
  256. package/.worktrees/8d2d0392/src/progress/stage-machine.js +594 -0
  257. package/.worktrees/8d2d0392/src/progress/step-store.js +265 -0
  258. package/.worktrees/8d2d0392/src/progress.js +1304 -0
  259. package/.worktrees/8d2d0392/src/quick-recommend.js +115 -0
  260. package/.worktrees/8d2d0392/src/quicklog.js +883 -0
  261. package/.worktrees/8d2d0392/src/review-tier.js +77 -0
  262. package/.worktrees/8d2d0392/src/run/command.js +1641 -0
  263. package/.worktrees/8d2d0392/src/run/complete-handlers.js +1552 -0
  264. package/.worktrees/8d2d0392/src/run/complete.js +1259 -0
  265. package/.worktrees/8d2d0392/src/run/concurrent-detect.js +156 -0
  266. package/.worktrees/8d2d0392/src/run/gates.js +1094 -0
  267. package/.worktrees/8d2d0392/src/run/multi-repo-context.js +208 -0
  268. package/.worktrees/8d2d0392/src/run/next.js +128 -0
  269. package/.worktrees/8d2d0392/src/run/prompt.js +843 -0
  270. package/.worktrees/8d2d0392/src/run/quick-audit.js +186 -0
  271. package/.worktrees/8d2d0392/src/run/scan-profile.js +269 -0
  272. package/.worktrees/8d2d0392/src/run/shared.js +1562 -0
  273. package/.worktrees/8d2d0392/src/run/stage.js +587 -0
  274. package/.worktrees/8d2d0392/src/run.js +23 -0
  275. package/.worktrees/8d2d0392/src/scan-diff.js +388 -0
  276. package/.worktrees/8d2d0392/src/scan-postcheck.js +524 -0
  277. package/.worktrees/8d2d0392/src/scan-staleness.js +95 -0
  278. package/.worktrees/8d2d0392/src/setup.js +414 -0
  279. package/.worktrees/8d2d0392/src/sillyhub-mcp/client.js +597 -0
  280. package/.worktrees/8d2d0392/src/sillyhub-mcp/config.js +62 -0
  281. package/.worktrees/8d2d0392/src/spec-dir-typo.js +50 -0
  282. package/.worktrees/8d2d0392/src/spec-sync.js +358 -0
  283. package/.worktrees/8d2d0392/src/stage-contract-engine.js +171 -0
  284. package/.worktrees/8d2d0392/src/stage-contract-spec.js +551 -0
  285. package/.worktrees/8d2d0392/src/stage-contract.js +948 -0
  286. package/.worktrees/8d2d0392/src/stage-review.js +597 -0
  287. package/.worktrees/8d2d0392/src/stages/archive.js +199 -0
  288. package/.worktrees/8d2d0392/src/stages/brainstorm-auto.js +285 -0
  289. package/.worktrees/8d2d0392/src/stages/brainstorm.js +624 -0
  290. package/.worktrees/8d2d0392/src/stages/cmd-existence.js +145 -0
  291. package/.worktrees/8d2d0392/src/stages/doctor.js +540 -0
  292. package/.worktrees/8d2d0392/src/stages/execute.js +1277 -0
  293. package/.worktrees/8d2d0392/src/stages/explore.js +43 -0
  294. package/.worktrees/8d2d0392/src/stages/index.js +29 -0
  295. package/.worktrees/8d2d0392/src/stages/knowledge.js +514 -0
  296. package/.worktrees/8d2d0392/src/stages/plan-postcheck.js +1398 -0
  297. package/.worktrees/8d2d0392/src/stages/plan.js +651 -0
  298. package/.worktrees/8d2d0392/src/stages/quick.js +167 -0
  299. package/.worktrees/8d2d0392/src/stages/scan.js +608 -0
  300. package/.worktrees/8d2d0392/src/stages/status.js +64 -0
  301. package/.worktrees/8d2d0392/src/stages/verify.js +246 -0
  302. package/.worktrees/8d2d0392/src/sync.js +1470 -0
  303. package/.worktrees/8d2d0392/src/task-review.js +1498 -0
  304. package/.worktrees/8d2d0392/src/taskcard-placeholders.js +21 -0
  305. package/.worktrees/8d2d0392/src/taskcard.js +217 -0
  306. package/.worktrees/8d2d0392/src/verify-postcheck.js +1718 -0
  307. package/.worktrees/8d2d0392/src/verify-probes.js +327 -0
  308. package/.worktrees/8d2d0392/src/version.js +19 -0
  309. package/.worktrees/8d2d0392/src/workflow.js +600 -0
  310. package/.worktrees/8d2d0392/src/workspace.js +125 -0
  311. package/.worktrees/8d2d0392/src/worktree-apply.js +1611 -0
  312. package/.worktrees/8d2d0392/src/worktree-deps.js +576 -0
  313. package/.worktrees/8d2d0392/src/worktree.js +1838 -0
  314. package/.worktrees/8d2d0392/templates/claude-instruction.md +23 -0
  315. package/.worktrees/8d2d0392/templates/prompts/taskcard-rules.md +29 -0
  316. package/.worktrees/8d2d0392/templates/prompts/testcase-design.md +12 -0
  317. package/.worktrees/8d2d0392/templates/prompts/verify-probes.md +32 -0
  318. package/.worktrees/8d2d0392/templates/workflows/archive-impact.yaml +79 -0
  319. package/.worktrees/8d2d0392/templates/workflows/scan-docs.yaml +132 -0
  320. package/architecture-analysis.md +448 -0
  321. package/eval/README.md +86 -0
  322. package/eval/eval.config.json +28 -0
  323. package/eval/lib/adapters/cli.mjs +63 -0
  324. package/eval/lib/adapters/dryrun.mjs +27 -0
  325. package/eval/lib/config.mjs +82 -0
  326. package/eval/lib/db.mjs +76 -0
  327. package/eval/lib/report.mjs +120 -0
  328. package/eval/lib/runner.mjs +157 -0
  329. package/eval/lib/tasks.mjs +73 -0
  330. package/eval/lib/util.mjs +107 -0
  331. package/eval/lib/workdir.mjs +30 -0
  332. package/eval/run.mjs +83 -0
  333. package/eval/tasks/001-slugify/files/package.json +6 -0
  334. package/eval/tasks/001-slugify/solution/src/slugify.mjs +26 -0
  335. package/eval/tasks/001-slugify/task.json +6 -0
  336. package/eval/tasks/001-slugify/verify.mjs +28 -0
  337. package/eval/tasks/002-each-day/files/package.json +6 -0
  338. package/eval/tasks/002-each-day/files/src/dates.mjs +4 -0
  339. package/eval/tasks/002-each-day/solution/src/dates.mjs +30 -0
  340. package/eval/tasks/002-each-day/task.json +6 -0
  341. package/eval/tasks/002-each-day/verify.mjs +31 -0
  342. package/eval/tasks/003-word-report/files/data/notes.txt +5 -0
  343. package/eval/tasks/003-word-report/files/package.json +6 -0
  344. package/eval/tasks/003-word-report/solution/bin/report.mjs +36 -0
  345. package/eval/tasks/003-word-report/task.json +6 -0
  346. package/eval/tasks/003-word-report/verify.mjs +38 -0
  347. package/package.json +4 -2
  348. package/src/agent-session-log.js +45 -5
  349. package/src/change-risk-profile.js +83 -14
  350. package/src/config-schema.js +18 -3
  351. package/src/contract-matrix.js +90 -29
  352. package/src/datetime.js +20 -0
  353. package/src/decision-distill.js +517 -0
  354. package/src/dispatch/backends/local-agent.js +4 -4
  355. package/src/docs-check.js +227 -0
  356. package/src/docs-debt.js +187 -24
  357. package/src/doctor-diagnostics.js +102 -2
  358. package/src/endpoint-extractor.js +41 -52
  359. package/src/foreign-declared.js +155 -0
  360. package/src/index.js +95 -13
  361. package/src/init.js +9 -7
  362. package/src/knowledge-match.js +107 -6
  363. package/src/local-register.js +7 -1
  364. package/src/module-resolve.js +12 -1
  365. package/src/modules.js +2 -1
  366. package/src/plan-adopt-waves.js +126 -0
  367. package/src/quicklog.js +50 -4
  368. package/src/run/command.js +124 -44
  369. package/src/run/complete-handlers.js +48 -19
  370. package/src/run/complete.js +5 -2
  371. package/src/run/gates.js +104 -22
  372. package/src/run/prompt.js +84 -42
  373. package/src/run/quick-audit.js +11 -0
  374. package/src/run/shared.js +152 -10
  375. package/src/run/stage.js +8 -5
  376. package/src/run.js +2 -2
  377. package/src/scan-postcheck.js +30 -17
  378. package/src/stage-contract-spec.js +3 -2
  379. package/src/stage-contract.js +68 -4
  380. package/src/stages/archive.js +53 -12
  381. package/src/stages/brainstorm-auto.js +50 -1
  382. package/src/stages/brainstorm.js +13 -0
  383. package/src/stages/doctor.js +157 -1
  384. package/src/stages/execute.js +32 -6
  385. package/src/stages/plan-postcheck.js +76 -17
  386. package/src/stages/plan.js +9 -4
  387. package/src/stages/quick.js +6 -1
  388. package/src/stages/verify.js +32 -15
  389. package/src/sync.js +41 -3
  390. package/src/task-review.js +2 -2
  391. package/src/taskcard-placeholders.js +21 -0
  392. package/src/taskcard.js +43 -1
  393. package/src/verify-postcheck.js +315 -13
  394. package/src/verify-probes.js +73 -10
  395. package/src/worktree-apply.js +569 -227
  396. package/src/worktree-deps.js +150 -1
  397. package/src/worktree.js +285 -77
  398. package/templates/prompts/taskcard-rules.md +2 -1
@@ -0,0 +1,388 @@
1
+ /**
2
+ * scan-diff — scan 文档 vs 源码漂移清单(change: 2026-08-16-scan-diff-command, task-01)
3
+ *
4
+ * 定位:scan 文档是架构快照(基线 = 生成时源码 commit,frontmatter source_commit),源码持续演进。
5
+ * scan-diff 算出「基线之后源码变了什么、哪些模块的 scan 文档需要核对/刷新」,按四分类输出:
6
+ * A 新增文件 → scan 文档缺记(缺文档)
7
+ * D 已删除文件 → scan 文档多记(多文档)
8
+ * M / R / C 修改 / 重命名 / 复制 → scan 文档过时
9
+ * 其余(T 等)→ unknown(不判漂移,只展示)
10
+ * 扫描范围缺省 = module-map paths 覆盖集(parseModuleMapSimple 收集全部 paths 去重),
11
+ * 非 src/-only——范围外的源码变更与模块文档无涉,不计入漂移。
12
+ *
13
+ * 分层:
14
+ * computeScanDiff —— 计算层(git 只读 + frontmatter/module-map 读取;无渲染无落盘)。
15
+ * 硬错误返回 { ok:false, error }(无效 base / git 失败 / 无 source_commit);
16
+ * advisory 问题进 warnings(非祖先 base / rev-list 降级 / module-map 缺失)。
17
+ * runScanDiff —— IO 面(终端聚合渲染 + --report 落盘 scan-diff-report.md),返回退出码。
18
+ *
19
+ * 复用(先读源码确认签名后使用):parseSourceCommit(scan-staleness)、
20
+ * matchFilesToModules(docs-debt,含裸名兜底)、safeGit(git-helper)、
21
+ * parseModuleMapSimple(modules.js);零新依赖。
22
+ */
23
+ import { existsSync, readFileSync, readdirSync, mkdirSync, writeFileSync } from 'node:fs'
24
+ import { join } from 'node:path'
25
+ import { safeGit } from './git-helper.js'
26
+ import { parseSourceCommit } from './scan-staleness.js'
27
+ import { matchFilesToModules } from './docs-debt.js'
28
+ import { parseModuleMapSimple } from './modules.js'
29
+
30
+ /** safeGit 超时(仿 docs-debt 的 GIT_TIMEOUT_MS 模式:大 diff 慢时降级不挂起) */
31
+ const GIT_TIMEOUT_MS = 10000
32
+
33
+ /** 每模块终端渲染默认上限(--full 展开全部) */
34
+ const DEFAULT_LIST_CAP = 5
35
+
36
+ /**
37
+ * 计算层:scan 文档 vs 源码漂移事实。
38
+ * @param {{ projectRoot: string, specBase: string, projectName: string, base?: string|null }} opts
39
+ * projectRoot:git 锚(diff/rev-list 的 -C 目录)
40
+ * specBase / projectName:scan 文档与 module-map 定位(specBase/docs/<projectName>/...)
41
+ * base:基线 commit(短/长哈希、引用均可);缺省读 scan 文档 frontmatter 的 source_commit
42
+ * @returns {{ ok: true, base, baseSource, head, behindCommits, daysSinceScan, scope,
43
+ * added, deleted, modified, unknown, unmapped, outOfScope, renameMap, byModule, warnings, driftCount }
44
+ * | { ok: false, error: string }}
45
+ * ok:false = 硬错误(参数缺失 / 无 source_commit / 无效 base / git diff 失败)
46
+ * added/deleted/modified/unknown:范围内变更文件(POSIX 相对路径;R/C 记新路径)
47
+ * unmapped:范围内未匹配到模块的文件;outOfScope:范围外变更(不计入漂移)
48
+ * renameMap:newPath → oldPath(R 归 modified 且保留 rename 细节)
49
+ * byModule:{ moduleId: { doc, added, deleted, modified, files } }(A/D/M 计数 + 文件清单)
50
+ * driftCount = added + deleted + modified
51
+ */
52
+ export function computeScanDiff({ projectRoot, specBase, projectName, base } = {}) {
53
+ if (!projectRoot || !specBase || !projectName) {
54
+ return { ok: false, error: 'projectRoot / specBase / projectName 必填,缺一不可' }
55
+ }
56
+ const warnings = []
57
+ let baseSource = '--base 显式指定'
58
+
59
+ // ── 基线:缺省读 specBase/docs/<projectName>/scan/*.md frontmatter 的 source_commit ──
60
+ if (!base) {
61
+ const sourceCommit = readSourceCommit(specBase, projectName)
62
+ if (!sourceCommit) {
63
+ return {
64
+ ok: false, error:
65
+ 'scan 文档无 source_commit 字段(绿地项目 / 旧版生成 / 无 scan 文档),无法定基线——请用 --base <commit> 显式指定基线',
66
+ }
67
+ }
68
+ base = sourceCommit
69
+ baseSource = 'scan 文档 frontmatter source_commit'
70
+ }
71
+ const baseDisplay = displayBase(base)
72
+
73
+ // ── base 有效性守卫(仿 computeScanStaleness 的 isAncestor 守卫)──
74
+ // merge-base --is-ancestor 对「无效 commit」与「有效但非祖先」都返回非 0(safeGit 记为 error),
75
+ // 无法直接区分——先用 rev-parse 验有效性:无效 → 报错;有效但非祖先 → 警告(diff 按两快照对比仍可算)。
76
+ const revRes = safeGit(projectRoot, ['rev-parse', '--verify', `${base}^{commit}`])
77
+ if (revRes.error || !revRes.value) {
78
+ return { ok: false, error: `基线 ${baseDisplay} 不是有效 commit(${revRes.error || '无输出'}),跳过 scan-diff` }
79
+ }
80
+ const resolvedBase = revRes.value.trim()
81
+ const headRes = safeGit(projectRoot, ['rev-parse', 'HEAD'])
82
+ const headShort = headRes.error ? 'HEAD' : headRes.value.trim().slice(0, 7)
83
+ const isAncestor = safeGit(projectRoot, ['merge-base', '--is-ancestor', resolvedBase, 'HEAD'])
84
+ if (isAncestor.error) {
85
+ warnings.push(`基线 ${baseDisplay} 不是 HEAD(${headShort})祖先(rebase / 分支切换)——diff 按两快照对比,删除后重建可能呈假新增`)
86
+ }
87
+
88
+ // ── 落后计数(advisory:超时/失败降级 null,不抛)──
89
+ const countRes = safeGit(projectRoot, ['rev-list', '--count', `${resolvedBase}..HEAD`], { timeout: GIT_TIMEOUT_MS })
90
+ let behindCommits = countRes.error ? null : parseInt(countRes.value, 10)
91
+ if (behindCommits === null || Number.isNaN(behindCommits)) {
92
+ behindCommits = null
93
+ warnings.push('rev-list 计数失败(git 降级),behindCommits=null')
94
+ }
95
+ const dateRes = safeGit(projectRoot, ['log', '-1', '--format=%cI', resolvedBase])
96
+ let daysSinceScan = null
97
+ if (!dateRes.error && dateRes.value) {
98
+ daysSinceScan = Math.floor((Date.now() - new Date(dateRes.value.trim()).getTime()) / 86400000)
99
+ }
100
+
101
+ // ── 扫描范围:module-map paths 覆盖集(默认范围,非 src/-only)──
102
+ const cardsDir = join(specBase, 'docs', projectName, 'modules')
103
+ const mapPath = join(cardsDir, '_module-map.yaml')
104
+ let moduleIndex = null
105
+ if (existsSync(mapPath)) {
106
+ try { moduleIndex = parseModuleMapSimple(readFileSync(mapPath, 'utf8')) } catch { moduleIndex = null }
107
+ }
108
+ const scope = collectScope(moduleIndex)
109
+ if (scope.length === 0) {
110
+ warnings.push('module-map 缺失 / 解析为空(跑 sillyspec modules rebuild 生成)——扫描范围退化=全部变更文件,无法归模块')
111
+ }
112
+
113
+ // ── diff:只算路径(--find-renames 检出重命名;超时降级为错误返回)──
114
+ const diffRes = safeGit(projectRoot, ['diff', '--name-status', '--find-renames', `${resolvedBase}..HEAD`], { timeout: GIT_TIMEOUT_MS })
115
+ if (diffRes.error) {
116
+ return { ok: false, error: `git diff ${baseDisplay}..HEAD 失败(${diffRes.error}),跳过 scan-diff` }
117
+ }
118
+
119
+ // ── 四分类 + 范围过滤 ──
120
+ const added = [], deleted = [], modified = [], unknown = [], outOfScope = []
121
+ const renameMap = {} // newPath → oldPath(R 归 modified 且保留 rename 细节)
122
+ const scopeFilter = scope.length > 0 ? scope : null
123
+ for (const item of parseNameStatus(diffRes.value)) {
124
+ if (scopeFilter && !inScope(item.path, scopeFilter)) { outOfScope.push(item.path); continue }
125
+ switch (item.status) {
126
+ case 'A': added.push(item.path); break
127
+ case 'D': deleted.push(item.path); break
128
+ case 'M': modified.push(item.path); break
129
+ case 'R': modified.push(item.path); renameMap[item.path] = item.oldPath; break
130
+ case 'C': modified.push(item.path); break // 复制目标(新路径)视同过时
131
+ default: unknown.push(item.path); break // T / B 等
132
+ }
133
+ }
134
+
135
+ // ── 归模块(matchFilesToModules,含裸名兜底;结果 = 同文件清单直接调用一致)──
136
+ const inScopeFiles = [...added, ...deleted, ...modified, ...unknown]
137
+ let byModuleMap = new Map()
138
+ let unmapped = inScopeFiles
139
+ if (inScopeFiles.length > 0) {
140
+ try {
141
+ const matched = matchFilesToModules(inScopeFiles, moduleIndex || {}, { cardsDir })
142
+ byModuleMap = matched.byModule
143
+ unmapped = matched.unmapped
144
+ } catch {
145
+ warnings.push('matchFilesToModules 归模块失败(降级为全部未归模块)')
146
+ }
147
+ } else {
148
+ unmapped = []
149
+ }
150
+
151
+ // ── byModule 聚合 A/D/M 计数 ──
152
+ const addedSet = new Set(added)
153
+ const deletedSet = new Set(deleted)
154
+ const modifiedSet = new Set(modified)
155
+ const byModule = {}
156
+ for (const [id, entry] of byModuleMap) {
157
+ let a = 0, d = 0, m = 0
158
+ for (const f of entry.files) {
159
+ if (addedSet.has(f)) a++
160
+ else if (deletedSet.has(f)) d++
161
+ else if (modifiedSet.has(f)) m++
162
+ }
163
+ byModule[id] = { doc: entry.doc || '', added: a, deleted: d, modified: m, files: entry.files }
164
+ }
165
+
166
+ return {
167
+ ok: true,
168
+ base: baseDisplay,
169
+ baseSource,
170
+ head: headShort,
171
+ behindCommits,
172
+ daysSinceScan,
173
+ scope,
174
+ added, deleted, modified, unknown, unmapped, outOfScope,
175
+ renameMap,
176
+ byModule,
177
+ warnings,
178
+ driftCount: added.length + deleted.length + modified.length,
179
+ }
180
+ }
181
+
182
+ /**
183
+ * IO 面:终端渲染 + 可选落盘报告。
184
+ * @param {{ projectRoot: string, specBase: string, projectName: string, base?: string|null,
185
+ * full?: boolean, report?: boolean }} opts
186
+ * full:终端展开每模块全部文件(缺省每模块 ≤5 条);report:落盘 scan-diff-report.md
187
+ * @returns {number} 退出码:0 无漂移 / 1 有漂移 / 2 错误(无效 base、git 失败、无 source_commit)
188
+ */
189
+ export function runScanDiff(opts = {}) {
190
+ const { projectRoot, specBase, projectName, base = null, full = false, report = false } = opts
191
+ const result = computeScanDiff({ projectRoot, specBase, projectName, base })
192
+ if (!result.ok) {
193
+ console.error(`scan-diff:${result.error}`)
194
+ return 2
195
+ }
196
+ for (const w of result.warnings) console.warn(` ⚠️ ${w}`)
197
+
198
+ console.log(`scan diff: ${result.base}..HEAD(${fmtNum(result.behindCommits)} commit / ${fmtNum(result.daysSinceScan)} 天)`)
199
+ if (result.driftCount === 0) {
200
+ console.log('scan 文档与源码一致(0 漂移)')
201
+ if (result.unknown.length > 0) {
202
+ console.log(` 无法归类(unknown)${result.unknown.length} 条:${result.unknown.join(', ')}`)
203
+ }
204
+ } else {
205
+ renderTerminal(result, { full })
206
+ }
207
+
208
+ if (report) {
209
+ const p = writeReport(result, { specBase, projectName })
210
+ if (p) console.log(`scan-diff 报告已落盘:${p}`)
211
+ else console.warn(' ⚠️ scan-diff-report.md 写入失败')
212
+ }
213
+ return result.driftCount > 0 ? 1 : 0
214
+ }
215
+
216
+ /** 终端聚合渲染:每模块计数 + 文件清单(缺省 ≤5 条,--full 展开全部) */
217
+ function renderTerminal(r, { full }) {
218
+ const cap = full ? Number.MAX_SAFE_INTEGER : DEFAULT_LIST_CAP
219
+ console.log(`扫描范围:module-map paths 覆盖集(${r.scope.length} 条路径)——范围外的变更不计入漂移`)
220
+ const moduleIds = Object.keys(r.byModule).sort()
221
+ if (moduleIds.length > 0) {
222
+ console.log('按模块聚合漂移(A=缺文档 D=多文档 M=过时,--full 展开全部):')
223
+ for (const id of moduleIds) {
224
+ const m = r.byModule[id]
225
+ const doc = m.doc ? `(${m.doc})` : ''
226
+ console.log(` [${id}] A${m.added} D${m.deleted} M${m.modified}${doc}`)
227
+ for (const f of m.files.slice(0, cap)) {
228
+ console.log(` - ${statusLetter(r, f)} ${f}${r.renameMap[f] ? `(原 ${r.renameMap[f]})` : ''}`)
229
+ }
230
+ if (m.files.length > cap) console.log(` ... 其余 ${m.files.length - cap} 条,--full 展开`)
231
+ }
232
+ }
233
+ if (r.unmapped.length > 0) {
234
+ console.log(`未归模块(unmapped,${r.unmapped.length} 条):`)
235
+ for (const f of r.unmapped.slice(0, cap)) {
236
+ console.log(` - ${statusLetter(r, f)} ${f}`)
237
+ }
238
+ if (r.unmapped.length > cap) console.log(` ... 其余 ${r.unmapped.length - cap} 条,--full 展开`)
239
+ }
240
+ if (r.unknown.length > 0) {
241
+ console.log(`无法归类(unknown,${r.unknown.length} 条):${r.unknown.slice(0, cap).join(', ')}`)
242
+ }
243
+ console.log(`漂移合计:${r.driftCount} 条(A${r.added.length} / D${r.deleted.length} / M${r.modified.length})`)
244
+ }
245
+
246
+ /** 文件归属状态字母(渲染用):R(renameMap 命中)> A > D > M */
247
+ function statusLetter(r, f) {
248
+ if (r.renameMap[f]) return 'R'
249
+ if (r.added.includes(f)) return 'A'
250
+ if (r.deleted.includes(f)) return 'D'
251
+ return 'M'
252
+ }
253
+
254
+ /** 落盘 scan-diff-report.md(markdown 完整清单,不截断) */
255
+ function writeReport(r, { specBase, projectName }) {
256
+ const dir = join(specBase, 'docs', projectName, 'scan')
257
+ try { mkdirSync(dir, { recursive: true }) } catch { return null }
258
+ const lines = [
259
+ '# scan-diff 报告',
260
+ '',
261
+ `- 生成时间:${new Date().toISOString()}`,
262
+ `- 项目:${projectName}`,
263
+ `- 基线:${r.base}(${r.baseSource})`,
264
+ `- 对比:\`${r.base}..HEAD\``,
265
+ `- 落后:${fmtNum(r.behindCommits)} commit / ${fmtNum(r.daysSinceScan)} 天`,
266
+ `- 扫描范围:module-map paths 覆盖集(${r.scope.length} 条路径)`,
267
+ '',
268
+ '## 漂移总览',
269
+ '',
270
+ '| 分类 | 含义 | 数量 |',
271
+ '| --- | --- | --- |',
272
+ `| added | 源码新增,scan 文档缺记 | ${r.added.length} |`,
273
+ `| deleted | 源码已删,scan 文档多记 | ${r.deleted.length} |`,
274
+ `| modified | 源码已变,scan 文档过时 | ${r.modified.length} |`,
275
+ `| unknown | 其他状态 | ${r.unknown.length} |`,
276
+ `| unmapped | 范围内未匹配模块 | ${r.unmapped.length} |`,
277
+ '',
278
+ '## 按模块聚合',
279
+ '',
280
+ ]
281
+ if (Object.keys(r.byModule).length === 0) {
282
+ lines.push('(无模块命中)', '')
283
+ } else {
284
+ for (const id of Object.keys(r.byModule).sort()) {
285
+ const m = r.byModule[id]
286
+ lines.push(`### ${id}(${m.doc || '无 doc'})`, '')
287
+ lines.push(`- added:${m.added}`, `- deleted:${m.deleted}`, `- modified:${m.modified}`, '- 文件:')
288
+ for (const f of m.files) {
289
+ const ren = r.renameMap[f] ? `,原 ${r.renameMap[f]}` : ''
290
+ lines.push(` - \`${f}\`(${statusLetter(r, f)}${ren})`)
291
+ }
292
+ lines.push('')
293
+ }
294
+ }
295
+ if (r.unmapped.length > 0) {
296
+ lines.push('## 未归模块(unmapped)', '')
297
+ for (const f of r.unmapped) lines.push(`- \`${f}\``)
298
+ lines.push('')
299
+ }
300
+ if (r.outOfScope.length > 0) {
301
+ lines.push('## 范围外变更(不计入漂移)', '')
302
+ for (const f of r.outOfScope) lines.push(`- \`${f}\``)
303
+ lines.push('')
304
+ }
305
+ if (r.warnings.length > 0) {
306
+ lines.push('## 提示', '')
307
+ for (const w of r.warnings) lines.push(`- ${w}`)
308
+ lines.push('')
309
+ }
310
+ lines.push('## 结论', '')
311
+ lines.push(`- 漂移合计:${r.driftCount} 条(A${r.added.length} / D${r.deleted.length} / M${r.modified.length})`)
312
+ if (r.driftCount === 0) {
313
+ lines.push('- scan 文档与源码一致(0 漂移)')
314
+ } else {
315
+ lines.push('- 存在漂移:请按需核对并刷新对应 scan 文档(sillyspec run scan)')
316
+ }
317
+ lines.push('')
318
+ lines.push('> 由 sillyspec scan-diff 自动生成;改动源码后请按需核对 scan 文档。')
319
+ const outPath = join(dir, 'scan-diff-report.md')
320
+ try {
321
+ writeFileSync(outPath, lines.join('\n') + '\n', 'utf8')
322
+ return outPath
323
+ } catch {
324
+ return null
325
+ }
326
+ }
327
+
328
+ /** 读 scan 目录任一 .md 的 source_commit(同 modules.js 写入同值,取首个命中;全失败 → null) */
329
+ function readSourceCommit(specBase, projectName) {
330
+ const scanDir = join(specBase, 'docs', projectName, 'scan')
331
+ if (!existsSync(scanDir)) return null
332
+ try {
333
+ for (const f of readdirSync(scanDir)) {
334
+ if (!f.endsWith('.md')) continue
335
+ const c = parseSourceCommit(readFileSync(join(scanDir, f), 'utf8'))
336
+ if (c) return c
337
+ }
338
+ } catch { /* 读失败 → null */ }
339
+ return null
340
+ }
341
+
342
+ /** 收集 module-map 全部 paths 去重(归一 Windows 反斜杠 + 去尾部斜杠;无 paths → 空数组) */
343
+ function collectScope(moduleIndex) {
344
+ const set = new Set()
345
+ for (const m of Object.values(moduleIndex || {})) {
346
+ if (!Array.isArray(m.paths)) continue
347
+ for (const raw of m.paths) {
348
+ if (typeof raw !== 'string' || !raw) continue
349
+ set.add(raw.replace(/\\/g, '/').replace(/\/+$/, ''))
350
+ }
351
+ }
352
+ return [...set]
353
+ }
354
+
355
+ /** 路径是否落在范围(p 可为目录或文件:精确相等或目录前缀) */
356
+ function inScope(filePath, scope) {
357
+ return scope.some((p) => p && (filePath === p || filePath.startsWith(p + '/')))
358
+ }
359
+
360
+ /** 解析 `git diff --name-status --find-renames` 输出 → [{ status, path, oldPath? }](R/C 双路径) */
361
+ function parseNameStatus(out) {
362
+ const items = []
363
+ if (!out) return items
364
+ for (const line of out.split('\n')) {
365
+ const l = line.replace(/\r$/, '')
366
+ if (!l) continue
367
+ const tab = l.indexOf('\t')
368
+ if (tab === -1) continue
369
+ const m = l.slice(0, tab).match(/^([A-Z])(\d*)$/)
370
+ if (!m) continue
371
+ const status = m[1]
372
+ const parts = l.slice(tab + 1).split('\t')
373
+ const item = { status, path: parts[parts.length - 1] }
374
+ if ((status === 'R' || status === 'C') && parts.length >= 2) item.oldPath = parts[0]
375
+ items.push(item)
376
+ }
377
+ return items
378
+ }
379
+
380
+ /** 40 位全哈希缩成 7 位显示;引用/短哈希原样 */
381
+ function displayBase(b) {
382
+ return /^[0-9a-f]{40}$/i.test(b) ? b.slice(0, 7) : b
383
+ }
384
+
385
+ /** null/NaN → '?'(降级显示) */
386
+ function fmtNum(n) {
387
+ return n === null || Number.isNaN(n) ? '?' : String(n)
388
+ }