免費

Claude Code Hooks 完整指南:讓 Claude 的每個動作都在你掌控之中

完整解析 Claude Code Hooks 機制,從五種類型到退出碼控制,搭配四個真實配置案例,讓 AI 操作可審計、可攔截、可自動化。


每次 Claude Code 讀檔案、寫程式碼、執行指令,背後都有一套事件系統在運作。Hooks 就是接入這套系統的介面——你可以在任意時機注入自己的邏輯,自動做程式碼檢查、記錄日誌、攔截危險操作,或者觸發任何 shell 指令。

這篇文章把 Hooks 的機制、設定方式和實戰用法完整講一遍。


Hooks 是什麼

Hooks 是設定在 settings.json 裡的 shell 指令,Claude Code 在特定事件發生時自動執行它們。

最直接的類比:git hooks。git 在 commit、push 等操作前後可以觸發腳本,Claude Code Hooks 的思路完全一樣,只是觸發點是 AI 的工具呼叫。

為什麼這很重要?

Claude Code 的能力越強,你越需要確定性的控制層。Hooks 提供的是:
- 不依賴 prompt 的執行保證(Claude 可能忽略指令,但 hook 一定跑)
- 可審計的操作記錄
- 自動化的品質檢查


五種 Hook 類型

類型 觸發時機 典型用途
PreToolUse 工具呼叫 攔截危險操作、記錄意圖
PostToolUse 工具呼叫 自動 lint、執行測試
PreCompact 上下文壓縮 儲存當前狀態快照
Notification Claude 發出通知時 桌面推播、Slack 訊息
Stop Claude 完成回覆時 彙整日誌、觸發後續流程

最常用的是 PreToolUsePostToolUse,圍繞工具呼叫做攔截和後處理。


設定格式

Hooks 寫在 ~/.claude/settings.json(全域)或專案根目錄的 .claude/settings.json(專案級)裡:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npm run lint --silent"
          }
        ]
      }
    ]
  }
}

三個核心欄位:

  • matcher:匹配工具名的正規表示式,決定這個 hook 在哪些工具呼叫後觸發。"Write|Edit" 表示寫檔案或編輯檔案時觸發。留空或 ".*" 匹配所有工具。
  • type:目前只有 "command"
  • command:任意 shell 指令。

工具名稱參考

matcher 時需要知道工具叫什麼名字:

工具名稱 對應操作
Write 寫入新檔案
Edit 編輯檔案
Bash 執行 shell 指令
Read 讀取檔案
Glob 檔案搜尋
Grep 內容搜尋
TodoWrite 更新任務清單

Hook 的執行環境

執行時透過 stdin 傳入 JSON:

{
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.rb",
    "content": "..."
  },
  "tool_response": "..."
}

PreToolUse 拿到 tool_input(呼叫前的入參),PostToolUse 同時拿到 tool_response(工具回傳結果)。

可以用這些資料寫有判斷邏輯的 hook:

#!/bin/bash
input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.file_path')

# 只對 .rb 檔案執行 rubocop
if [[ "$file" == *.rb ]]; then
  rubocop "$file" --autocorrect-all --no-color
fi

退出碼的含義

Hook 的退出碼控制 Claude Code 的後續行為:

退出碼 含義
0 成功,繼續執行
2 阻斷:取消當前工具呼叫,將 stderr 輸出回饋給 Claude
其他非零 記錄錯誤,但繼續執行

退出碼 2 是最有用的——它讓你可以在 PreToolUse 裡寫攔截邏輯,阻止 Claude 執行某個操作,並告訴它原因。


實戰:四個真實 Hook 設定

1. 寫檔案後自動格式化

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'file=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .file_path 2>/dev/null); [[ \"$file\" == *.rb ]] && rubocop -A \"$file\" --no-color -q || true'"
          }
        ]
      }
    ]
  }
}

2. 禁止刪除特定目錄

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'cmd=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .command); if echo \"$cmd\" | grep -qE \"rm.*/(migrations|seeds)\"; then echo \"禁止刪除 migrations 或 seeds 目錄\" >&2; exit 2; fi'"
          }
        ]
      }
    ]
  }
}

3. 操作日誌

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit|Bash",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"$(date '+%Y-%m-%d %H:%M:%S') $CLAUDE_TOOL_NAME: $(echo $CLAUDE_TOOL_INPUT | jq -c .)\" >> ~/.claude/audit.log"
          }
        ]
      }
    ]
  }
}

4. 完成後桌面通知

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude 已完成\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

全域 vs 專案級

兩個位置可以放 Hooks:

全域~/.claude/settings.json):適合所有專案通用的規則,比如日誌、通知。

專案級.claude/settings.json):適合專案專屬的檢查。專案級 hooks 和全域 hooks 會合併執行,不會互相覆蓋。

團隊專案建議把專案級 settings.json 提交到 git,這樣所有人都跑相同的 hooks。


除錯 Hook

Hook 不生效時的排查步驟:

  1. 確認 matcher 拼寫:工具名區分大小寫,write 不等於 Write
  2. 單獨跑指令:把 hook 裡的 command 複製出來在終端機直接執行
  3. 看 Claude Code 輸出:hooks 的 stderr 會顯示在對話裡
  4. 簡化測試:先用 echo "hook triggered" >> /tmp/hook.log 確認觸發

小結

Hooks 的核心價值是把不確定的 AI 行為和確定的工程規範接在一起。Claude 可能忘記執行測試,但 PostToolUse hook 不會。Claude 可能誤刪檔案,但 PreToolUse 攔截不會放行。

從一個 hook 開始:寫檔案後自動 lint。跑順了再加其他的。