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,19 @@ import { readFileSafe, readdirRecursive, PROJECT_ROOT, MAGIC_DIR } from "../shar
7
7
  import { prisma } from "../shared/prisma.js"
8
8
  import type { PlanRow } from "../shared/db-types.js"
9
9
  import { PlanRowSchema, validatedDelegate } from "../shared/db-types.js"
10
+ import { getRuntimeContext } from "../shared/env.js"
11
+ import { assertPathInRuntimeScope } from "../shared/runtime-context.js"
12
+ import { resolvePlanStatus } from "../shared/plan-lifecycle.js"
13
+ import { createPrismaPlanStatusStore } from "../shared/plan-status-store.js"
14
+ import {
15
+ trackPlanAndPublish,
16
+ type PlanTrackingDatabase,
17
+ } from "../shared/plan-tracking-mutation.js"
18
+ import {
19
+ closePlanRoundAndPublish,
20
+ type PlanRoundDatabase,
21
+ } from "../shared/plan-round-mutation.js"
22
+ import { queryPlanRounds, type PlanRoundReadClient } from "../shared/plan-round-store.js"
10
23
 
11
24
  // 无类型边界单点(zod 托管):动态加载的 prisma client 在此一次性转为运行期校验的泛型委托
12
25
  const db = {
@@ -14,6 +27,14 @@ const db = {
14
27
  }
15
28
 
16
29
  export function registerPlanTools(server: ToolRegistrar) {
30
+ const runtimeContext = getRuntimeContext()
31
+ const statusStore = createPrismaPlanStatusStore(prisma)
32
+ const parseStructuredState = (value: string, field: string): Record<string, unknown> | unknown[] => {
33
+ let parsed: unknown
34
+ try { parsed = JSON.parse(value) } catch { throw new Error(`${field} 必须是有效 JSON`) }
35
+ if (parsed === null || typeof parsed !== "object") throw new Error(`${field} 必须是非 null JSON 对象或数组`)
36
+ return parsed as Record<string, unknown> | unknown[]
37
+ }
17
38
 
18
39
  // ===== plan_track =====
19
40
  server.registerTool("plan_track", {
@@ -24,7 +45,7 @@ export function registerPlanTools(server: ToolRegistrar) {
24
45
  }),
25
46
  }, async (args: Record<string, unknown>) => {
26
47
  try {
27
- const { planName, scanAll } = args as { planName?: string; scanAll?: boolean }
48
+ const { planName } = args as { planName?: string; scanAll?: boolean }
28
49
  const pn = typeof planName === "string" ? planName : ""
29
50
  const plansDir = join(PROJECT_ROOT, MAGIC_DIR, "plans")
30
51
  const specsDir = join(PROJECT_ROOT, MAGIC_DIR, "specs")
@@ -32,7 +53,7 @@ export function registerPlanTools(server: ToolRegistrar) {
32
53
 
33
54
  if (!existsSync(plansDir)) return errorResponse(`plans 目录不存在: ${plansDir}`)
34
55
 
35
- let targets: { name: string; path: string; keyword: string }[] = []
56
+ const targets: { name: string; path: string; keyword: string }[] = []
36
57
  const newPlans: string[] = []
37
58
  const allFiles = await readdirRecursive(plansDir)
38
59
  // 排除 .hitl.md(HITL 提案不是 Plan 本体,避免 plan_track 误扫为独立 Plan)
@@ -54,8 +75,18 @@ export function registerPlanTools(server: ToolRegistrar) {
54
75
  // 提取 planKeyword(关键词: xxx)
55
76
  const kwMatch = content.match(/关键词[::]\s*(.+)/) || content.match(/planKeyword[::]\s*"?([^"\n]+)"?/)
56
77
  const keyword = kwMatch?.[1]?.trim() || t.name
57
- // 定位 spec dir
58
- const specDirName = basename(t.name).replace(/-plan-v\d+$/, "")
78
+ // 定位 spec dir(目录名可能带 -vN 版本后缀:优先带版本匹配,再回退不带版本,
79
+ // 最后回退解析 Plan §六 关联文档 Spec 路径真源)[Task 1.8 修复]
80
+ const planBase = basename(t.name).replace(/-plan-v\d+$/, "")
81
+ const versionSuffix = /-plan-(v\d+)$/.exec(basename(t.name))?.[1] ?? ""
82
+ const specCandidates = versionSuffix
83
+ ? [`${planBase}-${versionSuffix}`, planBase]
84
+ : [planBase]
85
+ const specRef = content.match(/specs\/([^/`\s]+)/)
86
+ if (specRef) specCandidates.push(specRef[1])
87
+ const specDirName =
88
+ specCandidates.find((d) => existsSync(join(specsDir, d))) ??
89
+ specCandidates[0]
59
90
  const tasksPath = join(specsDir, specDirName, "tasks.md")
60
91
  const checklistPath = join(specsDir, specDirName, "checklist.md")
61
92
  const specPath = join(specsDir, specDirName, "spec.md")
@@ -84,7 +115,6 @@ export function registerPlanTools(server: ToolRegistrar) {
84
115
  )
85
116
  const addRoutePath = addRouteFile ? join(plansDir, addRouteFile) : undefined
86
117
  // upsert PlanRecord
87
- const existing = await db.plan.findFirst({ where: { planName: t.name } })
88
118
  const data: Partial<PlanRow> = {
89
119
  planPath: t.path,
90
120
  specPath: existsSync(specPath) ? specPath : undefined,
@@ -94,11 +124,16 @@ export function registerPlanTools(server: ToolRegistrar) {
94
124
  totalTasks, doneTasks, checklistT, checklistTDone, checklistR,
95
125
  planKeyword: keyword,
96
126
  }
97
- if (existing) {
98
- await db.plan.update({ where: { id: existing.id }, data })
127
+ assertPathInRuntimeScope(runtimeContext, t.path)
128
+ const tracked = await trackPlanAndPublish(prisma as unknown as PlanTrackingDatabase, {
129
+ context: runtimeContext,
130
+ planName: t.name,
131
+ planPath: t.path,
132
+ projection: data,
133
+ })
134
+ if (!tracked.created) {
99
135
  results.push(` ✅ 已更新 (totalTasks=${totalTasks}, done=${doneTasks})` + (addRoutePath ? `, addRoute` : ``))
100
136
  } else {
101
- await db.plan.create({ data: { ...data, planName: t.name, planPath: t.path } })
102
137
  results.push(` ✅ 已创建 (totalTasks=${totalTasks}, done=${doneTasks})` + (addRoutePath ? `, addRoute` : ``))
103
138
  newPlans.push(t.name.replace(/-plan-v\d+$/, ""))
104
139
  }
@@ -106,7 +141,13 @@ export function registerPlanTools(server: ToolRegistrar) {
106
141
  // ── 孤儿对账(全量扫描时):PlanRecord 存在但 planPath 文件缺失 → 悬空清单。
107
142
  // 来源:create_hitl 预置行被中断 / 用户放弃未清理。允许暂时存在,不允许不可见。 ──
108
143
  if (!pn) {
109
- const allPlans = await db.plan.findMany({ orderBy: { createdAt: "desc" } })
144
+ const allPlans = await db.plan.findMany({
145
+ where: {
146
+ projectKey: runtimeContext.projectKey,
147
+ adapterKey: runtimeContext.adapterKey,
148
+ },
149
+ orderBy: { createdAt: "desc" },
150
+ })
110
151
  const orphans = allPlans.filter(p => !existsSync(p.planPath))
111
152
  if (orphans.length > 0) {
112
153
  results.push("", `⚠️ 悬空 Plan(记录在、文件缺失)— 续写或清理:`)
@@ -137,17 +178,35 @@ export function registerPlanTools(server: ToolRegistrar) {
137
178
  }, async (args: Record<string, unknown>) => {
138
179
  try {
139
180
  const { planName } = args as { planName: string }
140
- const plan = await db.plan.findFirst({ where: { planName } })
141
- if (!plan) return textResponse(`📋 Plan: 未跟踪\nplanName: ${planName}\n\n操作: plan_track({ planName: "${planName}" })`)
142
- const t = plan.totalTasks || 0
143
- const d = plan.doneTasks || 0
144
- const ct = plan.checklistT || 0
145
- const ctd = plan.checklistTDone || 0
181
+ const resolution = await resolvePlanStatus(statusStore, runtimeContext, planName)
182
+ if (resolution.availability === "STATUS_UNAVAILABLE") {
183
+ return errorResponse(`Plan status 不可用(禁止按文件猜测): ${resolution.reason}`)
184
+ }
185
+ if (resolution.planName === null) {
186
+ return textResponse(`📋 Plan: 未跟踪\nplanName: ${planName}\nsource: database\ncontextId: ${runtimeContext.contextId}\n\n操作: plan_track({ planName: "${planName}" })`)
187
+ }
188
+ const plan = await db.plan.findFirst({
189
+ where: {
190
+ projectKey: runtimeContext.projectKey,
191
+ adapterKey: runtimeContext.adapterKey,
192
+ planName,
193
+ },
194
+ })
195
+ if (!plan) return errorResponse(`Plan status 解析后记录消失,请重试: ${planName}`)
196
+ const t = resolution.progress.totalTasks
197
+ const d = resolution.progress.doneTasks
198
+ const ct = resolution.progress.checklistT
199
+ const ctd = resolution.progress.checklistTDone
146
200
  const cr = plan.checklistR || 0
147
201
  const taskPct = t > 0 ? Math.round((d / t) * 100) : 0
148
202
  const lines = [
149
203
  `📊 ${planName}`,
150
204
  ``,
205
+ `lifecycle: ${resolution.lifecycle} (${resolution.isActive ? "active" : "inactive"})`,
206
+ `approval: ${resolution.approvalStatus ?? "—"}`,
207
+ `revision: ${resolution.revision}`,
208
+ `source: database`,
209
+ `contextId: ${runtimeContext.contextId}`,
151
210
  `tasks.md: ${d}/${t} (${taskPct}%) ${taskPct >= 100 ? "✅" : d > 0 ? "🔄" : "⬜"}`,
152
211
  `checklist: [T] ${ctd}/${ct} | [R] ${cr} 项`,
153
212
  `planKeyword: ${plan.planKeyword || "—"}`,
@@ -156,6 +215,7 @@ export function registerPlanTools(server: ToolRegistrar) {
156
215
  // P3 #5 契约角色展示(PlanRecord 已设置时)
157
216
  plan.contractRole ? `contractRole: ${plan.contractRole}` : null,
158
217
  plan.contractName ? `contractName: ${plan.contractName}` : null,
218
+ `machine: ${JSON.stringify(resolution)}`,
159
219
  ].filter(Boolean)
160
220
  return textResponse(lines.join("\n"))
161
221
  } catch (e) {
@@ -172,9 +232,16 @@ export function registerPlanTools(server: ToolRegistrar) {
172
232
  }, async (args: Record<string, unknown>) => {
173
233
  try {
174
234
  const { planName } = args as { planName: string }
175
- const plan = await db.plan.findFirst({ where: { planName } })
235
+ const plan = await db.plan.findFirst({
236
+ where: {
237
+ projectKey: runtimeContext.projectKey,
238
+ adapterKey: runtimeContext.adapterKey,
239
+ planName,
240
+ },
241
+ })
176
242
  if (!plan) return errorResponse(`PlanRecord 未找到: ${planName}`)
177
243
  const planPath = plan.planPath
244
+ assertPathInRuntimeScope(runtimeContext, planPath)
178
245
  if (!existsSync(planPath)) return errorResponse(`Plan 文件不存在: ${planPath}`)
179
246
  const content = readFileSync(planPath, "utf-8")
180
247
  const t = plan.totalTasks || 0
@@ -195,7 +262,6 @@ export function registerPlanTools(server: ToolRegistrar) {
195
262
  ].join("\n")
196
263
  // 用 indexOf 安全替换(不用跨行正则)
197
264
  const marker = "## 📊 Plan 进度快照"
198
- const markerEnd = "\n## 📊 Plan 进度快照"
199
265
  // 找到现有快照区块的结束位置
200
266
  const startIdx = content.indexOf(marker)
201
267
  let newContent: string
@@ -217,4 +283,73 @@ export function registerPlanTools(server: ToolRegistrar) {
217
283
  }
218
284
  })
219
285
 
286
+ // ===== plan_round_close =====
287
+ server.registerTool("plan_round_close", {
288
+ description: "关闭一个逻辑 PlanRound:在当前 RuntimeContextKey 内幂等写入 ROUND_CLOSED,并于同一 Prisma transaction 发布 PostgreSQL 唤醒通知。不会关闭整个 Plan,也不会修改 lifecycle/revision。",
289
+ inputSchema: z.object({
290
+ planName: z.string().min(1),
291
+ round: z.number().int().positive(),
292
+ beforeState: z.string().describe("非 null JSON 对象或数组字符串"),
293
+ afterState: z.string().describe("非 null JSON 对象或数组字符串,包含本轮产出摘要"),
294
+ reason: z.string().optional(),
295
+ operationKey: z.string().optional(),
296
+ }),
297
+ }, async (args: Record<string, unknown>) => {
298
+ try {
299
+ const input = args as { planName: string; round: number; beforeState: string; afterState: string; reason?: string; operationKey?: string }
300
+ const result = await closePlanRoundAndPublish(prisma as unknown as PlanRoundDatabase, {
301
+ context: runtimeContext,
302
+ planName: input.planName,
303
+ round: input.round,
304
+ beforeState: parseStructuredState(input.beforeState, "beforeState"),
305
+ afterState: parseStructuredState(input.afterState, "afterState"),
306
+ reason: input.reason,
307
+ operationKey: input.operationKey,
308
+ })
309
+ return textResponse([
310
+ "✅ PlanRound 已关闭",
311
+ `planName: ${result.plan.planName}`,
312
+ `round: ${input.round}`,
313
+ `operationId: ${result.operation.id}`,
314
+ `targetId: ${result.operation.targetId}`,
315
+ `lifecycle: ${result.plan.lifecycle} (未修改)`,
316
+ `revision: ${result.plan.revision} (未修改)`,
317
+ "source: database",
318
+ ].join("\n"))
319
+ } catch (e) {
320
+ return errorResponse(`plan_round_close 失败: ${e instanceof Error ? e.message : String(e)}`)
321
+ }
322
+ })
323
+
324
+ // ===== plan_round_status =====
325
+ server.registerTool("plan_round_status", {
326
+ description: "查询当前 RuntimeContextKey 下的 ROUND_CLOSED 记录。数据库是权威状态;通知仅负责唤醒订阅者调用本查询。",
327
+ inputSchema: z.object({
328
+ planName: z.string().min(1),
329
+ round: z.number().int().positive().optional(),
330
+ }),
331
+ }, async (args: Record<string, unknown>) => {
332
+ try {
333
+ const input = args as { planName: string; round?: number }
334
+ const snapshot = await queryPlanRounds(prisma as unknown as PlanRoundReadClient, {
335
+ context: runtimeContext,
336
+ planName: input.planName,
337
+ round: input.round,
338
+ })
339
+ if (!snapshot.planId) return textResponse(`📋 PlanRound: 未找到 scoped Plan\nplanName: ${input.planName}\ncontextId: ${runtimeContext.contextId}`)
340
+ const lines = [
341
+ `📋 PlanRound: ${snapshot.planName}`,
342
+ `contextId: ${runtimeContext.contextId}`,
343
+ `source: database`,
344
+ `records: ${snapshot.rounds.length}`,
345
+ ]
346
+ for (const record of snapshot.rounds) {
347
+ lines.push(`- ${record.targetId} | ${record.createdAt.toISOString()} | operationId=${record.id}`)
348
+ }
349
+ return textResponse(lines.join("\n"))
350
+ } catch (e) {
351
+ return errorResponse(`plan_round_status 失败: ${e instanceof Error ? e.message : String(e)}`)
352
+ }
353
+ })
354
+
220
355
  }
@@ -7,6 +7,8 @@ import { readFileSafe, readdirRecursive, PROJECT_ROOT, MAGIC_DIR } from "../shar
7
7
  import { prisma } from "../shared/prisma.js"
8
8
  import type { PlanRow, ReviewRow } from "../shared/db-types.js"
9
9
  import { PlanRowSchema, ReviewRowSchema, validatedDelegate } from "../shared/db-types.js"
10
+ import { getRuntimeContext } from "../shared/env.js"
11
+ import { assertPathInRuntimeScope } from "../shared/runtime-context.js"
10
12
 
11
13
  const db = {
12
14
  get review() { return validatedDelegate<ReviewRow>(prisma.reviewRecord, ReviewRowSchema, "ReviewRecord") },
@@ -34,6 +36,7 @@ function countIssueType(content: string): { p0: number; p1: number; backflow: nu
34
36
  }
35
37
 
36
38
  export function registerReviewTools(server: ToolRegistrar) {
39
+ const runtimeContext = getRuntimeContext()
37
40
 
38
41
  // ===== review_track =====
39
42
  server.registerTool("review_track", {
@@ -44,7 +47,7 @@ export function registerReviewTools(server: ToolRegistrar) {
44
47
  }),
45
48
  }, async (args: Record<string, unknown>) => {
46
49
  try {
47
- const { planName, scanAll } = args as { planName?: string; scanAll?: boolean }
50
+ const { planName } = args as { planName?: string; scanAll?: boolean }
48
51
  const pn = typeof planName === "string" ? planName : ""
49
52
  const reviewsDir = join(PROJECT_ROOT, MAGIC_DIR, "reviews")
50
53
  if (!existsSync(reviewsDir)) return errorResponse(`reviews 目录不存在: ${reviewsDir}`)
@@ -57,7 +60,7 @@ export function registerReviewTools(server: ToolRegistrar) {
57
60
  const fullPath = join(reviewsDir, rf)
58
61
  // 从文件名推导 planName
59
62
  // 格式: {plan-name}-review-v1.md 或 {plan-name}-review-implementation.md
60
- let derivedPlan = basename(rf, ".md")
63
+ const derivedPlan = basename(rf, ".md")
61
64
  .replace(/-review-.*$/, "")
62
65
  .replace(/-implementation.*$/, "")
63
66
  .replace(/-runtime.*$/, "")
@@ -69,7 +72,11 @@ export function registerReviewTools(server: ToolRegistrar) {
69
72
  // 而 derivedPlan 为 {planPrefix}(从 review 文件名中去掉了 -review-v{n})
70
73
  // 需要用 contains 匹配,而不是 exact match
71
74
  const planMatch = await db.plan.findFirst({
72
- where: { planName: { contains: derivedPlan } },
75
+ where: {
76
+ projectKey: runtimeContext.projectKey,
77
+ adapterKey: runtimeContext.adapterKey,
78
+ planName: { contains: derivedPlan },
79
+ },
73
80
  })
74
81
  const actualPlanName = planMatch
75
82
  ? planMatch.planName
@@ -81,7 +88,12 @@ export function registerReviewTools(server: ToolRegistrar) {
81
88
  const rtype = detectReviewType(rf)
82
89
  const stats = countIssueType(content)
83
90
  const record = await db.review.findFirst({
84
- where: { planName: actualPlanName, type: rtype },
91
+ where: {
92
+ projectKey: runtimeContext.projectKey,
93
+ adapterKey: runtimeContext.adapterKey,
94
+ planName: actualPlanName,
95
+ type: rtype,
96
+ },
85
97
  })
86
98
 
87
99
  const data = {
@@ -95,7 +107,13 @@ export function registerReviewTools(server: ToolRegistrar) {
95
107
  results.push(`🔄 ${rf} (${rtype}) P0=${stats.p0} P1=${stats.p1}`)
96
108
  } else {
97
109
  await db.review.create({
98
- data: { planName: actualPlanName, type: rtype, ...data },
110
+ data: {
111
+ projectKey: runtimeContext.projectKey,
112
+ adapterKey: runtimeContext.adapterKey,
113
+ planName: actualPlanName,
114
+ type: rtype,
115
+ ...data,
116
+ },
99
117
  })
100
118
  results.push(`✅ ${rf} (${rtype}) P0=${stats.p0} P1=${stats.p1}`)
101
119
  }
@@ -118,7 +136,11 @@ export function registerReviewTools(server: ToolRegistrar) {
118
136
  try {
119
137
  const { planName } = args as { planName: string }
120
138
  const rows = await db.review.findMany({
121
- where: { planName },
139
+ where: {
140
+ projectKey: runtimeContext.projectKey,
141
+ adapterKey: runtimeContext.adapterKey,
142
+ planName,
143
+ },
122
144
  orderBy: { type: "asc" },
123
145
  })
124
146
 
@@ -130,9 +152,9 @@ export function registerReviewTools(server: ToolRegistrar) {
130
152
 
131
153
  const lines = [`📝 Review: ${planName}`, ``]
132
154
  for (const r of rows) {
133
- const p0 = r.p0Count as number || 0
134
- const p1 = r.p1Count as number || 0
135
- const br = r.backflowRate as number || 0
155
+ const p0 = r.p0Count || 0
156
+ const p1 = r.p1Count || 0
157
+ const br = r.backflowRate || 0
136
158
  const total = p0 + p1
137
159
  lines.push(
138
160
  ` [${r.type}] P0=${p0} P1=${p1} (合计 ${total}) 回流=${br}`,
@@ -140,8 +162,8 @@ export function registerReviewTools(server: ToolRegistrar) {
140
162
  )
141
163
  }
142
164
  // 汇总
143
- const tP0 = rows.reduce((s: number, r) => s + (r.p0Count as number || 0), 0)
144
- const tP1 = rows.reduce((s: number, r) => s + (r.p1Count as number || 0), 0)
165
+ const tP0 = rows.reduce((s: number, r) => s + (r.p0Count || 0), 0)
166
+ const tP1 = rows.reduce((s: number, r) => s + (r.p1Count || 0), 0)
145
167
  lines.push(``, `总计: P0=${tP0} P1=${tP1}`)
146
168
  return textResponse(lines.join("\n"))
147
169
  } catch (e) {
@@ -158,7 +180,13 @@ export function registerReviewTools(server: ToolRegistrar) {
158
180
  }, async (args: Record<string, unknown>) => {
159
181
  try {
160
182
  const { planName } = args as { planName: string }
161
- const rows = await db.review.findMany({ where: { planName } })
183
+ const rows = await db.review.findMany({
184
+ where: {
185
+ projectKey: runtimeContext.projectKey,
186
+ adapterKey: runtimeContext.adapterKey,
187
+ planName,
188
+ },
189
+ })
162
190
 
163
191
  if (!rows.length) return errorResponse(`无 ReviewRecord: ${planName}`)
164
192
 
@@ -169,10 +197,11 @@ export function registerReviewTools(server: ToolRegistrar) {
169
197
  results.push(`⚠️ 文件不存在: ${rp || r.type}`)
170
198
  continue
171
199
  }
200
+ assertPathInRuntimeScope(runtimeContext, rp)
172
201
  const content = readFileSync(rp, "utf-8")
173
- const p0 = r.p0Count as number || 0
174
- const p1 = r.p1Count as number || 0
175
- const br = r.backflowRate as number || 0
202
+ const p0 = r.p0Count || 0
203
+ const p1 = r.p1Count || 0
204
+ const br = r.backflowRate || 0
176
205
 
177
206
  const metaLines = [
178
207
  "<!-- REVIEW_META",
@@ -1,6 +1,17 @@
1
1
  import { McpServer } from "@modelcontextprotocol/server"
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"
3
3
  import { registerAll } from "./mcp-server/index.js"
4
+ import { redact } from "./mcp-server/shared/redact.js"
5
+ import { DATABASE_URL, getRuntimeContext } from "./mcp-server/shared/env.js"
6
+ import { prisma } from "./mcp-server/shared/prisma.js"
7
+ import { resolvePlanStatus } from "./mcp-server/shared/plan-lifecycle.js"
8
+ import { createPrismaPlanStatusStore } from "./mcp-server/shared/plan-status-store.js"
9
+ import {
10
+ createPgLifecycleListenClient,
11
+ PlanLifecycleSubscriber,
12
+ } from "./mcp-server/shared/plan-lifecycle-subscriber.js"
13
+ import { PlanRoundSubscriber } from "./mcp-server/shared/plan-round-subscriber.js"
14
+ import { queryPlanRounds, type PlanRoundReadClient } from "./mcp-server/shared/plan-round-store.js"
4
15
 
5
16
  async function main() {
6
17
  const server = new McpServer(
@@ -10,10 +21,58 @@ async function main() {
10
21
  registerAll(server)
11
22
  const transport = new StdioServerTransport()
12
23
  await server.connect(transport)
24
+ let lifecycleSubscriber: PlanLifecycleSubscriber | undefined
25
+ let planRoundSubscriber: PlanRoundSubscriber | undefined
26
+ if (/^postgres(ql)?:\/\//.test(DATABASE_URL)) {
27
+ const context = getRuntimeContext()
28
+ const store = createPrismaPlanStatusStore(prisma)
29
+ lifecycleSubscriber = new PlanLifecycleSubscriber({
30
+ context,
31
+ clientFactory: () => createPgLifecycleListenClient(DATABASE_URL),
32
+ resolveStatus: ({ context: subscriberContext, planId }) => resolvePlanStatus(
33
+ store,
34
+ subscriberContext,
35
+ planId ? { planId } : { activeOnly: true },
36
+ ),
37
+ onSnapshot: (snapshot, envelope) => {
38
+ const trigger = envelope ? `notify:${envelope.eventId}` : "initial"
39
+ console.error(`[ADD-MCP] lifecycle pull (${trigger}) ${JSON.stringify(snapshot)}`)
40
+ },
41
+ onError: (error) => console.error(`[ADD-MCP] lifecycle subscriber: ${redact(error.message)}`),
42
+ })
43
+ await lifecycleSubscriber.start().catch((error: unknown) => {
44
+ console.error(`[ADD-MCP] lifecycle initial pull failed: ${redact(error instanceof Error ? error.message : String(error))}`)
45
+ })
46
+ planRoundSubscriber = new PlanRoundSubscriber({
47
+ context,
48
+ clientFactory: () => createPgLifecycleListenClient(DATABASE_URL),
49
+ queryRounds: ({ context: subscriberContext, planId }) => queryPlanRounds(
50
+ prisma as unknown as PlanRoundReadClient,
51
+ { context: subscriberContext, planId },
52
+ ),
53
+ onSnapshot: (snapshot, envelope) => {
54
+ const trigger = envelope ? `notify:${envelope.eventId}` : "initial"
55
+ console.error(`[ADD-MCP] PlanRound pull (${trigger}) ${JSON.stringify({
56
+ contextId: snapshot.context.contextId,
57
+ planId: snapshot.planId,
58
+ planName: snapshot.planName,
59
+ records: snapshot.rounds.length,
60
+ })}`)
61
+ },
62
+ onError: (error) => console.error(`[ADD-MCP] PlanRound subscriber: ${redact(error.message)}`),
63
+ })
64
+ await planRoundSubscriber.start().catch((error: unknown) => {
65
+ console.error(`[ADD-MCP] PlanRound initial pull failed: ${redact(error instanceof Error ? error.message : String(error))}`)
66
+ })
67
+ }
68
+ process.stdin.once("end", () => {
69
+ void lifecycleSubscriber?.stop()
70
+ void planRoundSubscriber?.stop()
71
+ })
13
72
  console.error("[ADD-MCP] add-dev-tools MCP server started on stdio")
14
73
  }
15
74
 
16
75
  main().catch((error) => {
17
- console.error("[ADD-MCP] Fatal error:", error)
76
+ console.error("[ADD-MCP] Fatal error:", redact(error instanceof Error ? error.message : String(error)))
18
77
  process.exit(1)
19
78
  })
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ import { getRuntimeContext } from "./mcp-server/shared/env.js"
3
+ import { prisma } from "./mcp-server/shared/prisma.js"
4
+ import { resolvePlanStatus } from "./mcp-server/shared/plan-lifecycle.js"
5
+ import { createPrismaPlanStatusStore } from "./mcp-server/shared/plan-status-store.js"
6
+
7
+ async function main(): Promise<void> {
8
+ const context = getRuntimeContext()
9
+ const store = createPrismaPlanStatusStore(prisma)
10
+ const snapshot = await resolvePlanStatus(store, context, { activeOnly: true })
11
+ process.stdout.write(`${JSON.stringify(snapshot)}\n`)
12
+ if (snapshot.availability === "STATUS_UNAVAILABLE") process.exitCode = 3
13
+ }
14
+
15
+ main()
16
+ .catch((error: unknown) => {
17
+ process.stderr.write(`[ADD plan-status-bridge] ${error instanceof Error ? error.message : String(error)}\n`)
18
+ process.exitCode = 3
19
+ })
20
+ .finally(async () => {
21
+ const runtimeClient = prisma as unknown as Record<string, unknown>
22
+ const disconnect = runtimeClient.$disconnect
23
+ if (typeof disconnect === "function") {
24
+ await Reflect.apply(disconnect, prisma, []).catch(() => undefined)
25
+ }
26
+ })
@@ -379,7 +379,7 @@ Plan 级闭包: {业务功能描述}
379
379
 
380
380
  **活跃判定规则**:
381
381
 
382
- 1. **判定输入**:指定 magicDir 下的 `plans/` 目录(按当前端优先回退——以 Claude IDE 宿主环境为例:`.claude/plans/` → `.qoder/plans/` → `.add/plans/`,其他 IDE 同理取各自的 magicDir)
382
+ 1. **判定输入**:当前 adapter 自己的 `{{magicDir}}/plans/`。各 adapter Plan/Spec/Route/Review/HITL 相互独立,禁止跨 adapter fallback
383
383
  2. **索引优先**:先读 `plans/index.md` 匹配 planKeyword → 无匹配才 glob `*-plan-v*.md`
384
384
  3. **活跃定义**:读 add-route 文件 → 存在 `[ ]` 未勾选的 Step 产出项 = 活跃(进行中);全部 `[x]` = 已收敛(不活跃)
385
385
  4. **多 Plan 冲突**:多个 Plan 均有 `[ ]` → 取 add-route 文件最近修改时间(`mtime`)最大的 Plan
@@ -863,7 +863,7 @@ check_add_route_completeness({ planKeyword: "<Plan 核心关键词>" })
863
863
 
864
864
  ### 3.5.3 生成实现审查文档
865
865
 
866
- HITL temporary.md 流程执行:读取 `review-implementation-template.md` 先写 `{name}-review-implementation.temporary.md`(只含 HITL 发现总览表)→ 人类一次性拍板 逐条展开详细分析 写入 `.qoder/reviews/`(guard 放行)→ 删除 temporary。详见上方模板生成规则处的 Review HITL 磋商说明。
866
+ 读取 `review-implementation-template.md`,将实现审查发现整理为 `dimensions`,调用 `create_hitl({ planName: "{name}-review-implementation", type: "PLAN_REVIEW", dimensions })`。`create_hitl` 自动生成 `*.hitl.md` 审批提案并写入 HITL 记录;人类审核后调用 `update_hitl` 完成拍板。通过 `status_hitl` 确认状态为 `TONGYI` 后,方可逐条展开详细分析并写入 `{{magicDir}}/reviews/`;若为 `BOHUI`,修订发现后调用 `create_hitl` 新建 round。
867
867
 
868
868
  ### 3.5.4 生成运行时审查文档(所有 [T] 项通过后)
869
869
 
@@ -1,58 +1,61 @@
1
1
  {
2
+ "_generated": true,
3
+ "_generated_source": "guard-rules.toml",
2
4
  "template": "add-route-template.md",
3
- "shared_by": [
4
- "add-route-template.md",
5
- "add-route-template-heavyweight.md"
6
- ],
7
5
  "sections": [
8
6
  {
9
7
  "id": "step0",
10
- "heading": "## Step 0",
11
- "required": true
8
+ "required": true,
9
+ "heading": "## Step 0"
12
10
  },
13
11
  {
14
12
  "id": "step0_plan_track",
15
- "pattern": "plan_track\\(\\{ planName:",
16
13
  "required": true,
14
+ "anchor": "plan_track",
17
15
  "within": "## Step 0",
16
+ "pattern": "plan_track\\(\\{ planName:",
18
17
  "description": "Step 0 产出必须包含 plan_track 落库步骤"
19
18
  },
20
19
  {
21
20
  "id": "step1",
22
- "heading": "## Step 1",
23
- "required": true
21
+ "required": true,
22
+ "heading": "## Step 1"
24
23
  },
25
24
  {
26
25
  "id": "step2",
27
- "heading": "## Step 2",
28
- "required": true
26
+ "required": true,
27
+ "heading": "## Step 2"
29
28
  },
30
29
  {
31
30
  "id": "step3",
32
- "heading": "## Step 3",
33
- "required": true
31
+ "required": true,
32
+ "heading": "## Step 3"
34
33
  },
35
34
  {
36
35
  "id": "step3_5",
37
- "heading": "## Step 3.5",
38
- "required": false
36
+ "required": false,
37
+ "heading": "## Step 3.5"
39
38
  },
40
39
  {
41
40
  "id": "step4",
42
- "heading": "## Step 4",
43
- "required": true
41
+ "required": true,
42
+ "heading": "## Step 4"
44
43
  },
45
44
  {
46
45
  "id": "step5",
47
- "heading": "## Step 5",
48
- "required": true
46
+ "required": true,
47
+ "heading": "## Step 5"
49
48
  },
50
49
  {
51
50
  "id": "step8",
52
- "heading": "## Step 8",
53
- "required": true
51
+ "required": true,
52
+ "heading": "## Step 8"
54
53
  }
55
54
  ],
55
+ "shared_by": [
56
+ "add-route-template.md",
57
+ "add-route-template-heavyweight.md"
58
+ ],
56
59
  "placeholders": [
57
60
  "{需求域名}",
58
61
  "{核心内容}",
@@ -71,38 +74,3 @@
71
74
  "阶段"
72
75
  ]
73
76
  }
74
- {
75
- "template": "add-route-template.md",
76
- "shared_by": [
77
- "add-route-template.md",
78
- "add-route-template-heavyweight.md"
79
- ],
80
- "sections": [
81
- {
82
- "id": "step0",
83
- "heading": "## Step 0",
84
- "required": true
85
- },
86
- {
87
- "id": "step3",
88
- "heading": "## Step 3",
89
- "required": true
90
- },
91
- {
92
- "id": "step8",
93
- "heading": "## Step 8",
94
- "required": true
95
- }
96
- ],
97
- "placeholders": [
98
- "{需求域名}",
99
- "{核心内容}",
100
- "{版本号}"
101
- ],
102
- "forbidden_terms": [
103
- "Phase",
104
- "Round",
105
- "轮次",
106
- "阶段"
107
- ]
108
- }
@@ -169,7 +169,7 @@
169
169
  | 发起时机 | 契约新建 / 契约重大变更(参与者增减、边界重划、触发条件变更) |
170
170
  | round 机制 | 复用 HitlRecord.round(同一契约多次变更递增 round) |
171
171
  | 与子 Plan 审批关系 | 契约审批是**总控级**(planName=总控 Plan);子 Plan 审批独立进行(互不阻塞) |
172
- | 哨兵 | `.qoder/hitl/.tongyi-{contractName}`(复用现有哨兵机制) |
172
+ | 哨兵 | `{{magicDir}}/hitl/.tongyi-{contractName}`(复用当前 adapter 的哨兵机制) |
173
173
  | 审计 | 契约审批记录入 HitlRecord(type=COLLAB_CONTRACT)→ query_audit_logs 可查 |
174
174
 
175
175
  ---