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

Claude Code Hooks 実践レシピ集 — 全17イベント解説と本番で使える自動化パターン10選

(更新: 2026年03月05日)
Claude CodeHooksワークフロー自動化開発効率化

Claude Codeを使い込むほど感じる「この操作、毎回手動でやるの?」というストレス。Hooksを設定すれば、危険コマンドの自動ブロック、ファイル変更時のテスト自動実行、コンテキスト圧縮時のメモリ保全まで、あなたのワークフローをコードで制御できる。本記事では公式ドキュメント準拠の正確な仕様解説に加え、現場で即使える10のレシピをコピペ可能な設定付きで紹介する。

Hooksとは何か — 30秒で分かる仕組み

Hooksは「Claude Codeの特定アクションの前後にシェルコマンド・HTTPリクエスト・LLM評価を自動実行する仕組み」だ。settings.jsonhooksフィールドに、イベント(いつ)× マッチャー(何に対して)× ハンドラ(何をする) の3要素で定義する。

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Bashが実行されます' >> /tmp/claude-hook.log"
          }
        ]
      }
    ]
  }
}

設定ファイルの配置場所と優先順位は以下の通り。

場所 スコープ リポジトリ共有
.claude/settings.json プロジェクト単位 可(git commit推奨)
~/.claude/settings.json ユーザー全体 不可
.claude/settings.local.json プロジェクト単位 不可(gitignore対象)
マネージドポリシー 組織全体 管理者が制御

プロジェクト設定がユーザー設定より優先され、エンタープライズのマネージドポリシーが最優先となる。

CLAUDE.md・MCP・Hooksの使い分け:

  • CLAUDE.md: Claudeへの指示(「テストはvitestで実行して」等)
  • MCP: 外部ツール・データソースとの接続
  • Hooks: 決定論的な自動処理(「この条件では必ずこれを実行」)

4つのハンドラタイプ完全比較

command — シェル実行

最も汎用的なタイプ。stdinでJSON入力を受け取り、exit codeで結果を制御する。

  • exit 0: 成功。stdoutがJSON解析される
  • exit 2: ブロッキングエラー。操作を中止し、stderrがClaudeに通知される
  • exit その他: 非ブロッキングエラー。警告のみで続行

デフォルトタイムアウトは600秒async: trueを設定するとバックグラウンド実行が可能(commandのみ対応)。

http — HTTP POST送信

外部サービス連携向け。イベントのJSONがPOSTボディとして送信される。ヘッダーに$VAR_NAME形式で環境変数を埋め込めるが、allowedEnvVarsフィールドに明示的にリストした変数のみ展開され、未リストの変数は空文字に置換される点に注意。

prompt — LLM単一ターン評価

AIに判断させたい場面で使う。高速なモデルで評価し、{ "ok": true } または { "ok": false, "reason": "..." } を返す。タイムアウトは30秒と短いので注意。

agent — サブエージェント実行

Read・Grep・Globツールを使える最大50ターンのサブエージェントが起動する。タイムアウトは60秒。複雑な検証タスク向きだが、コストに注意。

同じ「ファイル変更通知」を4タイプで比較:

command — curlでSlackに通知:

json
{ "type": "command", "command": "curl -X POST -d '{\"text\":\"ファイル変更\"}' $SLACK_WEBHOOK_URL" }

http — 直接Webhookに送信:

json
{ "type": "http", "url": "https://hooks.slack.com/services/XXX" }

prompt — 変更の重要度をAI判定:

json
{ "type": "prompt", "prompt": "この変更は重要か判定してください: $ARGUMENTS" }

agent — 変更内容を読み取って影響範囲を分析:

json
{ "type": "agent", "prompt": "変更されたファイルの影響範囲を調査してください: $ARGUMENTS" }

全17イベント早見表

イベント 発火タイミング マッチャー対象 主要ユースケース
SessionStart セッション開始・再開時 起動種別 環境セットアップ
SessionEnd セッション終了時 終了理由 クリーンアップ
ConfigChange 設定ファイル変更時 設定ソース 設定監査ログ
UserPromptSubmit プロンプト送信時 なし 入力バリデーション
PreToolUse ツール実行前 ツール名(正規表現) セキュリティガード
PostToolUse ツール実行成功後 ツール名 自動テスト・lint
PostToolUseFailure ツール実行失敗後 ツール名 エラー追跡
PermissionRequest 権限確認ダイアログ表示時 ツール名 自動許可/拒否
SubagentStart サブエージェント起動時 エージェント種別 ログ記録
SubagentStop サブエージェント終了時 エージェント種別 品質チェック
Stop Claude応答完了時 なし タスク完了確認
TeammateIdle チームメイトがidle化 なし 継続指示
TaskCompleted タスク完了マーク時 なし 完了通知
PreCompact コンテキスト圧縮前 トリガー種別 メモリ保全
Notification 通知送信時 通知タイプ デスクトップ通知
WorktreeCreate ワークツリー作成時 なし カスタムVCS連携
WorktreeRemove ワークツリー削除時 なし クリーンアップ

PreToolUseが最も利用頻度が高い。マッチャーは正規表現で部分一致する点に注意。Bashと書くとBashを含むすべてにマッチするため、完全一致には^Bash$とアンカーが必要だ。

MCP toolはmcp__<server>__<tool>の命名規則に従うので、mcp__memory__.*のようなパターンでサーバー単位のフィルタリングも可能。

本番で使えるレシピ10選

各レシピは 課題 → 解決策 → 設定コード → 注意点 の統一フォーマットで紹介する。

レシピ1: 危険コマンドブロッカー

課題: rm -rf /git push --forceを誤って実行されるリスク。

設定:

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "^Bash$",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/guard.sh"
          }
        ]
      }
    ]
  }
}

.claude/hooks/guard.sh:

bash
#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

DANGEROUS_PATTERNS=(
  "rm -rf /"
  "rm -rf ~"
  "git push.*--force"
  "git reset --hard"
  "DROP TABLE"
  "DROP DATABASE"
)

for pattern in "${DANGEROUS_PATTERNS[@]}"; do
  if echo "$CMD" | grep -qiE "$pattern"; then
    echo "ブロック: '$pattern' に一致する危険なコマンドです" >&2
    exit 2
  fi
done

exit 0

注意点: exit 2がブロッキングエラーを返し、stderrの内容がClaudeに通知される。Claudeは理由を理解して代替手段を提案してくれる。

レシピ2: ファイル変更時の自動テスト実行

課題: ファイル編集後にテスト実行を忘れる。

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "cd \"$CLAUDE_PROJECT_DIR\" && npm test -- --bail 2>&1 | tail -20",
            "async": true,
            "timeout": 120
          }
        ]
      }
    ]
  }
}

注意点: async: trueにより編集作業をブロックしない。テスト結果は次の会話ターンでClaudeに伝わる。

レシピ3: PreCompactでのカスタム指示保存

課題: 手動で/compactを実行する際に指定したカスタム指示を記録として残したい。

.claude/hooks/save-memory.sh:

bash
#!/bin/bash
INPUT=$(cat)
INSTRUCTIONS=$(echo "$INPUT" | jq -r '.custom_instructions // empty')
MEMORY_FILE="$CLAUDE_PROJECT_DIR/.claude/memory/auto-compact.md"

# custom_instructionsは手動 /compact 実行時のユーザー入力テキスト
# 自動コンパクト時は空文字になるため、空の場合はスキップ
if [ -z "$INSTRUCTIONS" ]; then
  exit 0
fi

mkdir -p "$(dirname "$MEMORY_FILE")"
{
  echo ""
  echo "## $(date '+%Y-%m-%d %H:%M') の /compact カスタム指示"
  echo "$INSTRUCTIONS"
} >> "$MEMORY_FILE"
exit 0
json
{
  "hooks": {
    "PreCompact": [
      {
        "hooks": [
          { "type": "command", "command": ".claude/hooks/save-memory.sh" }
        ]
      }
    ]
  }
}

注意点: custom_instructionsは手動で/compactコマンドを実行した際にユーザーが入力したテキストのみが格納される。コンテキストウィンドウの上限到達による自動コンパクト時には空文字となるため、自動コンパクトでは何も保存されない。自動コンパクト時にも情報を保全したい場合は、CLAUDE.mdやメモリファイルへの手動記録と組み合わせる運用が必要だ。

レシピ4: Slack通知連携

課題: 長時間タスクの完了を見逃す。

json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "http",
            "url": "https://hooks.slack.com/services/T00/B00/xxx",
            "headers": { "Content-Type": "application/json" }
          }
        ]
      }
    ]
  }
}

注意点: http hookはイベントのJSONがそのままPOSTされる。Slackのペイロード形式に変換が必要な場合はcommand hookでcurlを使う方が柔軟。

レシピ5: コミット前の自動lint/format

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "^Bash$",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/pre-commit-lint.sh"
          }
        ]
      }
    ]
  }
}
bash
#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if echo "$CMD" | grep -q "git commit"; then
  cd "$CLAUDE_PROJECT_DIR" && npx lint-staged 2>&1 || {
    echo "lint-stagedが失敗しました。コードを修正してください。" >&2
    exit 2
  }
fi
exit 0

レシピ6: SessionStartでの環境セットアップ

json
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "echo \"ブランチ: $(git branch --show-current) | Node: $(node -v) | 未コミット: $(git status --short | wc -l | tr -d ' ')件\""
          }
        ]
      }
    ]
  }
}

注意点: SessionStartではstdoutがClaudeのコンテキストに追加される。セッション再開時に現在の状況を把握させるのに最適。

レシピ7: async hookによるバックグラウンドログ収集

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/audit-log.sh",
            "async": true
          }
        ]
      }
    ]
  }
}
bash
#!/bin/bash
INPUT=$(cat)
echo "$INPUT" | jq -c '{
  timestamp: now | todate,
  tool: .tool_name,
  command: .tool_input.command
}' >> "$CLAUDE_PROJECT_DIR/.claude/audit.jsonl"
exit 0

注意点: async: trueはcommand hookのみ対応。http/prompt/agentでは使えない。

レシピ8: prompt hookによるコード品質ゲート

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "prompt",
            "prompt": "以下のツール実行結果を確認し、生成・変更されたコードにセキュリティ上の問題(ハードコードされた秘密情報、SQLインジェクション、XSS脆弱性)がないか判定してください。問題がなければ{\"ok\":true}、あれば{\"ok\":false,\"reason\":\"具体的な問題点\"}を返してください。: $ARGUMENTS",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

注意点: prompt hookのデフォルトタイムアウトは30秒。高速なモデルが使われるため応答は速いが、大きなファイルの全体分析には向かない。

レシピ9: SubagentStop監視

json
{
  "hooks": {
    "SubagentStop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/check-subagent.sh"
          }
        ]
      }
    ]
  }
}
bash
#!/bin/bash
INPUT=$(cat)
LAST_MSG=$(echo "$INPUT" | jq -r '.last_assistant_message // empty')
if echo "$LAST_MSG" | grep -qi "error\|failed\|couldn't"; then
  echo "サブエージェントがエラーで終了した可能性があります: $(echo "$LAST_MSG" | head -c 200)" >&2
  exit 2
fi
exit 0

レシピ10: UserPromptSubmitでの入力バリデーション

json
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/validate-prompt.sh"
          }
        ]
      }
    ]
  }
}
bash
#!/bin/bash
INPUT=$(cat)
PROMPT=$(echo "$INPUT" | jq -r '.prompt // empty')

# 機密情報の誤送信を防止
if echo "$PROMPT" | grep -qE '(sk-[a-zA-Z0-9]{20,}|ghp_[a-zA-Z0-9]{36}|AKIA[A-Z0-9]{16})'; then
  echo "プロンプトにAPIキーまたはシークレットが含まれています" >&2
  exit 2
fi

exit 0

注意点: exit 2でプロンプトの処理がブロックされ、入力内容が破棄される。

ハマりポイントと解決策

~/.zshrcのechoがstdoutを汚染する

最も多いトラブル。.zshrcにfigletやechoで装飾を出力していると、hookのstdout JSONパースが壊れる。対策はhookスクリプトの冒頭で環境を明示的に設定するか、.zshrc側で非インタラクティブ時の出力をガードする。

bash
# .zshrcの先頭に追加
[[ $- != *i* ]] && return

タイムアウトの罠

prompt hookの30秒は意外と短い。重い判定処理はcommand hookにasync: trueを付けて逃がすのが定石。

デバッグ方法

開発中はhookへの入力JSONをファイルに書き出して確認する。

bash
#!/bin/bash
# デバッグ用: 入力をファイルに保存
cat | tee /tmp/claude-hook-debug.json | jq .
exit 0

claude --debugで起動するとhookの実行詳細がログに出力される。またCtrl+Oでverboseモードを切り替えられる。マッチャーの正規表現が意図通りか確認するときに便利だ。

まとめ — Hooksで「自分専用のClaude Code」を作る

Hooksは、Claude Codeを「汎用AIツール」から「自分のワークフローに最適化されたパートナー」に変える機能だ。

まずはレシピ1(危険コマンドブロッカー)レシピ3(PreCompactメモリ保全) の2つから始めるのがおすすめ。この2つだけでも安全性と継続性が大きく向上する。

チームで展開する場合は、.claude/settings.jsonにHooksを定義してリポジトリにコミットすれば、全メンバーに自動適用される。個人的な設定は~/.claude/settings.jsonに、プロジェクト固有かつgit管理不要なものは.claude/settings.local.jsonに分離すると運用しやすい。

関連リソース: