Skip to content

aliceric27/claude-code-hooks

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 

Repository files navigation

Claude Code Hooks 完整指南

透過註冊 shell 命令,客製化並擴展您的 Claude Code 工作流程。

📖 目錄

總覽

什麼是 Hooks?

Hooks 是您定義的 shell 命令,它們會在 Claude Code 生命週期的特定時間點自動執行。您可以把它們想像成是為您的 AI 程式設計夥伴設定的「自動化規則」或「觸發器」。

與其在提示中反覆告訴 Claude 要遵守某些規則,不如將這些規則編碼為 Hooks,使其成為您開發環境中可靠且自動化的一部分。

為什麼要使用 Hooks?

Hooks 讓您能夠對 Claude Code 的行為進行確定性的控制,確保某些動作總是會發生。常見的使用情境包括:

  • ✍️ 自動格式化: 在 Claude 每次編輯完畢後,自動對 .ts 檔案執行 prettier,或對 .go 檔案執行 gofmt
  • 🔔 自訂通知: 當 Claude 等待您的指令或執行權限時,透過系統通知或聲音提醒您。
  • 🛡️ 安全防護: 阻止 Claude 執行危險的命令(例如 rm -rf)或修改生產環境的設定檔。
  • 📝 合規性記錄: 追蹤所有由 Claude 執行的 shell 命令,以滿足稽核或除錯需求。
  • 💡 自動回饋: 當 Claude 產生的程式碼不符合您的專案規範時,自動向它提供修正建議。

入門教學

本教學將引導您建立三個 Hooks,從簡單到複雜,逐步掌握核心概念。

步驟 1:您的第一個 Hook - 檔案變更日誌

我們的第一個 Hook 非常簡單:每當 Claude 儲存一個檔案時,我們就在一個日誌檔中記錄下來。這個範例不需要任何外部工具。

  1. 在終端機中執行 /hooks 命令,打開 Hooks 設定介面。
  2. 從事件列表中選擇 PostToolUse。這個事件會在 Claude 成功執行工具之後觸發。
  3. 選擇 + Add new matcher…,輸入 Write|Edit|MultiEdit 來匹配檔案操作工具。
  4. 選擇 + Add new hook…,然後輸入以下命令:
    echo "Claude edited a file at $(date)" >> ~/.claude/file-edit-log.txt
  5. Enter 儲存 Hook。系統會詢問您儲存位置,選擇 User settings(使用者設定),這樣這個 Hook 就會在您所有的專案中生效。
  6. Esc 退出設定介面。

驗證一下:現在,請 Claude 隨意修改您專案中的任何一個檔案。完成後,檢查日誌檔的內容:

cat ~/.claude/file-edit-log.txt

您應該會看到類似 "Claude edited a file at [日期時間]" 的訊息。恭喜,您已經成功建立了第一個 Hook!

步驟 2:加入條件 - 使用匹配器

上一個 Hook 會在任何檔案被編輯後觸發。如果我們只想在特定工具被使用時觸發呢?這時就需要使用「匹配器」。

  1. 再次執行 /hooks,選擇 PostToolUse 事件。
  2. 選擇 + Add new matcher…,輸入 Bash。這樣 Hook 就只會在 Claude 執行 shell 命令後觸發。
  3. 新增一個 Hook 命令:
    echo "Claude ran a bash command at $(date)" >> ~/.claude/bash-log.txt
  4. 儲存設定並退出。

驗證一下

  • 請 Claude 執行一個 shell 命令,例如「列出目前目錄的檔案」。檢查 ~/.claude/bash-log.txt,您會發現新增了一條日誌。
  • 再請 Claude 編輯一個檔案。這次只會在 file-edit-log.txt 中看到記錄,而不會在 bash-log.txt 中看到。

匹配器讓您可以精準控制 Hook 的觸發時機與對象。

步驟 3:與 Claude 互動 - 記錄執行的命令

現在,我們來挑戰一個更進階的 Hook:記錄 Claude 嘗試執行的所有 shell 命令詳細資訊。這個 Hook 會讀取 Claude 傳遞的資料,並需要 jq 工具來解析 JSON。

先決條件:請確保您已安裝 jq。如果沒有,請使用您的套件管理器安裝:

  1. 執行 /hooks,這次選擇 PreToolUse 事件。這個事件在 Claude 準備執行一個工具(如 bash 命令)之前觸發。
  2. 為這個 Hook 新增一個匹配器,輸入 Bash,這樣它就只會監控 shell 命令。
  3. 選擇 + Add new hook… 並輸入以下命令:
    jq -r '"COMMAND: \(.tool_input.command) | DESCRIPTION: \(.tool_input.description // "None")"' >> ~/.claude/bash-command-log.txt
    這個命令會從傳入的 JSON 資料中提取 commanddescription 欄位,並將其格式化後寫入日誌檔。
  4. 儲存為 User settings 並退出。

驗證一下:請 Claude 執行一個 shell 命令,例如「列出目前目錄的所有檔案」。在您授權 Claude 執行之前,這個 Hook 就已經被觸發了。檢查日誌檔:

cat ~/.claude/bash-command-log.txt

您應該會看到類似 COMMAND: ls -l | DESCRIPTION: List all files in the current directory 的記錄。

透過這個教學,您已經學會了:

  • 如何在特定事件上建立 Hook
  • 如何使用匹配器來過濾事件
  • 如何讀取 Claude 傳遞的上下文資料

核心概念

Hook 事件生命週期

Hooks 可以在 Claude Code 生命週期的多個時間點觸發。以下是主要的事件類型:

事件名稱 觸發時機 常見用途 可阻止操作
UserPromptSubmit 使用者提交提示後,Claude 處理之前 驗證提示、根據提示內容注入額外上下文 ✅ 是
PreToolUse 在工具(如 Bash)被執行之前 阻止危險命令、記錄意圖、修改命令 ✅ 是
PostToolUse 在工具執行之後 記錄執行結果、基於結果觸發下一步 ❌ 否
Notification 當需要發送通知時 自訂通知方式、聲音提醒 ❌ 否
Stop 當 Claude 完成任務或遇到錯誤時 清理工作、後續處理 ✅ 是
SubagentStop 當子代理(Task 工具)完成時 針對子任務的後續處理 ✅ 是
PreCompact 在 Claude 執行上下文壓縮之前 根據壓縮類型執行不同操作、備份重要上下文 ❌ 否

Hook 的輸入 (stdin)

每個 Hook 在執行時,都會透過標準輸入(stdin)接收一個包含上下文資訊的 JSON 物件。您可以使用 jq 或其他工具來解析它。

通用欄位:

{
  "session_id": "string",
  "transcript_path": "string", // 對話記錄檔路徑
  "cwd": "string",             // Hook 被調用時的當前工作目錄
  "hook_event_name": "string"  // 觸發的 Hook 事件名稱
}

PreToolUse 的輸入範例:

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "ls -a",
    "description": "List all files, including hidden ones."
  }
}

PostToolUse 的輸入範例:

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.ts",
    "content": "console.log('Hello World');"
  },
  "tool_response": {
    "filePath": "/path/to/file.ts",
    "success": true
  }
}

Notification 的輸入範例:

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "Notification",
  "message": "Claude needs your permission to use Bash"
}

UserPromptSubmit 的輸入範例:

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "Write a function to calculate the factorial of a number"
}

Stop / SubagentStop 的輸入範例:

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "Stop",
  "stop_hook_active": true
}

stop_hook_activetrue 表示 Claude 正在因為前一個 Stop Hook 的結果而繼續執行。您可以檢查此值以避免無限循環。

PreCompact 的輸入範例:

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../session.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "PreCompact",
  "trigger": "manual",
  "custom_instructions": ""
}
  • trigger: 壓縮觸發原因
    • "manual": 由使用者透過 /compact 命令手動觸發
    • "auto": 由於上下文視窗已滿而自動觸發
  • custom_instructions: 對於 manual 觸發,包含使用者傳遞給 /compact 的自訂指令;對於 auto 觸發,此欄位為空字串

Hook 的輸出與控制

Hook 可以透過兩種方式影響 Claude Code 的行為:簡單的退出碼進階的 JSON 輸出。輸出主要用來溝通是否要阻止操作,以及應該向 Claude 和使用者顯示什麼回饋。

1. 簡單控制:退出代碼 (Exit Code)

這是最基本的回饋機制。

  • 退出代碼 0: 成功。stdout 的內容會以一般資訊的形式顯示給使用者(在 transcript 模式下可見),但 Claude 不會看到。
  • 退出代碼 2: 阻擋性錯誤。stderr 的內容會作為回饋提供給 Claude 進行處理。不同事件的具體行為不同(見下表)。
  • 其他退出代碼: 非阻擋性錯誤。stderr 的內容只會顯示給使用者,操作會繼續執行。

退出代碼 2 的行為細節

Hook 事件 阻擋行為
PreToolUse 阻擋工具執行,並將 stderr 內容交給 Claude 分析。
PostToolUse 工具已經執行,但會將 stderr 內容交給 Claude 進行後續修正。
UserPromptSubmit 阻擋使用者提示的處理,清除該提示,並將 stderr 顯示給使用者。
Stop / SubagentStop 阻擋 Claude 停止,並將 stderr 內容交給 Claude 以決定下一步。
Notification / PreCompact 無特殊阻擋效果,僅將 stderr 顯示給使用者。

⚠️ 重要提醒: 當退出代碼為 0 時,Claude 不會看到 stdout 的內容。只有 stderr 在退出代碼為 2 時才會被 Claude 處理。

2. 進階控制:JSON 輸出

為了進行更精細的控制,Hook 可以透過 stdout 返回一個 JSON 物件。

通用 JSON 欄位 (適用於所有事件):

{
  "continue": true,
  "stopReason": "使用者要求的操作已終止",
  "suppressOutput": true
}
  • continue (boolean, 預設 true): 設為 false 可在 Hook 執行後完全終止 Claude 的後續處理。
  • stopReason (string): 當 continuefalse 時,向使用者顯示的停止原因(不會顯示給 Claude)。
  • suppressOutput (boolean, 預設 false): 設為 true 可以隱藏 Hook 的 stdout,使其不在 transcript 模式中顯示。

重要行為說明:

  • continuefalse 時,會優先於任何 "decision": "block" 設定
  • 對於 PreToolUse,這與 "decision": "block" 不同 - 後者只阻擋特定工具呼叫並提供自動回饋給 Claude
  • 對於 PostToolUse,這與 "decision": "block" 不同 - 後者提供自動回饋給 Claude
  • 對於 UserPromptSubmit,這會阻止提示被處理
  • 對於 StopSubagentStop,這會優先於任何 "decision": "block" 輸出

特定事件的決策控制 (decision):

  • PreToolUse: 控制工具是否執行。

    {
      "decision": "approve" | "block",
      "reason": "批准原因或阻擋原因"
    }
    • "approve": 繞過權限詢問,直接執行。reason 顯示給使用者。
    • "block": 阻止工具執行。reason提供給 Claude
    • undefined: 保持預設的權限詢問流程。
  • PostToolUse / Stop / SubagentStop: 控制是否需要 Claude 進行後續處理。

    {
      "decision": "block",
      "reason": "需要 Claude 處理的回饋資訊"
    }
    • "block": 提示 Claude 根據 reason 繼續工作。例如,在 PostToolUse 中指出程式碼格式問題,或在 Stop 中要求 Claude 繼續執行下一步。
  • UserPromptSubmit: 控制使用者提示是否被處理。

    {
      "decision": "block",
      "reason": "向使用者顯示的阻擋原因"
    }
    • "block": 阻止提示被處理,並清除該提示。reason 只會顯示給使用者。

設定檔位置

Claude Code 的設定檔結構如下,您可以將 Hooks 設定放在其中:

{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolPattern",
        "hooks": [
          {
            "type": "command",
            "command": "your-command-here",
            "timeout": 30
          }
        ]
      }
    ]
  }
}
  • matcher: 工具名稱模式匹配(例如 Edit|Write),支援簡單字串精確匹配和正規表示式,對大小寫敏感。如果省略或為空字串,則會對所有工具生效。
  • hooks: 一組要執行的命令。
  • command: 要執行的 shell 命令。
  • timeout (可選): 命令執行的超時時間(秒)。

設定檔層級:

  • 使用者設定 (~/.claude/settings.json): 在這裡設定的 Hooks 會在您所有的專案中生效。適合通用規則,如通知、日誌記錄。
  • 專案設定 (<project_root>/.claude/settings.json): 在這裡設定的 Hooks 對當前專案生效。適合專案特定的規則,如程式碼格式化、特定於專案的安全防護。
  • 本地專案設定 (<project_root>/.claude/settings.local.json): 本地設定,不會被版本控制。
  • 企業管理政策設定: 如果您在企業環境中,管理員可能已配置全域策略設定。

⚠️ 重要提醒: "matcher": "*" 是無效的語法。如果要匹配所有工具,請省略 matcher 欄位或使用 "matcher": ""

實用範例庫

這裡有一些可以直接使用的範例,幫助您快速提升效率。

1. 程式碼自動格式化

TypeScript/JavaScript (Prettier)

在 Claude 每次修改完檔案後,自動執行 Prettier 格式化。

設定:

  • 事件: PostToolUse
  • 匹配器: Edit|MultiEdit|Write
  • Hook 命令:
# 檢查是否為 JS/TS 檔案並執行 prettier
file_path=$(jq -r '.tool_input.file_path // ""')
if [[ "$file_path" =~ \.(js|jsx|ts|tsx)$ ]] && [[ -f "$file_path" ]] && [[ -f "package.json" ]]; then
    npx --no-install prettier --write "$file_path"
    echo "✨ 已自動格式化檔案: $file_path"
fi

Go 程式碼格式化

設定:

  • 事件: PostToolUse
  • 匹配器: Edit|MultiEdit|Write
  • Hook 命令:
# 檢查是否為 Go 檔案並執行 gofmt
file_path=$(jq -r '.tool_input.file_path // ""')
if [[ "$file_path" =~ \.go$ ]] && [[ -f "$file_path" ]]; then
    gofmt -w "$file_path"
    echo "✨ 已自動格式化 Go 檔案: $file_path"
fi

2. 進階:使用 Python 腳本進行智慧驗證

這個範例展示了如何使用 Python 腳本,在 Claude 執行 Bash 命令前進行驗證,並使用 JSON 輸出 提供結構化的回饋。

設定:

  • 事件: PreToolUse
  • 匹配器: Bash
  • Hook 命令: python .claude/hooks/validate_bash.py

建立腳本 (.claude/hooks/validate_bash.py):

#!/usr/bin/env python3
import json
import re
import sys

# 定義驗證規則 (正規表示式, 建議訊息)
VALIDATION_RULES = [
    (
        r"\bgrep\b(?!.*\|)",
        "請改用 'rg' (ripgrep),它在專案範圍內的搜尋效能更好。",
    ),
    (
        r"\bfind\s+\S+\s+-name\b",
        "建議使用 'rg --files | rg <pattern>' 或 'rg -g '<pattern>'' 來取代 'find -name',速度更快。",
    ),
]

def validate_command(command: str) -> list[str]:
    issues = []
    for pattern, message in VALIDATION_RULES:
        if re.search(pattern, command):
            issues.append(message)
    return issues

try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError:
    sys.exit(1) # 無法解析 JSON,靜默退出

command = input_data.get("tool_input", {}).get("command", "")
if not command:
    sys.exit(0) # 如果沒有命令,則不進行任何操作

issues = validate_command(command)

if issues:
    # 如果發現問題,使用 JSON 輸出格式來阻擋並提供回饋
    response = {
        "decision": "block",
        "reason": "發現以下可以改進的地方:\n- " + "\n- ".join(issues)
    }
    print(json.dumps(response))
else:
    # 如果沒有問題,可以明確批准或不輸出任何東西讓流程繼續
    print(json.dumps({"decision": "approve", "reason": "命令檢查通過"}))

sys.exit(0)

3. 自訂通知系統

macOS 語音通知

當 Claude 等待您授權時,發出語音提醒。

設定:

  • 事件: Notification
  • 匹配器: (留空,適用於所有通知)
  • Hook 命令:
message=$(jq -r '.message // "Claude Code notification"')
say "Claude says: $message"

Linux 桌面通知

設定:

  • 事件: Notification
  • 匹配器: (留空)
  • Hook 命令:
# 需要安裝 libnotify-bin
title=$(jq -r '.title // "Claude Code"')
message=$(jq -r '.message // "Notification from Claude"')
notify-send "$title" "$message"

4. 安全防護範例

防止危險命令執行

阻止 Claude 執行可能危險的命令。

設定:

  • 事件: PreToolUse
  • 匹配器: Bash
  • Hook 命令:
command=$(jq -r '.tool_input.command // ""')

# 檢查危險命令模式
dangerous_patterns=("rm -rf" "sudo rm" "dd if=" "mkfs" "fdisk" "> /dev/")

for pattern in "${dangerous_patterns[@]}"; do
    if [[ "$command" == *"$pattern"* ]]; then
        # 使用退出碼 2 來阻擋操作,並將 stderr 的內容傳遞給 Claude
        echo "🚫 安全警告: 已阻止潛在危險命令: $command" >&2
        echo "💡 建議: 請使用更安全的替代方案或明確指定檔案" >&2
        exit 2
    fi
done

echo "✅ 命令安全檢查通過: $command"

保護敏感檔案

防止 Claude 修改重要的設定檔案。

設定:

  • 事件: PreToolUse
  • 匹配器: Edit|MultiEdit|Write
  • Hook 命令:
file_path=$(jq -r '.tool_input.file_path // ""')

# 敏感檔案模式
sensitive_files=(".env" ".env.local" ".env.production" "id_rsa" "id_ed25519" "package-lock.json" "yarn.lock")

for pattern in "${sensitive_files[@]}"; do
    if [[ "$file_path" == *"$pattern"* ]]; then
        reason=""
        case "$pattern" in
            "package-lock.json"|"yarn.lock")
                reason="💡 提示: 如需更新依賴,請讓 Claude 使用 'npm install' 或 'yarn install'"
                ;;
            ".env"*)
                reason="💡 提示: 環境變數檔案包含敏感資訊,請手動編輯"
                ;;
            "id_rsa"|"id_ed25519")
                reason="💡 提示: SSH 金鑰檔案不應被 Claude 修改"
                ;;
            *)
                reason="🔒 安全限制: 不允許 Claude 修改敏感檔案: $file_path"
                ;;
        esac
        # 使用 JSON 輸出阻擋操作
        jq -n --arg reason "$reason" '{decision: "block", reason: $reason}'
        exit 0
    fi
done

5. 自動測試執行

在程式碼修改後自動執行測試。

設定:

  • 事件: PostToolUse
  • 匹配器: Edit|MultiEdit|Write
  • Hook 命令:
file_path=$(jq -r '.tool_input.file_path // ""')

# 檢查是否為測試相關檔案或源碼檔案
if [[ "$file_path" =~ \.(test|spec)\. ]] || [[ "$file_path" =~ /src/ ]]; then
    echo "🧪 檢測到程式碼變更,執行相關測試..."
    
    # 檢查專案類型並執行適當的測試命令
    if [ -f "package.json" ]; then
        # --no-install 避免在沒有 node_modules 時的交互提示
        if jq -e '.scripts.test' package.json > /dev/null; then
            npm test -- --testPathPattern="$(basename "$file_path" | sed 's/\.[^.]*$//')"
        fi
    elif [ -f "go.mod" ]; then
        go test ./...
    elif [ -f "Cargo.toml" ]; then
        cargo test
    elif [ -f "requirements.txt" ] || [ -f "pyproject.toml" ]; then
        python -m pytest -xvs
    fi
fi

5. 官方範例:Bash 命令驗證

以下是官方提供的 Python 腳本範例,展示如何使用 JSON 輸出進行 Bash 命令驗證:

設定:

  • 事件: PreToolUse
  • 匹配器: Bash
  • Hook 命令: python /path/to/bash-validator.py

腳本內容 (bash-validator.py):

#!/usr/bin/env python3
import json
import re
import sys

# 定義驗證規則為 (正規表示式模式, 訊息) 的元組列表
VALIDATION_RULES = [
    (
        r"\bgrep\b(?!.*\|)",
        "建議使用 'rg' (ripgrep) 而非 'grep',效能和功能都更好",
    ),
    (
        r"\bfind\s+\S+\s+-name\b",
        "建議使用 'rg --files | rg pattern' 或 'rg --files -g pattern' 而非 'find -name',效能更好",
    ),
]

def validate_command(command: str) -> list[str]:
    issues = []
    for pattern, message in VALIDATION_RULES:
        if re.search(pattern, command):
            issues.append(message)
    return issues

try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"錯誤: 無效的 JSON 輸入: {e}", file=sys.stderr)
    sys.exit(1)

tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
command = tool_input.get("command", "")

if tool_name != "Bash" or not command:
    sys.exit(1)

# 驗證命令
issues = validate_command(command)

if issues:
    for message in issues:
        print(f"• {message}", file=sys.stderr)
    # 退出代碼 2 阻擋工具執行並將 stderr 傳遞給 Claude
    sys.exit(2)

6. 官方範例:使用者提示驗證與上下文添加

設定:

  • 事件: UserPromptSubmit
  • Hook 命令: python /path/to/prompt-validator.py

腳本內容 (prompt-validator.py):

#!/usr/bin/env python3
import json
import sys
import re
import datetime

# 從 stdin 載入輸入
try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"錯誤: 無效的 JSON 輸入: {e}", file=sys.stderr)
    sys.exit(1)

prompt = input_data.get("prompt", "")

# 檢查敏感模式
sensitive_patterns = [
    (r"(?i)\b(password|secret|key|token)\s*[:=]", "提示包含潛在的機密資訊"),
]

for pattern, message in sensitive_patterns:
    if re.search(pattern, prompt):
        # 使用 JSON 輸出阻擋並提供特定原因
        output = {
            "decision": "block",
            "reason": f"安全政策違規: {message}。請重新表述您的請求,不要包含敏感資訊。"
        }
        print(json.dumps(output))
        sys.exit(0)

# 添加當前時間到上下文
context = f"當前時間: {datetime.datetime.now()}"
print(context)

# 允許提示繼續處理,並包含額外的上下文
sys.exit(0)

7. 智慧備份系統

在重要檔案被修改前自動建立備份。

設定:

  • 事件: PreToolUse
  • 匹配器: Edit|MultiEdit|Write
  • Hook 命令:
file_path=$(jq -r '.tool_input.file_path // ""')

# 需要備份的重要檔案模式
important_patterns=("config" "settings" ".json" ".yaml" ".yml" "Dockerfile" "Makefile")

should_backup=false
for pattern in "${important_patterns[@]}"; do
    if [[ "$file_path" == *"$pattern"* ]]; then
        should_backup=true
        break
    fi
done

if [ "$should_backup" = true ] && [ -f "$file_path" ]; then
    backup_dir="$(dirname "$file_path")/.claude-backups"
    mkdir -p "$backup_dir"
    
    timestamp=$(date +"%Y%m%d_%H%M%S")
    backup_file="$backup_dir/$(basename "$file_path").backup.$timestamp"
    
    cp "$file_path" "$backup_file"
    echo "💾 已建立備份: $backup_file"
fi

Hook 執行細節

執行環境與限制

  • 執行超時: 預設 60 秒執行限制,可針對個別命令設定 timeout 參數
    • 個別命令的超時不會影響其他命令
  • 並行執行: 所有匹配的 Hooks 會並行執行
  • 執行環境: 在當前目錄中執行,使用 Claude Code 的環境變數
  • 輸入方式: 透過 stdin 接收 JSON 資料
  • 輸出處理:
    • PreToolUse/PostToolUse/Stop: 進度顯示在 transcript 模式 (Ctrl-R)
    • Notification: 僅記錄在除錯日誌中 (--debug)

Hook 匹配規則

對於 PreToolUsePostToolUse 事件:

  • matcher 是必要的,用於指定要監控的工具
  • 支援精確字串匹配:"Write" 只匹配 Write 工具
  • 支援正規表示式:"Edit|Write""Notebook.*"
  • 大小寫敏感
  • 如果省略或為空字串,則匹配所有工具

對於其他事件:

  • UserPromptSubmit, Notification, Stop, SubagentStop, PreCompact 不使用 matcher
  • 可以省略 matcher 欄位或將其設為空字串

常見工具匹配器

以下是可以在 PreToolUsePostToolUse 中使用的常見工具名稱:

  • Task - 代理任務
  • Bash - Shell 命令
  • Glob - 檔案模式匹配
  • Grep - 內容搜尋
  • Read - 檔案讀取
  • Edit, MultiEdit - 檔案編輯
  • Write - 檔案寫入
  • WebFetch, WebSearch - 網路操作

進階功能

MCP 工具整合

Claude Code 支援 MCP (Model Context Protocol) 工具,您也可以為這些工具設定 Hooks。MCP 工具遵循 mcp__<server>__<tool> 的命名模式。

範例:監控記憶體操作

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Memory operation: ' $(jq -r '.tool_name') >> ~/.claude/memory-log.txt"
          }
        ]
      }
    ]
  }
}

使用外部腳本

對於複雜的邏輯,建議建立獨立的腳本檔案:

建立腳本檔案 (.claude/hooks/security_check.js):

#!/usr/bin/env node

async function main() {
  const chunks = [];
  for await (const chunk of process.stdin) {
    chunks.push(chunk);
  }

  const toolData = JSON.parse(Buffer.concat(chunks).toString());
  const command = toolData.tool_input?.command || "";
  
  // 複雜的安全檢查邏輯
  const dangerousPatterns = [
    /rm\s+-rf\s+\//, // 刪除根目錄
    /sudo\s+rm/, // sudo 刪除
    /chmod\s+777/, // 過於寬鬆的權限
  ];
  
  for (const pattern of dangerousPatterns) {
    if (pattern.test(command)) {
      console.error(`🚫 檢測到危險命令模式: ${command}`);
      process.exit(2);
    }
  }
  
  console.log(`✅ 安全檢查通過`);
  process.exit(0);
}

main().catch(console.error);

在 Hook 中使用腳本:

# 方式一:使用 node 命令執行(推薦)
node .claude/hooks/security_check.js

# 方式二:直接執行(需要 shebang 行和執行權限)
# 確保腳本第一行包含: #!/usr/bin/env node
# 並設定執行權限: chmod +x .claude/hooks/security_check.js
.claude/hooks/security_check.js

安全考量

⚠️ 免責聲明

風險自負: Claude Code Hooks 會在您的系統上自動執行任意 shell 命令。使用 Hooks 表示您確認:

  • 您對配置的命令承擔全部責任
  • Hooks 可以修改、刪除或存取您的使用者帳戶能存取的任何檔案
  • 惡意或編寫不當的 Hooks 可能導致資料遺失或系統損害
  • Anthropic 不提供任何保證,並且不對 Hook 使用導致的任何損害承擔責任
  • 您應該在生產環境使用前,在安全環境中徹底測試 Hooks

在添加到您的配置之前,請務必審查並理解任何 Hook 命令。

🛡️ 重要安全原則

  1. 最小權限原則: 只授予 Hook 完成任務所需的最小權限
  2. 輸入驗證: 始終驗證來自 Claude 的輸入資料
  3. 避免命令注入: 如果使用來自 Claude 的資料建構命令,請正確轉義
  4. 定期審查: 定期檢查您的 Hook 設定和執行日誌
  5. 使用絕對路徑: 在腳本中盡量使用絕對路徑指定命令,避免 PATH 被劫持
  6. 避開敏感檔案: 設定規則以跳過 .env, .git/, SSH 金鑰等檔案

具體防護措施

輸入清理範例

# 安全地處理檔案路徑
file_path=$(jq -r '.tool_input.file_path // ""' | sed 's/[^a-zA-Z0-9._/-]//g')

# 防止路徑遍歷攻擊
if [[ "$file_path" == *".."* ]]; then
    echo "❌ 不允許路徑遍歷操作"
    exit 2
fi

權限檢查

# 檢查檔案是否在允許的目錄中
allowed_dirs=("/home/user/projects" "/tmp")
file_path=$(jq -r '.tool_input.file_path // ""')

is_allowed=false
for dir in "${allowed_dirs[@]}"; do
    if [[ "$file_path" == "$dir"* ]]; then
        is_allowed=true
        break
    fi
done

if [ "$is_allowed" = false ]; then
    echo "❌ 檔案路徑不在允許的目錄中: $file_path"
    exit 2
fi

專案設定安全性

當您載入包含專案級別 Hooks 的專案時,Claude Code 會顯示警告並要求您確認。這是為了防止惡意設定檔。

配置安全機制

對設定檔的直接編輯不會立即生效。Claude Code 採用以下安全措施:

  1. 啟動時快照: Claude Code 在啟動時擷取 Hooks 設定的快照
  2. 全程使用快照: 整個對話期間使用該快照
  3. 外部變更警告: 如果 Hooks 設定檔被外部修改,Claude Code 會發出警告
  4. 需要審查確認: 變更後的 Hooks 需要在 /hooks 管理介面中審查後才能生效

這可以防止惡意 Hook 修改在您目前的對話期間生效。

請務必:

  1. 仔細檢查 .claude/settings.json 中的所有 Hook 命令
  2. 理解每個命令的作用,特別是那些您不熟悉的
  3. 在沙盒環境中測試未知的 Hook 配置
  4. 不要盲目信任來自網路的專案設定

疑難排解

常見問題及解決方案

Hook 沒有執行

可能原因及解決方法:

  1. 事件類型或匹配器錯誤

    # 檢查設定檔語法
    cat ~/.claude/settings.json | jq .
  2. 命令路徑問題

    # 使用絕對路徑
    /usr/bin/echo "test" >> ~/.claude/debug.log
  3. 權限問題

    # 檢查腳本權限
    chmod +x .claude/hooks/your-script.sh

調試 Hook 執行

在 Hook 中加入調試輸出:

# 在命令開頭加入
set -x # 打開 shell 的詳細執行日誌
echo "Hook executed at $(date)" >> ~/.claude/hook-debug.log
echo "Input: $(cat)" >> ~/.claude/hook-debug.log
set +x # 關閉詳細日誌

檢查 Claude 傳遞的資料:

# 將完整輸入保存到檔案
cat > /tmp/claude-hook-input-$(date +%s).json

JSON 解析錯誤

安全的 JSON 處理:

# 檢查 JSON 是否有效
if echo "$input" | jq . > /dev/null 2>&1; then
    # JSON 有效,繼續處理
    command=$(echo "$input" | jq -r '.tool_input.command // ""')
else
    echo "❌ 無效的 JSON 輸入" >&2
    exit 1
fi

除錯工具

基本除錯步驟

如果您的 Hooks 無法正常運作,請依序檢查:

  1. 檢查配置 - 執行 /hooks 查看您的 Hook 是否已註冊
  2. 驗證語法 - 確保 JSON 設定有效
  3. 測試命令 - 先手動執行 Hook 命令
  4. 檢查權限 - 確保腳本檔案具有執行權限
  5. 查看日誌 - 使用 claude --debug 查看詳細的 Hook 執行資訊

常見問題:

  • 引號未跳脫 - JSON 字串中使用 \"
  • 匹配器錯誤 - 檢查工具名稱是否精確匹配(大小寫敏感)
  • 命令找不到 - 使用腳本的完整路徑

啟用詳細日誌

使用 --debug 標誌啟動 Claude Code 以查看詳細的日誌輸出:

claude --debug

您將會看到類似以下的日誌:

[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 60000ms
[DEBUG] Hook command completed with status 0: <Your stdout>

進度訊息會出現在 transcript 模式 (Ctrl-R) 中,顯示:

  • 正在執行哪個 Hook
  • 執行的命令
  • 成功/失敗狀態
  • 輸出或錯誤訊息

進階除錯技巧

對於複雜的 Hook 問題:

  1. 檢查 Hook 執行 - 使用 claude --debug 查看詳細的 Hook 執行過程
  2. 驗證 JSON Schema - 使用外部工具測試 Hook 輸入/輸出
  3. 檢查環境變數 - 驗證 Claude Code 的環境是否正確
  4. 測試邊緣情況 - 嘗試 Hook 處理異常檔案路徑或輸入
  5. 監控系統資源 - 檢查 Hook 執行期間是否有資源耗盡
  6. 使用結構化記錄 - 在 Hook 腳本中實作日誌記錄

Hook 執行監控

# 建立全域監控 Hook
echo 'echo "$(date): Hook executed" >> ~/.claude/all-hooks.log' > ~/.claude/hooks/monitor.sh
chmod +x ~/.claude/hooks/monitor.sh

最佳實踐總結

✅ 推薦做法

  1. 從簡單開始: 先實作基本的日誌記錄,再逐步加入複雜功能
  2. 分層設定: 通用規則放在使用者設定,專案特定規則放在專案設定
  3. 充分測試: 在安全環境中測試所有 Hook 再部署到生產環境
  4. 詳細註釋: 在設定檔中為每個 Hook 添加說明註釋
  5. 定期維護: 定期檢查和更新 Hook 設定,移除不需要的規則

❌ 避免事項

  1. 過度複雜化: 避免在單一 Hook 中包含過多邏輯
  2. 忽略錯誤處理: 確保 Hook 能夠妥善處理異常情況
  3. 硬編碼路徑: 使用相對路徑或環境變數而非絕對路徑
  4. 忽略效能: 避免在 Hook 中執行耗時操作
  5. 盲目信任: 不要無條件信任來自外部的 Hook 設定

🚀 進階技巧

  1. 條件執行: 使用環境變數控制 Hook 行為

    if [ "$CLAUDE_ENV" = "production" ]; then
        # 生產環境專用邏輯
    fi
  2. 並行處理: 對於獨立的操作,可以使用背景執行

    long_running_task &
    echo "Task started in background"
  3. 狀態追蹤: 使用暫存檔案追蹤 Hook 狀態

    echo "$(date): Hook started" > /tmp/claude-hook-status
  4. 整合外部服務: 透過 API 與外部服務整合

    curl -X POST "https://api.slack.com/..." -d "text=Claude completed task"

結語

Claude Code Hooks 是一個強大的自動化工具,能夠顯著提升您的開發效率和程式碼品質。透過合理的設定和使用,您可以:

  • 🎯 自動化重複性任務: 格式化程式碼、執行測試、產生文件
  • 🛡️ 增強安全性: 防止危險操作、保護敏感檔案
  • 📊 提升可見度: 記錄操作歷史、監控系統狀態
  • 🔄 優化工作流程: 整合現有工具鏈、自訂通知機制

記住,Hooks 的真正價值在於讓您專注於創造性的工作,而將重複性的、規則化的任務交給自動化系統處理。

開始您的 Claude Code Hooks 之旅,讓 AI 助手成為您開發團隊中最可靠的成員!


參考資源

About

Claude Code Hooks 完整指南

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages