add-coder 0.3.27 → 0.3.29

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 (262) hide show
  1. package/README.md +252 -150
  2. package/package.json +2 -1
  3. package/templates/.add-coder-src-hash.json +126 -148
  4. package/templates/adapters/claude/hooks/doc-format-guard.ts +32 -0
  5. package/templates/adapters/claude/hooks/lib/claude-env.ts +7 -0
  6. package/templates/adapters/claude/hooks/notification.ts +24 -0
  7. package/templates/adapters/claude/hooks/permission-denied.ts +33 -0
  8. package/templates/adapters/claude/hooks/permission-gate.ts +12 -0
  9. package/templates/adapters/claude/hooks/post-tool-failure.ts +4 -0
  10. package/templates/adapters/claude/hooks/post-tool-use.ts +17 -0
  11. package/templates/adapters/claude/hooks/pre-compact.ts +10 -0
  12. package/templates/adapters/claude/hooks/pre-tool-use.ts +81 -0
  13. package/templates/adapters/claude/hooks/prompt-submit.ts +82 -0
  14. package/templates/adapters/claude/hooks/review-checklist.ts +4 -0
  15. package/templates/adapters/claude/hooks/session-end.ts +10 -0
  16. package/templates/adapters/claude/hooks/session-start.ts +24 -0
  17. package/templates/adapters/claude/hooks/stop-check.ts +15 -0
  18. package/templates/adapters/claude/hooks/stop-failure.ts +34 -0
  19. package/templates/adapters/claude/hooks/subagent-guard.ts +25 -0
  20. package/templates/adapters/claude/hooks/subagent-stop.ts +12 -0
  21. package/templates/adapters/claude/settings.json +13 -13
  22. package/templates/adapters/codex/hooks/doc-format-guard.ts +30 -0
  23. package/templates/adapters/codex/hooks/notification.ts +26 -0
  24. package/templates/adapters/codex/hooks/permission-gate.ts +15 -0
  25. package/templates/adapters/codex/hooks/post-tool-failure.ts +14 -0
  26. package/templates/adapters/codex/hooks/post-tool-use.ts +85 -0
  27. package/templates/adapters/codex/hooks/pre-compact.ts +20 -0
  28. package/templates/adapters/codex/hooks/pre-tool-use.ts +141 -0
  29. package/templates/adapters/codex/hooks/prompt-submit.ts +38 -0
  30. package/templates/adapters/codex/hooks/review-checklist.ts +5 -0
  31. package/templates/adapters/codex/hooks/session-end.ts +19 -0
  32. package/templates/adapters/codex/hooks/session-start.ts +45 -0
  33. package/templates/adapters/codex/hooks/stop-check.ts +59 -0
  34. package/templates/adapters/codex/hooks/subagent-guard.ts +22 -0
  35. package/templates/adapters/codex/hooks/subagent-stop.ts +20 -0
  36. package/templates/adapters/codex/hooks.json +5 -5
  37. package/templates/adapters/qoder/hooks/doc-format-guard.ts +34 -0
  38. package/templates/adapters/qoder/hooks/lib/context-inject.ts +61 -0
  39. package/templates/adapters/qoder/hooks/lib/qoder-env.ts +23 -0
  40. package/templates/adapters/qoder/hooks/lib/review-checklist.ts +5 -0
  41. package/templates/adapters/qoder/hooks/notification.ts +34 -0
  42. package/templates/adapters/qoder/hooks/permission-gate.ts +26 -0
  43. package/templates/adapters/qoder/hooks/post-tool-failure.ts +30 -0
  44. package/templates/adapters/qoder/hooks/post-tool-use.ts +32 -0
  45. package/templates/adapters/qoder/hooks/pre-compact.ts +16 -0
  46. package/templates/adapters/qoder/hooks/pre-tool-use.ts +112 -0
  47. package/templates/adapters/qoder/hooks/prompt-submit.ts +66 -0
  48. package/templates/adapters/qoder/hooks/review-checklist.ts +24 -0
  49. package/templates/adapters/qoder/hooks/session-end.ts +27 -0
  50. package/templates/adapters/qoder/hooks/session-start.ts +83 -0
  51. package/templates/adapters/qoder/hooks/stop-check.ts +69 -0
  52. package/templates/adapters/qoder/hooks/subagent-guard.ts +16 -0
  53. package/templates/adapters/qoder/hooks/subagent-stop.ts +33 -0
  54. package/templates/adapters/qoder/settings.json +13 -13
  55. package/templates/adapters/trae/hooks/doc-format-guard.ts +30 -0
  56. package/templates/adapters/trae/hooks/notification.ts +22 -0
  57. package/templates/adapters/trae/hooks/permission-gate.ts +15 -0
  58. package/templates/adapters/trae/hooks/post-tool-failure.ts +14 -0
  59. package/templates/adapters/trae/hooks/post-tool-use.ts +24 -0
  60. package/templates/adapters/trae/hooks/pre-compact.ts +15 -0
  61. package/templates/adapters/trae/hooks/pre-tool-use.ts +40 -0
  62. package/templates/adapters/trae/hooks/prompt-submit.ts +29 -0
  63. package/templates/adapters/trae/hooks/review-checklist.ts +5 -0
  64. package/templates/adapters/trae/hooks/session-end.ts +16 -0
  65. package/templates/adapters/trae/hooks/session-start.ts +15 -0
  66. package/templates/adapters/trae/hooks/stop-check.ts +20 -0
  67. package/templates/adapters/trae/hooks/subagent-guard.ts +22 -0
  68. package/templates/adapters/trae/hooks/subagent-stop.ts +15 -0
  69. package/templates/adapters/trae/hooks.json +1 -1
  70. package/templates/adapters/vscode/hooks/doc-format-guard.ts +27 -0
  71. package/templates/adapters/vscode/hooks/notification.ts +22 -0
  72. package/templates/adapters/vscode/hooks/permission-gate.ts +15 -0
  73. package/templates/adapters/vscode/hooks/post-tool-failure.ts +32 -0
  74. package/templates/adapters/vscode/hooks/post-tool-use.ts +25 -0
  75. package/templates/adapters/vscode/hooks/pre-compact.ts +15 -0
  76. package/templates/adapters/vscode/hooks/pre-tool-use.ts +78 -0
  77. package/templates/adapters/vscode/hooks/prompt-submit.ts +74 -0
  78. package/templates/adapters/vscode/hooks/review-checklist.ts +5 -0
  79. package/templates/adapters/vscode/hooks/session-end.ts +16 -0
  80. package/templates/adapters/vscode/hooks/session-start.ts +24 -0
  81. package/templates/adapters/vscode/hooks/stop-check.ts +21 -0
  82. package/templates/adapters/vscode/hooks/subagent-guard.ts +21 -0
  83. package/templates/adapters/vscode/hooks/subagent-stop.ts +15 -0
  84. package/templates/core/docs/ADD-governance-claude-code.md +3 -3
  85. package/templates/core/docs/ADD-governance-codex.md +5 -5
  86. package/templates/core/docs/ADD-governance-qoder-cn.md +4 -4
  87. package/templates/core/docs/ADD-governance-trae.md +2 -2
  88. package/templates/core/docs/ADD-governance-vscode-copilot.md +21 -22
  89. package/templates/core/governance/audit-bridge.ts +47 -0
  90. package/templates/core/governance/common.ts +299 -0
  91. package/templates/core/governance/context-inject.ts +53 -0
  92. package/templates/core/governance/doc-format-guard.ts +440 -0
  93. package/templates/core/governance/notification-router.ts +62 -0
  94. package/templates/core/governance/notify.ts +66 -0
  95. package/templates/core/governance/permission-gate-router.ts +42 -0
  96. package/templates/core/governance/post-tool-failure-router.ts +56 -0
  97. package/templates/core/governance/post-tool-router.ts +212 -0
  98. package/templates/core/governance/pre-compact-guard.ts +41 -0
  99. package/templates/core/governance/pre-tool-guard.ts +309 -0
  100. package/templates/core/governance/preload-templates.ts +241 -0
  101. package/templates/core/governance/prompt-router.ts +222 -0
  102. package/templates/core/governance/review-checklist-guard.ts +186 -0
  103. package/templates/core/governance/rules.ts +720 -0
  104. package/templates/core/governance/session-end.ts +88 -0
  105. package/templates/core/governance/session-start-guard.ts +99 -0
  106. package/templates/core/governance/state-detect.ts +6 -0
  107. package/templates/core/governance/stop-router.ts +140 -0
  108. package/templates/core/governance/subagent-guard-router.ts +35 -0
  109. package/templates/core/governance/subagent-stop-router.ts +93 -0
  110. package/templates/core/governance/subagent-stop.ts +75 -0
  111. package/templates/core/governance/vocabulary.ts +109 -0
  112. package/templates/core/hooks/doc-format-guard.ts +21 -0
  113. package/templates/core/hooks/notification.ts +11 -0
  114. package/templates/core/hooks/permission-gate.ts +7 -0
  115. package/templates/core/hooks/post-tool-failure.ts +7 -0
  116. package/templates/core/hooks/post-tool-use.ts +23 -0
  117. package/templates/core/hooks/pre-compact.ts +9 -0
  118. package/templates/core/hooks/pre-tool-use.ts +30 -0
  119. package/templates/core/hooks/prompt-submit.ts +11 -0
  120. package/templates/core/hooks/review-checklist.ts +4 -0
  121. package/templates/core/hooks/session-end.ts +10 -0
  122. package/templates/core/hooks/session-start.ts +9 -0
  123. package/templates/core/hooks/stop-check.ts +14 -0
  124. package/templates/core/hooks/subagent-guard.ts +8 -0
  125. package/templates/core/hooks/subagent-stop.ts +9 -0
  126. package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +12 -1
  127. package/templates/core/scripts/mcp-server/tools/plan.ts +12 -2
  128. package/templates/adapters/claude/hooks/doc-format-guard.sh +0 -283
  129. package/templates/adapters/claude/hooks/lib/common.sh +0 -220
  130. package/templates/adapters/claude/hooks/lib/context-inject.sh +0 -96
  131. package/templates/adapters/claude/hooks/lib/notify.sh +0 -36
  132. package/templates/adapters/claude/hooks/lib/preload-templates.sh +0 -212
  133. package/templates/adapters/claude/hooks/lib/session-end.sh +0 -76
  134. package/templates/adapters/claude/hooks/lib/state-detect.sh +0 -13
  135. package/templates/adapters/claude/hooks/lib/subagent-stop.sh +0 -90
  136. package/templates/adapters/claude/hooks/lib/vocabulary.sh +0 -62
  137. package/templates/adapters/claude/hooks/notification.sh +0 -45
  138. package/templates/adapters/claude/hooks/permission-denied.sh +0 -42
  139. package/templates/adapters/claude/hooks/permission-gate.sh +0 -18
  140. package/templates/adapters/claude/hooks/post-tool-failure.sh +0 -10
  141. package/templates/adapters/claude/hooks/post-tool-use.sh +0 -35
  142. package/templates/adapters/claude/hooks/pre-compact.sh +0 -37
  143. package/templates/adapters/claude/hooks/pre-tool-use.sh +0 -153
  144. package/templates/adapters/claude/hooks/prompt-submit.sh +0 -99
  145. package/templates/adapters/claude/hooks/review-checklist.sh +0 -10
  146. package/templates/adapters/claude/hooks/session-end.sh +0 -21
  147. package/templates/adapters/claude/hooks/session-start.sh +0 -36
  148. package/templates/adapters/claude/hooks/stop-check.sh +0 -54
  149. package/templates/adapters/claude/hooks/stop-failure.sh +0 -44
  150. package/templates/adapters/claude/hooks/subagent-guard.sh +0 -36
  151. package/templates/adapters/claude/hooks/subagent-stop.sh +0 -20
  152. package/templates/adapters/codex/hooks/doc-format-guard.sh +0 -343
  153. package/templates/adapters/codex/hooks/lib/common.sh +0 -209
  154. package/templates/adapters/codex/hooks/lib/context-inject.sh +0 -96
  155. package/templates/adapters/codex/hooks/lib/notify.sh +0 -36
  156. package/templates/adapters/codex/hooks/lib/preload-templates.sh +0 -208
  157. package/templates/adapters/codex/hooks/lib/session-end.sh +0 -76
  158. package/templates/adapters/codex/hooks/lib/state-detect.sh +0 -8
  159. package/templates/adapters/codex/hooks/lib/subagent-stop.sh +0 -90
  160. package/templates/adapters/codex/hooks/lib/vocabulary.sh +0 -51
  161. package/templates/adapters/codex/hooks/notification.sh +0 -30
  162. package/templates/adapters/codex/hooks/permission-gate.sh +0 -18
  163. package/templates/adapters/codex/hooks/post-tool-failure.sh +0 -10
  164. package/templates/adapters/codex/hooks/post-tool-use.sh +0 -43
  165. package/templates/adapters/codex/hooks/pre-compact.sh +0 -37
  166. package/templates/adapters/codex/hooks/pre-tool-use.sh +0 -130
  167. package/templates/adapters/codex/hooks/prompt-submit.sh +0 -92
  168. package/templates/adapters/codex/hooks/review-checklist.sh +0 -10
  169. package/templates/adapters/codex/hooks/session-end.sh +0 -38
  170. package/templates/adapters/codex/hooks/session-start.sh +0 -63
  171. package/templates/adapters/codex/hooks/stop-check.sh +0 -71
  172. package/templates/adapters/codex/hooks/subagent-guard.sh +0 -15
  173. package/templates/adapters/codex/hooks/subagent-stop.sh +0 -47
  174. package/templates/adapters/qoder/hooks/doc-format-guard.sh +0 -341
  175. package/templates/adapters/qoder/hooks/lib/common.sh +0 -223
  176. package/templates/adapters/qoder/hooks/lib/context-inject.sh +0 -96
  177. package/templates/adapters/qoder/hooks/lib/notify.sh +0 -36
  178. package/templates/adapters/qoder/hooks/lib/preload-templates.sh +0 -212
  179. package/templates/adapters/qoder/hooks/lib/session-end.sh +0 -76
  180. package/templates/adapters/qoder/hooks/lib/state-detect.sh +0 -13
  181. package/templates/adapters/qoder/hooks/lib/subagent-stop.sh +0 -90
  182. package/templates/adapters/qoder/hooks/lib/vocabulary.sh +0 -62
  183. package/templates/adapters/qoder/hooks/notification.sh +0 -25
  184. package/templates/adapters/qoder/hooks/permission-gate.sh +0 -8
  185. package/templates/adapters/qoder/hooks/post-tool-failure.sh +0 -8
  186. package/templates/adapters/qoder/hooks/post-tool-use.sh +0 -121
  187. package/templates/adapters/qoder/hooks/pre-compact.sh +0 -37
  188. package/templates/adapters/qoder/hooks/pre-tool-use.sh +0 -154
  189. package/templates/adapters/qoder/hooks/prompt-submit.sh +0 -96
  190. package/templates/adapters/qoder/hooks/review-checklist.sh +0 -157
  191. package/templates/adapters/qoder/hooks/session-end.sh +0 -24
  192. package/templates/adapters/qoder/hooks/session-start.sh +0 -45
  193. package/templates/adapters/qoder/hooks/stop-check.sh +0 -59
  194. package/templates/adapters/qoder/hooks/subagent-guard.sh +0 -27
  195. package/templates/adapters/qoder/hooks/subagent-stop.sh +0 -26
  196. package/templates/adapters/trae/hooks/doc-format-guard.sh +0 -343
  197. package/templates/adapters/trae/hooks/lib/common.sh +0 -220
  198. package/templates/adapters/trae/hooks/lib/context-inject.sh +0 -96
  199. package/templates/adapters/trae/hooks/lib/notify.sh +0 -36
  200. package/templates/adapters/trae/hooks/lib/preload-templates.sh +0 -212
  201. package/templates/adapters/trae/hooks/lib/session-end.sh +0 -76
  202. package/templates/adapters/trae/hooks/lib/state-detect.sh +0 -10
  203. package/templates/adapters/trae/hooks/lib/subagent-stop.sh +0 -90
  204. package/templates/adapters/trae/hooks/lib/vocabulary.sh +0 -62
  205. package/templates/adapters/trae/hooks/notification.sh +0 -45
  206. package/templates/adapters/trae/hooks/permission-gate.sh +0 -18
  207. package/templates/adapters/trae/hooks/post-tool-failure.sh +0 -10
  208. package/templates/adapters/trae/hooks/post-tool-use.sh +0 -138
  209. package/templates/adapters/trae/hooks/pre-compact.sh +0 -37
  210. package/templates/adapters/trae/hooks/pre-tool-use.sh +0 -223
  211. package/templates/adapters/trae/hooks/prompt-submit.sh +0 -87
  212. package/templates/adapters/trae/hooks/review-checklist.sh +0 -10
  213. package/templates/adapters/trae/hooks/session-end.sh +0 -38
  214. package/templates/adapters/trae/hooks/session-start.sh +0 -53
  215. package/templates/adapters/trae/hooks/stop-check.sh +0 -54
  216. package/templates/adapters/trae/hooks/subagent-guard.sh +0 -15
  217. package/templates/adapters/trae/hooks/subagent-stop.sh +0 -47
  218. package/templates/adapters/vscode/hooks/doc-format-guard.sh +0 -282
  219. package/templates/adapters/vscode/hooks/lib/common.sh +0 -220
  220. package/templates/adapters/vscode/hooks/lib/context-inject.sh +0 -96
  221. package/templates/adapters/vscode/hooks/lib/notify.sh +0 -36
  222. package/templates/adapters/vscode/hooks/lib/preload-templates.sh +0 -212
  223. package/templates/adapters/vscode/hooks/lib/session-end.sh +0 -76
  224. package/templates/adapters/vscode/hooks/lib/state-detect.sh +0 -13
  225. package/templates/adapters/vscode/hooks/lib/subagent-stop.sh +0 -90
  226. package/templates/adapters/vscode/hooks/lib/vocabulary.sh +0 -62
  227. package/templates/adapters/vscode/hooks/notification.sh +0 -43
  228. package/templates/adapters/vscode/hooks/permission-gate.sh +0 -18
  229. package/templates/adapters/vscode/hooks/post-tool-failure.sh +0 -28
  230. package/templates/adapters/vscode/hooks/post-tool-use.sh +0 -35
  231. package/templates/adapters/vscode/hooks/pre-compact.sh +0 -37
  232. package/templates/adapters/vscode/hooks/pre-tool-use.sh +0 -153
  233. package/templates/adapters/vscode/hooks/prompt-submit.sh +0 -99
  234. package/templates/adapters/vscode/hooks/review-checklist.sh +0 -10
  235. package/templates/adapters/vscode/hooks/session-end.sh +0 -13
  236. package/templates/adapters/vscode/hooks/session-start.sh +0 -35
  237. package/templates/adapters/vscode/hooks/stop-check.sh +0 -62
  238. package/templates/adapters/vscode/hooks/subagent-guard.sh +0 -37
  239. package/templates/adapters/vscode/hooks/subagent-stop.sh +0 -12
  240. package/templates/core/hooks/doc-format-guard.sh +0 -343
  241. package/templates/core/hooks/lib/common.sh +0 -220
  242. package/templates/core/hooks/lib/context-inject.sh +0 -96
  243. package/templates/core/hooks/lib/notify.sh +0 -36
  244. package/templates/core/hooks/lib/preload-templates.sh +0 -212
  245. package/templates/core/hooks/lib/session-end.sh +0 -76
  246. package/templates/core/hooks/lib/state-detect.sh +0 -10
  247. package/templates/core/hooks/lib/subagent-stop.sh +0 -90
  248. package/templates/core/hooks/lib/vocabulary.sh +0 -62
  249. package/templates/core/hooks/notification.sh +0 -45
  250. package/templates/core/hooks/permission-gate.sh +0 -18
  251. package/templates/core/hooks/post-tool-failure.sh +0 -10
  252. package/templates/core/hooks/post-tool-use.sh +0 -138
  253. package/templates/core/hooks/pre-compact.sh +0 -37
  254. package/templates/core/hooks/pre-tool-use.sh +0 -223
  255. package/templates/core/hooks/prompt-submit.sh +0 -87
  256. package/templates/core/hooks/review-checklist.sh +0 -10
  257. package/templates/core/hooks/session-end.sh +0 -38
  258. package/templates/core/hooks/session-start.sh +0 -53
  259. package/templates/core/hooks/stop-check.sh +0 -54
  260. package/templates/core/hooks/subagent-guard.sh +0 -15
  261. package/templates/core/hooks/subagent-stop.sh +0 -47
  262. package/templates/shared/hooks-lib/common.sh +0 -22
@@ -0,0 +1,82 @@
1
+ // prompt-submit.ts — UserPromptSubmit 入口(Claude 版,Task 3.1 继承体系)
2
+ // 继承 core PromptRouter(模板方法固化 L1/L2/L3 分流);仅 override 协议差异(bash 原文逐字对照):
3
+ // ① extractPrompt: grep 正则提取(无 jq)
4
+ // ② acceptedText: 验收 4 行(无 ★ 同步检查行)
5
+ // ③ onAccepted: 无 review-checklist 子进程(core 有)
6
+ // ④ onDevKwMatched: stderr "[ADD PromptSubmit] 检测到开发关键词"
7
+ // ⑤ layer2ToStderr: false(Layer2 提示输出 stdout)
8
+ // ⑥ layer3Text: 「[ADD 当前状态]」多行块
9
+ // ⑦ dailyWarnText: 「建议创建 Plan」(无"或检查 hooks 误报")
10
+ // ⑧ afterLayer3: 模板全文注入(--full --top 5,tpl-injected 去重)
11
+ // ⑨ shouldSkipDaily: MAGIC_DIR 未设置时跳过日报
12
+
13
+ import { readHookInput } from "../../../core/governance/common.js"
14
+ import { PreloadTemplates } from "../../../core/governance/preload-templates.js"
15
+ import { PromptRouter } from "../../../core/governance/prompt-router.js"
16
+ import { resolveClaudeProjectDir } from "./lib/claude-env.js"
17
+
18
+ process.env.PROJECT_DIR = resolveClaudeProjectDir()
19
+
20
+ class ClaudePromptRouter extends PromptRouter {
21
+ /** ① prompt 提取(对齐 bash grep/sed,无 jq) */
22
+ protected override extractPrompt(input: string): string {
23
+ return input.match(/"prompt"\s*:\s*"([^"]*)"/)?.[1] ?? ""
24
+ }
25
+
26
+ /** ② 验收幂等文本(claude 4 行,无 ★ 同步检查) */
27
+ protected override acceptedText(): string {
28
+ return `[ADD 验收] ⚠️ 已验收。进入 Review 模式:
29
+ ① 重新检查 checklist [T]/[R] 项
30
+ ② 审查 audit 记录完整性
31
+ ③ 如有差异 → Review 回流至 handoff(增量更新)
32
+ ④ 无差异 → 记录 'Review 已确认,无新发现'
33
+ `
34
+ }
35
+
36
+ /** ③ 验收后置(claude 无 review-checklist 子进程) */
37
+ protected override onAccepted(_handoff: string, _addRoute: string): void {
38
+ // claude 协议: 无子进程调用
39
+ }
40
+
41
+ /** ④ 开发关键词命中提示(stderr) */
42
+ protected override onDevKwMatched(): void {
43
+ process.stderr.write("[ADD PromptSubmit] 检测到开发关键词\n")
44
+ }
45
+
46
+ /** ⑤ Layer 2 输出通道(claude: stdout) */
47
+ protected override layer2ToStderr(): boolean {
48
+ return false
49
+ }
50
+
51
+ /** ⑥ Layer 3 状态文本(多行块) */
52
+ protected override layer3Text(plan: string, rounds: string, step: string, handoff: string): string {
53
+ return `[ADD 当前状态]
54
+ Plan: ${plan}
55
+ 轮次: ${rounds}
56
+ 当前 Step: ${step}
57
+ handoff: ${handoff}
58
+ `
59
+ }
60
+
61
+ /** ⑦ 日报告警文本(无"或检查 hooks 误报") */
62
+ protected override dailyWarnText(noPlan: number, threshold: number): string {
63
+ return `[Hook ⚠️] 无 Plan 提示已达 ${noPlan} 次(≥${threshold}),建议创建 Plan\n`
64
+ }
65
+
66
+ /** ⑧ Layer 3 附加注入: 模板全文(--full --top 5) */
67
+ protected override afterLayer3(): void {
68
+ try {
69
+ process.stdout.write(new PreloadTemplates().full(5))
70
+ } catch {
71
+ /* 模板目录缺失 fail-fast:不阻断 */
72
+ }
73
+ }
74
+
75
+ /** ⑨ MAGIC_DIR 未设置时跳过日报(TS 修复: bash 空路径相对检查) */
76
+ protected override shouldSkipDaily(): boolean {
77
+ return !process.env.MAGIC_DIR
78
+ }
79
+ }
80
+
81
+ const input = readHookInput()
82
+ process.exit(new ClaudePromptRouter(process.env.MAGIC_DIR || "").run(input))
@@ -0,0 +1,4 @@
1
+ // review-checklist.ts — Review 检查清单校验(Claude 版,与 core 同构)
2
+
3
+ process.stdout.write("[ADD ReviewChecklist] 请在提交前确认 checklist.md 全部项已勾选。\n")
4
+ process.exit(0)
@@ -0,0 +1,10 @@
1
+ // session-end.ts — SessionEnd Claude 版(薄包装:source lib 并自动执行 main)
2
+ // 治理卡位 #2: 标记清理 + 会话审计结算 + Stop未触发兜底
3
+ // 薄包装语义: bash 版 source lib/session-end.sh 后 lib 的 main 自动执行(BASH_SOURCE 判定)。
4
+ // TS 版等价: 直接调用 SessionEndGuard.run()(lib 版行为)。
5
+
6
+ import { SessionEndGuard } from "../../../core/governance/session-end.js"
7
+ import { resolveClaudeProjectDir } from "./lib/claude-env.js"
8
+
9
+ process.env.PROJECT_DIR = resolveClaudeProjectDir()
10
+ process.exit(new SessionEndGuard(process.env.PROJECT_DIR).run())
@@ -0,0 +1,24 @@
1
+ // session-start.ts — SessionStart 入口(Claude 版,Task 3.1 继承体系)
2
+ // 继承 core SessionStartGuard;仅 override 协议差异(bash 原文对照):
3
+ // ① PROJECT_DIR 来源 CLAUDE_PROJECT_DIR(claude-env 注入链)
4
+ // ② 无 ③ 代办段、无 ④ HITL 待审批段(claude bash 版无此两段)
5
+
6
+ import { SessionStartGuard } from "../../../core/governance/session-start-guard.js"
7
+ import { resolveClaudeProjectDir } from "./lib/claude-env.js"
8
+
9
+ process.env.PROJECT_DIR = resolveClaudeProjectDir()
10
+ const PROJECT_DIR = process.env.PROJECT_DIR
11
+
12
+ class ClaudeSessionStartGuard extends SessionStartGuard {
13
+ /** ② claude 版无代办段 */
14
+ protected override emitTodoReminder(_state: string | null): void {
15
+ // claude 协议: 无代办刷新段
16
+ }
17
+
18
+ /** ② claude 版无 HITL 待审批段 */
19
+ protected override emitHitlPending(): void {
20
+ // claude 协议: 无 HITL 检测段
21
+ }
22
+ }
23
+
24
+ process.exit(new ClaudeSessionStartGuard(PROJECT_DIR).run())
@@ -0,0 +1,15 @@
1
+ // stop-check.ts — Stop 入口薄壳(Claude 版,Task 3.1 收敛)
2
+ // 治理逻辑: ../../../core/governance/stop-router.js(StopRouter Q0-Q4 分流)
3
+ // 协议差异: PROJECT_DIR 来源 CLAUDE_PROJECT_DIR(claude-env 注入链)
4
+
5
+ import { tryResolveMagicDir } from "../../../core/governance/common.js"
6
+ import { StopRouter } from "../../../core/governance/stop-router.js"
7
+ import { resolveClaudeProjectDir } from "./lib/claude-env.js"
8
+
9
+ process.env.PROJECT_DIR = resolveClaudeProjectDir()
10
+ const inferredMagicDir = tryResolveMagicDir()
11
+ if (inferredMagicDir && !process.env.MAGIC_DIR) {
12
+ process.env.MAGIC_DIR = inferredMagicDir
13
+ }
14
+
15
+ process.exit(new StopRouter().run())
@@ -0,0 +1,34 @@
1
+ // stop-failure.ts — Claude Code StopFailure 紧急审计转储(Claude 独有事件)
2
+ // 治理卡位 #8: 紧急审计转储 + 异常标记
3
+
4
+ import { writeFileSync } from "node:fs"
5
+ import { detectActiveAdd, localIsoSeconds, projectHash } from "../../../core/governance/common.js"
6
+ import { resolveClaudeProjectDir } from "./lib/claude-env.js"
7
+
8
+ const PROJECT_DIR = resolveClaudeProjectDir()
9
+ // 对齐 bash export PROJECT_DIR(后续 hash/路径均消费该值)
10
+ process.env.PROJECT_DIR = PROJECT_DIR
11
+
12
+ process.stderr.write(`[ADD StopFailure] ⚠️ Agent 异常退出 — ${localIsoSeconds()}\n`)
13
+
14
+ // 尝试 dump 当前 ADD 状态
15
+ const state = detectActiveAdd()
16
+ if (state !== null) {
17
+ const [plan, step, rounds, handoff, addRoute] = state.split("::")
18
+ process.stderr.write(`[ADD StopFailure] 异常退出时 ADD 状态:
19
+ Plan: ${plan}
20
+ Step: ${step}
21
+ 轮次: ${rounds}
22
+ handoff: ${handoff}
23
+ add-route: ${addRoute}
24
+ `)
25
+ }
26
+
27
+ // 标记异常终止(供 SessionEnd 兜底识别)
28
+ try {
29
+ writeFileSync(`/tmp/add_failure_${projectHash() || ""}`, "")
30
+ } catch {
31
+ /* ignore(对齐 touch || true) */
32
+ }
33
+
34
+ process.exit(0)
@@ -0,0 +1,25 @@
1
+ // subagent-guard.ts — SubagentStart 入口(Claude 版,Task 3.1 继承体系)
2
+ // 继承 core SubagentGuardRouter(状态注入模板方法——能力对齐);仅 override 协议差异:
3
+ // ① 输出形态: 纯文本状态块(core/qoder 是 hookSpecificOutput JSON)
4
+ // ② PROJECT_DIR 来源 CLAUDE_PROJECT_DIR(claude-env 注入链)
5
+
6
+ import { SubagentGuardRouter } from "../../../core/governance/subagent-guard-router.js"
7
+ import { resolveClaudeProjectDir } from "./lib/claude-env.js"
8
+
9
+ process.env.PROJECT_DIR = resolveClaudeProjectDir()
10
+
11
+ class ClaudeSubagentGuardRouter extends SubagentGuardRouter {
12
+ /** ① 纯文本状态块(claude 协议) */
13
+ protected override emit(state: string): void {
14
+ const [plan, step, rounds, handoff] = state.split("::")
15
+ process.stdout.write(`[ADD SubagentStart] 子代理上下文注入:
16
+ Plan: ${plan}
17
+ Step: ${step}
18
+ 轮次: ${rounds}
19
+ handoff: ${handoff}
20
+ 遵循 ADD 规范,完成后请检查 checklist 对应项
21
+ `)
22
+ }
23
+ }
24
+
25
+ process.exit(new ClaudeSubagentGuardRouter().run())
@@ -0,0 +1,12 @@
1
+ // subagent-stop.ts — SubagentStop 入口薄壳(Claude 版,Task 3.1 继承体系)
2
+ // 治理逻辑: core SubagentStopRouter(边界校验 + 审计聚合,与 core 完全同构)
3
+ // 协议差异: PROJECT_DIR 来源 CLAUDE_PROJECT_DIR(依赖注入,无 override 需求)
4
+
5
+ import { readHookInput } from "../../../core/governance/common.js"
6
+ import { SubagentStopRouter } from "../../../core/governance/subagent-stop-router.js"
7
+ import { resolveClaudeProjectDir } from "./lib/claude-env.js"
8
+
9
+ process.env.PROJECT_DIR = resolveClaudeProjectDir()
10
+
11
+ const input = readHookInput()
12
+ process.exit(new SubagentStopRouter().run(input))
@@ -5,7 +5,7 @@
5
5
  "hooks": [
6
6
  {
7
7
  "type": "command",
8
- "command": ".claude/hooks/session-start.sh"
8
+ "command": "node .claude/hooks/session-start.mjs"
9
9
  }
10
10
  ]
11
11
  }
@@ -15,7 +15,7 @@
15
15
  "hooks": [
16
16
  {
17
17
  "type": "command",
18
- "command": ".claude/hooks/prompt-submit.sh"
18
+ "command": "node .claude/hooks/prompt-submit.mjs"
19
19
  }
20
20
  ]
21
21
  }
@@ -26,11 +26,11 @@
26
26
  "hooks": [
27
27
  {
28
28
  "type": "command",
29
- "command": ".claude/hooks/pre-tool-use.sh"
29
+ "command": "node .claude/hooks/pre-tool-use.mjs"
30
30
  },
31
31
  {
32
32
  "type": "command",
33
- "command": ".claude/hooks/doc-format-guard.sh"
33
+ "command": "node .claude/hooks/doc-format-guard.mjs"
34
34
  }
35
35
  ]
36
36
  }
@@ -41,11 +41,11 @@
41
41
  "hooks": [
42
42
  {
43
43
  "type": "command",
44
- "command": ".claude/hooks/post-tool-use.sh"
44
+ "command": "node .claude/hooks/post-tool-use.mjs"
45
45
  },
46
46
  {
47
47
  "type": "command",
48
- "command": ".claude/hooks/doc-format-guard.sh"
48
+ "command": "node .claude/hooks/doc-format-guard.mjs"
49
49
  }
50
50
  ]
51
51
  }
@@ -55,7 +55,7 @@
55
55
  "hooks": [
56
56
  {
57
57
  "type": "command",
58
- "command": ".claude/hooks/post-tool-failure.sh"
58
+ "command": "node .claude/hooks/post-tool-failure.mjs"
59
59
  }
60
60
  ]
61
61
  }
@@ -65,7 +65,7 @@
65
65
  "hooks": [
66
66
  {
67
67
  "type": "command",
68
- "command": ".claude/hooks/stop-check.sh"
68
+ "command": "node .claude/hooks/stop-check.mjs"
69
69
  }
70
70
  ]
71
71
  }
@@ -75,7 +75,7 @@
75
75
  "hooks": [
76
76
  {
77
77
  "type": "command",
78
- "command": ".claude/hooks/subagent-guard.sh"
78
+ "command": "node .claude/hooks/subagent-guard.mjs"
79
79
  }
80
80
  ]
81
81
  }
@@ -85,7 +85,7 @@
85
85
  "hooks": [
86
86
  {
87
87
  "type": "command",
88
- "command": ".claude/hooks/subagent-guard.sh"
88
+ "command": "node .claude/hooks/subagent-guard.mjs"
89
89
  }
90
90
  ]
91
91
  }
@@ -95,7 +95,7 @@
95
95
  "hooks": [
96
96
  {
97
97
  "type": "command",
98
- "command": ".claude/hooks/pre-compact.sh"
98
+ "command": "node .claude/hooks/pre-compact.mjs"
99
99
  }
100
100
  ]
101
101
  }
@@ -106,7 +106,7 @@
106
106
  "hooks": [
107
107
  {
108
108
  "type": "command",
109
- "command": ".claude/hooks/notification.sh"
109
+ "command": "node .claude/hooks/notification.mjs"
110
110
  }
111
111
  ]
112
112
  }
@@ -116,7 +116,7 @@
116
116
  "hooks": [
117
117
  {
118
118
  "type": "command",
119
- "command": ".claude/hooks/permission-gate.sh"
119
+ "command": "node .claude/hooks/permission-gate.mjs"
120
120
  }
121
121
  ]
122
122
  }
@@ -0,0 +1,30 @@
1
+ // doc-format-guard.ts — schema.json 驱动的 ADD 文档格式守卫(Codex 版,Task 7.1 继承体系)
2
+ // 继承 core DocFormatGuard,命名子类 CodexDocFormatGuard:
3
+ // ① 探测链与 core 数据驱动同语义(PLAN 元信息 → 一、Plan 概述 → 四、Handoff → simple-plan,
4
+ // 2026-08-14 实态核验 codex 独立类 L133-135 与 core 基线一致)→ 当前无 override
5
+ // (空子类承载端身份 + 未来演进位)
6
+ // 协议差异仅剩: MAGIC_DIR 兜底 = ".codex"(构造参数)
7
+
8
+ import { readFileSync } from "node:fs"
9
+ import { DocFormatGuard } from "../../../core/governance/doc-format-guard.js"
10
+
11
+ class CodexDocFormatGuard extends DocFormatGuard {
12
+ /** 协议差异封装: MAGIC_DIR 兜底 = ".codex"(codex 端) */
13
+ constructor(projectDir: string, magicDir: string) {
14
+ super(projectDir, magicDir)
15
+ }
16
+
17
+ // 当前无 override(探测链/算法段同 core);未来 codex 协议演进在此扩展,不改 core
18
+ }
19
+
20
+ const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd()
21
+ const MAGIC_DIR = process.env.MAGIC_DIR || ".codex"
22
+
23
+ const guard = new CodexDocFormatGuard(PROJECT_DIR, MAGIC_DIR)
24
+ let raw = ""
25
+ try {
26
+ raw = readFileSync(0, "utf-8")
27
+ } catch {
28
+ raw = ""
29
+ }
30
+ process.exitCode = guard.run(raw)
@@ -0,0 +1,26 @@
1
+ // notification.ts — Notification 入口(Codex 版,Task 7.1 继承体系)
2
+ // 继承 core NotificationRouter,命名子类 CodexNotificationRouter:
3
+ // ① resolveMagicDir override: 无探测循环(codex bash 原文硬编码 .codex;
4
+ // TS 用唯一解析链:注入优先 → 物理推导 → .codex 兜底,2026-08-14 轮次 7 治理契约落实)
5
+ // ② 提醒文本 = 基类默认(含 Plan 前缀,与 codex bash 原文一致)
6
+ // 协议差异仅剩: PROJECT_DIR = $PWD(codex 小文件无 git toplevel,bash 原文逐字)
7
+
8
+ import { readHookInput } from "../../../core/governance/common.js"
9
+ import { NotificationRouter } from "../../../core/governance/notification-router.js"
10
+
11
+ class CodexNotificationRouter extends NotificationRouter {
12
+ /** ① codex 无跨端探测循环(bash 原文硬编码 .codex;唯一解析链 + 兜底) */
13
+ protected override resolveMagicDir(): string {
14
+ return process.env.MAGIC_DIR ?? ".codex"
15
+ }
16
+
17
+ /** 协议差异封装: fallbackMagicDir = ".codex" */
18
+ constructor(projectDir: string) {
19
+ super(projectDir, ".codex")
20
+ }
21
+ }
22
+
23
+ const input = readHookInput()
24
+ const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd()
25
+
26
+ process.exit(new CodexNotificationRouter(PROJECT_DIR).run(input))
@@ -0,0 +1,15 @@
1
+ // permission-gate.ts — PermissionRequest 入口(Codex 版,Task 7.1 继承体系)
2
+ // 继承 core PermissionGateRouter,命名子类 CodexPermissionGateRouter:
3
+ // ① tool_name 缺失回退 ""(bash `// empty` 语义)——基类默认,无 override
4
+ // ② emit = 基类默认(仅高风险工具 Bash/Write/Edit stdout 二次确认,bash 原文逐字)
5
+ // 协议差异: 无(与 core 同构);命名子类承载端身份 + 未来演进位
6
+
7
+ import { readHookInput } from "../../../core/governance/common.js"
8
+ import { PermissionGateRouter } from "../../../core/governance/permission-gate-router.js"
9
+
10
+ class CodexPermissionGateRouter extends PermissionGateRouter {
11
+ // 当前无 override(`// empty` 回退 + 高风险工具提示与 core 基线一致);命名子类承载端身份 + 未来演进位
12
+ }
13
+
14
+ const input = readHookInput()
15
+ process.exit(new CodexPermissionGateRouter().run(input))
@@ -0,0 +1,14 @@
1
+ // post-tool-failure.ts — PostToolUseFailure 入口(Codex 版,Task 7.1 继承体系)
2
+ // 继承 core PostToolFailureRouter,命名子类 CodexPostToolFailureRouter:
3
+ // ① emit = 基类默认(stdout 固定文本——bash 原文逐字,codex 为占位实现)
4
+ // 协议差异: 无(与 core 同构);命名子类承载端身份 + 未来演进位
5
+
6
+ import { readHookInput } from "../../../core/governance/common.js"
7
+ import { PostToolFailureRouter } from "../../../core/governance/post-tool-failure-router.js"
8
+
9
+ class CodexPostToolFailureRouter extends PostToolFailureRouter {
10
+ // 当前无 override(stdout 固定提示与 core 基线一致);命名子类承载端身份 + 未来演进位
11
+ }
12
+
13
+ const input = readHookInput()
14
+ process.exit(new CodexPostToolFailureRouter().run(input))
@@ -0,0 +1,85 @@
1
+ // post-tool-use.ts — PostToolUse 入口(Codex 版,Task 7.1 继承体系)
2
+ // 继承 core PostToolRouter,命名子类 CodexPostToolRouter(codex 私有协议差异 override):
3
+ // ① isFileTool: +SearchReplace(codex 支持 file_path/path 双字段)
4
+ // ② extractFilePath: file_path ?? path(codex 工具输入形态)
5
+ // ③ emitAuditReminder: 简化文本(无 ADD-7 字样,bash 原文逐字)
6
+ // ④ emitBashDone: lint/typecheck/test 闭环提示(bash 原文逐字)
7
+ // ⑤ applyPatchMatcher: 解析 `*** Add|Update|Delete File:` 行逐文件 report(codex apply_patch 特有)
8
+ // ⑥ docGuardText: 「请检查双向链接与 Plan/Spec 状态」(bash 原文逐字)
9
+ // 入口差异: PROJECT_DIR=git toplevel(codex 大文件协议)+ MAGIC_DIR 注入
10
+
11
+ import { execSync } from "node:child_process"
12
+ import { jsonGet, readHookInput, tryResolveMagicDir } from "../../../core/governance/common.js"
13
+ import { PostToolRouter } from "../../../core/governance/post-tool-router.js"
14
+
15
+ class CodexPostToolRouter extends PostToolRouter {
16
+ /** ① codex 文件写工具含 SearchReplace */
17
+ protected override isFileTool(toolName: string): boolean {
18
+ return toolName === "Edit" || toolName === "Write" || toolName === "SearchReplace"
19
+ }
20
+
21
+ /** ② file_path ?? path(codex 工具输入形态) */
22
+ protected override extractFilePath(input: string): string {
23
+ const fp = jsonGet(input, "file_path")
24
+ if (fp !== "") return fp
25
+ return jsonGet(input, "path")
26
+ }
27
+
28
+ /** ③ 审计提醒(codex bash 原文逐字——无 ADD-7 字样) */
29
+ protected override emitAuditReminder(filePath: string): string {
30
+ return `[ADD PostToolUse] 文件已写入: ${filePath};请执行 record_dev_operation 落库审计。\n`
31
+ }
32
+
33
+ /** ④ Bash 增强(codex bash 原文逐字——lint/typecheck/test 闭环) */
34
+ protected override emitBashDone(): string {
35
+ return "[ADD PostToolUse] Bash 命令完成;若产生 lint/typecheck/test 错误,请在本轮闭环。\n"
36
+ }
37
+
38
+ /** ⑤ apply_patch: 解析 `*** Add|Update|Delete File:` 行逐文件 report(codex 特有) */
39
+ protected override applyPatchMatcher(input: string): void {
40
+ let patchCommand = ""
41
+ if (input.trim() !== "") {
42
+ try {
43
+ const parsed = JSON.parse(input) as { tool_input?: { command?: unknown } }
44
+ patchCommand = typeof parsed.tool_input?.command === "string" ? parsed.tool_input.command : ""
45
+ } catch {
46
+ patchCommand = ""
47
+ }
48
+ }
49
+ const patchPaths: string[] = []
50
+ for (const line of patchCommand.split("\n")) {
51
+ const m = line.match(/^\*\*\* (Add|Update|Delete) File: (.+)$/)
52
+ if (m) patchPaths.push(m[2])
53
+ }
54
+ for (const p of patchPaths) this.reportFile(p)
55
+ }
56
+
57
+ /** 对齐 bash _report_file:ADD 文档守卫 + 审计提醒(codex 版) */
58
+ private reportFile(filePath: string): void {
59
+ if (filePath === "") return
60
+ if (/(^|\/)\.codex\/(plans|specs|reviews)\//.test(filePath)) {
61
+ this.emitLine(`[ADD PostToolUse] ADD 文档已写入: ${filePath};请检查双向链接与 Plan/Spec 状态。\n`)
62
+ }
63
+ this.emitLine(`[ADD PostToolUse] 文件已写入: ${filePath};请执行 record_dev_operation 落库审计。\n`)
64
+ // Task 8.1: apply_patch 也是文件写入路径——接入审计桥接(ADD-7 自动化)
65
+ this.emitWriteEvent(filePath)
66
+ }
67
+ }
68
+
69
+ // 对齐 bash: git rev-parse --show-toplevel || pwd
70
+ function resolveProjectDir(): string {
71
+ try {
72
+ return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim()
73
+ } catch {
74
+ return process.cwd()
75
+ }
76
+ }
77
+ const PROJECT_DIR = resolveProjectDir()
78
+ process.env.PROJECT_DIR = PROJECT_DIR
79
+ process.env.MAGIC_DIR = tryResolveMagicDir() || ".codex"
80
+
81
+ const input = readHookInput()
82
+ const toolName = jsonGet(input, "tool_name")
83
+ if (toolName === "") process.exit(0)
84
+
85
+ process.exit(new CodexPostToolRouter(PROJECT_DIR, process.env.MAGIC_DIR).run(input, toolName))
@@ -0,0 +1,20 @@
1
+ // pre-compact.ts — 上下文压缩前 ADD 状态保存(Codex 版,Task 7.1 继承体系)
2
+ // 继承 core PreCompactGuard,命名子类 CodexPreCompactGuard:
3
+ // ① 状态保存到恢复标记 + ② tpl 标记清理 = 基类(与 codex bash 原文逐字同构,
4
+ // 2026-08-14 实态核验——手写版逻辑与 core 基类完全一致,收敛为子类)
5
+ // 协议差异仅剩: PROJECT_DIR = $PWD(codex 小文件无 git toplevel,bash 原文逐字)
6
+ // + MAGIC_DIR 唯一解析链注入(.codex 兜底)
7
+
8
+ import { tryResolveMagicDir } from "../../../core/governance/common.js"
9
+ import { PreCompactGuard } from "../../../core/governance/pre-compact-guard.js"
10
+
11
+ class CodexPreCompactGuard extends PreCompactGuard {
12
+ // 当前无 override(状态保存/恢复清单/tpl 清理与 core 基线一致);命名子类承载端身份 + 未来演进位
13
+ }
14
+
15
+ process.env.PROJECT_DIR = process.env.PROJECT_DIR || process.cwd()
16
+ // 对齐 bash export MAGIC_DIR=".codex":唯一解析链(注入优先 → 物理推导),推导失败回退 .codex
17
+ const inferred = tryResolveMagicDir()
18
+ if (inferred && !process.env.MAGIC_DIR) process.env.MAGIC_DIR = inferred
19
+
20
+ process.exit(new CodexPreCompactGuard().run())
@@ -0,0 +1,141 @@
1
+ // pre-tool-use.ts — PreToolUse 入口(Codex 版,Task 7.1 继承体系)
2
+ // 继承 core PreToolUseGuard,命名子类 CodexPreToolUseGuard(codex 私有协议差异 override):
3
+ // ① 检测链: adapterName="codex" → [guard.adapter_detectors] codex 链(同 core 正则 + codex reason)
4
+ // ② onBlock/onSensitiveDeny/onHitlDeny: JSON **deny** 形态(core 是 ask;codex bash 原文 _deny 逐字,
5
+ // stderr `⛔ [ADD PreToolUse] reason` + logBlock + 事件 + exit 2)
6
+ // ③ onOtherTool: apply_patch 特有(解析 `*** Add|Update|Delete File:` 路径 → guardFilePath 逐文件 + markDevAction)
7
+ // ④ ~~sensitiveFileRegex / hitlExemptReviews / hitlMarkers~~ → Task 9.4.4 三项上提 core 默认(2026-08-14)
8
+ // ⑤ noPlanHint: true——无 Plan 仅 stderr 提示后继续(codex 协议差异保留)
9
+ // 入口差异: PROJECT_DIR=git toplevel + cd 统一 cwd + MAGIC_DIR 注入
10
+
11
+ import { execSync } from "node:child_process"
12
+ import { existsSync } from "node:fs"
13
+ import { join } from "node:path"
14
+ import { jsonGet, markDevAction, readHookInput, tryResolveMagicDir } from "../../../core/governance/common.js"
15
+ import { writeHookEvent } from "../../../core/governance/notify.js"
16
+ import { PreToolUseGuard } from "../../../core/governance/pre-tool-guard.js"
17
+
18
+ class CodexPreToolUseGuard extends PreToolUseGuard {
19
+ constructor(projectDir: string, magicDir: string) {
20
+ super(projectDir, magicDir, "codex")
21
+ }
22
+
23
+ /** ② §A 阻断: JSON deny(core 是 ask——codex bash 原文 _deny 逐字) */
24
+ protected override onBlock(blocked: { reason: string; stderr: string }, command: string): number {
25
+ process.stderr.write(`⛔ [ADD PreToolUse] ${blocked.reason}\n`)
26
+ process.stdout.write(
27
+ JSON.stringify({
28
+ hookSpecificOutput: {
29
+ hookEventName: "PreToolUse",
30
+ permissionDecision: "deny",
31
+ permissionDecisionReason: blocked.reason,
32
+ },
33
+ }) + "\n"
34
+ )
35
+ this.logBlock("检测器链", command)
36
+ writeHookEvent("pre-tool-use", "deny", command, blocked.reason, this.planKeyword, this.planStatus)
37
+ return 2
38
+ }
39
+
40
+ /** ② 敏感文件: JSON deny + 事件 + exit 2(core 无事件) */
41
+ protected override onSensitiveDeny(filePath: string): number {
42
+ const reason = `敏感文件受保护,禁止写入: ${filePath}`
43
+ process.stderr.write(`⛔ [ADD PreToolUse] ${reason}\n`)
44
+ process.stdout.write(
45
+ JSON.stringify({
46
+ hookSpecificOutput: {
47
+ hookEventName: "PreToolUse",
48
+ permissionDecision: "deny",
49
+ permissionDecisionReason: reason,
50
+ },
51
+ }) + "\n"
52
+ )
53
+ this.logBlock("sensitive-file", filePath)
54
+ writeHookEvent("pre-tool-use", "deny", `Write ${filePath}`, reason, this.planKeyword, this.planStatus)
55
+ return 2
56
+ }
57
+
58
+ /** ② HITL: JSON deny + 事件 + exit 2(core 是 exit 0 放行——codex 阻断) */
59
+ protected override onHitlDeny(toolName: string, filePath: string, tongyiMarker: string): number {
60
+ const reason = `HITL 未同意: ${filePath}。请先 create_hitl,再由人工 update_hitl(TONGYI)。`
61
+ process.stderr.write(`⛔ [ADD PreToolUse] ${reason}\n`)
62
+ process.stdout.write(
63
+ JSON.stringify({
64
+ hookSpecificOutput: {
65
+ hookEventName: "PreToolUse",
66
+ permissionDecision: "deny",
67
+ permissionDecisionReason: reason,
68
+ },
69
+ }) + "\n"
70
+ )
71
+ this.logBlock("hitl", filePath)
72
+ writeHookEvent("pre-tool-use", "deny", `${toolName} ${filePath}`, `HITL 未同意: ${tongyiMarker}`, this.planKeyword, this.planStatus)
73
+ return 2
74
+ }
75
+
76
+ /** ③ apply_patch: 解析 patch 路径 → guardFilePath 逐文件 + markDevAction(codex 特有) */
77
+ protected override onOtherTool(input: string, toolName: string): number {
78
+ if (toolName !== "apply_patch") return 0
79
+ let patchCommand = ""
80
+ if (input.trim() !== "") {
81
+ try {
82
+ const parsed = JSON.parse(input) as { tool_input?: { command?: unknown } }
83
+ patchCommand = typeof parsed.tool_input?.command === "string" ? parsed.tool_input.command : ""
84
+ } catch {
85
+ patchCommand = ""
86
+ }
87
+ }
88
+ const patchPaths: string[] = []
89
+ for (const line of patchCommand.split("\n")) {
90
+ const m = line.match(/^\*\*\* (Add|Update|Delete) File: (.+)$/)
91
+ if (m) patchPaths.push(m[2])
92
+ }
93
+ for (const p of patchPaths) {
94
+ if (p !== "") {
95
+ // Task 9.4 修复: guardFilePath 返回阻断码(敏感/HITL),apply_patch 路径必须透传 exit
96
+ const guardCode = this.guardFilePath(p, "apply_patch")
97
+ if (guardCode !== 0) return guardCode
98
+ }
99
+ }
100
+ markDevAction()
101
+ return 0
102
+ }
103
+
104
+ /** ⑦ 无 Plan 仅提示后继续(codex bash 原文) */
105
+ protected override noPlanHint(): boolean {
106
+ return true
107
+ }
108
+ }
109
+ // 注: ④ sensitiveFileRegex / ⑤ hitlExemptReviews / ⑥ hitlMarkers override 已删除——
110
+ // 2026-08-14 Task 9.4.4 三项上提 core 默认(锚定正则 / 仅 -runtime 豁免 / 双哨兵),收敛无 diff。
111
+
112
+ // 对齐 bash: git rev-parse --show-toplevel || pwd + cd
113
+ function resolveProjectDir(): string {
114
+ try {
115
+ return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim()
116
+ } catch {
117
+ return process.cwd()
118
+ }
119
+ }
120
+ const PROJECT_DIR = resolveProjectDir()
121
+ process.env.PROJECT_DIR = PROJECT_DIR
122
+ // 对齐 bash export MAGIC_DIR=".codex":唯一解析链(注入优先 → 物理推导),推导失败回退 .codex
123
+ const MAGIC_DIR = tryResolveMagicDir() || ".codex"
124
+ process.env.MAGIC_DIR = MAGIC_DIR
125
+ process.chdir(PROJECT_DIR) // 统一 cwd,避免从 repo 子目录启动时写偏审计文件
126
+
127
+ const input = readHookInput()
128
+ const toolName = jsonGet(input, "tool_name")
129
+ // Task 9.4 修复: Bash 与 apply_patch 的 command 均在 tool_input——分流依据 tool_name
130
+ // (原 jsonGet 顶层 command 判断会把 apply_patch 的 patch body 误当 Bash 命令走 §A)
131
+
132
+ const guard = new CodexPreToolUseGuard(PROJECT_DIR, MAGIC_DIR)
133
+
134
+ if (toolName === "Bash") {
135
+ process.exit(guard.runSectionA(input))
136
+ } else if (toolName === "apply_patch") {
137
+ // apply_patch 的 payload 在 tool_input.command——只走 §C matcher(避免把 patch body 误判成 shell)
138
+ process.exit(guard.runSectionC(input, toolName))
139
+ } else {
140
+ process.exit(guard.runSectionB(input, toolName))
141
+ }