01 / Definition
Hook = 決定論的な拡張ポイント
モデルの裁量に委ねず、毎回必ず発火する。matcher で発火対象を絞り、handler で「何をするか」を書く。
Skill が「Claude が読んで判断して実行する手順書」であるのに対し、Hook は「決まった瞬間に決まったコードを叩く」もの。 ガードレール(block 系)と自動化(format / notify 系)の両方に使えます。
02 / Lifecycle
どのイベントが、いつ走るか
セッションを横軸にした 6 レーン × 29 イベントのタイムライン。クリックすると詳細が出ます。
hook lifecycle · 29 events
セッションのどこで 何が発火する か
click events to inspect →
PostToolUse
ツール成功後。フォーマッタ・テスト走らせに
approximate position
38% / 100% — セッションの 38% 地点
example
{
"matcher": "Edit|Write",
"command": "biome format --write ${FILE}"
}lang: json
03 / Handlers
ハンドラは 4 種類
シェルコマンド・HTTP・LLM プロンプト・Subagent。組み合わせて使えます。
commandシェルコマンド。最も使われる。stdout / 終了コードで Claude に応答
"command": "biome format --write ${FILE}"http外部 webhook を叩く。Slack 通知や社内ロガーに
"url": "https://hooks.example/notify"
promptLLM プロンプトを評価させる。多基準の Stop 判定など
"prompt": "テストが追加されているか確認して"
agentSubagent を起動。重い検証を別文脈で
"agent": "TestRunner"
04 / I/O
入出力スキーマ
stdin で JSON が渡り、stdout / 終了コードで応答。block / inject / replace を制御できます。
# 入力(stdin に JSON)
{
"event": "PreToolUse",
"tool": "Edit",
"input": { "file_path": "src/api/users.ts", ... },
"session_id": "...", "cwd": "..."
}
# 出力(exit code)
exit 0 → そのまま許可
exit 2 → ブロック(理由は stderr に書く)
他 → エラー扱い
# 出力(JSON を stdout に出すと、より細かく制御できる)
{ "decision": "block", "reason": "ブランチが main の時は禁止" }
{ "decision": "approve" }
{ "addContext": "ヒント: foo は deprecated です" } # PreToolUse 等05 / Recipes
6 つの実戦レシピ
そのまま .claude/settings.json に貼って使える形です。
編集後に biome で自動フォーマット
PostToolUse{
"hooks": {
"PostToolUse": [
{ "matcher": "Edit|Write",
"command": "biome format --write ${FILE}" }
]
}
}本番設定への書込を確実にブロック
PreToolUse{
"hooks": {
"PreToolUse": [
{ "matcher": "Edit|Write",
"if": "file matches infra/prod/**",
"command": "exit 1" }
]
}
}Stop で Mac デスクトップ通知
Stop{
"hooks": {
"Stop": [
{ "command": "osascript -e 'display notification \"Claude finished\" with title \"Claude\"'" }
]
}
}編集後にテストを自動実行(async)
PostToolUse (async){
"hooks": {
"PostToolUse": [
{ "matcher": "Edit",
"command": "pnpm test --silent",
"async": true,
"timeout": 120000 }
]
}
}コンパクション直前に観点を渡す
PreCompact{
"hooks": {
"PreCompact": [
{ "type": "prompt",
"prompt": "現在のバグ修正の核心と、まだ未確認の検証手順は必ず残して" }
]
}
}SessionStart で env 注入
SessionStart{
"hooks": {
"SessionStart": [
{ "type": "json",
"command": "scripts/load-env.sh",
"output": "envVars" }
]
}
}06 / Async
長時間 hook は async に
同期 hook は 30 秒程度の上限あり。テスト実行のような長い処理は async + timeout で。
07 / Debug
動かない時のチェックリスト
hook が動かない時は、たいてい「matcher が合っていない」「JSON が壊れている」「上位で上書き」のどれか。
- 1/hooks で登録状況を確認
- 2claude settings show --layers で precedence を確認
- 3matcher を ".*" に緩めて発火するか試す
- 4command を `bash -lc 'echo "$JSON" >> /tmp/hook.log'` に差し替えて入力を可視化
- 5exit code 2 を返してみて、Claude 側でブロック扱いになるか確認