add-coder 0.3.25 → 0.3.27

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 (132) hide show
  1. package/README.md +36 -10
  2. package/dist/index.js +481 -209
  3. package/package.json +14 -4
  4. package/templates/.add-coder-src-hash.json +129 -106
  5. package/templates/adapters/claude/hooks/doc-format-guard.sh +114 -33
  6. package/templates/adapters/claude/hooks/lib/common.sh +46 -87
  7. package/templates/adapters/claude/hooks/lib/context-inject.sh +2 -2
  8. package/templates/adapters/claude/hooks/lib/notify.sh +6 -4
  9. package/templates/adapters/claude/hooks/lib/preload-templates.sh +27 -2
  10. package/templates/adapters/claude/hooks/lib/state-detect.sh +8 -112
  11. package/templates/adapters/claude/hooks/lib/vocabulary.sh +13 -7
  12. package/templates/adapters/claude/hooks/prompt-submit.sh +1 -1
  13. package/templates/adapters/claude/hooks/stop-check.sh +9 -0
  14. package/templates/adapters/codex/config.toml.example +12 -0
  15. package/templates/adapters/codex/hooks/doc-format-guard.sh +123 -41
  16. package/templates/adapters/codex/hooks/lib/common.sh +47 -99
  17. package/templates/adapters/codex/hooks/lib/context-inject.sh +4 -4
  18. package/templates/adapters/codex/hooks/lib/notify.sh +6 -4
  19. package/templates/adapters/codex/hooks/lib/preload-templates.sh +27 -6
  20. package/templates/adapters/codex/hooks/lib/state-detect.sh +5 -114
  21. package/templates/adapters/codex/hooks/lib/vocabulary.sh +3 -8
  22. package/templates/adapters/codex/hooks/notification.sh +3 -18
  23. package/templates/adapters/codex/hooks/post-tool-use.sh +24 -119
  24. package/templates/adapters/codex/hooks/pre-tool-use.sh +97 -184
  25. package/templates/adapters/codex/hooks/prompt-submit.sh +8 -3
  26. package/templates/adapters/codex/hooks/session-start.sh +19 -9
  27. package/templates/adapters/codex/hooks/stop-check.sh +44 -18
  28. package/templates/adapters/codex/hooks.json +67 -1
  29. package/templates/adapters/qoder/hooks/doc-format-guard.sh +121 -40
  30. package/templates/adapters/qoder/hooks/lib/common.sh +49 -87
  31. package/templates/adapters/qoder/hooks/lib/context-inject.sh +2 -2
  32. package/templates/adapters/qoder/hooks/lib/notify.sh +6 -4
  33. package/templates/adapters/qoder/hooks/lib/preload-templates.sh +27 -2
  34. package/templates/adapters/qoder/hooks/lib/state-detect.sh +8 -112
  35. package/templates/adapters/qoder/hooks/lib/vocabulary.sh +13 -7
  36. package/templates/adapters/qoder/hooks/prompt-submit.sh +1 -1
  37. package/templates/adapters/qoder/hooks/session-start.sh +1 -1
  38. package/templates/adapters/qoder/hooks/stop-check.sh +11 -0
  39. package/templates/adapters/trae/hooks/doc-format-guard.sh +122 -40
  40. package/templates/adapters/trae/hooks/lib/common.sh +46 -87
  41. package/templates/adapters/trae/hooks/lib/context-inject.sh +2 -2
  42. package/templates/adapters/trae/hooks/lib/notify.sh +5 -3
  43. package/templates/adapters/trae/hooks/lib/preload-templates.sh +27 -2
  44. package/templates/adapters/trae/hooks/lib/state-detect.sh +8 -115
  45. package/templates/adapters/trae/hooks/lib/vocabulary.sh +13 -7
  46. package/templates/adapters/trae/hooks/pre-tool-use.sh +14 -8
  47. package/templates/adapters/trae/hooks/stop-check.sh +9 -0
  48. package/templates/adapters/vscode/hooks/doc-format-guard.sh +114 -33
  49. package/templates/adapters/vscode/hooks/lib/common.sh +46 -87
  50. package/templates/adapters/vscode/hooks/lib/context-inject.sh +2 -2
  51. package/templates/adapters/vscode/hooks/lib/notify.sh +6 -4
  52. package/templates/adapters/vscode/hooks/lib/preload-templates.sh +27 -2
  53. package/templates/adapters/vscode/hooks/lib/state-detect.sh +8 -112
  54. package/templates/adapters/vscode/hooks/lib/vocabulary.sh +13 -7
  55. package/templates/adapters/vscode/hooks/notification.sh +3 -5
  56. package/templates/adapters/vscode/hooks/prompt-submit.sh +1 -1
  57. package/templates/adapters/vscode/hooks/stop-check.sh +9 -0
  58. package/templates/core/docs/ADD-governance-claude-code.md +1 -1
  59. package/templates/core/docs/ADD-governance-codex.md +1 -1
  60. package/templates/core/docs/ADD-governance-qoder-cn.md +1 -1
  61. package/templates/core/docs/ADD-governance-trae.md +1 -1
  62. package/templates/core/docs/ADD-governance-vscode-copilot.md +1 -1
  63. package/templates/core/hooks/doc-format-guard.sh +122 -40
  64. package/templates/core/hooks/lib/common.sh +46 -87
  65. package/templates/core/hooks/lib/context-inject.sh +2 -2
  66. package/templates/core/hooks/lib/notify.sh +5 -3
  67. package/templates/core/hooks/lib/preload-templates.sh +27 -2
  68. package/templates/core/hooks/lib/state-detect.sh +8 -115
  69. package/templates/core/hooks/lib/vocabulary.sh +13 -7
  70. package/templates/core/hooks/pre-tool-use.sh +14 -8
  71. package/templates/core/hooks/stop-check.sh +9 -0
  72. package/templates/core/prisma/add.prisma +140 -82
  73. package/templates/core/rules/project_rules.md +27 -24
  74. package/templates/core/scripts/db-ensure.sh +8 -0
  75. package/templates/core/scripts/gen-plan-index.sh +4 -15
  76. package/templates/core/scripts/mcp-server/notifications/hitl.ts +9 -1
  77. package/templates/core/scripts/mcp-server/notifications/hook.ts +25 -9
  78. package/templates/core/scripts/mcp-server/resources/hitl-approval-widget.ts +34 -0
  79. package/templates/core/scripts/mcp-server/resources/hook-events-report.ts +14 -2
  80. package/templates/core/scripts/mcp-server/resources/index.ts +2 -0
  81. package/templates/core/scripts/mcp-server/sampling/review.ts +25 -10
  82. package/templates/core/scripts/mcp-server/shared/db-types.ts +33 -8
  83. package/templates/core/scripts/mcp-server/shared/dps-scoring.strategy.ts +1 -1
  84. package/templates/core/scripts/mcp-server/shared/env.ts +28 -8
  85. package/templates/core/scripts/mcp-server/shared/fs.ts +3 -0
  86. package/templates/core/scripts/mcp-server/shared/hitl-interaction.strategy.ts +2 -3
  87. package/templates/core/scripts/mcp-server/shared/hitl-lifecycle-mutation.ts +129 -0
  88. package/templates/core/scripts/mcp-server/shared/hitl-proposal-mutation.ts +98 -0
  89. package/templates/core/scripts/mcp-server/shared/hitl-ui.ts +3 -0
  90. package/templates/core/scripts/mcp-server/shared/plan-lifecycle-events.ts +62 -0
  91. package/templates/core/scripts/mcp-server/shared/plan-lifecycle-mutation.ts +78 -0
  92. package/templates/core/scripts/mcp-server/shared/plan-lifecycle-subscriber.ts +145 -0
  93. package/templates/core/scripts/mcp-server/shared/plan-lifecycle.ts +153 -0
  94. package/templates/core/scripts/mcp-server/shared/plan-round-events.ts +56 -0
  95. package/templates/core/scripts/mcp-server/shared/plan-round-mutation.ts +143 -0
  96. package/templates/core/scripts/mcp-server/shared/plan-round-store.ts +47 -0
  97. package/templates/core/scripts/mcp-server/shared/plan-round-subscriber.ts +99 -0
  98. package/templates/core/scripts/mcp-server/shared/plan-status-store.ts +37 -0
  99. package/templates/core/scripts/mcp-server/shared/plan-tracking-mutation.ts +71 -0
  100. package/templates/core/scripts/mcp-server/shared/prisma.ts +12 -6
  101. package/templates/core/scripts/mcp-server/shared/project-root-strategy.ts +1 -2
  102. package/templates/core/scripts/mcp-server/shared/quant-recheck.strategy.ts +16 -0
  103. package/templates/core/scripts/mcp-server/shared/redact.ts +28 -0
  104. package/templates/core/scripts/mcp-server/shared/response.ts +3 -2
  105. package/templates/core/scripts/mcp-server/shared/runtime-context.ts +77 -0
  106. package/templates/core/scripts/mcp-server/shared/schema-topology.ts +159 -0
  107. package/templates/core/scripts/mcp-server/tools/audit.ts +49 -7
  108. package/templates/core/scripts/mcp-server/tools/context.ts +117 -121
  109. package/templates/core/scripts/mcp-server/tools/contract.ts +34 -9
  110. package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_status.ts +50 -3
  111. package/templates/core/scripts/mcp-server/tools/gateway/check_doc_similarity.ts +218 -0
  112. package/templates/core/scripts/mcp-server/tools/gateway/check_dps.ts +22 -7
  113. package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +75 -4
  114. package/templates/core/scripts/mcp-server/tools/gateway/check_spec_sync.ts +19 -6
  115. package/templates/core/scripts/mcp-server/tools/gateway/index.ts +2 -0
  116. package/templates/core/scripts/mcp-server/tools/hitl.ts +160 -75
  117. package/templates/core/scripts/mcp-server/tools/hook-event-report.ts +9 -2
  118. package/templates/core/scripts/mcp-server/tools/index.ts +69 -3
  119. package/templates/core/scripts/mcp-server/tools/plan.ts +140 -15
  120. package/templates/core/scripts/mcp-server/tools/review.ts +44 -15
  121. package/templates/core/scripts/mcp-server.ts +60 -1
  122. package/templates/core/scripts/plan-status-bridge.ts +26 -0
  123. package/templates/core/skills/add-paradigm/SKILL.md +2 -2
  124. package/templates/core/templates/add-route-template.schema.json +24 -56
  125. package/templates/core/templates/collab-contract-template.md +1 -1
  126. package/templates/core/templates/handoff-multi-round-template.schema.json +48 -0
  127. package/templates/core/templates/handoff-single-round-template.schema.json +92 -0
  128. package/templates/core/templates/handoff-single-round.schema.json +1 -1
  129. package/templates/core/templates/hitl-approval-widget.html +222 -37
  130. package/templates/core/templates/spec-template.schema.json +9 -46
  131. package/templates/core/templates/standard-plan-template.schema.json +1 -1
  132. package/templates/core/templates/tasks-template.schema.json +13 -54
@@ -1,217 +1,130 @@
1
1
  #!/bin/bash
2
- # pre-tool-use.sh — PreToolUse §A§B(阻断模式,通用适配版)
3
- # §A: Bash 裸写保护 — 拦截所有绕过 IDE 追踪的文件写操作
4
- # §B: Write/Edit 写入前置守卫 — Plan/Spec/Review 需要活跃 ADD Plan + 敏感文件保护
5
- # 治理卡位 #4: 危险命令拦截 / 模板路径兜底 / 写入前置守卫 / 敏感文件保护
2
+ # pre-tool-use.sh — Codex PreToolUse:Bash 裸写保护 + apply_patch 文件守卫
6
3
  set -euo pipefail
7
4
 
8
5
  input=$(cat)
9
-
10
- # 探测 MAGIC_DIR 和 PROJECT_DIR(兼容多种 adapter)
11
6
  HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
12
- PARENT="$(dirname "$HOOK_DIR")"
13
- MAGIC_DIR=".codex"
14
- PROJECT_DIR="$PWD"
15
-
16
- # ── Hook 通知: 拦截事件写入 jsonl(旁路,失败不阻断 exit 2)──
17
- source "${HOOK_DIR}/lib/notify.sh" 2>/dev/null || true
18
- ACTIVE_PLAN=$(detect_active_add 2>/dev/null || true)
19
- if [ -n "$ACTIVE_PLAN" ]; then
20
- PLAN_KEYWORD="${ACTIVE_PLAN%%::*}"
21
- PLAN_STATUS="active"
7
+ export CURRENT_MAGIC=".codex"
8
+ export MAGIC_DIR=".codex"
9
+ export PROJECT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
10
+ # core notify helper 使用相对 MAGIC_DIR;统一 cwd,避免从 repo 子目录启动时写偏审计文件。
11
+ cd "$PROJECT_DIR"
12
+
13
+ COMMON_LIB="$HOOK_DIR/lib/common.sh"
14
+ [ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
15
+ source "$HOOK_DIR/lib/notify.sh" 2>/dev/null || true
16
+
17
+ tool_name=$(echo "$input" | jq -r '.tool_name // empty')
18
+ tool_command=$(echo "$input" | jq -r '.tool_input.command // empty')
19
+ active_plan=$(detect_active_add 2>/dev/null || true)
20
+ if [ -n "$active_plan" ]; then
21
+ plan_keyword="${active_plan%%::*}"
22
+ plan_status="active"
22
23
  else
23
- PLAN_KEYWORD="no-active-plan"
24
- PLAN_STATUS="none"
24
+ plan_keyword="no-active-plan"
25
+ plan_status="none"
25
26
  fi
26
27
 
27
- # ── §A 辅助函数: 阻断日志 ──
28
28
  _log_block() {
29
- local rule="$1" cmd="$2"
30
- mkdir -p "$PROJECT_DIR/$MAGIC_DIR/debug-dump"
31
- cat >> "$PROJECT_DIR/$MAGIC_DIR/debug-dump/stdin.log" <<BLOCKLOG
32
- === $(date) [BLOCKED by §A: ${rule}] ===
33
- command: ${cmd:0:300}
34
- === DONE ===
35
- BLOCKLOG
29
+ local rule="$1" detail="$2"
30
+ mkdir -p "$PROJECT_DIR/$MAGIC_DIR/debug-dump" 2>/dev/null || return 0
31
+ printf '=== %s [BLOCKED: %s] ===\n%s\n=== DONE ===\n' \
32
+ "$(date -Iseconds)" "$rule" "${detail:0:500}" \
33
+ >> "$PROJECT_DIR/$MAGIC_DIR/debug-dump/stdin.log" 2>/dev/null || true
36
34
  }
37
35
 
38
- # ═══════════════ §A: Bash 工具写入保护 ═══════════════
39
- # 任何通过 Bash 修改文件内容的操作都会绕过 IDE 工具层(Write/SearchReplace),
40
- # 导致 Plan 关联检查、doc-format-guard、审计追踪全部失效。
41
- # 因此全局阻断所有可写文件的 Bash 命令,强制走 IDE 工具通道。
42
- command=$(echo "$input" | jq -r '.tool_input.command // empty')
43
- if [ -n "$command" ]; then
44
-
45
- # 检测 1: 脚本解释器 — 可写任意文件,无法解析脚本内容做细粒度拦截
46
- if echo "$command" | grep -qE '(^|;|\|\||&&|\|)\s*(python3?|node|ruby|perl|php)(\s|$)'; then
47
- _reason="禁止通过脚本解释器直接修改文件。请使用 Write 或 SearchReplace 工具操作文件。"
48
- cat >&2 <<'EOF'
49
- [ADD PreToolUse §A] 阻断: 禁止通过脚本解释器直接修改文件。
50
-
51
- python/node/ruby/perl/php 可在脚本中写入任意文件,绕过:
52
- · Plan 关联检查(哪个文件属于哪个 ADD Plan?)
53
- · doc-format-guard(章节/占位符/禁止词校验)
54
- · 审计追踪(agentAudit 无法感知 Bash 内部的文件变更)
55
-
56
- → 请改用 Write 或 SearchReplace 工具操作文件。
57
- → 如需运行构建/测试脚本,使用 npx/pnpm/npm 命令。
58
- EOF
59
- echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"ask\",\"permissionDecisionReason\":\"${_reason}\"}}"
60
- _log_block "脚本解释器" "$command"
61
- write_hook_event "pre-tool-use" "deny" "$command" "禁止通过脚本解释器直接修改文件" "$PLAN_KEYWORD" "$PLAN_STATUS" 2>/dev/null || true
62
- exit 2
63
- fi
36
+ _deny() {
37
+ local rule="$1" reason="$2" detail="${3:-$tool_name}"
38
+ echo "⛔ [ADD PreToolUse] $reason" >&2
39
+ jq -nc --arg reason "$reason" '{
40
+ hookSpecificOutput: {
41
+ hookEventName: "PreToolUse",
42
+ permissionDecision: "deny",
43
+ permissionDecisionReason: $reason
44
+ }
45
+ }'
46
+ _log_block "$rule" "$detail"
47
+ write_hook_event "pre-tool-use" "deny" "$tool_name" "$reason" "$plan_keyword" "$plan_status" 2>/dev/null || true
48
+ exit "${EXIT_BLOCK:-2}"
49
+ }
64
50
 
65
- # 检测 2: sed -i 原地编辑
66
- if echo "$command" | grep -qE '\bsed\b.*-i'; then
67
- _reason="禁止通过 sed -i 直接编辑文件。请使用 SearchReplace 工具。"
68
- cat >&2 <<'EOF'
69
- ⛔ [ADD PreToolUse §A] 阻断: 禁止通过 sed -i 原地编辑文件。
70
-
71
- sed -i 直接写入文件,绕过 IDE 工具层的所有校验。
72
- → 请改用 SearchReplace 工具。
73
- EOF
74
- echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"ask\",\"permissionDecisionReason\":\"${_reason}\"}}"
75
- _log_block "sed -i" "$command"
76
- write_hook_event "pre-tool-use" "deny" "$command" "禁止通过 sed -i 原地编辑" "$PLAN_KEYWORD" "$PLAN_STATUS" 2>/dev/null || true
77
- exit 2
78
- fi
51
+ _require_hitl_for() {
52
+ local file_path="$1" do_hitl=false plan_name marker_base marker_full
79
53
 
80
- # 检测 3: 输出重定向 (>/>>) 写入文件
81
- if echo "$command" | grep -qE '[>]{1,2}\s+\S'; then
82
- _reason="禁止通过重定向写入文件。请使用 Write 工具。"
83
- cat >&2 <<'EOF'
84
- [ADD PreToolUse §A] 阻断: 禁止通过重定向(>/>>)写入文件。
85
-
86
- 重定向写入绕过 IDE 工具层,变更无法追踪。
87
- → 请改用 Write 工具。
88
- EOF
89
- echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"ask\",\"permissionDecisionReason\":\"${_reason}\"}}"
90
- _log_block "重定向" "$command"
91
- write_hook_event "pre-tool-use" "deny" "$command" "禁止通过重定向写入文件" "$PLAN_KEYWORD" "$PLAN_STATUS" 2>/dev/null || true
92
- exit 2
54
+ if echo "$file_path" | grep -qE '(^|/)\.codex/plans/'; then
55
+ if ! echo "$file_path" | grep -qE -- '-handoff'; then
56
+ do_hitl=true
57
+ fi
58
+ elif echo "$file_path" | grep -qE '(^|/)\.codex/reviews/'; then
59
+ # Runtime Review 是运行时证据容器;Plan Review 与 Implementation Review 都必须走 create_hitl。
60
+ if ! echo "$file_path" | grep -qE -- '-runtime'; then
61
+ do_hitl=true
62
+ fi
93
63
  fi
94
64
 
95
- # 检测 4: tee / dd 写入
96
- if echo "$command" | grep -qE '\btee\b|\bdd\b.*of='; then
97
- _reason="禁止通过 tee/dd 写入文件。请使用 Write 或 SearchReplace 工具。"
98
- cat >&2 <<'EOF'
99
- ⛔ [ADD PreToolUse §A] 阻断: 禁止通过 tee/dd 写入文件。
100
-
101
- → 请改用 Write 或 SearchReplace 工具。
102
- EOF
103
- echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"ask\",\"permissionDecisionReason\":\"${_reason}\"}}"
104
- _log_block "tee/dd" "$command"
105
- write_hook_event "pre-tool-use" "deny" "$command" "禁止通过 tee/dd 写入文件" "$PLAN_KEYWORD" "$PLAN_STATUS" 2>/dev/null || true
106
- exit 2
107
- fi
65
+ [ "$do_hitl" = true ] || return 0
108
66
 
109
- # 检测 5: cp / mv / touch — 可创建或覆盖文件
110
- if echo "$command" | grep -qE '(^|;|\|\||&&|\|)\s*(cp|mv|touch)\b'; then
111
- _reason="禁止通过 cp/mv/touch 操作文件。请使用 Write SearchReplace 工具。"
112
- cat >&2 <<'EOF'
113
- [ADD PreToolUse §A] 阻断: 禁止通过 cp/mv/touch 操作文件。
114
-
115
- → 请改用 Write 或 SearchReplace 工具。
116
- EOF
117
- echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"ask\",\"permissionDecisionReason\":\"${_reason}\"}}"
118
- _log_block "cp/mv/touch" "$command"
119
- write_hook_event "pre-tool-use" "deny" "$command" "禁止通过 cp/mv/touch 操作文件" "$PLAN_KEYWORD" "$PLAN_STATUS" 2>/dev/null || true
120
- exit 2
67
+ plan_name=$(basename "$file_path" .md | sed 's/\.hitl$//')
68
+ marker_full="$PROJECT_DIR/$MAGIC_DIR/hitl/.tongyi-${plan_name}"
69
+ marker_base="$PROJECT_DIR/$MAGIC_DIR/hitl/.tongyi-$(echo "$plan_name" | sed 's/-plan-v[0-9]*$//;s/-add-route-v[0-9]*$//;s/-review-v[0-9]*$//;s/-review-implementation[^/]*$//')"
70
+ if [ ! -f "$marker_full" ] && [ ! -f "$marker_base" ]; then
71
+ _deny "hitl" "HITL 未同意: $file_path。请先 create_hitl,再由人工 update_hitl(TONGYI)。" "$file_path"
121
72
  fi
73
+ }
122
74
 
123
- # 放行: 构建工具(npx/pnpm/npm/yarn)、版本控制(git)、
124
- # 只读操作(ls/cat/grep/find/head/tail/wc)、目录操作(mkdir/rmdir)
125
- exit 0
126
- fi
75
+ _guard_file_path() {
76
+ local file_path="$1"
77
+ [ -n "$file_path" ] || return 0
127
78
 
128
- # ═══════════════ §B: Write/Edit 文件写入前置守卫 ═══════════════
129
- # Bash 工具(Write/Edit)的文件写入需要经过:
130
- # 1. Plan/Spec/Review 文档写入 → 需要活跃 ADD Plan
131
- # 2. 敏感文件保护 → 阻断写入
132
- # 3. Dev Action 标记 → 用于 Stop 检查
133
- # 注意: 不再对 src/**/*.ts 做 Plan 白名单放行,避免绕过 skill/rules 规定的 HITL。
134
- tool_name=$(echo "$input" | jq -r '.tool_name // empty')
135
- if [ "$tool_name" = "Write" ] || [ "$tool_name" = "Edit" ] || [ "$tool_name" = "SearchReplace" ]; then
136
- file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
137
- [ -z "$file_path" ] && exit 0
138
-
139
- if echo "$file_path" | grep -qE '\.(qoder|claude|add)/(plans|specs|reviews)/'; then
140
- if type detect_active_add >/dev/null 2>&1; then
141
- state=$(detect_active_add 2>/dev/null || true)
142
- if [ -z "$state" ]; then
143
- echo "[ADD 提示] 正在写入 Plan/Spec/Review 文档但无活跃 ADD Plan——首次创建场景放行,建议先执行 add-paradigm 生成 Plan+add-route" >&2
144
- echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"allow\",\"permissionDecisionReason\":\"无活跃 ADD Plan 但为 Plan/Spec/Review 写入(首次创建场景),提示而非拦截\"}}"
145
- write_hook_event "pre-tool-use" "info" "$tool_name $file_path" "无活跃 ADD Plan 下写入 Plan/Spec/Review(首次创建放行)" "$PLAN_KEYWORD" "$PLAN_STATUS" 2>/dev/null || true
146
- exit 0
147
- fi
148
- fi
79
+ if echo "$file_path" | grep -qE '(^|/)(\.env|\.env\.production|\.env\.local)$|credentials|secrets'; then
80
+ _deny "sensitive-file" "敏感文件受保护,禁止写入: $file_path" "$file_path"
149
81
  fi
150
82
 
151
- if echo "$file_path" | grep -qE '\.env$|\.env\.production$|\.env\.local$|credentials|secrets'; then
152
- echo "⛔ 敏感文件受保护,禁止写入: $file_path" >&2
153
- echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"敏感文件受保护\"}}"
154
- exit 2
155
- fi
83
+ _require_hitl_for "$file_path"
156
84
 
157
- # §C: 模板类型前置注入 plans/ 写入前提示应选模板
158
- if echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(plans)/'; then
159
- fname=$(basename "$file_path")
160
- if echo "$fname" | grep -qE 'plan-v[0-9]'; then
161
- echo "💡 [ADD PreToolUse] 写入 Plan → 模板: standard-plan-template.md(标准)或 simple-plan-template.md(≤3文件)" >&2
162
- elif echo "$fname" | grep -qE 'add-route'; then
163
- echo "💡 [ADD PreToolUse] 写入 ADD Route → 模板: add-route-template.md" >&2
164
- elif echo "$fname" | grep -qE 'handoff'; then
165
- echo "💡 [ADD PreToolUse] 写入 Handoff → 模板: handoff-single-round-template.md(单轮)或 handoff-multi-round-template.md(多轮)" >&2
166
- fi
167
- # Write 大文件适配 — 已存在大文件建议用 SearchReplace 分块
168
- if echo "$tool_name" | grep -qE 'Write' && [ -f "$file_path" ]; then
169
- fsize=$(wc -c < "$file_path" 2>/dev/null || echo "0")
170
- if [ "$fsize" -gt 2000 ] 2>/dev/null; then
171
- echo "⚠️ [ADD PreToolUse] 文件已有 ${fsize} 字节,Write 全量覆盖可能触发工具 payload 限制。建议用 SearchReplace 分块追加。" >&2
172
- fi
85
+ if echo "$file_path" | grep -qE '(^|/)\.codex/(plans|specs|reviews)/'; then
86
+ if [ -z "$active_plan" ]; then
87
+ echo "[ADD PreToolUse] 正在修改 ADD 文档但未检测到活跃 Plan: $file_path" >&2
173
88
  fi
174
89
  fi
90
+ }
175
91
 
176
- # §C: HITL tongyi 检查 plans/ + PLAN_REVIEW reviews/ 写入前必须有 .hitl-tongyi-{planName} 哨兵
177
- # implementation/runtime review 不需要 HITL,走 §B 活跃 Plan 检查
178
- # handoff Step 8 收敛产物(多轮交接手册),不经 HITL 审批,同样豁免
179
- # TODO 应该caijuehub管理
180
- if echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(plans)/'; then
181
- if echo "$file_path" | grep -qE '\-handoff'; then
182
- _do_hitl=false # handoff 不被 HITL 拦截(Step 8 产物,无独立审批)
183
- else
184
- _do_hitl=true
185
- fi
186
- elif echo "$file_path" | grep -qE '\.(qoder|claude|add|vscode|trae)/(reviews)/'; then
187
- if echo "$file_path" | grep -qE '-(implementation|runtime)'; then
188
- _do_hitl=false # implementation/runtime review 不被 HITL 拦截
189
- else
190
- _do_hitl=true # PLAN_REVIEW 需要 HITL
191
- fi
192
- else
193
- _do_hitl=false
92
+ # Codex Bash apply_patch 都把 payload 放在 tool_input.command。
93
+ # 只有 canonical Bash 才执行 shell 命令检查,避免把 patch body 误判成 shell。
94
+ if [ "$tool_name" = "Bash" ]; then
95
+ if echo "$tool_command" | grep -qE '(^|;|\|\||&&|\|)[[:space:]]*(python3?|node|ruby|perl|php)([[:space:]]|$)'; then
96
+ _deny "script-interpreter" "禁止通过脚本解释器直接修改文件;请使用 apply_patch。" "$tool_command"
194
97
  fi
195
-
196
- if [ "$_do_hitl" = true ]; then
197
- _relative=$(echo "$file_path" | sed 's|.*/\.\(qoder\|claude\|add\|vscode\|trae\)/\(plans\|reviews\)/||')
198
- _planName=$(basename "$_relative" .md | sed 's/\.hitl$//;s/-plan-v[0-9]*$//;s/-add-route-v[0-9]*$//;s/-review-v[0-9]*$//;s/-review-implementation$//;s/-review-runtime$//')
199
- if [ -n "$_planName" ]; then
200
- _tongyi_marker="${PROJECT_DIR}/${MAGIC_DIR}/hitl/.tongyi-${_planName}"
201
- if [ ! -f "$_tongyi_marker" ]; then
202
- echo "⛔ [ADD PreToolUse §C] HITL 未 tongyi: $file_path" >&2
203
- echo " 原因: 哨兵文件 $_tongyi_marker 不存在" >&2
204
- echo " 操作: 请先调用 create_hitl 创建审批,再 update_hitl({ status: \"TONGYI\" })" >&2
205
- _reason="HITL 未 tongyi: 哨兵 ${_tongyi_marker} 不存在。请先 create_hitl → 人工 tongyi → update_hitl 后再写入"
206
- echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"${_reason}\",\"additionalContext\":\"${_reason}\"}}"
207
- write_hook_event "pre-tool-use" "deny" "$tool_name $file_path" "HITL 未 tongyi: $_tongyi_marker" "$PLAN_KEYWORD" "$PLAN_STATUS" 2>/dev/null || true
208
- exit 0
209
- fi
210
- fi
98
+ # `-i` 必须是 sed 命令中的独立 option;不能把 session-init 等后续路径子串当成原地写入。
99
+ if echo "$tool_command" | grep -qE '(^|[;&|][[:space:]]*)sed[[:space:]]+([^;&|]*[[:space:]])?(-[[:alpha:]]*i[^[:space:];&|]*|--in-place(=[^[:space:];&|]*)?)([[:space:];&|]|$)'; then
100
+ _deny "sed-i" "禁止通过 sed -i 直接编辑文件;请使用 apply_patch。" "$tool_command"
101
+ fi
102
+ if echo "$tool_command" | grep -qE '[>]{1,2}[[:space:]]+[^[:space:]]'; then
103
+ _deny "redirect" "禁止通过重定向写入文件;请使用 apply_patch。" "$tool_command"
211
104
  fi
105
+ if echo "$tool_command" | grep -qE '\btee\b|\bdd\b.*of='; then
106
+ _deny "tee-dd" "禁止通过 tee/dd 写入文件;请使用 apply_patch。" "$tool_command"
107
+ fi
108
+ if echo "$tool_command" | grep -qE '(^|;|\|\||&&|\|)[[:space:]]*(cp|mv|touch)\b'; then
109
+ _deny "copy-move-touch" "禁止通过 cp/mv/touch 改变文件;请使用 apply_patch。" "$tool_command"
110
+ fi
111
+ exit 0
112
+ fi
212
113
 
114
+ if [ "$tool_name" = "apply_patch" ]; then
115
+ patch_paths=$(printf '%s\n' "$tool_command" | sed -nE 's/^\*\*\* (Add|Update|Delete) File: (.+)$/\2/p')
116
+ while IFS= read -r file_path; do
117
+ [ -n "$file_path" ] && _guard_file_path "$file_path"
118
+ done <<< "$patch_paths"
213
119
  mark_dev_action 2>/dev/null || true
214
120
  exit 0
215
121
  fi
216
122
 
123
+ # 兼容尚未 canonicalize 的宿主输入;当前 Codex 正常上报 apply_patch。
124
+ if [ "$tool_name" = "Write" ] || [ "$tool_name" = "Edit" ] || [ "$tool_name" = "SearchReplace" ]; then
125
+ file_path=$(echo "$input" | jq -r '.tool_input.file_path // .tool_input.path // empty')
126
+ _guard_file_path "$file_path"
127
+ mark_dev_action 2>/dev/null || true
128
+ fi
129
+
217
130
  exit 0
@@ -1,5 +1,5 @@
1
1
  #!/bin/bash
2
- # prompt-submit.sh — UserPromptSubmit 触发词智能路由(通用适配版)
2
+ # prompt-submit.sh — Codex UserPromptSubmit 触发词智能路由
3
3
  # 治理卡位 #3: Layer 1 精准触发 → Layer 2 阻断 → Layer 3 状态注入
4
4
  set -euo pipefail
5
5
 
@@ -8,8 +8,13 @@ prompt=$(echo "$input" | jq -r '.prompt // empty')
8
8
  [ -z "$prompt" ] && exit 0
9
9
 
10
10
  HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
11
+ export CURRENT_MAGIC=".codex"
12
+ export MAGIC_DIR=".codex"
13
+ export PROJECT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
14
+ # Codex hook command 继承 session cwd;先统一 git root,确保状态与报告路径稳定。
15
+ cd "$PROJECT_DIR"
11
16
  source "$HOOK_DIR/lib/vocabulary.sh" 2>/dev/null || true
12
- source "$HOOK_DIR/lib/state-detect.sh" 2>/dev/null || true
17
+ source "$HOOK_DIR/lib/common.sh" 2>/dev/null || true
13
18
  source "$HOOK_DIR/lib/notify.sh" 2>/dev/null || true
14
19
 
15
20
  # ─── Layer 1: 精准 P0 触发词 ───
@@ -72,7 +77,7 @@ handoff=$(echo "$state" | awk -F'::' '{print $4}')
72
77
  echo "[ADD 状态] Plan: ${plan}, 轮次: ${rounds}, Step: ${step}, handoff: ${handoff}"
73
78
 
74
79
  # ─── Hook 治理日报(注入 AI 上下文)───
75
- HOOK_JSONL="${MAGIC_DIR:-.qoder}/reports/hook-events.jsonl"
80
+ HOOK_JSONL="${MAGIC_DIR:-.codex}/reports/hook-events.jsonl"
76
81
  if [ -f "$HOOK_JSONL" ]; then
77
82
  TODAY="$(date +%Y-%m-%d)"
78
83
  TOTAL=$(grep -c "\"ts\":\"${TODAY}" "$HOOK_JSONL" 2>/dev/null || echo 0)
@@ -1,22 +1,28 @@
1
1
  #!/bin/bash
2
- # SessionStart — ADD 上下文恢复 + 模板索引注入(Claude Code 适配)
2
+ # SessionStart — ADD 上下文恢复 + 模板索引注入(Codex 适配)
3
3
  # 治理卡位 #1: ADD状态恢复 + 模板索引注入
4
4
  set -euo pipefail
5
5
 
6
6
  HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
7
- export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
7
+ export CURRENT_MAGIC=".codex"
8
+ export MAGIC_DIR=".codex"
9
+ export PROJECT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
8
10
 
9
11
  # 加载四端通用函数(路径统一后: .claude/hooks/lib/common.sh)
10
12
  COMMON_LIB="$HOOK_DIR/lib/common.sh"
11
13
  [ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
12
14
 
13
- # 设置项目目录
14
- export PROJECT_DIR="$PWD"
15
+ # Codex 可能从 repo 子目录启动;状态仍只读取 Codex 自己的 `.codex`。
16
+ export PLANS_DIR="$PROJECT_DIR/$MAGIC_DIR/plans"
15
17
 
16
18
  # ── ① ADD 状态恢复 ──
17
19
  state=$(detect_active_add 2>/dev/null || true)
18
20
  if [ -n "$state" ]; then
19
- IFS='::' read -r plan step rounds handoff add_route <<< "$state"
21
+ plan=$(echo "$state" | awk -F'::' '{print $1}')
22
+ step=$(echo "$state" | awk -F'::' '{print $2}')
23
+ rounds=$(echo "$state" | awk -F'::' '{print $3}')
24
+ handoff=$(echo "$state" | awk -F'::' '{print $4}')
25
+ add_route=$(echo "$state" | awk -F'::' '{print $5}')
20
26
  cat <<EOF
21
27
  [ADD SessionStart] 检测到活跃 ADD Plan:
22
28
  Plan: ${plan}
@@ -28,15 +34,19 @@ EOF
28
34
  fi
29
35
 
30
36
  # ── ② 模板索引注入 ──
31
- TPL_SCRIPT="$HOOK_DIR/lib/preload-templates.sh"
32
- if [ -f "$TPL_SCRIPT" ]; then
33
- bash "$TPL_SCRIPT" --index
37
+ # core lib 的相对路径面向其他 adapter;Codex 直接读取项目 `.codex/templates`。
38
+ TEMPLATES_DIR="$PROJECT_DIR/$CURRENT_MAGIC/templates"
39
+ if [ -d "$TEMPLATES_DIR" ]; then
40
+ echo "## ADD 可用模板清单"
41
+ for template_path in "$TEMPLATES_DIR"/*.md; do
42
+ [ -f "$template_path" ] || continue
43
+ echo "- $(basename "$template_path")"
44
+ done
34
45
  fi
35
46
 
36
47
  # ── ③ §代办刷新:活跃 Plan 时提醒加载 IDE 代办 ──
37
48
  # session-init Step 2.6 依赖 IDE resume invoke SKILL,本段作为兜底
38
49
  if [ -n "$state" ]; then
39
- IFS='::' read -r plan step rounds handoff add_route <<< "$state"
40
50
  cat <<EOF
41
51
  [代办] 检测到活跃 Plan: ${plan}。如有未加载的 IDE 代办清单,请从 tasks.md §IDE JSON 刷新 TodoWrite。
42
52
  EOF
@@ -1,45 +1,71 @@
1
1
  #!/bin/bash
2
- # stop-check.sh — Claude Code Stop:四象限分流 + 验收检查(few-shot 上下文注入)
3
- # 治理卡位 #7: 验收检查 + devlog + 阻断
2
+ # stop-check.sh — Codex Stop:四象限分流 + 验收检查
4
3
  set -euo pipefail
5
4
 
6
5
  HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
7
- export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
6
+ export CURRENT_MAGIC=".codex"
7
+ export MAGIC_DIR=".codex"
8
+ export PROJECT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
8
9
  COMMON_LIB="$HOOK_DIR/lib/common.sh"
9
10
  [ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
10
11
 
11
- export PROJECT_DIR="$PWD"
12
+ input=$(cat 2>/dev/null || echo '{}')
13
+
14
+ # Stop 已经续跑过一次时不再次阻断,避免递归 continuation。
15
+ if echo "$input" | jq -e '.stop_hook_active == true' >/dev/null 2>&1; then
16
+ exit 0
17
+ fi
18
+
19
+ _system_message() {
20
+ local message="$1"
21
+ jq -nc --arg message "$message" '{systemMessage: $message}'
22
+ }
12
23
 
13
24
  state=$(detect_active_add 2>/dev/null || true)
14
25
  has_dev=$(has_dev_action 2>/dev/null && echo "true" || echo "false")
15
26
 
16
- # ═══════ Q1: ADD + dev → 正常停 ═══════
27
+ # DB lifecycle 真相源;不可用时 fail closed,不能谎报“无活跃 Plan”。
28
+ if [ "${state%%::*}" = "__STATUS_UNAVAILABLE__" ]; then
29
+ reason=$(echo "$state" | awk -F'::' '{print $2}')
30
+ printf '%s\n' "[ADD Stop] ⛔ Plan status 暂不可用(${reason})。未回退 Handoff/add-route 猜测,请恢复数据库或 MCP resolver 后重试。" >&2
31
+ exit "${EXIT_BLOCK:-2}"
32
+ fi
33
+
34
+ # Q1: 无 ADD + 无 dev → exit 0 且 stdout 为空。
17
35
  if [ -z "$state" ] && [ "$has_dev" != "true" ]; then
18
36
  exit 0
19
37
  fi
20
38
 
21
- # ═══════ Q2: 无 ADD + 有 dev → 严重违规,few-shot 注入 ═══════
39
+ # Q2: 无 ADD + 有 dev → exit 2,stderr 成为 continuation reason。
22
40
  if [ -z "$state" ] && [ "$has_dev" = "true" ]; then
23
- build_stop_context "no_add_has_dev" >&2
24
- exit $EXIT_BLOCK
41
+ build_stop_context "no_add_has_dev" "" >&2
42
+ exit "${EXIT_BLOCK:-2}"
25
43
  fi
26
44
 
27
- IFS='::' read -r plan step rounds handoff add_route <<< "$state"
45
+ plan=$(echo "$state" | awk -F'::' '{print $1}')
46
+ step=$(echo "$state" | awk -F'::' '{print $2}')
47
+ approval=$(echo "$state" | awk -F'::' '{print $3}')
28
48
 
29
- # ═══════ Q3: 有 ADD + 无 dev → 注入状态 ═══════
49
+ # Q3: 有 ADD + 无 dev → 合法 JSON,不输出纯文本。
30
50
  if [ "$has_dev" != "true" ]; then
31
- echo "[ADD Stop] Plan: ${plan}, 轮次: ${rounds}, Step: ${step}"
32
- echo "本次无代码改动。下次继续时执行 session-init 恢复上下文。"
51
+ _system_message "[ADD Stop] Plan: ${plan};approval: ${approval};tasks: ${step}。本次无代码改动。"
33
52
  exit 0
34
53
  fi
35
54
 
36
- # ═══════ Q4: 有 ADD + 有 dev 验收检查 ═══════
37
- issues=$(check_add_completeness "$handoff" "$add_route" 2>/dev/null || true)
38
- if [ -n "$issues" ]; then
39
- build_stop_context "has_add_dev_unclosed" "$issues" >&2
40
- exit $EXIT_BLOCK
55
+ # Q4: 有 ADD + 有 dev。DB progress 决定本轮是否仍有任务;Handoff 只在
56
+ # Step 8 生成,不能参与 active 判定,也不能因缺失而把 ACTIVE Plan 判成无 Plan。
57
+ done_tasks=${step%%/*}
58
+ total_tasks=${step##*/}
59
+ if [[ "$done_tasks" =~ ^[0-9]+$ ]] && [[ "$total_tasks" =~ ^[0-9]+$ ]] && [ "$done_tasks" -lt "$total_tasks" ]; then
60
+ cat >&2 <<EOF
61
+ [ADD Stop] ⚠️ 当前 DB Plan 仍为 ACTIVE,任务进度 ${done_tasks}/${total_tasks}:${plan}
62
+
63
+ 请继续执行当前 Plan 的未完成 Task,并为本轮改动补齐 record_dev_operation 审计。
64
+ Handoff 只在 Step 8 生成;本提示没有使用 Handoff/add-route 猜测 active 状态。
65
+ EOF
66
+ exit "${EXIT_BLOCK:-2}"
41
67
  fi
42
68
 
43
69
  clear_dev_action 2>/dev/null || true
44
- echo "[ADD Stop] 验收通过——checklist 全部勾选,devlog 已记录。"
70
+ _system_message "[ADD Stop] DB Plan ${plan} 的 tasks 已完成(${done_tasks}/${total_tasks});可进入 Review/closure。"
45
71
  exit 0
@@ -1 +1,67 @@
1
- {"hooks":{"SessionStart":[{"command":"bash .codex/hooks/session-start.sh"}],"UserPromptSubmit":[{"command":"bash .codex/hooks/prompt-submit.sh"}],"PreToolUse":[{"command":"bash .codex/hooks/pre-tool-use.sh"}],"PostToolUse":[{"command":"bash .codex/hooks/post-tool-use.sh"}],"Stop":[{"command":"bash .codex/hooks/stop-check.sh"}],"Notification":[{"matcher":"result","command":"bash .codex/hooks/notification.sh"}]}}
1
+ {
2
+ "description": "ADD governance hooks for Codex.",
3
+ "hooks": {
4
+ "SessionStart": [
5
+ {
6
+ "matcher": "^(startup|resume|clear|compact)$",
7
+ "hooks": [
8
+ {
9
+ "type": "command",
10
+ "command": "bash \"$(git rev-parse --show-toplevel)/.codex/hooks/session-start.sh\"",
11
+ "statusMessage": "Loading ADD state",
12
+ "additionalContextLimit": 5000
13
+ }
14
+ ]
15
+ }
16
+ ],
17
+ "UserPromptSubmit": [
18
+ {
19
+ "hooks": [
20
+ {
21
+ "type": "command",
22
+ "command": "bash \"$(git rev-parse --show-toplevel)/.codex/hooks/prompt-submit.sh\"",
23
+ "statusMessage": "Routing ADD workflow"
24
+ }
25
+ ]
26
+ }
27
+ ],
28
+ "PreToolUse": [
29
+ {
30
+ "matcher": "^(Bash|apply_patch|Edit|Write)$",
31
+ "hooks": [
32
+ {
33
+ "type": "command",
34
+ "command": "bash \"$(git rev-parse --show-toplevel)/.codex/hooks/pre-tool-use.sh\"",
35
+ "statusMessage": "Checking ADD policy",
36
+ "timeout": 30
37
+ }
38
+ ]
39
+ }
40
+ ],
41
+ "PostToolUse": [
42
+ {
43
+ "matcher": "^(Bash|apply_patch|Edit|Write)$",
44
+ "hooks": [
45
+ {
46
+ "type": "command",
47
+ "command": "bash \"$(git rev-parse --show-toplevel)/.codex/hooks/post-tool-use.sh\"",
48
+ "statusMessage": "Recording ADD result",
49
+ "timeout": 30
50
+ }
51
+ ]
52
+ }
53
+ ],
54
+ "Stop": [
55
+ {
56
+ "hooks": [
57
+ {
58
+ "type": "command",
59
+ "command": "bash \"$(git rev-parse --show-toplevel)/.codex/hooks/stop-check.sh\"",
60
+ "statusMessage": "Checking ADD closure",
61
+ "timeout": 30
62
+ }
63
+ ]
64
+ }
65
+ ]
66
+ }
67
+ }