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,65 @@
1
+ /**
2
+ * check-primitives.js — 共享的产物字面校验原语(纯函数)
3
+ *
4
+ * 从 workflow.js 的 checkOutput / _checkWorkflow 抽出,作为"何谓 contains_sections / min_lines /
5
+ * no_placeholder / no_empty_files"的**全仓单一语义源**。workflow 引擎与 stage-contract 引擎
6
+ * (stage-contract-engine.js)共用同一组谓词,杜绝"workflow 认 `## X` 子串、stage-contract 认别的"
7
+ * 这类判定分叉(本任务的"事前==事后"同源在原语层就成立)。
8
+ *
9
+ * 设计原则:纯文本判定,不碰 fs。调用方负责读文件 + CRLF 归一化后传入 content——
10
+ * 这样 Windows CRLF 差异在调用方收敛(plan-postcheck 已用 LF-normalizing reader),引擎拿到
11
+ * 什么判什么,不在原语层二次引入 readFileSync 差异(缺陷 windows-python-crlf-taskcard 的根因)。
12
+ */
13
+
14
+ // no_placeholder 的默认占位词。与原 workflow.js checkOutput/_checkWorkflow 逐字一致——
15
+ // 改动会破坏存量扫描文档的占位判定,属契约级常量。
16
+ const DEFAULT_PLACEHOLDER_PATTERNS = [
17
+ '待补充', 'TODO', 'TBD', '未分析', '根据项目情况', '根据实际情况', '按需填写',
18
+ ]
19
+
20
+ /** 内容非空(trim 后有字符)。用于 no_empty_files。 */
21
+ export function contentNonEmpty(content) {
22
+ return typeof content === 'string' && content.trim().length > 0
23
+ }
24
+
25
+ /** 行数(split('\n'))。用于 min_lines 的 detail 文案与判定。 */
26
+ export function lineCount(content) {
27
+ return typeof content === 'string' ? content.split('\n').length : 0
28
+ }
29
+
30
+ /** 行数 ≥ min。用于 min_lines 判定。 */
31
+ export function meetsMinLines(content, min) {
32
+ return lineCount(content) >= min
33
+ }
34
+
35
+ /**
36
+ * 返回缺失的章节(sections 中未以 `## <section>` 子串出现的)。
37
+ * 用于 contains_sections——判定与原 workflow.js 逐字一致(`## ${s}` 前缀子串匹配)。
38
+ */
39
+ export function missingSections(content, sections) {
40
+ if (typeof content !== 'string') return sections.slice()
41
+ return sections.filter(s => !content.includes(`## ${s}`))
42
+ }
43
+
44
+ /** 是否含全部章节(missingSections 为空的语法糖)。 */
45
+ export function hasAllSections(content, sections) {
46
+ return missingSections(content, sections).length === 0
47
+ }
48
+
49
+ /**
50
+ * 返回命中的占位词(patterns 中匹配"独立成行"的)。
51
+ * 用于 no_placeholder——正则 `^\s*[-*]?\s*<p>\s*$`(m flag)与原 workflow.js 逐字一致。
52
+ * patterns 为空/未传时用 DEFAULT_PLACEHOLDER_PATTERNS(等价原 `check.patterns || [defaults]`)。
53
+ */
54
+ export function placeholderLineMatches(content, patterns) {
55
+ if (typeof content !== 'string') return []
56
+ const pats = (patterns && patterns.length > 0) ? patterns : DEFAULT_PLACEHOLDER_PATTERNS
57
+ return pats.filter(p => new RegExp(`^\\s*[-*]?\\s*${p}\\s*$`, 'm').test(content))
58
+ }
59
+
60
+ /** 无占位词命中(placeholderLineMatches 为空的语法糖)。 */
61
+ export function hasNoPlaceholderLine(content, patterns) {
62
+ return placeholderLineMatches(content, patterns).length === 0
63
+ }
64
+
65
+ export { DEFAULT_PLACEHOLDER_PATTERNS }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * classify-change.js — 变更规模分类器
3
+ *
4
+ * 将用户需求描述分类为 quick / auto / full,
5
+ * 供 auto 模式决定内部流程深度。
6
+ */
7
+
8
+ import { existsSync, readFileSync } from 'fs'
9
+ import { join } from 'path'
10
+ import jsYaml from 'js-yaml'
11
+
12
+ /**
13
+ * 用户显式指定的关键词 → 强制模式
14
+ */
15
+ const FORCE_FULL_PATTERNS = [
16
+ /数据库|database|schema/i,
17
+ /迁移|migration|migrate/i,
18
+ /鉴权|权限|auth|permission|rbac/i,
19
+ /支付|payment|billing/i,
20
+ /重构|refactor.*architectur/i,
21
+ /微服务|microserv/i,
22
+ ]
23
+
24
+ const FORCE_QUICK_PATTERNS = [
25
+ /fix typo/i,
26
+ /更新文案|改文案|文案修改/i,
27
+ /样式调整|style.*tweak/i,
28
+ /修复.*\s*\bbug\b.*\bfix\b/i,
29
+ ]
30
+
31
+ /**
32
+ * 读 local.yaml 的 auto_mode 段(best-effort,绝不抛)。
33
+ * 供 runCommand 接线:传给 classifyChange 的 localConfig,让 force_*_patterns 真生效。
34
+ * @param {string} cwd - 主仓库根(local.yaml 在 <cwd>/.sillyspec/local.yaml)
35
+ * @returns {{ force_full_patterns?: string[], force_quick_patterns?: string[] } | null}
36
+ * auto_mode 段含至少一个 string pattern → 归一化数组;文件缺/解析失败/无段/空 → null(降级默认关键词)
37
+ */
38
+ export function readAutoModeFromLocalYaml(cwd) {
39
+ try {
40
+ const p = join(cwd, '.sillyspec', 'local.yaml')
41
+ if (!existsSync(p)) return null
42
+ const doc = jsYaml.load(readFileSync(p, 'utf8'))
43
+ if (!doc || typeof doc !== 'object') return null
44
+ const am = doc.auto_mode
45
+ if (!am || typeof am !== 'object') return null
46
+ const ffp = Array.isArray(am.force_full_patterns) ? am.force_full_patterns.filter(s => typeof s === 'string') : []
47
+ const fqp = Array.isArray(am.force_quick_patterns) ? am.force_quick_patterns.filter(s => typeof s === 'string') : []
48
+ if (ffp.length === 0 && fqp.length === 0) return null
49
+ return { force_full_patterns: ffp, force_quick_patterns: fqp }
50
+ } catch {
51
+ return null
52
+ }
53
+ }
54
+
55
+ /**
56
+ * 分类变更规模
57
+ * @param {object} opts
58
+ * @param {string} opts.description - 用户需求描述
59
+ * @param {string} [opts.explicitMode] - 用户显式指定的模式(auto/quick/full)
60
+ * @param {object} [opts.localConfig] - local.yaml auto_mode 段(由 readAutoModeFromLocalYaml 读出)
61
+ * @returns {{ mode: 'quick'|'auto'|'full', reason: string }}
62
+ */
63
+ export function classifyChange({ description = '', explicitMode, localConfig } = {}) {
64
+ // 1. 用户显式指定优先级最高
65
+ if (explicitMode && ['quick', 'auto', 'full'].includes(explicitMode)) {
66
+ return { mode: explicitMode, reason: '用户显式指定' }
67
+ }
68
+
69
+ // 2. local.yaml force patterns
70
+ if (localConfig) {
71
+ const forceFullPatterns = localConfig.force_full_patterns || []
72
+ const forceQuickPatterns = localConfig.force_quick_patterns || []
73
+
74
+ for (const pattern of forceFullPatterns) {
75
+ try {
76
+ if (new RegExp(pattern, 'i').test(description)) {
77
+ return { mode: 'full', reason: `local.yaml force_full_pattern 匹配: ${pattern}` }
78
+ }
79
+ } catch {
80
+ // 非法正则跳过,不崩 auto 分类(review-2026-08-09 #30)
81
+ }
82
+ }
83
+ for (const pattern of forceQuickPatterns) {
84
+ try {
85
+ if (new RegExp(pattern, 'i').test(description)) {
86
+ return { mode: 'quick', reason: `local.yaml force_quick_pattern 匹配: ${pattern}` }
87
+ }
88
+ } catch {
89
+ // 非法正则跳过
90
+ }
91
+ }
92
+ }
93
+
94
+ // 3. 默认关键词匹配
95
+ for (const pattern of FORCE_QUICK_PATTERNS) {
96
+ if (pattern.test(description)) {
97
+ return { mode: 'quick', reason: `命中 quick 关键词: ${pattern.source}` }
98
+ }
99
+ }
100
+
101
+ for (const pattern of FORCE_FULL_PATTERNS) {
102
+ if (pattern.test(description)) {
103
+ return { mode: 'full', reason: `命中 full 关键词: ${pattern.source}` }
104
+ }
105
+ }
106
+
107
+ // 4. 默认 auto
108
+ return { mode: 'auto', reason: '默认模式' }
109
+ }
@@ -0,0 +1,202 @@
1
+ /**
2
+ * commit-suggest.js — `sillyspec commit` 的语义收集 + commit message 建议生成
3
+ * (2026-08-21 agent-手工产出审计第二批 G1-G3)。
4
+ *
5
+ * commit skill 此前让 agent 手工收集语义:取 LAST_COMMIT_TIME、cat 多处 QUICKLOG、
6
+ * 扫变更目录、按时间戳过滤已勾 task、按路径模式识别来源阶段——进度库/quicklog/
7
+ * git 全在 CLI 手里,这是「CLI 有数据却让 agent 重新收集」的最典型案例。
8
+ *
9
+ * 本模块一条命令产出全部上下文 + conventional commits 建议 message + 可照抄的
10
+ * git commit 命令。**只建议不执行**(对齐 skill 绝对规则「不要自动提交」——确认权在人)。
11
+ *
12
+ * 语义来源(与 skill 手工流程同源):
13
+ * A. QUICKLOG:specBase/quicklog/QUICKLOG-<user>.md + changes/<change>/quicklog/ 下的归属条目,
14
+ * 条目时间 > 上次 commit 时间者(首次提交全收)
15
+ * B. execute 已勾 task:活跃变更 tasks.md 的 [x] 行(tasks.md mtime > 上次 commit 才计入,
16
+ * 防旧变更全勾 task 永久污染建议)
17
+ * C. 阶段产出:changed paths 按路径模式归 scan/brainstorm/plan/execute/archive/knowledge
18
+ */
19
+ import { existsSync, readdirSync, readFileSync, statSync } from 'fs'
20
+ import { join } from 'path'
21
+ import { gitQuiet } from './git-helper.js'
22
+ import { resolveSpecDir } from './run/shared.js'
23
+
24
+ function listActiveChanges(specBase) {
25
+ try {
26
+ return readdirSync(join(specBase, 'changes'), { withFileTypes: true })
27
+ .filter(e => e.isDirectory() && e.name !== 'archive')
28
+ .map(e => e.name)
29
+ .sort()
30
+ } catch {
31
+ return []
32
+ }
33
+ }
34
+
35
+ /**
36
+ * 解析 QUICKLOG 文件里的条目(## ql-xxx | time | title + 状态:行)
37
+ */
38
+ function parseQuicklogEntries(file) {
39
+ let content
40
+ try {
41
+ content = readFileSync(file, 'utf8')
42
+ } catch {
43
+ return []
44
+ }
45
+ const entries = []
46
+ const lines = content.split('\n')
47
+ let cur = null
48
+ for (const line of lines) {
49
+ const h = line.match(/^## (ql-\S+) \| ([0-9: -]+?) \| (.*)$/)
50
+ if (h) {
51
+ if (cur) entries.push(cur)
52
+ cur = { qlId: h[1], time: h[2].trim(), title: h[3].trim(), status: '未知' }
53
+ continue
54
+ }
55
+ if (cur) {
56
+ const s = line.match(/^状态[::]\s*(\S+)/)
57
+ if (s) cur.status = s[1]
58
+ }
59
+ }
60
+ if (cur) entries.push(cur)
61
+ return entries
62
+ }
63
+
64
+ function classifyStagePath(p) {
65
+ const posix = p.replace(/\\/g, '/')
66
+ if (/^\.sillyspec\/docs\/[^/]+\/scan\//.test(posix)) return 'scan'
67
+ if (/^\.sillyspec\/changes\/archive\//.test(posix)) return 'archive'
68
+ if (/^\.sillyspec\/changes\/[^/]+\/(proposal|design)\.md/.test(posix)) return 'brainstorm'
69
+ if (/^\.sillyspec\/changes\/[^/]+\/(tasks|plan)\.md/.test(posix)) return 'plan'
70
+ if (/^\.sillyspec\/knowledge\//.test(posix)) return 'knowledge'
71
+ return null
72
+ }
73
+
74
+ /**
75
+ * 收集提交上下文并生成建议 message。
76
+ * @param {{ cwd: string, specDir?: string|null }} opts
77
+ * @returns {{
78
+ * lastCommitTime: string|null, hasChanges: boolean, changedCount: number, stat: string,
79
+ * quickEntries: Array<{qlId,time,title,status,file}>, checkedTasks: Array<{change,item}>,
80
+ * stageArtifacts: string[], suggestion: {type, subject, body}|null
81
+ * }}
82
+ */
83
+ export function collectCommitContext({ cwd, specDir = null }) {
84
+ const specBase = resolveSpecDir(cwd, { specDir })
85
+ const lastCommitTime = gitQuiet(cwd, ['log', '-1', '--format=%ci'])
86
+ const lastCommitKey = lastCommitTime ? lastCommitTime.slice(0, 19) : null
87
+
88
+ // -uall:未跟踪目录不折叠(.sillyspec/ 整体未跟踪时默认 porcelain 只给一行 `?? .sillyspec/`,
89
+ // 路径模式分类会瞎;-uall 逐文件列出,scan/brainstorm/plan 分类才有输入)
90
+ const porcelain = gitQuiet(cwd, ['status', '--porcelain', '-uall']) || ''
91
+ const changedLines = porcelain.split('\n').filter(Boolean)
92
+ const changedPaths = changedLines.map(l => l.slice(3).trim()).filter(Boolean)
93
+ const untrackedCount = changedLines.filter(l => l.startsWith('??')).length
94
+ const stat = gitQuiet(cwd, ['diff', 'HEAD', '--stat']) || (changedPaths.length > 0 ? `(${changedPaths.length} 个文件待首提交)` : '')
95
+
96
+ // ── 来源 A:QUICKLOG(主 quicklog + 各活跃变更归属 quicklog)──
97
+ const quickFiles = []
98
+ try {
99
+ for (const f of readdirSync(join(specBase, 'quicklog'))) {
100
+ if (/^QUICKLOG.*\.md$/.test(f)) quickFiles.push(join(specBase, 'quicklog', f))
101
+ }
102
+ } catch {}
103
+ for (const c of listActiveChanges(specBase)) {
104
+ try {
105
+ for (const f of readdirSync(join(specBase, 'changes', c, 'quicklog'))) {
106
+ if (f.endsWith('.md')) quickFiles.push(join(specBase, 'changes', c, 'quicklog', f))
107
+ }
108
+ } catch {}
109
+ }
110
+ const quickEntries = quickFiles
111
+ .flatMap(file => parseQuicklogEntries(file).map(e => ({ ...e, file })))
112
+ .filter(e => !lastCommitKey || e.time > lastCommitKey)
113
+ .sort((a, b) => (a.time < b.time ? -1 : 1))
114
+ const doneQuick = quickEntries.filter(e => e.status === '已完成')
115
+
116
+ // ── 来源 B:活跃变更已勾 task(tasks.md mtime 晚于上次 commit 才计入)──
117
+ const lastCommitMs = lastCommitTime ? Date.parse(lastCommitTime) : null
118
+ const checkedTasks = []
119
+ for (const c of listActiveChanges(specBase)) {
120
+ const tasksPath = join(specBase, 'changes', c, 'tasks.md')
121
+ if (!existsSync(tasksPath)) continue
122
+ if (lastCommitMs !== null && !Number.isNaN(lastCommitMs)) {
123
+ try {
124
+ // epoch 对比(非字符串):git %ci 带本地时区偏移,Date.parse 归一;字符串对比会因
125
+ // UTC/本地时差把刚改过的 tasks.md 误判为早于上次 commit 而漏收
126
+ if (statSync(tasksPath).mtime.getTime() <= lastCommitMs) continue
127
+ } catch { continue }
128
+ }
129
+ for (const line of readFileSync(tasksPath, 'utf8').split('\n')) {
130
+ const m = line.match(/^[-*]\s*\[[xX]\]\s*(task-\d+)\s*[::]?\s*(.*)/)
131
+ if (m) checkedTasks.push({ change: c, item: `${m[1]}: ${m[2].trim()}` })
132
+ }
133
+ }
134
+
135
+ // ── 来源 C:阶段产出(changed paths 按模式归类去重)──
136
+ const stageSet = new Set()
137
+ for (const p of changedPaths) {
138
+ const stage = classifyStagePath(p)
139
+ if (stage) stageSet.add(stage)
140
+ }
141
+ const stageArtifacts = [...stageSet]
142
+
143
+ // ── 建议 message(对齐 commit skill 的 type 表)──
144
+ let suggestion = null
145
+ const bodyLines = []
146
+ const hasQuick = doneQuick.length > 0
147
+ const hasTasks = checkedTasks.length > 0
148
+ const hasStages = stageArtifacts.length > 0
149
+
150
+ if (hasQuick || hasTasks || hasStages) {
151
+ let type
152
+ let subject
153
+ if (hasTasks && !hasQuick) {
154
+ type = 'feat'
155
+ const byChange = new Map()
156
+ for (const t of checkedTasks) {
157
+ if (!byChange.has(t.change)) byChange.set(t.change, [])
158
+ byChange.get(t.change).push(t.item)
159
+ }
160
+ const [firstChange, items] = [...byChange][0]
161
+ subject = `feat: ${firstChange} 完成 task ×${items.length}`
162
+ } else if (hasQuick && !hasTasks) {
163
+ type = 'fix'
164
+ if (doneQuick.length === 1) {
165
+ subject = `fix: ${doneQuick[0].title}`
166
+ } else if (doneQuick.length === 2) {
167
+ subject = `fix: ${doneQuick[0].title};${doneQuick[1].title}`
168
+ } else {
169
+ subject = `fix: quick 修复 ${doneQuick.length} 项`
170
+ }
171
+ } else if (hasTasks) {
172
+ type = 'feat'
173
+ subject = `feat: 变更交付 + quick 修复 ${doneQuick.length} 项`
174
+ } else {
175
+ type = 'fix'
176
+ subject = `fix: quick 修复 + 阶段产出`
177
+ }
178
+ if (doneQuick.length >= 2 || hasTasks || hasStages) {
179
+ for (const q of doneQuick) bodyLines.push(`- quick ${q.qlId}: ${q.title}`)
180
+ }
181
+ for (const t of checkedTasks) bodyLines.push(`- ${t.change} ${t.item}`)
182
+ for (const s of stageArtifacts) bodyLines.push(`- 阶段产出: ${s}`)
183
+ if (!hasStages && !hasTasks && doneQuick.length === 1) bodyLines.length = 0
184
+ suggestion = {
185
+ type,
186
+ subject: subject.length > 100 ? subject.slice(0, 100) + '…' : subject,
187
+ body: bodyLines.length > 0 ? bodyLines.join('\n') : '',
188
+ }
189
+ }
190
+
191
+ return {
192
+ lastCommitTime,
193
+ hasChanges: changedPaths.length > 0,
194
+ changedCount: changedPaths.length,
195
+ untrackedCount,
196
+ stat,
197
+ quickEntries,
198
+ checkedTasks,
199
+ stageArtifacts,
200
+ suggestion,
201
+ }
202
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * config-cat.js — local.yaml 实际值读取的权威路径解析器(`sillyspec config cat` 数据源)。
3
+ *
4
+ * 背景(2026-08-21 root-local-yaml 治理):`config schema` 只列键不含值,agent 拿实际配置
5
+ * 只能翻文件——worktree 内 `.sillyspec/` 是 checkout 副本,gitignored 的 local.yaml 不随
6
+ * checkout 出现,agent 找不到就漂去项目根乱找甚至乱建。cat 按「文件存在」解析真实路径,
7
+ * agent 不需要知道文件在哪。
8
+ *
9
+ * 候选链(首个「文件存在」者胜):
10
+ * 1. specBase(--spec-dir / 平台 pointer / resolveSpecDir 祖先链首个 .sillyspec 目录)——
11
+ * resolveSpecDir 按「目录存在」命中,worktree 内会停在副本目录,文件并不在那里;
12
+ * 2. cwd 祖先链逐级 <dir>/.sillyspec/local.yaml——worktree 副本无此文件会继续向上命中主仓
13
+ * (sillyspec worktree 恒挂在 <主仓>/.sillyspec/.runtime/worktrees/ 下,祖先链必达);
14
+ * 3. git common-dir 反推主仓根——用户手动 `git worktree add` 到任意位置时的兜底。
15
+ *
16
+ * 候选链不含项目根 <dir>/local.yaml——那里从来没有这个文件(hook 同步拦写)。
17
+ * 纯只读;平台 pointer 异常由调用方吞掉走祖先链(真实 reader 各自 fail-closed,cat 是
18
+ * 只读便利口,不必加重)。
19
+ */
20
+ import { existsSync } from 'fs'
21
+ import { join, resolve, dirname } from 'path'
22
+ import { gitQuiet } from './git-helper.js'
23
+
24
+ /**
25
+ * 解析真实 local.yaml 路径。
26
+ * @param {string} dir 起点目录(CLI --dir / cwd)
27
+ * @param {{ specBase?: string|null }} [opts] specBase = resolvePlatformSpecDir 预解析结果(可 null)
28
+ * @returns {{ exists: boolean, path: string|null, source: string|null, searched: string[] }}
29
+ * searched 为候选链前几个路径(未命中时供人看「找过哪里」,最多 6 条)
30
+ */
31
+ export function resolveLocalYaml(dir, opts = {}) {
32
+ const start = resolve(dir || process.cwd())
33
+ const seen = new Set()
34
+ const candidates = []
35
+ const push = (p, source) => {
36
+ const key = resolve(p)
37
+ if (seen.has(key)) return
38
+ seen.add(key)
39
+ candidates.push({ path: key, source })
40
+ }
41
+
42
+ if (opts.specBase) push(join(opts.specBase, 'local.yaml'), 'spec 根(--spec-dir/平台 pointer/最近 .sillyspec)')
43
+ for (let d = start; ; d = dirname(d)) {
44
+ push(join(d, '.sillyspec', 'local.yaml'), 'cwd 祖先链(worktree 内向上命中主仓)')
45
+ const parent = dirname(d)
46
+ if (parent === d) break
47
+ }
48
+ // git 兜底:linked worktree 内 --git-common-dir 指向主仓共享 .git,dirname 即主仓根
49
+ const commonDir = gitQuiet(start, ['rev-parse', '--git-common-dir'])
50
+ if (commonDir) {
51
+ const mainRoot = dirname(resolve(start, String(commonDir).trim()))
52
+ push(join(mainRoot, '.sillyspec', 'local.yaml'), 'git common-dir 反推主仓根')
53
+ }
54
+
55
+ const hit = candidates.find(c => existsSync(c.path))
56
+ return {
57
+ exists: !!hit,
58
+ path: hit ? hit.path : null,
59
+ source: hit ? hit.source : null,
60
+ searched: candidates.slice(0, 6).map(c => c.path),
61
+ }
62
+ }
63
+
64
+ /**
65
+ * 写侧目标解析(`sillyspec local register-repo` 用):已存在 local.yaml → 就地更新该文件
66
+ * (含 worktree → 主仓解析);全不存在 → 取 specBase(或 cwd 链)候选,但候选落在 worktree
67
+ * 副本下(<主仓>/.sillyspec/.runtime/worktrees/ 内)时重定向 git common-dir 主仓根——
68
+ * local.yaml 是 gitignored 不随 worktree checkout,写进副本目录是死配置。
69
+ *
70
+ * @param {string} dir 起点目录
71
+ * @param {{ specBase?: string|null }} [opts]
72
+ * @returns {{ path: string, existed: boolean }}
73
+ */
74
+ export function resolveLocalYamlWriteTarget(dir, opts = {}) {
75
+ const r = resolveLocalYaml(dir, opts)
76
+ if (r.exists) return { path: r.path, existed: true }
77
+ const start = resolve(dir || process.cwd())
78
+ const preferred = opts.specBase ? join(opts.specBase, 'local.yaml') : join(start, '.sillyspec', 'local.yaml')
79
+ const posix = resolve(preferred).replace(/\\/g, '/')
80
+ if (posix.includes('/.sillyspec/.runtime/worktrees/')) {
81
+ const commonDir = gitQuiet(start, ['rev-parse', '--git-common-dir'])
82
+ if (commonDir) {
83
+ const mainRoot = dirname(resolve(start, String(commonDir).trim()))
84
+ return { path: join(mainRoot, '.sillyspec', 'local.yaml'), existed: false }
85
+ }
86
+ }
87
+ return { path: resolve(preferred), existed: false }
88
+ }