跳至主要內容
模組 3:真實架構 3 / 6
進階 Session 15 Hooks Events

Hooks 系統

建構 PreToolUse、PostToolUse 和其他生命週期事件的事件訂閱者。

2026年3月20日 18 分鐘閱讀

你將學到什麼

Claude Code 有內建的權限系統,但組織需要更多。安全團隊可能想阻止所有對生產環境設定檔的寫入。合規團隊可能需要記錄每個執行的命令的稽核日誌。平台團隊可能想自動標記被 AI 修改的檔案。Hooks 系統讓這一切成為可能。

完成後,你將了解:

  • 什麼是 hooks 以及它們何時觸發
  • 四種 hook 事件類型及其用途
  • 如何在 settings.json 中設定 hooks
  • matcher 模式如何篩選哪些工具觸發 hook
  • 輸入/輸出契約(JSON stdin、exit codes)
  • 如何建構安全閘道和稽核系統

問題是什麼

內建的權限系統是二元的:允許或拒絕。但現實世界的政策是有細微差異的:

  • 「允許 Bash,但阻止任何包含 rm -rf 的命令」
  • 「允許檔案編輯,但不允許編輯 config/production/ 中的檔案」
  • 「允許一切,但將每個動作記錄到稽核檔案」
  • 「允許 git commit,但要求訊息中包含 ticket 編號」

你無法透過標準的權限 UI 表達這些規則。你需要可程式化的政策強制執行 — 在工具執行生命週期的特定時間點執行的程式碼,能夠檢查、修改或阻止動作。

Without hooks:
  AI calls tool → Permission check (allow/deny) → Execute

With hooks:
  AI calls tool → Permission check → PreToolUse hook

                                    inspect, validate,
                                    block if needed


                                    Execute tool


                                    PostToolUse hook

                                    log, audit,
                                    side effects

如何運作

Hook 類型

Claude Code 在四個生命週期事件點觸發 hooks:

┌──────────────────────────────────────────────────┐
│              Hook Lifecycle                       │
│                                                   │
│  ┌─────────────┐                                  │
│  │ Notification │  Fires when AI produces a        │
│  │    Hook      │  notification (status messages)  │
│  └─────────────┘                                  │
│                                                   │
│  ┌─────────────┐                                  │
│  │ PreToolUse  │  Fires BEFORE a tool executes    │
│  │    Hook     │  Can BLOCK the tool call          │
│  └──────┬──────┘                                  │
│         │                                         │
│         ▼                                         │
│  ┌─────────────┐                                  │
│  │ Tool        │  The actual tool execution        │
│  │ Execution   │                                   │
│  └──────┬──────┘                                  │
│         │                                         │
│         ▼                                         │
│  ┌─────────────┐                                  │
│  │ PostToolUse │  Fires AFTER a tool executes     │
│  │    Hook     │  For logging and side effects     │
│  └─────────────┘                                  │
│                                                   │
│  ┌─────────────┐                                  │
│  │   Stop      │  Fires when the agent loop        │
│  │   Hook      │  is about to end a turn           │
│  └─────────────┘                                  │
│                                                   │
└──────────────────────────────────────────────────┘
Hook何時觸發能否阻止?常見用途
PreToolUse工具執行前安全閘道、驗證
PostToolUse工具執行後日誌記錄、稽核、副作用
Notification狀態訊息時自訂通知
Stop回合結束前最終驗證、清理

設定

Hooks 定義在 .claude/settings.json(專案層級)或 ~/.claude/settings.json(使用者層級)中:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "command": "/path/to/validate-command.sh"
      },
      {
        "matcher": "Edit",
        "command": "/path/to/check-file-path.sh"
      }
    ],
    "PostToolUse": [
      {
        "matcher": "*",
        "command": "/path/to/audit-log.sh"
      }
    ]
  }
}

每個 hook 條目有:

  • matcher — 哪個工具觸發這個 hook。使用工具名稱如 "Bash""*" 匹配所有工具。
  • command — 要執行的 shell 命令。這就是你的 hook 腳本。

Matcher 模式

matcher 決定哪些工具呼叫會觸發這個 hook:

Matcher      Matches
────────     ────────────────────────────
"Bash"       Only Bash tool calls
"Edit"       Only Edit tool calls
"Read"       Only Read tool calls
"mcp__*"     All MCP tool calls
"*"          Every tool call

多個 hooks 可以匹配同一個工具呼叫。它們按順序執行。如果任何 PreToolUse hook 阻止了呼叫,工具呼叫就會被停止。

輸入/輸出契約

當 hook 觸發時,Claude Code 將上下文作為 JSON 傳遞到 stdin,並從 exit code 讀取決定:

輸入 (stdin):

{
  "hook_type": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf /tmp/build-cache"
  },
  "session_id": "abc-123",
  "message_id": "msg-456"
}

輸出 (exit code):

Exit Code意義效果
0允許工具呼叫正常繼續
2阻止工具呼叫被阻止,AI 被告知原因
其他錯誤視為允許(fail-open)

Stdout(可選,用於 exit code 2):

阻止時,hook 可以將原因寫入 stdout。這個原因會顯示給 AI,讓它可以調整:

BLOCKED: Cannot delete directories outside of project root.

AI 會收到這個作為工具錯誤,並可以嘗試不同的方法。

執行流程(詳細)

以下是設定 PreToolUse hook 後的完整流程:

AI wants to run: Bash("npm install express")


  ┌──────────────────┐
  │ Permission Check  │   Standard allow/deny
  └────────┬─────────┘
           │ (allowed)

  ┌──────────────────┐
  │ Matcher Check     │   Does "Bash" match any hooks?
  └────────┬─────────┘
           │ (yes, matched)

  ┌──────────────────┐
  │ Run Hook Script   │   Spawn process, pipe JSON to stdin
  │                   │
  │ stdin: {          │
  │   "tool_name":    │
  │     "Bash",       │
  │   "tool_input": { │
  │     "command":     │
  │     "npm install"  │
  │   }               │
  │ }                 │
  └────────┬─────────┘

     ┌─────┴─────┐
     │           │
  exit 0      exit 2
  (allow)     (block)
     │           │
     ▼           ▼
  Execute     Return error
  tool        to AI:
              "BLOCKED: ..."

關鍵洞見

Hooks 是組織政策的擴展點。 它們讓你在不修改 AI 指令的情況下強制執行 AI 必須遵守的規則。

這個區別很重要。你可以在 CLAUDE.md 中寫「永遠不要刪除生產檔案」,AI 通常會遵守。但指令可以被覆蓋、在壓縮後被遺忘,或在邊界情況下被忽略。一個檢查檔案路徑的 PreToolUse hook 是確定性的 — 無論 AI 做出什麼決定,它都無法被繞過。

可以這樣理解:

層級機制保證
CLAUDE.md 指令AI 閱讀並遵循盡力而為(AI 判斷)
權限系統每個工具允許/拒絕二元的,沒有細微差異
Hooks每次呼叫的自訂程式碼確定性、可程式化

Hooks 填補了「好好要求 AI」和「硬編碼到 Claude Code 中」之間的空隙。它們是政策強制執行層。

實作範例

範例 1:阻止危險命令

一個防止破壞性 shell 命令的 PreToolUse hook:

#!/bin/bash
# hooks/block-dangerous.sh
# PreToolUse hook for Bash tool

# Read JSON input from stdin
INPUT=$(cat)

# Extract the command
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# Define blocked patterns
BLOCKED_PATTERNS=(
  "rm -rf /"
  "rm -rf ~"
  "DROP DATABASE"
  "DROP TABLE"
  "mkfs"
  "> /dev/sda"
  "chmod -R 777 /"
)

# Check each pattern
for pattern in "${BLOCKED_PATTERNS[@]}"; do
  if echo "$COMMAND" | grep -qi "$pattern"; then
    echo "BLOCKED: Command contains dangerous pattern: $pattern"
    exit 2
  fi
done

# Allow the command
exit 0

範例 2:稽核日誌

一個記錄每次工具執行的 PostToolUse hook:

#!/bin/bash
# hooks/audit-log.sh
# PostToolUse hook for all tools

INPUT=$(cat)

# Extract fields
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
SESSION=$(echo "$INPUT" | jq -r '.session_id')

# Build log entry
LOG_ENTRY=$(jq -n \
  --arg ts "$TIMESTAMP" \
  --arg tool "$TOOL" \
  --arg session "$SESSION" \
  --argjson input "$(echo "$INPUT" | jq '.tool_input')" \
  '{timestamp: $ts, tool: $tool, session: $session, input: $input}')

# Append to audit log
echo "$LOG_ENTRY" >> ~/.claude/audit.jsonl

# PostToolUse hooks always exit 0 (can't block after execution)
exit 0

這會產生一個 JSONL 稽核軌跡:

{"timestamp":"2026-03-14T10:00:01Z","tool":"Bash","session":"abc","input":{"command":"npm test"}}
{"timestamp":"2026-03-14T10:00:03Z","tool":"Edit","session":"abc","input":{"file_path":"src/auth.ts",...}}
{"timestamp":"2026-03-14T10:00:05Z","tool":"Read","session":"abc","input":{"file_path":"package.json"}}

範例 3:保護生產檔案

一個防止編輯生產設定的 PreToolUse hook:

#!/bin/bash
# hooks/protect-prod-config.sh
# PreToolUse hook for Edit and Write tools

INPUT=$(cat)

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

# Define protected paths
PROTECTED=(
  "config/production"
  ".env.production"
  "docker-compose.prod"
  "k8s/production"
)

for protected in "${PROTECTED[@]}"; do
  if echo "$FILE_PATH" | grep -q "$protected"; then
    echo "BLOCKED: Cannot modify production file: $FILE_PATH. Use a staging config instead."
    exit 2
  fi
done

exit 0

整合在一起

這是包含所有三個 hooks 的完整 settings.json

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "command": "/project/.claude/hooks/block-dangerous.sh"
      },
      {
        "matcher": "Edit",
        "command": "/project/.claude/hooks/protect-prod-config.sh"
      },
      {
        "matcher": "Write",
        "command": "/project/.claude/hooks/protect-prod-config.sh"
      }
    ],
    "PostToolUse": [
      {
        "matcher": "*",
        "command": "/project/.claude/hooks/audit-log.sh"
      }
    ]
  }
}

使用此設定:

  1. 每個 Bash 命令在執行前都會對照黑名單進行檢查
  2. 每次檔案編輯在執行前都會對照受保護路徑進行檢查
  3. 每次工具執行(所有工具)在執行後都會被記錄到稽核檔案

AI 不知道 hooks 的存在。它正常呼叫工具。如果 hook 阻止了呼叫,AI 會收到錯誤訊息並進行調整 — 通常是選擇一個更安全的替代方案。

前後對比

沒有 Hooks有 Hooks
政策是指令(盡力而為)政策是程式碼(確定性)
沒有稽核軌跡每個動作都被記錄
保護依賴 AI 的遵從性保護在執行時強制執行
所有專案相同的規則按專案設定 hook
無法與外部系統整合Hooks 可以呼叫任何外部 API
安全性基於信任安全性是先驗證再信任

下一堂課

這完成了模組 3:真實架構。你現在理解了讓 Claude Code 可擴展的三大支柱:控制協議(CLI 和 SDK 如何通訊)、MCP(外部工具如何接入)、和 hooks(政策如何被強制執行)。

第 16 堂課開始模組 4:設定與自訂,介紹 Session 儲存 — Claude Code 如何在 session 之間持久化狀態,實現連續性和記憶。