CoreCode部落格

用法

Claude Code Hooks 教程:怎麼配置 hooks、選事件、套用現成示例

釋出於 · 最後核對

Hooks 是使用者自定義的 shell 命令,Claude Code 在生命週期的特定時刻執行它們。執行的是 hook 而不是模型,所以這個動作一定會發生。這一頁是操作步驟:hook 放在哪裡、一條 hook 長什麼樣、哪些事件最常用、四個可以直接貼上的示例、退出碼怎麼攔截操作、hook 沒反應時看什麼。內容按 Anthropic 的 Automate actions with hooks 和 Hooks reference 中文整理,2026 年 10 月 30 日對照原文核對。

要點
  • hook 就是設定檔案裡的一個 hooks 塊:先選事件,再加 matcher 過濾,最後定義要執行的 handler。放在 ~/.claude/settings.json 對你所有專案生效,放在 .claude/settings.json 可以提交到倉庫和團隊共享。
  • 在 Claude Code 裡輸入 /hooks,確認 hook 登記在正確的事件下面。
  • 退出碼 0 表示成功;退出碼 2 在可以攔截的事件上阻止操作,你寫到 stderr 的內容就是原因;其他退出碼是非阻塞錯誤。
  • command hook 以你的完整使用者權限執行 shell 命令,新增之前先審查並測試每一條命令。

Hooks 是什麼,什麼時候該用

Hooks 是使用者自定義的 shell 命令。Claude Code 在生命週期的特定時刻執行它們,讓你獲得確定性的控制:某些動作一定會發生,而不是依賴 LLM 自己決定要不要執行。可以用 hooks 來落實專案規則、自動化重複任務,以及把 Claude Code 接進你已有的工具。

Anthropic 給出的用法包括:編輯後格式化檔案、在命令執行前攔截命令、Claude 需要輸入時發通知、工作階段開始時注入上下文。需要判斷而不是確定性規則的決定,指南指向 prompt hook 和 agent hook,它們用 Claude 模型來評估條件。想在每次工作階段開始時注入上下文,指南建議改用 CLAUDE.md。想給 Claude 額外的指令和可執行命令,見技能(skills)。

安全提醒:command hook 以你的完整使用者權限執行 shell 命令,可以修改、刪除或訪問你的使用者賬號能訪問的任何檔案。新增到配置之前,先審查並測試所有 hook 命令。

Hooks 配置在哪裡

Hooks 定義在 JSON 設定檔案裡。hook 加在哪裡,決定了它的作用範圍:

位置作用範圍能否共享
~/.claude/settings.json你的所有專案否,只在你的機器上
.claude/settings.json單個專案是,可以提交到倉庫
.claude/settings.local.json單個專案否,Claude Code 往裡儲存設定時會被 gitignore
託管策略設定(Managed policy settings)整個組織是,由管理員控制
外掛的 hooks/hooks.json外掛啟用時是,隨外掛打包
技能(skill)的 frontmatter技能被呼叫之後的本次工作階段是,定義在技能檔案裡
子代理(subagent)的 frontmatter該子代理執行期間是,定義在子代理檔案裡

在 Claude Code 裡輸入 /hooks,會開啟一個只讀的 hooks 瀏覽器,列出已配置的 hooks,並標明每個 hook 的來源,例如使用者設定、專案設定、本地設定、某個外掛或當前工作階段。想停用 hooks,在設定檔案裡寫 "disableAllHooks": true。Claude Code 執行時直接編輯設定檔案,檔案監視器通常會自動讀取 hook 的改動。

在互動工作階段裡,Claude Code 會先扣住所有設定檔案裡的 hooks,包括你自己的 ~/.claude/settings.json,直到你接受該資料夾的工作區信任對話框,或者接受某個信任範圍覆蓋到它的上級目錄。

一條 hook 的結構

配置分三層巢狀:選一個要響應的 hook 事件,比如 PreToolUse 或 Stop;加一個 matcher 組來過濾觸發時機,比如“只對 Bash 工具”;再定義匹配時要執行的一個或多個 hook handler。每個事件名都是同一個 hooks 物件裡的鍵。下面是 Anthropic 指南里的第一個 hook,macOS 上的桌面通知:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

如果你的設定檔案裡已經有 hooks 鍵,把新事件作為已有事件鍵的同級項加進去,不要替換整個物件。

內層 hooks 陣列裡的每個物件是一個 hook handler。大多數 hook 用 "type": "command",執行一條 shell 命令。另外還有四種型別:"type": "http"、"type": "mcp_tool"、"type": "prompt" 和 "type": "agent"。Agent hook 是實驗性的,可能會變。handler 的常用欄位:

欄位必填作用
type是"command"、"http"、"mcp_tool"、"prompt" 或 "agent"
if否用權限規則語法過濾這個 hook 何時執行,例如 "Bash(git *)" 或 "Edit(*.ts)"。只在工具事件上求值:PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest 和 PermissionDenied。在其他事件上,設了 if 的 hook 永遠不會執行
timeout否取消前等待的秒數。預設值:command、http、mcp_tool 為 600;prompt 為 30;agent 為 60
statusMessage否hook 執行期間顯示的自定義轉圈提示

Matcher 怎麼寫

沒有 matcher 時,hook 在對應事件每次發生時都會觸發。matcher 欄位用來過濾觸發時機,怎麼求值取決於它包含哪些字元:

Matcher 的值求值方式示例
"*"、"" 或省略匹配全部該事件每次發生都觸發
只含字母、數字、_、-、空格、, 和 |精確字串,或用 |、, 分隔的精確字串列表,分隔符兩側可以有空白Bash 只匹配 Bash 工具;Edit|Write 和 Edit, Write 都精確匹配這兩個工具中的任意一個
含有其他任何字元JavaScript 正規表示式,不錨定^Notebook 匹配名字以 Notebook 開頭的任何工具;mcp__memory__.* 匹配 memory 伺服器的所有工具

每種事件匹配的欄位不同:PreToolUse 和 PostToolUse 匹配工具名,SessionStart 匹配工作階段的啟動方式(startup、resume、clear、compact、fork),Notification 匹配通知型別。Matcher 區分大小寫。UserPromptSubmit、Stop 等少數事件不支援 matcher,每次發生都會觸發。

if 欄位比 matcher 更細,matcher 只在組的層面按工具名過濾。下面這個配置只在 Claude 用 git 命令時執行 hook,而不是所有 Bash 命令:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(git *)",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-git-policy.sh"
          }
        ]
      }
    ]
  }
}

這個過濾是盡力而為的,所以指南說,要強制執行硬性的允許或拒絕,請用權限系統,不要用 hook。

最常用的事件

事件觸發時,Claude Code 會並行執行匹配的 hooks。指南列出的事件遠不止這些;下表是本頁示例用到的,加上幾個常見的。

事件何時觸發能否攔截
SessionStart工作階段開始或恢復時不能。退出碼 2 只把 stderr 顯示給使用者
UserPromptSubmit提交提示詞時,在 Claude 處理之前能,攔截這條提示詞
PreToolUse工具呼叫執行之前能,攔截這次工具呼叫
PermissionRequest工具呼叫需要權限決定時不能。不認退出碼 2;要拒絕,改用 decision 物件
PostToolUse工具呼叫成功之後不能。把 stderr 顯示給 Claude;工具已經執行過了
NotificationClaude Code 發出通知時不能。退出碼和 stderr 都被忽略
StopClaude 回答完畢時能,阻止 Claude 停下,對話繼續
ConfigChange工作階段期間某個配置檔案發生變化時能,阻止這次變更生效(policy_settings 除外)
CwdChanged工作目錄變化時,例如 Claude 執行了 cd 命令不能。只把 stderr 顯示給使用者
PreCompact上下文壓縮之前能,阻止壓縮
SessionEnd工作階段結束時不能。只把 stderr 顯示給使用者

四個可以直接貼上的示例

每個都是 Anthropic 指南里現成可用的配置塊。Bash 示例用 jq 解析 JSON:macOS 上用 brew install jq 安裝,Debian 和 Ubuntu 上用 apt-get install jq,也可以看 jq 的下載頁。

Claude 需要輸入時收到通知

這個 hook 用 Notification 事件,Claude Code 在 Claude 等待輸入或權限時觸發它。macOS 的寫法在上一節裡;下面是 Linux 的,用 notify-send。加到 ~/.claude/settings.json:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "notify-send 'Claude Code' 'Claude Code needs your attention'"
          }
        ]
      }
    ]
  }
}

空的 matcher 對所有通知型別都觸發。只想在特定情況觸發,把它設成某個通知型別,例如 permission_prompt(Claude 需要你批准一次工具使用)或 idle_prompt(Claude 大約 60 秒前回答完,之後你一直沒有輸入)。macOS 上沒有通知時:osascript 透過內建的“指令碼編輯器”(Script Editor)應用發通知,如果指令碼編輯器沒有通知權限,命令會靜默失敗。Linux 上,notify-send 需要桌面通知常駐程式,無頭伺服器、SSH 工作階段和大多數容器都沒有。

編輯後自動格式化程式碼

這個 hook 用 PostToolUse 事件加 Edit|Write matcher,所以只在檔案編輯類工具之後執行。命令用 jq 取出被編輯的檔案路徑,再交給 Prettier。加到專案根目錄的 .claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

測試方法:讓 Claude 往一個 JavaScript 檔案里加一行帶單引號字串的程式碼,然後開啟檔案:用 Prettier 的預設設定,hook 會把它們改成雙引號。hook 成功時,Claude Code 不會在對話裡顯示任何東西,所以要看被編輯的檔案有沒有被重新格式化。Bash 命令也可能改寫檔案;想讓某個特定檔案不管怎麼變化都被重新格式化,改用 FileChanged hook。

攔截對受保護檔案的編輯

這個示例阻止 Claude 修改敏感檔案,比如 .env、package-lock.json,或者 .git/ 裡的任何東西。Claude 會收到說明編輯為什麼被攔截的反饋,從而調整做法。hook 呼叫一個指令碼,指令碼拿目標檔案路徑和一組受保護的模式比對,並用退出碼 2 攔截編輯。把指令碼存成 .claude/hooks/protect-files.sh:

#!/bin/bash
# protect-files.sh

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# Normalize Windows backslash separators so the patterns below match
FILE_PATH="${FILE_PATH//\\//}"

PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")

for pattern in "${PROTECTED_PATTERNS[@]}"; do
  if [[ "$FILE_PATH" == *"$pattern"* ]]; then
    echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
    exit 2
  fi
done

exit 0

hook 指令碼必須可執行,Claude Code 才能執行它。在 macOS 和 Linux 上:

chmod +x .claude/hooks/protect-files.sh

然後在 .claude/settings.json 里加一個 PreToolUse hook,在任何 Edit 或 Write 工具呼叫之前執行這個指令碼:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}

測試方法:讓 Claude 往你的 .env 檔案里加一行註釋。Claude Code 會在編輯執行之前攔截它,並把指令碼的 Blocked: 訊息作為反饋交給 Claude。

壓縮之後重新注入上下文

Claude 的上下文視窗滿了以後,壓縮(compaction)會把對話總結一遍來騰出空間,這可能丟掉重要細節。用帶 compact matcher 的 SessionStart hook,在每次壓縮之後重新注入關鍵上下文。你的命令寫到 stdout 的純文字,Claude Code 會加進 Claude 的上下文。加到專案根目錄的 .claude/settings.json:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.'"
          }
        ]
      }
    ]
  }
}

可以把 echo 換成任何會產生動態輸出的命令,比如用 git log --oneline -5 顯示最近的提交。

退出碼,以及攔截是怎麼生效的

Hooks 透過 stdin、stdout、stderr 和退出碼與 Claude Code 通訊。事件觸發時,Claude Code 把和事件相關的資料以 JSON 傳給你指令碼的 stdin。對一條 Bash 命令,PreToolUse hook 收到的欄位包括 session_id、cwd、hook_event_name、tool_name 和 tool_input,其中 tool_input.command 是那條 shell 命令。下面這個指令碼攔截一條命令:

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q "drop table"; then
  echo "Blocked: dropping tables is not allowed" >&2  # stderr becomes Claude's feedback
  exit 2                                               # exit 2 = block the action
fi

exit 0  # exit 0 = no decision; the normal permission flow applies

退出碼為 0 的 hook,Claude 永遠看不到它的 stderr。想從 PostToolUse hook 向 Claude 傳一條警告,改用退出碼 2,這樣即使工具已經執行過,Claude 也能看到 stderr。PostToolUse hook 無法撤銷已經發生的動作,因為工具已經執行了。

退出碼只能讓你攔截或保持沉默。想要更細的控制,退出碼用 0,並往 stdout 列印一個 JSON 物件。每個 hook 只選一種方式:退出碼 2 加 stderr 訊息來攔截,或者退出碼 0 加 JSON 來做結構化控制。例如,PreToolUse hook 可以拒絕一次工具呼叫並告訴 Claude 原因:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Use rg instead of grep for better performance"
  }
}

返回 "deny" 時,Claude Code 取消這次工具呼叫,並把 permissionDecisionReason 反饋給 Claude。返回 permissionDecision: "deny" 的 hook,即使在 bypassPermissions 模式下或帶 --dangerously-skip-permissions 時,也會攔截工具。多個 hook 匹配同一個事件時,每個 hook 的命令都會執行完,Claude Code 才合併結果,所以某個 hook 返回 deny 並不會阻止同級的其他 hook 執行。

怎麼除錯 hook

echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh
echo $?  # Check the exit code
claude --debug-file /tmp/claude.log
tail -f /tmp/claude.log

常見問題排查

現象檢查什麼
hook 從不觸發執行 /hooks,確認它出現在正確的事件下。檢查 matcher 是否和工具名完全一致,matcher 區分大小寫。確認你觸發的是對的事件型別:PreToolUse 在工具執行前觸發,PostToolUse 在之後觸發。
/hooks 裡沒有 hooks幾秒鐘後還沒出現,就重啟工作階段強制重新載入。確認 JSON 合法:不允許尾逗號和註釋。確認檔案位置正確:專案 hooks 用 .claude/settings.json,全域性 hooks 用 ~/.claude/settings.json。
對話記錄裡出現 “PreToolUse hook error: ...”你的指令碼意外地以非零退出碼退出。往它裡面管道輸入示例 JSON 手動測試。指令碼根本沒執行時,給它加可執行權限:chmod +x ./my-hook.sh。
“command not found”引用指令碼時用絕對路徑或 ${CLAUDE_PROJECT_DIR}。
“jq: command not found”安裝 jq,或者改用 Python/Node.js 解析 JSON。
hook 列印了 JSON 卻沒有效果可能有別的東西先寫了 stdout,通常是 shell 配置檔案裡無條件的 echo,輸出就不再以 { 開頭。把這些 echo 包起來,只在互動式 shell 裡執行。也可能是欄位放錯了層級:permissionDecision 屬於 hookSpecificOutput 內部,不是頂層。用 claude --debug 啟動,在除錯日誌裡搜 Hook output does not start with { 或 Hook JSON output had unrecognized keys。
Claude 一直在幹活,並警告 Stop hook 攔截次數太多Stop hook 在連續攔截八次、期間 Claude 沒有任何工具呼叫時,Claude Code 會推翻它。從 JSON 輸入裡解析 stop_hook_active 欄位,如果是 true 就提前退出。

stop_hook_active 的檢查寫法如下:

#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0  # Allow Claude to stop
fi
# ... rest of your hook logic

常見問題

什麼時候該用 hook,而不是在 CLAUDE.md 裡寫一條指令?

某個動作必須一定發生時用 hook。hook 是 Claude Code 在特定時刻執行的 shell 命令,不依賴 LLM 自己決定要不要執行。想在每次工作階段開始時注入上下文,Anthropic 的指南建議改用 CLAUDE.md;壓縮之後重新注入上下文的例子,則是帶 compact matcher 的 SessionStart hook。

hook 該放進哪個設定檔案?

對你所有專案生效、又只留在你機器上的 hook,放 ~/.claude/settings.json。只針對一個專案、可以提交進倉庫的,放 .claude/settings.json。只針對一個專案又不想共享的,放 .claude/settings.local.json。不同層級的 hook 條目是合併的,不會互相替換。

在繞過權限的模式下,hook 還能攔截嗎?

能。返回 permissionDecision: "deny" 的 PreToolUse hook,即使在 bypassPermissions 模式下或帶 --dangerously-skip-permissions 時也會攔截工具,因為 PreToolUse hooks 在任何權限模式檢查之前觸發。返回 "allow" 的 hook 繞不過設定裡的 deny 規則。

來源