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,409 @@
1
+ /**
2
+ * endpoint-extractor.js — 从代码中提取 HTTP 端点定义和调用
3
+ *
4
+ * provider 端:扫描 router 文件,提取注册的 API 路径
5
+ * consumer 端:扫描前端文件,提取 apiFetch/request 调用路径
6
+ *
7
+ * 契约对账时:provider 产出 ≠ consumer 消费 → gap
8
+ */
9
+
10
+ import { existsSync, readFileSync, readdirSync, statSync } from 'fs'
11
+ import { join, extname, basename, dirname, resolve } from 'path'
12
+ import { execSync } from 'child_process'
13
+
14
+ // ─── Provider: 扫描后端 router 注册的端点 ───────────────────────────────
15
+
16
+ /**
17
+ * matchIndex → 装饰器/路由起始行号(全文匹配配套;1-based)。
18
+ * 坑 endpoint-multiline-decorator-miss(2026-08-24 用户实证):三框架提取器原为逐行匹配,
19
+ * 多行装饰器(`@router.get(\n "/path",\n response_model=…)` 等)路径不在装饰器行 → 端点
20
+ * 静默漏 → endpoints.json artifact 与 live 扫描双失真 → probe5 missingBackend 误报(11 个
21
+ * 存量端点)。装饰器匹配改为全文正则(`\s*` 天然跨换行),行号取起始行。
22
+ */
23
+ function lineOfIndex(content, index) {
24
+ return content.slice(0, index).split('\n').length
25
+ }
26
+
27
+ /**
28
+ * 从单个文件提取 FastAPI router 端点
29
+ * 支持 APIRouter(prefix=...) 和 @router.get/post/put/delete/patch("/path")
30
+ *
31
+ * @param {string} filePath - 文件绝对路径
32
+ * @returns {Array<{ method: string, path: string, source: string, line: number }>}
33
+ */
34
+ export function extractFastApiEndpoints(filePath) {
35
+ const content = readFileSync(filePath, 'utf8')
36
+ const lines = content.split('\n')
37
+ const endpoints = []
38
+
39
+ // 1. 提取 router prefix(逐行,同文件近似——维持原行为)
40
+ let routerPrefix = ''
41
+ for (const line of lines) {
42
+ const prefixMatch = line.match(/(?:APIRouter|router)\s*\(\s*(?:prefix\s*=\s*)?["'`]([^"'`]+)["'`]/)
43
+ || line.match(/\.include_router\s*\([^)]*prefix\s*=\s*["'`]([^"'`]+)["'`]/)
44
+ if (prefixMatch) {
45
+ routerPrefix = prefixMatch[1]
46
+ }
47
+ }
48
+
49
+ // 2. 装饰器全文匹配:单行 `@router.get("/path")`、分散式 `@router.get\n("/path")`、
50
+ // 多行参数 `@router.get(\n "/path",\n response_model=…)` 三态合一(\s* 跨换行)
51
+ const decoratorRe = /@(?:router|api_router)\.(get|post|put|delete|patch)\s*\(\s*["'`]([^"'`]+)["'`]/g
52
+ let m
53
+ while ((m = decoratorRe.exec(content)) !== null) {
54
+ endpoints.push({
55
+ method: m[1].toUpperCase(),
56
+ path: routerPrefix + m[2],
57
+ source: filePath,
58
+ line: lineOfIndex(content, m.index),
59
+ })
60
+ }
61
+
62
+ return endpoints
63
+ }
64
+
65
+ /**
66
+ * 从单个文件提取 Express(Node)路由端点。
67
+ * 支持 router/app.get|post|put|delete|patch("path") 与链式 router.route("path").<method>()。
68
+ * app.use("prefix", router) 的前缀只做同文件近似(跨文件追踪不在首版范围)。
69
+ *
70
+ * @param {string} filePath - 文件绝对路径
71
+ * @returns {Array<{ method: string, path: string, source: string, line: number }>}
72
+ */
73
+ export function extractExpressEndpoints(filePath) {
74
+ const content = readFileSync(filePath, 'utf8')
75
+ const lines = content.split('\n')
76
+ const endpoints = []
77
+
78
+ // 同文件内最后一个 app.use("prefix", ...) 作为前缀近似
79
+ let routerPrefix = ''
80
+ for (const line of lines) {
81
+ const useMatch = line.match(/\bapp\.use\s*\(\s*["'`]([^"'`]+)["'`]/)
82
+ if (useMatch) routerPrefix = useMatch[1]
83
+ }
84
+
85
+ // 路由全文匹配(坑 endpoint-multiline-decorator-miss:`router.get(\n "/path",\n handler)` 同病)
86
+ const routeRe = /\b(?:app|router)\.(get|post|put|delete|patch)\s*\(\s*["'`]([^"'`]+)["'`]/gi
87
+ let m
88
+ while ((m = routeRe.exec(content)) !== null) {
89
+ endpoints.push({ method: m[1].toUpperCase(), path: routerPrefix + m[2], source: filePath, line: lineOfIndex(content, m.index) })
90
+ }
91
+
92
+ // 链式 .route(path).get(h).put(h) —— 同行所有 method 配上 path(跨行链式不扩面,维持逐行)
93
+ for (let i = 0; i < lines.length; i++) {
94
+ const line = lines[i]
95
+ const routeChain = line.match(/\.route\s*\(\s*["'`]([^"'`]+)["'`]/)
96
+ if (routeChain) {
97
+ const chainPath = routerPrefix + routeChain[1]
98
+ for (const mm of line.matchAll(/\.(get|post|put|delete|patch)\s*\(/gi)) {
99
+ endpoints.push({ method: mm[1].toUpperCase(), path: chainPath, source: filePath, line: i + 1 })
100
+ }
101
+ }
102
+ }
103
+
104
+ return endpoints
105
+ }
106
+
107
+ /**
108
+ * 从单个文件提取 Spring(Java)端点。
109
+ * 支持 @GetMapping/@PostMapping/.../@PatchMapping 与旧式 @RequestMapping(value="path", method=RequestMethod.X);
110
+ * 类级 @RequestMapping("api") 前缀合并(取文件内第一个作类前缀,近似)。
111
+ *
112
+ * @param {string} filePath - 文件绝对路径
113
+ * @returns {Array<{ method: string, path: string, source: string, line: number }>}
114
+ */
115
+ export function extractSpringEndpoints(filePath) {
116
+ const content = readFileSync(filePath, 'utf8')
117
+ const lines = content.split('\n')
118
+ const endpoints = []
119
+
120
+ let classPrefix = ''
121
+ for (const line of lines) {
122
+ const cm = line.match(/@RequestMapping\s*\(\s*(?:value\s*=\s*)?["'`]([^"'`]+)["'`]/)
123
+ if (cm) { classPrefix = cm[1]; break }
124
+ }
125
+
126
+ const methodMap = { get: 'GET', post: 'POST', put: 'PUT', delete: 'DELETE', patch: 'PATCH' }
127
+
128
+ // 短形式全文匹配(坑 endpoint-multiline-decorator-miss:`@GetMapping(\n "/path"\n)` 同病)
129
+ const shortRe = /@(Get|Post|Put|Delete|Patch)Mapping\s*\(\s*(?:value\s*=\s*)?["'`]([^"'`]+)["'`]/gi
130
+ let m
131
+ while ((m = shortRe.exec(content)) !== null) {
132
+ endpoints.push({ method: methodMap[m[1].toLowerCase()], path: classPrefix + m[2], source: filePath, line: lineOfIndex(content, m.index) })
133
+ }
134
+ // 旧形式全文匹配([^)]*? 字符类可跨行,`@RequestMapping(\n value="/p",\n method=…)` 随全文化一并覆盖)
135
+ const oldRe = /@RequestMapping\s*\(\s*(?:value\s*=\s*)?["'`]([^"'`]+)["'`][^)]*?method\s*=\s*RequestMethod\.(\w+)/gi
136
+ while ((m = oldRe.exec(content)) !== null) {
137
+ endpoints.push({ method: m[2].toUpperCase(), path: classPrefix + m[1], source: filePath, line: lineOfIndex(content, m.index) })
138
+ }
139
+
140
+ return endpoints
141
+ }
142
+
143
+ /**
144
+ * 从目录递归扫描后端端点(多框架)。
145
+ * 按扩展名分派:.py→FastAPI / .js,.ts→Express / .java→Spring。
146
+ * 保留 excludePatterns(向后兼容 opts);filePattern 在多框架分派下不再适用。
147
+ * @param {string} dir
148
+ * @param {{ excludePatterns?: RegExp[] }} opts
149
+ * @returns {Array<{ method: string, path: string, source: string, line: number }>}
150
+ */
151
+ export function scanBackendEndpoints(dir, opts = {}) {
152
+ // 排除清单对齐 scanFrontendApiCalls(坑 parity-scan-stale-dirs:.claude/worktrees 陈旧检出
153
+ // 与各类 build 产物混入端点扫描全是噪音)——backend 侧另含 __pycache__/.gradle/maven target
154
+ const excludePatterns = opts.excludePatterns || [
155
+ /^__pycache__$/, /^node_modules$/, /^\.venv$/, /^venv$/, /^\.git$/, /^\.gradle$/,
156
+ /^\.claude$/, /^\.vscode$/, /^\.idea$/, /^\.cursor$/, /^\.next$/, /^\.nuxt$/,
157
+ /^\.output$/, /^\.turbo$/, /^\.parcel-cache$/, /^coverage$/,
158
+ /^\.sillyspec$/, /^\.worktrees$/, /^\.d\.ts$/,
159
+ /^(dist|build|target|out)$/i,
160
+ /test/i,
161
+ ]
162
+
163
+ const results = []
164
+ if (!existsSync(dir)) return results
165
+
166
+ function walk(d) {
167
+ for (const entry of readdirSync(d, { withFileTypes: true })) {
168
+ const full = join(d, entry.name)
169
+ if (entry.isDirectory()) {
170
+ if (excludePatterns.some(p => p.test(entry.name))) continue
171
+ walk(full)
172
+ } else if (entry.isFile()) {
173
+ const ext = extname(entry.name).toLowerCase()
174
+ try {
175
+ if (ext === '.py') results.push(...extractFastApiEndpoints(full))
176
+ else if (ext === '.js' || ext === '.ts') results.push(...extractExpressEndpoints(full))
177
+ else if (ext === '.java') results.push(...extractSpringEndpoints(full))
178
+ } catch {}
179
+ }
180
+ }
181
+ }
182
+ walk(dir)
183
+ return results
184
+ }
185
+
186
+ // ─── Consumer: 扫描前端 API 调用路径 ─────────────────────────────────────
187
+
188
+ /**
189
+ * 从前端文件提取 API 调用路径
190
+ * 支持:
191
+ * apiFetch("/api/xxx")
192
+ * request("/api/xxx")
193
+ * axios.get("/api/xxx")
194
+ * axios.post("/api/xxx")
195
+ * fetch("/api/xxx")
196
+ *
197
+ * @param {string} filePath
198
+ * @returns {Array<{ method: string, path: string, source: string, line: number, raw: string }>}
199
+ */
200
+ export function extractFrontendApiCalls(filePath) {
201
+ const content = readFileSync(filePath, 'utf8')
202
+ const lines = content.split('\n')
203
+ const results = []
204
+
205
+ for (let i = 0; i < lines.length; i++) {
206
+ const line = lines[i]
207
+
208
+ // Pattern 1: apiFetch<T>("/path", { method: "POST" }) — default GET
209
+ const apiFetchMatch = line.match(/apiFetch\s*(?:<[^>]*>)?\s*\(\s*["'`]([^"'`]+)["'`]/)
210
+ if (apiFetchMatch) {
211
+ // 检查是否有 method 字段(在后续行或同一行)
212
+ let method = 'GET'
213
+ const snippet = lines.slice(i, Math.min(i + 3, lines.length)).join(' ')
214
+ const methodMatch = snippet.match(/method\s*:\s*["'`](GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)["'`]/i)
215
+ if (methodMatch) method = methodMatch[1].toUpperCase()
216
+
217
+ results.push({
218
+ method,
219
+ path: normalizePath(apiFetchMatch[1]),
220
+ source: filePath,
221
+ line: i + 1,
222
+ raw: apiFetchMatch[1],
223
+ })
224
+ continue
225
+ }
226
+
227
+ // Pattern 2: axios.get/post/put/delete("/path")
228
+ const axiosMatch = line.match(/axios\.(get|post|put|delete|patch)\s*\(\s*["'`]([^"'`]+)["'`]/i)
229
+ if (axiosMatch) {
230
+ results.push({
231
+ method: axiosMatch[1].toUpperCase(),
232
+ path: normalizePath(axiosMatch[2]),
233
+ source: filePath,
234
+ line: i + 1,
235
+ raw: axiosMatch[2],
236
+ })
237
+ continue
238
+ }
239
+
240
+ // Pattern 3: fetch("/api/xxx", { method: "POST" })
241
+ const fetchMatch = line.match(/(?:api_?)?fetch\s*\(\s*["'`]([^"'`]+)["'`]/)
242
+ if (fetchMatch && !apiFetchMatch) {
243
+ let method = 'GET'
244
+ const snippet = lines.slice(i, Math.min(i + 3, lines.length)).join(' ')
245
+ const methodMatch = snippet.match(/method\s*:\s*["'`](GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)["'`]/i)
246
+ if (methodMatch) method = methodMatch[1].toUpperCase()
247
+
248
+ results.push({
249
+ method,
250
+ path: normalizePath(fetchMatch[1]),
251
+ source: filePath,
252
+ line: i + 1,
253
+ raw: fetchMatch[1],
254
+ })
255
+ continue
256
+ }
257
+ }
258
+
259
+ return results
260
+ }
261
+
262
+ /**
263
+ * 递归扫描前端目录的 API 调用
264
+ * @param {string} dir
265
+ * @param {{ filePattern?: RegExp, excludePatterns?: RegExp[] }} opts
266
+ * @returns {Array<{ method: string, path: string, source: string, line: number, raw: string }>}
267
+ */
268
+ export function scanFrontendApiCalls(dir, opts = {}) {
269
+ const filePattern = opts.filePattern || /\.(ts|tsx|js|jsx)$/
270
+ // 排除清单(坑 parity-scan-stale-dirs,2026-08-21 实证:扫进 .claude/worktrees/agent-* 陈旧
271
+ // 检出与 build 产物,872 条调用全是噪音)——目录名匹配,覆盖:
272
+ // 版本控制/编辑器:.git .claude(含 worktrees/agent-* 陈旧检出).vscode .idea .cursor
273
+ // 构建产物:dist build out .next .nuxt .output .turbo .parcel-cache coverage
274
+ // 依赖/运行时:node_modules .venv venv target(rust/java)__pycache__
275
+ // 本工具自身:.sillyspec .sillyspec-platform.json 所在 .worktrees
276
+ // 文件级:.d.ts 声明、*.min.js / *.bundle.js 压缩产物(正则命中即跳)
277
+ const excludePatterns = opts.excludePatterns || [
278
+ /^node_modules$/, /^\.git$/, /^\.claude$/, /^\.vscode$/, /^\.idea$/, /^\.cursor$/,
279
+ /^dist$/, /^build$/, /^out$/, /^\.next$/, /^\.nuxt$/, /^\.output$/, /^\.turbo$/,
280
+ /^\.parcel-cache$/, /^coverage$/, /^\.venv$/, /^venv$/, /^target$/, /^__pycache__$/,
281
+ /^\.sillyspec$/, /^\.worktrees$/, /^__tests__$/, /^\.d\.ts$/,
282
+ ]
283
+
284
+ const results = []
285
+ if (!existsSync(dir)) return results
286
+
287
+ function walk(d) {
288
+ for (const entry of readdirSync(d, { withFileTypes: true })) {
289
+ const full = join(d, entry.name)
290
+ if (entry.isDirectory()) {
291
+ if (excludePatterns.some(p => p.test(entry.name))) continue
292
+ walk(full)
293
+ } else if (entry.isFile() && filePattern.test(entry.name)) {
294
+ if (/\.d\.ts$/.test(entry.name)) continue
295
+ if (/\.min\.(js|ts)$/.test(entry.name) || /\.bundle\.(js|ts)$/.test(entry.name)) continue
296
+ try {
297
+ results.push(...extractFrontendApiCalls(full))
298
+ } catch {}
299
+ }
300
+ }
301
+ }
302
+ walk(dir)
303
+ return results
304
+ }
305
+
306
+ // ─── 路径归一化 ─────────────────────────────────────────────────────────
307
+
308
+ /**
309
+ * 将动态路径归一化为参数占位符
310
+ * /api/ppm/project-plan/${id}/plan-nodes → /api/ppm/project-plan/{param}/plan-nodes
311
+ * /api/ppm/project-plan/:planId/plan-nodes → /api/ppm/project-plan/{param}/plan-nodes
312
+ * @param {string} rawPath
313
+ * @returns {string}
314
+ */
315
+ export function normalizePath(rawPath) {
316
+ return rawPath
317
+ .replace(/\$\{[^}]+\}/g, '{param}')
318
+ .replace(/:\w+/g, '{param}')
319
+ .replace(/\$\w+/g, '{param}')
320
+ .replace(/\{[^}]+\}/g, '{param}') // Spring {planId} / FastAPI {plan_id} → {param}
321
+ }
322
+
323
+ // ─── 对账 ───────────────────────────────────────────────────────────────
324
+
325
+ /**
326
+ * 比较前端调用的路径和后端注册的端点,返回差异
327
+ *
328
+ * @param {Array<{ path: string, method: string, source: string }>} frontendCalls
329
+ * @param {Array<{ path: string, method: string, source: string }>} backendEndpoints
330
+ * @returns {{
331
+ * missingBackend: Array<{ path: string, method: string, consumerFile: string, consumerLine: number }>,
332
+ * unusedBackend: Array<{ path: string, method: string, providerFile: string }>
333
+ * }}
334
+ */
335
+ export function diffApiParity(frontendCalls, backendEndpoints) {
336
+ // 构建 backend 注册表:归一化 path + method → endpoint
337
+ const backendMap = new Map()
338
+ for (const ep of backendEndpoints) {
339
+ const key = `${ep.method}:${normalizePath(ep.path)}`
340
+ if (!backendMap.has(key)) backendMap.set(key, ep)
341
+ }
342
+
343
+ const missingBackend = []
344
+ for (const call of frontendCalls) {
345
+ const key = `${call.method}:${normalizePath(call.path)}`
346
+ if (!backendMap.has(key)) {
347
+ missingBackend.push({
348
+ path: normalizePath(call.path),
349
+ method: call.method,
350
+ consumerFile: call.source,
351
+ consumerLine: call.line,
352
+ })
353
+ }
354
+ }
355
+
356
+ // 构建 frontend 调用表
357
+ const frontendSet = new Set(
358
+ frontendCalls.map(c => `${c.method}:${normalizePath(c.path)}`)
359
+ )
360
+
361
+ const unusedBackend = []
362
+ for (const ep of backendEndpoints) {
363
+ const key = `${ep.method}:${normalizePath(ep.path)}`
364
+ if (!frontendSet.has(key)) {
365
+ unusedBackend.push({
366
+ path: normalizePath(ep.path),
367
+ method: ep.method,
368
+ providerFile: ep.source,
369
+ })
370
+ }
371
+ }
372
+
373
+ return { missingBackend, unusedBackend, ok: missingBackend.length === 0 }
374
+ }
375
+
376
+ // ─── CLI 入口 ────────────────────────────────────────────────────────────
377
+
378
+ /**
379
+ * CLI 子命令入口:sillyspec contract scan [--backend dir] [--frontend dir]
380
+ * 输出 JSON 格式的端点清单和对账结果
381
+ */
382
+ export async function contractScan(args, cwd) {
383
+ const backendIdx = args.indexOf('--backend')
384
+ const frontendIdx = args.indexOf('--frontend')
385
+ const backendDir = backendIdx !== -1 && args[backendIdx + 1]
386
+ ? resolve(cwd, args[backendIdx + 1])
387
+ : resolve(cwd, 'backend')
388
+ const frontendDir = frontendIdx !== -1 && args[frontendIdx + 1]
389
+ ? resolve(cwd, args[frontendIdx + 1])
390
+ : resolve(cwd, 'frontend')
391
+
392
+ const backendEndpoints = scanBackendEndpoints(backendDir)
393
+ const frontendCalls = scanFrontendApiCalls(frontendDir)
394
+ const { missingBackend, unusedBackend } = diffApiParity(frontendCalls, backendEndpoints)
395
+
396
+ return {
397
+ backend: backendEndpoints.map(e => ({ method: e.method, path: normalizePath(e.path), file: e.source })),
398
+ frontend: frontendCalls.map(c => ({ method: c.method, path: normalizePath(c.path), file: c.source })),
399
+ missingBackend,
400
+ unusedBackend,
401
+ summary: {
402
+ backendEndpointCount: backendEndpoints.length,
403
+ frontendCallCount: frontendCalls.length,
404
+ missingBackendCount: missingBackend.length,
405
+ unusedBackendCount: unusedBackend.length,
406
+ ok: missingBackend.length === 0,
407
+ },
408
+ }
409
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * 他者会话显式声明文件集(坑 verify-reconcile-foreign-wip,2026-08-23 实证:verify 实测
3
+ * 对账在主仓共享工作区取 git diff,无 meta 回退时把并行会话在途 WIP 全量算进本变更——误
4
+ * 命中他者模块跑他者测试 / 他者删除进本变更删除对账误报)。
5
+ *
6
+ * 归属口径与坑 35(quick 审计豁免,run/shared 的 collectOtherQuickSessionDeclarations)
7
+ * 同款:只认**显式声明**——其他 quick 会话 guard.allowedFiles + 其他变更 design §6 清单;
8
+ * 无主文件保留参与判定(fail-closed,不放松「未声明删除/越界」的抓取底线)。
9
+ *
10
+ * 独立零环模块:被 verify-postcheck / verify-probes / contract-matrix 共用(verify-
11
+ * postcheck import contract-matrix,parity 侧不能反向 import verify-postcheck)。
12
+ */
13
+ import { existsSync, readFileSync, readdirSync } from 'fs'
14
+ import { join } from 'path'
15
+ import { parseFileChangeListDetailed } from './change-list.js'
16
+
17
+ /**
18
+ * @param {string} cwd 项目根
19
+ * @param {string|null} currentChangeName 本变更名(quick 会话名或 change 名,排除自身)
20
+ * @returns {Map<string, string[]>} file → 声明者列表(quick-<hex> 或变更名)
21
+ */
22
+ export function collectForeignDeclaredFiles(cwd, currentChangeName) {
23
+ const foreign = new Map()
24
+ const add = (file, owner) => {
25
+ const f = String(file || '').replace(/\\/g, '/')
26
+ if (!f) return
27
+ if (!foreign.has(f)) foreign.set(f, [])
28
+ if (!foreign.get(f).includes(owner)) foreign.get(f).push(owner)
29
+ }
30
+ try {
31
+ const specBase = join(cwd, '.sillyspec')
32
+ // ① 其他 quick 会话的 --files 显式声明(guard.json)
33
+ const sessionsDir = join(specBase, '.runtime', 'quick-sessions')
34
+ let sessionDirs = []
35
+ try { sessionDirs = readdirSync(sessionsDir, { withFileTypes: true }).filter(e => e.isDirectory()).map(e => e.name) } catch {}
36
+ for (const sid of sessionDirs) {
37
+ if (sid === currentChangeName) continue
38
+ try {
39
+ const guard = JSON.parse(readFileSync(join(sessionsDir, sid, 'guard.json'), 'utf8'))
40
+ for (const f of Array.isArray(guard.allowedFiles) ? guard.allowedFiles : []) add(f, sid)
41
+ } catch { /* 损坏/缺失跳过 */ }
42
+ }
43
+ // ② 其他变更的 design §6 清单(目录在即视为在途变更;archive/ 下不算)
44
+ const changesDir = join(specBase, 'changes')
45
+ let changeDirs = []
46
+ try { changeDirs = readdirSync(changesDir, { withFileTypes: true }).filter(e => e.isDirectory()).map(e => e.name) } catch {}
47
+ for (const cn of changeDirs) {
48
+ if (cn === currentChangeName || cn === 'archive') continue
49
+ const designPath = join(changesDir, cn, 'design.md')
50
+ if (!existsSync(designPath)) continue
51
+ try {
52
+ for (const e of parseFileChangeListDetailed(designPath)) add(e.path, cn)
53
+ } catch { /* 清单解析失败跳过该变更 */ }
54
+ }
55
+ } catch { /* 整体异常 → 空集(等价不过滤,回到原行为) */ }
56
+ return foreign
57
+ }
58
+
59
+ /**
60
+ * 按他者声明集切分 diff 文件(collectForeignDeclaredFiles 的消费端快捷封装)。
61
+ * @param {string} cwd 项目根
62
+ * @param {string|null} currentChangeName 本变更名
63
+ * @param {string[]|null} diffFiles 待切分文件列表(null 透传,保持调用方 null 语义)
64
+ * @returns {{ own: string[]|null, foreign: Array<{file: string, owners: string[]}> }}
65
+ */
66
+ export function splitOwnVsForeignDiffFiles(cwd, currentChangeName, diffFiles) {
67
+ if (!Array.isArray(diffFiles)) return { own: diffFiles, foreign: [] }
68
+ const foreignMap = collectForeignDeclaredFiles(cwd, currentChangeName)
69
+ if (foreignMap.size === 0) return { own: diffFiles, foreign: [] }
70
+ const own = []
71
+ const foreign = []
72
+ for (const f of diffFiles) {
73
+ const owners = foreignMap.get(String(f).replace(/\\/g, '/'))
74
+ if (owners) foreign.push({ file: f, owners })
75
+ else own.push(f)
76
+ }
77
+ return { own, foreign }
78
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * fs-atomic.js — 原子文件写 + Windows 友好的 rename 重试
3
+ *
4
+ * 用于 .runtime/*.json、pointer、guard.json 等「会被其他进程 / hook 读取」的配置文件。
5
+ * 保证读者要么看到旧完整内容、要么看到新完整内容,不会读到半截 JSON。
6
+ *
7
+ * 注意:DB 持久化由 node:sqlite 引擎承担(提交即落盘 sillyspec.db + WAL 侧车),
8
+ * 不经此处的 writeAtomicSync;sillyspec.db 不走原子写改名。
9
+ */
10
+ import { writeFileSync, renameSync, unlinkSync } from 'fs';
11
+ import { randomBytes } from 'node:crypto';
12
+ import { dirname, basename, join } from 'path';
13
+
14
+ // Windows 上杀毒 / 索引 / IDE 占用文件时,rename 偶发这些错误,短退避重试即可
15
+ const RENAME_RETRY_CODES = ['EPERM', 'EBUSY', 'EACCES', 'ENOTEMPTY'];
16
+
17
+ /** 同步退避:优先 Atomics.wait,不可用时退化忙等(仅在 rename 冲突的极端情况触发)。 */
18
+ function sleepSync(ms) {
19
+ try {
20
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
21
+ } catch {
22
+ const end = Date.now() + ms;
23
+ while (Date.now() < end) { /* busy wait */ }
24
+ }
25
+ }
26
+
27
+ /**
28
+ * rename 带退避重试,覆盖 Windows 偶发的 EPERM/EBUSY/EACCES/ENOTEMPTY。
29
+ * 目录 rename 同样适用(归档移动目录时可复用)。
30
+ * @param {string} from
31
+ * @param {string} to
32
+ * @param {number} [retries=5]
33
+ */
34
+ export function renameSyncRetry(from, to, retries = 5) {
35
+ let lastErr;
36
+ for (let attempt = 0; attempt <= retries; attempt++) {
37
+ try {
38
+ renameSync(from, to);
39
+ return;
40
+ } catch (err) {
41
+ lastErr = err;
42
+ const code = err && err.code;
43
+ // 非可重试错误码立即抛出,不浪费时间
44
+ if (!code || !RENAME_RETRY_CODES.includes(code)) throw err;
45
+ if (attempt < retries) sleepSync(15 * (attempt + 1));
46
+ }
47
+ }
48
+ throw lastErr;
49
+ }
50
+
51
+ /**
52
+ * 原子写文本文件:同目录 tmp 写入 → rename 覆盖目标(带 Windows 退避重试)。
53
+ * - tmp 名含 pid,避免多进程并发写同一目标时 tmp 名碰撞(如多会话写 local.yaml)。
54
+ * - rename 失败时清理 tmp,避免留下孤儿 .tmp 文件。
55
+ * - Node 的 rename 在 Windows 上用 MoveFileEx(REPLACE_EXISTING),可覆盖已存在目标;
56
+ * EPERM 仅在文件被其他进程锁定时出现,由 renameSyncRetry 退避重试。
57
+ * @param {string} filePath - 目标文件绝对路径
58
+ * @param {string} content - 文件内容
59
+ */
60
+ export function writeAtomicSync(filePath, content) {
61
+ const dir = dirname(filePath);
62
+ // pid + 随机段双因子:Windows PID 重用激进,两进程可能撞同 pid,tmp 名碰撞 rename 互相覆盖。
63
+ // crypto 随机源(Math.random 可预测,tmp 名被预猜中有 symlink 预放置理论面)
64
+ const rnd = randomBytes(6).toString('hex');
65
+ const tmpPath = join(dir, `.${basename(filePath)}.${process.pid}.${rnd}.tmp`);
66
+ writeFileSync(tmpPath, content);
67
+ try {
68
+ renameSyncRetry(tmpPath, filePath);
69
+ } catch (err) {
70
+ try { unlinkSync(tmpPath); } catch { /* tmp 可能已被 rename 走,忽略 */ }
71
+ throw err;
72
+ }
73
+ }