Hooks 事件掛鉤:自動化你的 Claude Code 工作流程

Claude Code 的 Hooks(事件掛鉤)是一套強大的自動化機制,讓你可以在 Claude Code 的生命週期中插入自訂邏輯。無論是在工具執行前進行安全檢查、在檔案寫入後自動執行格式化、還是在 Session 啟動時初始化環境變數,Hooks 都能幫你實現。透過在 settings.json 中設定 Shell 指令、HTTP 請求、Prompt 或 Agent,你可以完全掌控 Claude Code 的行為,打造符合團隊需求的自動化工作流程。

什麼是 Hooks

Hooks 是使用者定義的自動化處理程式,會在 Claude Code 生命週期的特定時間點自動觸發執行。你可以把它想像成程式開發中的「事件監聽器」——當 Claude Code 執行特定動作(如使用工具、啟動 Session、完成回應)時,你預先設定好的 Hook 就會自動執行。

Hooks 支援四種處理類型:

  • Command Hooks:執行 Shell 指令,透過 stdin 接收事件資料,用 exit code 和 stdout 回傳結果
  • HTTP Hooks:發送 JSON POST 請求到指定的 URL 端點,適合整合外部服務
  • Prompt Hooks:將事件資料送給 Claude 模型評估,回傳 yes/no 決策
  • Agent Hooks:啟動子代理進行驗證,可使用 Read、Grep、Glob 等工具

每種類型都有各自適合的應用場景。Command Hooks 最靈活且常用,適合大部分自動化需求;HTTP Hooks 適合與外部審批系統整合;Prompt Hooks 讓 AI 幫你做智慧判斷;Agent Hooks 則適合需要深入分析程式碼的驗證任務。

Hooks 的設定方式

Hooks 的設定統一放在 settings.json 中,根據適用範圍的不同,可以放在以下位置:

位置路徑適用範圍
使用者設定~/.claude/settings.json所有專案(僅本機)
專案設定.claude/settings.json此專案(可提交到版本控制)
專案本機設定.claude/settings.local.json此專案(僅本機)
企業管理設定Managed policy settings整個組織
Plugin Hookshooks/hooks.json啟用該 Plugin 時

基本的設定結構如下,每個事件名稱下可以設定多組 matcher 和 hooks:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/check-bash.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

其中 matcher 欄位用來過濾觸發條件,支援正規表達式語法。例如 Bash 只匹配 Bash 工具、Edit|Write 匹配 Edit 或 Write 工具、mcp__.* 匹配所有 MCP 工具。沒有設定 matcher 的事件(如 StopUserPromptSubmit)會在每次事件發生時都觸發。

Hook 的生命週期事件

Claude Code 的 Hooks 涵蓋了整個使用生命週期,從 Session 啟動到結束,每個階段都有對應的事件可以掛鉤。以下是主要的事件分類:

Session 階段

事件觸發時機Matcher
SessionStartSession 啟動或恢復時startupresumeclearcompact
InstructionsLoadedCLAUDE.md 或規則檔案載入時session_startnested_traversalinclude
NotificationClaude 發送通知時notification(對話繼續需要注意)
StopClaude 完成回應時end_turn(主動結束)或 max_turns(達到上限)

工具執行階段(最常用)

事件觸發時機可否阻擋
PreToolUse工具執行之前可以(allow/deny/ask)
PostToolUse工具執行成功之後可以回饋訊息
PostToolUseFailure工具執行失敗之後可提供額外上下文
PermissionRequest權限對話框出現時可自動允許或拒絕
PermissionDeniedAuto mode 拒絕工具呼叫時可要求重試

回應與通知階段

事件觸發時機說明
UserPromptSubmit使用者提交 Prompt 時可過濾或阻擋特定內容
StopClaude 完成回應時可阻擋讓 Claude 繼續工作
StopFailureAPI 錯誤導致中止時用於錯誤監控與通知
NotificationClaude 發送通知時可自訂通知行為
SubagentStart / SubagentStop子代理啟動或結束時可注入額外上下文

PreToolUse:工具執行前的守門員

PreToolUse 是最重要也最常用的 Hook 事件,因為它是唯一能夠在工具執行前攔截並阻擋操作的事件。當 Claude 準備使用任何工具(Bash、Edit、Write、Read 等)時,PreToolUse Hook 會先被觸發,你可以在這裡進行安全檢查、自動審批或修改工具輸入。

Hook 接收到的輸入 JSON 包含工具名稱和工具參數:

{
  "session_id": "abc123",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf node_modules",
    "description": "Remove node_modules"
  },
  "tool_use_id": "toolu_01ABC..."
}

Hook 可以透過輸出 JSON 來控制 Claude 的行為,最關鍵的是 permissionDecision 欄位,支援四種決策:

決策效果適用場景
allow允許工具執行,跳過權限提示自動核准安全指令
deny阻擋工具執行封鎖危險操作
ask強制顯示權限提示敏感操作需要人工確認
defer不做決定,交由下一個 Hook 或預設行為處理條件不匹配時

以下是一個封鎖危險 Bash 指令的 PreToolUse Hook 範例:

#!/bin/bash
# .claude/hooks/block-dangerous.sh

# 從 stdin 讀取事件 JSON
COMMAND=$(jq -r '.tool_input.command' < /dev/stdin)

# 檢查是否包含危險指令
if echo "$COMMAND" | grep -qE 'rm -rf /|mkfs|dd if=|:(){ :|&}'; then
  # 輸出 JSON 阻擋執行
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "偵測到危險指令,已自動阻擋"
    }
  }'
else
  # exit 0 且不輸出 JSON 表示不干預
  exit 0
fi

對應的 settings.json 設定:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous.sh",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

PostToolUse:工具執行後的品質把關

PostToolUse 在工具成功執行之後觸發,適合用來進行後處理作業,例如自動格式化程式碼、執行 Lint 檢查、記錄操作日誌等。與 PreToolUse 不同的是,PostToolUse 無法阻擋已完成的操作,但可以將檢查結果回饋給 Claude,讓 Claude 根據結果調整後續行為。

PostToolUse Hook 接收到的 JSON 除了 tool_input 外,還包含 tool_response(工具執行結果):

{
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/project/src/utils.ts",
    "content": "..."
  },
  "tool_response": {
    "filePath": "/project/src/utils.ts",
    "success": true
  }
}

以下是一個在檔案寫入後自動執行 ESLint 檢查的範例:

#!/bin/bash
# .claude/hooks/auto-lint.sh

FILE_PATH=$(jq -r '.tool_input.file_path' < /dev/stdin)

# 只對 TypeScript 和 JavaScript 檔案執行
if [[ "$FILE_PATH" == *.ts ]] || [[ "$FILE_PATH" == *.tsx ]] || \
   [[ "$FILE_PATH" == *.js ]] || [[ "$FILE_PATH" == *.jsx ]]; then
  
  LINT_RESULT=$(npx eslint "$FILE_PATH" 2>&1)
  
  if [ $? -ne 0 ]; then
    # 將 Lint 錯誤回饋給 Claude
    jq -n --arg ctx "$LINT_RESULT" '{
      hookSpecificOutput: {
        hookEventName: "PostToolUse",
        additionalContext: ("ESLint 檢查發現問題,請修正:\n" + $ctx)
      }
    }'
  fi
fi

exit 0

設定方式如下,使用 Edit|Write 作為 matcher 來同時匹配編輯和寫入操作:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/auto-lint.sh"
          }
        ]
      }
    ]
  }
}

Notification Hook:自訂通知行為

Notification 事件在 Claude Code 發送通知時觸發,例如等待使用者輸入、需要權限確認、驗證成功等情境。你可以透過 matcher 過濾特定類型的通知:

Matcher 值觸發時機
permission_promptClaude 需要使用者授權工具時
idle_promptClaude 等待使用者輸入時
auth_success驗證成功時
elicitation_dialogMCP Server 請求輸入時

以下範例在 Claude 等待權限確認時,自動發送桌面通知提醒你:

#!/bin/bash
# .claude/hooks/desktop-notify.sh

INPUT=$(cat)
MESSAGE=$(echo "$INPUT" | jq -r '.message')
TITLE=$(echo "$INPUT" | jq -r '.title // "Claude Code"')

# macOS 桌面通知
if command -v osascript &> /dev/null; then
  osascript -e "display notification \"$MESSAGE\" with title \"$TITLE\""
fi

# Linux 桌面通知
if command -v notify-send &> /dev/null; then
  notify-send "$TITLE" "$MESSAGE"
fi

exit 0
{
  "hooks": {
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/desktop-notify.sh",
            "async": true
          }
        ]
      }
    ]
  }
}

注意這裡使用了 "async": true,讓通知腳本在背景執行,不會阻塞 Claude Code 的主流程。

Hook 的四種處理類型詳解

Command Hook:Shell 指令

Command Hook 是最基本也最靈活的類型,透過執行 Shell 指令來處理事件。事件的 JSON 資料會透過 stdin 傳入,Hook 透過 exit code 和 stdout 回傳結果。

Exit Code意義JSON 處理
0成功解析 stdout 的 JSON
2阻擋性錯誤忽略 JSON,使用 stderr 訊息
其他非阻擋性錯誤忽略 JSON,在 verbose 模式顯示 stderr
{
  "type": "command",
  "command": ".claude/hooks/my-hook.sh",
  "shell": "bash",
  "timeout": 30,
  "async": false
}

HTTP Hook:遠端服務整合

HTTP Hook 將事件 JSON 以 POST 請求發送到指定的 URL,非常適合與團隊的審批系統、日誌服務或監控平台整合。支援自訂 Headers 和環境變數插值:

{
  "type": "http",
  "url": "https://approval.example.com/hooks/validate",
  "headers": {
    "Authorization": "Bearer $MY_API_TOKEN"
  },
  "allowedEnvVars": ["MY_API_TOKEN"],
  "timeout": 10
}

遠端端點回傳的 JSON 格式與 Command Hook 相同,非 2xx 回應碼會被視為非阻擋性錯誤。

Prompt Hook:AI 智慧判斷

Prompt Hook 將事件資料送給 Claude 模型,讓 AI 做出 allow 或 deny 的智慧判斷。使用 $ARGUMENTS 占位符來插入事件 JSON:

{
  "type": "prompt",
  "prompt": "審查以下 Shell 指令是否安全。如果指令可能刪除重要檔案、修改系統設定或存取敏感資料,回答 deny;否則回答 allow。指令內容:$ARGUMENTS",
  "timeout": 30
}

Agent Hook:子代理驗證

Agent Hook 會啟動一個子代理來進行更深入的驗證,子代理可以使用 Read、Grep、Glob 等工具來分析程式碼,適合需要理解上下文才能做出判斷的場景:

{
  "type": "agent",
  "prompt": "驗證這個指令是否符合專案的安全政策。檢查 .claude/SECURITY.md 中的規則,並確認指令不會違反任何安全限制。指令內容:$ARGUMENTS",
  "timeout": 60
}

共用設定欄位參考

所有類型的 Hook 都支援以下共用欄位:

欄位必填說明
type處理類型:commandhttppromptagent
if額外的過濾條件,使用權限規則語法,例如 Bash(git *)
timeout逾時秒數,預設值依類型不同(Command: 600s、HTTP: 30s、Prompt: 30s、Agent: 60s)
statusMessage執行時顯示的自訂 Spinner 訊息
once設為 true 時每個 Session 只執行一次(僅限 Skill 中的 Hook)

if 欄位特別有用,它讓你可以在 matcher 的基礎上進一步過濾。例如你想只針對 rm 開頭的 Bash 指令觸發 Hook,可以設定 "if": "Bash(rm *)",這樣 lsgit status 等指令就不會觸發。

Hook 中可用的環境變數

Claude Code 會在執行 Hook 時提供幾個實用的環境變數:

變數說明
$CLAUDE_PROJECT_DIR專案根目錄路徑(建議用雙引號包裹,以處理路徑中的空格)
${CLAUDE_PLUGIN_ROOT}Plugin 安裝目錄
${CLAUDE_PLUGIN_DATA}Plugin 持久化資料目錄

CLAUDE_ENV_FILE 是一個特殊的環境變數,只在特定事件中可用。你可以透過寫入這個檔案來持久化環境變數,讓後續的 Claude Code 操作都能存取到這些變數:

#!/bin/bash
# 在 SessionStart hook 中初始化環境
if [ -n "$CLAUDE_ENV_FILE" ]; then
  # 載入 nvm 並設定 Node 版本
  export NVM_DIR="$HOME/.nvm"
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
  nvm use 18
  
  # 將環境變數寫入持久化檔案
  echo 'export NODE_ENV=development' >> "$CLAUDE_ENV_FILE"
  echo 'export DEBUG=app:*' >> "$CLAUDE_ENV_FILE"
fi

exit 0

實戰應用範例

範例一:自動核准安全指令

在日常開發中,你可能會頻繁使用 git statusnpm testls 等安全指令,每次都需要手動確認權限會降低效率。以下 Hook 可以自動核准這些安全指令:

#!/bin/bash
# .claude/hooks/approve-safe-commands.sh

COMMAND=$(jq -r '.tool_input.command' < /dev/stdin)

# 定義安全指令的模式
if [[ "$COMMAND" =~ ^(npm|yarn|pnpm).*(test|lint|build|check) ]] || \
   [[ "$COMMAND" =~ ^git.*(status|log|diff|branch|tag) ]] || \
   [[ "$COMMAND" =~ ^(ls|pwd|cat|head|tail|wc|echo) ]]; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "allow",
      permissionDecisionReason: "安全的唯讀指令,已自動核准"
    }
  }'
else
  exit 0
fi

範例二:操作日誌記錄

在團隊或企業環境中,記錄 Claude Code 的所有操作是很重要的合規需求。以下 Hook 會將每次工具使用記錄到日誌檔:

#!/bin/bash
# .claude/hooks/audit-log.sh

INPUT=$(cat)
EVENT=$(echo "$INPUT" | jq -r '.hook_event_name')
TOOL=$(echo "$INPUT" | jq -r '.tool_name // "N/A"')
SESSION=$(echo "$INPUT" | jq -r '.session_id')
CWD=$(echo "$INPUT" | jq -r '.cwd')

# 記錄到日誌檔
LOG_DIR="$CLAUDE_PROJECT_DIR/.claude/logs"
mkdir -p "$LOG_DIR"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Event=$EVENT Tool=$TOOL Session=$SESSION CWD=$CWD" \
  >> "$LOG_DIR/audit.log"

exit 0

將這個 Hook 同時掛載到 PreToolUse 和 PostToolUse 事件,就能完整記錄所有工具的使用情況:

{
  "hooks": {
    "PreToolUse": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/audit-log.sh",
            "async": true
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/audit-log.sh",
            "async": true
          }
        ]
      }
    ]
  }
}

範例三:Prompt 內容過濾

在企業環境中,你可能需要防止 Claude 接收包含敏感資訊的 Prompt。以下 Hook 使用 UserPromptSubmit 事件來過濾使用者輸入:

#!/bin/bash
# .claude/hooks/filter-prompts.sh

PROMPT=$(jq -r '.prompt' < /dev/stdin)

# 檢查是否包含敏感關鍵字
if echo "$PROMPT" | grep -qi 'password\|api.key\|secret\|credential'; then
  jq -n '{
    decision: "block",
    reason: "Prompt 中包含敏感資訊關鍵字,請移除後再試"
  }'
else
  exit 0
fi
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/filter-prompts.sh"
          }
        ]
      }
    ]
  }
}

範例四:Session 啟動環境初始化

利用 SessionStart Hook 在每次啟動 Claude Code 時自動初始化開發環境,載入專案所需的環境變數和工具版本:

#!/bin/bash
# .claude/hooks/init-env.sh

if [ -n "$CLAUDE_ENV_FILE" ]; then
  # 載入 .env 檔案中的環境變數
  if [ -f "$CLAUDE_PROJECT_DIR/.env" ]; then
    while IFS= read -r line; do
      [[ "$line" =~ ^#.*$ ]] && continue
      [[ -z "$line" ]] && continue
      echo "export $line" >> "$CLAUDE_ENV_FILE"
    done < "$CLAUDE_PROJECT_DIR/.env"
  fi
  
  # 設定專案特定的工具路徑
  echo "export PATH=$CLAUDE_PROJECT_DIR/node_modules/.bin:\$PATH" >> "$CLAUDE_ENV_FILE"
fi

# 回傳環境資訊給 Claude
jq -n '{
  hookSpecificOutput: {
    hookEventName: "SessionStart",
    additionalContext: "開發環境已初始化。.env 已載入,node_modules/.bin 已加入 PATH。"
  }
}'
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/init-env.sh"
          }
        ]
      }
    ]
  }
}

除錯與管理 Hooks

Claude Code 提供了幾個實用的工具來幫助你除錯和管理 Hooks:

查看已設定的 Hooks:在 Claude Code 中輸入 /hooks 指令,會在瀏覽器中開啟一個頁面,顯示所有已設定的 Hooks 及其來源(User、Project、Local、Plugin、Session 或 Built-in)。

停用所有 Hooks:如果你需要暫時停用所有 Hooks(例如排除問題時),可以在 settings.json 中加入:

{
  "disableAllHooks": true
}

注意,這個設定會遵循設定層級,企業管理層級設定的 Hooks 只能透過企業管理設定中的 disableAllHooks 來停用。

輸出上限:Hook 的 JSON 輸出有 10,000 字元的上限。超過此限制的內容會被儲存到檔案中,不會直接傳遞給 Claude。因此建議保持 Hook 輸出簡潔,只回傳必要的資訊。

常見問題與疑難排解

問題解決方案
Hook 沒有觸發確認 matcher 正確匹配工具名稱(區分大小寫),使用 /hooks 確認 Hook 已載入
Hook 觸發了但沒有效果確認 exit code 為 0,且 stdout 輸出的是合法的 JSON 格式
Hook 執行太慢設定較短的 timeout,或使用 "async": true 在背景執行
JSON 解析錯誤使用 jq -n 來生成 JSON 而非手動拼接字串,確保特殊字元被正確跳脫
環境變數無法存取確認 Hook 腳本有可執行權限(chmod +x),且 $CLAUDE_PROJECT_DIR 路徑正確
PreToolUse 的 allow/deny 無效使用 hookSpecificOutput.permissionDecision 而非頂層的 decision 欄位

Hooks 最佳實踐

  • 保持 Hook 腳本簡短快速:Hook 會阻塞 Claude Code 的主流程(除非使用 async),長時間執行的腳本會影響使用體驗。非必要的後處理可以使用 "async": true
  • 善用 matcher 和 if 過濾:避免對所有工具呼叫都觸發 Hook,只針對特定工具和特定模式觸發,減少不必要的開銷
  • 使用 jq 處理 JSON:Hook 的輸入和輸出都是 JSON 格式,jq 是處理 JSON 的最佳工具,避免用字串操作來解析或生成 JSON
  • 正確處理 exit code:exit 0 表示成功、exit 2 表示阻擋性錯誤、其他值表示非阻擋性錯誤。確保你的腳本在所有路徑上都有正確的 exit code
  • 將安全相關的 Hook 放在專案設定中:將 .claude/hooks/ 目錄和 .claude/settings.json 提交到版本控制,確保團隊所有成員都使用相同的安全規則
  • 測試 Hook 腳本:在正式使用之前,先用 echo '{"tool_name":"Bash","tool_input":{"command":"test"}}' | ./hook.sh 手動測試你的 Hook 腳本是否正確運作

總結

Hooks 是 Claude Code 中實現自動化工作流程的核心機制。透過 PreToolUse 你可以建立安全防線,透過 PostToolUse 你可以自動化品質檢查,透過 SessionStart 你可以初始化開發環境,透過 Notification 你可以自訂通知行為。四種處理類型(Command、HTTP、Prompt、Agent)讓你能夠靈活地整合各種工具和服務,無論是本地腳本還是遠端 API 都能輕鬆對接。

建議從最簡單的 Command Hook 開始,先建立一個自動核准安全指令的 PreToolUse Hook 來提升日常開發效率,再根據團隊需求逐步擴展到日誌記錄、程式碼品質檢查等進階應用。

延伸閱讀