Claude Code Hooks 完全実装ガイド|29イベントで品質ゲート・安全運用を仕組み化【2026年5月版】
Claude Code を「使う」と「安全に運用する」の間には深い溝があります。その溝を埋めるのが Hooks(フック) です。2026年5月時点で 全29種類のイベントが定義されており、PreToolUse でツール実行前に品質ゲートを走らせたり、Bash の rm -rf を強制ブロックしたり、SessionStart で本日の予定を自動表示したり、Stop で完了通知を Slack へ流したりと、AI の外側に仕組み化された守りを構築できます。本記事は自社で365日 quality-gate.sh を PostToolUse に組み込んで運用している筆者が、29イベントの全体像・6階層の設定ファイル・実装5パターン・exit code 2 の罠まで、2026年5月最新仕様で完全整理します。Subagents(5/15公開)と Components(5/16公開)と並んで、Claude Code 完全運用の3部作の最終章です。
目次
なぜ Hooks が「安全運用」を分けるのか
Claude Code は強力ですが、デフォルトのままで本番運用するとリスクが残ります。rm -rf を AI が判断ミスで実行する可能性、品質チェックを通さずに記事を公開する可能性、長時間セッションでコンテキストが汚れたまま重要操作に進む可能性などです。これらを人間の注意力だけで防ぐのは現実的ではありません。疲れている深夜にこそ事故は起きます。
そこで登場するのが Hooks です。Anthropic 公式 Hooks ドキュメントでは「Claude Code のライフサイクル各タイミングに、ユーザーが書いた処理を差し込める仕組み」と定義されています。これは 家のセキュリティシステムのようなもので、玄関のドアを開けた瞬間(SessionStart)、誰かが鍵を差し込もうとした瞬間(PreToolUse)、ドアを閉めた瞬間(Stop)に、それぞれ自動で動く警報やログ取得を仕込んでおけば、人間が一晩中見張る必要はありません。
Subagents や Skills、Plugin(5/15・5/16 公開)はすべて AI の応答ロジック側に作用する仕組みでした。一方 Hooks は AI の応答とは独立に動く監視・制御レイヤーです。たとえるなら 運転手の隣に座るドライブレコーダーのようなもので、運転手の判断には口を出さず、危険なシーンを検知したら自動で警告音を鳴らす役割です。AI を信用しないわけではなく、信用とは別レイヤーで守りを置くのが Hooks の哲学です。2025年に Anthropic が Hooks 機構を導入して以降、2026年5月時点でイベントは 29種類に達しています。
29イベント早見表(4レイヤー分類)
Hooks は実行タイミングで 4つのレイヤーに分類できます。セッション単位・ターン単位・ツール単位・非同期です。家の防犯システムに例えるなら、セッション層は「家全体の警報セット/解除」、ターン層は「来客対応」、ツール層は「個別の鍵の開閉」、非同期層は「外周カメラの常時監視」のような関係です。
29イベントすべてを使う必要はありません。実運用で最初に触るべき 主要9イベントは、SessionStart・UserPromptSubmit・PreToolUse・PostToolUse・PreCompact・SessionEnd・Notification・Stop・SubagentStop です。これだけ押さえれば、品質ゲート連携・通知・ルール注入の3大用途は完成します。残り20イベントは特定の業界要件(金融のコンプライアンス、大企業の権限制御、MCP連携の細かい監査など)で必要になったときに追加すれば十分です。
設定ファイル6階層の使い分け
Hooks は 6種類の設定ファイルに書けます。最初に「どこに書くか」を間違えると、チーム配布や個人カスタムが噛み合わなくなります。たとえるなら 家・職場・カフェのWiFi設定のようなもので、どこに書いたかで効く範囲が決まります。
初心者が混乱するのは、これらが 優先順位ではなく重ね合わせで動く点です。PreToolUse で Bash にマッチする Hooks を ②プロジェクト共有 と ③個人専用 の両方に書いた場合、両方が並列または逐次で実行されます。片方が exit 2 を返してブロックしたら、もう片方の結果に関係なくツール実行は止まります。「上書き」ではなく「全部足し合わせ」と覚えておきましょう。これは 家とオフィスのWiFi両方をPCにつなぐような関係のもので、両方アクティブだが優先するのは状況次第というイメージです。
実運用での使い分けの定石は次のとおりです。①個人共通の Hooks(rm -rf 全プロジェクト遮断など)は ~/.claude/settings.json、②チームに配布したい品質ゲートは .claude/settings.json、③自分だけの実験は .claude/settings.local.json、④組織のコンプライアンス要件は Managed policy、⑤汎用処理は Plugin として配布、⑥特定タスクに紐づく検証は Skill/Agent のフロントマターに同梱。Plugin 同梱 Hooks の構造は、5/16 公開の Claude Code Plugin・Skills・MCP・Subagents 4要素ガイドを参照してください。
最重要9イベント完全解説
ここからは実運用で必ず触る 主要9イベントを整理します。残り20イベントは特殊要件向けで、この9つはすべての Claude Code ユーザーの必修科目です。
4-1. SessionStart|開始時にコンテキスト動的注入
セッション開始または再開時に1回発火します。入力JSONの source で startup・resume・clear・compact を判別。最大の使い道は hookSpecificOutput.additionalContext で動的なテキストを注入することです。本日の予定・前回のhandoff・未完了タスクを毎セッションの先頭に自動表示できます。「あれ今日何するんだっけ」のロスがゼロになります。
4-2. UserPromptSubmit|送信前のルール注入
ユーザーがプロンプトを送信した直後、Claudeに渡される直前に発火します。入力JSONには prompt と permission_mode が含まれます。decision: block で送信自体を止めることもできますが、一般的な用途は追加コンテキスト注入です。たとえば「リプ」「返信」が含まれていたらリプ作成3原則を強制注入する Hooks があれば、毎回 CLAUDE.md を読み込ませなくても確実にルールが効きます。timeout は30秒固定なので外部APIは避けましょう。
4-3. PreToolUse|ツール実行前の許可制御(最頻出)
Claude がツールを呼ぼうとする直前に発火する Hooks の主役です。入力JSONには tool_name・tool_input・permission_mode・effort.level が含まれ、出力の hookSpecificOutput.permissionDecision で allow / deny / ask / defer の4択を返します。
| permissionDecision | 挙動 |
|---|---|
| allow | 確認なしで即実行 |
| deny | 実行をブロック・理由をユーザーに表示 |
| ask | ユーザーに確認ダイアログを出す |
| defer | 通常パーミッション判定に委ねる |
典型例は 危険なコマンドの強制ブロックです。rm -rf や git push --force を含む場合は問答無用で deny を返す Hooks を入れれば、AI が一瞬気を抜いた瞬間の事故を防げます。判定の流れは次の通りです。
シートベルトのようなもので、毎回意識しなくても自動で守ってくれる安心感が運用にとって大きいです。
4-4. PostToolUse|ツール成功後の検証(品質ゲートの主戦場)
ツール実行が成功した直後に発火します。失敗時は別イベント PostToolUseFailure が発火します。筆者の運用で最も価値を発揮しているのが、ここでの quality-gate.sh 連携です。記事ファイルを Edit/Write した直後に PostToolUse が発火し、tasks/articles/ 配下が変更されていれば quality-gate.sh を自動実行。文字数・SVG・JSON-LDの構造を即座に検証し、FAIL なら decision: block を返してClaudeに「ここで止まれ」と通知します。
4-5. PreCompact|コンテキスト圧縮前のセーフネット
コンテキストが自動コンパクションされる直前に発火します。matcher で manual(ユーザー手動)か auto(自動)かを判別できます。compact前にセッションログを退避させるのが主用途で、session-log.md にその日の学びを1〜3行で追記します。compact で文脈が薄くなる前に、後から自分が読み返せる形で残しておく仕組みです。
4-6. SessionEnd|セッション終了時の最終処理
セッション終了時に発火します。matcher は clear・logout・other の3種類です。典型用途はhandoff.mdへの自動書き出し、外部ダッシュボードへの作業ログ送信、ローカルバックアップなど。SessionEnd で書き残しを忘れない仕組みにしておくと、次セッション開始の SessionStart 側で読み込めて連続運用が成立します。
4-7. Notification|各種通知のフック
Claude Code が通知を出すときに発火します。type で permission_prompt・idle_prompt・auth_success・elicitation_dialog などを判別。Slack や macOS の通知センターに転送すれば、長時間タスクをバックグラウンド実行している時の気づき遅れを防げます。
4-8. Stop|ターン完了時のフック
Claude が応答を完了したときに発火します。入力JSONには effort.level(low/medium/high/xhigh/max)が含まれ、その応答にどれだけの計算量が使われたかが分かります。完了通知をSlack に流す、DB記録する、長時間処理後にBGMを停止するなど、ターン単位の後処理に最適です。
4-9. SubagentStop|サブエージェント完了時のフック
Subagent が処理を完了したときに発火します。ファクトチェックや自己評価のSubagentが終わった直後に結果ファイル(tasks/factchecks/factcheck-*.md)の構造を検証できます。Subagentの位置づけは Claude Code Subagents 実践ガイドを併読すると深まります。
Matcher 構文と if フィールド
Hooks は同じイベントでも matcher 構文で発火対象を絞り込みます。これを正しく書けないと「全Bashコマンドで毎回走る」など過剰実行でパフォーマンスを落とします。matcher は値の文字種類で 3種類のマッチング方式に自動切り替わります。食材の選別でいうところの「全部 vs 種類別 vs 細かい条件付き」のようなものです。
もうひとつ重要なのが if フィールドです。Claude Code のパーミッション規則構文で、ツール内の引数まで深掘りしてマッチを絞り込めます。"if": "Bash(git *)" なら git サブコマンドだけ、"if": "Edit(*.ts)" なら TypeScript 編集だけ、"if": "Bash(rm -rf *)" なら破壊的削除コマンドだけ発火させられます。matcher(ツール名)と if(引数内容)の2段絞り込みで過剰実行を最小化しましょう。
出力JSON仕様の3階層
Hooks が返すJSONには 3階層のフィールドがあります。トップレベル汎用・イベント別 decision・hookSpecificOutput です。玄関・各部屋・引き出しの3段階の鍵のようなものと覚えれば迷いません。
| 階層 | 代表フィールド | 用途 |
|---|---|---|
| トップレベル | continue / stopReason / suppressOutput / systemMessage | セッション全体への影響 |
| イベント別decision | decision: block / reason | その動作をブロック |
| hookSpecificOutput | permissionDecision / additionalContext | イベント固有の細かい制御 |
たとえば PreToolUse で実行を拒否する場合:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "rm -rf は全プロジェクトでブロックされています"
}
}
SessionStartで動的コンテキストを注入する場合:
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "本日のhandoff: ...\n未完了タスク: ..."
}
}
自社運用5実装パターン|コード公開
ここからは実運用コードです。oishi-ai プロジェクトで 365日稼働させている5つのHooks実装を公開します。コピーするだけで Claude Codeの安全運用レベルが一段上がります。
パターン1:危険なBashコマンドの全プロジェクト共通ブロック
~/.claude/settings.json に書く全プロジェクト共通の防衛線です。シートベルトを全車両に標準装備するような位置づけです。
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "${HOME}/.claude/hooks/block-dangerous.sh",
"timeout": 5
}]
}]
}
}
#!/bin/bash
# ~/.claude/hooks/block-dangerous.sh
CMD=$(jq -r '.tool_input.command' < /dev/stdin)
PATTERNS='rm -rf|git push --force|git reset --hard|sudo |dd if=|mkfs\\.'
if echo "$CMD" | grep -E "$PATTERNS" > /dev/null; then
jq -n --arg cmd "$CMD" '{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": ("禁止コマンドを検出: " + $cmd)
}
}'
fi
exit 0
パターン2:記事編集後の品質ゲート自動実行
記事HTMLを Edit/Write した直後に quality-gate.sh を走らせ、FAIL なら Claude に修正を促します。.claude/settings.json に書きプロジェクト全員で共有します。
{
"hooks": {
"PostToolUse": [{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "bash ${CLAUDE_PROJECT_DIR}/.claude/hooks/quality-gate-post.sh",
"timeout": 120
}]
}]
}
}
#!/bin/bash
# .claude/hooks/quality-gate-post.sh
FILE=$(jq -r '.tool_input.file_path' < /dev/stdin)
[[ ! "$FILE" =~ tasks/articles/.*\\.html$ ]] && exit 0
RESULT=$(bash "${CLAUDE_PROJECT_DIR}/scripts/quality-gate.sh" article "$FILE" 2>&1)
if echo "$RESULT" | grep -q 'FAIL: [1-9]'; then
jq -n --arg r "$RESULT" '{
"decision": "block",
"reason": ("品質ゲート不合格:\\n" + $r)
}'
fi
exit 0
パターン3:SessionStartで本日の文脈を動的注入
セッション開始時に handoff.md・週次スケジュール・未完了タスクを連結して additionalContext に返します。朝の打ち合わせ前にホワイトボードを見るようなものです。
{
"hooks": {
"SessionStart": [{
"matcher": "startup|resume",
"hooks": [{
"type": "command",
"command": "bash ${CLAUDE_PROJECT_DIR}/.claude/hooks/session-context.sh",
"timeout": 30
}]
}]
}
}
#!/bin/bash
# .claude/hooks/session-context.sh
CONTEXT=""
[ -f tasks/handoff.md ] && CONTEXT+="## 前回のhandoff\\n$(tail -50 tasks/handoff.md)\\n\\n"
[ -f tasks/weekly-schedule.md ] && CONTEXT+="## 本日の予定\\n$(grep "$(date '+%Y-%m-%d')" tasks/weekly-schedule.md)\\n"
jq -n --arg ctx "$CONTEXT" '{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": $ctx
}
}'
パターン4:UserPromptSubmitでルール強制注入
「リプ」「返信」が含まれていたらリプ作成3原則を強制注入します。CLAUDE.md だけだと毎回意識されないルールも、Hooks で強制すれば100%効きます。
#!/bin/bash
# .claude/hooks/reply-rules-injection.sh
PROMPT=$(jq -r '.prompt' < /dev/stdin)
if echo "$PROMPT" | grep -E 'リプ|返信|絡みたい' > /dev/null; then
jq -n '{
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "🔍 リプ大前提:①ファクトチェック必須 ②やんわり版のみ ③160-200字"
}
}'
fi
exit 0
パターン5:Stopで完了通知を macOS 通知センターに
長時間タスク完了の瞬間に通知を出します。Slack に流す場合は curl で Webhook を叩くだけで応用可能です。
{
"hooks": {
"Stop": [{
"hooks": [{
"type": "command",
"command": "osascript -e 'display notification \\"完了\\" with title \\"oishi-ai\\"'",
"timeout": 5,
"async": true
}]
}]
}
}
async: true をつけることで、通知処理が Claude の次ターン進行をブロックしません。軽い処理は async、重要な検証処理は同期と覚えておきましょう。
セキュリティ落とし穴8つ
Hooks は強力ですが、書き方を間違えると 動いてるつもりで動いてない状態に陥ります。実装でハマりやすい落とし穴を整理します。
| No | 落とし穴 | 対処 |
|---|---|---|
| 1 | exit 1 はブロックされない | exit 2 か JSON で deny を返す |
| 2 | stdout が JSON 以外を含むと全壊 | デバッグ出力は stderr へ流す |
| 3 | HTTP は非2xxでブロック不可 | 2xx + decision block で返す |
| 4 | PermissionDenied で exit 2 無効 | retry: true のみ有効 |
| 5 | 環境変数補間が黙って空文字に | allowedEnvVars を必ず指定 |
| 6 | シェル展開でパス破損 | command と args を分離指定 |
| 7 | MCP は接続済みサーバー限定 | SessionStart で MCP 使わない |
| 8 | async のエラーは黙って消える | 検証系は同期、通知系のみ async |
最大の罠は exit 1 ではブロックされないこと。Unix の慣例だと「失敗=非ゼロ」ですが、Claude Code は exit 2 のみがブロック扱いです。初心者の9割がここで「ブロックしてるはずなのに実行される」とハマります。赤信号と黄信号で意味がまったく違う交通ルールのようなものと覚えておきましょう。
timeout / async / Managed Policy 運用判断
プロダクション運用で最後に整理しておきたいのが timeout・async・Managed Policy の3つの運用判断です。
| Hook 型 | デフォルト | UserPromptSubmit |
|---|---|---|
| command | 600秒 | 30秒 |
| http | 600秒 | 30秒 |
| mcp_tool | 600秒 | 30秒 |
| prompt | 30秒 | 30秒 |
| agent | 60秒 | 60秒 |
実運用では PreToolUse は5〜10秒、PostToolUse は60〜120秒、SessionStart は30〜60秒に設定。PreToolUse は毎ツール呼び出し前に走るため軽量バリデーション専用、PostToolUse は quality-gate.sh のような重い検証を許容、SessionStart は外部ファイル読み込みが主なので30秒で足りる、という棲み分けです。
async は通知系のみ、検証系は必ず同期。夜間警備員と監視カメラの違いのようなもので、警備員(同期)は判断して止める役割、カメラ(async)は記録するだけで止める権限はないという棲み分けです。
Managed Policy の allowManagedHooksOnly: true は組織統制の決定打です。これを true にすると ユーザー個人・プロジェクト・プラグインのすべての Hooks がブロックされ、管理者が指定した Hooks のみが有効になります。企業のAIガバナンス完全ガイドと組み合わせて運用する場面が増えています。
まとめ|3部作で組む安全運用
Claude Code を 「個人ツール」から「365日運用できるシステム」に押し上げるのが、Subagents(5/15公開)・Components(5/16公開)・Hooks(本記事)の3部作です。
| 要素 | 役割 | 主な使い道 |
|---|---|---|
| Subagents | 独立コンテキストで動く専用AIエージェント | ファクトチェック・自己評価 |
| Components | Plugin・Skills・MCP・Subagents 4要素配布 | 能力配布・チーム共有 |
| Hooks(本記事) | AIの外側で動くイベント駆動スクリプト | 品質ゲート・通知・安全制御 |
Subagents で 誰がやるかを分け、Components で 何を持たせるかを配布し、Hooks で どこで止めるかを仕組み化する。この3層が揃って初めて、Claude Code は深夜でも安全に動き続ける本物の運用基盤になります。本記事の5パターンはすべて自社365日稼働中。コピーして ~/.claude/ に置けば運用レベルが一段上がります。今のうちに Hooks を仕組み化できるかが、半年後の運用品質を決めます。
FAQ|よくある質問7問
Q1. Hooks の設定変更はリロード必要?
セッション中の設定変更は ConfigChange イベントが発火し、原則として次のターンから新しい設定が適用されます。即時反映を確実にしたい場合はセッションを再起動するのが安全です。
Q2. Hooks で Claude のプロンプト内容を改変できる?
UserPromptSubmit で additionalContext を返すことでコンテキストの追加は可能ですが、ユーザーが送信したプロンプト本体の改変はできません。改変ではなく 注入で対応するのが基本設計です。
Q3. async: true と asyncRewake: true の違いは?
async: true はバックグラウンド実行で完了通知なし。asyncRewake: true は完了時にClaudeを再起動して結果を渡せます。長時間検証用途に使います。
Q4. Hooks のテストはどうやる?
想定する入力JSONをファイルにして cat input.json | bash ~/.claude/hooks/foo.sh でstdoutを確認します。実セッションに組み込む前にJSON形式・終了コード・パフォーマンスを検証するのが定石です。
Q5. WorktreeCreate Hooks は git 外でも使える?
使えます。git リポジトリでない作業ディレクトリでも、WorktreeCreate/WorktreeRemove Hooks を設定すれば Claude Code に VCS非依存の分離環境を作る挙動を委譲できます。
Q6. Plugin 同梱と自前 Hooks が同じイベントで競合したら?
両方が並列または逐次で実行されます。どちらか1つでも exit 2 や decision: block を返せば最終的にブロック動作になります。「上書き」ではなく「足し合わせ」です。Managed Policy で allowManagedHooksOnly: true が立っている場合のみ例外です。
Q7. Hooks のログはどこに出る?
Claude Code 本体のデバッグログに記録されます。suppressOutput: true で stdout はログ除外できますが stderr は常時記録です。機密情報の出力には注意しましょう。