鉤子 (Hooks)
鉤子 (Hooks) 是在 Claude Code 工作階段 (Session) 中響應特定事件而執行的自動化腳本。它們支援自動化、驗證、權限管理以及自訂工作流程。
概觀 (Overview)
鉤子 (Hooks) 是當 Claude Code 中發生特定事件時自動執行的自動化動作(Shell 指令、HTTP Webhook、LLM 提示詞、MCP 工具呼叫或子代理評估)。它們接收 JSON 輸入並透過結束碼 (Exit Codes) 與 JSON 輸出傳達結果。
主要特性:
- 事件驅動 (Event-driven) 的自動化
- 基於 JSON 的輸入/輸出 (Input/Output)
- 支援
command、http、mcp_tool、prompt和agent等鉤子類型 - 針對特定工具的鉤子模式匹配 (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)
{
"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。隱含包含 async。 | true |
shell | 接受 "bash" 或 "powershell"。預設為 "bash",未安裝 Git Bash 的 Windows 則預設為 "powershell"。 | "bash" |
statusMessage | 當鉤子執行時顯示的自訂載入訊息 (Spinner Message) | "Formatting…" |
注意:某些事件會降低預設逾時時間。
UserPromptSubmit將command、http與mcp_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) — 與 type 和 command 同級,位於 hooks 陣列內部 — 而非位於 matcher 上:
{
"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 及結束碼進行溝通。
{
"type": "command",
"command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/validate.py\"",
"timeout": 60
}Exec 形式 (args)
於 v2.1.139 新增。
指令鉤子除了使用 Shell 形式的 "command": "..." 之外,還可以透過包含 args 陣列的 execve() 直接啟動二進位檔案。由於沒有 Shell 解析,路徑預留位置永遠不需要加引號,且設定不受 Shell 注入漏洞影響。
{
"type": "command",
"args": ["python3", "$CLAUDE_PROJECT_DIR/.claude/hooks/validate.py", "--strict"],
"timeout": 60
}這兩種形式為互斥 (Mutually exclusive) — 若鉤子同時設定了 command 和 args,在載入設定時會被拒絕。當您需要管道 (Pipes)、重導向、&& 鏈結或 Shell 展開時,請使用 command;當您要帶著引數呼叫單一二進位檔案時,請使用 args。
HTTP 鉤子 (HTTP Hooks)
於 v2.1.63 新增。
接收與指令鉤子相同 JSON 輸入的遠端 Webhook 端點。HTTP 鉤子向 URL 發送 POST JSON 並接收 JSON 回應。啟用沙盒 (Sandboxing) 時,HTTP 鉤子會透過沙盒進行路由。出於安全考量,URL 中的環境變數插值需要明確的 allowedEnvVars 准許清單。
{
"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 進行評估的提示詞。主要與 Stop 和 SubagentStop 事件搭配使用,用於智慧型任務完成度檢查。
{
"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 伺服器中時,這非常有用。
{
"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 評估)不同,代理鉤子可以使用工具並執行多步驟推理。
注意:代理鉤子目前處於實驗階段,可能會有所變動。
{
"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 | 子代理完成時 | 代理類型名稱 | 是 | 子代理驗證 |
| Stop | Claude 完成回應時 | (無) | 是 | 任務完成度檢查 |
| StopFailure | API 錯誤終止回合時 | (無) | 否 | 錯誤復原、紀錄 |
| TeammateIdle | 代理團隊成員閒置時 | (無) | 是 | 團隊成員協調 |
| TaskCompleted | 任務標記為完成時 | (無) | 是 | 任務後置動作 |
| TaskCreated | 透過 TaskCreate 建立任務時 | (無) | 否 | 任務追蹤、紀錄 |
| ConfigChange | 設定檔變更時 | (無) | 是 (策略除外) | 對設定更新做出反應 |
| CwdChanged | 工作目錄變更時 | (無) | 否 | 目錄專屬設定 |
| DirectoryAdded | 在工作階段中透過 /add-dir 或 SDK register_repo_root 控制請求註冊新工作目錄時 (v2.1.219) | (無) | 否 | 為新新增的目錄設定工具 |
| FileChanged | 監視的檔案變更時 | (無) | 否 | 檔案監控、重新建置 |
| PreCompact | 脈絡精簡 (Compaction) 前 | manual/auto | 否 | 精簡前動作 |
| PostCompact | 精簡完成後 | (無) | 否 | 精簡後動作 |
| PreModelSwitch | Claude Code 套用請求的模型切換前 | 要切換到的模型規範名稱(來自 to_model) | 是 | 門控或否決模型變更 |
| PostModelSwitch | 工作階段模型變更後(包含 Claude Code 本身做出的變更,如恢復時還原模型) | 切換到的模型規範名稱(來自 to_model) | 否 | 紀錄或對模型變更做出反應 |
| WorktreeCreate | 正在建立 Worktree 時 | (無) | 是 (傳回路徑) | Worktree 初始化 |
| WorktreeRemove | 正在移除 Worktree 時 | (無) | 否 | Worktree 清理 |
| Elicitation | MCP 伺服器請求使用者輸入時 | (無) | 是 | 輸入驗證 |
| ElicitationResult | 使用者回應引導 (Elicitation) 時 | (無) | 是 | 回應處理 |
| SessionEnd | 工作階段終止時 | (無) | 否 | 清理、最終紀錄 |
PreModelSwitch 與 PostModelSwitch 需要 v2.1.251 或更高版本。兩者皆接收 from_model 與 to_model;匹配器會針對源自 to_model 的規範名稱(例如 claude-opus-5、.*opus.*)進行評估。它們的 command、http 與 mcp_tool 預設逾時降至 30 秒。
TaskCreated與TaskCompleted需要啟用待辦事項工具 (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):
PostToolUse與PostToolUseFailure鉤子輸入現在包含duration_ms— 詳情請參閱 PostToolUse 章節。
PreToolUse
在 Claude 建立工具參數之後、執行處理之前運行。可用於驗證或修改工具輸入。
設定範例:
{
"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"優雅退出以便稍後恢復工具;此值會忽略permissionDecisionReason、updatedInput和additionalContext- 無論鉤子傳回什麼,拒絕 (Deny) 與詢問 (Ask) 規則仍會被評估。當多個
PreToolUse鉤子意見不一致時,優先順序為deny>defer>ask>allow
permissionDecisionReason: 決策的解釋說明。對於"allow"和"ask",會顯示給使用者(而非 Claude);對於"deny",會顯示給 Claude;對於"defer"則忽略updatedInput: 修改後的工具輸入參數
PostToolUse
在工具執行完成後立即運行。用於驗證、紀錄或將脈絡傳回給 Claude。
設定範例:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/security-scan.py"
}
]
}
]
}
}輸出控制:
"block"決策會帶著回饋提示 ClaudeadditionalContext: 為 Claude 新增的脈絡
新增輸入欄位 (v2.1.119):
| 欄位 (Field) | 類型 (Type) | 說明 (Description) |
|---|---|---|
duration_ms | number | 工具執行時間(毫秒)。不包含花費在權限提示與 PreToolUse 鉤子執行的時間。在 PostToolUse 和 PostToolUseFailure 鉤子上均可使用。 |
可復原的阻塞 (continueOnBlock, v2.1.139)
預設情況下,傳回 "decision": "block" 的 PostToolUse 鉤子會中斷當前回合。在鉤子上設定 "continueOnBlock": true 則會將拒絕資訊作為 tool_result 呈現給 Claude,以便模型能讀取回饋並重試或進行調整。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/policy-check.py",
"continueOnBlock": true
}
]
}
]
}
}當鉤子的 reason 是 Claude 可以據以採取行動的內容時(例如「此檔案為唯讀;請寫入其他地方」),請使用此屬性;當阻塞必須完全中止回合時,請保持未設定狀態。
UserPromptSubmit
當使用者提交提示詞、Claude 處理之前運行。
設定範例:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/validate-prompt.py"
}
]
}
]
}
}輸出控制:
decision:"block"用於阻止處理reason: 若被阻塞時的解釋說明additionalContext: 新增至提示詞的脈絡
Stop 與 SubagentStop
當 Claude 完成回應 (Stop) 或子代理完成時 (SubagentStop) 運行。支援基於提示詞的評估,以進行智慧型任務完成度檢查。
額外輸入欄位: Stop 與 SubagentStop 鉤子在其 JSON 輸入中皆會收到 last_assistant_message 欄位,其中包含 Claude 或子代理停止前發出的最後一條訊息。這對於評估任務完成度非常有幫助。
設定範例:
{
"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):
Stop或SubagentStop鉤子可以傳回hookSpecificOutput.additionalContext以給予 Claude 回饋並繼續回合而不顯示錯誤標籤。以前,透過 Stop 鉤子影響模型非常笨拙;現在鉤子可以乾淨地注入脈絡,避免舊版回饋路徑(例如"decision": "block")的錯誤標籤行為。
{
"hookSpecificOutput": {
"hookEventName": "Stop",
"additionalContext": "Reminder: run the test suite before declaring done."
}
}SubagentStart
當子代理開始執行時運行。匹配器輸入為代理類型名稱,允許鉤子針對特定的子代理類型。
設定範例:
{
"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 持久化環境變數(在 CwdChanged 與 FileChanged 鉤子中亦可用):
#!/bin/bash
if [ -n "$CLAUDE_ENV_FILE" ]; then
echo 'export NODE_ENV=development' >> "$CLAUDE_ENV_FILE"
fi
exit 0工作階段作用域的輸出 (v2.1.152): SessionStart 鉤子可以傳回 JSON 以重新掃描技能並設定工作階段標題:
{
"reloadSkills": true,
"hookSpecificOutput": {
"sessionTitle": "Payments migration"
}
}頂層的 reloadSkills: true 會在同一個工作階段中觸發技能重新掃描(與 /reload-skills 指令動作相同),使鉤子剛剛安裝的技能立即可用。hookSpecificOutput.sessionTitle 會在啟動與恢復時設定工作階段的顯示標題。
SessionEnd
當工作階段結束時運行,用於執行清理或最終紀錄。無法阻塞終止流程。
Reason 欄位值:
clear- 使用者清除了工作階段logout- 使用者登出prompt_input_exit- 使用者透過提示詞輸入退出other- 其他原因
設定範例:
{
"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 顯示為錯誤,因此工作階段會保持其當前模型。使用此功能可門控或否決模型變更 — 例如防止對成本敏感的專案切換到最昂貴的模型。
逾時時間: 此事件會將 command、http 與 mcp_tool 的預設逾時降低至 30 秒。
設定範例:
{
"hooks": {
"PreModelSwitch": [
{
"matcher": ".*opus.*",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/gate-model-switch.sh"
}
]
}
]
}
}#!/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 0PostModelSwitch
在工作階段的模型變更後運行。它也會在 Claude Code 本身做出變更時觸發 — 例如當您恢復工作階段時還原先前選擇的模型 — 而非僅在您主動請求切換時觸發。需要 v2.1.251 或更高版本。
匹配器: 與 PreModelSwitch 相同 — 源自 to_model 的規範名稱。
輸入欄位: 除通用欄位外,包含 from_model 和 to_model。
是否可阻塞: 否。切換已經發生;鉤子只能觀察並做出反應。
逾時時間: 此事件會將 command、http 與 mcp_tool 的預設逾時降低至 30 秒。
設定範例:
{
"hooks": {
"PostModelSwitch": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/log-model-switch.sh"
}
]
}
]
}
}#!/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 中:
---
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 鉤子。這能確保停止鉤子僅在該特定子代理完成時觸發,而非在主工作階段停止時觸發。
---
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 事件
使用自訂輸出格式處理權限請求:
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow|deny",
"updatedInput": {},
"message": "Custom message",
"interrupt": false
}
}
}鉤子輸入與輸出 (Hook Input and Output)
JSON 輸入 (透過 stdin)
所有鉤子皆透過 stdin 接收 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"、子代理類型名稱等) |
worktree | Git 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)
{
"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 工具。在Bash、Edit、Read等工具上的PostToolUse鉤子可以在 Claude 看到之前重寫工具的輸出 — 這對於隱藏敏感資訊、規範化 diff 或過濾雜亂的指令輸出非常有幫助。範例(從Bash輸出中抹除 ANSI 顏色碼):json{ "hookSpecificOutput": { "hookEventName": "PostToolUse", "updatedToolOutput": "<plain-text output with ANSI escapes removed>" } }
retry(PermissionDenied):使用 JSONhookSpecificOutput.retry: true來告知模型它可以重試被拒絕的工具呼叫。
已廢棄的
PreToolUse決策形式:對於PreToolUse,頂層的decision和reason欄位已廢棄 — 請改用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) |
|---|---|---|
terminalSequence | string | 原始逸出序列(通常為 OSC 9 / OSC 0 / OSC 777)。逐字寫入主機終端機。 |
範例 — 長時間運行的任務完成時觸發 OSC 9 桌面通知:
{
"terminalSequence": " ]9;Task complete "
}將其設定在 Stop 鉤子上,以便在 Claude 完成回合時觸發通知。序列支援取決於終端機;Kitty/iTerm2/Windows Terminal 支援 OSC 9。
環境變數 (Environment Variables)
| 變數 (Variable) | 可用性 (Availability) | 說明 (Description) |
|---|---|---|
CLAUDE_PROJECT_DIR | 所有鉤子 | 專案根目錄的絕對路徑 |
CLAUDE_ENV_FILE | SessionStart, CwdChanged, FileChanged | 用於持久化環境變數的檔案路徑 |
CLAUDE_CODE_REMOTE | 所有鉤子 | 若在遠端環境中執行則為 "true" |
${CLAUDE_PLUGIN_ROOT} | 插件鉤子 | 插件目錄的路徑 |
${CLAUDE_PLUGIN_DATA} | 插件鉤子 | 插件資料目錄的路徑 |
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS | SessionEnd 鉤子 | 可設定的 SessionEnd 鉤子毫秒逾時時間(覆寫預設值) |
CLAUDE_CODE_SESSION_ID | Bash 工具子處理序 (v2.1.132+) | 工作階段 UUID;匹配鉤子輸入 JSON 中的 session_id 欄位。用於將 bash 日誌與鉤子遙測數據建立關聯。 |
CLAUDE_EFFORT | Bash 工具子處理序 (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)
對於 Stop 與 SubagentStop 事件,您可以使用基於 LLM 的評估:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Review if all tasks are complete. Return your decision.",
"timeout": 30
}
]
}
]
}
}LLM 回應 Schema:
{
"decision": "approve",
"reason": "All tasks completed successfully",
"continue": false,
"stopReason": "Task complete"
}範例 (Examples)
範例 1:Bash 指令驗證器 (PreToolUse)
檔案: .claude/hooks/validate-bash.py
#!/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()設定檔:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/validate-bash.py\""
}
]
}
]
}
}範例 2:安全性掃描器 (PostToolUse)
檔案: .claude/hooks/security-scan.py
#!/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
#!/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
#!/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 鉤子(基於提示詞)
{
"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
#!/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()設定檔:
{
"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\""
}
]
}
]
}
}運作原理:
UserPromptSubmit在您的提示詞被處理前觸發 - 儲存當前的 Token 計算數量Stop在 Claude 回應後觸發 - 計算差值並回報使用量- 每個工作階段透過暫存檔名中的
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
# 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:*) |
| 常見 Shell | Bash(ls:*), Bash(cat:*), Bash(find:*), Bash(cp:*), Bash(mv:*) |
| GitHub CLI | Bash(gh pr view:*), Bash(gh pr create:*), Bash(gh issue list:*) |
刻意排除的內容(此腳本絕不新增):
rm -rf、sudo、強制推送 (force push)、git reset --hardDROP TABLE、kubectl delete、terraform destroynpm publish、curl | bash、正式環境部署 (production deploys)
範例 8:學習進度紀錄器 (SessionEnd)
在每個 Claude Code 工作階段結束時紀錄您學習了哪些模組。進度儲存在 ~/.claude-howto-progress.json 中 — 位於儲存庫之外,因此在執行 git pull 時不會被覆寫。
為什麼使用 SessionEnd 而非 Stop?Stop 會在 Claude 的每一次回應後觸發。SessionEnd 則是在工作階段終止時觸發一次 — 這正是您進行工作階段結束日記紀錄所需要的。
為什麼輸入要使用 /dev/tty? 鉤子腳本透過 stdin 接收鉤子 JSON 載荷,因此互動式 read 必須直接使用 /dev/tty 才能連接到終端機。
檔案: 06-hooks/session-end.sh
#!/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 中的路徑能正確解析:
mkdir -p .claude/hooks
cp 06-hooks/session-end.sh .claude/hooks/
chmod +x .claude/hooks/session-end.sh設定檔 (Configuration)(位於 .claude/settings.json 中):
{
"hooks": {
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/session-end.sh\""
}
]
}
]
}
}輸出結果 — ~/.claude-howto-progress.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,請在瀏覽器中開啟附帶的追蹤器:
open local-progress/index.html進度儲存在瀏覽器的 localStorage 中(絕不寫入儲存庫內部的磁碟)。 使用 Export 按鈕將快照儲存為 JSON,使用 Import 按鈕進行還原。
插件鉤子 (Plugin Hooks)
插件可以在其 hooks/hooks.json 檔案中包含鉤子:
檔案: plugins/hooks/hooks.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> 模式:
{
"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)
- 需要工作區信任:
statusLine與fileSuggestion鉤子輸出指令現在需要先接受工作區信任 (Workspace Trust) 才能生效。 - 狀態列終端機尺寸 (v2.1.153): 狀態列指令腳本現在會收到
COLUMNS和LINES環境變數,因此腳本可以調整其輸出以適應終端機寬度/高度(例如[ "$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 以獲取詳細的鉤子日誌:
claude --debug詳細模式 (Verbose Mode)
在 Claude Code 中使用 Ctrl+O 啟用詳細模式並查看鉤子執行進度。
獨立測試鉤子 (Test Hooks Independently)
# 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)
{
"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:建立鉤子目錄
mkdir -p ~/.claude/hooks步驟 2:複製範例鉤子
cp 06-hooks/*.sh ~/.claude/hooks/
chmod +x ~/.claude/hooks/*.sh步驟 3:在設定檔中進行設定
編輯 ~/.claude/settings.json 或 .claude/settings.json 並加入上述鉤子設定。
相關概念 (Related Concepts)
- 檢查點與復原 (Checkpoints and Rewind) - 儲存與還原對話狀態
- 斜線指令 (Slash Commands) - 建立自訂斜線指令
- 技能 (Skills) - 可重複使用的自主能力
- 子代理 (Subagents) - 委派任務執行
- 插件 (Plugins) - 打包的擴充套件包
- 進階功能 (Advanced Features) - 探索 Claude Code 進階能力
其他資源 (Additional Resources)
- 官方鉤子文件 (Official Hooks Documentation) - 完整鉤子參考資料
- CLI 參考資料 (CLI Reference) - 命令列介面文件
- 記憶指南 (Memory Guide) - 持久化脈絡設定
最後更新:2026年9月2日 Claude Code 版本:2.1.257 資料來源:
- https://code.claude.com/docs/en/hooks
- https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md
- https://code.claude.com/docs/en/sub-agents相容模型:Claude Fable 5, Claude Opus 5, Claude Sonnet 5, Claude Sonnet 4.6, Claude Opus 4.8, Claude Haiku 4.5
