Skip to main content
這個單元解決什麼問題Hook 是在工具執行前後、session 起訖、prompt 提交等 30 個生命週期事件點自動跑的確定性腳本:格式化、驗證、攔截、防護網。它把「每次都要記得做」的事從模型軟約束變成系統硬保證。Claude Code v2.1 起 hook 系統大幅擴充:30 個事件、五種 handler 類型(command / http / mcp_tool / prompt / agent)、與 exit code 2 攔截語義。本單元講主要觸發點語義、settings.json 配置、實戰範本,以及 hook 作為可執行碼所附帶的供應鏈風險與掃描 SOP。

學習目標

  • 說出 Claude Code hook 的 30 個事件按觸發時機分組,並指出 PreToolUse、PostToolUse、Stop、SessionStart 的語義與典型用途。
  • 正確撰寫 settings.jsonhooks 設定,含 matchercommand,並能區分 matcher 的三種解析規則。
  • exit 2 攔截 PreToolUse 工具呼叫,以及用 additionalContext 為 Claude 注入額外 context。
  • 辨識 hook 作為外部可執行碼的供應鏈風險,並在引入第三方 hook 前做基本掃描。
  • 用 hook 把本庫的 normalize_punctuation.pyvalidate_kb --hook 接成確定性防護網。

1. Hook 是什麼:確定性自動化,補「不一定會記得」的缺口

CLAUDE.md 與 rules 是「請模型遵守」(軟約束、context 層)。Hook 是「系統強制執行」(硬保證、shell 層)。它不在模型的推理鏈內:hook 跑在你 shell 上、讀 stdin 上的工具輸入 JSON、決定要不要放行這次工具呼叫,模型預設不知道 hook 在跑。你用 additionalContext 主動告知時例外。 三個關鍵事實:
  • Hook 是確定性的:不靠模型「記得」或「判斷」,shell 該 exit 2 就是 exit 2。每次都一樣。
  • Hook 是可執行程式碼:裝 hook 等同於把一段 shell 指令加進你的工作流,這是供應鏈面的入口。
  • Hook 的觸發點遠比一般認得多:截至 2026-06 共 30 個事件,按觸發節奏分四組 [1]。
與 04-1 / 04-2 / 04-4 的分工寫「不要 commit .env」在 CLAUDE.md 的話,模型多半會記,但偶爾漏。 寫在 .claude/rules/security.md(path-scoped)的話,Claude 讀到相關檔案時收到約束,但仍只是 context。 寫成 hook(PreToolUse on Bash,matcher git commit *)的話,shell 端檢查 staged 檔案含 .envexit 2 攔截,任何時候都擋下判準只有一句:要「模型記得」還是「系統保證」? 後者就必須走 hook。

2. 30 個觸發事件分組

Claude Code v2.1 起 hook 系統涵蓋 30 個事件([1]),按觸發節奏分四組。

2.1 每次 session 一次

2.2 每次對話輪次一次

2.3 每個工具呼叫一次(agentic loop 內)

2.4 Subagent / task 事件

2.5 異步 / side-channel

本單元聚焦四個高頻事件30 個事件全部講會失焦。實務上 80% 的 hook 用在 PreToolUse、PostToolUse、Stop、SessionStart 這四個,其他事件留到遇到具體需求再查文件 [1]。

3. 配置:settings.json 結構

Hook 設定在 settings.jsonhooks 區塊,三層巢狀:事件 → matcher group → hook handlers([1])。

3.1 五種 handler 類型

type 欄位決定 handler 怎麼跑([1]): 預設 typecommand,省略 type 等同顯式指定 command

3.2 設定位置與 scope

Managed 層級優先組織可用 managed setting 設 allowManagedHooksOnly: true 阻擋使用者 / 專案 / plugin 層 hook。Plugin 透過 enabledPlugins 強制啟用的不受影響 [1]。

3.3 matcher 語法

matcher 解析規則按字元自動選擇([1]): 每個事件 match 不同欄位
  • 工具事件(PreToolUse / PostToolUse 等)match tool_name
  • SessionStart match 來源(startup / resume / clear / compact
  • Notification match 類型
  • FileChanged 是字面檔名(非 regex / 非 exact-list)
  • 沒有 matcher 機制的事件(UserPromptSubmit、PostToolBatch、Stop、TeammateIdle、TaskCreated、TaskCompleted、WorktreeCreate、WorktreeRemove、MessageDisplay、CwdChanged):always fire 或 always ignored if matcher set
MCP tool matcher 必須含 .*mcp__memory__.* 匹配所有 memory 工具;mcp__memory 當作精確字串、match 不到東西 [1]。

3.4 第二層篩選:if

if 欄位是 permission-rule 語法,提供更細的條件過濾([1]):
支援的規則格式如 "Bash(rm *)""Edit(*.ts)"不支援 && / || / 清單組合,要多條件時拆成多個 handler。
if 只在工具事件生效僅 PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest、PermissionDenied 會評估 if。其他事件若有 if 一律不跑 [1]。

4. 輸入:stdin 上的 JSON

所有 command hook 從 stdin 收到統一格式的 JSON([1]);http hook 收到 POST body。 基本 shape:
工具事件附加:
--agent 或 Subagent 內執行時,會附加 agent_idagent_type只有 SessionStart 會收到 model 欄位 [1]。
從 stdin 取參數Bash 範例(用 jq 從 stdin 解出欄位):

4.1 路徑佔位符

args exec 形式避免 quoting 問題([1]):
  • ${CLAUDE_PROJECT_DIR}:專案根
  • ${CLAUDE_PLUGIN_ROOT}:plugin 安裝目錄
  • ${CLAUDE_PLUGIN_DATA}:plugin 持久資料
  • ${user_config.*}:plugin 使用者設定

5. Exit code 與決策

command hook 透過 exit code 表態([1]):

5.1 exit 2 阻擋效果一覽

不是所有事件都把 exit 2 視為阻擋。真正會擋下的事件([1]):
  • PreToolUse → 阻擋工具呼叫
  • UserPromptSubmit → reject 並清除 prompt
  • UserPromptExpansion → 阻擋 expansion
  • Stop / SubagentStop / TeammateIdle → 防止停止
  • PreCompact → 阻擋 compact
  • PostToolBatch → 在下個 model call 前停止 agentic loop
  • WorktreeCreate → 任何非零 exit 都 fail creation
exit 2PostToolUse / PostToolUseFailure / Notification 等無阻擋效果,只顯示 stderr。多數 Unix 直覺是「exit 1 是錯誤」,但 Claude Code hook 系統把 exit 1 當非阻斷錯誤。要強制擋下請用 exit 2

5.2 為 Claude 注入額外 context

回傳 JSON 帶 hookSpecificOutput.additionalContext 給 Claude 看(需有對應的 hookEventName)([1]):
注入位置依事件:
  • SessionStart / Setup / SubagentStart → 對話開頭
  • UserPromptSubmit / UserPromptExpansion → 跟著 prompt
  • PreToolUse / PostToolUse* / PostToolBatch → 跟著工具結果
上限 10,000 字元;超長值寫到檔案、附 preview。多個 hook 的值都會注入。用陳述句,不要用指令句(避免觸發 prompt injection 防禦)[1]。

5.3 通用 JSON 輸出欄位

5.4 決策控制模式(不同事件用不同欄位)

6. 五個實戰範本

6.1 PreToolUse 攔截危險 Bash

對應 settings.json
為什麼用 permissionDecision: deny 而不是 exit 2PreToolUse 來說,回 JSON 帶 permissionDecisionexit 2 更乾淨:可以附帶 permissionDecisionReason 給 Claude 看、可以選擇 deny(明確拒絕)、allow(明確允許跳過 prompt)、ask(回到使用者確認)、defer(回到正常流程)。exit 2 只能用 stderr 表態,不能表達 allow [1]。

6.2 PostToolUse 自動格式化(本庫用法)

路徑從 stdin 取,不靠帶路徑的環境變數上例的腳本自行掃描待處理檔案。若要對「剛編輯的單檔」處理,從 stdin 的 hook 輸入 JSON 解出 .tool_input.file_path(見第 4 節的 jq 範例)。Claude Code 不提供 $CLAUDE_TOOL_INPUT_FILE_PATH 這類帶路徑的環境變數(截至 2026-06);可在 command 字串內展開的是 ${CLAUDE_PROJECT_DIR}${CLAUDE_PLUGIN_ROOT}${CLAUDE_PLUGIN_DATA} 等定址變數 [1]。
PostToolUse 必設 timeout 與增量沒設 timeout 時,PostToolUse 預設 600 秒上限 [1]。但快速連續編輯時,多個 hook 會疊加執行、消耗資源。本庫的 normalize_punctuation.py 對單檔處理是輕量,但重型 hook(tsc、ruff —fix)務必:
  • timeout 欄位(30-60 秒)防止 hang
  • --incremental 等增量旗標
  • Windows 上若依賴 GNU timeout,改用 PowerShell 包裝(Start-Process -TimeoutSec

6.3 Stop 跑收尾驗證(本庫用法)

validate_kb.py --hook 是 Stop hook 模式:有 CRITICAL/HIGH 才擋下、工具錯誤 fail-open 不誤傷正常流程。它在本庫作為確定性防護網,即使自然語言驅動的自主檢核漏跑,回合結束時仍會擋下

6.4 SessionStart 注入環境變數

SessionStart hook 可透過 $CLAUDE_ENV_FILE 環境變數(指向 session 專屬暫存檔)持久化環境變數 [1]:
這是 Claude Code 設定環境變數的官方支援方式(截至 2026-06 僅 SessionStart / Setup / CwdChanged / FileChanged 可用)。

6.5 工具呼叫 log(PostToolUse)

最小欄位符合 01-4 上下文工程 提到的審查需求:timestamp、session_id、tool、input。可事後回放、稽核、或喂給 04-4 Skill 做 pattern 分析。
背景執行"async": true 讓 hook 不阻塞主流程([1]):
"asyncRewake": trueexit 2 時透過 system reminder 喚醒 Claude。

7. Skill / Subagent frontmatter 內的 hook

不只是 settings.jsonSkill 與 Subagent 的 frontmatter 內可宣告 hook,只在該 Skill / Subagent 生命週期內生效([1]):
Subagent 的 Stop 自動轉 SubagentStopSubagent frontmatter 內寫的 Stop hook 會自動轉成 SubagentStop,不需要手動改 [1]。這讓「subagent 完成時跑驗證」是 frontmatter 內一句話的事。

8. 進階:HTTP / MCP / prompt handler

8.1 HTTP handler 範本

allowedEnvVars 是白名單:只暴露列出的環境變數給 hook。不要在 headers 直接寫值(會進 plugin 設定檔),用 $VAR 引用環境變數 [1]。

8.2 MCP tool handler 範本

適合跨 hook 共用 MCP 能力,例如每個檔案編輯後跑 MCP 上的 security scan。

8.3 prompt handler 範本

需要 LLM 判斷、但不想跑整個 agent 時用。timeout 預設 30 秒 [1]。

9. 安全:hook 是可執行碼,外部來源即供應鏈面

Hook 的 command 欄位直接由 shell 執行。安裝來路不明的 hook 等同於把第三方程式碼的執行權交出去。

9.1 已知威脅

  • CVE-2025-59536(CVSS 8.7):.claude/ 內的程式碼在使用者核准信任對話前已執行。已於 Claude Code v1.0.111+ 修復 [2]。未更新版本有被預置惡意指令的風險。
  • CVE-2026-21852:攻擊者控制的 app 改寫 ANTHROPIC_BASE_URL,把 API 流量導向他處並在 trust 前外洩 API key。修於 v2.0.65+ [2]。
  • Snyk ToxicSkills(2026-02):公開 skill 中 36% 含 prompt injection。Hook 與 skill 同屬供應鏈面,引入前先掃描。

9.2 引入外部 hook 前必跑的掃描

任何一項 hit 都要人工 review 上下文,不要直接裝。

9.3 最小權限原則

Hook 的 command 只授予完成任務所需的最小存取:
  • 不要在 hook 內讀 ~/.ssh~/.aws**/.env*。這些路徑都該在 permissions.deny 阻擋。
  • 不要讓 hook 寫到 repo 外,用 ${CLAUDE_PROJECT_DIR} 限定在專案內。
  • allowedEnvVars 白名單環境變數(HTTP handler),不要把整個 process env 暴露。

9.4 版本與 managed 政策

  • disableAllHooks: true 在 settings 可暫時關掉所有 hook(managed 級 hook 只能由 managed 級設定關掉)[1]。
  • 組織可用 allowManagedHooksOnly: true 擋下使用者 / 專案 / plugin 層 hook(plugin 透過 enabledPlugins 強制啟用者除外)。
  • 保持 Claude Code >= v2.0.65(CVE-2026-21852 修復版)+ >= v2.1.139(hook 改用 systemMessage / terminalSequence 而非直接寫 /dev/tty)[1, 2]。

10. 工具對照

各家工具對 hook 機制的支援差異極大(截至 2026-06):
命名澄清與邊界
  • Claude Code 的 hook 系統截至 2026-06 是 30 個事件的完整生命週期鉤子([1]);其他 CLI 工具的「hook」多指 command hook 一類,事件數遠少。
  • OpenAI Codex、Antigravity、GitHub Copilot CLI 的 hook 機制演進快,使用前以各家當前官方文件為準。
  • Cursor 截至 2026-06 未在 CLI / IDE 提供一等公民的 hook 機制;本 Playbook 短提一欄。
  • 本庫的 validate_kb --hook(Stop 模式)以本表主範本(Claude Code)的語意設計。

11. 自我檢核動手做

30 分鐘練習
  1. PostToolUse 自動格式化:照本庫的 PostToolUse 範本,在 .claude/settings.json 接上 normalize_punctuation.py,編輯任一 .md 觀察自動正規化。
  2. PreToolUse 攔截危險 Bash:寫 block-rm.sh 與對應設定;嘗試讓 Claude 跑 rm -rf /tmp/xxx 觀察 permissionDecision: deny 與給 Claude 的 reason。
  3. Stop 收尾驗證:接上 validate_kb.py --hook,故意製造一個 frontmatter 缺 updated 欄的單元,觀察 Stop hook 擋下並回報。
  4. 供應鏈掃描:在 ~/.claude/.claude/ 跑本節的 rg 掃描,確認沒有可疑外連、沒有隱藏 Unicode。
  5. SessionStart 注入 env:寫一個 SessionStart hook 透過 CLAUDE_ENV_FILEMY_FLAG=1,在新 session 跑 echo $MY_FLAG 驗證生效。

12. 常見誤區

反模式清單
  • PostToolUse hook 未設 timeout:每次存檔觸發一個長任務,多次快速存檔後任務堆積、消耗資源。每個 hook 都該有 timeout 與合理上限。
  • 把昂貴的全量型別檢查綁在每次 Write/Edit,沒加 --incremental 旗標:拖慢迭代節奏。tsc / mypy / ruff 都有增量模式,務必用。
  • 從公開 repo 複製 hook 配置直接貼上使用,未先掃描 command 內容:引入供應鏈風險。貼之前先 rg 掃、讀每個 command 的 shell code。
  • exit 1 期待阻擋:Unix 直覺是 exit 1 是錯誤、該擋下。Claude Code hook 系統不是:exit 1 是非阻斷錯誤,只有 exit 2 對特定事件才阻擋。要擋下用 exit 2 或回 JSON permissionDecision: deny
  • PreToolUse 用 exit 2 攔截但沒給 Claude 原因exit 2 把 stderr 餵回 Claude,但內容只是純文字。對 PreToolUse 來說,回 JSON 帶 permissionDecision: deny + permissionDecisionReason 更結構化,模型也更容易理解與遵守 [1]。
  • 把「希望模型做」寫成 hook:hook 跑在 shell 層、不在模型推理鏈內。希望模型「記得」的事寫在 CLAUDE.md 或 rules;希望「系統保證執行」的事才走 hook。混淆這層會讓 hook 跑一堆 context 維護邏輯、浪費 shell 資源。
  • matcher 寫 regex 卻用 \| 混搭\| 只在精確清單語法生效。混搭其他字元(如 Bash|Edit\.ts)會被當 regex 解析、可能不是你要的行為 [1]。

自我檢核

通過本單元的標準
  1. 你能說出 hook 與 CLAUDE.md / rules 的根本差別嗎?(shell 確定性 vs. context 軟約束)
  2. 你能在 5 分鐘內寫出一個 PreToolUse 攔截 git commit.env 的 hook 嗎?
  3. 你能說出 exit 0 / exit 2 / 其他 exit code 在不同事件的語義嗎?
  4. 你能說出 hook 作為可執行程式碼的三條供應鏈防護 SOP 嗎?
  5. 在你目前的工作流中,有哪一件「每次應該做但不一定記得做」的事,可以改用 PostToolUse 或 Stop hook 來保證?說出觸發事件、要跑的指令、以及選 PostToolUse 還是 Stop 的理由。

來源與延伸閱讀

事實主張依官方文件,快變動項標註截至 2026-05。
  • [1] Anthropic, “Hooks,” code.claude.com, 2026. [Online]. Available: https://code.claude.com/docs/en/hooks (截至 2026-06;含 30 個事件、五種 handler 類型、matcher 三模式解析、exit 2 阻擋語義、additionalContext 注入、v2.1.139+ 改用 terminalSequence
  • [2] Anthropic, “Claude Code release notes,” code.claude.com, 2026. [Online]. Available: https://code.claude.com/docs/en/changelog (截至 2026-06;CVE-2025-59536 與 CVE-2026-21852 修復版本)
  • [3] Anthropic, “Extend Claude with skills,” code.claude.com, 2026. [Online]. Available: https://code.claude.com/docs/en/skills (截至 2026-06;Skill frontmatter 內 hooks 設定)
  • [4] Anthropic, “Create custom subagents,” code.claude.com, 2026. [Online]. Available: https://code.claude.com/docs/en/subagents (截至 2026-06;Subagent frontmatter 內 hooksStop→SubagentStop 自動轉換)