Skip to content
Claude How To

鉤子 (Hooks)

鉤子 (Hooks) 是在 Claude Code 工作階段 (Session) 中響應特定事件而執行的自動化腳本。它們支援自動化、驗證、權限管理以及自訂工作流程。

概觀 (Overview)

鉤子 (Hooks) 是當 Claude Code 中發生特定事件時自動執行的自動化動作(Shell 指令、HTTP Webhook、LLM 提示詞、MCP 工具呼叫或子代理評估)。它們接收 JSON 輸入並透過結束碼 (Exit Codes) 與 JSON 輸出傳達結果。

主要特性:

  • 事件驅動 (Event-driven) 的自動化
  • 基於 JSON 的輸入/輸出 (Input/Output)
  • 支援 commandhttpmcp_toolpromptagent 等鉤子類型
  • 針對特定工具的鉤子模式匹配 (Pattern Matching)

設定 (Configuration)

鉤子 (Hooks) 設定在具有特定結構的設定檔中:

  • ~/.claude/settings.json - 使用者設定 (全域專案)
  • .claude/settings.json - 專案設定 (可共享,已版本控制)
  • .claude/settings.local.json - 本地專案設定 (未版本控制)
  • 受控策略 (Managed Policy) - 組織層級全域設定
  • 插件 hooks/hooks.json - 插件作用域 (Plugin-scoped) 的鉤子
  • 技能/代理 前置詮釋資料 (Skill/Agent Frontmatter) - 元件生命週期鉤子

基本設定結構 (Basic Configuration Structure)

json
{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolPattern",
        "hooks": [
          {
            "type": "command",
            "command": "your-command-here",
            "timeout": 60
          }
        ]
      }
    ]
  }
}

重要欄位:

欄位 (Field)說明 (Description)範例 (Example)
matcher匹配工具名稱的模式(區分大小寫)"Write", "Edit|Write", "*"
hooks鉤子定義陣列[{ "type": "command", ... }]
type鉤子類型:"command" (bash)、"prompt" (LLM)、"http" (webhook)、"mcp_tool" (MCP 工具呼叫,v2.1.118+) 或 "agent" (子代理)"command"
command要執行的 Shell 指令"$CLAUDE_PROJECT_DIR/.claude/hooks/format.sh"
timeout選填的逾時時間(秒)。預設值:command/http/mcp_tool 為 600 秒,prompt 為 30 秒,agent 為 60 秒。30
once若為 true,每個工作階段僅執行一次該鉤子true
async若為 true,在背景執行且不阻塞流程true
asyncRewake若為 true,在背景執行並在結束碼為 2 時喚醒 Claude。隱含包含 asynctrue
shell接受 "bash""powershell"。預設為 "bash",未安裝 Git Bash 的 Windows 則預設為 "powershell""bash"
statusMessage當鉤子執行時顯示的自訂載入訊息 (Spinner Message)"Formatting…"

注意:某些事件會降低預設逾時時間。UserPromptSubmitcommandhttpmcp_tool 的預設值降至 30 秒,MessageDisplay 將其降至 10 秒。SessionEnd 鉤子共享 1.5 秒的配額;若您的設定指定了更長的單一鉤子 timeout,Claude Code 將提高該配額以匹配,最高達 60 秒。

匹配器模式 (Matcher Patterns)

模式 (Pattern)說明 (Description)範例 (Example)
精確字串匹配特定工具"Write"
正則表達式模式匹配多個工具"Edit|Write"
逗點分隔匹配列出的任一工具 (v2.1.191+)"Write,Edit"
通配符 (Wildcard)匹配所有工具"*"""
MCP 工具伺服器與工具模式"mcp__memory__.*"

匹配器採精確匹配 (v2.1.195+)。 包含連字號的標識符(例如包含連字號的 MCP 工具名稱)不再會意外子字串匹配到不同的工具。像 "Write,Edit" 這樣的逗點分隔匹配器會在清單中的任何工具上觸發 — 早期版本曾靜默地永不觸發。

InstructionsLoaded 匹配器值:

匹配器值 (Matcher Value)說明 (Description)
session_start工作階段啟動時載入的指令 (Instructions)
nested_traversal巢狀目錄巡覽期間載入的指令
path_glob_match透過路徑 Glob 模式匹配載入的指令

使用 if 條件進行精細化過濾(工具引數路徑)

matcher 欄位透過工具名稱 ("Write", "Edit|Write", "*" ) 來選擇鉤子。若要根據工具的引數 (Arguments) 進行更精細的過濾 — 例如僅在修改觸及 src/ 時執行鉤子,或保護敏感檔案的讀取 — 可以在個別鉤子處理常式中加入 if 條件。這與工具名稱匹配器不同:matcher 決定哪個工具,而 if 決定哪個呼叫

if 使用權限規則語法 (Permission-rule syntax) (ToolName(pattern)),對工具名稱以及其引數一併進行評估。對於 Read/Edit/Write,路徑模式遵循 gitignore 語意,並帶有與權限規則相同的錨點:像 .env 這樣的簡單名稱可以在任何深度匹配,src/** 相對於目前目錄,/src/** 相對於專案根目錄,~/... 相對於您的家目錄,而 //... 則是絕對檔案系統路徑。

if 欄位部位於鉤子處理常式層級 (Hook-handler level) — 與 typecommand 同級,位於 hooks 陣列內部 — 而非位於 matcher 上:

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "if": "Edit(src/**)",
            "command": "./hooks/lint-src.sh"
          }
        ]
      },
      {
        "matcher": "Read",
        "hooks": [
          {
            "type": "command",
            "if": "Read(.env)",
            "command": "./hooks/block-secret-read.sh"
          }
        ]
      }
    ]
  }
}

有效的 if 模式範例:Edit(src/**) (src/ 底下的修改)、Read(~/.ssh/**) (讀取任何 SSH 金鑰)、Read(.env) (目前目錄或其子目錄下的任何 .env)、Bash(git push *) (僅限 git push 子指令)。

v2.1.214 更新:鉤子 if 條件中的單一片段 dir/** 模式(如 Edit(src/**))現在僅匹配 <cwd>/dir — 而非樹狀結構中任何深度的該目錄。此前 src/** 也會匹配 foo/src/**。若您需要任意深度匹配,請使用 **/dir/**重要說明:此縮小範圍僅適用於鉤子 if: 條件與允許規則的自動核准 — 拒絕/詢問 (deny/ask) 權限規則仍會在任意深度匹配 dir/**

鉤子類型 (Hook Types)

Claude Code 支援五種鉤子類型:

指令鉤子 (Command Hooks)

預設的鉤子類型。執行 Shell 指令並透過 JSON stdin/stdout 及結束碼進行溝通。

json
{
  "type": "command",
  "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/validate.py\"",
  "timeout": 60
}

Exec 形式 (args)

於 v2.1.139 新增。

指令鉤子除了使用 Shell 形式的 "command": "..." 之外,還可以透過包含 args 陣列的 execve() 直接啟動二進位檔案。由於沒有 Shell 解析,路徑預留位置永遠不需要加引號,且設定不受 Shell 注入漏洞影響。

json
{
  "type": "command",
  "args": ["python3", "$CLAUDE_PROJECT_DIR/.claude/hooks/validate.py", "--strict"],
  "timeout": 60
}

這兩種形式為互斥 (Mutually exclusive) — 若鉤子同時設定了 commandargs,在載入設定時會被拒絕。當您需要管道 (Pipes)、重導向、&& 鏈結或 Shell 展開時,請使用 command;當您要帶著引數呼叫單一二進位檔案時,請使用 args

HTTP 鉤子 (HTTP Hooks)

於 v2.1.63 新增。

接收與指令鉤子相同 JSON 輸入的遠端 Webhook 端點。HTTP 鉤子向 URL 發送 POST JSON 並接收 JSON 回應。啟用沙盒 (Sandboxing) 時,HTTP 鉤子會透過沙盒進行路由。出於安全考量,URL 中的環境變數插值需要明確的 allowedEnvVars 准許清單。

json
{
  "hooks": {
    "PostToolUse": [{
      "type": "http",
      "url": "https://my-webhook.example.com/hook",
      "matcher": "Write"
    }]
  }
}

主要屬性:

  • "type": "http" -- 識別為 HTTP 鉤子
  • "url" -- Webhook 端點 URL
  • 啟用沙盒時透過沙盒路由
  • URL 中的任何環境變數插值都需要明確的 allowedEnvVars 清單

提示詞鉤子 (Prompt Hooks)

由 LLM 評估的提示詞,其中鉤子內容為 Claude 進行評估的提示詞。主要與 StopSubagentStop 事件搭配使用,用於智慧型任務完成度檢查。

json
{
  "type": "prompt",
  "prompt": "Evaluate if Claude completed all requested tasks.",
  "timeout": 30
}

LLM 會評估提示詞並傳回結構化的決策(詳情請參閱 基於提示詞的鉤子)。

MCP 工具鉤子 (MCP Tool Hooks)

於 v2.1.118 新增。

mcp_tool 類型直接調用已設定的 MCP 工具;設定引用的是 MCP 伺服器與工具名稱,而非 Shell 指令或 URL。當驗證或反應邏輯已經存在於您設定的 MCP 伺服器中時,這非常有用。

json
{
  "matcher": "Edit",
  "hooks": [{
    "type": "mcp_tool",
    "server": "my-mcp-server",
    "tool": "validate_edit"
  }]
}

主要屬性:

  • "type": "mcp_tool" -- 識別為 MCP 工具鉤子
  • "server" -- 已設定的 MCP 伺服器名稱
  • "tool" -- 要調用的該伺服器上的工具名稱

鉤子輸入(工具名稱、工具輸入、工作階段脈絡)會作為 MCP 工具的引數傳入。有關設定 MCP 伺服器的說明,請參閱 MCP 伺服器設定

代理鉤子 (Agent Hooks)

基於子代理 (Subagent) 的驗證鉤子,會啟動專屬代理來評估條件或執行複雜檢查。與提示詞鉤子(單輪 LLM 評估)不同,代理鉤子可以使用工具並執行多步驟推理。

注意:代理鉤子目前處於實驗階段,可能會有所變動。

json
{
  "type": "agent",
  "prompt": "Verify the code changes follow our architecture guidelines. Check the relevant design docs and compare.",
  "timeout": 120
}

主要屬性:

  • "type": "agent" -- 識別為代理鉤子
  • "prompt" -- 給予子代理的任務描述
  • 代理可以使用工具(Read、Grep、Bash 等)來執行其評估
  • 傳回類似提示詞鉤子的結構化決策

鉤子事件 (Hook Events)

Claude Code 支援 33 個鉤子事件

事件 (Event)觸發時機 (When Triggered)匹配器輸入 (Matcher Input)是否可阻塞 (Can Block)常見用途 (Common Use)
SessionStart工作階段開始/恢復/清除/精簡 (Session begins/resumes/clear/compact)startup/resume/clear/compact/fork環境設定
Setup初始環境設定(每個工作階段一次)(無)佈署工具、安裝相依套件
InstructionsLoaded載入 CLAUDE.md 或規則檔案後(無)修改/過濾指令
UserPromptSubmit使用者提交提示詞時(無)驗證提示詞
UserPromptExpansion使用者提示詞經展開後(例如解析 @ 提及、斜線指令)(無)轉換或檢查展開後的提示詞
PreToolUse工具執行前工具名稱是 (allow/deny/ask/defer)驗證、修改輸入
PermissionRequest顯示權限對話方塊時工具名稱自動核准/拒絕
PermissionDenied使用者拒絕權限提示時工具名稱紀錄、分析、策略執行
PostToolUse工具成功執行後工具名稱新增脈絡、回饋
PostToolUseFailure工具執行失敗工具名稱錯誤處理、紀錄
PostToolBatch一批工具使用完成後(無)聚合報告、批次驗證
Notification發送通知時通知類型自訂通知
MessageDisplay顯示助理訊息文字時(無)轉換或隱藏顯示的訊息文字 (v2.1.152)
SubagentStart產生子代理時代理類型名稱子代理設定
SubagentStop子代理完成時代理類型名稱子代理驗證
StopClaude 完成回應時(無)任務完成度檢查
StopFailureAPI 錯誤終止回合時(無)錯誤復原、紀錄
TeammateIdle代理團隊成員閒置時(無)團隊成員協調
TaskCompleted任務標記為完成時(無)任務後置動作
TaskCreated透過 TaskCreate 建立任務時(無)任務追蹤、紀錄
ConfigChange設定檔變更時(無)是 (策略除外)對設定更新做出反應
CwdChanged工作目錄變更時(無)目錄專屬設定
DirectoryAdded在工作階段中透過 /add-dir 或 SDK register_repo_root 控制請求註冊新工作目錄時 (v2.1.219)(無)為新新增的目錄設定工具
FileChanged監視的檔案變更時(無)檔案監控、重新建置
PreCompact脈絡精簡 (Compaction) 前manual/auto精簡前動作
PostCompact精簡完成後(無)精簡後動作
PreModelSwitchClaude Code 套用請求的模型切換前要切換到的模型規範名稱(來自 to_model門控或否決模型變更
PostModelSwitch工作階段模型變更後(包含 Claude Code 本身做出的變更,如恢復時還原模型)切換到的模型規範名稱(來自 to_model紀錄或對模型變更做出反應
WorktreeCreate正在建立 Worktree 時(無)是 (傳回路徑)Worktree 初始化
WorktreeRemove正在移除 Worktree 時(無)Worktree 清理
ElicitationMCP 伺服器請求使用者輸入時(無)輸入驗證
ElicitationResult使用者回應引導 (Elicitation) 時(無)回應處理
SessionEnd工作階段終止時(無)清理、最終紀錄

PreModelSwitchPostModelSwitch 需要 v2.1.251 或更高版本。兩者皆接收 from_modelto_model;匹配器會針對源自 to_model 的規範名稱(例如 claude-opus-5.*opus.*)進行評估。它們的 commandhttpmcp_tool 預設逾時降至 30 秒。

TaskCreatedTaskCompleted 需要啟用待辦事項工具 (v2.1.233)。 這兩個事件由待辦事項/任務追蹤工具 (TaskCreate/Get/Update/List, TodoWrite) 觸發,這些工具在 Opus 4.8、Sonnet 5、Fable 5、Mythos 5 與更新的模型上不再預設提供。在這些模型上,鉤子仍是有效的設定,但只會靜默地不觸發 — 不會產生輸出或錯誤。請設定 CLAUDE_CODE_ENABLE_TODO_TOOLS=1 以找回這些工具及對應事件。

PostToolUse 執行時間 (v2.1.119): PostToolUsePostToolUseFailure 鉤子輸入現在包含 duration_ms — 詳情請參閱 PostToolUse 章節。

PreToolUse

在 Claude 建立工具參數之後、執行處理之前運行。可用於驗證或修改工具輸入。

設定範例:

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/validate-bash.py"
          }
        ]
      }
    ]
  }
}

常見匹配器: Task, Bash, Glob, Grep, Read, Edit, Write, WebFetch, WebSearch

輸出控制:

  • permissionDecision: "allow", "deny", "ask", 或 "defer"
    • "allow" 跳過權限提示(需要使用者互動的工具以及組織設定為 ask 的連接器工具除外)
    • "deny" 阻止工具呼叫
    • "ask" 提示使用者確認
    • "defer" 優雅退出以便稍後恢復工具;此值會忽略 permissionDecisionReasonupdatedInputadditionalContext
    • 無論鉤子傳回什麼,拒絕 (Deny) 與詢問 (Ask) 規則仍會被評估。當多個 PreToolUse 鉤子意見不一致時,優先順序為 deny > defer > ask > allow
  • permissionDecisionReason: 決策的解釋說明。對於 "allow""ask",會顯示給使用者(而非 Claude);對於 "deny",會顯示給 Claude;對於 "defer" 則忽略
  • updatedInput: 修改後的工具輸入參數

PostToolUse

在工具執行完成後立即運行。用於驗證、紀錄或將脈絡傳回給 Claude。

設定範例:

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/security-scan.py"
          }
        ]
      }
    ]
  }
}

輸出控制:

  • "block" 決策會帶著回饋提示 Claude
  • additionalContext: 為 Claude 新增的脈絡

新增輸入欄位 (v2.1.119):

欄位 (Field)類型 (Type)說明 (Description)
duration_msnumber工具執行時間(毫秒)。不包含花費在權限提示與 PreToolUse 鉤子執行的時間。在 PostToolUsePostToolUseFailure 鉤子上均可使用。

可復原的阻塞 (continueOnBlock, v2.1.139)

預設情況下,傳回 "decision": "block"PostToolUse 鉤子會中斷當前回合。在鉤子上設定 "continueOnBlock": true 則會將拒絕資訊作為 tool_result 呈現給 Claude,以便模型能讀取回饋並重試或進行調整。

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/policy-check.py",
            "continueOnBlock": true
          }
        ]
      }
    ]
  }
}

當鉤子的 reason 是 Claude 可以據以採取行動的內容時(例如「此檔案為唯讀;請寫入其他地方」),請使用此屬性;當阻塞必須完全中止回合時,請保持未設定狀態。

UserPromptSubmit

當使用者提交提示詞、Claude 處理之前運行。

設定範例:

json
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/validate-prompt.py"
          }
        ]
      }
    ]
  }
}

輸出控制:

  • decision: "block" 用於阻止處理
  • reason: 若被阻塞時的解釋說明
  • additionalContext: 新增至提示詞的脈絡

Stop 與 SubagentStop

當 Claude 完成回應 (Stop) 或子代理完成時 (SubagentStop) 運行。支援基於提示詞的評估,以進行智慧型任務完成度檢查。

額外輸入欄位: StopSubagentStop 鉤子在其 JSON 輸入中皆會收到 last_assistant_message 欄位,其中包含 Claude 或子代理停止前發出的最後一條訊息。這對於評估任務完成度非常有幫助。

設定範例:

json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Evaluate if Claude completed all requested tasks.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

連續阻塞的安全上限 (v2.1.143):若 Stop 鉤子在同一個回合中連續 8 次傳回 "decision": "block"(或設定 continue: false),Claude Code 會短路該迴圈並帶有警告訊息結束工作階段。可透過環境變數 CLAUDE_CODE_STOP_HOOK_BLOCK_CAP=<integer> 覆寫閾值(設定為 0 可完全停用上限)。這能防止有問題的 Stop 鉤子造成工作階段無限迴圈。

傳回欄位 (v2.1.163): StopSubagentStop 鉤子可以傳回 hookSpecificOutput.additionalContext 以給予 Claude 回饋並繼續回合而不顯示錯誤標籤。以前,透過 Stop 鉤子影響模型非常笨拙;現在鉤子可以乾淨地注入脈絡,避免舊版回饋路徑(例如 "decision": "block")的錯誤標籤行為。

json
{
  "hookSpecificOutput": {
    "hookEventName": "Stop",
    "additionalContext": "Reminder: run the test suite before declaring done."
  }
}

SubagentStart

當子代理開始執行時運行。匹配器輸入為代理類型名稱,允許鉤子針對特定的子代理類型。

設定範例:

json
{
  "hooks": {
    "SubagentStart": [
      {
        "matcher": "code-review",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/subagent-init.sh"
          }
        ]
      }
    ]
  }
}

SessionStart

當工作階段開始或恢復時運行。可以持久化環境變數。

匹配器: startup, resume, clear, compact, fork

v2.1.214 更新:分支的工作階段 (Forked session) 現在會回報來源 "fork" — 此前曾回報 "resume"

特殊功能: 使用 CLAUDE_ENV_FILE 持久化環境變數(在 CwdChangedFileChanged 鉤子中亦可用):

bash
#!/bin/bash
if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=development' >> "$CLAUDE_ENV_FILE"
fi
exit 0

工作階段作用域的輸出 (v2.1.152): SessionStart 鉤子可以傳回 JSON 以重新掃描技能並設定工作階段標題:

json
{
  "reloadSkills": true,
  "hookSpecificOutput": {
    "sessionTitle": "Payments migration"
  }
}

頂層的 reloadSkills: true 會在同一個工作階段中觸發技能重新掃描(與 /reload-skills 指令動作相同),使鉤子剛剛安裝的技能立即可用。hookSpecificOutput.sessionTitle 會在啟動與恢復時設定工作階段的顯示標題。

SessionEnd

當工作階段結束時運行,用於執行清理或最終紀錄。無法阻塞終止流程。

Reason 欄位值:

  • clear - 使用者清除了工作階段
  • logout - 使用者登出
  • prompt_input_exit - 使用者透過提示詞輸入退出
  • other - 其他原因

設定範例:

json
{
  "hooks": {
    "SessionEnd": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/session-cleanup.sh\""
          }
        ]
      }
    ]
  }
}

Notification 事件

通知事件更新後的匹配器:

  • permission_prompt - 權限請求通知
  • idle_prompt - 閒置狀態通知
  • auth_success - 身份驗證成功
  • elicitation_dialog - 顯示給使用者的對話方塊
  • agent_needs_input - 背景代理需要輸入 (v2.1.198)
  • agent_completed - 背景代理完成 (v2.1.198)

PreModelSwitch

在 Claude Code 套用請求的模型切換運行 — 例如當您執行 /model,或當元件請求不同的模型時。需要 v2.1.251 或更高版本。

匹配器: 源自 to_model 的要切換到的模型規範名稱。匹配精確模型(claude-opus-5)或使用正則表達式匹配模型系列(.*opus.*)。

輸入欄位: 除通用欄位外,鉤子還會收到 from_model(切換前使用的模型)和 to_model(請求的模型)。

是否可阻塞: 是。結束碼 2 會阻塞切換並將 stderr 顯示為錯誤,因此工作階段會保持其當前模型。使用此功能可門控或否決模型變更 — 例如防止對成本敏感的專案切換到最昂貴的模型。

逾時時間: 此事件會將 commandhttpmcp_tool 的預設逾時降低至 30 秒。

設定範例:

json
{
  "hooks": {
    "PreModelSwitch": [
      {
        "matcher": ".*opus.*",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/gate-model-switch.sh"
          }
        ]
      }
    ]
  }
}
bash
#!/bin/bash
# gate-model-switch.sh - 拒絕在此專案中切換至 Opus
input=$(cat)
to_model=$(echo "$input" | jq -r '.to_model')

if [[ "$to_model" == *opus* ]]; then
  echo "This project is budgeted for Sonnet; staying on the current model." >&2
  exit 2
fi

exit 0

PostModelSwitch

在工作階段的模型變更運行。它也會在 Claude Code 本身做出變更時觸發 — 例如當您恢復工作階段時還原先前選擇的模型 — 而非僅在您主動請求切換時觸發。需要 v2.1.251 或更高版本。

匹配器:PreModelSwitch 相同 — 源自 to_model 的規範名稱。

輸入欄位: 除通用欄位外,包含 from_modelto_model

是否可阻塞: 否。切換已經發生;鉤子只能觀察並做出反應。

逾時時間: 此事件會將 commandhttpmcp_tool 的預設逾時降低至 30 秒。

設定範例:

json
{
  "hooks": {
    "PostModelSwitch": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/log-model-switch.sh"
          }
        ]
      }
    ]
  }
}
bash
#!/bin/bash
# log-model-switch.sh - 將每一次模型變更附加到工作階段日誌中
input=$(cat)
from=$(echo "$input" | jq -r '.from_model')
to=$(echo "$input" | jq -r '.to_model')

echo "$(date -Iseconds) $from -> $to" >> ~/.claude/model-switches.log
exit 0

元件作用域的鉤子 (Component-Scoped Hooks)

鉤子可以在特定元件(技能 Skills、代理 Agents、指令 Commands)的前置詮釋資料 (Frontmatter) 中進行附加:

在 SKILL.md、agent.md 或 command.md 中:

yaml
---
name: secure-operations
description: Perform operations with security checks
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/check.sh"
          once: true  # 每個工作階段僅執行一次
---

元件鉤子支援的事件: PreToolUse, PostToolUse, Stop

這允許直接在使用鉤子的元件中定義鉤子,使相關程式碼保持在一起。

子代理 Frontmatter 中的鉤子

當在子代理的 Frontmatter 中定義 Stop 鉤子時,它會自動轉換為作用域限於該子代理的 SubagentStop 鉤子。這能確保停止鉤子僅在該特定子代理完成時觸發,而非在主工作階段停止時觸發。

yaml
---
name: code-review-agent
description: Automated code review subagent
hooks:
  Stop:
    - hooks:
        - type: prompt
          prompt: "Verify the code review is thorough and complete."
  # 上述 Stop 鉤子會自動轉換為此子代理的 SubagentStop
---

需要工作區信任 (v2.1.218): 專案子代理中的 Frontmatter 鉤子現在在執行前,需要對該代理檔案所在的資料夾接受工作區信任 (Workspace Trust)。在 v2.1.218 之前,這些鉤子可以從您未信任的資料夾中執行。請參閱 子代理文件 以了解哪些作用域可獲得豁免。

PermissionRequest 事件

使用自訂輸出格式處理權限請求:

json
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow|deny",
      "updatedInput": {},
      "message": "Custom message",
      "interrupt": false
    }
  }
}

鉤子輸入與輸出 (Hook Input and Output)

JSON 輸入 (透過 stdin)

所有鉤子皆透過 stdin 接收 JSON 輸入:

json
{
  "session_id": "abc123",
  "transcript_path": "/path/to/transcript.jsonl",
  "cwd": "/current/working/directory",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.js",
    "content": "..."
  },
  "tool_use_id": "toolu_01ABC123...",
  "agent_id": "agent-abc123",
  "agent_type": "main",
  "worktree": "/path/to/worktree",
  "effort": { "level": "medium" }
}

通用欄位:

欄位 (Field)說明 (Description)
session_id唯一工作階段識別碼
transcript_path對話逐字稿檔案路徑
cwd目前工作目錄
prompt_id正在處理的提示詞 UUID;與 OpenTelemetry prompt.id 屬性相關聯 (v2.1.196)
hook_event_name觸發鉤子的事件名稱
agent_id執行此鉤子的代理識別碼
agent_type代理類型("main"、子代理類型名稱等)
worktreeGit worktree 路徑(若代理在其中執行)
effort.level(v2.1.133+) 當前努力程度 (Effort level):low, medium, high, xhigh, 或 max

結束碼 (Exit Codes)

結束碼 (Exit Code)意義 (Meaning)行為 (Behavior)
0成功 (Success)繼續,解析 JSON stdout
2阻塞性錯誤 (Blocking error)阻塞操作,stderr 顯示為錯誤
其他非阻塞性錯誤 (Non-blocking error)繼續,stderr 在詳細模式 (Verbose mode) 下顯示

JSON 輸出 (stdout, 結束碼 0)

json
{
  "continue": true,
  "stopReason": "Optional message if stopping",
  "suppressOutput": false,
  "systemMessage": "Optional warning message",
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "File is in allowed directory",
    "updatedInput": {
      "file_path": "/modified/path.js"
    }
  }
}

作用域 (v2.1.121+): hookSpecificOutput.updatedToolOutput 現在對所有工具生效,而非僅限於 MCP 工具。在 BashEditRead 等工具上的 PostToolUse 鉤子可以在 Claude 看到之前重寫工具的輸出 — 這對於隱藏敏感資訊、規範化 diff 或過濾雜亂的指令輸出非常有幫助。範例(從 Bash 輸出中抹除 ANSI 顏色碼):

json
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "updatedToolOutput": "<plain-text output with ANSI escapes removed>"
  }
}

retry (PermissionDenied):使用 JSON hookSpecificOutput.retry: true 來告知模型它可以重試被拒絕的工具呼叫。

已廢棄的 PreToolUse 決策形式:對於 PreToolUse,頂層的 decisionreason 欄位已廢棄 — 請改用 hookSpecificOutput.permissionDecision (allow / deny / ask / defer) 和 permissionDecisionReason。決策之間的優先順序為 deny > defer > ask > allow。另請注意,suppressOutput 雖被接受但沒有任何效果

terminalSequence (v2.1.141)

鉤子可以透過在 JSON 輸出中設定 terminalSequence 來發出原始 OSC (作業系統指令 Operating System Command) 逸出序列。當鉤子傳回時,主機將該序列寫入其控制終端機 — 對於桌面通知、視窗標題更新和終端機響鈴非常有幫助,無需您自己的 TTY。

欄位 (Field)類型 (Type)說明 (Description)
terminalSequencestring原始逸出序列(通常為 OSC 9 / OSC 0 / OSC 777)。逐字寫入主機終端機。

範例 — 長時間運行的任務完成時觸發 OSC 9 桌面通知:

json
{
  "terminalSequence": " ]9;Task complete "
}

將其設定在 Stop 鉤子上,以便在 Claude 完成回合時觸發通知。序列支援取決於終端機;Kitty/iTerm2/Windows Terminal 支援 OSC 9。

環境變數 (Environment Variables)

變數 (Variable)可用性 (Availability)說明 (Description)
CLAUDE_PROJECT_DIR所有鉤子專案根目錄的絕對路徑
CLAUDE_ENV_FILESessionStart, CwdChanged, FileChanged用於持久化環境變數的檔案路徑
CLAUDE_CODE_REMOTE所有鉤子若在遠端環境中執行則為 "true"
${CLAUDE_PLUGIN_ROOT}插件鉤子插件目錄的路徑
${CLAUDE_PLUGIN_DATA}插件鉤子插件資料目錄的路徑
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MSSessionEnd 鉤子可設定的 SessionEnd 鉤子毫秒逾時時間(覆寫預設值)
CLAUDE_CODE_SESSION_IDBash 工具子處理序 (v2.1.132+)工作階段 UUID;匹配鉤子輸入 JSON 中的 session_id 欄位。用於將 bash 日誌與鉤子遙測數據建立關聯。
CLAUDE_EFFORTBash 工具子處理序 (v2.1.133+)當前努力程度 (low/medium/high/xhigh/max);匹配鉤子輸入 JSON 中的 effort.level
CLAUDE_CODE_STOP_HOOK_BLOCK_CAP進程層級 (Process-wide, v2.1.143+)工作階段帶著警告結束前的最大連續 Stop 鉤子阻塞次數(預設為 8)。設定為 0 可停用上限。

基於提示詞的鉤子 (Prompt-Based Hooks)

對於 StopSubagentStop 事件,您可以使用基於 LLM 的評估:

json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Review if all tasks are complete. Return your decision.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

LLM 回應 Schema:

json
{
  "decision": "approve",
  "reason": "All tasks completed successfully",
  "continue": false,
  "stopReason": "Task complete"
}

範例 (Examples)

範例 1:Bash 指令驗證器 (PreToolUse)

檔案: .claude/hooks/validate-bash.py

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

BLOCKED_PATTERNS = [
    (r"\brm\s+-rf\s+/", "Blocking dangerous rm -rf / command"),
    (r"\bsudo\s+rm", "Blocking sudo rm command"),
]

def main():
    input_data = json.load(sys.stdin)

    tool_name = input_data.get("tool_name", "")
    if tool_name != "Bash":
        sys.exit(0)

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

    for pattern, message in BLOCKED_PATTERNS:
        if re.search(pattern, command):
            print(message, file=sys.stderr)
            sys.exit(2)  # Exit 2 = blocking error

    sys.exit(0)

if __name__ == "__main__":
    main()

設定檔:

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/validate-bash.py\""
          }
        ]
      }
    ]
  }
}

範例 2:安全性掃描器 (PostToolUse)

檔案: .claude/hooks/security-scan.py

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

SECRET_PATTERNS = [
    (r"password\s*=\s*['\"][^'\"]+['\"]", "Potential hardcoded password"),
    (r"api[_-]?key\s*=\s*['\"][^'\"]+['\"]", "Potential hardcoded API key"),
]

def main():
    input_data = json.load(sys.stdin)

    tool_name = input_data.get("tool_name", "")
    if tool_name not in ["Write", "Edit"]:
        sys.exit(0)

    tool_input = input_data.get("tool_input", {})
    content = tool_input.get("content", "") or tool_input.get("new_string", "")
    file_path = tool_input.get("file_path", "")

    warnings = []
    for pattern, message in SECRET_PATTERNS:
        if re.search(pattern, content, re.IGNORECASE):
            warnings.append(message)

    if warnings:
        output = {
            "hookSpecificOutput": {
                "hookEventName": "PostToolUse",
                "additionalContext": f"Security warnings for {file_path}: " + "; ".join(warnings)
            }
        }
        print(json.dumps(output))

    sys.exit(0)

if __name__ == "__main__":
    main()

範例 3:自動排版程式碼 (PostToolUse)

檔案: .claude/hooks/format-code.sh

bash
#!/bin/bash

# Read JSON from stdin
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | python3 -c "import sys, json; print(json.load(sys.stdin).get('tool_name', ''))")
FILE_PATH=$(echo "$INPUT" | python3 -c "import sys, json; print(json.load(sys.stdin).get('tool_input', {}).get('file_path', ''))")

if [ "$TOOL_NAME" != "Write" ] && [ "$TOOL_NAME" != "Edit" ]; then
    exit 0
fi

# Format based on file extension
case "$FILE_PATH" in
    *.js|*.jsx|*.ts|*.tsx|*.json)
        command -v prettier &>/dev/null && prettier --write "$FILE_PATH" 2>/dev/null
        ;;
    *.py)
        command -v black &>/dev/null && black "$FILE_PATH" 2>/dev/null
        ;;
    *.go)
        command -v gofmt &>/dev/null && gofmt -w "$FILE_PATH" 2>/dev/null
        ;;
esac

exit 0

範例 4:提示詞驗證器 (UserPromptSubmit)

檔案: .claude/hooks/validate-prompt.py

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

BLOCKED_PATTERNS = [
    (r"delete\s+(all\s+)?database", "Dangerous: database deletion"),
    (r"rm\s+-rf\s+/", "Dangerous: root deletion"),
]

def main():
    input_data = json.load(sys.stdin)
    prompt = input_data.get("user_prompt", "") or input_data.get("prompt", "")

    for pattern, message in BLOCKED_PATTERNS:
        if re.search(pattern, prompt, re.IGNORECASE):
            output = {
                "decision": "block",
                "reason": f"Blocked: {message}"
            }
            print(json.dumps(output))
            sys.exit(0)

    sys.exit(0)

if __name__ == "__main__":
    main()

範例 5:智慧型 Stop 鉤子(基於提示詞)

json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Review if Claude completed all requested tasks. Check: 1) Were all files created/modified? 2) Were there unresolved errors? If incomplete, explain what's missing.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

範例 6:脈絡使用量追蹤器(鉤子配對)

透過搭配使用 UserPromptSubmit(訊息發送前)與 Stop(回應完成後)鉤子,追蹤每次請求的 Token 消耗量。

檔案: .claude/hooks/context-tracker.py

python
#!/usr/bin/env python3
"""
Context Usage Tracker - Tracks token consumption per request.

Uses UserPromptSubmit as "pre-message" hook and Stop as "post-response" hook
to calculate the delta in token usage for each request.

Token Counting Methods:
1. Character estimation (default): ~4 chars per token, no dependencies
2. tiktoken (optional): More accurate (~90-95%), requires: pip install tiktoken
"""
import json
import os
import sys
import tempfile

# Configuration
CONTEXT_LIMIT = 128000  # Claude's context window (adjust for your model)
USE_TIKTOKEN = False    # Set True if tiktoken is installed for better accuracy


def get_state_file(session_id: str) -> str:
    """Get temp file path for storing pre-message token count, isolated by session."""
    return os.path.join(tempfile.gettempdir(), f"claude-context-{session_id}.json")


def count_tokens(text: str) -> int:
    """
    Count tokens in text.

    Uses tiktoken with p50k_base encoding if available (~90-95% accuracy),
    otherwise falls back to character estimation (~80-90% accuracy).
    """
    if USE_TIKTOKEN:
        try:
            import tiktoken
            enc = tiktoken.get_encoding("p50k_base")
            return len(enc.encode(text))
        except ImportError:
            pass  # Fall back to estimation

    # Character-based estimation: ~4 characters per token for English
    return len(text) // 4


def read_transcript(transcript_path: str) -> str:
    """Read and concatenate all content from transcript file."""
    if not transcript_path or not os.path.exists(transcript_path):
        return ""

    content = []
    with open(transcript_path, "r") as f:
        for line in f:
            try:
                entry = json.loads(line.strip())
                # Extract text content from various message formats
                if "message" in entry:
                    msg = entry["message"]
                    if isinstance(msg.get("content"), str):
                        content.append(msg["content"])
                    elif isinstance(msg.get("content"), list):
                        for block in msg["content"]:
                            if isinstance(block, dict) and block.get("type") == "text":
                                content.append(block.get("text", ""))
            except json.JSONDecodeError:
                continue

    return "\n".join(content)


def handle_user_prompt_submit(data: dict) -> None:
    """Pre-message hook: Save current token count before request."""
    session_id = data.get("session_id", "unknown")
    transcript_path = data.get("transcript_path", "")

    transcript_content = read_transcript(transcript_path)
    current_tokens = count_tokens(transcript_content)

    # Save to temp file for later comparison
    state_file = get_state_file(session_id)
    with open(state_file, "w") as f:
        json.dump({"pre_tokens": current_tokens}, f)


def handle_stop(data: dict) -> None:
    """Post-response hook: Calculate and report token delta."""
    session_id = data.get("session_id", "unknown")
    transcript_path = data.get("transcript_path", "")

    transcript_content = read_transcript(transcript_path)
    current_tokens = count_tokens(transcript_content)

    # Load pre-message count
    state_file = get_state_file(session_id)
    pre_tokens = 0
    if os.path.exists(state_file):
        try:
            with open(state_file, "r") as f:
                state = json.load(f)
                pre_tokens = state.get("pre_tokens", 0)
        except (json.JSONDecodeError, IOError):
            pass

    # Calculate delta
    delta_tokens = current_tokens - pre_tokens
    remaining = CONTEXT_LIMIT - current_tokens
    percentage = (current_tokens / CONTEXT_LIMIT) * 100

    # Report usage
    method = "tiktoken" if USE_TIKTOKEN else "estimated"
    print(f"Context ({method}): ~{current_tokens:,} tokens ({percentage:.1f}% used, ~{remaining:,} remaining)", file=sys.stderr)
    if delta_tokens > 0:
        print(f"This request: ~{delta_tokens:,} tokens", file=sys.stderr)


def main():
    data = json.load(sys.stdin)
    event = data.get("hook_event_name", "")

    if event == "UserPromptSubmit":
        handle_user_prompt_submit(data)
    elif event == "Stop":
        handle_stop(data)

    sys.exit(0)


if __name__ == "__main__":
    main()

設定檔:

json
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/context-tracker.py\""
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/context-tracker.py\""
          }
        ]
      }
    ]
  }
}

運作原理:

  1. UserPromptSubmit 在您的提示詞被處理前觸發 - 儲存當前的 Token 計算數量
  2. Stop 在 Claude 回應後觸發 - 計算差值並回報使用量
  3. 每個工作階段透過暫存檔名中的 session_id 進行隔離

Token 計算方法:

方法 (Method)準確度 (Accuracy)相依套件 (Dependencies)速度 (Speed)
字元預估法 (Character estimation)~80-90%<1ms
tiktoken (p50k_base)~90-95%pip install tiktoken<10ms

注意: Anthropic 尚未釋出官方的離線 Tokenizer。兩種方法皆為近似值。逐字稿包含使用者提示詞、Claude 的回應以及工具輸出,但不包含系統提示詞或內部脈絡。

範例 7:預先播種自動模式權限(一次性設定腳本)

這是一個一次性設定腳本,可用相當於 Claude Code 自動模式基線的 ~67 個安全權限規則預先播種 ~/.claude/settings.json — 無需任何鉤子,亦無需記住未來的選擇。執行一次即可;重複執行亦安全(會跳過已存在的規則)。

檔案: 09-advanced-features/setup-auto-mode-permissions.py

bash
# Preview what would be added
python3 09-advanced-features/setup-auto-mode-permissions.py --dry-run

# Apply
python3 09-advanced-features/setup-auto-mode-permissions.py

新增的內容:

分類 (Category)範例 (Examples)
內建工具Read(*), Edit(*), Write(*), Glob(*), Grep(*), Agent(*), WebSearch(*)
Git 讀取Bash(git status:*), Bash(git log:*), Bash(git diff:*)
Git 寫入 (本地)Bash(git add:*), Bash(git commit:*), Bash(git checkout:*)
套件管理工具Bash(npm install:*), Bash(pip install:*), Bash(cargo build:*)
建置與測試Bash(make:*), Bash(pytest:*), Bash(go test:*)
常見 ShellBash(ls:*), Bash(cat:*), Bash(find:*), Bash(cp:*), Bash(mv:*)
GitHub CLIBash(gh pr view:*), Bash(gh pr create:*), Bash(gh issue list:*)

刻意排除的內容(此腳本絕不新增):

  • rm -rfsudo、強制推送 (force push)、git reset --hard
  • DROP TABLEkubectl deleteterraform destroy
  • npm publishcurl | bash、正式環境部署 (production deploys)

範例 8:學習進度紀錄器 (SessionEnd)

在每個 Claude Code 工作階段結束時紀錄您學習了哪些模組。進度儲存在 ~/.claude-howto-progress.json 中 — 位於儲存庫之外,因此在執行 git pull 時不會被覆寫。

為什麼使用 SessionEnd 而非 StopStop 會在 Claude 的每一次回應後觸發。SessionEnd 則是在工作階段終止時觸發一次 — 這正是您進行工作階段結束日記紀錄所需要的。

為什麼輸入要使用 /dev/tty 鉤子腳本透過 stdin 接收鉤子 JSON 載荷,因此互動式 read 必須直接使用 /dev/tty 才能連接到終端機。

檔案: 06-hooks/session-end.sh

bash
#!/usr/bin/env bash
# SessionEnd hook: prompts for modules worked on, then appends a session record
# to ~/.claude-howto-progress.json for persistent learning progress tracking.

PROGRESS_FILE="$HOME/.claude-howto-progress.json"

# Guard: only run inside this repo
if [[ "$CLAUDE_PROJECT_DIR" != *"claude-howto"* ]] && [[ "$PWD" != *"claude-howto"* ]]; then
  exit 0
fi

if [ ! -f "$PROGRESS_FILE" ]; then
  echo '{"sessions":[]}' > "$PROGRESS_FILE"
fi

DATE=$(date +"%Y-%m-%d")
TIME=$(date +"%H:%M")

echo ""
echo " Which modules did you work on? (e.g. 06,07 or press Enter to skip)"
echo " 01=Slash  02=Memory  03=Skills  04=Subagents  05=MCP"
echo " 06=Hooks  07=Plugins 08=Checkpoints 09=Advanced 10=CLI"
printf " > "
read -r INPUT </dev/tty

if [ -z "$INPUT" ] || [ "$INPUT" = "skip" ]; then
  exit 0
fi

MODULES_JSON=$(echo "$INPUT" | tr ',' '\n' | tr -d ' ' | while read -r m; do
  case "$m" in
    01) echo '"01-slash-commands"' ;;
    02) echo '"02-memory"' ;;
    03) echo '"03-skills"' ;;
    04) echo '"04-subagents"' ;;
    05) echo '"05-mcp"' ;;
    06) echo '"06-hooks"' ;;
    07) echo '"07-plugins"' ;;
    08) echo '"08-checkpoints"' ;;
    09) echo '"09-advanced-features"' ;;
    10) echo '"10-cli"' ;;
    *)  echo "\"$m\"" ;;
  esac
done | paste -sd ',' -)

printf " Notes? (optional, press Enter to skip): "
read -r NOTES </dev/tty

# Pass NOTES as a separate argument so Python handles JSON escaping —
# avoids broken JSON when notes contain quotes or backslashes.
python3 - "$PROGRESS_FILE" "$DATE" "$TIME" "$MODULES_JSON" "$NOTES" <<'PYEOF'
import sys, json

path, date, time_str, modules_raw, notes = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5]

new_session = {
    "date": date,
    "time": time_str,
    "modules": json.loads(f"[{modules_raw}]") if modules_raw else [],
    "notes": notes,
}

with open(path, 'r') as f:
    data = json.load(f)

data.setdefault('sessions', []).append(new_session)

with open(path, 'w') as f:
    json.dump(data, f, indent=2)
PYEOF

echo " Saved to $PROGRESS_FILE"

安裝 (Install) — 將腳本複製到專案的鉤子目錄中,以便 settings.json 中的路徑能正確解析:

bash
mkdir -p .claude/hooks
cp 06-hooks/session-end.sh .claude/hooks/
chmod +x .claude/hooks/session-end.sh

設定檔 (Configuration)(位於 .claude/settings.json 中):

json
{
  "hooks": {
    "SessionEnd": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/session-end.sh\""
          }
        ]
      }
    ]
  }
}

輸出結果 — ~/.claude-howto-progress.json

json
{
  "sessions": [
    {
      "date": "2026-04-18",
      "time": "14:32",
      "modules": ["06-hooks", "07-plugins"],
      "notes": "Installed first hook, tried pre-commit example"
    }
  ]
}

示範的核心模式:

模式 (Pattern)重要原因 (Why it matters)
SessionEnd 事件在退出時觸發一次 — 而非像 Stop 那樣在每次回應後都觸發
read -r INPUT </dev/tty鉤子擁有 stdin(JSON 載荷);對使用者輸入使用 /dev/tty
$CLAUDE_PROJECT_DIR可移植路徑 — 絕不寫死 /Users/yourname/...
頂部的守衛子句 (Guard clause)若進行全域安裝,可防止鉤子在無關的專案中執行
儲存在儲存庫外~/ 路徑可在 git pull 時存留且不會覆寫您的資料

搭配工具:視覺化進度追蹤器

如需涵蓋所有 10 個模組的完整勾選清單 UI,請在瀏覽器中開啟附帶的追蹤器:

bash
open local-progress/index.html

進度儲存在瀏覽器的 localStorage 中(絕不寫入儲存庫內部的磁碟)。 使用 Export 按鈕將快照儲存為 JSON,使用 Import 按鈕進行還原。

插件鉤子 (Plugin Hooks)

插件可以在其 hooks/hooks.json 檔案中包含鉤子:

檔案: plugins/hooks/hooks.json

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh"
          }
        ]
      }
    ]
  }
}

插件鉤子中的環境變數:

  • ${CLAUDE_PLUGIN_ROOT} - 插件目錄的路徑
  • ${CLAUDE_PLUGIN_DATA} - 插件資料目錄的路徑

這允許插件包含自訂的驗證與自動化鉤子。

MCP 工具鉤子 (MCP Tool Hooks)

MCP 工具遵循 mcp__<server>__<tool> 模式:

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo '{\"systemMessage\": \"Memory operation logged\"}'"
          }
        ]
      }
    ]
  }
}

安全考量 (Security Considerations)

免責聲明 (Disclaimer)

風險自負 (USE AT YOUR OWN RISK):鉤子會執行任意 Shell 指令。您需對以下事項承擔全部責任:

  • 您設定的指令
  • 檔案存取/修改權限
  • 潛在的資料遺失或系統損壞
  • 在生產環境使用前先在安全環境中測試鉤子

安全注意事項 (Security Notes)

  • 需要工作區信任: statusLinefileSuggestion 鉤子輸出指令現在需要先接受工作區信任 (Workspace Trust) 才能生效。
  • 狀態列終端機尺寸 (v2.1.153): 狀態列指令腳本現在會收到 COLUMNSLINES 環境變數,因此腳本可以調整其輸出以適應終端機寬度/高度(例如 [ "$COLUMNS" -lt 80 ] && short_output)。
  • HTTP 鉤子與環境變數: HTTP 鉤子需要明確的 allowedEnvVars 清單才能在 URL 中使用環境變數插值。這可以防止敏感環境變數意外洩露給遠端端點。
  • 受控設定階層: disableAllHooks 設定現在會遵循受控設定階層 (Managed Settings Hierarchy),這意味著組織層級的設定可以強制停用鉤子,且個人使用者無法覆寫。
  • PowerShell 自動核准 (v2.1.119): PowerShell 工具指令可在權限模式下自動核准,對標 Bash。這為在使用 PowerShell 支援的 Shell 工具的 Windows 使用者帶來了一致性。
  • Bash 純環境變數自動核准漏洞已修補 (v2.1.145): 在 v2.1.145 之前,形式為 FOO=bar somecommand 的 Bash 指令(與非准許清單指令同行的純變數指派),當僅有 FOO=bar 本身在准許清單上時可能會被自動核准。v2.1.145 修補了此漏洞 — 此類指令現在會觸發權限提示。依賴隱式允許的腳本將開始提示;請透過涵蓋完整指令(而非僅變數指派)的 Bash(...) 權限規則明確地重新允許它們。

最佳實踐 (Best Practices)

應該 (Do)不應該 (Don't)
驗證並清理所有輸入盲目信任輸入資料
為 Shell 變數加引號:"$VAR"使用未加引號的變數:$VAR
阻止路徑巡覽 (..)允許任意路徑
使用帶有 $CLAUDE_PROJECT_DIR 的絕對路徑將路徑寫死
跳過敏感檔案 (.env, .git/, 金鑰)處理所有檔案
先單獨測試鉤子部署未測試的鉤子
對 HTTP 鉤子使用明確的 allowedEnvVars向 Webhook 暴露所有環境變數

偵錯 (Debugging)

啟用偵錯模式 (Enable Debug Mode)

帶著偵錯標記執行 Claude 以獲取詳細的鉤子日誌:

bash
claude --debug

詳細模式 (Verbose Mode)

在 Claude Code 中使用 Ctrl+O 啟用詳細模式並查看鉤子執行進度。

獨立測試鉤子 (Test Hooks Independently)

bash
# Test with sample JSON input
echo '{"tool_name": "Bash", "tool_input": {"command": "ls -la"}}' | python3 .claude/hooks/validate-bash.py

# Check exit code
echo $?

完整設定範例 (Complete Configuration Example)

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/validate-bash.py\"",
            "timeout": 10
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/format-code.sh\"",
            "timeout": 30
          },
          {
            "type": "command",
            "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/security-scan.py\"",
            "timeout": 10
          }
        ]
      }
    ],
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/validate-prompt.py\""
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/session-init.sh\""
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Verify all tasks are complete before stopping.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

鉤子執行細節 (Hook Execution Details)

面向 (Aspect)行為 (Behavior)
逾時 (Timeout)command/http/mcp_tool 預設 600 秒(prompt 為 30 秒,agent 為 60 秒);每個鉤子均可設定
並行化 (Parallelization)所有匹配的鉤子並行執行
去重 (Deduplication)相同的鉤子指令會進行去重
環境 (Environment)在目前目錄下帶著 Claude Code 的環境執行

疑難排解 (Troubleshooting)

鉤子未執行 (Hook Not Executing)

  • 檢查 JSON 設定語法是否正確
  • 檢查匹配器模式是否符合工具名稱
  • 確保腳本存在且具有可執行權限:chmod +x script.sh
  • 執行 claude --debug 查看鉤子執行日誌
  • 確認鉤子是從 stdin 讀取 JSON(而非指令引數)

鉤子意外阻塞 (Hook Blocks Unexpectedly)

  • 使用範例 JSON 測試鉤子:echo '{"tool_name": "Write", ...}' | ./hook.py
  • 檢查結束碼:允許應為 0,阻塞應為 2
  • 檢查 stderr 輸出(結束碼為 2 時顯示)

JSON 解析錯誤 (JSON Parsing Errors)

  • 始終從 stdin 讀取,而非指令引數
  • 使用正確的 JSON 解析(而非字串處理)
  • 優雅地處理缺失的欄位

安裝 (Installation)

步驟 1:建立鉤子目錄

bash
mkdir -p ~/.claude/hooks

步驟 2:複製範例鉤子

bash
cp 06-hooks/*.sh ~/.claude/hooks/
chmod +x ~/.claude/hooks/*.sh

步驟 3:在設定檔中進行設定

編輯 ~/.claude/settings.json.claude/settings.json 並加入上述鉤子設定。

其他資源 (Additional Resources)


最後更新:2026年9月2日 Claude Code 版本:2.1.257 資料來源

Released under the MIT License.