Claude Code Hooks:我的95個Hook各自存在的原因
我為Claude Code打造了95個hook。每一個都是因為某件事先出了差錯才誕生的。git-safety-guardian的存在,是因為Claude曾經對main執行了force-push。recursion-guard的存在,是因為一個子代理產生了無限的子程序。blog-quality-gate的存在,是因為我發布了一篇包含7個被動語態句子和一個懸空腳註的文章。1
TL;DR
Claude Code hooks在AI輔助開發的特定生命週期節點執行shell指令。Hooks在一個機率性系統(LLM)之上提供確定性保證(阻擋危險的git指令、注入上下文、強制品質標準)。在我的基礎架構中建構了95個hook之後,我發現最好的hook來自事故,而非規劃。本文涵蓋了架構設計、最關鍵hook背後的起源故事,以及我從9個月的hook開發中學到的模式。
架構設計
Claude Code公開了生命週期事件,讓hook可以在這些節點攔截、修改或阻擋行為:2
工作階段事件
| 事件 | 觸發時機 | 我的Hooks |
|---|---|---|
| SessionStart | 新工作階段開始時 | session-start.sh — 注入日期、驗證venv、初始化遞迴狀態 |
| SessionEnd | 工作階段結束時 | 清理暫存檔案 |
工具執行事件
| 事件 | 觸發時機 | 我的Hooks |
|---|---|---|
| PreToolUse | 任何工具執行之前 | git-safety-guardian.sh、recursion-guard.sh、credentials-check.sh |
| PostToolUse | 工具完成之後 | post-deliberation.sh、log-bash.sh |
回應事件
| 事件 | 觸發時機 | 我的Hooks |
|---|---|---|
| UserPromptSubmit | 使用者送出提示時 | 上下文注入器(日期、分支、模型資訊) |
| Stop | Claude完成回應時 | deliberation-pride-check.sh、reviewer-stop-gate.sh |
每個hook透過stdin接收JSON,並透過stdout進行通訊:
{"decision": "block", "reason": "Force push to main is prohibited"}
或是以退出碼0靜默放行。3
起源故事:最重要的Hooks
Hook 1:git-safety-guardian.sh(PreToolUse:Bash)
事故經過: 在一次早期的Claude Code工作階段中,我要求代理「清理git歷史紀錄」。代理執行了git push --force origin main。這次force push覆蓋了共享分支上三天的提交。我從本機備份中恢復了資料,但4小時的復原過程讓我確信:機率性判斷永遠不應該控制破壞性的git操作。
Hook內容:
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
# Only check git commands
echo "$COMMAND" | grep -qE '\bgit\b' || exit 0
# Block force push to main/master
if echo "$COMMAND" | grep -qiE 'git\s+push\s+.*--force.*\b(main|master)\b'; then
cat << EOF
{"decision": "block", "reason": "Force push to main/master blocked by safety hook"}
EOF
fi
教訓: 這個hook不會嘗試理解意圖。它對指令字串進行模式匹配。簡單、確定性、無法透過巧妙的提示繞過。代理仍然可以對功能分支執行force-push(有時這是合理的),但main/master永遠受到保護。4
累計攔截次數: 9個月內攔截了8次force-push嘗試。
Hook 2:recursion-guard.sh(PreToolUse:Task)
事故經過: 在建構審議系統時,我執行了一個產生3個探索子代理的工作階段。每個子代理因為缺乏產生限制,又各自產生了自己的子代理。這個遞迴以正常速率10倍的速度消耗API代幣。在注意到加速的代幣消耗後,我手動終止了該工作階段。
Hook內容:
#!/bin/bash
CONFIG_FILE="${HOME}/.claude/configs/recursion-limits.json"
STATE_FILE="${HOME}/.claude/state/recursion-depth.json"
MAX_DEPTH=2
MAX_CHILDREN=5
DELIB_SPAWN_BUDGET=2
DELIB_MAX_AGENTS=12
# Validate integers safely (((VAR++)) crashes with set -e when VAR=0)
is_positive_int() {
[[ "$1" =~ ^[0-9]+$ ]] && [[ "$1" -gt 0 ]]
}
關鍵設計決策: 代理從父代理繼承產生預算,而非遞增深度。一個預算為12的根代理可以將該預算分配到任何樹狀結構中。基於深度的限制過於僵化(它們會阻止深但窄的鏈式結構,而這些結構其實完全安全)。5
累計阻擋次數: 23次失控的產生嘗試。
Hook 3:blog-quality-gate.sh(Stop)
事故經過: 我發布了一篇部落格文章,其中包含7個被動語態句子、一個在正文中被引用但在參考文獻區缺失的腳註,以及以「was implemented by the team」作為開頭句。這篇文章在編輯器中看起來很精美,但未能通過任何人類審閱者都會發現的基本品質檢查。
Hook功能: 對任何修改過的部落格內容檔案執行我的12模組部落格檢查工具。檢查被動語態、懸空腳註、缺失的meta描述、未標記的程式碼區塊以及引用完整性。每項發現都很具體:「第47行:在『was implemented by the team』中偵測到被動語態。建議:『the team implemented。』」
與人類回饋的相似之處: 這個hook批評的是作品,而非操作者。它說「第47行有被動語態」,而非「你寫得很差」。讓人類回饋具建設性的同一原則,也讓自動化回饋變得實用。
95個Hooks背後的模式
組態驅動架構
我的hooks從寫死的值演進為組態驅動的行為:
~/.claude/configs/
├── recursion-limits.json # Depth, spawn budgets, timeouts
├── deliberation-config.json # Consensus thresholds per task type
├── consensus-profiles.json # security=85%, docs=50%
├── circuit-breaker.json # Failure mode configurations
└── file-scope-rules.json # Path-scoped hook application
將閾值移至JSON組態檔意味著我可以在不編輯bash腳本的情況下調整行為。當我需要安全相關共識達到85%但文件只需50%時,組態變更只需30秒。程式碼變更則需要編輯、測試和重新部署。6
生命週期分層模式
我的95個hooks形成了四層安全網:
第1層:預防(PreToolUse) — 在壞事發生之前阻止它們。git-safety-guardian、credentials-check、recursion-guard。
第2層:上下文(UserPromptSubmit、SessionStart) — 注入代理所需的資訊。日期、分支、專案上下文、記憶條目、哲學原則。
第3層:驗證(PostToolUse) — 驗證已完成的操作是否符合標準。post-deliberation共識檢查、輸出日誌記錄。
第4層:品質(Stop) — 把關最終輸出。Pride check、品質把關、審閱者停止閘門。
每一層都是獨立的。如果PreToolUse hook靜默失敗,Stop hook仍然會捕捉到品質問題。縱深防禦,應用於AI代理行為。
組態設定
Hooks存放在.claude/settings.json中:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "~/.claude/hooks/git-safety-guardian.sh",
"timeout": 5000
}
],
"PreToolUse:Task": [
{
"command": "~/.claude/hooks/recursion-guard.sh"
}
]
}
}
matcher欄位篩選哪些工具觸發該hook。PreToolUse:Task是僅在Task工具調用時觸發的matcher簡寫。非同步hooks(async: true)在背景執行,不會阻擋流程。7
範圍層級
- 使用者層級(
~/.claude/settings.json)— 適用於所有專案。我的95個hooks存放於此。 - 專案層級(
.claude/settings.json)— 新增專案特定的hooks。 - 技能/子代理frontmatter — 範圍限定於特定元件的生命週期。8
如果重來,我會怎麼做
從3個hooks開始,而非25個。 我的第一個月產出了25個hooks,其中許多新增了代理本身已具備的上下文。在每次工具呼叫時載入25個hooks的開銷是可以被測量到的。我最終精簡為那些產生真實價值的hooks(預防了真實事故、捕捉到真實的品質問題)。
從第一天就採用組態驅動。 我花了兩週將寫死的閾值重構為JSON組態檔。如果一開始就採用組態驅動,這次重構本來是零成本的。
及早建立測試基礎設施。 前20個hooks沒有測試。當我加入測試工具(48個bash整合測試)時,我發現有3個hooks在邊界案例中靜默失敗。測試應該從第1個hook就一起交付。
關鍵要點
給剛開始使用hooks的開發者: - 從三個hooks開始:git安全(PreToolUse:Bash)、上下文注入(UserPromptSubmit)和品質把關(Stop);只有在發生足以證明其必要性的事故時才增加更多 - 使用決策時機框架:hook架構是不可逆的(95個hooks依賴於它),因此在編寫hooks之前,先投資於生命週期模型
給正在標準化hooks的團隊: - 在使用者層級標準化hooks,讓每位團隊成員獲得相同的安全防護 - 追蹤hook指標(被阻擋的操作、被攔截的事故)以證明維護成本的合理性 - 每月審查hook日誌,識別值得自動化的新模式
參考文獻
-
作者的hook基礎架構。橫跨6個生命週期事件的95個hooks,歷時9個月開發(2025-2026)。 ↩
-
Anthropic,“Claude Code Documentation,” 2025。Hook生命週期事件。 ↩
-
Anthropic,“Claude Code Documentation,” 2025。Hook輸入/輸出JSON格式。 ↩
-
作者的git-safety-guardian.sh。8次被攔截的force-push嘗試記錄於
~/.claude/state/。 ↩ -
作者的recursion-guard.sh。預算繼承模型記載於
~/.claude/configs/recursion-limits.json。 ↩ -
作者的組態驅動架構。14個JSON組態檔編碼所有hook閾值與規則。 ↩
-
Anthropic,“Claude Code Documentation,” 2025。Hook組態設定與非同步執行。 ↩
-
Anthropic,“Claude Code Documentation,” 2025。Hook範圍層級。 ↩