Claude Code Hooks:95個のHooksが存在する理由
Claude Codeのために95個のHooksを構築しました。すべては、まず何かがうまくいかなかったことから生まれています。git-safety-guardianは、Claudeがmainにforce-pushしたから存在します。recursion-guardは、サブエージェントが無限に子プロセスを生成したから存在します。blog-quality-gateは、受動態が7文あり、参照先のない脚注が残ったまま記事を公開してしまったから存在します。1
TL;DR
Claude Code Hooksは、AIアシスト開発のライフサイクル上の特定のポイントでシェルコマンドを実行する仕組みです。Hooksは、確率的なシステム(LLM)の上に決定論的な保証(危険なgitコマンドのブロック、コンテキストの注入、品質の強制)を提供します。自分のインフラ全体で95個のHooksを構築した結果、最良のHooksは計画からではなく、インシデントから生まれることがわかりました。この記事では、アーキテクチャ、最も重要なHooksの起源、そして9ヶ月のHook開発から学んだパターンについて解説します。
アーキテクチャ
Claude Codeは、Hooksが動作をインターセプト、変更、またはブロックできるライフサイクルイベントを公開しています。2
セッションイベント
| イベント | 発火タイミング | 使用しているHooks |
|---|---|---|
| SessionStart | 新しいセッション開始時 | session-start.sh — 日付の注入、venvの検証、再帰状態の初期化 |
| SessionEnd | セッション終了時 | 一時ファイルのクリーンアップ |
ツール実行イベント
| イベント | 発火タイミング | 使用しているHooks |
|---|---|---|
| PreToolUse | ツール実行前 | git-safety-guardian.sh、recursion-guard.sh、credentials-check.sh |
| PostToolUse | ツール完了後 | post-deliberation.sh、log-bash.sh |
レスポンスイベント
| イベント | 発火タイミング | 使用しているHooks |
|---|---|---|
| UserPromptSubmit | ユーザーがプロンプトを送信した時 | コンテキストインジェクター(日付、ブランチ、モデル情報) |
| Stop | Claudeが応答を完了した時 | deliberation-pride-check.sh、reviewer-stop-gate.sh |
すべてのHooksはstdinでJSONを受け取り、stdoutで通信します。
{"decision": "block", "reason": "Force push to main is prohibited"}
または、終了コード0で静かに許可します。3
起源:最も重要なHooks
Hook 1:git-safety-guardian.sh(PreToolUse:Bash)
インシデント: 初期のClaude Codeセッションで、エージェントに「gitの履歴を整理して」と依頼しました。エージェントはgit push --force origin mainを実行しました。このforce-pushにより、共有ブランチの3日分のコミットが上書きされました。ローカルバックアップから復旧しましたが、4時間の復旧作業を経て、確率的な判断に破壊的なgit操作を委ねるべきではないと確信しました。
Hookの内容:
#!/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
教訓: このHookは意図を理解しようとしません。コマンド文字列をパターンマッチするだけです。シンプルで、決定論的で、巧みなプロンプトでは回避できません。エージェントはfeatureブランチへのforce-pushは引き続き可能ですが(正当な場合もあるため)、main/masterは永久に保護されています。4
累計の防止回数: 9ヶ月間で8回のforce-push試行をインターセプトしました。
Hook 2:recursion-guard.sh(PreToolUse:Task)
インシデント: 審議システムを構築中、3つの探索サブエージェントを生成するセッションを実行しました。各サブエージェントは、生成制限がなかったため、独自のサブエージェントを生成しました。この再帰により、APIトークンが通常の10倍の速度で消費されました。加速するトークン消費に気づいた後、手動でセッションを終了しました。
Hookの内容:
#!/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 ]]
}
重要な設計判断: エージェントは深度をインクリメントするのではなく、親から生成バジェットを継承します。ルートエージェントがbudget=12を持つ場合、そのバジェットを任意のツリー形状に分配できます。深度ベースの制限は柔軟性に欠けます(完全に安全な深いが狭いチェーンを阻止してしまうため)。5
累計のブロック回数: 23回の暴走的な生成試行をブロックしました。
Hook 3:blog-quality-gate.sh(Stop)
インシデント: 受動態が7文、テキスト内で参照されているのに参照セクションにない脚注、そして冒頭が「was implemented by the team」というブログ記事を公開してしまいました。エディタ上では洗練されて見えましたが、人間のレビュアーなら誰でも気づく基本的な品質チェックに失敗していました。
Hookの内容: 変更されたブログコンテンツファイルに対して12モジュールのブログリンターを実行します。受動態、参照先のない脚注、メタディスクリプションの欠如、タグなしのコードブロック、引用の整合性をチェックします。各指摘は具体的です。例:「47行目:’was implemented by the team’に受動態を検出。提案:’the team implemented’」
人間のフィードバックとの類似点: このHookは作業を批評しますが、操作者を批評しません。「47行目に受動態があります」と言い、「あなたの文章は下手です」とは言いません。人間のフィードバックを建設的にするのと同じ原則が、自動化されたフィードバックを有用にしています。
95個のHooksの背後にあるパターン
設定駆動アーキテクチャ
Hooksはハードコードされた値から設定駆動の動作へと進化しました。
~/.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個のHooksは4つのレイヤーからなるセーフティネットを形成しています。
レイヤー1:予防(PreToolUse) — 問題が発生する前に阻止します。git-safety-guardian、credentials-check、recursion-guard。
レイヤー2:コンテキスト(UserPromptSubmit、SessionStart) — エージェントに必要な情報を注入します。日付、ブランチ、プロジェクトコンテキスト、メモリエントリ、設計原則。
レイヤー3:検証(PostToolUse) — 完了したアクションが基準を満たしているか確認します。post-deliberationのコンセンサスチェック、出力のロギング。
レイヤー4:品質(Stop) — 最終出力をゲートします。Pride check、quality gate、reviewer stop gate。
各レイヤーは独立しています。PreToolUseのHookがサイレントに失敗しても、StopのHookは品質の問題を検出します。多層防御をAIエージェントの動作に適用した形です。
設定方法
Hooksは.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フィールドは、どのツールがHookをトリガーするかをフィルタリングします。PreToolUse:Taskは、Taskツールの呼び出し時にのみ発火するmatcherの省略形です。非同期Hooks(async: true)はバックグラウンドで実行され、処理をブロックしません。7
スコープの階層
- ユーザーレベル(
~/.claude/settings.json) — すべてのプロジェクトに適用されます。95個のHooksはここに定義しています。 - プロジェクトレベル(
.claude/settings.json) — プロジェクト固有のHooksを追加します。 - スキル/サブエージェントのフロントマター — 特定のコンポーネントのライフサイクルにスコープされます。8
振り返り:こうすればよかったこと
25個ではなく、3個から始めるべきでした。 最初の1ヶ月で25個のHooksを作りましたが、その多くはエージェントがすでに持っている情報をコンテキストとして追加するものでした。ツール呼び出しのたびに25個のHooksをロードするオーバーヘッドは計測可能なレベルでした。最終的に、実際の価値を生み出すHooks(実際のインシデントを防止し、実際の品質問題を検出するもの)に絞り込みました。
初日から設定駆動にすべきでした。 ハードコードされた閾値をJSON設定にリファクタリングするのに2週間かかりました。最初から設定駆動にしていれば、このリファクタリングは不要でした。
テストインフラを早期に構築すべきでした。 最初の20個のHooksにはテストがありませんでした。テストハーネス(48個のbash統合テスト)を追加した際、エッジケースでサイレントに失敗する3つのHooksを発見しました。テストはHook #1と一緒に導入すべきでした。
まとめ
Hooksを始める開発者の方へ: - まず3つのHooksから始めてください。git安全性(PreToolUse:Bash)、コンテキスト注入(UserPromptSubmit)、品質ゲート(Stop)の3つです。追加するのは、それを正当化するインシデントが発生してからにしてください - 意思決定タイミングのフレームワークを活用してください。Hookアーキテクチャは不可逆です(95個のHooksがそれに依存しています)。Hooksを書く前にライフサイクルモデルに投資してください
Hooksを標準化するチームの方へ: - ユーザーレベルでHooksを標準化し、すべてのチームメンバーが同じセーフティレールを利用できるようにしてください - Hookのメトリクス(ブロックした操作、インターセプトしたインシデント)を追跡し、メンテナンスコストを正当化してください - Hookのログを月次でレビューし、自動化すべき新しいパターンを特定してください
参考文献
-
著者のHookインフラ。6つのライフサイクルイベントにわたる95個のHooks、9ヶ月間で開発(2025年〜2026年)。 ↩
-
Anthropic、“Claude Code Documentation”、2025年。Hookライフサイクルイベント。 ↩
-
Anthropic、“Claude Code Documentation”、2025年。Hook入出力のJSONフォーマット。 ↩
-
著者のgit-safety-guardian.sh。
~/.claude/state/で追跡された8回のforce-pushインターセプト。 ↩ -
著者のrecursion-guard.sh。
~/.claude/configs/recursion-limits.jsonに記載されたバジェット継承モデル。 ↩ -
著者の設定駆動アーキテクチャ。すべてのHook閾値とルールをエンコードする14個のJSON設定ファイル。 ↩
-
Anthropic、“Claude Code Documentation”、2025年。Hook設定と非同期実行。 ↩
-
Anthropic、“Claude Code Documentation”、2025年。Hookスコープの階層。 ↩