08-04. Hooks で Claude の動作を制御する
この回のゴール
- Hooks の主要イベント(PreToolUse / PostToolUse / Stop / UserPromptSubmit)とその使い分けを理解する
.claude/settings.jsonで Hook を 2 つ以上書ける- Exit Code の意味 (0 / 2 / その他) を覚え、Hook で Claude をブロックできる
1. 動機: Skills では「強制」できない
08-03 で作った Skills は Claude が「使うべき」と判断したら呼ばれる = lazy。 だが業務には 絶対に毎回守らせたい ルールがある:
- Edit の 後、必ずフォーマッタを通す
- Bash で
rm -rfを打とうとしたら、必ず止める - ユーザーが質問を投げる 前 に、特定のコンテキスト(現在の git ブランチなど)を自動で付ける
→ Hooks = Claude のセッション中、特定のイベントで 無条件に実行されるシェルコマンド。
2. Hook の主要イベント
| Event | 発火タイミング | 主な用途 |
|---|---|---|
PreToolUse |
Claude が tool (Edit/Bash 等) を呼ぶ直前 | 危険コマンドのブロック、入力検査 |
PostToolUse |
tool 実行直後 | フォーマッタ、テスト、commit 自動化 |
Stop |
Claude のターンが終わる時 | セッションログ保存、通知 |
UserPromptSubmit |
ユーザーがプロンプト送信時 | コンテキスト自動注入 |
SessionStart |
セッション開始時 | 環境変数 export、状態確認 |
3. Exit Code = 強制力
Hook はシェルコマンド(or スクリプト)。返り値が:
| Exit Code | 意味 | Claude の動作 |
|---|---|---|
| 0 | 成功 | そのまま続行 |
| 2 | ブロック | tool 実行を中止、Hook の stderr を Claude に伝える |
| その他 (1, 137 など) | 警告 | 続行はするが Claude に通知 |
→ exit 2 が 唯一の強制ブロック手段。
4. 設定ファイル .claude/settings.json
最小例(危険コマンドブロック + Edit 後フォーマッタ):
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "if echo \"$CLAUDE_TOOL_INPUT\" | grep -qE 'rm -rf|sudo rm'; then echo 'Blocked: dangerous command' >&2; exit 2; fi"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "ruff format $CLAUDE_FILE_PATHS 2>&1 || true"
}
]
}
]
}
}
環境変数(Hook に渡されるもの)
CLAUDE_TOOL_INPUT— tool の入力(Bash の場合はコマンド文字列)CLAUDE_FILE_PATHS— Edit/Write 対象のファイルパスCLAUDE_SESSION_ID— セッション識別子- (詳細は公式 docs に変動あり、現状の最新を確認)
5. ハンズオン
5.1 危険コマンドブロック Hook
上記の例の PreToolUse だけをまず入れて起動:
claude
> ホームディレクトリ全部消して
→ Claude が rm -rf ~/ を試みる → Hook が exit 2 → ブロックされる挙動を確認。
5.2 Edit 後フォーマッタ Hook
Python リポジトリで ruff (or black, prettier) をインストール。
> README.md を整形して(行末スペース削除など)
→ Edit 後に ruff format が走り、フォーマット済みになることを確認。
5.3 Stop Hook でログ保存
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "echo \"[$(date)] session $CLAUDE_SESSION_ID ended\" >> ~/.claude/sessions.log"
}
]
}
]
}
}
セッション終了ごとに ~/.claude/sessions.log に追記される。
6. アンチパターン
| 症状 | 原因 | 対策 |
|---|---|---|
| セッションが遅い | Hook が長時間ブロッキング (重いテストを毎回実行) | Hook 内で軽量チェックのみ、重い処理はバックグラウンド |
| Hook で誤ってブロック | 正規表現が広すぎる | grep -qE のパターンを慎重に、テストする |
Hook 実行で command not found |
パス設定が PATH に通っていない | コマンドはフルパスで書く |
| デバッグが難しい | Hook の出力が見えない | claude --debug で Hook の stderr 確認 |
7. CLAUDE.md / Skills / Hooks の総まとめ
| 強制力 | 場面 | 例 | |
|---|---|---|---|
| CLAUDE.md | なし(参考情報) | 常時 | プロジェクトの全体像、コーディング規約 |
| Skills | なし(Claude が判断) | 場面別 | コミット規約、PR レビュー観点 |
| Hooks | 強制(exit 2 でブロック可) | イベント別 | フォーマッタ、危険コマンド阻止、ログ |
→ 階層: CLAUDE.md (Soft) → Skills (Lazy) → Hooks (Hard)
8. 今回の限界 (notes に書く内容)
- Hook は強力だが、Claude Code 本体の挙動を変えるだけ
- 「Claude Code そのものを部分的に 再構成 したい」「特化したエージェント を CI 内で動かしたい」場合は不十分
- → 次回 (08-05) で Agent SDK を学び、自分用エージェント を作る方法を見る