add-coder 0.3.26 → 0.3.28

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 (323) hide show
  1. package/README.md +282 -155
  2. package/dist/index.js +481 -209
  3. package/package.json +15 -4
  4. package/templates/.add-coder-src-hash.json +186 -185
  5. package/templates/adapters/claude/hooks/doc-format-guard.ts +32 -0
  6. package/templates/adapters/claude/hooks/lib/claude-env.ts +7 -0
  7. package/templates/adapters/claude/hooks/notification.ts +24 -0
  8. package/templates/adapters/claude/hooks/permission-denied.ts +33 -0
  9. package/templates/adapters/claude/hooks/permission-gate.ts +12 -0
  10. package/templates/adapters/claude/hooks/post-tool-failure.ts +4 -0
  11. package/templates/adapters/claude/hooks/post-tool-use.ts +17 -0
  12. package/templates/adapters/claude/hooks/pre-compact.ts +10 -0
  13. package/templates/adapters/claude/hooks/pre-tool-use.ts +81 -0
  14. package/templates/adapters/claude/hooks/prompt-submit.ts +82 -0
  15. package/templates/adapters/claude/hooks/review-checklist.ts +4 -0
  16. package/templates/adapters/claude/hooks/session-end.ts +10 -0
  17. package/templates/adapters/claude/hooks/session-start.ts +24 -0
  18. package/templates/adapters/claude/hooks/stop-check.ts +15 -0
  19. package/templates/adapters/claude/hooks/stop-failure.ts +34 -0
  20. package/templates/adapters/claude/hooks/subagent-guard.ts +25 -0
  21. package/templates/adapters/claude/hooks/subagent-stop.ts +12 -0
  22. package/templates/adapters/claude/settings.json +13 -13
  23. package/templates/adapters/codex/config.toml.example +12 -0
  24. package/templates/adapters/codex/hooks/doc-format-guard.ts +30 -0
  25. package/templates/adapters/codex/hooks/notification.ts +26 -0
  26. package/templates/adapters/codex/hooks/permission-gate.ts +15 -0
  27. package/templates/adapters/codex/hooks/post-tool-failure.ts +14 -0
  28. package/templates/adapters/codex/hooks/post-tool-use.ts +85 -0
  29. package/templates/adapters/codex/hooks/pre-compact.ts +20 -0
  30. package/templates/adapters/codex/hooks/pre-tool-use.ts +141 -0
  31. package/templates/adapters/codex/hooks/prompt-submit.ts +38 -0
  32. package/templates/adapters/codex/hooks/review-checklist.ts +5 -0
  33. package/templates/adapters/codex/hooks/session-end.ts +19 -0
  34. package/templates/adapters/codex/hooks/session-start.ts +45 -0
  35. package/templates/adapters/codex/hooks/stop-check.ts +59 -0
  36. package/templates/adapters/codex/hooks/subagent-guard.ts +22 -0
  37. package/templates/adapters/codex/hooks/subagent-stop.ts +20 -0
  38. package/templates/adapters/codex/hooks.json +67 -1
  39. package/templates/adapters/qoder/hooks/doc-format-guard.ts +34 -0
  40. package/templates/adapters/qoder/hooks/lib/context-inject.ts +61 -0
  41. package/templates/adapters/qoder/hooks/lib/qoder-env.ts +23 -0
  42. package/templates/adapters/qoder/hooks/lib/review-checklist.ts +5 -0
  43. package/templates/adapters/qoder/hooks/notification.ts +34 -0
  44. package/templates/adapters/qoder/hooks/permission-gate.ts +26 -0
  45. package/templates/adapters/qoder/hooks/post-tool-failure.ts +30 -0
  46. package/templates/adapters/qoder/hooks/post-tool-use.ts +32 -0
  47. package/templates/adapters/qoder/hooks/pre-compact.ts +16 -0
  48. package/templates/adapters/qoder/hooks/pre-tool-use.ts +112 -0
  49. package/templates/adapters/qoder/hooks/prompt-submit.ts +66 -0
  50. package/templates/adapters/qoder/hooks/review-checklist.ts +24 -0
  51. package/templates/adapters/qoder/hooks/session-end.ts +27 -0
  52. package/templates/adapters/qoder/hooks/session-start.ts +83 -0
  53. package/templates/adapters/qoder/hooks/stop-check.ts +69 -0
  54. package/templates/adapters/qoder/hooks/subagent-guard.ts +16 -0
  55. package/templates/adapters/qoder/hooks/subagent-stop.ts +33 -0
  56. package/templates/adapters/qoder/settings.json +13 -13
  57. package/templates/adapters/trae/hooks/doc-format-guard.ts +30 -0
  58. package/templates/adapters/trae/hooks/notification.ts +22 -0
  59. package/templates/adapters/trae/hooks/permission-gate.ts +15 -0
  60. package/templates/adapters/trae/hooks/post-tool-failure.ts +14 -0
  61. package/templates/adapters/trae/hooks/post-tool-use.ts +24 -0
  62. package/templates/adapters/trae/hooks/pre-compact.ts +15 -0
  63. package/templates/adapters/trae/hooks/pre-tool-use.ts +40 -0
  64. package/templates/adapters/trae/hooks/prompt-submit.ts +29 -0
  65. package/templates/adapters/trae/hooks/review-checklist.ts +5 -0
  66. package/templates/adapters/trae/hooks/session-end.ts +16 -0
  67. package/templates/adapters/trae/hooks/session-start.ts +15 -0
  68. package/templates/adapters/trae/hooks/stop-check.ts +20 -0
  69. package/templates/adapters/trae/hooks/subagent-guard.ts +22 -0
  70. package/templates/adapters/trae/hooks/subagent-stop.ts +15 -0
  71. package/templates/adapters/trae/hooks.json +1 -1
  72. package/templates/adapters/vscode/hooks/doc-format-guard.ts +27 -0
  73. package/templates/adapters/vscode/hooks/notification.ts +22 -0
  74. package/templates/adapters/vscode/hooks/permission-gate.ts +15 -0
  75. package/templates/adapters/vscode/hooks/post-tool-failure.ts +32 -0
  76. package/templates/adapters/vscode/hooks/post-tool-use.ts +25 -0
  77. package/templates/adapters/vscode/hooks/pre-compact.ts +15 -0
  78. package/templates/adapters/vscode/hooks/pre-tool-use.ts +78 -0
  79. package/templates/adapters/vscode/hooks/prompt-submit.ts +74 -0
  80. package/templates/adapters/vscode/hooks/review-checklist.ts +5 -0
  81. package/templates/adapters/vscode/hooks/session-end.ts +16 -0
  82. package/templates/adapters/vscode/hooks/session-start.ts +24 -0
  83. package/templates/adapters/vscode/hooks/stop-check.ts +21 -0
  84. package/templates/adapters/vscode/hooks/subagent-guard.ts +21 -0
  85. package/templates/adapters/vscode/hooks/subagent-stop.ts +15 -0
  86. package/templates/core/docs/ADD-governance-claude-code.md +4 -4
  87. package/templates/core/docs/ADD-governance-codex.md +6 -6
  88. package/templates/core/docs/ADD-governance-qoder-cn.md +5 -5
  89. package/templates/core/docs/ADD-governance-trae.md +3 -3
  90. package/templates/core/docs/ADD-governance-vscode-copilot.md +22 -23
  91. package/templates/core/governance/audit-bridge.ts +47 -0
  92. package/templates/core/governance/common.ts +299 -0
  93. package/templates/core/governance/context-inject.ts +53 -0
  94. package/templates/core/governance/doc-format-guard.ts +440 -0
  95. package/templates/core/governance/notification-router.ts +62 -0
  96. package/templates/core/governance/notify.ts +66 -0
  97. package/templates/core/governance/permission-gate-router.ts +42 -0
  98. package/templates/core/governance/post-tool-failure-router.ts +56 -0
  99. package/templates/core/governance/post-tool-router.ts +212 -0
  100. package/templates/core/governance/pre-compact-guard.ts +41 -0
  101. package/templates/core/governance/pre-tool-guard.ts +309 -0
  102. package/templates/core/governance/preload-templates.ts +241 -0
  103. package/templates/core/governance/prompt-router.ts +222 -0
  104. package/templates/core/governance/review-checklist-guard.ts +186 -0
  105. package/templates/core/governance/rules.ts +720 -0
  106. package/templates/core/governance/session-end.ts +88 -0
  107. package/templates/core/governance/session-start-guard.ts +99 -0
  108. package/templates/core/governance/state-detect.ts +6 -0
  109. package/templates/core/governance/stop-router.ts +140 -0
  110. package/templates/core/governance/subagent-guard-router.ts +35 -0
  111. package/templates/core/governance/subagent-stop-router.ts +93 -0
  112. package/templates/core/governance/subagent-stop.ts +75 -0
  113. package/templates/core/governance/vocabulary.ts +109 -0
  114. package/templates/core/hooks/doc-format-guard.ts +21 -0
  115. package/templates/core/hooks/notification.ts +11 -0
  116. package/templates/core/hooks/permission-gate.ts +7 -0
  117. package/templates/core/hooks/post-tool-failure.ts +7 -0
  118. package/templates/core/hooks/post-tool-use.ts +23 -0
  119. package/templates/core/hooks/pre-compact.ts +9 -0
  120. package/templates/core/hooks/pre-tool-use.ts +30 -0
  121. package/templates/core/hooks/prompt-submit.ts +11 -0
  122. package/templates/core/hooks/review-checklist.ts +4 -0
  123. package/templates/core/hooks/session-end.ts +10 -0
  124. package/templates/core/hooks/session-start.ts +9 -0
  125. package/templates/core/hooks/stop-check.ts +14 -0
  126. package/templates/core/hooks/subagent-guard.ts +8 -0
  127. package/templates/core/hooks/subagent-stop.ts +9 -0
  128. package/templates/core/prisma/add.prisma +140 -82
  129. package/templates/core/rules/project_rules.md +27 -24
  130. package/templates/core/scripts/db-ensure.sh +8 -0
  131. package/templates/core/scripts/gen-plan-index.sh +4 -15
  132. package/templates/core/scripts/mcp-server/notifications/hitl.ts +9 -1
  133. package/templates/core/scripts/mcp-server/notifications/hook.ts +25 -9
  134. package/templates/core/scripts/mcp-server/resources/hitl-approval-widget.ts +34 -0
  135. package/templates/core/scripts/mcp-server/resources/hook-events-report.ts +14 -2
  136. package/templates/core/scripts/mcp-server/resources/index.ts +2 -0
  137. package/templates/core/scripts/mcp-server/sampling/review.ts +25 -10
  138. package/templates/core/scripts/mcp-server/shared/db-types.ts +33 -8
  139. package/templates/core/scripts/mcp-server/shared/dps-scoring.strategy.ts +1 -1
  140. package/templates/core/scripts/mcp-server/shared/env.ts +28 -8
  141. package/templates/core/scripts/mcp-server/shared/fs.ts +3 -0
  142. package/templates/core/scripts/mcp-server/shared/hitl-interaction.strategy.ts +2 -3
  143. package/templates/core/scripts/mcp-server/shared/hitl-lifecycle-mutation.ts +129 -0
  144. package/templates/core/scripts/mcp-server/shared/hitl-proposal-mutation.ts +98 -0
  145. package/templates/core/scripts/mcp-server/shared/hitl-ui.ts +3 -0
  146. package/templates/core/scripts/mcp-server/shared/plan-lifecycle-events.ts +62 -0
  147. package/templates/core/scripts/mcp-server/shared/plan-lifecycle-mutation.ts +78 -0
  148. package/templates/core/scripts/mcp-server/shared/plan-lifecycle-subscriber.ts +145 -0
  149. package/templates/core/scripts/mcp-server/shared/plan-lifecycle.ts +153 -0
  150. package/templates/core/scripts/mcp-server/shared/plan-round-events.ts +56 -0
  151. package/templates/core/scripts/mcp-server/shared/plan-round-mutation.ts +143 -0
  152. package/templates/core/scripts/mcp-server/shared/plan-round-store.ts +47 -0
  153. package/templates/core/scripts/mcp-server/shared/plan-round-subscriber.ts +99 -0
  154. package/templates/core/scripts/mcp-server/shared/plan-status-store.ts +37 -0
  155. package/templates/core/scripts/mcp-server/shared/plan-tracking-mutation.ts +71 -0
  156. package/templates/core/scripts/mcp-server/shared/prisma.ts +12 -6
  157. package/templates/core/scripts/mcp-server/shared/project-root-strategy.ts +1 -2
  158. package/templates/core/scripts/mcp-server/shared/quant-recheck.strategy.ts +16 -0
  159. package/templates/core/scripts/mcp-server/shared/redact.ts +28 -0
  160. package/templates/core/scripts/mcp-server/shared/response.ts +3 -2
  161. package/templates/core/scripts/mcp-server/shared/runtime-context.ts +77 -0
  162. package/templates/core/scripts/mcp-server/shared/schema-topology.ts +159 -0
  163. package/templates/core/scripts/mcp-server/tools/audit.ts +49 -7
  164. package/templates/core/scripts/mcp-server/tools/context.ts +117 -121
  165. package/templates/core/scripts/mcp-server/tools/contract.ts +34 -9
  166. package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_status.ts +50 -3
  167. package/templates/core/scripts/mcp-server/tools/gateway/check_doc_similarity.ts +218 -0
  168. package/templates/core/scripts/mcp-server/tools/gateway/check_dps.ts +22 -7
  169. package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +86 -4
  170. package/templates/core/scripts/mcp-server/tools/gateway/check_spec_sync.ts +19 -6
  171. package/templates/core/scripts/mcp-server/tools/gateway/index.ts +2 -0
  172. package/templates/core/scripts/mcp-server/tools/hitl.ts +160 -75
  173. package/templates/core/scripts/mcp-server/tools/hook-event-report.ts +9 -2
  174. package/templates/core/scripts/mcp-server/tools/index.ts +69 -3
  175. package/templates/core/scripts/mcp-server/tools/plan.ts +152 -17
  176. package/templates/core/scripts/mcp-server/tools/review.ts +44 -15
  177. package/templates/core/scripts/mcp-server.ts +60 -1
  178. package/templates/core/scripts/plan-status-bridge.ts +26 -0
  179. package/templates/core/skills/add-paradigm/SKILL.md +2 -2
  180. package/templates/core/templates/add-route-template.schema.json +24 -56
  181. package/templates/core/templates/collab-contract-template.md +1 -1
  182. package/templates/core/templates/handoff-multi-round-template.schema.json +48 -0
  183. package/templates/core/templates/handoff-single-round-template.schema.json +92 -0
  184. package/templates/core/templates/handoff-single-round.schema.json +1 -1
  185. package/templates/core/templates/hitl-approval-widget.html +222 -37
  186. package/templates/core/templates/spec-template.schema.json +9 -46
  187. package/templates/core/templates/standard-plan-template.schema.json +1 -1
  188. package/templates/core/templates/tasks-template.schema.json +13 -54
  189. package/templates/adapters/claude/hooks/doc-format-guard.sh +0 -202
  190. package/templates/adapters/claude/hooks/lib/common.sh +0 -261
  191. package/templates/adapters/claude/hooks/lib/context-inject.sh +0 -96
  192. package/templates/adapters/claude/hooks/lib/notify.sh +0 -34
  193. package/templates/adapters/claude/hooks/lib/preload-templates.sh +0 -187
  194. package/templates/adapters/claude/hooks/lib/session-end.sh +0 -76
  195. package/templates/adapters/claude/hooks/lib/state-detect.sh +0 -117
  196. package/templates/adapters/claude/hooks/lib/subagent-stop.sh +0 -90
  197. package/templates/adapters/claude/hooks/lib/vocabulary.sh +0 -56
  198. package/templates/adapters/claude/hooks/notification.sh +0 -45
  199. package/templates/adapters/claude/hooks/permission-denied.sh +0 -42
  200. package/templates/adapters/claude/hooks/permission-gate.sh +0 -18
  201. package/templates/adapters/claude/hooks/post-tool-failure.sh +0 -10
  202. package/templates/adapters/claude/hooks/post-tool-use.sh +0 -35
  203. package/templates/adapters/claude/hooks/pre-compact.sh +0 -37
  204. package/templates/adapters/claude/hooks/pre-tool-use.sh +0 -153
  205. package/templates/adapters/claude/hooks/prompt-submit.sh +0 -99
  206. package/templates/adapters/claude/hooks/review-checklist.sh +0 -10
  207. package/templates/adapters/claude/hooks/session-end.sh +0 -21
  208. package/templates/adapters/claude/hooks/session-start.sh +0 -36
  209. package/templates/adapters/claude/hooks/stop-check.sh +0 -45
  210. package/templates/adapters/claude/hooks/stop-failure.sh +0 -44
  211. package/templates/adapters/claude/hooks/subagent-guard.sh +0 -36
  212. package/templates/adapters/claude/hooks/subagent-stop.sh +0 -20
  213. package/templates/adapters/codex/hooks/doc-format-guard.sh +0 -261
  214. package/templates/adapters/codex/hooks/lib/common.sh +0 -261
  215. package/templates/adapters/codex/hooks/lib/context-inject.sh +0 -96
  216. package/templates/adapters/codex/hooks/lib/notify.sh +0 -34
  217. package/templates/adapters/codex/hooks/lib/preload-templates.sh +0 -187
  218. package/templates/adapters/codex/hooks/lib/session-end.sh +0 -76
  219. package/templates/adapters/codex/hooks/lib/state-detect.sh +0 -117
  220. package/templates/adapters/codex/hooks/lib/subagent-stop.sh +0 -90
  221. package/templates/adapters/codex/hooks/lib/vocabulary.sh +0 -56
  222. package/templates/adapters/codex/hooks/notification.sh +0 -45
  223. package/templates/adapters/codex/hooks/permission-gate.sh +0 -18
  224. package/templates/adapters/codex/hooks/post-tool-failure.sh +0 -10
  225. package/templates/adapters/codex/hooks/post-tool-use.sh +0 -138
  226. package/templates/adapters/codex/hooks/pre-compact.sh +0 -37
  227. package/templates/adapters/codex/hooks/pre-tool-use.sh +0 -217
  228. package/templates/adapters/codex/hooks/prompt-submit.sh +0 -87
  229. package/templates/adapters/codex/hooks/review-checklist.sh +0 -10
  230. package/templates/adapters/codex/hooks/session-end.sh +0 -38
  231. package/templates/adapters/codex/hooks/session-start.sh +0 -53
  232. package/templates/adapters/codex/hooks/stop-check.sh +0 -45
  233. package/templates/adapters/codex/hooks/subagent-guard.sh +0 -15
  234. package/templates/adapters/codex/hooks/subagent-stop.sh +0 -47
  235. package/templates/adapters/qoder/hooks/doc-format-guard.sh +0 -260
  236. package/templates/adapters/qoder/hooks/lib/common.sh +0 -261
  237. package/templates/adapters/qoder/hooks/lib/context-inject.sh +0 -96
  238. package/templates/adapters/qoder/hooks/lib/notify.sh +0 -34
  239. package/templates/adapters/qoder/hooks/lib/preload-templates.sh +0 -187
  240. package/templates/adapters/qoder/hooks/lib/session-end.sh +0 -76
  241. package/templates/adapters/qoder/hooks/lib/state-detect.sh +0 -117
  242. package/templates/adapters/qoder/hooks/lib/subagent-stop.sh +0 -90
  243. package/templates/adapters/qoder/hooks/lib/vocabulary.sh +0 -56
  244. package/templates/adapters/qoder/hooks/notification.sh +0 -25
  245. package/templates/adapters/qoder/hooks/permission-gate.sh +0 -8
  246. package/templates/adapters/qoder/hooks/post-tool-failure.sh +0 -8
  247. package/templates/adapters/qoder/hooks/post-tool-use.sh +0 -121
  248. package/templates/adapters/qoder/hooks/pre-compact.sh +0 -37
  249. package/templates/adapters/qoder/hooks/pre-tool-use.sh +0 -154
  250. package/templates/adapters/qoder/hooks/prompt-submit.sh +0 -96
  251. package/templates/adapters/qoder/hooks/review-checklist.sh +0 -157
  252. package/templates/adapters/qoder/hooks/session-end.sh +0 -24
  253. package/templates/adapters/qoder/hooks/session-start.sh +0 -45
  254. package/templates/adapters/qoder/hooks/stop-check.sh +0 -48
  255. package/templates/adapters/qoder/hooks/subagent-guard.sh +0 -27
  256. package/templates/adapters/qoder/hooks/subagent-stop.sh +0 -26
  257. package/templates/adapters/trae/hooks/doc-format-guard.sh +0 -261
  258. package/templates/adapters/trae/hooks/lib/common.sh +0 -261
  259. package/templates/adapters/trae/hooks/lib/context-inject.sh +0 -96
  260. package/templates/adapters/trae/hooks/lib/notify.sh +0 -34
  261. package/templates/adapters/trae/hooks/lib/preload-templates.sh +0 -187
  262. package/templates/adapters/trae/hooks/lib/session-end.sh +0 -76
  263. package/templates/adapters/trae/hooks/lib/state-detect.sh +0 -117
  264. package/templates/adapters/trae/hooks/lib/subagent-stop.sh +0 -90
  265. package/templates/adapters/trae/hooks/lib/vocabulary.sh +0 -56
  266. package/templates/adapters/trae/hooks/notification.sh +0 -45
  267. package/templates/adapters/trae/hooks/permission-gate.sh +0 -18
  268. package/templates/adapters/trae/hooks/post-tool-failure.sh +0 -10
  269. package/templates/adapters/trae/hooks/post-tool-use.sh +0 -138
  270. package/templates/adapters/trae/hooks/pre-compact.sh +0 -37
  271. package/templates/adapters/trae/hooks/pre-tool-use.sh +0 -217
  272. package/templates/adapters/trae/hooks/prompt-submit.sh +0 -87
  273. package/templates/adapters/trae/hooks/review-checklist.sh +0 -10
  274. package/templates/adapters/trae/hooks/session-end.sh +0 -38
  275. package/templates/adapters/trae/hooks/session-start.sh +0 -53
  276. package/templates/adapters/trae/hooks/stop-check.sh +0 -45
  277. package/templates/adapters/trae/hooks/subagent-guard.sh +0 -15
  278. package/templates/adapters/trae/hooks/subagent-stop.sh +0 -47
  279. package/templates/adapters/vscode/hooks/doc-format-guard.sh +0 -201
  280. package/templates/adapters/vscode/hooks/lib/common.sh +0 -261
  281. package/templates/adapters/vscode/hooks/lib/context-inject.sh +0 -96
  282. package/templates/adapters/vscode/hooks/lib/notify.sh +0 -34
  283. package/templates/adapters/vscode/hooks/lib/preload-templates.sh +0 -187
  284. package/templates/adapters/vscode/hooks/lib/session-end.sh +0 -76
  285. package/templates/adapters/vscode/hooks/lib/state-detect.sh +0 -117
  286. package/templates/adapters/vscode/hooks/lib/subagent-stop.sh +0 -90
  287. package/templates/adapters/vscode/hooks/lib/vocabulary.sh +0 -56
  288. package/templates/adapters/vscode/hooks/notification.sh +0 -45
  289. package/templates/adapters/vscode/hooks/permission-gate.sh +0 -18
  290. package/templates/adapters/vscode/hooks/post-tool-failure.sh +0 -28
  291. package/templates/adapters/vscode/hooks/post-tool-use.sh +0 -35
  292. package/templates/adapters/vscode/hooks/pre-compact.sh +0 -37
  293. package/templates/adapters/vscode/hooks/pre-tool-use.sh +0 -153
  294. package/templates/adapters/vscode/hooks/prompt-submit.sh +0 -99
  295. package/templates/adapters/vscode/hooks/review-checklist.sh +0 -10
  296. package/templates/adapters/vscode/hooks/session-end.sh +0 -13
  297. package/templates/adapters/vscode/hooks/session-start.sh +0 -35
  298. package/templates/adapters/vscode/hooks/stop-check.sh +0 -53
  299. package/templates/adapters/vscode/hooks/subagent-guard.sh +0 -37
  300. package/templates/adapters/vscode/hooks/subagent-stop.sh +0 -12
  301. package/templates/core/hooks/doc-format-guard.sh +0 -261
  302. package/templates/core/hooks/lib/common.sh +0 -261
  303. package/templates/core/hooks/lib/context-inject.sh +0 -96
  304. package/templates/core/hooks/lib/notify.sh +0 -34
  305. package/templates/core/hooks/lib/preload-templates.sh +0 -187
  306. package/templates/core/hooks/lib/session-end.sh +0 -76
  307. package/templates/core/hooks/lib/state-detect.sh +0 -117
  308. package/templates/core/hooks/lib/subagent-stop.sh +0 -90
  309. package/templates/core/hooks/lib/vocabulary.sh +0 -56
  310. package/templates/core/hooks/notification.sh +0 -45
  311. package/templates/core/hooks/permission-gate.sh +0 -18
  312. package/templates/core/hooks/post-tool-failure.sh +0 -10
  313. package/templates/core/hooks/post-tool-use.sh +0 -138
  314. package/templates/core/hooks/pre-compact.sh +0 -37
  315. package/templates/core/hooks/pre-tool-use.sh +0 -217
  316. package/templates/core/hooks/prompt-submit.sh +0 -87
  317. package/templates/core/hooks/review-checklist.sh +0 -10
  318. package/templates/core/hooks/session-end.sh +0 -38
  319. package/templates/core/hooks/session-start.sh +0 -53
  320. package/templates/core/hooks/stop-check.sh +0 -45
  321. package/templates/core/hooks/subagent-guard.sh +0 -15
  322. package/templates/core/hooks/subagent-stop.sh +0 -47
  323. package/templates/shared/hooks-lib/common.sh +0 -22
@@ -7,6 +7,7 @@ import { readdirRecursive, PROJECT_ROOT, MAGIC_DIR } from "../shared/fs.js"
7
7
  import { prisma } from "../shared/prisma.js"
8
8
  import type { CollabContractRow, PlanRow } from "../shared/db-types.js"
9
9
  import { CollabContractRowSchema, PlanRowSchema, validatedDelegate } from "../shared/db-types.js"
10
+ import { getRuntimeContext } from "../shared/env.js"
10
11
 
11
12
  const db = {
12
13
  get collabContract() { return validatedDelegate<CollabContractRow>(prisma.collabContract, CollabContractRowSchema, "CollabContract") },
@@ -67,6 +68,7 @@ function parseContractDoc(content: string): ParsedContract {
67
68
  }
68
69
 
69
70
  export function registerContractTools(server: ToolRegistrar) {
71
+ const runtimeContext = getRuntimeContext()
70
72
 
71
73
  // ===== contract_track =====
72
74
  server.registerTool("contract_track", {
@@ -124,13 +126,30 @@ export function registerContractTools(server: ToolRegistrar) {
124
126
  results.push(`⚠️ ${name} 缺少「总控 Plan:」声明,跳过`)
125
127
  continue
126
128
  }
127
- const existing = await db.collabContract.findFirst({ where: { contractName: name } })
129
+ const masterPlan = await db.plan.findFirst({
130
+ where: {
131
+ projectKey: runtimeContext.projectKey,
132
+ adapterKey: runtimeContext.adapterKey,
133
+ planName: masterPlanName,
134
+ },
135
+ })
136
+ if (!masterPlan) {
137
+ results.push(`⚠️ ${name} 当前 RuntimeContextKey 下不存在 Master Plan: ${masterPlanName}`)
138
+ continue
139
+ }
140
+ const existing = await db.collabContract.findFirst({
141
+ where: { projectKey: runtimeContext.projectKey, contractName: name },
142
+ })
128
143
  const version = (existing?.version as number ?? 0) + 1
129
144
 
130
145
  const data = {
131
146
  contractName: name,
147
+ projectKey: runtimeContext.projectKey,
148
+ ownerAdapterKey: runtimeContext.adapterKey,
132
149
  contractPath: path,
133
150
  masterPlanName,
151
+ masterProjectKey: runtimeContext.projectKey,
152
+ masterAdapterKey: runtimeContext.adapterKey,
134
153
  participants: parsed.participants as never,
135
154
  abilityMatrix: parsed.abilityMatrix as never,
136
155
  stages: parsed.stages as never,
@@ -142,7 +161,7 @@ export function registerContractTools(server: ToolRegistrar) {
142
161
  }
143
162
 
144
163
  if (existing) {
145
- await db.collabContract.update({ where: { contractName: name }, data })
164
+ await db.collabContract.update({ where: { id: existing.id }, data })
146
165
  results.push(`📄 ${name} ✅ 已更新 (v${version})`)
147
166
  } else {
148
167
  await db.collabContract.create({ data })
@@ -152,8 +171,12 @@ export function registerContractTools(server: ToolRegistrar) {
152
171
  // 若 masterPlan 存在,标记 PlanRecord 角色
153
172
  if (masterPlanName) {
154
173
  await db.plan.updateMany({
155
- where: { planName: masterPlanName },
156
- data: { contractRole: "MASTER", contractName: name } as never,
174
+ where: {
175
+ projectKey: runtimeContext.projectKey,
176
+ adapterKey: runtimeContext.adapterKey,
177
+ planName: masterPlanName,
178
+ },
179
+ data: { contractRole: "MASTER", contractName: name },
157
180
  })
158
181
  }
159
182
  }
@@ -173,7 +196,9 @@ export function registerContractTools(server: ToolRegistrar) {
173
196
  }, async (args: Record<string, unknown>) => {
174
197
  try {
175
198
  const { contractName } = args as { contractName: string }
176
- const contract = await db.collabContract.findFirst({ where: { contractName } })
199
+ const contract = await db.collabContract.findFirst({
200
+ where: { projectKey: runtimeContext.projectKey, contractName },
201
+ })
177
202
  if (!contract) {
178
203
  return textResponse(`📋 契约: 未跟踪\ncontractName: ${contractName}\n\n操作: contract_track({ contractName: "${contractName}" })`)
179
204
  }
@@ -182,11 +207,11 @@ export function registerContractTools(server: ToolRegistrar) {
182
207
  const boundaries = (contract.fileBoundaries as unknown[]) || []
183
208
  const parallelCount = stages.filter((s) => (s as Record<string, unknown>).parallelism === "parallel").length
184
209
  const serialCount = stages.filter((s) => (s as Record<string, unknown>).parallelism === "serial").length
185
- const versionStr = String(contract.version as number ?? "?")
186
- const statusStr = String(contract.status as string ?? "?")
187
- const masterStr = String(contract.masterPlanName as string ?? "(未绑定)")
210
+ const versionStr = String(contract.version ?? "?")
211
+ const statusStr = String(contract.status ?? "?")
212
+ const masterStr = String(contract.masterPlanName ?? "(未绑定)")
188
213
  const depStr = contract.dependencyGraph ? "已声明" : "未声明"
189
- const pathStr = String(contract.contractPath as string ?? "(无)")
214
+ const pathStr = String(contract.contractPath ?? "(无)")
190
215
  return textResponse(
191
216
  `📊 ${contractName}\n` +
192
217
  `版本: v${versionStr} (${statusStr})\n` +
@@ -17,6 +17,7 @@ import {
17
17
  MAGIC_DIR,
18
18
  } from "../../shared/fs.js";
19
19
  import { prisma } from "../../shared/prisma.js";
20
+ import { getRuntimeContext } from "../../shared/env.js";
20
21
 
21
22
  function scanCheckboxes(content: string) {
22
23
  let t = 0,
@@ -61,6 +62,7 @@ function scanCheckboxes(content: string) {
61
62
  }
62
63
 
63
64
  export function registerCheckAddRouteStatus(server: ToolRegistrar) {
65
+ const runtimeContext = getRuntimeContext();
64
66
  server.registerTool(
65
67
  "check_add_route_status",
66
68
  {
@@ -85,10 +87,13 @@ export function registerCheckAddRouteStatus(server: ToolRegistrar) {
85
87
  prisma.auditLog as Record<string, (...a: unknown[]) => unknown>
86
88
  ).findMany({
87
89
  where: {
88
- OR: [
89
- { targetId: { contains: "add-route", mode: "insensitive" } },
90
+ projectKey: runtimeContext.projectKey,
91
+ producerAdapterKey: runtimeContext.adapterKey,
92
+ // 按当前 Plan 精准定位:targetId 同时含 planKeyword 与 add-route(与文件匹配语义一致)
93
+ AND: [
94
+ { targetId: { startsWith: `${MAGIC_DIR}/` } },
90
95
  { targetId: { contains: planKeyword, mode: "insensitive" } },
91
- { reason: { contains: "add-route", mode: "insensitive" } },
96
+ { targetId: { contains: "add-route", mode: "insensitive" } },
92
97
  ],
93
98
  },
94
99
  orderBy: { createdAt: "desc" },
@@ -113,6 +118,48 @@ export function registerCheckAddRouteStatus(server: ToolRegistrar) {
113
118
  } catch {
114
119
  /* empty */
115
120
  }
121
+ // 补查 DevOperation 表(record_dev_operation 写入通道;R10 修复,2026-08-12 用户拍板不另立 Plan,本 Plan 内闭环)
122
+ try {
123
+ const devLogs = await (
124
+ prisma.devOperation as Record<string, (...a: unknown[]) => unknown>
125
+ ).findMany({
126
+ where: {
127
+ projectKey: runtimeContext.projectKey,
128
+ producerAdapterKey: runtimeContext.adapterKey,
129
+ // 按当前 Plan 精准定位(AND:planKeyword + add-route),命中集天然为当前 Plan 的 add-route 记录
130
+ AND: [
131
+ { targetId: { startsWith: `${MAGIC_DIR}/` } },
132
+ { targetId: { contains: planKeyword, mode: "insensitive" } },
133
+ { targetId: { contains: "add-route", mode: "insensitive" } },
134
+ ],
135
+ },
136
+ orderBy: { createdAt: "desc" },
137
+ take: 100,
138
+ }) as Array<{
139
+ targetId?: string;
140
+ reason?: string;
141
+ action: string;
142
+ createdAt: Date;
143
+ }>;
144
+ const pl = planKeyword.toLowerCase();
145
+ for (const l of devLogs) {
146
+ const ti = (l.targetId || "").toLowerCase();
147
+ const r = (l.reason || "").toLowerCase();
148
+ if (
149
+ (ti.includes("add-route") && ti.includes(pl)) ||
150
+ (r.includes("add-route") && r.includes(pl))
151
+ ) {
152
+ auditHasRecord = true;
153
+ auditRecords.push({
154
+ action: l.action,
155
+ targetId: l.targetId || "unknown",
156
+ createdAt: l.createdAt,
157
+ });
158
+ }
159
+ }
160
+ } catch {
161
+ /* empty */
162
+ }
116
163
  let fileExists = false;
117
164
  const matchedFiles: string[] = [];
118
165
  try {
@@ -0,0 +1,218 @@
1
+ /*
2
+ * @Description : 量化复检层 — 形似义异判定(锚点加权 Jaccard + TF-IDF cosine + RRF + EWMA ±kσ)
3
+ * @File : templates/core/scripts/mcp-server/tools/gateway/check_doc_similarity.ts
4
+ * @契约 : 消费 v3 Spec §6 jsonl(anchor_hit/struct_score/override)与 quant-events.jsonl(自记录)
5
+ * @回流 : Specs Review P0-1(参照物章节全文)/P0-2(候选集)/P1-5(量纲解耦)/P1-6(校准样本)/P1-7(decide 事件)
6
+ */
7
+ import * as z from "zod/v4";
8
+ import type { ToolRegistrar } from "../registrar.js";
9
+ import { join, basename } from "path";
10
+ import { appendFileSync, mkdirSync, existsSync, readFileSync } from "fs";
11
+ import { readFileSafe, readdirRecursive, PROJECT_ROOT, MAGIC_DIR } from "../../shared/fs.js";
12
+ import { textResponse, errorResponse } from "../../shared/response.js";
13
+ import { QUANT_RECHECK_CONFIG as CFG } from "../../shared/quant-recheck.strategy.js";
14
+ import { tokenize, jaccard, tfVector } from "./helpers.js";
15
+ import { cosineSimilarity } from "vector-cosine-similarity";
16
+
17
+ // ── 锚点加权 Jaccard(锚点 token ×ANCHOR_WEIGHT,普通 token ×1)──
18
+ export function weightedJaccard(a: Set<string>, b: Set<string>, anchors: Set<string>): number {
19
+ const w = (t: string) => (anchors.has(t) ? CFG.ANCHOR_WEIGHT : 1);
20
+ let num = 0, den = 0;
21
+ const all = new Set([...a, ...b]);
22
+ for (const t of all) {
23
+ const wa = a.has(t) ? w(t) : 0;
24
+ const wb = b.has(t) ? w(t) : 0;
25
+ num += Math.min(wa, wb);
26
+ den += Math.max(wa, wb);
27
+ }
28
+ return den > 0 ? num / den : 1;
29
+ }
30
+
31
+ // ── RRF 排名融合(Spec §2 语义:目标文档双路排名和,非候选均值)──
32
+ export function rrfScore(candidates: { j: number; t: number }[]): number {
33
+ // 双路排名:分数降序得 rank(并列同 rank)
34
+ const rankBy = (get: (c: { j: number; t: number }) => number) => {
35
+ const sorted = [...candidates].sort((x, y) => get(y) - get(x));
36
+ const ranks = new Map<{ j: number; t: number }, number>();
37
+ sorted.forEach((c, i) => { if (!ranks.has(c)) ranks.set(c, i + 1); });
38
+ return ranks;
39
+ };
40
+ const rj = rankBy((c) => c.j);
41
+ const rt = rankBy((c) => c.t);
42
+ // 目标文档 = 双路综合最优候选(j+t 最高)
43
+ const best = candidates.reduce((a, b) => (a.j + a.t >= b.j + b.t ? a : b));
44
+ // RRF(文档) = 1/(K+rank_j) + 1/(K+rank_t)
45
+ return 1 / (CFG.RRF_K + (rj.get(best) ?? 1)) + 1 / (CFG.RRF_K + (rt.get(best) ?? 1));
46
+ }
47
+
48
+ // ── EWMA ±kσ 阈值(历史复合分序列;冷启动用保守基线)──
49
+ export function ewmaThreshold(history: number[]): { mu: number; sigma: number; lower: number } {
50
+ if (history.length < CFG.EWMA_COLD_START) {
51
+ return { mu: CFG.EWMA_BASELINE, sigma: 0, lower: CFG.EWMA_BASELINE };
52
+ }
53
+ let mu = history[0], varEst = 0;
54
+ for (let i = 1; i < history.length; i++) {
55
+ const diff = history[i] - mu;
56
+ mu = CFG.EWMA_LAMBDA * history[i] + (1 - CFG.EWMA_LAMBDA) * mu;
57
+ varEst = CFG.EWMA_LAMBDA * diff * diff + (1 - CFG.EWMA_LAMBDA) * varEst;
58
+ }
59
+ const sigma = Math.sqrt(varEst);
60
+ return { mu, sigma, lower: mu - CFG.EWMA_K * sigma };
61
+ }
62
+
63
+ // ── 模板匹配(R3:目录扫描 + 特征序 + 存在性校验,与守卫注册表同序,不硬编码死模板)──
64
+ const TEMPLATE_PATTERNS: [RegExp, string][] = [
65
+ [/add-route.*heavy|heavy.*add-route/, "add-route-template-heavyweight.md"],
66
+ [/add-route/, "add-route-template.md"],
67
+ [/hitl/, "hitl-template.md"],
68
+ [/handoff/, "handoff-multi-round-template.md"],
69
+ [/checklist/, "checklist-template.md"],
70
+ [/fix-verif/, "fix-verification-template.md"],
71
+ [/report.*runtime|runtime.*report/, "runtime-report-template.md"],
72
+ [/report/, "report-template.md"],
73
+ [/tasks/, "tasks-template.md"],
74
+ [/spec/, "spec-template.md"],
75
+ [/prd.*increment|increment.*prd/, "prd-incremental-template.md"],
76
+ [/prd/, "prd-standard-template.md"],
77
+ [/simple.*plan|plan.*simple/, "simple-plan-template.md"],
78
+ [/plan/, "standard-plan-template.md"],
79
+ [/review.*runtime|runtime.*review/, "review-runtime-template.md"],
80
+ [/review.*implement|implement.*review/, "review-implementation-template.md"],
81
+ [/review/, "review-template.md"],
82
+ ];
83
+
84
+ async function matchTemplate(templatesDir: string, docPath: string): Promise<string> {
85
+ // 目录扫描可用模板清单(模板增删自动感知,不硬编码)
86
+ const all = (await readdirRecursive(templatesDir)) as string[];
87
+ const tmpls = new Set(all.filter((f) => f.endsWith("-template.md")));
88
+ // 只匹配文件名(basename),避免路径目录词污染(如 reports/ 误命中 report 模板——R4)
89
+ const base = basename(docPath || "");
90
+ const n = base.toLowerCase();
91
+ for (const [re, tmpl] of TEMPLATE_PATTERNS) {
92
+ if (re.test(n) && tmpls.has(tmpl)) return tmpl;
93
+ }
94
+ return "standard-plan-template.md"; // 缺省回退 Plan 模板
95
+ }
96
+
97
+ // ── 判定记录(P1-5:quant-events.jsonl 追加,与 hook-events 量纲解耦)──
98
+ function appendQuantEvent(rec: Record<string, unknown>): void {
99
+ try {
100
+ const dir = join(PROJECT_ROOT, MAGIC_DIR, "reports");
101
+ mkdirSync(dir, { recursive: true });
102
+ appendFileSync(join(dir, "quant-events.jsonl"), `${JSON.stringify(rec)}\n`, "utf-8");
103
+ } catch { /* 旁路:判定不因落盘失败中断 */ }
104
+ }
105
+
106
+ // ── 建议清单(P1-7/Task 2.2:status + event 追加式流转,人类 decide 事件为校准样本)──
107
+ function appendSuggestion(rec: Record<string, unknown>): void {
108
+ try {
109
+ const dir = join(PROJECT_ROOT, MAGIC_DIR, "reports");
110
+ mkdirSync(dir, { recursive: true });
111
+ appendFileSync(join(dir, "quant-suggestions.jsonl"), `${JSON.stringify(rec)}\n`, "utf-8");
112
+ } catch { /* 旁路 */ }
113
+ }
114
+
115
+ export function registerCheckDocSimilarity(server: ToolRegistrar) {
116
+ server.registerTool(
117
+ "check_doc_similarity",
118
+ {
119
+ description:
120
+ `量化复检层:「形似义异」语义判定。锚点加权 Jaccard + TF-IDF cosine 双路相似度,RRF 融合,EWMA ±kσ 自适应阈值。非关键路径调用(Step 3.5/收敛判断时)。`,
121
+ inputSchema: z.object({
122
+ planKeyword: z.string().describe("Plan 文件的关键词"),
123
+ docPath: z.string().optional().describe("目标文档相对路径(缺省取当前活跃 Plan)"),
124
+ }),
125
+ },
126
+ async (args: Record<string, unknown>, _ctx: unknown) => {
127
+ try {
128
+ const pp = args.planKeyword as string;
129
+ if (!pp) return errorResponse("planKeyword 参数不能为空");
130
+
131
+ // ── 候选集(P0-2):模板全部 sections 章节全文(P0-1 参照物粒度)──
132
+ const templatesDir = join(PROJECT_ROOT, MAGIC_DIR, "templates");
133
+ const plansDir = join(PROJECT_ROOT, MAGIC_DIR, "plans");
134
+ const reportsDir = join(PROJECT_ROOT, MAGIC_DIR, "reports");
135
+
136
+ // 定位 Plan(递归扫日期子目录;排除 .hitl.md 提案)
137
+ const planFiles = (await readdirRecursive(plansDir)) as string[];
138
+ const planFile = planFiles.find((f) => f.toLowerCase().includes(pp.toLowerCase()) && f.includes("-plan-v") && !f.endsWith(".hitl.md"));
139
+ if (!planFile) return errorResponse(`未找到匹配的 Plan(关键词: ${pp})`);
140
+ const planPath = join(plansDir, planFile);
141
+ const planContent = await readFileSafe(planPath) || "";
142
+ const anchorMatch = planContent.match(/anchor\s*[::]\s*"?([A-Za-z_]+)/);
143
+ const anchor = anchorMatch ? anchorMatch[1] : "plan_track";
144
+
145
+ // 读模板文件全文(R3:按文档类型匹配模板,refText = 模板章节全文——P0-1)
146
+ const tmplName = await matchTemplate(templatesDir, String(args.docPath ?? ""));
147
+ const tmplPath = join(templatesDir, tmplName);
148
+ const tmplContent = await readFileSafe(tmplPath) || "";
149
+ const refTokens = tokenize(tmplContent);
150
+ const anchorTokens = new Set([...tokenize(anchor)]);
151
+
152
+ // 候选集:模板 sections(按 ## 切分)+ 历史文档抽样
153
+ const sections = tmplContent.split(/^##\s+/m).filter((s) => s.trim().length > 0);
154
+ const candidates = sections.map((sec) => tokenize(sec)).slice(0, 20);
155
+ const docContent = args.docPath
156
+ ? (await readFileSafe(join(PROJECT_ROOT, String(args.docPath))) || "")
157
+ : planContent;
158
+ const docTokens = tokenize(docContent);
159
+
160
+ // 双路打分(候选集内)
161
+ const scored = candidates.map((ct) => {
162
+ const j = weightedJaccard(docTokens, ct, anchorTokens);
163
+ const globalTokens = candidates.length >= CFG.IDF_MIN_DOCS ? candidates : [ct];
164
+ const t = cosineSimilarity(tfVector(docTokens, globalTokens), tfVector(ct, globalTokens));
165
+ return { j, t };
166
+ });
167
+
168
+ // RRF 融合(候选不足退保守路径)
169
+ const rrf = scored.length >= CFG.RRF_MIN_CANDIDATES ? rrfScore(scored) : Math.min(...scored.map((s) => s.j));
170
+
171
+ // EWMA 阈值(P1-5:序列仅量化层复合分)
172
+ let history: number[] = [];
173
+ try {
174
+ const evFile = join(reportsDir, "quant-events.jsonl");
175
+ if (existsSync(evFile)) {
176
+ history = readFileSync(evFile, "utf-8").trim().split("\n")
177
+ .map((l) => { try { return (JSON.parse(l) as { rrf?: number }).rrf ?? NaN; } catch { return NaN; } })
178
+ .filter((v) => Number.isFinite(v)) as number[];
179
+ }
180
+ } catch { /* 冷启动 */ }
181
+ const th = ewmaThreshold(history);
182
+ // 复合判定:RRF 相对排名 ≥ 自适应阈值 且 双路综合绝对匹配达标(双闸门)
183
+ // 绝对闸门用 max(j, t):真实文档与模板全文 token 重叠天然低(骨架 vs 实例),Jaccard 单路会误杀
184
+ const bestMatch = Math.max(...scored.map((s) => Math.max(s.j, s.t)));
185
+ const absOk = bestMatch >= CFG.MIN_ABS_SIMILARITY;
186
+ const verdict = rrf >= th.lower && absOk ? "PASS" : "WARN_BLOCKED";
187
+
188
+ // 判定记录(P1-5/6/7:decide 事件由建议清单人类裁决写入)
189
+ appendQuantEvent({ ts: new Date().toISOString(), planKeyword: pp, jaccard: scored[0]?.j ?? 0, tfidf: scored[0]?.t ?? 0, rrf, ewmaThreshold: th.lower, verdict });
190
+
191
+ const suggestion = verdict === "WARN_BLOCKED"
192
+ ? { schemaHint: `${tmplName} anchor=${anchor}`, evidence: `j=${scored[0]?.j.toFixed(3)} t=${scored[0]?.t.toFixed(3)} rrf=${rrf.toFixed(4)}`, suggestion: "review schema 声明松紧(guard-rules.toml)" }
193
+ : null;
194
+
195
+ // 建议落盘(P1-7:status=open + event=create;人类 decide 后追加 event=decide + status=closed)
196
+ if (suggestion) {
197
+ appendSuggestion({
198
+ ts: new Date().toISOString(), planKeyword: pp, docPath: args.docPath ?? planFile,
199
+ ...suggestion, status: "open", event: "create",
200
+ });
201
+ }
202
+
203
+ return textResponse(
204
+ `=== check_doc_similarity(量化复检)===\n` +
205
+ `Plan: ${planFile}\n` +
206
+ `Jaccard: ${(scored[0]?.j ?? 0).toFixed(3)}(锚点加权 ×${CFG.ANCHOR_WEIGHT})\n` +
207
+ `TF-IDF: ${(scored[0]?.t ?? 0).toFixed(3)}(候选集 ${scored.length},IDF 语料锚 ${candidates.length >= CFG.IDF_MIN_DOCS ? "启用" : "退化" })\n` +
208
+ `RRF: ${rrf.toFixed(4)}(K=${CFG.RRF_K})\n` +
209
+ `EWMA 阈值: ${th.lower.toFixed(4)}(历史 ${history.length} 条${history.length < CFG.EWMA_COLD_START ? ",冷启动基线" : ",自适应"})\n` +
210
+ `判定: ${verdict}\n` +
211
+ (suggestion ? `建议: ${JSON.stringify(suggestion)}\n` : "")
212
+ );
213
+ } catch (e) {
214
+ return errorResponse(`check_doc_similarity 异常: ${e instanceof Error ? e.message : String(e)}`);
215
+ }
216
+ },
217
+ );
218
+ }
@@ -28,8 +28,10 @@ import {
28
28
  fftWeights,
29
29
  getEmbeddings,
30
30
  } from "./helpers.js";
31
+ import { getRuntimeContext } from "../../shared/env.js";
31
32
 
32
33
  export function registerCheckDps(server: ToolRegistrar) {
34
+ const runtimeContext = getRuntimeContext();
33
35
  server.registerTool(
34
36
  "check_dps",
35
37
  {
@@ -57,12 +59,18 @@ export function registerCheckDps(server: ToolRegistrar) {
57
59
  const apf = (await readdirRecursive(plansDir)).filter(
58
60
  (f) => f.endsWith(".md") && !f.includes(".hitl"),
59
61
  );
60
- const pm = apf.find(
62
+ // 多版本(-plan-vN)共存时取版本号最高者:活跃 Plan 优先,避免评分旧版(2026-08-12 修复)
63
+ const matched = apf.filter(
61
64
  (f) =>
62
65
  f.toLowerCase().includes(pp.toLowerCase()) && f.includes("-plan-v"),
63
66
  );
64
- if (!pm)
67
+ if (!matched.length)
65
68
  return errorResponse(`未找到匹配的 Plan 文件(关键词: ${pp})`);
69
+ const pm = matched.sort((a, b) => {
70
+ const va = parseInt(a.match(/-plan-v(\d+)/)?.[1] ?? "0", 10);
71
+ const vb = parseInt(b.match(/-plan-v(\d+)/)?.[1] ?? "0", 10);
72
+ return vb - va;
73
+ })[0];
66
74
  const planPath = join(plansDir, pm);
67
75
  const pc = await readFileSafe(planPath);
68
76
  if (!pc) return errorResponse(`无法读取 Plan 文件: ${pm}`);
@@ -106,8 +114,7 @@ export function registerCheckDps(server: ToolRegistrar) {
106
114
  // 子维度 A: 映射结构 (40%) — 映射表是否存在 + 覆盖行数
107
115
  // 子维度 B: 延续性 (60%) — Plan 决策文本 vs Spec §N 内容的 embedding 余弦相似度
108
116
  const planTerms = tokenize(pc),
109
- specTermsRaw = tokenize(sc),
110
- reviewTerms = tokenize(rc);
117
+ specTermsRaw = tokenize(sc);
111
118
  const planMapping = pc.match(/###\s+\d+\.\d+\s+Plan.*?Spec.*?实施映射/);
112
119
  let mappingTotal = 0,
113
120
  mappingMatched = 0;
@@ -456,12 +463,16 @@ export function registerCheckDps(server: ToolRegistrar) {
456
463
 
457
464
  // FFT 自适应权重
458
465
  const fourScores = [semScore, entropyScore, cpmScore, structFinal];
459
- let histMatrix: number[][] = [];
466
+ const histMatrix: number[][] = [];
460
467
  try {
461
468
  const history = (await (
462
469
  prisma.planRecord as Record<string, (...a: unknown[]) => unknown>
463
470
  ).findMany({
464
- where: { dpsComposite: { not: null } },
471
+ where: {
472
+ projectKey: runtimeContext.projectKey,
473
+ adapterKey: runtimeContext.adapterKey,
474
+ dpsComposite: { not: null },
475
+ },
465
476
  orderBy: { updatedAt: "desc" },
466
477
  take: CFG.FFT_HISTORY_LIMIT,
467
478
  })) as {
@@ -534,7 +545,11 @@ export function registerCheckDps(server: ToolRegistrar) {
534
545
  (...a: unknown[]) => unknown
535
546
  >;
536
547
  await planRec.updateMany({
537
- where: { planName: { contains: pp } },
548
+ where: {
549
+ projectKey: runtimeContext.projectKey,
550
+ adapterKey: runtimeContext.adapterKey,
551
+ planName: { contains: pp },
552
+ },
538
553
  data: {
539
554
  dpsSemScore: semScore,
540
555
  dpsEntropyScore: entropyScore,
@@ -9,7 +9,7 @@
9
9
  import * as z from "zod/v4";
10
10
  import type { ToolRegistrar } from "../registrar.js";
11
11
  import { existsSync } from "fs";
12
- import { join } from "path";
12
+ import { join, basename } from "path";
13
13
  import { textResponse, errorResponse } from "../../shared/response.js";
14
14
  import {
15
15
  readFileSafe,
@@ -19,8 +19,10 @@ import {
19
19
  } from "../../shared/fs.js";
20
20
  import { prisma } from "../../shared/prisma.js";
21
21
  import { runCommand } from "../../shared/run-command.js";
22
+ import { getRuntimeContext } from "../../shared/env.js";
22
23
 
23
24
  export function registerCheckRahs(server: ToolRegistrar) {
25
+ const runtimeContext = getRuntimeContext();
24
26
  server.registerTool(
25
27
  "check_rahs",
26
28
  {
@@ -42,7 +44,9 @@ export function registerCheckRahs(server: ToolRegistrar) {
42
44
  );
43
45
  const pm = apf.find(
44
46
  (f) =>
45
- f.toLowerCase().includes(pp.toLowerCase()) && f.includes("-plan-v"),
47
+ f.toLowerCase().includes(pp.toLowerCase()) &&
48
+ f.includes("-plan-v") &&
49
+ !f.includes(".hitl"),
46
50
  );
47
51
  if (!pm)
48
52
  return errorResponse(`未找到匹配的 Plan 文件(关键词: ${pp})`);
@@ -64,12 +68,14 @@ export function registerCheckRahs(server: ToolRegistrar) {
64
68
  0,
65
69
  100 - tsc.stderr.split("\n").filter(Boolean).length * 5,
66
70
  );
67
- } catch {}
71
+ } catch { /* type check is best-effort */ }
68
72
  try {
69
73
  const logs = (await (
70
74
  prisma.devOperation as Record<string, (...a: unknown[]) => unknown>
71
75
  ).findMany({
72
76
  where: {
77
+ projectKey: runtimeContext.projectKey,
78
+ producerAdapterKey: runtimeContext.adapterKey,
73
79
  OR: [
74
80
  { planKeyword: { contains: pp, mode: "insensitive" } },
75
81
  { targetId: { contains: pp, mode: "insensitive" } },
@@ -79,7 +85,83 @@ export function registerCheckRahs(server: ToolRegistrar) {
79
85
  take: 20,
80
86
  })) as Array<{ id: string }>;
81
87
  auditScore = Math.min(100, logs.length * 10);
82
- } catch {}
88
+ } catch { /* audit score remains conservative */ }
89
+ // ── 范围保真度 / Spec 合规 / 阶段对称性:从静态基线改为动态计算(修复:
90
+ // 三个维度原为固定 80,任何 Plan 的 RAHS 上限 88 永远无法通过)──
91
+ try {
92
+ const arFile = apf.find(
93
+ (f) =>
94
+ f.toLowerCase().includes(pp.toLowerCase()) &&
95
+ f.toLowerCase().includes("add-route"),
96
+ );
97
+ if (arFile) {
98
+ const arContent =
99
+ (await readFileSafe(join(plansDir, arFile))) || "";
100
+ const checks = arContent.match(/^- \[[ x]\]/gm) || [];
101
+ const done = checks.filter((c) => c.includes("[x]")).length;
102
+ // 阶段对称性:add-route Step 产出项勾选率(Handoff/Report Closure 等
103
+ // 条件项未勾选时按比例折算,不阻塞)
104
+ symScore = checks.length > 0
105
+ ? Math.max(70, Math.round((done / checks.length) * 100))
106
+ : 70;
107
+ }
108
+ } catch { /* add-route progress unavailable */ }
109
+ try {
110
+ // Spec 合规:checklist [T] 项勾选率([R] 运行时项不计入)
111
+ const specDir = join(PROJECT_ROOT, MAGIC_DIR, "specs");
112
+ // spec 目录名可能带 -vN 版本后缀:优先带版本匹配,回退不带版本 [Task 1.8 修复]
113
+ const rahsPlanBase = basename(pm, ".md").replace(/-plan-v\d+$/i, "");
114
+ const rahsVersionSuffix = /-plan-(v\d+)$/i.exec(basename(pm, ".md"))?.[1] ?? "";
115
+ const rahsCandidates = rahsVersionSuffix
116
+ ? [`${rahsPlanBase}-${rahsVersionSuffix}`, rahsPlanBase]
117
+ : [rahsPlanBase];
118
+ const planContent = (await readFileSafe(join(plansDir, pm))) || "";
119
+ const rahsSpecRef = planContent.match(/specs\/([^/`\s]+)/);
120
+ if (rahsSpecRef) rahsCandidates.push(rahsSpecRef[1]);
121
+ const sn =
122
+ rahsCandidates.find((d) => existsSync(join(specDir, d))) ??
123
+ rahsCandidates[0];
124
+ const clPath = join(specDir, sn, "checklist.md");
125
+ if (existsSync(clPath)) {
126
+ const cl = (await readFileSafe(clPath)) || "";
127
+ const tItems = cl.match(/^- \[[ x]\] \[T\]/gm) || [];
128
+ const tDone = tItems.filter((c) => c.includes("[x]")).length;
129
+ specScore = tItems.length > 0
130
+ ? Math.max(70, Math.round((tDone / tItems.length) * 100))
131
+ : 70;
132
+ }
133
+ } catch { /* checklist progress unavailable */ }
134
+ try {
135
+ // 范围保真度:git diff 变更文件与 add-route 附录清单匹配率
136
+ const arFile = apf.find(
137
+ (f) =>
138
+ f.toLowerCase().includes(pp.toLowerCase()) &&
139
+ f.toLowerCase().includes("add-route"),
140
+ );
141
+ if (arFile) {
142
+ const arContent =
143
+ (await readFileSafe(join(plansDir, arFile))) || "";
144
+ const appendix = (
145
+ arContent.match(/`[^`]+\.(ts|js|sh|md|tsx|json|yml|yaml)`/g) ||
146
+ []
147
+ ).map((f: string) => f.replace(/`/g, "").toLowerCase());
148
+ const diff = runCommand("git", ["diff", "--name-only"], {
149
+ cwd: PROJECT_ROOT,
150
+ timeout: 5000,
151
+ });
152
+ const diffFiles = diff.stdout
153
+ .trim()
154
+ .split("\n")
155
+ .filter(Boolean)
156
+ .map((f: string) => f.toLowerCase());
157
+ if (appendix.length > 0 && diffFiles.length > 0) {
158
+ const matched = diffFiles.filter((f) =>
159
+ appendix.some((a) => f === a || f.endsWith(`/${a}`)),
160
+ ).length;
161
+ scopeScore = Math.max(70, Math.round((matched / diffFiles.length) * 100));
162
+ }
163
+ }
164
+ } catch { /* git scope evidence unavailable */ }
83
165
  const parts = [
84
166
  "=== RAHS:Runtime Architecture Health Score ===",
85
167
  `Plan 关键词: "${pp}"`,
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import * as z from "zod/v4";
11
11
  import type { ToolRegistrar } from "../registrar.js";
12
+ import { MAGIC_PREFIXES } from "../../shared/fs.js";
12
13
  import { existsSync } from "fs";
13
14
  import { join, basename } from "path";
14
15
  import { textResponse, errorResponse } from "../../shared/response.js";
@@ -43,10 +44,17 @@ export function registerCheckSpecSync(server: ToolRegistrar) {
43
44
  f.endsWith(".md"),
44
45
  );
45
46
  const kw = args.planKeyword as string;
46
- let planMatch = planFiles.find(
47
- (f) =>
48
- f.toLowerCase().includes(kw.toLowerCase()) && f.includes("-plan-v"),
49
- );
47
+ // 多版本(-plan-vN)共存时取版本号最高者:活跃 Plan 优先,避免评分旧版(2026-08-12 修复,同 check_dps R9)
48
+ let planMatch: string | undefined = planFiles
49
+ .filter(
50
+ (f) =>
51
+ f.toLowerCase().includes(kw.toLowerCase()) && f.includes("-plan-v"),
52
+ )
53
+ .sort((a, b) => {
54
+ const va = parseInt(a.match(/-plan-v(\d+)/)?.[1] ?? "0", 10);
55
+ const vb = parseInt(b.match(/-plan-v(\d+)/)?.[1] ?? "0", 10);
56
+ return vb - va;
57
+ })[0];
50
58
  if (!planMatch)
51
59
  planMatch = planFiles.find((f) =>
52
60
  f.toLowerCase().includes(kw.toLowerCase()),
@@ -69,7 +77,7 @@ export function registerCheckSpecSync(server: ToolRegistrar) {
69
77
  lines.push(`add-route: ${arFile}`);
70
78
  const arContent = (await readFileSafe(join(plansDir, arFile))) || "";
71
79
  // 提取 add-route 附录文件清单
72
- const appendixFiles = (arContent.match(/`[^`]+\.(ts|js|sh|md|tsx|json)`/g) || [])
80
+ const appendixFiles = (arContent.match(/`[^`]+\.(ts|js|sh|md|tsx|json|yml|yaml)`/g) || [])
73
81
  .map((f: string) => f.replace(/`/g, ""));
74
82
  lines.push(`附录文件: ${appendixFiles.length} 个`);
75
83
 
@@ -89,8 +97,13 @@ export function registerCheckSpecSync(server: ToolRegistrar) {
89
97
  // 交叉比对
90
98
  if (appendixFiles.length > 0 && diffFiles.length > 0) {
91
99
  const appendixSet = new Set(appendixFiles.map((f: string) => f.toLowerCase()));
100
+ // 豁免 sync 自动生成产物(mirror 副本 + 备份)——非本 Plan 实施文件(2026-08-12 修复)
101
+ const isSyncGenerated = (lf: string) =>
102
+ MAGIC_PREFIXES.some((p) => lf.startsWith(`${p}/`));
92
103
  const unmatched = diffFiles.filter(
93
- (f: string) => !appendixSet.has(f.toLowerCase()),
104
+ (f: string) =>
105
+ !isSyncGenerated(f.toLowerCase()) &&
106
+ !appendixSet.has(f.toLowerCase()),
94
107
  );
95
108
  if (unmatched.length > 0) {
96
109
  lines.push(`⚠️ ${unmatched.length} 个文件在 git diff 中但不在 add-route 附录中:`);
@@ -6,6 +6,7 @@ import { registerCheckSpecSync } from "./check_spec_sync.js";
6
6
  import { registerCheckAddRouteCompleteness } from "./check_add_route_completeness.js";
7
7
  import { registerCheckDps } from "./check_dps.js";
8
8
  import { registerCheckRahs } from "./check_rahs.js";
9
+ import { registerCheckDocSimilarity } from "./check_doc_similarity.js";
9
10
 
10
11
  export function registerGatewayTools(server: ToolRegistrar) {
11
12
  registerCheckAddRouteStatus(server);
@@ -13,4 +14,5 @@ export function registerGatewayTools(server: ToolRegistrar) {
13
14
  registerCheckAddRouteCompleteness(server);
14
15
  registerCheckDps(server);
15
16
  registerCheckRahs(server);
17
+ registerCheckDocSimilarity(server);
16
18
  }