← 所有文章

Claude Code Hooks:我的 95 個 Hook 為何各自存在

From the guide: Claude Code Comprehensive Guide

我為 Claude Code 建立了 95 個 hook。每一個的存在都源於某次出錯的經驗。git-safety-guardian 的存在,是因為 Claude 曾經強制推送至 main。recursion-guard 的存在,是因為某個子代理生成了無限多的子代理。blog-quality-gate 的存在,是因為我曾發布過一篇含有 7 個被動語態句子與一個懸空註腳的文章。1

Claude Code hook 是在特定生命週期節點(工作階段開始、工具使用前後、提示送出以及回應完成)執行的 shell 指令,能在機率性的 LLM 行為之上,提供確定性的防護欄。 Hook 透過 stdin 接收 JSON,並透過 stdout 回傳決策(阻擋、允許或修改)。它們能執行諸如阻擋強制推送至 main、防止遞迴生成代理、在每個工作階段注入上下文,以及把關 LLM 本身無法保證的輸出品質等政策。

TL;DR

Claude Code hook 會在 AI 輔助開發過程中的特定生命週期節點執行 shell 指令。Hook 在機率性系統(LLM)之上提供確定性保證(阻擋危險的 git 指令、注入上下文、強制品質把關)。在我的基礎設施中建立 95 個 hook 之後,我發現最好的 hook 來自於事故,而不是規劃。本文涵蓋這套架構、我最關鍵幾個 hook 的起源故事,以及從 9 個月 hook 開發中學到的模式。


架構

Claude Code 公開了 26 個生命週期事件,hook 可在其中攔截、修改或阻擋行為(截至 v2.1.116,2026 年 4 月)。2 我的 hook 鎖定其中最常見的六個:

工作階段事件

事件 觸發時機 我的 Hook
SessionStart 新工作階段開始 session-start.sh:注入日期、驗證 venv、初始化遞迴狀態
SessionEnd 工作階段終止 清理暫存檔案

工具執行事件

事件 觸發時機 我的 Hook
PreToolUse 任何工具執行前 git-safety-guardian.shrecursion-guard.shcredentials-check.sh
PostToolUse 工具完成後 post-deliberation.shlog-bash.sh

回應事件

事件 觸發時機 我的 Hook
UserPromptSubmit 使用者送出提示 上下文注入器(日期、分支、模型資訊)
Stop Claude 完成回應 deliberation-pride-check.shreviewer-stop-gate.sh

每個 hook 都透過 stdin 接收 JSON,並透過 stdout 進行溝通:

{"decision": "block", "reason": "Force push to main is prohibited"}

或以退出碼 0 靜默允許。3


起源故事:最重要的幾個 Hook

Hook 1:git-safety-guardian.sh(PreToolUse:Bash)

事故經過: 在早期的一次 Claude Code 工作階段中,我請代理「整理一下 git 歷史」。代理執行了 git push --force origin main。這次強制推送覆蓋掉一個共享分支上三天的提交。我從本地備份中還原,但耗時 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 不試圖理解意圖,而是對指令字串進行模式比對。簡單、確定、無法透過巧妙的提示繞過。代理仍可強制推送至功能分支(有時是合理需求),但 main/master 受到永久保護。4

累計成效: 9 個月內攔截了 8 次強制推送嘗試。

Hook 2:recursion-guard.sh(PreToolUse:Task)

事故經過: 在建置審議系統時,我執行了一個工作階段,它生成了 3 個探索型子代理。每個子代理由於沒有生成上限,又各自生成更多子代理。這種遞迴以正常速度的 10 倍消耗 API token。我在注意到 token 消耗加速後,手動終止了該工作階段。

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 模組部落格 linter。檢查被動語態、懸空註腳、缺失的 meta 描述、未標記的程式碼區塊,以及引文完整性。每個發現都是具體的:「第 47 行:在『was implemented by the team』中偵測到被動語態。建議:『the team implemented』。」

人類回饋的相似之處: Hook 批評作品,而不是操作者。它說的是「第 47 行有被動語態」,而不是「你寫得很差」。使人類回饋具建設性的同一原則,也使自動化回饋具有實用價值。


95 個 Hook 背後的模式

由配置驅動的架構

我的 hook 從寫死的數值,逐漸演變成由配置驅動的行為:

~/.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 個 hook 構成了一張四層的安全網:

第 1 層:預防(PreToolUse): 在壞事發生前阻止它。git-safety-guardian、credentials-check、recursion-guard。

第 2 層:上下文(UserPromptSubmit、SessionStart): 注入代理所需的資訊。日期、分支、專案上下文、記憶條目、哲學原則。

第 3 層:驗證(PostToolUse): 確認已完成的動作符合標準。審議後的共識檢查、輸出紀錄。

第 4 層:品質(Stop): 把關最終輸出。Pride check、品質把關、reviewer stop gate。這一層實現了後設認知監控,由代理評估自身推理品質,而不僅僅是輸出結果。

各層彼此獨立。即使某個 PreToolUse hook 靜默失敗,Stop hook 仍能抓到品質問題。縱深防禦,應用於 AI 代理行為之上。


設定

Hook 定義於 .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 的簡寫。非同步 hook(async: true)會在背景執行而不阻塞主流程。7

範圍階層

  1. 使用者層級~/.claude/settings.json):套用於所有專案。我的 95 個 hook 都位於此處。
  2. 專案層級.claude/settings.json):新增專案專屬的 hook。
  3. Skill/Subagent frontmatter: 限定於特定元件的生命週期。8

若能重來一次

從 3 個 hook 開始,而不是 25 個。 我的第一個月就產出了 25 個 hook,其中許多所注入的上下文,代理其實早已擁有。每次工具呼叫都載入 25 個 hook 的負擔是可量測的。最終我把它們精簡到真正產生價值的那些(阻止了真實事故、抓到了真實品質問題)。

從第一天就採用由配置驅動。 我花了兩週把寫死的閾值重構成 JSON 配置。若一開始就採用配置,這段重構完全可以避免。

盡早建立測試基礎設施。 前 20 個 hook 沒有測試。當我加入測試框架(48 個 bash 整合測試)後,發現有 3 個 hook 在邊緣案例下靜默失敗。測試本應隨第一個 hook 一併交付。


重點摘要

給剛開始使用 hook 的開發者: - 從三個 hook 開始:git 安全(PreToolUse:Bash)、上下文注入(UserPromptSubmit)與品質把關(Stop);只有在遇到足以證明其必要性的事故時,才增加更多 - 運用決策時機框架:hook 架構是不可逆的(95 個 hook 依賴於它),因此在撰寫 hook 之前,請先投資於生命週期模型的設計

給正在將 hook 標準化的團隊: - 在使用者層級標準化 hook,讓每位團隊成員都享有相同的安全護欄 - 追蹤 hook 指標(被阻擋的操作、攔截到的事故),以證明維護成本的合理性 - 每月審閱 hook 紀錄,找出值得自動化的新模式


FAQ

什麼是 Claude Code hook?

Hook 是在 Claude Code 工作階段中,於特定生命週期節點執行的 shell 腳本。它們會在特定時刻確定性地觸發:在工具執行前(PreToolUse)、工具完成後(PostToolUse)、送出提示時(UserPromptSubmit)、工作階段開始時(SessionStart),以及 Claude 完成回應時(Stop)。Hook 透過 stdin 接收帶有完整上下文(工具名稱、輸入、工作階段 ID)的 JSON,並可阻擋操作、注入上下文或強制執行品質把關。它們在機率性的 LLM 之上提供確定性保證。

Claude Code 可以執行多少個 hook?會有效能成本嗎?

對 hook 的數量並無硬性限制。我在 26 個可用生命週期事件中的 6 個(截至 v2.1.116,2026 年 4 月)執行了 95 個 hook,每個事件大約增加 200 毫秒的總負擔。實際上的限制在於延遲:每個 hook 都會在工具呼叫前後增加執行時間。關鍵的最佳化方式是使用 dispatcher(每個事件一個,從快取的 stdin 依序執行 hook),而不是各自獨立、分別讀取 stdin 的 hook。在我早期的設定中,獨立 hook 並行寫入會造成 JSON 損毀;使用 dispatcher 徹底消除了這種失敗模式。

Claude Code hook 會在哪些事件上觸發?

截至 v2.1.116(2026 年 4 月),Claude Code 公開了 26 個可供 hook 使用的生命週期事件。我的 hook 所鎖定最常見的六個為:SessionStart(新工作階段開始)、SessionEnd(工作階段終止)、PreToolUse(任何工具執行前)、PostToolUse(工具完成後)、UserPromptSubmit(使用者送出提示),以及 Stop(Claude 完成回應)。其他還包括 SubagentStart、PermissionRequest、PermissionDenied、TaskCreated、CwdChanged、FileChanged、PreCompact 等。PreToolUse 與 PostToolUse 可用如 PreToolUse:BashPreToolUse:Task 等 matcher 進一步限定範圍,使其僅在特定工具類型上觸發。每個事件都透過 stdin 接收帶有相關上下文的 JSON。

Claude Code hook 可以阻擋工具呼叫嗎?

可以。將 {"decision": "block", "reason": "explanation"} 輸出至 stdout 的 hook,會阻止該工具呼叫執行。這是安全 hook 的核心機制:我的 git-safety-guardian 阻擋對 main 的強制推送、credentials-check 阻擋讀取 .env 檔案,而 recursion-guard 則阻擋過度的代理生成。這種阻擋是確定性的,無法透過提示繞過。以退出碼 0 靜默結束的 hook,則允許操作繼續進行。

PreToolUse 與 PostToolUse hook 有什麼差別?

PreToolUse 在工具執行前觸發,可完全阻擋該操作。請用它作為安全閘門:阻擋危險指令、檢查憑證、強制執行生成預算。PostToolUse 則在工具完成後觸發,可提供回饋或引發後續動作。請用它進行驗證:檢查輸出品質、記錄活動、驗證共識。兩者共同構成四層安全網中的第一層與第三層:預防(PreToolUse)、上下文注入(UserPromptSubmit)、驗證(PostToolUse),以及品質把關(Stop)。

References


  1. Author’s hook infrastructure. 95 hooks across 6 of the 26 available lifecycle events (v2.1.116, April 2026), developed over 9 months (2025-2026). 

  2. Anthropic, “Claude Code Hooks Reference,” 2026. 26 lifecycle event types as of v2.1.116 (April 2026). 

  3. Anthropic, “Claude Code Documentation,” 2025. Hook input/output JSON format. 

  4. Author’s git-safety-guardian.sh. 8 intercepted force-push attempts tracked in ~/.claude/state/

  5. Author’s recursion-guard.sh. Budget inheritance model documented in ~/.claude/configs/recursion-limits.json

  6. Author’s config-driven architecture. 14 JSON config files encoding all hook thresholds and rules. 

  7. Anthropic, “Claude Code Documentation,” 2025. Hook configuration and async execution. 

  8. Anthropic, “Claude Code Documentation,” 2025. Hook scope hierarchy. 

相關文章

為 Claude Code 打造自訂技能:完整教學指南

從零建構程式碼審查技能。涵蓋目錄結構、frontmatter欄位、基於LLM的比對、上下文預算和自動啟動。

5 分鐘閱讀

上下文視窗管理:50次開發實戰教會我的AI開發心法

我測量了50次Claude Code開發工作階段的token消耗量。上下文耗盡會在您察覺之前悄悄降低輸出品質。以下是解決這個問題的模式。

2 分鐘閱讀

Claude Code Hooks 教學:從零打造 5 個生產級 Hook

從零開始建立 5 個實用的 Claude Code hook:自動格式化、安全閘門、測試執行器、通知提醒與品質檢查。

3 分鐘閱讀