Claude Code Hooks:95個のフックそれぞれが存在する理由
Claude Code 用に95個のフックを構築しました。すべて、最初に何かがうまくいかなかったから存在しています。git-safety-guardian が存在するのは、Claude が main に強制プッシュしたからです。recursion-guard が存在するのは、サブエージェントが無限に子エージェントを生成したからです。blog-quality-gate が存在するのは、受動態の文が7つと参照先のない脚注を含む記事を公開してしまったからです。1
Claude Code フックとは、特定のライフサイクルポイント(セッション開始、ツール使用の前後、プロンプト送信、レスポンス完了)で実行されるシェルコマンドであり、確率的な LLM の振る舞いの上に決定論的なガードレールを提供します。 フックは stdin で JSON を受け取り、stdout を通じて判断(ブロック、許可、または変更)を返します。main への強制プッシュのブロック、再帰的なエージェント生成の防止、セッションごとのコンテキスト注入、LLM 単体では保証できない出力品質のゲーティングといったポリシーを実施するのです。
TL;DR
Claude Code フックは、AI支援開発のセッション中、特定のライフサイクルポイントでシェルコマンドを実行します。確率的なシステム(LLM)の上に、決定論的な保証(危険な git コマンドのブロック、コンテキストの注入、品質の強制)を提供するものです。自分のインフラ全体で95個のフックを構築してきた経験から、最良のフックは計画からではなくインシデントから生まれると気づきました。この記事では、アーキテクチャ、最も重要なフックの誕生の物語、そして9ヶ月間のフック開発から学んだパターンを取り上げます。
アーキテクチャ
Claude Code は、フックが振る舞いをインターセプト、変更、またはブロックできる26個のライフサイクルイベントを公開しています(v2.1.116、2026年4月時点)。2 私のフックは、その中でも最も一般的な6つを対象としています。
セッションイベント
| イベント | 発火タイミング | 自分のフック |
|---|---|---|
| SessionStart | 新しいセッションの開始時 | session-start.sh:日付の注入、venv の検証、再帰状態の初期化 |
| SessionEnd | セッション終了時 | 一時ファイルのクリーンアップ |
ツール実行イベント
| イベント | 発火タイミング | 自分のフック |
|---|---|---|
| PreToolUse | 任意のツールが実行される前 | git-safety-guardian.sh、recursion-guard.sh、credentials-check.sh |
| PostToolUse | ツール完了後 | post-deliberation.sh、log-bash.sh |
レスポンスイベント
| イベント | 発火タイミング | 自分のフック |
|---|---|---|
| UserPromptSubmit | ユーザーがプロンプトを送信したとき | コンテキスト注入(日付、ブランチ、モデル情報) |
| Stop | Claude が応答を完了したとき | deliberation-pride-check.sh、reviewer-stop-gate.sh |
すべてのフックは stdin で JSON を受け取り、stdout を通じて通信します。
{"decision": "block", "reason": "Force push to main is prohibited"}
または、終了コード 0 で静かに許可します。3
誕生の物語:最も重要なフックたち
フック 1:git-safety-guardian.sh(PreToolUse:Bash)
インシデント: 初期の Claude Code セッション中、エージェントに「git 履歴を整理して」と頼みました。エージェントは git push --force origin main を実行。この強制プッシュにより、共有ブランチ上の3日分のコミットが上書きされてしまったのです。ローカルバックアップから復旧できたものの、4時間に及ぶ復旧作業を経て、確率的な判断に破壊的な git 操作を任せてはならないと確信しました。
フック:
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
# Only check git commands
echo "$COMMAND" | grep -qE '\bgit\b' || exit 0
# Block force push to main/master
if echo "$COMMAND" | grep -qiE 'git\s+push\s+.*--force.*\b(main|master)\b'; then
cat << EOF
{"decision": "block", "reason": "Force push to main/master blocked by safety hook"}
EOF
fi
教訓: このフックは意図を理解しようとしません。コマンド文字列に対してパターンマッチするだけです。シンプルで、決定論的、巧妙なプロンプトでもバイパスは不可能。エージェントは feature ブランチへの強制プッシュは依然として可能ですが(時には正当な操作)、main/master は恒久的に保護されます。4
累計で防いだ件数: 9ヶ月で8件の強制プッシュ試行をインターセプト。
フック 2:recursion-guard.sh(PreToolUse:Task)
インシデント: 熟議システムを構築中、3つの探索サブエージェントを生成するセッションを実行しました。各サブエージェントは生成制限を持たず、それぞれがさらにサブエージェントを生成。この再帰により、API のトークンが通常の10倍の速度で消費されたのです。加速するトークン消費に気づいて手動でセッションを終了しました。
フック:
#!/bin/bash
CONFIG_FILE="${HOME}/.claude/configs/recursion-limits.json"
STATE_FILE="${HOME}/.claude/state/recursion-depth.json"
MAX_DEPTH=2
MAX_CHILDREN=5
DELIB_SPAWN_BUDGET=2
DELIB_MAX_AGENTS=12
# Validate integers safely (((VAR++)) crashes with set -e when VAR=0)
is_positive_int() {
[[ "$1" =~ ^[0-9]+$ ]] && [[ "$1" -gt 0 ]]
}
鍵となる設計判断: エージェントは深度を増やすのではなく、親から生成予算を継承します。予算12のルートエージェントは、その予算を任意のツリー形状に分配できるのです。深度ベースの制限は硬すぎます(完全に安全な深くて狭いチェーンを防いでしまう)。5
累計ブロック数: 23件の暴走生成試行。
フック 3:blog-quality-gate.sh(Stop)
インシデント: 受動態の文が7つ、本文中で参照されているのに参考文献セクションから欠落している脚注、冒頭の一文が「was implemented by the team」というブログ記事を公開してしまいました。エディタ上では洗練されて見えても、人間のレビュアーなら誰でも捕らえる基本的な品質チェックをすり抜けてしまったのです。
フック: 変更されたブログコンテンツファイルに対して、12モジュールのブログリンターを実行します。受動態、参照切れの脚注、メタ記述の欠落、タグのないコードブロック、引用の整合性をチェック。それぞれの指摘は具体的です。「Line 47: passive voice detected in ‘was implemented by the team.’ Suggestion: ‘the team implemented.’」
人間からのフィードバックとの類似点: このフックは作業物を批評するのであって、作業者を批評するわけではありません。「47行目に受動態があります」と言うのであって、「あなたの文章は下手です」とは言わない。人間のフィードバックを建設的なものにするのと同じ原則が、自動化されたフィードバックを有用なものにするのです。
95個のフックを支えるパターン
設定駆動のアーキテクチャ
私のフックは、ハードコードされた値から設定駆動の振る舞いへと進化してきました。
~/.claude/configs/
├── recursion-limits.json # Depth, spawn budgets, timeouts
├── deliberation-config.json # Consensus thresholds per task type
├── consensus-profiles.json # security=85%, docs=50%
├── circuit-breaker.json # Failure mode configurations
└── file-scope-rules.json # Path-scoped hook application
しきい値を JSON 設定に移したことで、bash スクリプトを編集せずに振る舞いを調整できるようになりました。セキュリティ関連の合意を85%、ドキュメントを50%にする必要が出たとき、設定変更は30秒で済みました。コード変更なら、編集、テスト、再デプロイが必要だったでしょう。6
ライフサイクル階層化パターン
95個のフックは、4つのレイヤーからなるセーフティネットを形成しています。
レイヤー 1:予防(PreToolUse): 悪い事が起こる前に止める。git-safety-guardian、credentials-check、recursion-guard。
レイヤー 2:コンテキスト(UserPromptSubmit、SessionStart): エージェントが必要とする情報を注入する。日付、ブランチ、プロジェクトコンテキスト、メモリエントリ、哲学原則。
レイヤー 3:検証(PostToolUse): 完了したアクションが基準を満たしているか検証する。post-deliberation の合意チェック、出力のログ記録。
レイヤー 4:品質(Stop): 最終出力をゲーティングする。プライドチェック、品質ゲート、レビュアーストップゲート。このレイヤーは、エージェントが出力だけでなく自らの推論品質を評価するメタ認知的モニタリングを実装しているのです。
各レイヤーは独立しています。PreToolUse のフックが静かに失敗しても、Stop フックが品質問題を捕らえます。多層防御を、AIエージェントの振る舞いに適用したかたちです。
設定
フックは .claude/settings.json に定義します。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "~/.claude/hooks/git-safety-guardian.sh",
"timeout": 5000
}
],
"PreToolUse:Task": [
{
"command": "~/.claude/hooks/recursion-guard.sh"
}
]
}
}
matcher フィールドは、どのツールがフックをトリガーするかをフィルタします。PreToolUse:Task は、Task ツールの呼び出しのみで発火するマッチャーの略記です。非同期フック(async: true)はブロックせずにバックグラウンドで実行されます。7
スコープ階層
- ユーザーレベル(
~/.claude/settings.json):すべてのプロジェクトに適用。私の95個のフックはここにあります。 - プロジェクトレベル(
.claude/settings.json):プロジェクト固有のフックを追加。 - スキル/サブエージェントの frontmatter: 特定のコンポーネントのライフサイクルにスコープ。8
今ならどう変えるか
25個ではなく3個のフックから始める。 最初の1ヶ月で25個のフックを作りましたが、その多くはエージェントが既に持っているコンテキストを追加するだけのものでした。ツール呼び出しのたびに25個のフックを読み込むオーバーヘッドは計測可能なレベルだったのです。最終的には、実際に価値を生むフック(実際のインシデントを防ぎ、実際の品質問題を捕らえるもの)に絞り込みました。
初日から設定駆動で。 ハードコードされたしきい値を JSON 設定にリファクタリングするのに2週間を費やしました。最初から設定で始めていれば、そのリファクタリングは不要だったでしょう。
テストインフラは早めに。 最初の20個のフックにはテストがありませんでした。テストハーネス(48件の bash 統合テスト)を追加したとき、エッジケースで静かに失敗する3つのフックが見つかったのです。テストはフック#1と一緒に出荷すべきでした。
要点
フックを始める開発者向け: - 3つのフックから始めましょう。git セーフティ(PreToolUse:Bash)、コンテキスト注入(UserPromptSubmit)、そして品質ゲート(Stop)。正当化するインシデントがあってはじめて追加していきましょう - 意思決定タイミングのフレームワークを使ってください。フックのアーキテクチャは不可逆です(95個のフックがそれに依存する)。だからフックを書く前にライフサイクルモデルに投資しましょう
フックを標準化するチーム向け: - すべてのチームメンバーが同じセーフティレールを得られるよう、ユーザーレベルでフックを標準化しましょう - メンテナンスコストを正当化するために、フックのメトリクス(ブロックされた操作、インターセプトされたインシデント)を追跡しましょう - 自動化する価値のある新しいパターンを特定するため、フックのログを毎月レビューしましょう
FAQ
Claude Code フックとは何ですか?
フックは、Claude Code セッション中の特定のライフサイクルポイントで実行されるシェルスクリプトです。ツールが実行される前(PreToolUse)、完了後(PostToolUse)、プロンプトを送信したとき(UserPromptSubmit)、セッションが開始したとき(SessionStart)、そして Claude が応答を完了したとき(Stop)という特定の瞬間に、決定論的に発火します。フックは stdin で完全なコンテキスト(ツール名、入力、セッションID)を含む JSON を受け取り、操作のブロック、コンテキストの注入、品質ゲートの強制ができます。確率的な LLM の上に、決定論的な保証を提供するものです。
Claude Code はいくつのフックを実行でき、パフォーマンスコストはありますか?
フック数に厳密な制限はありません。私は利用可能な26個のライフサイクルイベントのうち6個で95個のフックを実行しており(v2.1.116、2026年4月時点)、イベントあたりの合計オーバーヘッドは約200msです。実用上の制限はレイテンシです。各フックはツール呼び出しの前後に実行時間を追加します。鍵となる最適化は、stdin をそれぞれ独立して読む独立したフックではなく、ディスパッチャー(イベントごとに1つで、キャッシュされた stdin から順次フックを実行)を使うこと。初期セットアップでは独立したフックからの同時書き込みによって JSON が破損したのですが、ディスパッチャーによりその失敗モードは完全に排除できました。
Claude Code フックはどのイベントで発火しますか?
Claude Code は v2.1.116(2026年4月)時点で、フック向けに26個のライフサイクルイベントを公開しています。私のフックが対象とする最も一般的な6つは、SessionStart(新しいセッション開始)、SessionEnd(セッション終了)、PreToolUse(任意のツールが実行される前)、PostToolUse(ツール完了後)、UserPromptSubmit(ユーザーがプロンプトを送信したとき)、そして Stop(Claude が応答を完了したとき)です。その他、SubagentStart、PermissionRequest、PermissionDenied、TaskCreated、CwdChanged、FileChanged、PreCompact などがあります。PreToolUse と PostToolUse は、PreToolUse:Bash や PreToolUse:Task のようなマッチャーでさらにスコープを絞り、特定のツールタイプでのみ発火させられます。各イベントは stdin で関連するコンテキストを含む JSON を受け取ります。
Claude Code フックはツール呼び出しをブロックできますか?
はい。stdout に {"decision": "block", "reason": "explanation"} を出力するフックは、ツール呼び出しの実行を防ぎます。これがセーフティフックの中核的な仕組みです。git-safety-guardian は main への強制プッシュをブロック、credentials-check は .env ファイルの読み取りをブロック、recursion-guard は過剰なエージェント生成をブロックします。ブロックは決定論的で、プロンプトでは回避できません。終了コード 0 で静かに終了するフックは、操作の続行を許可するのです。
PreToolUse フックと PostToolUse フックの違いは何ですか?
PreToolUse はツールが実行される前に発火し、操作を完全にブロックできます。セーフティゲートに使いましょう。危険なコマンドのブロック、認証情報のチェック、生成予算の強制などです。PostToolUse はツールが完了した後に発火し、フィードバックを提供したり後続のアクションをトリガーしたりできます。検証に使いましょう。出力品質のチェック、活動のログ記録、合意の検証などです。両者を合わせると、4層のセーフティネットの1層目と3層目を形成します。予防(PreToolUse)、コンテキスト注入(UserPromptSubmit)、検証(PostToolUse)、品質ゲーティング(Stop)です。
References
-
Author’s hook infrastructure. 95 hooks across 6 of the 26 available lifecycle events (v2.1.116, April 2026), developed over 9 months (2025-2026). ↩
-
Anthropic, “Claude Code Hooks Reference,” 2026. 26 lifecycle event types as of v2.1.116 (April 2026). ↩
-
Anthropic, “Claude Code Documentation,” 2025. Hook input/output JSON format. ↩
-
Author’s git-safety-guardian.sh. 8 intercepted force-push attempts tracked in
~/.claude/state/. ↩ -
Author’s recursion-guard.sh. Budget inheritance model documented in
~/.claude/configs/recursion-limits.json. ↩ -
Author’s config-driven architecture. 14 JSON config files encoding all hook thresholds and rules. ↩
-
Anthropic, “Claude Code Documentation,” 2025. Hook configuration and async execution. ↩
-
Anthropic, “Claude Code Documentation,” 2025. Hook scope hierarchy. ↩