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,576 @@
1
+ /**
2
+ * worktree-deps.js — Worktree 依赖供给引擎
3
+ *
4
+ * 在 worktree.create() 的 baseline overlay 之后调用,让 worktree 立即可构建/测试。
5
+ * 策略:junction/symlink 快路径(lockfile 一致)+ install 兜底;多语言按 local.yaml
6
+ * project.type + lockfile 推断 install 命令。供给可失败,但状态写进 meta 供验证硬门读取。
7
+ *
8
+ * 见 change 2026-06-28-worktree-deps-provision / D-005@v1, D-007@v1。
9
+ */
10
+
11
+ import { existsSync, readFileSync, realpathSync, lstatSync } from 'fs';
12
+ import { join, isAbsolute, relative, resolve as resolvePath, sep as pathSep } from 'path';
13
+ import { execFileSync } from 'child_process';
14
+ import { createHash } from 'crypto';
15
+
16
+ const LOCKFILES = ['pnpm-lock.yaml', 'package-lock.json', 'yarn.lock'];
17
+ const DEFAULT_TIMEOUT_MS = 300 * 1000;
18
+
19
+ /**
20
+ * 取目录下首个命中的 lockfile 的 sha256 前 16 位;
21
+ * 无 lockfile 则 hash package.json;都没有返回 null。
22
+ */
23
+ export function lockfileHash(dir) {
24
+ if (!dir || !existsSync(dir)) return null;
25
+ for (const lf of LOCKFILES) {
26
+ const p = join(dir, lf);
27
+ if (existsSync(p)) {
28
+ return createHash('sha256').update(readFileSync(p)).digest('hex').slice(0, 16);
29
+ }
30
+ }
31
+ const pkg = join(dir, 'package.json');
32
+ if (existsSync(pkg)) {
33
+ return createHash('sha256').update(readFileSync(pkg)).digest('hex').slice(0, 16);
34
+ }
35
+ return null;
36
+ }
37
+
38
+ /** 命中目录下的 lockfile 文件名(用于判断 nodejs 子类)*/
39
+ function detectLockfile(dir) {
40
+ if (!dir || !existsSync(dir)) return null;
41
+ for (const lf of LOCKFILES) {
42
+ if (existsSync(join(dir, lf))) return lf;
43
+ }
44
+ return null;
45
+ }
46
+
47
+ /**
48
+ * 读取 local.yaml 文本(specBase 优先,回退 worktreePath/.sillyspec;不读 process.cwd 避免环境配置泄漏)。
49
+ * 返回 { text, source }——source 标记信任级(体检 SEC-01):
50
+ * - 'specBase':主仓配置,可信(其 commands.install 允许执行,仍过白名单+元字符门)
51
+ * - 'worktree':worktree 内副本,agent/子代理可写区——只作 project.type / modules 等
52
+ * 只读探测,其 commands.install 永不执行(否则绕过 worktree-guard 的危险命令拦截)
53
+ */
54
+ function readLocalYamlSourced(specBase, worktreePath) {
55
+ if (specBase) {
56
+ const p = join(specBase, 'local.yaml');
57
+ if (existsSync(p)) return { text: readFileSync(p, 'utf8'), source: 'specBase' };
58
+ }
59
+ if (worktreePath) {
60
+ const p = join(worktreePath, '.sillyspec', 'local.yaml');
61
+ if (existsSync(p)) return { text: readFileSync(p, 'utf8'), source: 'worktree' };
62
+ }
63
+ return { text: null, source: null };
64
+ }
65
+
66
+ /** 兼容旧签名:只要文本 */
67
+ function readLocalYaml(specBase, worktreePath) {
68
+ return readLocalYamlSourced(specBase, worktreePath).text;
69
+ }
70
+
71
+ /** 从 local.yaml 文本提取 commands.install(轻量正则,不引 yaml 依赖,与 scan-postcheck 一致)*/
72
+ function extractUserInstall(yamlText) {
73
+ if (!yamlText) return null;
74
+ // 匹配 commands: 段下的 install: "..." 或 install: '...'(unavailable 视为无)
75
+ const m = yamlText.match(/install:\s*["']([^"']+)["']/);
76
+ if (m && m[1] && m[1].toLowerCase() !== 'unavailable') return m[1];
77
+ return null;
78
+ }
79
+
80
+ /** 从 local.yaml 提取 project.type;缺失时按文件特征推断 */
81
+ export function detectProjectType(worktreePath, specBase) {
82
+ const yamlText = readLocalYaml(specBase, worktreePath);
83
+ if (yamlText) {
84
+ const m = yamlText.match(/type:\s*(\S+)/);
85
+ if (m && m[1]) return m[1];
86
+ }
87
+ if (existsSync(join(worktreePath, 'pom.xml'))) return 'maven';
88
+ if (existsSync(join(worktreePath, 'build.gradle')) || existsSync(join(worktreePath, 'build.gradle.kts'))) return 'gradle';
89
+ if (existsSync(join(worktreePath, 'package.json'))) return 'nodejs';
90
+ // Python:pyproject.toml(uv/poetry/pdm)或 requirements.txt(pip)——治 worktree 内 ruff/pre-commit 等
91
+ // 二进制不供给(exec 复盘 3-②:detectProjectType 原无 python 分支,python 项目根被误判 generic → n/a)
92
+ if (existsSync(join(worktreePath, 'pyproject.toml')) || existsSync(join(worktreePath, 'requirements.txt'))) return 'python';
93
+ return 'generic';
94
+ }
95
+
96
+ /** 按 project.type + lockfile 推断 install 命令(无 commands.install 时)*/
97
+ export function inferInstallCommand(projectType, worktreePath, userInstall) {
98
+ if (userInstall) return userInstall;
99
+ switch (projectType) {
100
+ case 'nodejs':
101
+ if (existsSync(join(worktreePath, 'pnpm-lock.yaml'))) return 'pnpm install --frozen-lockfile';
102
+ if (existsSync(join(worktreePath, 'package-lock.json'))) return 'npm ci';
103
+ if (existsSync(join(worktreePath, 'yarn.lock'))) return 'yarn install --frozen-lockfile';
104
+ return 'npm install'; // 无 lockfile 兜底(X-2)
105
+ case 'maven':
106
+ return 'mvn -o test';
107
+ case 'gradle':
108
+ // win32 cmd.exe 跑不了 ./gradlew(体检 BUG-08 同型):优先 gradlew.bat,否则全局 gradle
109
+ if (process.platform === 'win32') {
110
+ return existsSync(join(worktreePath, 'gradlew.bat')) ? 'gradlew.bat test' : 'gradle test';
111
+ }
112
+ return './gradlew test';
113
+ case 'python':
114
+ // uv 优先(现代 Python 工具链,pyproject.toml/uv.lock 项目走 uv sync 建 .venv + 装依赖,
115
+ // 与 execute worktree 环境预告一致);纯 requirements.txt(无 pyproject)回退 pip
116
+ if (existsSync(join(worktreePath, 'uv.lock')) || existsSync(join(worktreePath, 'pyproject.toml'))) return 'uv sync';
117
+ return 'pip install -r requirements.txt';
118
+ default:
119
+ return null; // generic → n/a
120
+ }
121
+ }
122
+
123
+ /** 在 worktreePath 创建 node_modules 链接到 mainNodeModules;失败回退 */
124
+ function tryLink(mainNodeModules, linkPath) {
125
+ // 已存在:确认是否已指向 main(幂等)
126
+ if (existsSync(linkPath)) {
127
+ try {
128
+ const resolved = realpathSync(linkPath);
129
+ const resolvedTarget = realpathSync(mainNodeModules);
130
+ if (resolved === resolvedTarget) return { ok: true, method: process.platform === 'win32' ? 'junction' : 'symlink' };
131
+ } catch {}
132
+ // 真实目录 ≠ 已有依赖(坑 provision-preexisting-dir-fake-linked,2026-08-23 实证:doctor --fix
133
+ // 报 re-provisioned 但链接没建——worktree node_modules 是 install 半途中断残留的真实目录时,
134
+ // realpath 不等 main → 被 preexisting 短路标 ok,根快路径据此写 depsStatus=linked,一个链接
135
+ // 都没建且后验证 existsSync 对空/残目录照样过)。真实目录 → ok:false + preexistingDir 标记,
136
+ // 交调用方决策:根快路径降级 tryInstall 真重建(宁可重装不说谎);linkOneDir 视为本地安装
137
+ // 保留(installed 语义,合法状态)。指向别处的 link 仍是「他者安装」,尊重不 clobber。
138
+ try {
139
+ if (!lstatSync(linkPath).isSymbolicLink()) {
140
+ return { ok: false, preexistingDir: true, error: `node_modules 已存在且为真实目录(非链接,疑似 install 残留): ${linkPath}` };
141
+ }
142
+ } catch {}
143
+ // 指向别处 → 不 clobber,视为已有依赖(installed 语义)
144
+ return { ok: true, method: 'install', preexisting: true };
145
+ }
146
+ // broken junction 清理(existsSync 对目标丢失的 junction/symlink 返回 false,但目录项占位会使
147
+ // mklink/ln 撞名失败 → 根:落 install 慢路径;子模块:linkOneDir failed。此坑在 _doctorReprovision
148
+ // 的解链段同样存在——它也用 existsSync 前置判断,broken junction 永远解不掉):lstat 是 link 而
149
+ // existsSync false → 删目录项后重建(rmdir 删 junction 不跟随 reparse,与 worktree.js 解链同款)。
150
+ try {
151
+ if (lstatSync(linkPath).isSymbolicLink()) {
152
+ if (process.platform === 'win32') {
153
+ execFileSync('cmd.exe', ['/c', 'rmdir', linkPath], { stdio: ['pipe', 'pipe', 'pipe'] });
154
+ } else {
155
+ unlinkSync(linkPath);
156
+ }
157
+ }
158
+ } catch { /* lstat 抛 = 目录项真不存在,直接建 */ }
159
+ try {
160
+ if (process.platform === 'win32') {
161
+ // execFileSync 数组形式不经 shell:POSIX 双引号内 `/$() 会执行、cmd.exe 双引号内 %VAR% 仍展开,
162
+ // linkPath 含 local.yaml 模块 path,属项目内可配置值(安全收敛,与 git-helper 同范式)
163
+ execFileSync('cmd.exe', ['/c', 'mklink', '/J', linkPath, mainNodeModules], { stdio: ['pipe', 'pipe', 'pipe'] });
164
+ } else {
165
+ execFileSync('ln', ['-s', mainNodeModules, linkPath], { stdio: ['pipe', 'pipe', 'pipe'] });
166
+ }
167
+ // 创建后实物复核(坑 provision-silent-fake-installed 第②层,2026-08-21 实证:doctor 报
168
+ // re-provisioned 成功但 junction 实际没建)——mklink/ln 退出码 0 不等于链接落盘(cmd.exe
169
+ // 垫片链存在静默失败面),lstat+existsSync 复核不存在即判失败,交由调用方走 install 兜底
170
+ //(junction(reparse point)与 symlink 的 lstat 均报 isSymbolicLink=true)
171
+ try {
172
+ const st = lstatSync(linkPath);
173
+ if (!st.isSymbolicLink() || !existsSync(linkPath)) {
174
+ return { ok: false, error: 'link 创建后复核失败:lstat 非 link 或路径不可达' };
175
+ }
176
+ } catch (e) {
177
+ return { ok: false, error: `link 创建后复核失败(lstat ${e.message})` };
178
+ }
179
+ return { ok: true, method: process.platform === 'win32' ? 'junction' : 'symlink' };
180
+ } catch (e) {
181
+ return { ok: false, error: e.message };
182
+ }
183
+ }
184
+
185
+ /**
186
+ * 执行 install 命令(带超时)。
187
+ *
188
+ * 安全收口(体检 SEC-01):commands.install 来自 local.yaml 配置,若经 execSync(shell)
189
+ * 原样执行,yaml 里的 `$(...)`/反引号/`;` 必然被解释——且 worktree 副本属 agent 可写区。
190
+ * 三道门:
191
+ * 1. 调用方保证来源可信(provisionDeps 只对 source==='specBase' 的 userInstall 走到这里)
192
+ * 2. 可执行前缀白名单(包管理器家族)
193
+ * 3. shell 元字符黑名单(win32 额外拦 %,cmd.exe 会展开 %VAR%),通过后拆 argv 数组执行不经 shell
194
+ *
195
+ * 链式命令支持(坑 worktree-install-whitelist-monorepo-chain,2026-08-20 实证):monorepo 的
196
+ * commands.install 常为链式写法(`cd web && pnpm install` / `npm install && npm run build:pkg`),
197
+ * 旧实现整条过白名单 + 元字符门,`&&` 必拒 → depsStatus=failed → execute deps 门控卡死且无自愈
198
+ * 路径(改 local.yaml 成单命令对需要 post-install build 的 monorepo 不可行)。现按 `&&` 拆段
199
+ * 逐段校验执行:每段独立过白名单 + 元字符门(拆分后残余的单 `&`/`|`/`;` 仍被元字符门拦),
200
+ * 段内允许 `cd <相对子路径>`(resolve 后必须仍在 worktree 根内,防越界建产物/读外部),
201
+ * 全程 argv 数组执行不经 shell、任一段失败即停(&& 语义)。安全面不变:每个执行段仍是
202
+ * 白名单包管理器命令,`||`/管道/`;`/后台均不支持。
203
+ */
204
+ const INSTALL_BINARY_WHITELIST = /^(?:\.\/)?(?:pnpm|npm|yarn|bun|mvn|gradle|gradlew|uv|pip|pip3|python|python3|poetry|make)(?:\.cmd|\.bat|\.exe)?(?:\s|$)/;
205
+ const SHELL_METACHARS = /[;&|<>$`\n]/;
206
+ const WIN_SHELL_METACHARS = /[;&|<>$`\n%]/;
207
+ const CD_SEGMENT_RE = /^cd\s+([^\s]+)$/;
208
+
209
+ function tryInstall(cmd, cwd, timeout) {
210
+ const trimmed = String(cmd || '').trim();
211
+ if (!trimmed) {
212
+ return { ok: false, error: 'install 命令为空' };
213
+ }
214
+ const metaRe = process.platform === 'win32' ? WIN_SHELL_METACHARS : SHELL_METACHARS;
215
+ // && 拆段(单段命令与原路径完全一致,零回归);空段(`a &&`/`&& b`)视为畸形命令拒绝
216
+ const segments = trimmed.split('&&').map(s => s.trim());
217
+ if (segments.some(s => s === '')) {
218
+ return { ok: false, error: `install 链式命令含空段,拒绝执行: ${trimmed}` };
219
+ }
220
+ let curCwd = cwd;
221
+ const root = resolvePath(cwd);
222
+ const planned = [];
223
+ for (const seg of segments) {
224
+ // 白名单先于元字符(保持原判定顺序):`curl … | sh` 报非白名单而非元字符,
225
+ // `npm install; rm …` 过白名单后被元字符门拦——两类拒绝语义与原实现一致
226
+ const cd = seg.match(CD_SEGMENT_RE);
227
+ if (!cd && !INSTALL_BINARY_WHITELIST.test(seg)) {
228
+ return { ok: false, error: `install 命令不在包管理器白名单内,拒绝执行: ${seg}(支持 && 链式与 cd <子目录> 段,各执行段仍须为白名单包管理器命令)` };
229
+ }
230
+ if (metaRe.test(seg)) {
231
+ return { ok: false, error: `install 命令段含 shell 元字符,拒绝执行: ${seg}(链式仅支持 &&;|| / 管道 / ; / 后台不支持)` };
232
+ }
233
+ if (cd) {
234
+ const target = resolvePath(curCwd, cd[1]);
235
+ if (target !== root && !target.startsWith(root + pathSep)) {
236
+ return { ok: false, error: `install 命令的 cd 段越出 worktree 根,拒绝执行: ${seg}(cwd ${cwd})` };
237
+ }
238
+ curCwd = target;
239
+ continue;
240
+ }
241
+ planned.push(seg);
242
+ }
243
+ for (const seg of planned) {
244
+ const argv = seg.split(/\s+/);
245
+ try {
246
+ if (process.platform === 'win32') {
247
+ // Windows 包管理器是 .cmd 垫片,无 shell 的 spawn 无法解析——经 cmd.exe /c 传参;
248
+ // 元字符(含 %)已拦,残余风险与 tryLink 的 mklink 同款
249
+ execFileSync('cmd.exe', ['/c', ...argv], { cwd: curCwd, timeout, stdio: ['pipe', 'pipe', 'pipe'] });
250
+ } else {
251
+ execFileSync(argv[0], argv.slice(1), { cwd: curCwd, timeout, stdio: ['pipe', 'pipe', 'pipe'] });
252
+ }
253
+ } catch (e) {
254
+ const msg = e.killed ? `timeout after ${timeout}ms` : ((e.stderr && e.stderr.toString()) || e.message);
255
+ return { ok: false, error: `${seg} failed (cwd ${curCwd}): ${msg}` };
256
+ }
257
+ }
258
+ return { ok: true };
259
+ }
260
+
261
+ /** 目录是否含 nodejs 标记(package.json 或任一 lockfile)——识别 monorepo 里的 nodejs 子模块 */
262
+ function hasNodeMarker(dir) {
263
+ if (!dir || !existsSync(dir)) return false;
264
+ if (existsSync(join(dir, 'package.json'))) return true;
265
+ return LOCKFILES.some(lf => existsSync(join(dir, lf)));
266
+ }
267
+
268
+ /**
269
+ * 从 local.yaml 提取 modules 块的 path 列表(generic monorepo 子模块路径)。
270
+ * 兼容 inline flow(`frontend: { path: "frontend/" }`)与 block(`frontend:\n path: frontend/`)。
271
+ * 轻量正则,不引 yaml 依赖(与 extractUserInstall / scan-postcheck 风格一致)。
272
+ * 坑 execute-worktree-pnpm-monorepo-no-node-modules:generic 项目 worktree 子模块无 node_modules。
273
+ * @returns {string[]} 去重 + 去 trailing 斜杠的相对路径
274
+ */
275
+ function extractModulePaths(yamlText) {
276
+ if (!yamlText) return [];
277
+ const idx = yamlText.search(/^modules:\s*$/m);
278
+ if (idx < 0) return [];
279
+ const nlIdx = yamlText.indexOf('\n', idx);
280
+ if (nlIdx < 0) return [];
281
+ let rest = yamlText.slice(nlIdx + 1);
282
+ const nextTop = rest.search(/^\S/m); // 下一个顶层 key(行首非空白)
283
+ const block = nextTop >= 0 ? rest.slice(0, nextTop) : rest;
284
+ const paths = [];
285
+ const re = /path:\s*["']?([^"'\s,}]+)/g;
286
+ let pm;
287
+ while ((pm = re.exec(block)) !== null) {
288
+ const p = pm[1].replace(/\/+$/, '').trim();
289
+ if (p && p !== '.') paths.push(p);
290
+ }
291
+ return [...new Set(paths)];
292
+ }
293
+
294
+ /**
295
+ * modules 块 path 安全校验(体检 SEC-07):只允许 worktree 内的相对子路径——
296
+ * 拒绝绝对路径与 `..` 段,防止 local.yaml 配置把 junction/symlink 建到 worktree 之外。
297
+ */
298
+ function isSafeModulePath(p) {
299
+ if (!p || isAbsolute(p)) return false;
300
+ const segs = p.split(/[\\/]+/);
301
+ return !segs.includes('..');
302
+ }
303
+
304
+ /**
305
+ * 对单个子模块目录 tryLink main 的 node_modules → wt 的 node_modules(modules 子模块专用)。
306
+ * 仅走 link 快路径(不 install——子模块 install 慢且易失败;lockfile 不一致时交给用户 pnpm install)。
307
+ * lockfile 一致才 link,避免误链不匹配的 deps。
308
+ */
309
+ function linkOneDir(wtDir, mainDir) {
310
+ const mainNodeModules = join(mainDir, 'node_modules');
311
+ if (!existsSync(mainNodeModules)) return { status: 'skipped', reason: 'main 无 node_modules' };
312
+ const wtHash = lockfileHash(wtDir);
313
+ const mainHash = lockfileHash(mainDir);
314
+ if (wtHash && mainHash && wtHash !== mainHash) {
315
+ return { status: 'mismatch', reason: `lockfile 不一致 main=${mainHash} wt=${wtHash}` };
316
+ }
317
+ const r = tryLink(mainNodeModules, join(wtDir, 'node_modules'));
318
+ if (r.ok) return { status: 'linked', method: r.method };
319
+ // 真实目录 = 子模块本地安装过(合法状态,如 mismatch 后用户手动 pnpm install):保留为
320
+ // installed 真话,不假 linked 也不误报 failed(tryLink 的 preexistingDir 细分见其注释)
321
+ if (r.preexistingDir) return { status: 'installed', reason: '子模块 node_modules 为真实目录(本地安装),保留' };
322
+ return { status: 'failed', error: r.error };
323
+ }
324
+
325
+ /**
326
+ * 供给依赖。返回 deps 状态对象(合并进 meta)。两段:
327
+ * 1. 根目录供给(原有逻辑:lockfile 一致 link / 不一致 install / generic→根 n/a)
328
+ * 2. modules 块的 nodejs 子模块 link(generic monorepo:根无 deps 但 frontend/daemon 等子模块需要)
329
+ * generic + 子模块 link 成功时,整体 depsStatus 从 n/a 升级为 linked(deps gate 不再误判 unknown 阻断)。
330
+ *
331
+ * @param {string} worktreePath - worktree 根目录
332
+ * @param {string} mainCwd - 主 checkout 根目录(node_modules 来源)
333
+ * @param {{ specBase?: string, timeout?: number, force?: boolean }} opts
334
+ * - force=true(doctor --fix 强制重装):绕过根目录 lockfile 一致快路径(下方 line 216 区块),
335
+ * 强制走 install 分支重装。该快路径是 tryLink 的唯一根调用点,绕过它即同时绕过 tryLink
336
+ * 的 preexisting 幂等短路(101-110),实现"强制重装"语义。配合调用方先解 junction
337
+ * (worktree.js _doctorReprovision),避免 install 经 junction 误改主仓 node_modules。
338
+ * modules 子模块 link 不受 force 影响(仅根 install)。
339
+ * @returns {{ depsStatus, depsMethod, depsSource, depsLockHash, depsCheckedAt, depsError?, depsModules? }}
340
+ */
341
+ export function provisionDeps(worktreePath, mainCwd, opts = {}) {
342
+ const { specBase = null, timeout = DEFAULT_TIMEOUT_MS, force = false } = opts;
343
+ const depsCheckedAt = new Date().toISOString();
344
+ const { text: yamlText, source: yamlSource } = readLocalYamlSourced(specBase, worktreePath);
345
+ const wtHash = lockfileHash(worktreePath);
346
+
347
+ // ── 1. 根目录供给 ──
348
+ const projectType = detectProjectType(worktreePath, specBase);
349
+ // SEC-01:commands.install 只信主仓 specBase 的 local.yaml——worktree 副本是 agent 可写区,
350
+ // 其 install 命令若执行等于绕过 worktree-guard 的危险命令拦截
351
+ const userInstall = yamlSource === 'specBase' ? extractUserInstall(yamlText) : null;
352
+ if (yamlSource === 'worktree' && extractUserInstall(yamlText)) {
353
+ console.warn('⚠️ worktree 内 local.yaml 的 commands.install 被忽略(agent 可写区,只信主仓 .sillyspec/local.yaml)');
354
+ }
355
+ const installCmd = inferInstallCommand(projectType, worktreePath, userInstall);
356
+
357
+ let result;
358
+ if (!installCmd) {
359
+ // generic / 无可执行 install → 根无 deps 动作(n/a);但下方 modules 子模块仍可能 link 升级
360
+ result = { depsStatus: 'n/a', depsMethod: null, depsSource: null, depsLockHash: wtHash };
361
+ } else {
362
+ // 快路径:main 有 node_modules 且 lockfile hash 一致 → junction/symlink
363
+ // force=true 时绕过(doctor --fix 强制重装;绕过此块即同时绕过 tryLink 101-110 幂等短路)
364
+ const mainNodeModules = mainCwd ? join(mainCwd, 'node_modules') : null;
365
+ const mainHash = lockfileHash(mainCwd);
366
+ let linked = false;
367
+ if (!force && mainNodeModules && existsSync(mainNodeModules) && mainHash && wtHash && mainHash === wtHash) {
368
+ const linkResult = tryLink(mainNodeModules, join(worktreePath, 'node_modules'));
369
+ if (linkResult.ok) {
370
+ result = {
371
+ depsStatus: 'linked',
372
+ depsMethod: linkResult.method,
373
+ depsSource: linkResult.preexisting ? 'install' : 'main-checkout',
374
+ depsLockHash: wtHash,
375
+ };
376
+ linked = true;
377
+ }
378
+ }
379
+ if (!linked) {
380
+ // 兜底:install
381
+ const installResult = tryInstall(installCmd, worktreePath, timeout);
382
+ result = installResult.ok
383
+ ? { depsStatus: 'installed', depsMethod: 'install', depsSource: 'install', depsLockHash: wtHash }
384
+ : { depsStatus: 'failed', depsMethod: null, depsSource: null, depsLockHash: wtHash, depsError: installResult.error };
385
+ }
386
+ // ── 结果后验证(坑 provision-silent-fake-installed,2026-08-21 Windows 实证;
387
+ // 坑 provision-monorepo-subpackage-fake-installed,2026-08-22 复发:pnpm monorepo
388
+ // workspace 根 package.json 的 dependencies 常为空(依赖在 frontend/daemon 子包),
389
+ // 只查根会把 installed 状态整体跳过——doctor 标 installed 但子包 node_modules 全缺,
390
+ // Wave 1 测试才挂)。分层校验:
391
+ // linked:根 junction 实存硬校验(link 声称成功必留痕);
392
+ // installed:校验目标集 = 根 + local.yaml modules 块声明的含 package.json 子模块,
393
+ // 其中【声明了依赖】的目录逐一要求 node_modules 存在(pnpm/npm/yarn workspace
394
+ // 安装都会给子包建 node_modules;无依赖声明的空壳目录合法地不建,不校验)。
395
+ // python/.venv、jvm 本地仓库本就不适用(整体仅 nodejs)。
396
+ if (projectType === 'nodejs') {
397
+ const pkgHasDeps = (dir) => {
398
+ try {
399
+ const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'))
400
+ return ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']
401
+ .some(k => pkg[k] && Object.keys(pkg[k]).length > 0)
402
+ } catch { return false }
403
+ }
404
+ // 目标集:根 + modules 块内含 package.json 的安全子模块(monorepo 子包)
405
+ const verifyDirs = [worktreePath]
406
+ for (const mp of extractModulePaths(yamlText)) {
407
+ if (!isSafeModulePath(mp)) continue
408
+ const sub = join(worktreePath, mp)
409
+ if (existsSync(join(sub, 'package.json'))) verifyDirs.push(sub)
410
+ }
411
+ const depsDeclaredDirs = verifyDirs.filter(pkgHasDeps)
412
+ const needVerify = result.depsStatus === 'linked'
413
+ || (result.depsStatus === 'installed' && depsDeclaredDirs.length > 0)
414
+ if (needVerify) {
415
+ // linked 只验根(junction 语义);installed 验全部声明依赖的目标目录
416
+ const targets = result.depsStatus === 'linked' ? [worktreePath] : depsDeclaredDirs
417
+ const missing = targets.filter(dir => !existsSync(join(dir, 'node_modules')))
418
+ if (missing.length > 0) {
419
+ const missingRel = missing.map(dir => relative(worktreePath, dir) || '(根)')
420
+ result = {
421
+ depsStatus: 'failed', depsMethod: null, depsSource: null, depsLockHash: wtHash,
422
+ depsError: `${result.depsStatus} 后验证失败:${missingRel.join('、')} 的 node_modules 不存在(报成功但实际未落盘——Windows 静默失败面:cmd.exe 垫片解析/杀毒拦截/frozen-lockfile 快退)。` +
423
+ `手动兜底:进 worktree 手动跑 ${installCmd}(子包随 workspace 一起装),或对 lockfile 一致的目录用 junction:New-Item -ItemType Junction -Path "<目标>/node_modules" -Target "<主仓对应目录>/node_modules",然后重试 doctor --fix`,
424
+ }
425
+ }
426
+ }
427
+ }
428
+ }
429
+ result.depsCheckedAt = depsCheckedAt;
430
+
431
+ // ── 2. modules 块的 nodejs 子模块 link(坑 execute-worktree-pnpm-monorepo-no-node-modules)──
432
+ // generic monorepo(如 multi-agent-platform)worktree 子模块 frontend/sillyhub-daemon 无 node_modules,
433
+ // 跑 pnpm test 失败。读 local.yaml modules 块,对 nodejs 子模块 tryLink main 的 node_modules。
434
+ const modulePaths = extractModulePaths(yamlText);
435
+ const moduleResults = [];
436
+ for (const mp of modulePaths) {
437
+ if (!isSafeModulePath(mp)) {
438
+ moduleResults.push({ path: mp, status: 'skipped', reason: 'path 越界(绝对路径或 .. 段),拒绝 link' });
439
+ continue;
440
+ }
441
+ const wtDir = join(worktreePath, mp);
442
+ const mainDir = mainCwd ? join(mainCwd, mp) : null;
443
+ if (!existsSync(wtDir) || !mainDir || !hasNodeMarker(wtDir)) continue;
444
+ moduleResults.push({ path: mp, ...linkOneDir(wtDir, mainDir) });
445
+ }
446
+
447
+ if (moduleResults.length > 0) {
448
+ result.depsModules = moduleResults;
449
+ const linkedModules = moduleResults.filter(m => m.status === 'linked');
450
+ if (linkedModules.length > 0 && result.depsStatus === 'n/a') {
451
+ // 标 generic 但子模块 link 成功 → 升级为 linked(deps gate 不再误判 unknown 阻断 execute)
452
+ result.depsStatus = 'linked';
453
+ result.depsMethod = linkedModules[0].method;
454
+ result.depsSource = 'main-checkout';
455
+ }
456
+ // 子模块 link 后验证(坑 modules-submodule-link-verify,2026-08-23 实证:Windows 下漏链
457
+ // frontend node_modules——linkOneDir 报 linked 但 junction 实际未建或 main 侧本就没有)。
458
+ // linked 状态的子模块逐一核验 wt/<mp>/node_modules 实存:缺失降 failed 并给兜底指引
459
+ //(不静默放行——跑测试时才发现就晚了)。
460
+ for (const m of moduleResults) {
461
+ if (m.status !== 'linked') continue
462
+ const nmPath = join(worktreePath, m.path, 'node_modules')
463
+ if (!existsSync(nmPath)) {
464
+ m.status = 'failed'
465
+ m.error = `linked 后验证失败:${m.path}/node_modules 不存在(报成功但实际未落盘)。兜底:New-Item -ItemType Junction -Path "<wt>/${m.path}/node_modules" -Target "<主仓>/${m.path}/node_modules"`
466
+ }
467
+ }
468
+ const failedModules = moduleResults.filter(m => m.status === 'failed');
469
+ if (failedModules.length > 0) {
470
+ const fmsg = failedModules.map(m => `${m.path}: ${m.error}`).join('; ');
471
+ result.depsError = result.depsError ? `${result.depsError}; ${fmsg}` : fmsg;
472
+ }
473
+ // mismatch 可见性(坑 modules-mismatch-invisible,2026-08-23 实证:mismatch/skipped 只写
474
+ // depsModules 而 src 内零消费——doctor 报 re-provisioned 成功、子模块实际无链接且无任何
475
+ // 提示)。mismatch(worktree 与主仓 lockfile 不一致,link 不适用)摘要进 depsError:可见、
476
+ // 落 meta、doctor msg 带出(_doctorReprovision 不再抹);但不降级 depsStatus——mismatch 是
477
+ // 配置分叉态不是供给失败,降级会经 deps gate 卡死 execute(正确动作是用户在 worktree 跑
478
+ // pnpm install 或对齐 lockfile, doctor --fix 的 force 重装路径也会覆盖此场景)。
479
+ const mismatchModules = moduleResults.filter(m => m.status === 'mismatch');
480
+ if (mismatchModules.length > 0) {
481
+ const mmsg = `子模块 lockfile 与主仓不一致(未链接,本地 install 或对齐 lockfile): ${mismatchModules.map(m => m.path).join('、')}`;
482
+ result.depsError = result.depsError ? `${result.depsError}; ${mmsg}` : mmsg;
483
+ }
484
+ }
485
+
486
+ return result;
487
+ }
488
+
489
+ /**
490
+ * 统一的 worktree deps 新鲜度判定(H1,change 2026-08-05-tooling-feedback-fixes)。
491
+ * 供 doctor(worktree.js)与 ensureDepsFreshness(run/stage.js)共用,消除两处双写漂移。
492
+ *
493
+ * 判定优先级:failed → missing → stale → main-drift → fresh
494
+ * - failed: meta.depsStatus==='failed'(上次供给失败,占最高优先级)
495
+ * - missing: depsStatus ∈ {linked, installed} 且 node_modules 不存在
496
+ * - stale: wtHash 与 meta.depsLockHash 不一致(worktree 自身 lockfile 在上次供给后变化)
497
+ * - main-drift: wtHash 与主仓 mainHash 不一致(主仓 lockfile 漂移;复用 linkOneDir 177-178 的 mismatch 判据)
498
+ * - fresh: 其余情况
499
+ *
500
+ * lockfileHash 返回 null(无 lockfile/package.json)时相关比较优雅降级——不报 stale / main-drift,
501
+ * 避免无 lockfile 项目(如纯 generic / 非 nodejs)误判漂移。
502
+ *
503
+ * @param {object} [meta] - worktree meta(读 depsStatus / depsLockHash / depsError)
504
+ * @param {string} wtPath - worktree 根目录
505
+ * @param {string} mainCwd - 主 checkout 根目录
506
+ * @returns {{ status: 'fresh'|'missing'|'stale'|'main-drift'|'failed', detail: string, wtHash: (string|null), mainHash: (string|null), metaLockHash: (string|null) }}
507
+ */
508
+ export function checkDepsFreshness(meta, wtPath, mainCwd) {
509
+ const depsStatus = meta && meta.depsStatus ? meta.depsStatus : null;
510
+ const metaLockHash = meta && meta.depsLockHash ? meta.depsLockHash : null;
511
+ const wtHash = lockfileHash(wtPath);
512
+ const mainHash = lockfileHash(mainCwd);
513
+
514
+ // 1. failed 最高优先级(对齐 task-01 蓝图:failed → missing → stale → main-drift → fresh)
515
+ if (depsStatus === 'failed') {
516
+ return {
517
+ status: 'failed',
518
+ detail: '上次依赖供给失败' + (meta && meta.depsError ? ': ' + meta.depsError : ''),
519
+ wtHash, mainHash, metaLockHash,
520
+ };
521
+ }
522
+
523
+ // 2. missing:曾 link/install 但 node_modules 已丢失(对齐 doctor 914-915 / ensure 403)
524
+ const nmExists = wtPath ? existsSync(join(wtPath, 'node_modules')) : false;
525
+ if (['linked', 'installed'].includes(depsStatus) && !nmExists) {
526
+ return {
527
+ status: 'missing',
528
+ detail: `meta.depsStatus=${depsStatus} 但 node_modules 缺失`,
529
+ wtHash, mainHash, metaLockHash,
530
+ };
531
+ }
532
+
533
+ // 2b. 子模块 missing(坑 modules-submodule-missing-no-selfheal):meta.depsModules 中 linked
534
+ // 状态的子模块逐一核验 wt/<mp>/node_modules 实存。provisionDeps 内的同款核验(坑
535
+ // modules-submodule-link-verify)只管供给时刻,此后 junction 被删(杀毒/手工误删/半途清理)
536
+ // 时本函数只查根(上方)→ 永远 fresh → doctor 不再报 → 漏链永不自愈。此处补 doctor 侧
537
+ // 闭环:缺失 → missing → doctor --fix 走 relinkFirst 重链(provisionDeps 非 force 路径
538
+ // 的 linkOneDir 对缺失目录直接重建 junction)。
539
+ if (Array.isArray(meta && meta.depsModules)) {
540
+ const missingMods = meta.depsModules.filter(m =>
541
+ m && m.status === 'linked' && typeof m.path === 'string'
542
+ && !existsSync(join(wtPath, m.path, 'node_modules')));
543
+ if (missingMods.length > 0) {
544
+ return {
545
+ status: 'missing',
546
+ detail: `${missingMods.length} 个 linked 子模块 node_modules 缺失: ${missingMods.map(m => m.path).join('、')}`,
547
+ wtHash, mainHash, metaLockHash,
548
+ };
549
+ }
550
+ }
551
+
552
+ // 3. stale:worktree 自身 lockfile 与 meta 快照不一致(对齐 doctor 916-917 / ensure 404)
553
+ if (metaLockHash && wtHash && wtHash !== metaLockHash) {
554
+ return {
555
+ status: 'stale',
556
+ detail: `lockfile 变化 (${metaLockHash} -> ${wtHash})`,
557
+ wtHash, mainHash, metaLockHash,
558
+ };
559
+ }
560
+
561
+ // 4. main-drift(新增):wtHash 与主仓 mainHash 不一致——主仓 lockfile 更新过、worktree 未跟
562
+ if (wtHash && mainHash && wtHash !== mainHash) {
563
+ return {
564
+ status: 'main-drift',
565
+ detail: `worktree lockfile 与主仓不一致 (wt=${wtHash} main=${mainHash})`,
566
+ wtHash, mainHash, metaLockHash,
567
+ };
568
+ }
569
+
570
+ // 5. fresh
571
+ return {
572
+ status: 'fresh',
573
+ detail: '依赖新鲜',
574
+ wtHash, mainHash, metaLockHash,
575
+ };
576
+ }