AI Tools 2026年4月15日

Claude Code Hooks完全ガイド:12種のライフサイクルイベントで自動化を極める

Claude CodeのHooks機能を徹底解説。PreToolUse・PostToolUse・SessionStartなど12種以上のイベント、command/http/prompt/agentの4ハンドラ、設定例と実践パターンを2026年4月の最新仕様でまとめました。

TL;DR

Claude Code の Hooks は、セッション開始・ツール呼び出し・エージェント制御など 12 種以上のライフサイクルイベントに、シェルコマンド・HTTP リクエスト・プロンプト・サブエージェントの 4 種のハンドラを紐付けられる機能です。~/.claude/settings.json(グローバル)または .claude/settings.json(プロジェクト)に JSON で記述します。

Hooks で実現できる主な自動化:

  • ファイル編集後に auto-format(prettier / gofmt など)を実行
  • rm -rf など危険なコマンドを事前ブロック
  • ツール呼び出しのログを外部システムに送信
  • セッション終了時にサマリーを Slack 通知

Hooks の仕組み

Claude Code がアクションを起こす際、内部でライフサイクルイベントが発火します。Hooks はそのイベントをフックして、Claude とは独立した処理をユーザー定義のコードとして差し込む仕組みです。

Claude Code の動作フロー(Hooks あり)

UserPromptSubmit → [UserPromptSubmit Hook] → Claude が処理
Claude がツール呼び出し → [PreToolUse Hook] → ツール実行 → [PostToolUse Hook]
Claude がセッション終了 → [Stop Hook] → [SessionEnd Hook]

Hooks はあくまで「Claude Code のプロセスの外側」で動きます。Claude 自身が Hooks の処理を知ることはなく、Hook の stdout を Claude に渡したい場合は JSON を返す形で明示的に連携します。


ライフサイクルイベント一覧

2026 年 4 月時点で利用可能なイベントは以下のとおりです。

セッション系

イベント発火タイミング
SessionStartセッション開始時(最初のユーザー入力前)
SessionEndセッション終了時(/exit やウィンドウクローズ)

ターン・制御系

イベント発火タイミング
UserPromptSubmitユーザーがプロンプトを送信した直後
StopClaude が応答を終了した直後
StopFailureClaude が応答を完了できなかった場合
PreCompactコンテキスト圧縮前
PostCompactコンテキスト圧縮後

ツール呼び出し系

イベント発火タイミング特殊機能
PreToolUseツール実行前exitcode 2 でブロック、入力の書き換え可能
PostToolUseツール実行後結果へのアクセス可能
PostToolUseFailureツール実行が失敗した場合エラー情報へのアクセス可能
PermissionRequestClaude がパーミッションを要求した時承認・拒否の自動化
PermissionDeniedパーミッションが拒否された時ログ・通知向け

エージェント・タスク系

イベント発火タイミング
SubagentStartサブエージェントが起動した時
SubagentStopサブエージェントが終了した時
TaskCreatedタスクが作成された時
TaskCompletedタスクが完了した時
TeammateIdleAgent Teams のメンバーがアイドル状態になった時

その他

イベント発火タイミング
FileChanged監視ファイルが変更された時
CwdChanged作業ディレクトリが変わった時
ConfigChange設定ファイルが変更された時
InstructionsLoadedCLAUDE.md や Skills が読み込まれた時
NotificationClaude Code が通知を送ろうとした時
WorktreeCreateWorktree が作成された時
WorktreeRemoveWorktree が削除された時
ElicitationClaude がユーザーへの確認を求めた時

ハンドラの種類

Hooks には 4 種類のハンドラがあります。

command

最も汎用性が高い。シェルコマンドを実行し、stdout・exit code で Claude Code と連携します。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit",
        "hooks": [
          {
            "type": "command",
            "command": "prettier --write $CLAUDE_TOOL_RESULT_FILE_PATH 2>/dev/null || true"
          }
        ]
      }
    ]
  }
}

http

HTTP エンドポイントに POST します。ログ収集・外部 Webhook・Zapier 連携などに使います。

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "http",
            "url": "https://hooks.example.com/claude-session-end",
            "headers": {
              "X-API-Key": "$WEBHOOK_SECRET"
            }
          }
        ]
      }
    ]
  }
}

prompt

Claude Code 自身に追加プロンプトを投げ込みます。ツール実行結果を受けて「次に何をするか」を Claude に自律判断させたい場合に使います。

agent

サブエージェントを起動します。メインの Claude Code セッションとは独立したコンテキストウィンドウで処理させたい場合に使います。


設定ファイルの書き方

基本構造

// ~/.claude/settings.json または .claude/settings.json
{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolName",   // 省略可能(特定ツールにのみ発火させる場合)
        "hooks": [
          {
            "type": "command",
            "command": "your-command",
            "if": "条件式(省略可能)"
          }
        ]
      }
    ]
  }
}

matcher の書き方

matcher は特定のツール名やツール入力にマッチさせるパターンです。

"matcher": "Bash"                    // ツール名で完全一致
"matcher": "Bash(rm *)"             // ツール名 + コマンドパターン
"matcher": "Edit|Write"             // OR 条件
"matcher": ""                        // すべてのツールにマッチ(省略と同義)

複数 Hook を組み合わせる

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash(rm *)",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/block-rm.sh"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit",
        "hooks": [
          {
            "type": "command",
            "command": "cd $CLAUDE_PROJECT_DIR && npx prettier --write $CLAUDE_TOOL_INPUT_FILE_PATH 2>/dev/null || true"
          }
        ]
      }
    ],
    "SessionEnd": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Session ended at $(date)' >> ~/.claude/session.log"
          }
        ]
      }
    ]
  }
}

exit code の意味

command ハンドラの終了コードは特別な意味を持ちます。

exit code動作
0正常終了。stdout の JSON をパース(設定されていれば Claude に渡す)
2PreToolUse のみ:ツール呼び出しをブロックする
その他エラーとして扱う(Hook 自体は無視して Claude Code は続行)

PreToolUse でのブロック例

#!/bin/bash
# .claude/hooks/block-rm.sh

# $CLAUDE_TOOL_INPUT に JSON でツール入力が渡される
COMMAND=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.command')

if echo "$COMMAND" | grep -qE "^rm\s+-rf\s+/"; then
  echo "危険: / 以下への rm -rf は禁止されています" >&2
  exit 2
fi

exit 0

ツール入力の書き換え(v2.0.10 以降)

PreToolUse では Hook の stdout に hookSpecificOutput.updatedInput を返すことで、ツールへの入力を Claude に気づかれずに書き換えられます。

#!/bin/bash
# Bash ツールに渡されるコマンドにタイムアウトを自動付与する

COMMAND=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.command')

# timeout が付いていないコマンドに 30 秒のタイムアウトを追加
if ! echo "$COMMAND" | grep -q "timeout"; then
  PATCHED="timeout 30 $COMMAND"
  echo '{"hookSpecificOutput": {"updatedInput": {"command": "'"$PATCHED"'"}}}'
fi

環境変数

Hook スクリプト内では以下の環境変数が利用できます。

変数内容
$CLAUDE_PROJECT_DIRプロジェクトのルートディレクトリ
$CLAUDE_SESSION_ID現在のセッション ID
$CLAUDE_TOOL_NAME呼び出されたツール名(例: BashEdit
$CLAUDE_TOOL_INPUTツール入力の JSON 文字列
$CLAUDE_TOOL_INPUT_FILE_PATHEdit ツールで編集されたファイルパス
$CLAUDE_TOOL_RESULTPostToolUse でのツール実行結果
$CLAUDE_TOOL_RESULT_FILE_PATHPostToolUse での結果ファイルパス
$CLAUDE_ENV_FILEHook から書き込むと、以降のセッションで読み込まれる環境変数ファイルのパス

$CLAUDE_ENV_FILE は SessionStart で echo "FOO=bar" >> "$CLAUDE_ENV_FILE" のように追記すると、その変数がセッション全体で参照できるようになります。プロジェクト固有の API ベースURLや一時的なフィーチャーフラグの注入に使えます。


/hooks コマンドで対話セットアップ

JSON を直接書く代わりに、/hooks コマンドからメニュー形式で Hook を追加・編集できます。

/hooks

新規追加では「イベント → matcher → ハンドラ種別 → 実行内容」を順に選び、最終的に settings.json に書き込まれます。コマンド本体を直接書くか、.claude/hooks/*.sh に切り出してパスだけ指定するかも選べます。複雑な処理は外部 sh ファイル化してリポジトリに置くと、Git でレビュー・差分追跡できる利点があります。

既存 Hook の一覧確認にも /hooks が使えます。設定が想定どおり読み込まれているかの最初の切り分けポイントとして覚えておくと便利です。


実践パターン集

パターン 1:編集後に自動フォーマット

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "cd $CLAUDE_PROJECT_DIR && npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\" 2>/dev/null || true"
          }
        ]
      }
    ]
  }
}

パターン 2:危険コマンドのブロック

#!/bin/bash
# .claude/hooks/safety-check.sh

CMD=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.command // empty')
[ -z "$CMD" ] && exit 0

DANGEROUS_PATTERNS=(
  "rm -rf /"
  "chmod -R 777 /"
  "dd if=/dev/zero"
  "> /etc/passwd"
)

for PATTERN in "${DANGEROUS_PATTERNS[@]}"; do
  if echo "$CMD" | grep -qF "$PATTERN"; then
    echo "ブロック: '$PATTERN' を含むコマンドは実行禁止です" >&2
    exit 2
  fi
done
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/safety-check.sh" }]
      }
    ]
  }
}

パターン 3:セッション終了時に Slack 通知

#!/bin/bash
# セッション終了時に要約を Slack に送る
SUMMARY="Claude Code セッション終了: $(date '+%Y-%m-%d %H:%M') — プロジェクト: $(basename $CLAUDE_PROJECT_DIR)"

curl -s -X POST "$SLACK_WEBHOOK_URL" \
  -H 'Content-Type: application/json' \
  -d "{\"text\": \"$SUMMARY\"}"

パターン 4:ツール呼び出しの監査ログ

#!/bin/bash
LOG_FILE="$CLAUDE_PROJECT_DIR/.claude/audit.log"
echo "$(date -u +%FT%TZ) | $CLAUDE_SESSION_ID | $CLAUDE_TOOL_NAME | $(echo "$CLAUDE_TOOL_INPUT" | jq -c '.')" >> "$LOG_FILE"

パターン 5:SessionStart で開発環境の自動セットアップ

セッション開始のたびに npm install や Docker 起動などの定型作業を流す例です。SessionStart は matcher を取らないので、条件分岐はスクリプト側で書きます。

#!/bin/bash
# .claude/hooks/session-init.sh
cd "$CLAUDE_PROJECT_DIR"

# package.json があり、node_modules が無ければ install
if [ -f package.json ] && [ ! -d node_modules ]; then
  npm install
fi

# Docker Compose で DB が必要なら立ち上げ
if [ -f docker-compose.yml ] && ! docker compose ps db --status running -q | grep -q .; then
  docker compose up -d db
fi

# セッション中で使う API ベースURLを永続化
echo "API_BASE_URL=http://localhost:3000" >> "$CLAUDE_ENV_FILE"
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/session-init.sh" }]
      }
    ]
  }
}

パターン 6:UserPromptSubmit でコンテキスト注入と機密情報ブロック

UserPromptSubmit は stdin に JSON でユーザー入力が渡され、stdout に書いた内容がそのままプロンプトに追加コンテキストとして注入されます。exit 2 でブロックも可能です。

#!/bin/bash
# .claude/hooks/prompt-guard.sh
PAYLOAD=$(cat)
PROMPT=$(echo "$PAYLOAD" | jq -r '.prompt')

# 機密情報の検出(API キー / 認証情報)。検出時はブロック
if echo "$PROMPT" | grep -qE "(sk-[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}|password\s*=\s*['\"])"; then
  echo "機密情報を含むプロンプトはブロックされました" >&2
  exit 2
fi

# 現在のブランチ名と最近の変更ファイルを毎回コンテキストに追加
echo "## 作業状況"
echo "- 現在のブランチ: $(git -C "$CLAUDE_PROJECT_DIR" branch --show-current)"
echo "- 直近の変更:"
git -C "$CLAUDE_PROJECT_DIR" diff --name-only HEAD~3 2>/dev/null | head -10 | sed 's/^/  - /'
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/prompt-guard.sh" }]
      }
    ]
  }
}

CLAUDE.md に毎回書くと長くなる「直近の作業状況」のような揮発的なコンテキストは、UserPromptSubmit 側に寄せると CLAUDE.md を肥大化させずに済みます。


async: true で非同期実行

async: true を Hook 定義に追加すると、その Hook は完了を待たずバックグラウンドで実行されます(v2.0.50 前後で追加)。整形・通知・ログ送信のように「結果が即座に必要ない」処理は async 化すると体感速度が改善します。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "cd $CLAUDE_PROJECT_DIR && npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\"",
            "async": true
          }
        ]
      }
    ]
  }
}

ただし async 化してはいけないものがあります。

async に向くasync に向かない
フォーマッタ、Lint、通知、ログ送信、メトリクス記録PreToolUse のブロック判定(exit 2 が間に合わない)
Slack / Discord への完了通知Claude にコンテキストを返す処理(stdout が捨てられる)
バックグラウンドのキャッシュ更新updatedInput を返してツール入力を書き換える Hook

判断軸はシンプルで、「Claude Code の挙動を変える Hook は同期、副作用だけの Hook は async」と覚えておけば事故は防げます。


ハマりポイント・注意事項

Hook スクリプトは必ず実行可能にする

chmod +x .claude/hooks/*.sh

環境変数の展開はシングルクォートに注意 JSON 内の command はシェル展開されます。$CLAUDE_PROJECT_DIR は動的に展開されますが、ダブルクォートを含む場合はエスケープが必要です。

PreToolUse のブロックは Claude に見える exit code 2 でブロックされた場合、Claude Code はブロックされたことを知らされます。Claude が同じことを別の方法で試みる場合があります。完全にやめさせるには CLAUDE.md にも明示的に禁止事項を書くのが確実です。

Hook の実行は直列 同一イベントに複数の Hook が定義されている場合、上から順番に実行されます(並列実行ではありません)。重い処理をバックグラウンドで走らせたい場合は & でデタッチしてください。

/hooks コマンドで確認 現在有効なすべての Hook 設定は /hooks コマンドで確認できます。設定が反映されていない場合はここで確認するのが最初のステップです。


参考リンク