Claude Media
Claude Code Hooksとは — 33イベントの選び方とexit codeの規則

Claude Code Hooksとは — 33イベントの選び方とexit codeの規則

Claude Code Hooksは、ツール実行やセッション開始などの節目で自分のコマンドを必ず走らせる仕組みです。33イベントの一覧、exit codeの意味、動かないときの切り分けをまとめます。

Claude Code Hooksとは、ツール実行の前後・セッション開始・応答完了といった節目で、自分が登録したコマンドをClaude Codeに必ず実行させる仕組みです。登録先は settings.json の hooks キー。イベントの情報はJSONでコマンドの標準入力(stdin)に渡され、終了コード(exit code)で結果を返します。

CLAUDE.mdの指示はClaudeが読んで従うものなので、守られない余地が残ります。Hooksは条件が合えば毎回動きます。整形・遮断・通知のような「忘れてはいけない処理」を任せる場所です。

用途

Hooksでできる4つのこと

  • 遮断する

    PreToolUse でツール実行を止められます。.env への書き込みや危険なコマンドが典型です。

  • 書き換える

    PreToolUse の updatedInput で、ツールに渡る引数そのものを差し替えられます。

  • 文脈を足す

    SessionStart や UserPromptSubmit の出力は、Claudeが読むコンテキストに入ります。

  • 副作用を起こす

    編集後の整形、テスト実行、完了通知、ログ記録など、Claudeの外側の処理です。

最初の1個を手順どおりに動かしたい場合はClaude Code Hooksの設定方法、用途別のコピペ用レシピはClaude Code Hooks実例カタログが受け持ちます。ここでは、イベントの選び方、設定の置き場所、終了コードの規則、動かないときの切り分けを扱います。

最小の動くHooks — 3階層の設定を読む

Hooksの設定は「イベント → matcherグループ → handler」の3階層です。次の例は、Edit か Write が成功するたびに、編集したファイルのパスをログへ追記します。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path // empty' >> /tmp/hooks-demo.log"
          }
        ]
      }
    ]
  }
}

PostToolUse がイベント、matcher が対象ツールの絞り込み、hooks 配列の中身がhandlerです。command のhandlerには、イベントのJSONがstdinで届きます。jq はそのJSONから値を取り出しています。

登録できたかは、セッション内の /hooks で見ます。このメニューは閲覧専用で、追加や変更は設定ファイルを直接編集します。

/hooks

ファイルを書き換えた後は、通常は数秒で反映されます。反映されないときだけ、セッションを再起動します。

設定はどこに書くか — 7つの置き場所

置き場所でスコープと共有範囲が決まります。同じイベントのhookを複数の場所に置くと、すべてが実行されます。

置き場所スコープ共有
~/.claude/settings.jsonスコープ自分の全プロジェクト共有不可(自分のマシンだけ)
.claude/settings.jsonスコープそのプロジェクト共有可(リポジトリにコミット)
.claude/settings.local.jsonスコープそのプロジェクト共有不可(Claude Codeが書き込むときはgitignore対象)
管理ポリシー設定スコープ組織全体共有管理者が制御
プラグインの hooks/hooks.jsonスコーププラグインの有効中共有プラグインに同梱
SkillのfrontmatterスコープSkill起動後、セッションの終わりまで共有Skillファイル内
サブエージェントのfrontmatterスコープそのサブエージェントの実行中共有エージェントファイル内

チームで同じ遮断ルールを使うなら .claude/settings.json です。個人の通知設定なら ~/.claude/settings.json が向きます。同じhandlerを複数の設定ファイルに書いても、実行は1回に統合されます。

管理者が allowManagedHooksOnly を設定した環境では、ユーザー・プロジェクト・ローカル・プラグインのhookはブロックされます。/hooks に「Only hooks from managed settings run here」と出たら、これが原因です。

全hookを一時的に止めたいときは、設定に "disableAllHooks": true を書きます。1回の起動だけ止めるなら、--settings '{"disableAllHooks": true}' を渡します。個別のhookだけを残したまま無効にする方法はありません。

落とし穴

信頼していないフォルダでは、対話とclaude -pで挙動が逆になる

対話セッションでは、ワークスペースの信頼ダイアログを承諾するまで、すべての設定ファイルのhookが保留されます。自分の ~/.claude/settings.json も含みます。

claude -p やAgent SDKのセッションではダイアログが出ず、フォルダは信頼済みとして扱われます。他人のリポジトリに .claude/settings.json のhookがあると、そのまま走ります。実行前に中身を読むか、--settings '{"disableAllHooks": true}' を付けてください。

33イベントの一覧 — 発火のタイミングと止められるか

イベントは33種類です。「止められる」列は、exit code 2が処理を止める効果を持つかどうかを表します。×のイベントは、すでに起きたことを扱うか、止める手段を持たないイベントです。

セッションと設定まわり(12)

イベント発火タイミング止められる
SessionStart発火タイミングセッションの開始・再開止められる×
Setup発火タイミング--init-only、または -p での --init / --maintenance止められる×
SessionEnd発火タイミングセッションの終了止められる×
InstructionsLoaded発火タイミングCLAUDE.mdや .claude/rules/*.md の読み込み止められる×
ConfigChange発火タイミングセッション中の設定ファイル変更止められる○(policy_settings を除く)
CwdChanged発火タイミング作業ディレクトリの変更止められる×
DirectoryAdded発火タイミング/add-dir などでディレクトリを追加した後止められる×
FileChanged発火タイミング監視対象ファイルの変更止められる×
PreCompact発火タイミングコンテキスト圧縮の直前止められる○
PostCompact発火タイミングコンテキスト圧縮の完了後止められる×
PreModelSwitch発火タイミングモデル切り替えの直前止められる○
PostModelSwitch発火タイミングモデルが切り替わった後止められる×

1ターンの入出力(6)

イベント発火タイミング止められる
UserPromptSubmit発火タイミングプロンプト送信後、Claudeが処理する前止められる○
UserPromptExpansion発火タイミング入力したコマンドがプロンプトに展開される前止められる○
MessageDisplay発火タイミング応答テキストの表示中止められる×(元の表示のまま)
Stop発火タイミングClaudeの応答完了止められる○(続行させる)
StopFailure発火タイミングAPIエラーでターンが終わったとき止められる×
Notification発火タイミングClaude Codeが通知を出したとき止められる×

ツール呼び出し(6)

イベント発火タイミング止められる
PreToolUse発火タイミングツール実行の直前止められる○
PermissionRequest発火タイミング権限の判断が要るとき止められる×(JSONで拒否する)
PermissionDenied発火タイミングautoモードがツール呼び出しを拒否したとき止められる×
PostToolUse発火タイミングツールが成功した後止められる×(stderrはClaudeに届く)
PostToolUseFailure発火タイミングツールが失敗した後止められる×(stderrはClaudeに届く)
PostToolBatch発火タイミング並列ツール呼び出し一式の完了後、次のモデル呼び出しの前止められる○

サブエージェント・タスク・外部連携(9)

イベント発火タイミング止められる
SubagentStart発火タイミングサブエージェントの起動止められる×
SubagentStop発火タイミングサブエージェントの完了止められる○
TaskCreated発火タイミングTaskCreate でタスクを作るとき止められる○(作成を取り消す)
TaskCompleted発火タイミングタスクを完了にするとき止められる○
TeammateIdle発火タイミングエージェントチームのメンバーが待機に入る直前止められる○
WorktreeCreate発火タイミングworktreeの作成。標準のgit処理を置き換える止められる0以外の終了でworktree作成が失敗
WorktreeRemove発火タイミングWorktreeCreate で作ったworktreeの削除止められる0以外の終了で、ディレクトリが残れば削除が失敗
Elicitation発火タイミングMCPサーバーが入力を求めたとき止められる○(要求を辞退)
ElicitationResult発火タイミングユーザーの応答を返す前止められる○(辞退扱い)

33種類すべてを覚える必要はありません。まずは PreToolUse・PostToolUse・Stop・Notification・SessionStart の5つで足ります。残りは「その節目で何かしたい」と思ったときに引く索引です。

やりたいことから選ぶ

やりたいことイベント要点
編集後に整形・lintするイベントPostToolUse(Edit|Write)要点stdinの tool_input.file_path を整形コマンドへ渡す
危険なコマンドや保護ファイルへの書き込みを止めるイベントPreToolUse要点理由をstderrに書いてexit 2
圧縮で消えた文脈を戻すイベントSessionStart(compact)要点stdoutがClaudeの文脈に入る
完了条件を満たすまで止めないイベントStop要点exit 2、または prompt / agent タイプで判定
入力待ち・完了を通知するイベントNotification / Stop要点OS通知やWebhookを呼ぶ
権限ダイアログに自動で答えるイベントPermissionRequest要点JSONの decision.behavior で応答
設定変更を監査するイベントConfigChange要点exit 2で反映を止められる

「止めたいなら Pre*、観察して副作用を起こしたいなら Post* と Stop」が、選ぶときの基本の軸です。

Stop に付ける完了条件の判定は、組み込みの /goal コマンドが同じことをします。公式ドキュメントでは、セッション単位のpromptタイプのStop hookへの近道として紹介されています。設定を書かずに済むなら、まず /goal を試す選択肢があります。

matcherの3つの評価ルール

matcher は、書いた文字の種類で評価方法が切り替わります。

matcherの書き方評価例
"*"、空文字、省略評価すべてに一致例そのイベントの全発火
英数字・_・-・空白・,・| だけ評価完全一致。| か , で列挙例Bash / Edit|Write
それ以外の文字を含む評価非アンカーのJavaScript正規表現例^Notebook / mcp__memory__.*

Edit|Write は正規表現ではなく、2つの名前の列挙です。逆に正規表現になった側は部分一致で、Edit.* は NotebookEdit にも当たります。全体一致にしたいときは ^Edit$ と書きます。matcherは大文字小文字を区別するので、bash では Bash に当たりません。

matcherが見るものは、イベントごとに違います。

イベントmatcherが絞り込む対象値の例
PreToolUse / PostToolUse / PostToolUseFailure / PermissionRequest / PermissionDeniedmatcherが絞り込む対象ツール名値の例Bash、Edit|Write、mcp__.*
SessionStartmatcherが絞り込む対象開始の経緯値の例startup、resume、clear、compact、fork
SessionEndmatcherが絞り込む対象終了の理由値の例clear、resume、logout、prompt_input_exit、other
PreCompact / PostCompactmatcherが絞り込む対象圧縮のきっかけ値の例manual、auto
SubagentStartmatcherが絞り込む対象エージェントの種類値の例Explore、Plan、カスタムエージェント名

FileChanged と StopFailure は、完全一致の文字種がさらに狭く、英数字・_・| だけです。ハイフン・空白・カンマを含めた時点で正規表現として扱われます。

ifでツール名の先まで絞る

if フィールドは、権限ルールと同じ書き方(Bash(git *) や Edit(*.ts))で、引数まで見て発火を絞ります。

{
  "type": "command",
  "if": "Bash(git *)",
  "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-git-policy.sh"
}

使える場所は、ツール系の5イベント(PreToolUse・PostToolUse・PostToolUseFailure・PermissionRequest・PermissionDenied)に限られます。それ以外のイベントに if を付けたhookは、一度も実行されません。エラーも出ません。

if に書けるルールは1つです。&& や ||、リストは使えません。複数の条件が要るなら、handlerを分けます。コマンドの中身を解析できないときは、条件に関係なくhookが走ります。確実に遮断したい処理は、if ではなく権限システムに任せる設計です。

handlerの5つのtype

handlerの type は5種類です。既定のタイムアウトは種類ごとに違います。

type実行されるもの既定のタイムアウト
command実行されるものシェルコマンド。stdinにJSONが届く既定のタイムアウト600秒
http実行されるもの指定URLへのPOST(本文がイベントのJSON)既定のタイムアウト600秒
mcp_tool実行されるもの接続済みMCPサーバーのツール既定のタイムアウト600秒
prompt実行されるものClaudeモデルによる1回きりの判定既定のタイムアウト30秒
agent実行されるものRead・Grep・Globなどを使えるサブエージェントによる検証既定のタイムアウト60秒

prompt と agent は、機械的なルールでは書けない判定向けです。モデルは { "ok": true/false } 形式で結果を返します。model を省略すると、Claude Codeがバックグラウンド処理に使うモデルで評価されます。agent は最大50ターンまで動けますが、実験的機能とされ、本番運用では command が勧められています。詳しい使い方はagent hookでテストが通るまで止めない、prompt hookでStopを判断させる、HTTPエンドポイントで受けるにあります。

同じイベントに登録したhookは並列に実行されます。prompt と agent が13件以上あるときは、12件ずつ走ります(同時実行の上限)。

タイムアウトは、イベントによって短く設定されています。

対象command / http / mcp_tool の既定
UserPromptSubmit・PreModelSwitch・PostModelSwitchcommand / http / mcp_tool の既定30秒
MessageDisplaycommand / http / mcp_tool の既定10秒
SessionEndcommand / http / mcp_tool の既定全hookで1.5秒を分け合う。個別の timeout を長くすると、最大60秒まで予算が伸びる
上記以外command / http / mcp_tool の既定600秒

毎プロンプトの前に走る UserPromptSubmit に重い処理を置くと、応答の開始が遅れます。長い処理は async: true でバックグラウンドへ逃がせますが、その場合は結果で遮断も書き換えもできません。

stdin・exit code・JSONの規則

hookが受け取るもの

全イベントに共通するstdinのフィールドは、session_id・transcript_path・cwd・permission_mode・hook_event_name などです。その上に、イベント固有の値が載ります。PreToolUse なら tool_name・tool_input・tool_use_id です。サブエージェント内で発火したhookには、agent_id と agent_type も付き、メインセッションと見分けられます。

イベントごとにJSONの形が違うので、実装前に手元でサンプルを流して確かめると確実です。

echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh
echo $?

exit codeの意味

exit code意味動作
0意味成功動作stdoutのJSONがあれば適用。SessionStart・UserPromptSubmit などではstdoutがClaudeの文脈に入る
2意味ブロッキングエラー動作「止められる」イベントでは処理を止め、stderrが理由としてClaudeに返る
それ以外意味非ブロッキングエラー動作処理は続行。stderrの先頭行が画面に出る

exit 2の効果は、前掲の33イベントの表のとおりイベントごとに違います。例外が3つあります。

  • PermissionRequest:exit 2は解釈されず、権限フローが進みます。拒否するときはJSONの hookSpecificOutput.decision を使います
  • SessionStart・SubagentStart・SessionEnd など:exit 2でも止まらず、stderrが利用者の画面に出るだけです
  • Setup・Notification・InstructionsLoaded など:終了コードもstderrも無視されます

落とし穴

exit 1では止まらない

Unixの慣習ではexit 1が失敗ですが、hookでは非ブロッキングエラーで、処理は続行します。ルールを強制するhookは、exit 2 で終えてください。スクリプトのパスを打ち間違えて起動できなかったときも、同じ非ブロッキングエラーです。

起動失敗や失敗時にも止めたい場合は、command と http のhandlerに "onFailure": "block" を付けます(Claude Code v2.1.295以降)。

PreToolUse を止める最小のスクリプトは、次のようになります。

#!/bin/bash
input=$(cat)
command=$(jq -r '.tool_input.command' <<<"$input")
 
if [[ "$command" == rm* ]]; then
  echo "Blocked: rm commands are not allowed" >&2
  exit 2
fi
 
exit 0

rm で始まるコマンドは、stderrの文言がClaudeに返ってブロックされます。それ以外はexit 0で、通常の権限フローに進みます。

JSON出力で細かく制御する

exit codeでは「止める」か「何もしない」かの2択です。細かい制御は、exit 0でstdoutにJSONオブジェクトを1つだけ出して行います。

目的イベント書くもの
ツール実行を許可・拒否・確認・保留イベントPreToolUse書くものhookSpecificOutput.permissionDecision(allow / deny / ask / defer)
ツールの引数を書き換えるイベントPreToolUse書くものhookSpecificOutput.updatedInput(入力オブジェクト全体を置き換える)
ブロックして理由を返すイベントUserPromptSubmit・PostToolUse・Stop など書くものトップレベルの decision: "block" と reason
Claudeの文脈に足すイベントSessionStart など書くものhookSpecificOutput.additionalContext
権限ダイアログに代わりに答えるイベントPermissionRequest書くものhookSpecificOutput.decision.behavior(allow / deny)
処理全体を止めるイベント全イベント共通書くものcontinue: false と stopReason

複数の PreToolUse hookが違う判定を返したときの優先順位は、deny > defer > ask > allow です。updatedInput で同じツールの入力を複数のhookが書き換えると、どの書き換えが採用されるかは保証されません。書き換えるhookは、1つのツールにつき1つに絞ります。

exit 2とJSONを混ぜた場合、exit 2のブロックが優先されます。stdoutに permissionDecision: "allow" を書いても、覆りません。方式はhookごとに1つ選ぶのが安全です。

追加の文脈(additionalContext など)とstdoutは、10,000文字が上限です。超えた分はファイルに保存され、Claudeには保存先のパスと冒頭の2,000文字までのプレビューが渡ります。

よくあるつまずきと切り分け

切り分け

hookが動かないときの切り分け

  1. Q/hooks に、そのイベント名で表示されていますか?
    答えが表示されていない

    JSONの構文(末尾のカンマやコメントは使えません)と、置き場所を見直します。数秒待っても出ないときはセッションを再起動します。Only hooks from managed settings run here と出ている場合は、管理者が allowManagedHooksOnly を設定しています。

    答えがはい
  2. Qワークスペースの信頼ダイアログを承諾しましたか?
    答えが承諾していない

    承諾するまで、設定ファイルのhookはすべて保留されます。新しいリポジトリで「グローバル設定が効かない」ときの典型です。

    答えがはい
  3. Qmatcherの綴りと大文字小文字は、ツール名と一致していますか?
    答えが一致していない

    bash ではなく Bash のように、ツールの正式名で書きます。if を付けたイベントが、ツール系の5イベントかも確認します。

    答えがはい

それでも動かないとき

サンプルJSONをパイプで流してスクリプトを単体で試し、終了コードを見ます。権限が付いていなければ chmod +x します。起動時に出力先を渡す --debug-file でログを取り、別のターミナルから追えます。

動いているのに効かない場合は、次の表で原因を探します。

症状原因対処
JSONが無視される、パースエラーが出る原因シェルのプロファイルが、非対話でも読み込まれて echo を出し、stdoutの先頭が { でなくなる対処プロファイルの echo を対話シェル限定にする(if [[ $- == *i* ]]; then ...)。jq でJSONを組み立てる
jq: command not found原因jq が未インストール対処インストールするか、PythonやNodeでJSONを読む
スクリプトが起動しない原因パスが相対、または実行権限がない対処"$CLAUDE_PROJECT_DIR"/.claude/hooks/... と書き、chmod +x する。args を付けるとシェルを介さず直接起動する
PostToolUse でexit 2しても取り消せない原因ツールはすでに実行済み対処取り消したい処理は PreToolUse に置く。PostToolUse のstderrはClaudeに届くので、失敗を伝える用途には使える
PreToolUse が時間切れしたのに実行された原因タイムアウトしたhookは、遮断の判定を返さない対処遮断に使うhookは "onFailure": "block" を付ける
Stop hookが何度も続行させる原因続行が8回続くと、Claude Codeが次のブロックを上書きしてターンを終える対処stdinの stop_hook_active が true なら早めにexit 0する。上限は CLAUDE_CODE_STOP_HOOK_BLOCK_CAP で変更できる
WindowsのPowerShell hookで止まらない原因Git Bashの有無で既定のシェルが変わる対処"shell": "powershell" と "onFailure": "block" を併記する

Stop hookの無限再発火は/goal Stop hookが止まらない、PowerShellの件はPowerShell hookがexit 2で止まらない問題に、症状別の詳細があります。

よくある質問

HooksとCLAUDE.mdは、どう使い分けますか?

「必ず実行する処理」はHooks、「判断の指針」はCLAUDE.mdです。CLAUDE.mdはClaudeが読んで従う前提なので、解釈の余地があります。Hooksは節目で自動実行されます。

bypassPermissionsモードでもHooksは効きますか?

効きます。PreToolUse は権限モードの確認より前に発火します。permissionDecision: "deny" を返すと、bypassPermissions や --dangerously-skip-permissions でもツールが止まります。逆に、hookの allow で設定側のdenyルールを覆すことはできません。

サブエージェントの中のツール実行にも発火しますか?

発火します。設定ファイルやプラグインのhookは、サブエージェント内でも同じように走ります。stdinの agent_id と agent_type で見分けます。サブエージェントの仕組みはSub-agents完全ガイドにあります。

hookからスラッシュコマンドやツールを呼べますか?

command のhookは、stdout・stderr・exit codeだけで結果を返します。スラッシュコマンドもツール呼び出しも起動できません。Claudeに伝えたいことは、additionalContext として文脈に足します。

settings.json全体の書き方は、どこで確認できますか?

hooks 以外のキーも含めた全体は、settings.jsonの完全ガイドで扱っています。Claude Code自体の位置付けはClaude Codeの全体像から辿れます。

まとめ

Hooksを設計する手順は、3つの問いに落ちます。止めたいのか(Pre* とexit 2)、観察して副作用を起こしたいのか(Post*・Stop・Notification)、機械的なルールで書けない判定なのか(prompt / agent)です。

最初の1個は、PostToolUse の整形か PreToolUse の保護ファイル遮断が、効果と失敗の少なさの両面で扱いやすい選択です。動いたら、実例カタログから自分の運用に近いレシピを足していけます。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/hooks
  2. code.claude.com/docs/en/hooks-guide
この記事を共有:XはてブLinkedIn