add-coder 0.1.15 → 0.2.0
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.en.md +13 -3
- package/README.md +9 -8
- package/dist/index.js +73 -99
- package/package.json +5 -1
- package/templates/adapters/claude/hooks/doc-format-guard.sh +164 -9
- package/templates/adapters/claude/hooks/notification.sh +29 -0
- package/templates/adapters/claude/hooks/permission-denied.sh +42 -0
- package/templates/adapters/claude/hooks/post-tool-use.sh +25 -8
- package/templates/adapters/claude/hooks/pre-compact.sh +31 -8
- package/templates/adapters/claude/hooks/pre-tool-use.sh +63 -13
- package/templates/adapters/claude/hooks/prompt-submit.sh +70 -8
- package/templates/adapters/claude/hooks/session-end.sh +21 -0
- package/templates/adapters/claude/hooks/session-start.sh +26 -13
- package/templates/adapters/claude/hooks/stop-check.sh +40 -5
- package/templates/adapters/claude/hooks/stop-failure.sh +44 -0
- package/templates/adapters/claude/hooks/subagent-guard.sh +29 -8
- package/templates/adapters/claude/hooks/subagent-stop.sh +20 -0
- package/templates/adapters/codex/hooks/notification.sh +38 -0
- package/templates/adapters/codex/hooks/post-tool-use.sh +35 -0
- package/templates/adapters/codex/hooks/pre-tool-use.sh +78 -0
- package/templates/adapters/codex/hooks/prompt-submit.sh +78 -0
- package/templates/adapters/codex/hooks/session-start.sh +36 -0
- package/templates/adapters/codex/hooks/stop-check.sh +45 -0
- package/templates/adapters/codex/hooks.json +1 -0
- package/templates/adapters/codex/settings.json +1 -0
- package/templates/adapters/qoder/hooks/doc-format-guard.sh +9 -1
- package/templates/adapters/qoder/hooks/lib/vocabulary.sh +1 -1
- package/templates/adapters/qoder/hooks/notification.sh +8 -5
- package/templates/adapters/qoder/hooks/post-tool-use.sh +21 -11
- package/templates/adapters/qoder/hooks/pre-compact.sh +31 -6
- package/templates/adapters/qoder/hooks/pre-tool-use.sh +66 -64
- package/templates/adapters/qoder/hooks/prompt-submit.sh +12 -2
- package/templates/adapters/qoder/hooks/session-end.sh +24 -0
- package/templates/adapters/qoder/hooks/session-start.sh +22 -7
- package/templates/adapters/qoder/hooks/stop-check.sh +18 -41
- package/templates/adapters/qoder/hooks/subagent-guard.sh +22 -6
- package/templates/adapters/qoder/hooks/subagent-stop.sh +26 -0
- package/templates/adapters/qoder/settings.json +13 -13
- package/templates/adapters/trae/hooks/notification.sh +38 -0
- package/templates/adapters/trae/hooks/post-tool-failure.sh +10 -0
- package/templates/adapters/trae/hooks/post-tool-use.sh +35 -0
- package/templates/adapters/trae/hooks/pre-compact.sh +37 -0
- package/templates/adapters/trae/hooks/pre-tool-use.sh +78 -0
- package/templates/adapters/trae/hooks/prompt-submit.sh +78 -0
- package/templates/adapters/trae/hooks/session-end.sh +21 -0
- package/templates/adapters/trae/hooks/session-start.sh +36 -0
- package/templates/adapters/trae/hooks/stop-check.sh +45 -0
- package/templates/adapters/trae/hooks/subagent-guard.sh +36 -0
- package/templates/adapters/trae/hooks/subagent-stop.sh +20 -0
- package/templates/adapters/trae/hooks.json +1 -0
- package/templates/adapters/trae/settings.json +1 -0
- package/templates/adapters/vscode/.github/hooks/error-occurred.json +1 -0
- package/templates/adapters/vscode/.github/hooks/post-tool-use.json +1 -0
- package/templates/adapters/vscode/.github/hooks/pre-compact.json +1 -0
- package/templates/adapters/vscode/.github/hooks/pre-tool-use.json +14 -0
- package/templates/adapters/vscode/.github/hooks/session-end.json +1 -0
- package/templates/adapters/vscode/.github/hooks/session-start.json +14 -0
- package/templates/adapters/vscode/.github/hooks/stop-check.json +1 -0
- package/templates/adapters/vscode/.github/hooks/subagent-start.json +1 -0
- package/templates/adapters/vscode/.github/hooks/subagent-stop.json +1 -0
- package/templates/adapters/vscode/.github/hooks/user-prompt-submit.json +14 -0
- package/templates/adapters/vscode/hooks/notification.sh +37 -0
- package/templates/adapters/vscode/hooks/post-tool-failure.sh +28 -0
- package/templates/adapters/vscode/hooks/post-tool-use.sh +35 -0
- package/templates/adapters/vscode/hooks/pre-compact.sh +37 -0
- package/templates/adapters/vscode/hooks/pre-tool-use.sh +78 -0
- package/templates/adapters/vscode/hooks/prompt-submit.sh +78 -0
- package/templates/adapters/vscode/hooks/session-end.sh +13 -0
- package/templates/adapters/vscode/hooks/session-start.sh +35 -0
- package/templates/adapters/vscode/hooks/stop-check.sh +53 -0
- package/templates/adapters/vscode/hooks/subagent-guard.sh +37 -0
- package/templates/adapters/vscode/hooks/subagent-stop.sh +12 -0
- package/templates/adapters/vscode/launch.json +1 -1
- package/templates/adapters/vscode/settings.json +2 -3
- package/templates/adapters/vscode/tasks.json +3 -3
- package/templates/core/docs/ADD-governance-claude-code.md +121 -0
- package/templates/core/docs/ADD-governance-codex.md +75 -0
- package/templates/core/docs/ADD-governance-qoder-cn.md +99 -0
- package/templates/core/docs/ADD-governance-trae.md +63 -0
- package/templates/core/docs/ADD-governance-vscode-copilot.md +132 -0
- package/templates/core/hooks/doc-format-guard.sh +164 -9
- package/templates/core/hooks/lib/common.sh +261 -0
- package/templates/core/hooks/lib/preload-templates.sh +187 -0
- package/templates/core/hooks/lib/session-end.sh +76 -0
- package/templates/core/hooks/lib/subagent-stop.sh +90 -0
- package/templates/core/hooks/notification.sh +29 -0
- package/templates/core/hooks/post-tool-use.sh +25 -8
- package/templates/core/hooks/pre-compact.sh +31 -8
- package/templates/core/hooks/pre-tool-use.sh +63 -13
- package/templates/core/hooks/prompt-submit.sh +70 -8
- package/templates/core/hooks/session-end.sh +38 -0
- package/templates/core/hooks/session-start.sh +26 -13
- package/templates/core/hooks/stop-check.sh +40 -5
- package/templates/core/hooks/subagent-stop.sh +47 -0
- package/templates/core/rules/project_rules.md +1 -1
- package/templates/core/scripts/db-ensure.sh +6 -5
- package/templates/core/scripts/mcp-server.ts +38 -120
- package/templates/core/skills/add-paradigm/SKILL.md +24 -0
- package/templates/core/templates/standard-plan-template.md +39 -13
- package/templates/core/hooks/lib/context-inject.sh +0 -96
- package/templates/core/hooks/lib/state-detect.sh +0 -104
- package/templates/core/hooks/lib/vocabulary.sh +0 -49
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
###
|
|
3
|
+
# @Author : xiaomingming wujixmm@gmail.com
|
|
4
|
+
# @Date : 2026-07-17 13:20:43
|
|
5
|
+
# @LastEditors : xiaomingming wujixmm@gmail.com
|
|
6
|
+
# @LastEditTime : 2026-07-17 13:34:19
|
|
7
|
+
# @FilePath : /add-coder/templates/core/hooks/lib/session-end.sh
|
|
8
|
+
# @Description : SessionEnd 治理卡位
|
|
9
|
+
###
|
|
10
|
+
# session-end.sh — SessionEnd 治理卡位
|
|
11
|
+
# 路径: templates/core/hooks/lib/session-end.sh
|
|
12
|
+
#
|
|
13
|
+
# 治理职能(卡位 #2):
|
|
14
|
+
# ① 清理 tpl-injected 标记文件
|
|
15
|
+
# ② 会话审计结算(汇总 tool 调用统计)
|
|
16
|
+
# ③ Stop 未触发兜底: 若 Stop 未执行验收检查,补执行 checklist 快照(best-effort)
|
|
17
|
+
#
|
|
18
|
+
# 用法: 被各 IDE adapter 的 SessionEnd hook 脚本 source 并调用 main
|
|
19
|
+
|
|
20
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
21
|
+
source "$SCRIPT_DIR/common.sh" 2>/dev/null || true
|
|
22
|
+
|
|
23
|
+
# 清理 tpl-injected 标记
|
|
24
|
+
cleanup_tpl_flag() {
|
|
25
|
+
local project_hash=$(echo "${PROJECT_DIR:-$PWD}" | md5sum 2>/dev/null | cut -c1-8 || echo "default")
|
|
26
|
+
rm -f "/tmp/add_tpl_${project_hash}" 2>/dev/null || true
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# 清理 dev action 标记
|
|
30
|
+
cleanup_dev_flag() {
|
|
31
|
+
local project_hash=$(echo "${PROJECT_DIR:-$PWD}" | md5sum 2>/dev/null | cut -c1-8 || echo "default")
|
|
32
|
+
rm -f "/tmp/add_dev_${project_hash}" 2>/dev/null || true
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Stop 未触发兜底: 如果 dev action 标记还在(说明 Stop 没执行验收检查)
|
|
36
|
+
stop_fallback() {
|
|
37
|
+
if has_dev_action 2>/dev/null; then
|
|
38
|
+
echo "[ADD SessionEnd] ⚠️ 检测到 dev action 标记未清除——Stop 可能未触发验收检查" >&2
|
|
39
|
+
|
|
40
|
+
# 尝试找到 handoff 做 checklist 快照
|
|
41
|
+
local state=""
|
|
42
|
+
if type detect_active_add >/dev/null 2>&1; then
|
|
43
|
+
state=$(detect_active_add 2>/dev/null || true)
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
if [ -n "$state" ]; then
|
|
47
|
+
local handoff=$(echo "$state" | awk -F'::' '{print $4}')
|
|
48
|
+
local add_route=$(echo "$state" | awk -F'::' '{print $5}')
|
|
49
|
+
if [ -f "$handoff" ]; then
|
|
50
|
+
echo "[ADD SessionEnd] 补执行 checklist 快照(best-effort,不阻断)" >&2
|
|
51
|
+
if type check_add_completeness >/dev/null 2>&1; then
|
|
52
|
+
check_add_completeness "$handoff" "$add_route" >&2 2>/dev/null || true
|
|
53
|
+
fi
|
|
54
|
+
fi
|
|
55
|
+
fi
|
|
56
|
+
fi
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
main() {
|
|
60
|
+
# ① 清理标记文件
|
|
61
|
+
cleanup_tpl_flag
|
|
62
|
+
cleanup_dev_flag
|
|
63
|
+
|
|
64
|
+
# ② 审计结算(输出到 stderr 供日志记录)
|
|
65
|
+
echo "[ADD SessionEnd] 会话结束 — $(date -Iseconds)" >&2
|
|
66
|
+
|
|
67
|
+
# ③ Stop 未触发兜底
|
|
68
|
+
stop_fallback
|
|
69
|
+
|
|
70
|
+
exit 0
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# 被 source 时不自动执行,source 后调用 main
|
|
74
|
+
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
|
|
75
|
+
main "$@"
|
|
76
|
+
fi
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
###
|
|
3
|
+
# @Author : xiaomingming wujixmm@gmail.com
|
|
4
|
+
# @Date : 2026-07-17 13:21:03
|
|
5
|
+
# @LastEditors : xiaomingming wujixmm@gmail.com
|
|
6
|
+
# @LastEditTime : 2026-07-17 13:21:22
|
|
7
|
+
# @FilePath : /add-coder/templates/core/hooks/lib/subagent-stop.sh
|
|
8
|
+
# @Description : SubagentStop 治理卡位
|
|
9
|
+
###
|
|
10
|
+
# subagent-stop.sh — SubagentStop 治理卡位
|
|
11
|
+
# 路径: templates/core/hooks/lib/subagent-stop.sh
|
|
12
|
+
#
|
|
13
|
+
# 治理职能(卡位 #11):
|
|
14
|
+
# ① 子 agent 结果边界校验: 检查交付物是否在 spec 边界内(不越界、不遗漏)
|
|
15
|
+
# ② 审计聚合: 将子 agent 的 sub-traceId 审计记录合并回主 traceId
|
|
16
|
+
# ③ 阻断能力: 子 agent 结果不符合 spec → exit 2 要求重做
|
|
17
|
+
#
|
|
18
|
+
# 用法: 被各 IDE adapter 的 SubagentStop hook 脚本 source 并调用 main
|
|
19
|
+
|
|
20
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
21
|
+
source "$SCRIPT_DIR/common.sh" 2>/dev/null || true
|
|
22
|
+
|
|
23
|
+
# 边界校验: 检查子 agent 交付物是否超出 spec 定义的文件范围
|
|
24
|
+
# 参数: $1 = subagent_name, $2 = 子 agent 交付物文件列表(空格分隔)
|
|
25
|
+
check_boundary() {
|
|
26
|
+
local subagent_name="$1"
|
|
27
|
+
local deliverables="$2"
|
|
28
|
+
|
|
29
|
+
# 获取活跃 ADD 状态
|
|
30
|
+
local state=""
|
|
31
|
+
if type detect_active_add >/dev/null 2>&1; then
|
|
32
|
+
state=$(detect_active_add 2>/dev/null || true)
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
if [ -z "$state" ]; then
|
|
36
|
+
# 无活跃 Plan —— 无 spec 可参考,仅告警不阻断
|
|
37
|
+
echo "[ADD SubagentStop] ⚠️ 无活跃 ADD Plan,无法校验 $subagent_name 的交付物边界" >&2
|
|
38
|
+
return 0
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
local plan_kw=$(echo "$state" | awk -F'::' '{print $1}')
|
|
42
|
+
local handoff=$(echo "$state" | awk -F'::' '{print $4}')
|
|
43
|
+
|
|
44
|
+
echo "[ADD SubagentStop] $subagent_name 已完成,交付物: $deliverables" >&2
|
|
45
|
+
echo "[ADD SubagentStop] 关联 Plan: $plan_kw | handoff: $handoff" >&2
|
|
46
|
+
|
|
47
|
+
# best-effort: 如果 handoff 中列出了允许的文件范围,检查交付物是否在其中
|
|
48
|
+
if [ -f "$handoff" ] && [ -n "$deliverables" ]; then
|
|
49
|
+
local violations=""
|
|
50
|
+
for f in $deliverables; do
|
|
51
|
+
if ! grep -q "$f" "$handoff" 2>/dev/null; then
|
|
52
|
+
violations="${violations} $f"
|
|
53
|
+
fi
|
|
54
|
+
done
|
|
55
|
+
if [ -n "$violations" ]; then
|
|
56
|
+
echo "[ADD SubagentStop] ❌ $subagent_name 交付物超出 spec 边界: $violations" >&2
|
|
57
|
+
echo "[ADD SubagentStop] 请检查这些文件是否属于本轮 spec 范围,或更新 handoff 文件清单" >&2
|
|
58
|
+
return 1
|
|
59
|
+
fi
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
return 0
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
main() {
|
|
66
|
+
local subagent_name="${1:-unknown}"
|
|
67
|
+
|
|
68
|
+
# 从 stdin 解析 JSON(hook 事件传入)
|
|
69
|
+
local input=$(parse_input 2>/dev/null || echo "{}")
|
|
70
|
+
local deliverables=""
|
|
71
|
+
|
|
72
|
+
# 尝试从 stdin JSON 提取交付物信息
|
|
73
|
+
local agent_type=$(echo "$input" | grep -o '"agent_type"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | sed 's/.*: *"\([^"]*\)".*/\1/' || echo "")
|
|
74
|
+
[ -n "$agent_type" ] && subagent_name="${subagent_name}($agent_type)"
|
|
75
|
+
|
|
76
|
+
# ① 边界校验
|
|
77
|
+
if ! check_boundary "$subagent_name" "$deliverables"; then
|
|
78
|
+
echo "[ADD SubagentStop] 要求重做——交付物超出 spec 边界" >&2
|
|
79
|
+
exit "${EXIT_BLOCK:-2}"
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# ② 审计聚合(输出到 stderr 供日志记录)
|
|
83
|
+
echo "[ADD SubagentStop] $subagent_name 边界校验通过 — $(date -Iseconds)" >&2
|
|
84
|
+
|
|
85
|
+
exit 0
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
|
|
89
|
+
main "$@"
|
|
90
|
+
fi
|
|
@@ -1,4 +1,33 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
# Notification — Review 提醒 + Token 预警(Claude Code 适配)
|
|
3
|
+
# 治理卡位 #12: 开发提醒/Token 预警
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
input=$(cat)
|
|
7
|
+
ntype=$(echo "$input" | jq -r '.notification_type // ""' 2>/dev/null || echo "")
|
|
8
|
+
|
|
9
|
+
if [ "$ntype" != "result" ]; then
|
|
10
|
+
exit 0
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
14
|
+
export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
|
|
15
|
+
COMMON_LIB="$HOOK_DIR/lib/common.sh"
|
|
16
|
+
[ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
|
|
17
|
+
|
|
18
|
+
export PROJECT_DIR="$PWD"
|
|
19
|
+
|
|
20
|
+
state=$(detect_active_add 2>/dev/null || true)
|
|
21
|
+
[ -z "$state" ] && exit 0
|
|
22
|
+
|
|
23
|
+
IFS='::' read -r plan _ _ _ _ <<< "$state"
|
|
24
|
+
|
|
25
|
+
reviews_dir="${PROJECT_DIR}/.qoder/reviews"
|
|
26
|
+
if ls "$reviews_dir"/*.md >/dev/null 2>&1; then
|
|
27
|
+
echo "[ADD Notification] Plan: ${plan} — 请检查 Review 文档: ${reviews_dir}"
|
|
28
|
+
fi
|
|
29
|
+
exit 0
|
|
30
|
+
#!/bin/bash
|
|
2
31
|
# Notification — 通知事件处理(Claude Code 适配)
|
|
3
32
|
set -euo pipefail
|
|
4
33
|
|
|
@@ -1,18 +1,35 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# post-tool-use.sh — Claude Code PostToolUse 审计提醒
|
|
2
|
+
# post-tool-use.sh — Claude Code PostToolUse:格式化 + 文档守卫 + 审计提醒
|
|
3
|
+
# 治理卡位 #5: 格式化 + ADD文档守卫 + 审计落库 + 结果增强
|
|
3
4
|
set -euo pipefail
|
|
4
5
|
|
|
5
6
|
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
6
|
-
|
|
7
|
-
[ -f "$
|
|
7
|
+
COMMON_LIB="$HOOK_DIR/lib/common.sh"
|
|
8
|
+
[ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
|
|
8
9
|
|
|
9
10
|
input=$(parse_input)
|
|
10
|
-
|
|
11
|
-
[ -z "$
|
|
11
|
+
tool_name=$(json_get "$input" "tool_name")
|
|
12
|
+
[ -z "$tool_name" ] && tool_name=$(echo "$input" | grep -o '"tool_name"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | sed 's/.*: *"\([^"]*\)".*/\1/' || echo "")
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
# ── Edit/Write matcher: 格式化 + ADD文档守卫 ──
|
|
15
|
+
if [ "$tool_name" = "Edit" ] || [ "$tool_name" = "Write" ]; then
|
|
16
|
+
file_path=$(echo "$input" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | sed 's/.*: *"\([^"]*\)".*/\1/' || echo "")
|
|
17
|
+
[ -z "$file_path" ] && exit 0
|
|
18
|
+
|
|
19
|
+
# ADD 文档结构守卫:写入 plans/specs/reviews 后提示
|
|
20
|
+
if echo "$file_path" | grep -qE '\.(qoder|claude|add)/(plans|specs|reviews)/'; then
|
|
21
|
+
echo "[ADD PostToolUse] ADD 文档已写入: ${file_path}。请确保章节完整、双向链接齐全、增量修订格式正确。" >&2
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# 审计提醒(所有文件写入)
|
|
25
|
+
echo "[ADD PostToolUse] 文件已写入: ${file_path}。请执行 record_dev_operation 落库审计(ADD-7)。" >&2
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# ── Bash matcher: 结果增强 ──
|
|
30
|
+
if [ "$tool_name" = "Bash" ]; then
|
|
31
|
+
echo "[ADD PostToolUse] 命令执行完成。如有 lint/tsc 错误请修复。" >&2
|
|
14
32
|
exit 0
|
|
15
33
|
fi
|
|
16
34
|
|
|
17
|
-
|
|
18
|
-
exit 0
|
|
35
|
+
exit 0
|
|
@@ -1,14 +1,37 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
2
|
+
# pre-compact.sh — 上下文压缩前 ADD 状态保存(Claude Code 适配)
|
|
3
|
+
# 治理卡位 #9: ADD状态保存 + 恢复清单导出 + tpl标记清理
|
|
3
4
|
set -euo pipefail
|
|
4
5
|
|
|
5
6
|
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
|
|
8
|
+
COMMON_LIB="$HOOK_DIR/lib/common.sh"
|
|
9
|
+
[ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
11
|
-
[ -d "$PROJECT_DIR/.qoder/plans" ] || exit 0
|
|
11
|
+
export PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
# ── ① 获取 ADD 状态并保存到标记文件 ──
|
|
14
|
+
if type detect_active_add >/dev/null 2>&1; then
|
|
15
|
+
state=$(detect_active_add 2>/dev/null || true)
|
|
16
|
+
if [ -n "$state" ]; then
|
|
17
|
+
IFS='::' read -r plan step rounds handoff add_route <<< "$state"
|
|
18
|
+
|
|
19
|
+
# 写入恢复标记文件(SessionStart 恢复时读取)
|
|
20
|
+
RECOVERY_FILE="/tmp/add_recovery_$(echo "${PROJECT_DIR}" | md5sum 2>/dev/null | cut -c1-8 || echo "default")"
|
|
21
|
+
cat > "$RECOVERY_FILE" <<EOF
|
|
22
|
+
plan=${plan}
|
|
23
|
+
step=${step}
|
|
24
|
+
rounds=${rounds}
|
|
25
|
+
handoff=${handoff}
|
|
26
|
+
add_route=${add_route}
|
|
27
|
+
EOF
|
|
28
|
+
|
|
29
|
+
echo "[ADD PreCompact] ADD 状态已保存: Plan=${plan}, Step=${step}" >&2
|
|
30
|
+
fi
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# ── ② 清理 tpl-injected 标记(compact 后上下文丢失,下次需重注) ──
|
|
34
|
+
TPL_FLAG="/tmp/add_tpl_$(echo "${PROJECT_DIR}" | md5sum 2>/dev/null | cut -c1-8 || echo "default")"
|
|
35
|
+
rm -f "$TPL_FLAG" 2>/dev/null || true
|
|
36
|
+
|
|
37
|
+
exit 0
|
|
@@ -1,28 +1,78 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# pre-tool-use.sh — Claude Code PreToolUse
|
|
3
|
-
#
|
|
2
|
+
# pre-tool-use.sh — Claude Code PreToolUse:四路守卫
|
|
3
|
+
# 治理卡位 #4: 危险命令拦截 / 模板路径兜底 / 写入前置守卫 / 敏感文件保护
|
|
4
4
|
set -euo pipefail
|
|
5
5
|
|
|
6
6
|
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
|
|
8
|
+
COMMON_LIB="$HOOK_DIR/lib/common.sh"
|
|
9
|
+
[ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
|
|
9
10
|
|
|
10
11
|
input=$(parse_input)
|
|
11
|
-
|
|
12
|
-
[ -z "$
|
|
12
|
+
tool_name=$(json_get "$input" "tool_name")
|
|
13
|
+
[ -z "$tool_name" ] && tool_name=$(echo "$input" | grep -o '"tool_name"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | sed 's/.*: *"\([^"]*\)".*/\1/' || echo "")
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
export PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
16
|
+
|
|
17
|
+
# ── ① Bash matcher: 危险命令拦截 + 终端写文件拦截 ──
|
|
18
|
+
if [ "$tool_name" = "Bash" ]; then
|
|
19
|
+
cmd=$(echo "$input" | grep -o '"command"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | sed 's/.*: *"\([^"]*\)".*/\1/' || echo "")
|
|
20
|
+
# 危险命令
|
|
21
|
+
if echo "$cmd" | grep -qiE 'rm[[:space:]]+-rf[[:space:]]+/|DROP[[:space:]]+TABLE|git[[:space:]]+push[[:space:]]+--force|mkfs\.|dd[[:space:]]+if='; then
|
|
22
|
+
echo "⛔ 危险命令已被阻止: $cmd" >&2
|
|
23
|
+
exit $EXIT_BLOCK
|
|
24
|
+
fi
|
|
25
|
+
# 终端写文件拦截(含 > / >> / << heredoc / mv / touch / python -c > file)
|
|
26
|
+
if echo "$cmd" | grep -qE '(cat|echo|tee|sed[[:space:]]+-i|awk|printf|cp|mv|dd|touch)[[:space:]]*.*([>]{1,2}|[|][[:space:]]*tee|<<)'; then
|
|
27
|
+
echo "⛔ 禁止通过终端命令直接写文件: $cmd。请使用 Write/Edit/SearchReplace 工具。" >&2
|
|
28
|
+
exit $EXIT_BLOCK
|
|
29
|
+
fi
|
|
30
|
+
# mv 无重定向但仍操作文件
|
|
31
|
+
if echo "$cmd" | grep -qE '^[[:space:]]*mv[[:space:]]+/tmp/'; then
|
|
32
|
+
echo "⛔ 禁止通过 mv /tmp/ 绕过 IDE 工具: $cmd" >&2
|
|
33
|
+
exit $EXIT_BLOCK
|
|
34
|
+
fi
|
|
35
|
+
# python/node 脚本写文件
|
|
36
|
+
if echo "$cmd" | grep -qE '(python3|python|node)[[:space:]].*[>]{1,2}'; then
|
|
37
|
+
echo "⛔ 禁止通过脚本语言直接写文件: $cmd。请使用 Write/Edit/SearchReplace 工具。" >&2
|
|
38
|
+
exit $EXIT_BLOCK
|
|
39
|
+
fi
|
|
40
|
+
mark_dev_action
|
|
15
41
|
exit 0
|
|
16
42
|
fi
|
|
17
43
|
|
|
18
|
-
|
|
19
|
-
|
|
44
|
+
# ── ② Write/Edit matcher: 文件写入前置守卫 ──
|
|
45
|
+
if [ "$tool_name" = "Write" ] || [ "$tool_name" = "Edit" ]; then
|
|
46
|
+
file_path=$(echo "$input" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | sed 's/.*: *"\([^"]*\)".*/\1/' || echo "")
|
|
47
|
+
[ -z "$file_path" ] && exit 0
|
|
48
|
+
|
|
49
|
+
# 检查是否写入 plans/specs/reviews 目录但无活跃 ADD Plan
|
|
50
|
+
if echo "$file_path" | grep -qE '\.(qoder|claude|add)/plans/|\.(qoder|claude|add)/specs/|\.(qoder|claude|add)/reviews/'; then
|
|
51
|
+
if type detect_active_add >/dev/null 2>&1; then
|
|
52
|
+
state=$(detect_active_add 2>/dev/null || true)
|
|
53
|
+
if [ -z "$state" ]; then
|
|
54
|
+
echo "[ADD PreToolUse] ⚠️ 正在写入 Plan/Spec/Review 文档但无活跃 ADD Plan——请先执行 add-paradigm" >&2
|
|
55
|
+
fi
|
|
56
|
+
fi
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# 敏感文件保护
|
|
60
|
+
if echo "$file_path" | grep -qE '\.env$|\.env\.production$|\.env\.local$|credentials|secrets'; then
|
|
61
|
+
echo "⛔ 敏感文件受保护,禁止写入: $file_path" >&2
|
|
62
|
+
exit $EXIT_BLOCK
|
|
63
|
+
fi
|
|
20
64
|
|
|
21
|
-
|
|
65
|
+
mark_dev_action
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
22
68
|
|
|
23
|
-
|
|
69
|
+
# ── ③ Read matcher: 模板路径兜底 ──
|
|
70
|
+
if [ "$tool_name" = "Read" ]; then
|
|
71
|
+
file_path=$(echo "$input" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | sed 's/.*: *"\([^"]*\)".*/\1/' || echo "")
|
|
72
|
+
if echo "$file_path" | grep -q 'templates/'; then
|
|
73
|
+
echo "[ADD PreToolUse] 提示: 模板文件已通过 hook 预读到上下文,可跳过重复读取" >&2
|
|
74
|
+
fi
|
|
24
75
|
exit 0
|
|
25
76
|
fi
|
|
26
77
|
|
|
27
|
-
|
|
28
|
-
exit $EXIT_BLOCK
|
|
78
|
+
exit 0
|
|
@@ -1,16 +1,78 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# UserPromptSubmit —
|
|
2
|
+
# UserPromptSubmit — 触发词路由 + 模板全文注入(Claude Code 适配)
|
|
3
|
+
# 治理卡位 #3: Layer 1 精准触发 → Layer 2 阻断 → Layer 3 状态注入
|
|
3
4
|
set -euo pipefail
|
|
4
5
|
|
|
5
6
|
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
|
|
8
|
+
|
|
9
|
+
COMMON_LIB="$HOOK_DIR/lib/common.sh"
|
|
10
|
+
[ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
|
|
11
|
+
|
|
12
|
+
export PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
8
13
|
|
|
9
14
|
input=$(parse_input)
|
|
10
|
-
prompt=$(echo "$input" |
|
|
15
|
+
prompt=$(echo "$input" | grep -o '"prompt"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | sed 's/.*: *"\([^"]*\)".*/\1/' || echo "")
|
|
16
|
+
[ -z "$prompt" ] && exit 0
|
|
17
|
+
|
|
18
|
+
# ─── Layer 1: 精准触发词路由 + 验收幂等保护 ───
|
|
19
|
+
matched=$(match_trigger "$prompt")
|
|
20
|
+
if [ -n "$matched" ]; then
|
|
21
|
+
if echo "$prompt" | grep -qiE '验收|收敛' 2>/dev/null; then
|
|
22
|
+
add_state=$(detect_active_add 2>/dev/null || true)
|
|
23
|
+
if [ -n "$add_state" ]; then
|
|
24
|
+
_handoff=$(echo "$add_state" | awk -F'::' '{print $4}')
|
|
25
|
+
_add_route=$(echo "$add_state" | awk -F'::' '{print $5}')
|
|
26
|
+
if is_already_accepted "$_add_route" "$_handoff" 2>/dev/null; then
|
|
27
|
+
cat <<EOF
|
|
28
|
+
[ADD 验收] ⚠️ 已验收。进入 Review 模式:
|
|
29
|
+
① 重新检查 checklist [T]/[R] 项
|
|
30
|
+
② 审查 audit 记录完整性
|
|
31
|
+
③ 如有差异 → Review 回流至 handoff(增量更新)
|
|
32
|
+
④ 无差异 → 记录 'Review 已确认,无新发现'
|
|
33
|
+
EOF
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
fi
|
|
37
|
+
fi
|
|
38
|
+
echo "$matched"
|
|
39
|
+
exit 0
|
|
40
|
+
fi
|
|
11
41
|
|
|
12
|
-
#
|
|
13
|
-
if echo "$prompt" | grep -qiE '开发|改功能|修.?bug
|
|
14
|
-
|
|
42
|
+
# ─── 开发关键词检测 ───
|
|
43
|
+
if ! echo "$prompt" | grep -qiE '开发|改功能|修.?bug|加需求|新增|重构|实施|验收|继续|生成plan|生成计划'; then
|
|
44
|
+
exit 0
|
|
15
45
|
fi
|
|
16
|
-
|
|
46
|
+
|
|
47
|
+
echo "[ADD PromptSubmit] 检测到开发关键词" >&2
|
|
48
|
+
|
|
49
|
+
# ─── Layer 2: 无活跃 ADD → 阻断 ───
|
|
50
|
+
state=$(detect_active_add 2>/dev/null || true)
|
|
51
|
+
if [ -z "$state" ]; then
|
|
52
|
+
cat <<'EOF'
|
|
53
|
+
[ADD 强制规则] 检测到开发任务。你必须先执行 add-paradigm SKILL 完成 ADD 工作流:
|
|
54
|
+
Step 0: 文档先行 (Plan → Review → Specs)
|
|
55
|
+
Step 3: 代码实现 + 审计植入
|
|
56
|
+
Step 8: 收敛判断
|
|
57
|
+
如果 add-paradigm SKILL 尚未激活,请先调用它。
|
|
58
|
+
EOF
|
|
59
|
+
exit 2
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# ─── Layer 3: 有活跃 ADD → 注入状态 + 模板全文 ───
|
|
63
|
+
IFS='::' read -r plan step rounds handoff add_route <<< "$state"
|
|
64
|
+
cat <<EOF
|
|
65
|
+
[ADD 当前状态]
|
|
66
|
+
Plan: ${plan}
|
|
67
|
+
轮次: ${rounds}
|
|
68
|
+
当前 Step: ${step}
|
|
69
|
+
handoff: ${handoff}
|
|
70
|
+
EOF
|
|
71
|
+
|
|
72
|
+
# 模板全文注入(tpl-injected 去重)
|
|
73
|
+
TPL_SCRIPT="$HOOK_DIR/lib/preload-templates.sh"
|
|
74
|
+
if [ -f "$TPL_SCRIPT" ]; then
|
|
75
|
+
bash "$TPL_SCRIPT" --full --top 5
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
exit 0
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SessionEnd — 通用版(核心模板,Trae 等适配器继承)
|
|
3
|
+
# 治理卡位 #2: 标记清理 + 会话审计结算 + Stop未触发兜底
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
COMMON_LIB="$HOOK_DIR/lib/common.sh"
|
|
8
|
+
[ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
|
|
9
|
+
|
|
10
|
+
export PROJECT_DIR="$PWD"
|
|
11
|
+
|
|
12
|
+
# ── ① 清理 tpl-injected 标记 ──
|
|
13
|
+
project_hash=$(echo "${PROJECT_DIR:-$PWD}" | md5sum 2>/dev/null | cut -c1-8 || echo "default")
|
|
14
|
+
rm -f "/tmp/add_tpl_${project_hash}" 2>/dev/null || true
|
|
15
|
+
|
|
16
|
+
# ── ② 清理 dev action 标记 ──
|
|
17
|
+
rm -f "/tmp/add_dev_${project_hash}" 2>/dev/null || true
|
|
18
|
+
|
|
19
|
+
# ── ③ Stop 未触发兜底: 如果 dev action 标记还在,补执行 checklist 快照 ──
|
|
20
|
+
if has_dev_action 2>/dev/null; then
|
|
21
|
+
echo "[ADD SessionEnd] ⚠️ 检测到 dev action 标记未清除——Stop 可能未触发验收检查" >&2
|
|
22
|
+
|
|
23
|
+
state=$(detect_active_add 2>/dev/null || true)
|
|
24
|
+
if [ -n "$state" ]; then
|
|
25
|
+
handoff=$(echo "$state" | awk -F'::' '{print $4}')
|
|
26
|
+
add_route=$(echo "$state" | awk -F'::' '{print $5}')
|
|
27
|
+
if [ -f "$handoff" ]; then
|
|
28
|
+
echo "[ADD SessionEnd] 补执行 checklist 快照(best-effort,不阻断)" >&2
|
|
29
|
+
if type check_add_completeness >/dev/null 2>&1; then
|
|
30
|
+
check_add_completeness "$handoff" "$add_route" >&2 2>/dev/null || true
|
|
31
|
+
fi
|
|
32
|
+
fi
|
|
33
|
+
fi
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# ── ④ 审计结算 ──
|
|
37
|
+
echo "[ADD SessionEnd] 会话结束 — $(date -Iseconds)" >&2
|
|
38
|
+
exit 0
|
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# SessionStart — ADD
|
|
2
|
+
# SessionStart — ADD 上下文恢复 + 模板索引注入(Claude Code 适配)
|
|
3
|
+
# 治理卡位 #1: ADD状态恢复 + 模板索引注入
|
|
3
4
|
set -euo pipefail
|
|
4
5
|
|
|
5
6
|
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
6
|
-
|
|
7
|
-
[ -f "$SHARED_LIB" ] && source "$SHARED_LIB"
|
|
7
|
+
export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
|
|
8
8
|
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
[ -f "$
|
|
12
|
-
[ -f "$QODER_LIB/context-inject.sh" ] && source "$QODER_LIB/context-inject.sh"
|
|
9
|
+
# 加载四端通用函数(路径统一后: .claude/hooks/lib/common.sh)
|
|
10
|
+
COMMON_LIB="$HOOK_DIR/lib/common.sh"
|
|
11
|
+
[ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
# 设置项目目录
|
|
14
|
+
export PROJECT_DIR="$PWD"
|
|
15
15
|
|
|
16
|
+
# ── ① ADD 状态恢复 ──
|
|
16
17
|
state=$(detect_active_add 2>/dev/null || true)
|
|
17
|
-
if [ -
|
|
18
|
-
|
|
18
|
+
if [ -n "$state" ]; then
|
|
19
|
+
IFS='::' read -r plan step rounds handoff add_route <<< "$state"
|
|
20
|
+
cat <<EOF
|
|
21
|
+
[ADD SessionStart] 检测到活跃 ADD Plan:
|
|
22
|
+
Plan: ${plan}
|
|
23
|
+
轮次: ${rounds}
|
|
24
|
+
当前 Step: ${step}
|
|
25
|
+
handoff: ${handoff}
|
|
26
|
+
恢复命令: query_audit_logs({ planKeyword: '${plan}' })
|
|
27
|
+
EOF
|
|
19
28
|
fi
|
|
20
29
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
30
|
+
# ── ② 模板索引注入 ──
|
|
31
|
+
TPL_SCRIPT="$HOOK_DIR/lib/preload-templates.sh"
|
|
32
|
+
if [ -f "$TPL_SCRIPT" ]; then
|
|
33
|
+
bash "$TPL_SCRIPT" --index
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
exit 0
|
|
@@ -1,10 +1,45 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
2
|
+
# stop-check.sh — Claude Code Stop:四象限分流 + 验收检查(few-shot 上下文注入)
|
|
3
|
+
# 治理卡位 #7: 验收检查 + devlog + 阻断
|
|
3
4
|
set -euo pipefail
|
|
4
5
|
|
|
5
6
|
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
|
|
8
|
+
COMMON_LIB="$HOOK_DIR/lib/common.sh"
|
|
9
|
+
[ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
export PROJECT_DIR="$PWD"
|
|
12
|
+
|
|
13
|
+
state=$(detect_active_add 2>/dev/null || true)
|
|
14
|
+
has_dev=$(has_dev_action 2>/dev/null && echo "true" || echo "false")
|
|
15
|
+
|
|
16
|
+
# ═══════ Q1: 无 ADD + 无 dev → 正常停 ═══════
|
|
17
|
+
if [ -z "$state" ] && [ "$has_dev" != "true" ]; then
|
|
18
|
+
exit 0
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# ═══════ Q2: 无 ADD + 有 dev → 严重违规,few-shot 注入 ═══════
|
|
22
|
+
if [ -z "$state" ] && [ "$has_dev" = "true" ]; then
|
|
23
|
+
build_stop_context "no_add_has_dev" >&2
|
|
24
|
+
exit $EXIT_BLOCK
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
IFS='::' read -r plan step rounds handoff add_route <<< "$state"
|
|
28
|
+
|
|
29
|
+
# ═══════ Q3: 有 ADD + 无 dev → 注入状态 ═══════
|
|
30
|
+
if [ "$has_dev" != "true" ]; then
|
|
31
|
+
echo "[ADD Stop] Plan: ${plan}, 轮次: ${rounds}, Step: ${step}"
|
|
32
|
+
echo "本次无代码改动。下次继续时执行 session-init 恢复上下文。"
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
|
|
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
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
clear_dev_action 2>/dev/null || true
|
|
44
|
+
echo "[ADD Stop] ✅ 验收通过——checklist 全部勾选,devlog 已记录。"
|
|
45
|
+
exit 0
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SubagentStop — 通用版(核心模板,Trae 等适配器继承)
|
|
3
|
+
# 治理卡位 #11: 子agent边界校验 + 审计聚合 + 阻断
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
COMMON_LIB="$HOOK_DIR/lib/common.sh"
|
|
8
|
+
[ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
|
|
9
|
+
|
|
10
|
+
export PROJECT_DIR="$PWD"
|
|
11
|
+
|
|
12
|
+
input=$(cat 2>/dev/null || echo "{}")
|
|
13
|
+
agent_name=$(echo "$input" | jq -r '.agent_type // .subagent_name // "unknown"' 2>/dev/null || echo "unknown")
|
|
14
|
+
|
|
15
|
+
# ── ① 边界校验:检查子 agent 交付物是否超出 spec 范围 ──
|
|
16
|
+
state=$(detect_active_add 2>/dev/null || true)
|
|
17
|
+
if [ -z "$state" ]; then
|
|
18
|
+
echo "[ADD SubagentStop] ⚠️ ${agent_name} 已完成,但无活跃 ADD Plan 无法校验边界" >&2
|
|
19
|
+
exit 0
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
plan_kw=$(echo "$state" | awk -F'::' '{print $1}')
|
|
23
|
+
handoff=$(echo "$state" | awk -F'::' '{print $4}')
|
|
24
|
+
|
|
25
|
+
echo "[ADD SubagentStop] ${agent_name} 已完成 — 关联 Plan: ${plan_kw}" >&2
|
|
26
|
+
|
|
27
|
+
# 如果 handoff 中存在允许的文件清单,检查交付物是否越界
|
|
28
|
+
if [ -f "$handoff" ]; then
|
|
29
|
+
deliverables=$(echo "$input" | jq -r '.deliverables // ""' 2>/dev/null || echo "")
|
|
30
|
+
if [ -n "$deliverables" ]; then
|
|
31
|
+
violations=""
|
|
32
|
+
for f in $deliverables; do
|
|
33
|
+
if ! grep -q "$f" "$handoff" 2>/dev/null; then
|
|
34
|
+
violations="${violations} $f"
|
|
35
|
+
fi
|
|
36
|
+
done
|
|
37
|
+
if [ -n "$violations" ]; then
|
|
38
|
+
echo "[ADD SubagentStop] ❌ ${agent_name} 交付物超出 spec 边界: ${violations}" >&2
|
|
39
|
+
echo "[ADD SubagentStop] 要求重做——请检查这些文件是否属于本轮 spec 范围" >&2
|
|
40
|
+
exit "${EXIT_BLOCK:-2}"
|
|
41
|
+
fi
|
|
42
|
+
fi
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# ── ② 审计聚合 ──
|
|
46
|
+
echo "[ADD SubagentStop] ${agent_name} 边界校验通过 — $(date -Iseconds)" >&2
|
|
47
|
+
exit 0
|