add-coder 0.3.26 → 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.
- package/README.md +36 -10
- package/dist/index.js +481 -209
- package/package.json +14 -4
- package/templates/.add-coder-src-hash.json +129 -106
- package/templates/adapters/claude/hooks/doc-format-guard.sh +112 -31
- package/templates/adapters/claude/hooks/lib/common.sh +46 -87
- package/templates/adapters/claude/hooks/lib/context-inject.sh +2 -2
- package/templates/adapters/claude/hooks/lib/notify.sh +6 -4
- package/templates/adapters/claude/hooks/lib/preload-templates.sh +27 -2
- package/templates/adapters/claude/hooks/lib/state-detect.sh +8 -112
- package/templates/adapters/claude/hooks/lib/vocabulary.sh +13 -7
- package/templates/adapters/claude/hooks/prompt-submit.sh +1 -1
- package/templates/adapters/claude/hooks/stop-check.sh +9 -0
- package/templates/adapters/codex/config.toml.example +12 -0
- package/templates/adapters/codex/hooks/doc-format-guard.sh +121 -39
- package/templates/adapters/codex/hooks/lib/common.sh +47 -99
- package/templates/adapters/codex/hooks/lib/context-inject.sh +4 -4
- package/templates/adapters/codex/hooks/lib/notify.sh +6 -4
- package/templates/adapters/codex/hooks/lib/preload-templates.sh +27 -6
- package/templates/adapters/codex/hooks/lib/state-detect.sh +5 -114
- package/templates/adapters/codex/hooks/lib/vocabulary.sh +3 -8
- package/templates/adapters/codex/hooks/notification.sh +3 -18
- package/templates/adapters/codex/hooks/post-tool-use.sh +24 -119
- package/templates/adapters/codex/hooks/pre-tool-use.sh +97 -184
- package/templates/adapters/codex/hooks/prompt-submit.sh +8 -3
- package/templates/adapters/codex/hooks/session-start.sh +19 -9
- package/templates/adapters/codex/hooks/stop-check.sh +44 -18
- package/templates/adapters/codex/hooks.json +67 -1
- package/templates/adapters/qoder/hooks/doc-format-guard.sh +119 -38
- package/templates/adapters/qoder/hooks/lib/common.sh +49 -87
- package/templates/adapters/qoder/hooks/lib/context-inject.sh +2 -2
- package/templates/adapters/qoder/hooks/lib/notify.sh +6 -4
- package/templates/adapters/qoder/hooks/lib/preload-templates.sh +27 -2
- package/templates/adapters/qoder/hooks/lib/state-detect.sh +8 -112
- package/templates/adapters/qoder/hooks/lib/vocabulary.sh +13 -7
- package/templates/adapters/qoder/hooks/prompt-submit.sh +1 -1
- package/templates/adapters/qoder/hooks/session-start.sh +1 -1
- package/templates/adapters/qoder/hooks/stop-check.sh +11 -0
- package/templates/adapters/trae/hooks/doc-format-guard.sh +120 -38
- package/templates/adapters/trae/hooks/lib/common.sh +46 -87
- package/templates/adapters/trae/hooks/lib/context-inject.sh +2 -2
- package/templates/adapters/trae/hooks/lib/notify.sh +5 -3
- package/templates/adapters/trae/hooks/lib/preload-templates.sh +27 -2
- package/templates/adapters/trae/hooks/lib/state-detect.sh +8 -115
- package/templates/adapters/trae/hooks/lib/vocabulary.sh +13 -7
- package/templates/adapters/trae/hooks/pre-tool-use.sh +14 -8
- package/templates/adapters/trae/hooks/stop-check.sh +9 -0
- package/templates/adapters/vscode/hooks/doc-format-guard.sh +112 -31
- package/templates/adapters/vscode/hooks/lib/common.sh +46 -87
- package/templates/adapters/vscode/hooks/lib/context-inject.sh +2 -2
- package/templates/adapters/vscode/hooks/lib/notify.sh +6 -4
- package/templates/adapters/vscode/hooks/lib/preload-templates.sh +27 -2
- package/templates/adapters/vscode/hooks/lib/state-detect.sh +8 -112
- package/templates/adapters/vscode/hooks/lib/vocabulary.sh +13 -7
- package/templates/adapters/vscode/hooks/notification.sh +3 -5
- package/templates/adapters/vscode/hooks/prompt-submit.sh +1 -1
- package/templates/adapters/vscode/hooks/stop-check.sh +9 -0
- package/templates/core/docs/ADD-governance-claude-code.md +1 -1
- package/templates/core/docs/ADD-governance-codex.md +1 -1
- package/templates/core/docs/ADD-governance-qoder-cn.md +1 -1
- package/templates/core/docs/ADD-governance-trae.md +1 -1
- package/templates/core/docs/ADD-governance-vscode-copilot.md +1 -1
- package/templates/core/hooks/doc-format-guard.sh +120 -38
- package/templates/core/hooks/lib/common.sh +46 -87
- package/templates/core/hooks/lib/context-inject.sh +2 -2
- package/templates/core/hooks/lib/notify.sh +5 -3
- package/templates/core/hooks/lib/preload-templates.sh +27 -2
- package/templates/core/hooks/lib/state-detect.sh +8 -115
- package/templates/core/hooks/lib/vocabulary.sh +13 -7
- package/templates/core/hooks/pre-tool-use.sh +14 -8
- package/templates/core/hooks/stop-check.sh +9 -0
- package/templates/core/prisma/add.prisma +140 -82
- package/templates/core/rules/project_rules.md +27 -24
- package/templates/core/scripts/db-ensure.sh +8 -0
- package/templates/core/scripts/gen-plan-index.sh +4 -15
- package/templates/core/scripts/mcp-server/notifications/hitl.ts +9 -1
- package/templates/core/scripts/mcp-server/notifications/hook.ts +25 -9
- package/templates/core/scripts/mcp-server/resources/hitl-approval-widget.ts +34 -0
- package/templates/core/scripts/mcp-server/resources/hook-events-report.ts +14 -2
- package/templates/core/scripts/mcp-server/resources/index.ts +2 -0
- package/templates/core/scripts/mcp-server/sampling/review.ts +25 -10
- package/templates/core/scripts/mcp-server/shared/db-types.ts +33 -8
- package/templates/core/scripts/mcp-server/shared/dps-scoring.strategy.ts +1 -1
- package/templates/core/scripts/mcp-server/shared/env.ts +28 -8
- package/templates/core/scripts/mcp-server/shared/fs.ts +3 -0
- package/templates/core/scripts/mcp-server/shared/hitl-interaction.strategy.ts +2 -3
- package/templates/core/scripts/mcp-server/shared/hitl-lifecycle-mutation.ts +129 -0
- package/templates/core/scripts/mcp-server/shared/hitl-proposal-mutation.ts +98 -0
- package/templates/core/scripts/mcp-server/shared/hitl-ui.ts +3 -0
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle-events.ts +62 -0
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle-mutation.ts +78 -0
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle-subscriber.ts +145 -0
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle.ts +153 -0
- package/templates/core/scripts/mcp-server/shared/plan-round-events.ts +56 -0
- package/templates/core/scripts/mcp-server/shared/plan-round-mutation.ts +143 -0
- package/templates/core/scripts/mcp-server/shared/plan-round-store.ts +47 -0
- package/templates/core/scripts/mcp-server/shared/plan-round-subscriber.ts +99 -0
- package/templates/core/scripts/mcp-server/shared/plan-status-store.ts +37 -0
- package/templates/core/scripts/mcp-server/shared/plan-tracking-mutation.ts +71 -0
- package/templates/core/scripts/mcp-server/shared/prisma.ts +12 -6
- package/templates/core/scripts/mcp-server/shared/project-root-strategy.ts +1 -2
- package/templates/core/scripts/mcp-server/shared/quant-recheck.strategy.ts +16 -0
- package/templates/core/scripts/mcp-server/shared/redact.ts +28 -0
- package/templates/core/scripts/mcp-server/shared/response.ts +3 -2
- package/templates/core/scripts/mcp-server/shared/runtime-context.ts +77 -0
- package/templates/core/scripts/mcp-server/shared/schema-topology.ts +159 -0
- package/templates/core/scripts/mcp-server/tools/audit.ts +49 -7
- package/templates/core/scripts/mcp-server/tools/context.ts +117 -121
- package/templates/core/scripts/mcp-server/tools/contract.ts +34 -9
- package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_status.ts +50 -3
- package/templates/core/scripts/mcp-server/tools/gateway/check_doc_similarity.ts +218 -0
- package/templates/core/scripts/mcp-server/tools/gateway/check_dps.ts +22 -7
- package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +75 -4
- package/templates/core/scripts/mcp-server/tools/gateway/check_spec_sync.ts +19 -6
- package/templates/core/scripts/mcp-server/tools/gateway/index.ts +2 -0
- package/templates/core/scripts/mcp-server/tools/hitl.ts +160 -75
- package/templates/core/scripts/mcp-server/tools/hook-event-report.ts +9 -2
- package/templates/core/scripts/mcp-server/tools/index.ts +69 -3
- package/templates/core/scripts/mcp-server/tools/plan.ts +140 -15
- package/templates/core/scripts/mcp-server/tools/review.ts +44 -15
- package/templates/core/scripts/mcp-server.ts +60 -1
- package/templates/core/scripts/plan-status-bridge.ts +26 -0
- package/templates/core/skills/add-paradigm/SKILL.md +2 -2
- package/templates/core/templates/add-route-template.schema.json +24 -56
- package/templates/core/templates/collab-contract-template.md +1 -1
- package/templates/core/templates/handoff-multi-round-template.schema.json +48 -0
- package/templates/core/templates/handoff-single-round-template.schema.json +92 -0
- package/templates/core/templates/handoff-single-round.schema.json +1 -1
- package/templates/core/templates/hitl-approval-widget.html +222 -37
- package/templates/core/templates/spec-template.schema.json +9 -46
- package/templates/core/templates/standard-plan-template.schema.json +1 -1
- package/templates/core/templates/tasks-template.schema.json +13 -54
|
@@ -1,217 +1,130 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# pre-tool-use.sh — PreToolUse
|
|
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
|
-
|
|
13
|
-
MAGIC_DIR=".codex"
|
|
14
|
-
PROJECT_DIR="$
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
24
|
-
|
|
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"
|
|
30
|
-
mkdir -p "$PROJECT_DIR/$MAGIC_DIR/debug-dump"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
66
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
fi
|
|
75
|
+
_guard_file_path() {
|
|
76
|
+
local file_path="$1"
|
|
77
|
+
[ -n "$file_path" ] || return 0
|
|
127
78
|
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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/
|
|
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:-.
|
|
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 上下文恢复 + 模板索引注入(
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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 —
|
|
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
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
#
|
|
49
|
+
# Q3: 有 ADD + 无 dev → 合法 JSON,不输出纯文本。
|
|
30
50
|
if [ "$has_dev" != "true" ]; then
|
|
31
|
-
|
|
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
|
-
#
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
70
|
+
_system_message "[ADD Stop] DB Plan ${plan} 的 tasks 已完成(${done_tasks}/${total_tasks});可进入 Review/closure。"
|
|
45
71
|
exit 0
|
|
@@ -1 +1,67 @@
|
|
|
1
|
-
{
|
|
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
|
+
}
|