メインコンテンツへスキップ
ブログ一覧

Claude Code Hooks実践ガイド — 全22イベント×4ハンドラータイプで構築する自動化ワークフロー

(更新: 2026年03月21日)
Claude CodeHooks開発自動化CI/CD

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にフィードバックされ、操作が中止される
  • それ以外: 非ブロッキングエラー。ログに記録されるが処理は続行する

最小構成の例を見てみよう。

json
{
  "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がファイルを書き換えたら、自動でフォーマッタを走らせる。matcherWriteEditツールに限定するのがポイントだ。

json
{
  "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)\""
      }
    ]
  }
}

もう少し読みやすくしたい場合は、シェルスクリプトに切り出すほうがよい。

bash
#!/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
json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": { "tool_name": "Write|Edit" },
        "type": "command",
        "command": "bash .claude/hooks/auto-format.sh"
      }
    ]
  }
}

正直、インラインのワンライナーは可読性が厳しいので、個人的にはスクリプト分離派だ。

レシピ2: PreToolUse × ファイル保護(.envや本番設定の書き換えをブロック)

.envやプロダクション設定ファイルをClaude が書き換えようとしたら、exit code 2でブロックする。

bash
#!/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
json
{
  "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コミットを作成する。大きなリファクタリング中に「戻したい」と思ったときに助かる。

bash
#!/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
json
{
  "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をバックグラウンドで実行して確実に制限内に収める。

bash
#!/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
json
{
  "hooks": {
    "SessionEnd": [
      {
        "type": "command",
        "command": "bash .claude/hooks/notify-slack.sh"
      }
    ]
  }
}

タイムアウトを延長したい場合は、環境変数で上書きできる。

bash
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 claude

レシピ5: UserPromptSubmit × promptタイプでプロンプト品質チェック

promptタイプを使えば、ユーザーの入力をLLM(デフォルトはHaiku)に判定させられる。曖昧なプロンプトを事前に検出できる。

json
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "type": "prompt",
        "prompt": "ユーザーのプロンプトを評価してください。具体的なファイル名・関数名・期待する動作のいずれかが含まれていれば ok: true を返してください。曖昧すぎる場合は ok: false で、reason に改善案を書いてください。"
      }
    ]
  }
}

ok: falseが返ると、Claude にフィードバックされてプロンプトの明確化を促してくれる。チーム内で「プロンプトの書き方が雑で手戻りが多い」という課題がある場合に効果的だ。

レシピ6: PreToolUse × Bashコマンドの安全性バリデーション

Claudeが実行しようとするBashコマンドに危険なパターンが含まれていないかチェックする。

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
json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": { "tool_name": "Bash" },
        "type": "command",
        "command": "bash .claude/hooks/validate-bash.sh"
      }
    ]
  }
}

よくあるハマりポイントと対処法

デバッグの基本: stdinパースとログ出力

HookスクリプトはstdinからイベントデータをJSON形式で受け取る。パースにはjqが手軽だ。

bash
# 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で書く場合はこうなる。

bash
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再帰呼び出しは避けよう。

また、matchertool_nameはパイプ区切りのパターンマッチで、Write|Editのように指定する。ワイルドカード(*)は使えない。

チーム共有とCI/CDでの活用

.claude/settings.jsonでチーム標準Hooksを共有

プロジェクトルートの.claude/settings.jsonをGit管理すれば、チーム全員に同じHooksが適用される。

json
{
  "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で同等のロジックを組む必要がある。

json
{
  "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との協業をより安全で効率的なものにしてほしい。

もっと読む他の技術記事も読む