原典: Hooks reference / 日本語版
一行要約
hooks-guide が「何ができるか」を説明するのに対し、こちらは exit code 2 がイベントごとにどう振る舞うか、イベントごとの決定制御の形、ハンドラ種別ごとのフィールドという、書くときに毎回引く3つの表。
解説と例は hooks-guide を参照。この文書はそこに載っていないリファレンス部分だけを収録している。
plugin は JavaScript の関数として hook を登録することもでき、そういう plugin を mod と呼ぶ。 mod の関数 hook は別の文書(React to events)の扱いで、この文書の hook は mod と並んで動き続ける。
要点
イベントの発火頻度(3つの周期)
- セッションに1回 —
SessionStart/SessionEnd - ターンに1回 —
UserPromptSubmit/Stop/StopFailure。UserPromptSubmitは自分が打ったプロンプトだけで発火するのではない —— scheduled task の発火(/loopの各回を含む)、background subagent が起動元へ報告するとき、別のセッションが main の会話へ送ったメッセージでも走る - agentic ループ内の全ツール呼び出し —
PreToolUse/PostToolUse。ただしEndConversationの呼び出しだけは両方を飛ばす
発火の例外(2つ)
SubagentStopは自分で起こした subagent だけのものではない。 prompt suggestions や/btwの side question のように、Claude Code 自身の機能が内部で走らせるエージェントが 終わったときにも発火する。このときのagent_typeはセッション自身の型(--agentやagent設定で与えた名前)で、何も指定していないセッションでは空文字列になる。 エージェント型を並べたmatcherは空文字列に当たらない —— 空も拾うのは、matcher を 省いたときと""/"*"、および空文字列に一致する正規表現だけInstructionsLoadedは、Project instructions の設定でAGENTS.mdを直接読んだときには 発火しない。 発火するのはCLAUDE.mdがAGENTS.mdを import したとき(他の import と 同じくload_reasonはinclude)と、CLAUDE.mdがAGENTS.mdへの symlink のとき (ふつうのCLAUDE.md読み込みとして)
hook が解決される順序
PreToolUse を例にすると、イベント発火 → matcher → if → ハンドラ実行 → 結果の適用
の順に絞り込まれる。matcher を省くか "*" にすると、そのイベントの全発火でグループが動く。
if はプロセスを起こす前のフィルタで、これが一致しなければハンドラは spawn されない
(if を書かなければグループ内の全ハンドラが走る)。
Exit code 0 with no output means the hook has no decision to report, so the tool call continues through the normal permission flow. The hook can deny the call, but staying silent doesn’t approve it.
出力なしの exit code 0 は「この hook に報告すべき決定は無い」という意味で、ツール呼び出しは通常の権限フローへ進む。hook は呼び出しを拒否できるが、黙っていることは承認にはならない。
全 hook が受け取る共通フィールド
command hook は stdin、HTTP hook は POST ボディで受け取る。
| フィールド | 内容 |
|---|---|
session_id | 現在のセッション識別子 |
prompt_id | ユーザープロンプトを識別する UUID。OpenTelemetry の prompt.id と一致するので、hook 出力とテレメトリを1プロンプト単位で突き合わせられる。最初のユーザー入力までは無い。v2.1.196 以降 |
transcript_path | 会話 JSON へのパス。非同期に書かれるので現在のターンの最新メッセージがまだ無いことがある。現ターンの最終 assistant テキストが要るなら、transcript を読まずに Stop / SubagentStop の last_assistant_message を使う |
cwd | 現在の作業ディレクトリ |
scratchpad_dir | セッションの scratchpad ディレクトリ。scratchpad が無いか temp が使えないときは欠ける。v2.1.257 以降 |
permission_mode | "default" / "plan" / "acceptEdits" / "auto" / "dontAsk" / "bypassPermissions"。表示が Manual のモードは "default" で届く。"manual" にはならない |
effort | level("low" 〜 "max")を持つオブジェクト。モデルが対応しない effort を要求した場合は、実際に使われた降格後の値。届くのはツール利用の文脈で発火するイベント(PreToolUse / PostToolUse / Stop / SubagentStop など)で、かつモデルが effort に対応しているとき。hook コマンドと Bash ツールからは $CLAUDE_EFFORT でも読める |
hook_event_name | 発火したイベント名 |
agent_id | subagent の識別子(subagent 文脈のみ) |
agent_type | エージェント名("Explore" など)。subagent では、その型がセッションの --agent より優先される。SubagentStop では空文字列で届くことがある(上記「発火の例外」) |
モデル名を取る道は2つしかない。 model フィールドを受け取れるのは SessionStart だけで、
しかも必ず在るとは限らない。$CLAUDE_MODEL という環境変数は存在しない。 親の環境は継承するので
自分で $ANTHROPIC_MODEL を設定していれば読めるが、セッション中に /model で切り替えても
その値は変わらない。 セッション中のモデルを追いたいなら PostModelSwitch を使う ——
PreModelSwitch / PostModelSwitch だけは model の代わりに from_model と to_model を
受け取る。なお OTEL_* のエクスポータ変数は、hook を含む全サブプロセスから
取り除かれる。CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 を設定すると、剥がされる変数がさらに増える。
端末に直接書けない。 macOS と Linux では、command hook は制御端末を持たない独自のセッションで
走る。hook とその子プロセスは /dev/tty を開けず、エスケープシーケンスを直接送れない
(Windows に /dev/tty は無い)。ユーザーに見せたいなら systemMessage、デスクトップ通知・
ウィンドウタイトル・ベルなら terminalSequence を JSON で返す。
exit code 2 の挙動(イベント別)
exit code 2 は「止めろ、これをするな」の合図。 ただし効果はイベントで違う。
ブロックできるもの:
| イベント | exit 2 の効果 |
|---|---|
PreToolUse | ツール呼び出しをブロック |
UserPromptSubmit | プロンプトをブロックし、Claude に届かせない(本文がどこに残るかは下の「ブロックしたプロンプトが残すもの」) |
UserPromptExpansion | 展開をブロック |
Stop | Claude が止まるのを防ぎ、会話を続けさせる |
SubagentStop | subagent が止まるのを防ぐ |
TeammateIdle | teammate が idle になるのを防ぐ |
TaskCreated | タスク作成をロールバックする |
TaskCompleted | 完了マークを防ぐ |
ConfigChange | 設定変更をブロック(policy_settings を除く) |
PreCompact | compaction をブロック |
Elicitation | elicitation を拒否 |
ElicitationResult | 応答をブロック(action が decline になる) |
WorktreeCreate | 非ゼロの exit code はすべて作成を失敗させる |
WorktreeRemove | 非ゼロの exit code は、ディレクトリが残っていれば削除を失敗させる(git の後始末にも落ちない) |
PostToolBatch | 次のモデル呼び出しの前に agentic ループを止める |
PreModelSwitch | モデルの切り替えをブロックする |
ブロックできないもの:
| イベント | exit 2 の効果 |
|---|---|
PostToolUse / PostToolUseFailure | stderr を Claude に見せる(ツールは既に走っている) |
PermissionDenied | exit code と stderr は無視される。 JSON の retry: true を使う |
PermissionRequest | exit 2 は効かない。 権限フローはそのまま進む。拒否するなら decision オブジェクトを使う |
SubagentStart / SessionStart / SessionEnd / CwdChanged / FileChanged / PostCompact / PostModelSwitch | stderr をユーザーにだけ見せる |
Notification / Setup | exit code も stderr も無視 |
DirectoryAdded | stderr は debug ログへ |
InstructionsLoaded | exit code は無視 |
StopFailure | 出力も exit code も無視(terminalSequence だけは効く) |
MessageDisplay | 元のテキストが表示される |
SessionStart / SubagentStart / PostModelSwitch の exit 2 の stderr は、transcript に <hook name> hook error として出るだけ —— Claude には見えず、セッションや subagent はそのまま進む。SubagentStart の分は親の会話ではなく subagent 自身の transcriptに出る。
exit code と JSON の関係:
- JSON はどの exit code でも読まれる。 exit 0 でなくても、schema 検証を通ればそちらが結果を決め、エラーにもならない
- exit 2 のブロックだけは JSON で覆せない。
permissionDecision: "allow"を返しても止まる。JSON が schema 検証に落ちてもブロックは残り、理由には stderr が使われる(v2.1.214 より前は非ブロックのエラー扱いだった) - stdout が JSON として読まれるのは
{で始まり}で終わるときだけ。{で始まっても}で終わらなければただの文字列、JSON 配列や引用符つき文字列もただの文字列 - JSON として読もうとして失敗したら、exit 2 以外のすべてで非ブロックのエラーになる。 context に足すイベントでもテキストは足されない(v2.1.248 より前はプレーンテキストとして扱っていた)
- exit 1 は止まらない。 方針を強制したいなら
exit 2(例外は worktree の2イベントで、非ゼロならすべて失敗させる) - 起動できなかった hook も同じ非ブロック扱い。 パスの打ち間違いは
Failed with non-blocking status code: ... No such file or directoryとして1行出るだけなので、門番のつもりの hook が黙って無効になる
タイムアウトしたとき(async: true を除き、hook はキャンセルされ出力は捨てられる):
PreToolUseのcommand/http/mcp_toolhook はブロックしない。 止まったままの hook を門番として当てにできない- Agent SDK の callback hook は、タイムアウトするとツール呼び出しをブロックする
PreModelSwitchはタイムアウトでモデル切り替えをブロックする
モデル切り替えの2イベント(v2.1.251 以降):
PreModelSwitch | PostModelSwitch | |
|---|---|---|
| タイミング | 要求された切り替えを適用する前 | セッションのモデルが変わった後 |
| 発火する経路 | /model とそのピッカー、Option+P / Alt+P、/config のモデル設定、fast mode の切り替えでモデルが変わるとき、SDK / Remote Control の set_model と apply_flag_settings | 上の全部に加えて、自動フォールバック、opusplan の plan mode 出入り、resume 時のモデル復元 |
| 発火しない | Claude Code が自分で起こした切り替え(自動フォールバック、resume の復元)—— これらは PostModelSwitch にだけ届く | fallback model chain の代打(1ターンで終わり、セッションのモデルは変わらないため) |
| 同期性 | 同期。ブロックできる。タイムアウトもブロックになる(既定30秒。command / http / mcp_tool のみ) | 非同期。切り替えは即座に効き、hook は裏で走る |
| 決定制御 | exit 2 かトップレベル decision: "block"、または permissionDecision("allow" / "deny" / "ask"。"defer" も updatedInput も additionalContext も受け付けない)。複数の hook が割れたら deny > ask > allow | additionalContext(または exit 0 のプレーンテキスト stdout)が次のリクエストで Claude に届く |
"ask"の確認が出るのは対話セッションの/modelだけ。-p・/config・set_modelでは拒否として扱われるsystemMessageは決定によらずユーザーに表示される —— 切り替えの費用を知らせるだけの hook は、これを返して exit 0 すればよいPostModelSwitchの出力は、次のプロンプト送信から5秒以内に終わらなければそのリクエストには乗らず、次の次に回される。 次のリクエストまでに何度も切り替わったら、最後の切り替え先の分だけが届く
A PreModelSwitch hook that doesn’t respond before its timeout blocks the switch. On PreToolUse, by contrast, a timed-out command hook lets the tool call continue.
タイムアウトまでに応答しない
PreModelSwitchhook は、切り替えをブロックする。対してPreToolUseでは、タイムアウトした command hook はツール呼び出しを通してしまう。
matcher は「切り替え先」の正規名に当たる。 [1m] 接尾辞は無視される。 opus のような alias、日付つきのモデル ID、Amazon Bedrock のようなベンダ固有の ID はすべて同じ正規名に解決されるので、claude-opus-5 と書けば Opus 5 のどの綴りにも当たる。.*opus.* のような正規表現も書ける。
正規名が決まらないとき(LLM gateway だけが知っている独自のモデル ID など)は、matcher によらず全部の PreModelSwitch hook が走る。だからブロックする hook は matcher に頼らず、入力の to_model を自分で見る。
決定制御の形(イベント別)
| イベント | 決定の形 | 主なフィールド |
|---|---|---|
UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange, PreCompact | トップレベルの decision | decision: "block", reason。Stop と SubagentStop は hookSpecificOutput.additionalContext も受け付ける —— エラー扱いにせず会話を続けたまま feedback を渡す用 |
TeammateIdle, TaskCreated, TaskCompleted | exit code または continue: false | {"continue": false, "stopReason": "..."} |
PreToolUse | hookSpecificOutput | permissionDecision(allow/deny/ask/defer), permissionDecisionReason("ask" では確認プロンプトでユーザーに見せる。ただし誰も答えられない -p で Claude Code が呼び出しを拒否したときは、Claude がツール結果でこの理由を読む。"deny" では Claude に見え、"allow" / "defer" では debug log にしか書かれない) |
PermissionRequest | hookSpecificOutput | decision.behavior(allow/deny) |
PermissionDenied | hookSpecificOutput | retry: true |
WorktreeCreate | パスを返す | command hook は stdout にパスを出す。HTTP hook は hookSpecificOutput.worktreePath |
Elicitation / ElicitationResult | hookSpecificOutput | action(accept/decline/cancel), content |
MessageDisplay | hookSpecificOutput | displayContent(表示のみ置き換え。transcript は変わらない) |
SessionStart, Setup, SubagentStart | context のみ | additionalContext。SessionStart は加えて initialUserMessage, watchPaths, sessionTitle, reloadSkills |
PreModelSwitch / PostModelSwitch | 上の「モデル切り替えの2イベント」の表を参照 | permissionDecision / systemMessage |
WorktreeRemove, Notification, SessionEnd, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, DirectoryAdded, FileChanged | なし | 副作用のみ |
内容の書き換え:
| イベント | 書き換えフィールド |
|---|---|
PreToolUse | hookSpecificOutput 直下の updatedInput がツール引数を置き換える |
PermissionRequest | decision オブジェクトの中の updatedInput |
PostToolUse | updatedToolOutput がツール結果を置き換える |
UserPromptSubmit | プロンプトは置き換えられない。 additionalContext の注入のみ |
UserPromptSubmit が受け取る prompt は、画面に出ていた文字列とは違う。 [Pasted text #N]
に畳まれていた貼り付けはその場に展開されて届く。さらに貼り付けを Claude 向けに印づける設定の
セッションでは、展開された部分が <pasted_content id="…"> 行と </pasted_content id="…"> 行に
挟まれる。プロンプトを parse する hook は、この2行を勘定に入れる。
ブロックしたプロンプトは Claude には届かないが、どこからも消えるわけではない(以前の取得で追加)。既定では、利用者に見せるブロックメッセージの末尾に Original prompt: と本文が付き、そのメッセージが transcript ファイルに書かれる。hookSpecificOutput に "suppressOriginalPrompt": true を入れればメッセージから外せる(decision: "block" でも exit 2 でも。JSON を出さない exit 2 は必ず本文を含む)。ただし変わるのはメッセージだけで、本文は transcript やプロンプト履歴に残りうる:
The submitted text can still appear in local files such as the session transcript and your prompt history, so a blocking hook isn’t a way to keep a secret off disk.
送信した本文は、セッションの transcript やプロンプト履歴のようなローカルファイルにまだ現れうる。だからブロックする hook は、秘密をディスクに残さないための手段にはならない。
UserPromptSubmit は session_title も受け取る(カスタムタイトルがあるとき。生成タイトルは入らない)。
全 JSON 出力に共通のフィールド
| フィールド | 既定 | 内容 |
|---|---|---|
continue | true | false で Claude が完全に停止する。イベント固有の決定より優先される |
stopReason | — | continue: false のときユーザーに見せるメッセージ。会話に残るので、会話が続けば Claude にも見える |
suppressOutput | false | 効果は無い。 フィールドは受け付けるが何もしない —— 成功した hook の stdout はもともと transcript に出ず、debug ログにだけ残る |
systemMessage | — | ユーザーに見せる警告。Agent SDK と --output-format stream-json では SDKInformationalMessage として届くことがある |
terminalSequence | — | 端末エスケープシーケンス(OSC 0/1/2/9/99/777 または BEL。終端は BEL か ST)。allowlist 外が1つでも混ざるとフィールドごと無視される(カーソル移動・色・OSC 8 リンク・OSC 52 クリップボードなど) |
additionalContext | — | system reminder として Claude の context に注入される文字列。同じイベントで複数の hook が返せば全部届く。10,000 文字を超えるとファイルに書かれ、パスと先頭 2,000 文字までのプレビューだけが渡る |
terminalSequence は「/dev/tty に書けない」ことへの答え。 Claude Code が自分の端末出力経路で書くので競合せず、tmux でも screen でも Windows でも効く。systemMessage や continue を捨てるイベント(Notification、StopFailure)でも効く。 ただし対話セッションで画面が出ている間だけで、-p と Agent SDK では無視される。WorktreeCreate の command hook は stdout がパスとして読まれるので JSON を返せない(HTTP hook なら返せる)。
additionalContext が差し込まれる位置はイベントで違う —— SessionStart / SubagentStart は会話の先頭(最初のプロンプトより前)、UserPromptSubmit / UserPromptExpansion は送信したプロンプトの隣、ツール系はツール結果の隣、Stop / SubagentStop はターンの終わり、PostModelSwitch は切り替え後の次のリクエスト。
10,000 文字の上限は、1本ずつ別々に測られる。 同じイベントで複数の hook が走っても 各文字列を単独で測り、JSON 出力ならフィールドごとに、素の stdout はまとめて1本として測る。
A hook’s
additionalContext,systemMessage, andinitialUserMessagestrings, and its plain stdout, are capped at 10,000 characters … Claude Code saves the output to a file in the session directory and replaces it with the file path and a preview of up to the first 2,000 characters. … Unlike that Bash ceiling, this cap has no setting or environment variable to raise it. … Claude Code doesn’t ask Claude to read the file, so keep anything Claude must always see within the cap.hook の
additionalContext・systemMessage・initialUserMessageと素の stdout は 10,000 文字で頭打ちになる。……超えた分はセッションディレクトリのファイルに保存され、ファイルパスと先頭 2,000 文字までのプレビューに置き換えられる(切り捨てではない)。……この上限は、Bash の出力上限と違って、設定でも環境変数でも引き上げられない。……Claude Code は Claude にそのファイルを読めとは言わないので、必ず見せたいものは上限の中に収める。
ハンドラ種別ごとのフィールド
command hook:
| フィールド | 必須 | 内容 |
|---|---|---|
type | ○ | "command" |
command | ○ | 実行するシェルコマンド。args があると実行可能ファイルとして解決し直接 spawn する |
args | — | 引数リスト。あると exec 形式(シェルを介さない) |
async | — | true でブロックせずバックグラウンド実行 |
asyncRewake | — | true でバックグラウンド実行し、exit code 2 で Claude を起こす。 async を含意する |
shell | — | "bash"(既定)。Windows で Git Bash が入っていなければ "powershell" が既定。CLAUDE_CODE_USE_POWERSHELL_TOOL は不要(hook は PowerShell を直接起こす)。args があると無視される |
if | — | permission rule 構文のフィルタ。ツールイベントのみ。書けるルールはちょうど1つ —— && も || もリストも無いので、条件を足すならハンドラを分ける |
timeout | — | 秒。既定 600(UserPromptSubmit / PreModelSwitch / PostModelSwitch は 30、MessageDisplay は 10)。SessionEnd は hook 全体で 1.5 秒の予算を共有する(設定でより長い timeout を書くと、最大 60 秒まで予算が引き上げられる) |
statusMessage | — | 実行中に出すスピナーのメッセージ |
once | — | true で初回の成功後に削除される。失敗・exit 2 でのブロック・タイムアウトでは残るので次の一致でまた走る。skill frontmatter でのみ有効(設定ファイルと agent frontmatter では無視される) |
exec 形式 vs シェル形式:
argsがあるとシェルを介さず、各要素が書いたとおりに1つの引数になる(特殊文字はそのまま渡る)。argsを省くとシェルに渡され、パイプ・&&・リダイレクト・glob が使える。
if の Bash マッチング(hooks-guide の表に、原典がここへ送っている分を足したもの。先頭の VAR=value 代入はマッチング前に取り除かれる):
if パターン | Bash コマンド | 走るか | 理由 |
|---|---|---|---|
Bash(git *) | FOO=bar git push | yes | 先頭の代入が取り除かれ、git push が一致 |
Bash(git *) | npm test && git push | yes | 各サブコマンドが検査される |
Bash(rm *) | echo $(rm -rf /) | yes | $() とバッククォートの中も検査される |
Bash(rm *) | echo $(date) | no | どのサブコマンドも一致しない |
Bash(git push *) | echo $(date) | yes | コマンド名以上を指定したパターンは、$() / バッククォート / $VAR があると念のため走る |
Bash の入力がどのコマンドを走らせるか Claude Code が判断できないときは、パターンに関わらず hook を走らせる。 if は best-effort なので、確実な許可・拒否は hook ではなく permission の仕組みで強制する。
ファイルツールの if では、"Edit(src/**)" は作業ディレクトリ直下の src だけに当たる。 どの深さの src にも当てたいなら "Edit(**/src/**)"(v2.1.214 より前は前者がどの深さにも当たっていた)。
HTTP hook — url(必須)、headers($VAR_NAME / ${VAR_NAME} 展開)、allowedEnvVars(展開を許す変数名のリスト)。
応答の扱い:
| 応答 | 扱い |
|---|---|
| 2xx + 空ボディ | 成功(exit 0 相当) |
| 2xx + JSON | JSON 出力スキーマとしてパース。schema に落ちると非ブロックのエラー |
| 2xx + プレーンテキストなど他のボディ | 非ブロックのエラー。context には追加されない |
| 非 2xx または接続失敗 | ブロックしないエラー。実行は続く |
To block, return 2xx JSON with appropriate decision fields. HTTP のステータスコードだけではブロックできない。
MCP tool hook — server(plugin 同梱なら plugin:<plugin-name>:<server-name>)、tool、input(文字列は hook 入力からの ${path} 置換をサポート、例: "${tool_input.file_path}")。
呼べるのは設定済みの MCP サーバ。接続を待つかどうかはイベントで違う:
- ブロックや結果の変更ができるイベント(
PreToolUse、Stopなど)—— 接続中のサーバを、最大MCP_TIMEOUTまで、かつ hook 自身のtimeoutの範囲で待ってからツールを呼ぶ - 観察だけのイベント(
Notification、SessionEndなど)—— 待たない cached状態のサーバは、hook がツールを呼んだ時点で接続する- ツールのテキスト結果は、command hook の stdout と同じ規則(exit 0 のパース規則)で読まれる —— 決定や
additionalContextはそこから読み取られる - その時点で未接続、またはツールが
isError: trueを返すと、ブロックしないエラーになり実行は続く - hook が OAuth フローを起こすことはない。 先に
/mcpでサーバを認証しておく
起動時の SessionStart(--continue / --resume を含む)と、すべての Setup は、MCP サーバが hook に使えるようになる前に発火する。 そこの mcp_tool hook はツールを呼ばずに飛ばされ、debug log に no MCP client context と残るだけ(/clear や compaction の後の SessionStart では走る)。起動時に要るものは command 型の SessionStart hook で用意する。
prompt hook — prompt($ARGUMENTS が hook 入力 JSON に置換される)、model(既定は Claude Code がバックグラウンド処理に使うモデル)、timeout の既定は 30 秒。
agent hook — 同上だが timeout の既定は 60 秒。prompt hook と同じイベントで使えるが、
PermissionRequest だけは例外で agent hook を受け付けない —— 書いても黙って飛ばされ、
権限フローはそのまま進む。ここで許可・拒否を返したいなら command か http にする
(PermissionRequest が受け付けるのは command / http / mcp_tool / prompt)。
重複・作業ディレクトリ・無効化
- 同じハンドラを複数の設定ファイルに書いても1回しか走らない。 ただし plugin や skill が持つ同じハンドラは別物として扱われる
- ハンドラは現在のディレクトリで走る。そのディレクトリが消えていたら(別のシェルが worktree や temp を消した場合など)、セッション開始時のディレクトリ → プロジェクトルート → ホーム → システムの temp の順に生きているものへ落ちる(debug ログに警告が出る)
--settings '{"disableAllHooks": true}'はプロジェクトやローカルの設定より優先されるので、1回の実行だけ hook を止められる。個別の hook だけを無効にする方法は無い$CLAUDE_CODE_REMOTEはリモートの web 環境で"true"、ローカル CLI では未設定。Remote Control が繋がっている間は$CLAUDE_CODE_BRIDGE_SESSION_IDにそのセッション ID が入る(v2.1.199 以降)
非同期 hook
| フィールド | 内容 |
|---|---|
async | バックグラウンドで走り、Claude Code をブロックしない |
asyncRewake | バックグラウンドで走り、exit code 2 で Claude を起こす。stderr(空なら stdout)が system reminder として Claude に見える |
用途: CI やデプロイのような長時間の処理。Claude は待つ間も作業を続けられ、失敗したら起こされる。
制約(同期 hook と違うところ):
- 止められない・決められない。
decision/permissionDecision/continueは効かない。 それらが制御したはずの動作はもう終わっているから - 結果は次の会話ターンで届く。 セッションが idle なら次のユーザー操作まで待つ。
例外は
asyncRewakeの exit 2 —— idle でも即座に Claude を起こす - 届くのは
additionalContextとsystemMessage。同期のsystemMessageと違い、どちらもユーザーには見えない - JSON 応答は同期と同じスキーマで検証され、型の違うフィールドは配送されず落とされる
(
--debugで落ちたフィールド名が警告に出る)。v2.1.202 より前は、壊れた JSON がセッションを 落とすことがあり、resume のたびに再発した - 完了通知は既定で抑制される。 見るには
Ctrl+Oか--verbose - 発火ごとに別プロセスが立つ。重複排除は無い
claude -pでは、teardown 時にまだ走っている async hook は kill され、cancelledとして確定する。 セッションより長く生かしたい仕事は、hook から完全に切り離したプロセスとして起こす
skill / agent の frontmatter に書く hook
全イベントがサポートされる。 subagent では Stop hook が自動的に SubagentStop に変換される。
設定ファイルと同じ形式だが、登録されている長さが2つで違う。
| いつ登録され、いつ外れるか | |
|---|---|
| subagent | その subagent が走っている間だけ。 終わると外れる |
| skill | 呼ばれた時点で登録され、セッションの残り全体で走り続ける(skill 自身のターンが終わっても外れない)。1回で終わらせたいなら once: true |
workspace trust の扱いも2つで違う。
Trust requirement (v2.1.218+): project subagent の frontmatter hook は、そのエージェントファイルが由来するフォルダの workspace trust ダイアログを承認した後にのみ走る。(
-pセッションは承認の代わりにならない)
skill の frontmatter hook は設定ファイルの hook と同じ規則で、-p なら信頼していないフォルダでも、skill が呼ばれた時点で登録されて走る。
そのまま使える具体例
skill の frontmatter に hook を書く:
---
name: secure-operations
description: Perform operations with security checks
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "./scripts/security-check.sh"
---長時間の CI を待たずに走らせ、失敗したら Claude を起こす:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "./scripts/run-ci.sh",
"asyncRewake": true,
"statusMessage": "CI running in background"
}
]
}
]
}
}exec 形式で特殊文字をそのまま渡す:
{
"type": "command",
"command": "/usr/local/bin/my-hook",
"args": ["--pattern", "a && b || c"]
}MCP ツールを hook として呼ぶ(${path} で hook 入力を差し込む):
{
"type": "mcp_tool",
"server": "audit-service",
"tool": "log_edit",
"input": {
"path": "${tool_input.file_path}",
"session": "${session_id}"
}
}PostToolUse でツール結果を書き換える:
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"updatedToolOutput": "..."
}
}Claude を完全に止める(イベント固有の決定より優先される):
{
"continue": false,
"stopReason": "Deployment window is closed until Friday."
}原典で言及されている関連文書
- mods-events — mod が JavaScript の関数として登録する hook(React to events)
- hooks-guide — 解説と使用例(この文書の対になる文書)
- permissions — exit code 2 が allow ルールより優先されること
- skills — skill frontmatter の
hooks - sub-agents — subagent frontmatter の
hooks、Stop→SubagentStop変換 - agent-teams —
TeammateIdle/TaskCreated/TaskCompleted - tools-reference — ツールの正規名(permission rule と hook matcher が使うのはこちら)
- mcp —
mcp_toolhook が呼ぶサーバのcached状態と/mcpでの認証 - env-vars —
MCP_TIMEOUT - costs — prompt / agent hook の既定モデルである「バックグラウンド処理に使うモデル」