Claude Code Hooks実践ガイド — 全22イベント×4ハンドラータイプで構築する自動化ワークフロー
Claude Code Hooks完全ガイド — 全22イベントと6つの実践レシピ
Claude Codeを使っていて「ファイル保存のたびにフォーマッタを手動で走らせるのが面倒」「危険なファイルをうっかり書き換えられた」と感じたことはないだろうか。Hooksを使えば、AIの行動に対してシェルコマンド・HTTP通知・LLM判定を自動で差し込める。本記事では全22イベントを体系的に整理し、今日から使える6つの実践レシピをsettings.json設定付きで解説する。
Hooksとは何か — AIの行動にガードレールを組み込む仕組み
Hooksの基本構造:イベント×ハンドラータイプ
Hooksは「Claudeがツールを呼ぶ前後」「セッション開始・終了時」などのイベントに、任意の処理を差し込む仕組みだ。たとえばファイル編集後に自動フォーマットをかけたり、危険な操作をブロックしたりできる。
ハンドラータイプは4種類ある。
| タイプ | 概要 | 主な用途 |
|---|---|---|
command | シェルコマンドを実行 | フォーマッタ実行、Git操作、ファイルチェック |
http | 指定URLにPOST | Slack通知、外部API連携 |
prompt | 単一ターンのLLM判定 | プロンプト品質チェック、コード規約検証 |
agent | マルチターンのサブエージェント検証 | 複雑なコードレビュー、依存関係チェック |
commandタイプの場合、exit codeで挙動が変わる。
- exit 0: 成功。処理を続行する
- exit 2: ブロック。stderrの内容がClaudeにフィードバックされ、操作が中止される
- それ以外: 非ブロッキングエラー。ログに記録されるが処理は続行する
最小構成の例を見てみよう。
{
"hooks": {
"PreToolUse": [
{
"type": "command",
"command": "echo 'Tool about to be used' >&2"
}
]
}
}
設定スコープ:ユーザー / プロジェクト / ローカル
設定ファイルは主に3つのスコープがある。
| ファイル | スコープ | Git管理 |
|---|---|---|
~/.claude/settings.json | 全プロジェクト共通 | No |
.claude/settings.json | プロジェクト単位 | Yes(チーム共有可) |
.claude/settings.local.json | プロジェクト単位(個人) | No(.gitignore推奨) |
チームで共通のHooksを使いたい場合は.claude/settings.jsonをリポジトリにコミットし、個人のカスタマイズは.claude/settings.local.jsonに書く、という使い分けが基本になる。
全22イベント早見表 — どこで何が発火するか
v2.1.78時点でHooksイベントは全22種。競合記事では12〜14と記載されていることが多いが、サブエージェント系やコンテキスト圧縮系など、後から追加されたイベントがある。
セッション系
| イベント | 発火タイミング | 主な用途 |
|---|---|---|
SessionStart | セッション開始時 | 環境セットアップ、ログ開始 |
SessionEnd | セッション終了時 | 通知送信、クリーンアップ |
InstructionsLoaded | CLAUDE.md等の読み込み後 | 設定検証 |
ConfigChange | 設定変更検出時 | 設定同期 |
ツール系
| イベント | 発火タイミング | 主な用途 |
|---|---|---|
PreToolUse | ツール実行前 | ファイル保護、コマンド検証 |
PostToolUse | ツール実行後 | auto-format、Git操作 |
PostToolUseFailure | ツール実行失敗後 | エラー通知、リカバリ |
PermissionRequest | 権限承認の確認時 | カスタム権限ロジック |
エージェント系
| イベント | 発火タイミング | 主な用途 |
|---|---|---|
SubagentStart | サブエージェント起動時 | ログ、リソース管理 |
SubagentStop | サブエージェント終了時 | 結果収集 |
TeammateIdle | チームメイトがアイドル時 | タスク割り当て |
TaskCompleted | タスク完了時 | 完了通知 |
プロンプト・コンテキスト系
| イベント | 発火タイミング | 主な用途 |
|---|---|---|
UserPromptSubmit | ユーザー入力送信時 | プロンプト品質チェック |
PreCompact | コンテキスト圧縮前 | ログ保全 |
PostCompact | コンテキスト圧縮後 | 圧縮結果の検証 |
Stop | 応答停止時 | 後処理 |
StopFailure | 停止処理失敗時 | エラーハンドリング |
その他
| イベント | 発火タイミング | 主な用途 |
|---|---|---|
Notification | 通知発生時 | 外部通知連携 |
Elicitation | 情報要求時 | カスタム入力UI |
ElicitationResult | 情報要求結果受信時 | 入力検証 |
WorktreeCreate | worktree作成時 | 環境準備 |
WorktreeRemove | worktree削除時 | クリーンアップ |
押さえておくべきポイントがいくつかある。PermissionRequestはヘッドレスモード(-p)では発火しない。自動化パイプラインではPreToolUseを使おう。SessionEndのデフォルトタイムアウトは1.5秒と極端に短い。重い処理を入れると確実にタイムアウトする。
6つの実践レシピ
レシピ1: PostToolUse × auto-format(Biome/Prettierを自動実行)
Claudeがファイルを書き換えたら、自動でフォーマッタを走らせる。matcherでWriteとEditツールに限定するのがポイントだ。
{
"hooks": {
"PostToolUse": [
{
"matcher": { "tool_name": "Write|Edit" },
"type": "command",
"command": "cat | node -e \"const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); const f=d.tool_input?.file_path; if(f?.match(/\\.(ts|tsx|js|jsx)$/)) require('child_process').execSync('npx biome format --write '+f)\""
}
]
}
}
もう少し読みやすくしたい場合は、シェルスクリプトに切り出すほうがよい。
#!/bin/bash
# .claude/hooks/auto-format.sh
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE" =~ \.(ts|tsx|js|jsx)$ ]]; then
npx biome format --write "$FILE" 2>/dev/null
fi
exit 0
{
"hooks": {
"PostToolUse": [
{
"matcher": { "tool_name": "Write|Edit" },
"type": "command",
"command": "bash .claude/hooks/auto-format.sh"
}
]
}
}
正直、インラインのワンライナーは可読性が厳しいので、個人的にはスクリプト分離派だ。
レシピ2: PreToolUse × ファイル保護(.envや本番設定の書き換えをブロック)
.envやプロダクション設定ファイルをClaude が書き換えようとしたら、exit code 2でブロックする。
#!/bin/bash
# .claude/hooks/protect-files.sh
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
PROTECTED_PATTERNS=(
".env"
".env.production"
"*.pem"
"*.key"
"docker-compose.prod.yml"
)
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE" == *"$pattern"* ]]; then
echo "BLOCKED: $FILE is a protected file" >&2
exit 2
fi
done
exit 0
{
"hooks": {
"PreToolUse": [
{
"matcher": { "tool_name": "Write|Edit" },
"type": "command",
"command": "bash .claude/hooks/protect-files.sh"
}
]
}
}
exit 2で終了すると、stderrに出力した理由がClaudeにフィードバックされる。Claudeは「このファイルは保護されている」と理解し、別のアプローチを提案してくれる。
レシピ3: PostToolUse × Git自動バックアップ(編集ごとにWIPコミット)
ファイル編集のたびにWIPコミットを作成する。大きなリファクタリング中に「戻したい」と思ったときに助かる。
#!/bin/bash
# .claude/hooks/auto-backup.sh
cd "$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0
git add -A && git commit --allow-empty-message -m "" --no-verify 2>/dev/null
exit 0
{
"hooks": {
"PostToolUse": [
{
"matcher": { "tool_name": "Write|Edit" },
"type": "command",
"command": "bash .claude/hooks/auto-backup.sh"
}
]
}
}
これは地味に便利だ。ただし、コミット履歴が大量に増えるので、作業後にgit rebase -iで整理するのを忘れずに。
レシピ4: SessionEnd × Slack通知(セッション終了サマリーを送信)
セッション終了時にSlackへ通知を送る。ここで重要なのが、SessionEndのデフォルトタイムアウトは1.5秒という点だ。curlをバックグラウンドで実行して確実に制限内に収める。
#!/bin/bash
# .claude/hooks/notify-slack.sh
WEBHOOK_URL="$SLACK_WEBHOOK_URL"
INPUT=$(cat)
TRANSCRIPT=$(echo "$INPUT" | jq -r '.transcript_summary // "No summary"')
curl -s -X POST "$WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "{\"text\": \"Claude Codeセッション終了: $TRANSCRIPT\"}" &
exit 0
{
"hooks": {
"SessionEnd": [
{
"type": "command",
"command": "bash .claude/hooks/notify-slack.sh"
}
]
}
}
タイムアウトを延長したい場合は、環境変数で上書きできる。
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 claude
レシピ5: UserPromptSubmit × promptタイプでプロンプト品質チェック
promptタイプを使えば、ユーザーの入力をLLM(デフォルトはHaiku)に判定させられる。曖昧なプロンプトを事前に検出できる。
{
"hooks": {
"UserPromptSubmit": [
{
"type": "prompt",
"prompt": "ユーザーのプロンプトを評価してください。具体的なファイル名・関数名・期待する動作のいずれかが含まれていれば ok: true を返してください。曖昧すぎる場合は ok: false で、reason に改善案を書いてください。"
}
]
}
}
ok: falseが返ると、Claude にフィードバックされてプロンプトの明確化を促してくれる。チーム内で「プロンプトの書き方が雑で手戻りが多い」という課題がある場合に効果的だ。
レシピ6: PreToolUse × Bashコマンドの安全性バリデーション
Claudeが実行しようとするBashコマンドに危険なパターンが含まれていないかチェックする。
#!/bin/bash
# .claude/hooks/validate-bash.sh
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
BLOCKED_PATTERNS=(
"rm -rf /"
"rm -rf ~"
":(){ :|:& };:"
"mkfs"
"dd if="
"> /dev/sd"
"curl.*| ?sh"
"wget.*| ?sh"
)
for pattern in "${BLOCKED_PATTERNS[@]}"; do
if echo "$COMMAND" | grep -qE "$pattern"; then
echo "BLOCKED: Dangerous command detected: $pattern" >&2
exit 2
fi
done
exit 0
{
"hooks": {
"PreToolUse": [
{
"matcher": { "tool_name": "Bash" },
"type": "command",
"command": "bash .claude/hooks/validate-bash.sh"
}
]
}
}
よくあるハマりポイントと対処法
デバッグの基本: stdinパースとログ出力
HookスクリプトはstdinからイベントデータをJSON形式で受け取る。パースにはjqが手軽だ。
# Bashでの基本パターン
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
Node.jsで書く場合はこうなる。
cat | node -e "
let d='';
process.stdin.on('data',c=>d+=c);
process.stdin.on('end',()=>{
const json=JSON.parse(d);
console.error(JSON.stringify(json, null, 2));
});
"
デバッグ時はCtrl+Oでverboseモードを有効にすると、Hook実行の詳細ログが確認できる。
タイムアウト・パフォーマンス問題
ハンドラータイプごとにデフォルトタイムアウトが異なる点を意識しておこう。
| タイプ | デフォルトタイムアウト |
|---|---|
command | 600秒 |
http | 30秒 |
prompt | 30秒 |
agent | 60秒 |
| SessionEnd全体 | 1.5秒 |
timeoutフィールドで個別に上書きできるが、SessionEndはイベント全体の上限が1.5秒なので、個別のtimeoutを伸ばしても意味がない。延長には環境変数を使う。
もう一つ注意したいのが、Hookスクリプト内でclaudeコマンドを呼ぶと無限ループになる可能性があること。Claude CLIの起動がSessionStartを発火させ、それがまたclaudeを呼び…という連鎖だ。commandタイプのHook内からのCLI再帰呼び出しは避けよう。
また、matcherのtool_nameはパイプ区切りのパターンマッチで、Write|Editのように指定する。ワイルドカード(*)は使えない。
チーム共有とCI/CDでの活用
.claude/settings.jsonでチーム標準Hooksを共有
プロジェクトルートの.claude/settings.jsonをGit管理すれば、チーム全員に同じHooksが適用される。
{
"hooks": {
"PreToolUse": [
{
"matcher": { "tool_name": "Write|Edit" },
"type": "command",
"command": "bash .claude/hooks/protect-files.sh"
}
],
"PostToolUse": [
{
"matcher": { "tool_name": "Write|Edit" },
"type": "command",
"command": "bash .claude/hooks/auto-format.sh"
}
]
}
}
個人が追加したいHooksは.claude/settings.local.jsonに書き、.gitignoreに追加しておけばよい。
ヘッドレスモード(-p)でのHooks活用
CI/CDパイプラインなどでclaude -pを使う場合、PermissionRequestイベントは発火しない。権限チェックが必要な場合は、PreToolUseで同等のロジックを組む必要がある。
{
"hooks": {
"PreToolUse": [
{
"matcher": { "tool_name": "Bash" },
"type": "command",
"command": "bash .claude/hooks/validate-bash.sh"
}
],
"PostToolUse": [
{
"type": "command",
"command": "bash .claude/hooks/ci-log-collector.sh"
}
]
}
}
ヘッドレスモードではPostToolUseにログ収集Hookを仕込んでおくと、CI実行後の振り返りに役立つ。品質ゲートとしてpromptタイプのHookを使い、生成コードが規約に沿っているかをLLMに判定させる、という構成も実用的だ。
Hooksは「AIが何かする前後に、自分のルールを差し込む」仕組みだ。auto-formatやファイル保護のような基本レシピから始めて、チーム共有・CI/CD統合へと段階的に活用範囲を広げていける。まずは1つレシピを試して、Claude Codeとの協業をより安全で効率的なものにしてほしい。
