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.
Files changed (102) hide show
  1. package/README.en.md +13 -3
  2. package/README.md +9 -8
  3. package/dist/index.js +73 -99
  4. package/package.json +5 -1
  5. package/templates/adapters/claude/hooks/doc-format-guard.sh +164 -9
  6. package/templates/adapters/claude/hooks/notification.sh +29 -0
  7. package/templates/adapters/claude/hooks/permission-denied.sh +42 -0
  8. package/templates/adapters/claude/hooks/post-tool-use.sh +25 -8
  9. package/templates/adapters/claude/hooks/pre-compact.sh +31 -8
  10. package/templates/adapters/claude/hooks/pre-tool-use.sh +63 -13
  11. package/templates/adapters/claude/hooks/prompt-submit.sh +70 -8
  12. package/templates/adapters/claude/hooks/session-end.sh +21 -0
  13. package/templates/adapters/claude/hooks/session-start.sh +26 -13
  14. package/templates/adapters/claude/hooks/stop-check.sh +40 -5
  15. package/templates/adapters/claude/hooks/stop-failure.sh +44 -0
  16. package/templates/adapters/claude/hooks/subagent-guard.sh +29 -8
  17. package/templates/adapters/claude/hooks/subagent-stop.sh +20 -0
  18. package/templates/adapters/codex/hooks/notification.sh +38 -0
  19. package/templates/adapters/codex/hooks/post-tool-use.sh +35 -0
  20. package/templates/adapters/codex/hooks/pre-tool-use.sh +78 -0
  21. package/templates/adapters/codex/hooks/prompt-submit.sh +78 -0
  22. package/templates/adapters/codex/hooks/session-start.sh +36 -0
  23. package/templates/adapters/codex/hooks/stop-check.sh +45 -0
  24. package/templates/adapters/codex/hooks.json +1 -0
  25. package/templates/adapters/codex/settings.json +1 -0
  26. package/templates/adapters/qoder/hooks/doc-format-guard.sh +9 -1
  27. package/templates/adapters/qoder/hooks/lib/vocabulary.sh +1 -1
  28. package/templates/adapters/qoder/hooks/notification.sh +8 -5
  29. package/templates/adapters/qoder/hooks/post-tool-use.sh +21 -11
  30. package/templates/adapters/qoder/hooks/pre-compact.sh +31 -6
  31. package/templates/adapters/qoder/hooks/pre-tool-use.sh +66 -64
  32. package/templates/adapters/qoder/hooks/prompt-submit.sh +12 -2
  33. package/templates/adapters/qoder/hooks/session-end.sh +24 -0
  34. package/templates/adapters/qoder/hooks/session-start.sh +22 -7
  35. package/templates/adapters/qoder/hooks/stop-check.sh +18 -41
  36. package/templates/adapters/qoder/hooks/subagent-guard.sh +22 -6
  37. package/templates/adapters/qoder/hooks/subagent-stop.sh +26 -0
  38. package/templates/adapters/qoder/settings.json +13 -13
  39. package/templates/adapters/trae/hooks/notification.sh +38 -0
  40. package/templates/adapters/trae/hooks/post-tool-failure.sh +10 -0
  41. package/templates/adapters/trae/hooks/post-tool-use.sh +35 -0
  42. package/templates/adapters/trae/hooks/pre-compact.sh +37 -0
  43. package/templates/adapters/trae/hooks/pre-tool-use.sh +78 -0
  44. package/templates/adapters/trae/hooks/prompt-submit.sh +78 -0
  45. package/templates/adapters/trae/hooks/session-end.sh +21 -0
  46. package/templates/adapters/trae/hooks/session-start.sh +36 -0
  47. package/templates/adapters/trae/hooks/stop-check.sh +45 -0
  48. package/templates/adapters/trae/hooks/subagent-guard.sh +36 -0
  49. package/templates/adapters/trae/hooks/subagent-stop.sh +20 -0
  50. package/templates/adapters/trae/hooks.json +1 -0
  51. package/templates/adapters/trae/settings.json +1 -0
  52. package/templates/adapters/vscode/.github/hooks/error-occurred.json +1 -0
  53. package/templates/adapters/vscode/.github/hooks/post-tool-use.json +1 -0
  54. package/templates/adapters/vscode/.github/hooks/pre-compact.json +1 -0
  55. package/templates/adapters/vscode/.github/hooks/pre-tool-use.json +14 -0
  56. package/templates/adapters/vscode/.github/hooks/session-end.json +1 -0
  57. package/templates/adapters/vscode/.github/hooks/session-start.json +14 -0
  58. package/templates/adapters/vscode/.github/hooks/stop-check.json +1 -0
  59. package/templates/adapters/vscode/.github/hooks/subagent-start.json +1 -0
  60. package/templates/adapters/vscode/.github/hooks/subagent-stop.json +1 -0
  61. package/templates/adapters/vscode/.github/hooks/user-prompt-submit.json +14 -0
  62. package/templates/adapters/vscode/hooks/notification.sh +37 -0
  63. package/templates/adapters/vscode/hooks/post-tool-failure.sh +28 -0
  64. package/templates/adapters/vscode/hooks/post-tool-use.sh +35 -0
  65. package/templates/adapters/vscode/hooks/pre-compact.sh +37 -0
  66. package/templates/adapters/vscode/hooks/pre-tool-use.sh +78 -0
  67. package/templates/adapters/vscode/hooks/prompt-submit.sh +78 -0
  68. package/templates/adapters/vscode/hooks/session-end.sh +13 -0
  69. package/templates/adapters/vscode/hooks/session-start.sh +35 -0
  70. package/templates/adapters/vscode/hooks/stop-check.sh +53 -0
  71. package/templates/adapters/vscode/hooks/subagent-guard.sh +37 -0
  72. package/templates/adapters/vscode/hooks/subagent-stop.sh +12 -0
  73. package/templates/adapters/vscode/launch.json +1 -1
  74. package/templates/adapters/vscode/settings.json +2 -3
  75. package/templates/adapters/vscode/tasks.json +3 -3
  76. package/templates/core/docs/ADD-governance-claude-code.md +121 -0
  77. package/templates/core/docs/ADD-governance-codex.md +75 -0
  78. package/templates/core/docs/ADD-governance-qoder-cn.md +99 -0
  79. package/templates/core/docs/ADD-governance-trae.md +63 -0
  80. package/templates/core/docs/ADD-governance-vscode-copilot.md +132 -0
  81. package/templates/core/hooks/doc-format-guard.sh +164 -9
  82. package/templates/core/hooks/lib/common.sh +261 -0
  83. package/templates/core/hooks/lib/preload-templates.sh +187 -0
  84. package/templates/core/hooks/lib/session-end.sh +76 -0
  85. package/templates/core/hooks/lib/subagent-stop.sh +90 -0
  86. package/templates/core/hooks/notification.sh +29 -0
  87. package/templates/core/hooks/post-tool-use.sh +25 -8
  88. package/templates/core/hooks/pre-compact.sh +31 -8
  89. package/templates/core/hooks/pre-tool-use.sh +63 -13
  90. package/templates/core/hooks/prompt-submit.sh +70 -8
  91. package/templates/core/hooks/session-end.sh +38 -0
  92. package/templates/core/hooks/session-start.sh +26 -13
  93. package/templates/core/hooks/stop-check.sh +40 -5
  94. package/templates/core/hooks/subagent-stop.sh +47 -0
  95. package/templates/core/rules/project_rules.md +1 -1
  96. package/templates/core/scripts/db-ensure.sh +6 -5
  97. package/templates/core/scripts/mcp-server.ts +38 -120
  98. package/templates/core/skills/add-paradigm/SKILL.md +24 -0
  99. package/templates/core/templates/standard-plan-template.md +39 -13
  100. package/templates/core/hooks/lib/context-inject.sh +0 -96
  101. package/templates/core/hooks/lib/state-detect.sh +0 -104
  102. package/templates/core/hooks/lib/vocabulary.sh +0 -49
@@ -0,0 +1,78 @@
1
+ #!/bin/bash
2
+ # pre-tool-use.sh — Claude Code PreToolUse:四路守卫
3
+ # 治理卡位 #4: 危险命令拦截 / 模板路径兜底 / 写入前置守卫 / 敏感文件保护
4
+ set -euo pipefail
5
+
6
+ HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
7
+ export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
8
+ COMMON_LIB="$HOOK_DIR/lib/common.sh"
9
+ [ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
10
+
11
+ input=$(parse_input)
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 "")
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
41
+ exit 0
42
+ fi
43
+
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
64
+
65
+ mark_dev_action
66
+ exit 0
67
+ fi
68
+
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
75
+ exit 0
76
+ fi
77
+
78
+ exit 0
@@ -0,0 +1,78 @@
1
+ #!/bin/bash
2
+ # UserPromptSubmit — 触发词路由 + 模板全文注入(Claude Code 适配)
3
+ # 治理卡位 #3: Layer 1 精准触发 → Layer 2 阻断 → Layer 3 状态注入
4
+ set -euo pipefail
5
+
6
+ HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
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}"
13
+
14
+ input=$(parse_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
41
+
42
+ # ─── 开发关键词检测 ───
43
+ if ! echo "$prompt" | grep -qiE '开发|改功能|修.?bug|加需求|新增|重构|实施|验收|继续|生成plan|生成计划'; then
44
+ exit 0
45
+ fi
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,36 @@
1
+ #!/bin/bash
2
+ # SessionStart — ADD 上下文恢复 + 模板索引注入(Claude Code 适配)
3
+ # 治理卡位 #1: ADD状态恢复 + 模板索引注入
4
+ set -euo pipefail
5
+
6
+ HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
7
+ export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
8
+
9
+ # 加载四端通用函数(路径统一后: .claude/hooks/lib/common.sh)
10
+ COMMON_LIB="$HOOK_DIR/lib/common.sh"
11
+ [ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
12
+
13
+ # 设置项目目录
14
+ export PROJECT_DIR="$PWD"
15
+
16
+ # ── ① ADD 状态恢复 ──
17
+ state=$(detect_active_add 2>/dev/null || true)
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
28
+ fi
29
+
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
@@ -0,0 +1,45 @@
1
+ #!/bin/bash
2
+ # stop-check.sh — Claude Code Stop:四象限分流 + 验收检查(few-shot 上下文注入)
3
+ # 治理卡位 #7: 验收检查 + devlog + 阻断
4
+ set -euo pipefail
5
+
6
+ HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
7
+ export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
8
+ COMMON_LIB="$HOOK_DIR/lib/common.sh"
9
+ [ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
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 @@
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"}]}}
@@ -0,0 +1 @@
1
+ {}
@@ -84,10 +84,18 @@ if [ -z "$TEMPLATE_NAME" ]; then
84
84
  *plan*) TEMPLATE_NAME="standard-plan-template.md" ;;
85
85
  *add-route*heavy*) TEMPLATE_NAME="add-route-template-heavyweight.md" ;;
86
86
  *add-route*) TEMPLATE_NAME="add-route-template.md" ;;
87
+ *tasks*) TEMPLATE_NAME="tasks-template.md" ;;
88
+ *spec*) TEMPLATE_NAME="spec-template.md" ;;
89
+ *checklist*) TEMPLATE_NAME="checklist-template.md" ;;
87
90
  *report*runtime*) TEMPLATE_NAME="runtime-report-template.md" ;;
88
91
  *report*) TEMPLATE_NAME="report-template.md" ;;
89
92
  *fix-verif*) TEMPLATE_NAME="fix-verification-template.md" ;;
90
93
  *)
94
+ # 增量修订识别:包含 ~~删除线~~ / → 新增标记 / [修订日期] 任意一个 → 视为增量更新,放行
95
+ if echo "$CONTENT" | grep -qE '~~.+~~|→|\[[0-9]{4}-[0-9]{2}-[0-9]{2}\s+修订'; then
96
+ echo "[doc-format-guard] 检测到增量修订格式,跳过完整章节校验" >&2
97
+ exit 0
98
+ fi
91
99
  echo "⛔ 拒绝:无法识别文档类型 (file_path: $file_path),缺少模板匹配规则" >&2
92
100
  echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":"无法识别 ADD 文档类型,请联系管理员更新 doc-format-guard.sh"}}'
93
101
  exit 2
@@ -96,7 +104,7 @@ if [ -z "$TEMPLATE_NAME" ]; then
96
104
  fi
97
105
  fi
98
106
 
99
- SCHEMA_FILE="$TEMPLATES_DIR/${TEMPLATE_NAME%.md}.schema.json"
107
+ SCHEMA_FILE="$TEMPLATES_DIR/$(echo "${TEMPLATE_NAME%.md}" | sed 's/-template$//').schema.json"
100
108
  # L84: schema 文件不存在 → 无校验规则,阻断(不允许无规则放行)
101
109
  if [ ! -f "$SCHEMA_FILE" ]; then
102
110
  echo "⛔ 阻断:模板 ${TEMPLATE_NAME} 缺少对应的 .schema.json 校验规则" >&2
@@ -2,7 +2,7 @@
2
2
  # vocabulary.sh — 从 vocabulary markdown 表格加载触发词
3
3
  # 单一数据源: .qoder/vocabulary/add-governance-vocabulary.md §类别 A-F 表格
4
4
 
5
- VOCABULARY_FILE="$PWD/.qoder/vocabulary/add-governance-vocabulary.md"
5
+ VOCABULARY_FILE="${PROJECT_DIR:-$PWD}/.qoder/vocabulary/add-governance-vocabulary.md"
6
6
 
7
7
  # 输出格式: 优先级::触发词正则::响应文本(:: 避免与触发词内的 | 冲突)
8
8
  load_triggers() {
@@ -1,21 +1,24 @@
1
1
  #!/bin/bash
2
- # notification.sh — Notification Review 提醒
2
+ # notification.sh — Qoder CN NotificationReview 提醒
3
+ # 治理卡位 #12: 开发提醒/Token 预警
3
4
  set -euo pipefail
5
+
4
6
  input=$(cat)
5
- ntype=$(echo "$input" | jq -r '.notification_type // ""')
7
+ ntype=$(echo "$input" | jq -r '.notification_type // ""' 2>/dev/null || echo "")
6
8
 
7
9
  if [ "$ntype" != "result" ]; then
8
10
  exit 0
9
11
  fi
10
12
 
11
13
  HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
12
- source "$HOOK_DIR/lib/state-detect.sh"
14
+ export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
15
+ export PROJECT_DIR="${QODER_PROJECT_DIR:-${QODERCN_PROJECT_DIR:-$PWD}}"
16
+ source "$HOOK_DIR/lib/common.sh" 2>/dev/null || true
13
17
 
14
18
  state=$(detect_active_add 2>/dev/null || true)
15
19
  [ -z "$state" ] && exit 0
16
20
 
17
- # 检查是否有未读 Review
18
- reviews_dir="${QODER_DIR:-.qoder}/reviews"
21
+ reviews_dir="${PROJECT_DIR}/.qoder/reviews"
19
22
  if ls "$reviews_dir"/*.md >/dev/null 2>&1; then
20
23
  echo "[ADD Notification] 请检查 Review 文档: ${reviews_dir}"
21
24
  fi
@@ -1,20 +1,30 @@
1
1
  #!/bin/bash
2
- # post-tool-use.sh — PostToolUse 审计提醒
2
+ # post-tool-use.sh — Qoder CN PostToolUse:格式化 + 文档守卫 + 审计提醒
3
+ # 治理卡位 #5: 格式化 + ADD文档守卫 + 审计落库 + 结果增强
3
4
  set -euo pipefail
5
+
4
6
  input=$(cat)
5
- file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
6
- [ -z "$file_path" ] && exit 0
7
+ HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
8
+ source "$HOOK_DIR/lib/common.sh" 2>/dev/null || true
9
+
10
+ tool_name=$(json_get "$input" "tool_name")
11
+ [ -z "$tool_name" ] && tool_name=$(echo "$input" | grep -o '"tool_name"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | sed 's/.*: *"\([^"]*\)".*/\1/' || echo "")
12
+
13
+ if [ "$tool_name" = "Edit" ] || [ "$tool_name" = "Write" ]; then
14
+ file_path=$(echo "$input" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | sed 's/.*: *"\([^"]*\)".*/\1/' || echo "")
15
+ [ -z "$file_path" ] && exit 0
16
+
17
+ if echo "$file_path" | grep -qE '\.(qoder|claude|add)/(plans|specs|reviews)/'; then
18
+ echo "[ADD PostToolUse] ADD 文档已写入: ${file_path}。请确保章节完整、双向链接齐全、增量修订格式正确。" >&2
19
+ fi
7
20
 
8
- # 只在写入核心文件时提醒
9
- if ! echo "$file_path" | grep -qE 'src/agents/|src/lib/agent-audit-logger\.ts|src/types/|prisma/schema\.prisma'; then
21
+ echo "[ADD PostToolUse] 文件已写入: ${file_path}。请执行 record_dev_operation 落库审计(ADD-7)。" >&2
10
22
  exit 0
11
23
  fi
12
24
 
13
- HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
14
- source "$HOOK_DIR/lib/state-detect.sh"
15
-
16
- state=$(detect_active_add 2>/dev/null || true)
17
- [ -z "$state" ] && exit 0
25
+ if [ "$tool_name" = "Bash" ]; then
26
+ echo "[ADD PostToolUse] 命令执行完成。如有 lint/tsc 错误请修复。" >&2
27
+ exit 0
28
+ fi
18
29
 
19
- echo "[ADD PostToolUse] 文件已写入: ${file_path}。如果尚未记录,请执行 record_dev_operation({ targetId: '${file_path}' }) 落库审计。"
20
30
  exit 0
@@ -1,12 +1,37 @@
1
1
  #!/bin/bash
2
- # pre-compact.sh — PreCompact ADD 状态快照
2
+ # pre-compact.sh — 上下文压缩前 ADD 状态保存(Claude Code 适配)
3
+ # 治理卡位 #9: ADD状态保存 + 恢复清单导出 + tpl标记清理
3
4
  set -euo pipefail
5
+
4
6
  HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
5
- source "$HOOK_DIR/lib/state-detect.sh"
7
+ export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
8
+ COMMON_LIB="$HOOK_DIR/lib/common.sh"
9
+ [ -f "$COMMON_LIB" ] && source "$COMMON_LIB"
10
+
11
+ export PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$PWD}"
12
+
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
6
32
 
7
- state=$(detect_active_add 2>/dev/null || true)
8
- [ -z "$state" ] && exit 0
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
9
36
 
10
- IFS='::' read -r plan step rounds handoff add_route <<< "$state"
11
- echo "[ADD PreCompact] 压缩前状态: Plan=${plan}, 轮次=${rounds}, Step=${step}"
12
37
  exit 0
@@ -1,77 +1,79 @@
1
1
  #!/bin/bash
2
- # pre-tool-use.sh — PreToolUse §B:源码 Plan 关联检查(阻断模式 v3)
2
+ # pre-tool-use.sh — Qoder CN PreToolUse:四路守卫
3
+ # 治理卡位 #4: 危险命令拦截 / 模板路径兜底 / 写入前置守卫 / 敏感文件保护
3
4
  set -euo pipefail
4
5
 
5
6
  input=$(cat)
6
- file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
7
- [ -z "$file_path" ] && exit 0
8
-
9
- if ! echo "$file_path" | grep -qE '(src/|/src/).*\.(ts|tsx)$'; then
7
+ HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
8
+ export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
9
+ export PROJECT_DIR="${QODER_PROJECT_DIR:-${QODERCN_PROJECT_DIR:-$PWD}}"
10
+ source "$HOOK_DIR/lib/common.sh" 2>/dev/null || true
11
+
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 "")
14
+
15
+ # ── ① Bash matcher: 危险命令拦截 + 终端写文件拦截 ──
16
+ if [ "$tool_name" = "Bash" ]; then
17
+ cmd=$(echo "$input" | grep -o '"command"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | sed 's/.*: *"\([^"]*\)".*/\1/' || echo "")
18
+ # 危险命令
19
+ if echo "$cmd" | grep -qiE 'rm[[:space:]]+-rf[[:space:]]+/|DROP[[:space:]]+TABLE|git[[:space:]]+push[[:space:]]+--force|mkfs\.|dd[[:space:]]+if='; then
20
+ echo "⛔ 危险命令已被阻止: $cmd" >&2
21
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"危险命令已被阻止\"}}"
22
+ exit 2
23
+ fi
24
+ # 终端写文件拦截(含 > / >> / << heredoc / mv / touch / python -c > file)
25
+ if echo "$cmd" | grep -qE '(cat|echo|tee|sed[[:space:]]+-i|awk|printf|cp|mv|dd|touch)[[:space:]]*.*([>]{1,2}|[|][[:space:]]*tee|<<)'; then
26
+ echo "⛔ 禁止通过终端命令直接写文件: $cmd。请使用 Write/Edit/SearchReplace 工具。" >&2
27
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"禁止通过终端直接写文件,请使用 IDE 工具\"}}"
28
+ exit 2
29
+ fi
30
+ # mv 无重定向但仍操作文件
31
+ if echo "$cmd" | grep -qE '^[[:space:]]*mv[[:space:]]+/tmp/'; then
32
+ echo "⛔ 禁止通过 mv /tmp/ 绕过 IDE 工具: $cmd" >&2
33
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"禁止通过 mv 绕过 IDE 工具\"}}"
34
+ exit 2
35
+ fi
36
+ # python/node 脚本写文件
37
+ if echo "$cmd" | grep -qE '(python3|python|node)[[:space:]].*[>]{1,2}'; then
38
+ echo "⛔ 禁止通过脚本语言直接写文件: $cmd。请使用 Write/Edit/SearchReplace 工具。" >&2
39
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"禁止通过脚本语言直接写文件,请使用 IDE 工具\"}}"
40
+ exit 2
41
+ fi
42
+ mark_dev_action 2>/dev/null || true
10
43
  exit 0
11
44
  fi
12
45
 
13
- PROJECT_DIR="${QODER_PROJECT_DIR:-${QODERCN_PROJECT_DIR:-$PWD}}"
14
- MOD=$(basename "$file_path" | sed 's/\.[jt]sx\?$//')
15
-
16
- MATCHES=$(grep -rl "$MOD" "$PROJECT_DIR/.qoder/plans" "$PROJECT_DIR/.qoder/specs" "$PROJECT_DIR/.qoder/reports" 2>/dev/null | wc -l)
17
-
18
- if [ "$MATCHES" -gt 0 ]; then
19
- echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"相关 ADD Plan 已存在"}}'
46
+ # ── ② Write/Edit matcher: 文件写入前置守卫 ──
47
+ if [ "$tool_name" = "Write" ] || [ "$tool_name" = "Edit" ]; then
48
+ file_path=$(echo "$input" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | sed 's/.*: *"\([^"]*\)".*/\1/' || echo "")
49
+ [ -z "$file_path" ] && exit 0
50
+
51
+ if echo "$file_path" | grep -qE '\.(qoder|claude|add)/(plans|specs|reviews)/'; then
52
+ if type detect_active_add >/dev/null 2>&1; then
53
+ state=$(detect_active_add 2>/dev/null || true)
54
+ if [ -z "$state" ]; then
55
+ echo "[ADD PreToolUse] ⚠️ 正在写入 Plan/Spec/Review 文档但无活跃 ADD Plan——请先执行 add-paradigm" >&2
56
+ fi
57
+ fi
58
+ fi
59
+
60
+ if echo "$file_path" | grep -qE '\.env$|\.env\.production$|\.env\.local$|credentials|secrets'; then
61
+ echo "⛔ 敏感文件受保护,禁止写入: $file_path" >&2
62
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"敏感文件受保护\"}}"
63
+ exit 2
64
+ fi
65
+
66
+ mark_dev_action 2>/dev/null || true
20
67
  exit 0
21
68
  fi
22
69
 
23
- echo "⛔ 阻断: ${file_path} 无相关 ADD Plan。请先创建 Plan(plan-template / simple-plan-template)。" >&2
24
- echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"无相关 ADD Plan。请用 AskUserQuestion 询问用户选择 plan-template 或 simple-plan-template 创建 Plan。"}}'
25
- exit 2
26
- #!/bin/bash
27
- # pre-tool-use.sh PreToolUse §B:源码 Plan 关联检查(阻断模式)
28
- set -euo pipefail
29
-
30
- input=$(cat)
31
- file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
32
- [ -z "$file_path" ] && exit 0
33
-
34
- # 只处理 src/**/*.ts / src/**/*.tsx
35
- if ! echo "$file_path" | grep -qE '^src/.*\.(ts|tsx)$' && ! echo "$file_path" | grep -qE '^/home/.*/src/.*\.(ts|tsx)$'; then
70
+ # ── Read matcher: 模板路径兜底 ──
71
+ if [ "$tool_name" = "Read" ]; then
72
+ file_path=$(echo "$input" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | sed 's/.*: *"\([^"]*\)".*/\1/' || echo "")
73
+ if echo "$file_path" | grep -q 'templates/'; then
74
+ echo "[ADD PreToolUse] 提示: 模板文件已通过 hook 预读到上下文,可跳过重复读取" >&2
75
+ fi
36
76
  exit 0
37
77
  fi
38
78
 
39
- PROJECT_DIR="${QODER_PROJECT_DIR:-${QODERCN_PROJECT_DIR:-$PWD}}"
40
- PLANS_DIR="$PROJECT_DIR/.qoder/plans"
41
- SPECS_DIR="$PROJECT_DIR/.qoder/specs"
42
- REPORTS_DIR="$PROJECT_DIR/.qoder/reports"
43
-
44
- MOD=$(basename "$file_path" | sed 's/\.[jt]sx\?$//')
45
-
46
- MATCHES=$(grep -rl "$MOD" "$PLANS_DIR" "$SPECS_DIR" "$REPORTS_DIR" 2>/dev/null \
47
- | sed "s|$PROJECT_DIR/||" \
48
- | grep -oE '[^/]*-(plan|handoff|review|report)-v[0-9]+' \
49
- | sed 's/\(plan\|handoff\|review\|report\)-v[0-9]\+\.md//' \
50
- | sort -u | head -10 2>/dev/null || true)
51
-
52
- if [ -n "$MATCHES" ]; then
53
- cat >&2 <<EOF
54
- [ADD PreToolUse] 📋 相关文档:
55
- $(echo "$MATCHES" | sed 's/^/ - /')
56
- 允许编辑 $file_path。
57
- EOF
58
- exit 0
59
- fi
60
-
61
- cat >&2 <<EOF
62
- [ADD PreToolUse] ⛔ 阻断:${file_path} 无相关 ADD 文档。
63
-
64
- 必须先生成 Plan 后才能修改源码。请执行:
65
- ① 用 AskUserQuestion 询问用户:要创建 Plan 吗?
66
- A) plan-template — 复杂改动,含架构设计 + 独立 Spec
67
- B) simple-plan-template — 单一修复,Tasks 内联于 Plan 体
68
- ② 按模板创建 Plan → Plan 中引用此文件路径
69
- ③ 重新执行修改操作
70
-
71
- JSON 输出格式(阻断时必须):
72
- {"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"无相关 ADD Plan,请先生成 Plan 文档"}}
73
- EOF
74
-
75
- # 输出阻断 JSON 到 stdout,Qoder 会读它来展示阻断原因
76
- echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"无相关 ADD Plan,请先生成 Plan 文档"}}'
77
- exit 2
79
+ exit 0
@@ -1,13 +1,20 @@
1
1
  #!/bin/bash
2
2
  # prompt-submit.sh — UserPromptSubmit 触发词智能路由(★入口)
3
+ # 治理卡位 #3: 触发词路由 + 模板全文注入(增量插入,保留 Layer 1/2/3)
3
4
  input=$(cat)
4
5
  prompt=$(echo "$input" | jq -r '.prompt // empty')
5
6
  [ -z "$prompt" ] && exit 0
6
7
 
7
8
  HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
9
+ export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
10
+ export PROJECT_DIR="${QODER_PROJECT_DIR:-${QODERCN_PROJECT_DIR:-$PWD}}"
11
+ source "$HOOK_DIR/lib/common.sh" 2>/dev/null || true
8
12
  source "$HOOK_DIR/lib/vocabulary.sh"
9
13
  source "$HOOK_DIR/lib/state-detect.sh"
10
14
 
15
+ # ─── additionalContext 注入(Qoder CN IDE 通过 stdout JSON 注入模型上下文)───
16
+ echo '{"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":"ADD workflow active. Templates preloaded. Use add-paradigm SKILL."}}'
17
+
11
18
  # ─── Layer 1: 精准 P0 触发词 ───
12
19
  matched=$(match_trigger "$prompt")
13
20
  if [ -n "$matched" ]; then
@@ -27,6 +34,7 @@ if [ -n "$matched" ]; then
27
34
  " ★ 同步检查: 如 checklist 有新 cuid 但 handoff 审计表缺失 → 更新 handoff ADD-7 表 + query_audit_logs 命令"
28
35
  # 执行 checklist 质量检查
29
36
  HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
37
+ export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
30
38
  "$HOOK_DIR/review-checklist.sh" "$_handoff" "$_add_route" 2>/dev/null
31
39
  exit 0
32
40
  fi
@@ -46,7 +54,7 @@ if ! echo "$prompt" | grep -qiE "$dev_kw"; then
46
54
  exit 0
47
55
  fi
48
56
 
49
- # ─── Layer 2/3: 按活跃 ADD 分流 ───
57
+ # ─── Layer 2/3: 按活跃 ADD 分流(原 Layer 2/3,不修改) ───
50
58
  state=$(detect_active_add 2>/dev/null || true)
51
59
  if [ -z "$state" ]; then
52
60
  # Layer 2: 无活跃 ADD → 强制启动
@@ -68,5 +76,7 @@ plan=$(echo "$state" | awk -F'::' '{print $1}')
68
76
  step=$(echo "$state" | awk -F'::' '{print $2}')
69
77
  rounds=$(echo "$state" | awk -F'::' '{print $3}')
70
78
  handoff=$(echo "$state" | awk -F'::' '{print $4}')
71
- echo "[ADD 状态] Plan: ${plan}, 轮次: ${rounds}, Step: ${step}, handoff: ${handoff}"
79
+ add_route=$(echo "$state" | awk -F'::' '{print $5}')
80
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"UserPromptSubmit\",\"additionalContext\":\"[ADD 状态] Plan: ${plan}, 轮次: ${rounds}, Step: ${step}, handoff: ${handoff}\"}}"
72
81
  exit 0
82
+
@@ -0,0 +1,24 @@
1
+ #!/bin/bash
2
+ # SessionEnd — Qoder CN 专用(独立实现,stdout JSON additionalContext 注入)
3
+ # 治理卡位 #2: 标记清理 + 会话审计结算 + Stop未触发兜底
4
+ set -euo pipefail
5
+
6
+ HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
7
+ export CURRENT_MAGIC=$(basename "$(dirname "$HOOK_DIR")")
8
+ export PROJECT_DIR="${QODER_PROJECT_DIR:-${QODERCN_PROJECT_DIR:-$PWD}}"
9
+ source "$HOOK_DIR/lib/common.sh" 2>/dev/null || true
10
+
11
+ # ── ① 清理标记文件 ──
12
+ project_hash=$(echo "${PROJECT_DIR:-$PWD}" | md5sum 2>/dev/null | cut -c1-8 || echo "default")
13
+ rm -f "/tmp/add_tpl_${project_hash}" 2>/dev/null || true
14
+ rm -f "/tmp/add_dev_${project_hash}" 2>/dev/null || true
15
+
16
+ # ── ② Stop 未触发兜底 ──
17
+ fallback_msg=""
18
+ if has_dev_action 2>/dev/null; then
19
+ fallback_msg="⚠️ 检测到 dev action 标记未清除——Stop 可能未触发验收检查"
20
+ fi
21
+
22
+ # ── ③ stdout JSON additionalContext ──
23
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"SessionEnd\",\"additionalContext\":\"[ADD SessionEnd] 会话结束 — $(date -Iseconds)。标记已清理。${fallback_msg}\"}}"
24
+ exit 0