hooks でアクションを自動化する
Claude Code がファイルを編集したり、タスクを完了したり、入力が必要になったりしたときに、シェルコマンドを自動的に実行します。コードをフォーマットし、通知を送信し、コマンドを検証し、プロジェクトルールを適用します。
Hooks はユーザー定義のシェルコマンドです。Claude Code はそのライフサイクルの特定のポイントで実行され、決定論的な制御を提供します。LLM が実行を選択するのに依存するのではなく、特定のアクションが常に発生します。Hooks を使用して、プロジェクトルールを適用し、反復的なタスクを自動化し、Claude Code を既存のツールと統合します。
判断が必要な決定については、決定論的なルールではなく、Claude モデルを使用して条件を評価する プロンプトベースの hooks または エージェントベースの hooks を使用することもできます。
Claude Code を拡張する他の方法については、Claude に追加の指示と実行可能なコマンドを与えるための skills、分離されたコンテキストでタスクを実行するための subagents、プロジェクト全体で共有する拡張機能をパッケージ化するための plugins を参照してください。
このガイドでは一般的なユースケースと始め方をカバーしています。完全なイベントスキーマ、JSON 入力/出力形式、非同期 hooks や MCP ツール hooks などの高度な機能については、Hooks リファレンス を参照してください。
最初の hook をセットアップする
Hook を作成するには、設定ファイル に hooks ブロックを追加します。このチュートリアルではデスクトップ通知 hook を作成するため、Claude があなたの入力を待っているときにアラートを受け取ることができます。ターミナルを監視する代わりに。
hook を設定に追加する
~/.claude/settings.json を開き、Notification hook を追加します。ファイルが存在しない場合は、作成してください。以下の例は macOS 用に osascript を使用しています。Linux と Windows のコマンドについては、Claude が入力を必要とするときに通知を受け取る を参照してください。
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}
設定ファイルに既に hooks キーがある場合は、オブジェクト全体を置き換えるのではなく、Notification を既存のイベントキーの兄弟として追加します。各イベント名は単一の hooks オブジェクト内のキーです:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }]
}
],
"Notification": [
{
"matcher": "",
"hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'" }]
}
]
}
}
CLI で説明することで、Claude に hook を書いてもらうこともできます。
設定を確認する
/hooks と入力して hooks ブラウザを開きます。利用可能なすべての hook イベントのリストが表示され、hooks が設定されているイベントの横に数が表示されます。Notification を選択して、新しい hook がリストに表示されることを確認します。Hook を選択すると、その詳細が表示されます:イベント、マッチャー、タイプ、ソースファイル、およびコマンド。
hook をテストする
Esc を押して CLI に戻ります。Shift+Tab を押してステータスバーに ⏸ manual mode on が表示されるまで続け、Claude に許可が必要な何かをするよう依頼し、ターミナルから切り替えます。デスクトップ通知を受け取るはずです。
/hooks メニューは読み取り専用です。Hooks を追加、変更、または削除するには、設定 JSON を直接編集するか、Claude に変更を依頼します。
自動化できるもの
Hooks を使用すると、Claude Code のライフサイクルの主要なポイントでコードを実行できます:編集後にファイルをフォーマットし、実行前にコマンドをブロックし、Claude が入力を必要とするときに通知を送信し、セッション開始時にコンテキストを注入するなど。Hook イベントの完全なリストについては、Hooks リファレンス を参照してください。
各例には、設定ファイル に追加する準備ができた設定ブロックが含まれています。
本番環境での hooks の例として、別のモデルレビューを実行し、その結果をセッションにフィードバックする場合は、security-guidance プラグインが Claude Code と統合する方法 を参照してください。
Claude が入力を必要とするときに通知を受け取る
Claude が作業を完了して入力を必要とするときはいつでもデスクトップ通知を取得し、ターミナルをチェックせずに他のタスクに切り替えることができます。
この hook は Notification イベントを使用します。これは Claude が入力または許可を待っているときに発火します。各通知タイプが発火するタイミング を参照して、正確なタイミングを確認してください。以下の各タブはプラットフォームのネイティブ通知コマンドを使用します。これを ~/.claude/settings.json に追加します:
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}
通知が表示されない場合
osascript は組み込みの Script Editor アプリを通じて通知をルーティングします。Script Editor に通知権限がない場合、コマンドは静かに失敗し、macOS はそれを付与するよう求めません。Terminal でこれを 1 回実行して、Script Editor を通知設定に表示させます:
osascript -e 'display notification "test"'
まだ何も表示されません。System Settings > Notifications を開き、リストで Script Editor を見つけて、Allow Notifications をオンにします。コマンドを再度実行して、テスト通知が表示されることを確認します。
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Claude Code needs your attention'"
}
]
}
]
}
}
通知が表示されない場合
notify-send はデスクトップ通知デーモンが必要です。ヘッドレスサーバー、SSH セッション、ほとんどのコンテナにはこれがありません。まずコマンドを直接テストしてください:
notify-send 'Claude Code' 'test'
コマンドが見つからない場合は、Debian と Ubuntu で libnotify-bin パッケージをインストールするか、ディストリビューションの同等のものをインストールしてください。
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "powershell.exe -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')\""
}
]
}
]
}
}
ダイアログが表示されない場合
このコマンドは画面の隅の通知ではなくダイアログボックスを開くため、ダイアログはターミナルウィンドウの背後で開く可能性があります。まず PowerShell でコマンドを直接テストしてください。Claude Code を WSL 内で実行する場合、powershell.exe は Windows interop を通じて PATH で利用可能である必要があります。
空の matcher はすべての通知タイプで発火します。特定のイベントでのみ発火させるには、次のいずれかの値に設定します:
| Matcher | 発火するタイミング |
|---|---|
permission_prompt |
Claude がツール使用を承認する必要があるか、サンドボックス化されたコマンドのネットワークリクエストを承認する必要があり、プロンプトが約 6 秒待機している |
idle_prompt |
Claude が約 60 秒前に応答を完了し、その後入力していない |
auth_success |
認証が完了したとき |
elicitation_dialog |
MCP サーバーが引き出しフォームを開き、約 6 秒入力していない |
elicitation_url_dialog |
MCP サーバーがブラウザURL を開くよう求め、約 6 秒入力していない |
elicitation_complete |
MCP サーバーがURL モード引き出しが完了したことを報告する |
elicitation_response |
MCP 引き出し応答がサーバーに送り返されたとき |
agent_needs_input |
バックグラウンドセッションがあなたの入力を待つのを開始するか、現在のセッションがagent team チームメイトのターミナルセットアップ質問を尋ね、約 6 秒入力していない。agent view が開いている間のみ発火します |
agent_completed |
バックグラウンドセッションが完了または失敗します。agent view が開いている間のみ発火します |
quota_auto_resume_fired |
Claude Code は claude.ai 使用制限が一時停止した後、タスクを続行します:リセット時、または Claude Code 中に何かを行うことで使用可能になったとき(使用クレジットの追加、プランのアップグレード、モデルの切り替えなど)。モデル設定の例外を参照してください |
quota_auto_resume_stale |
claude.ai 使用制限がコンピュータが約 30 分以上スリープしている間にリセットされました。Claude Code はタスクを続行する代わりに Enter キーを押すのを待ちます。より短いスリープの後、それは続行し、代わりに quota_auto_resume_fired を発火します |
quota_auto_resume_disabled |
Claude Code は claude.ai 使用制限の待機をタスクを続行せずに終了します:autoContinueAtUsageLimit がオフになったか、リセットが Claude Code が開始した待機中に 24 時間以上先に移動した、続行されたタスクが制限に引き続きヒットした、または継続がモデルに到達する前にブロックされました。Esc または Ctrl+C を押すか、Don't continue automatically を選択したときは発火しません |
Claude Code は permission_prompt をターミナルと Claude Desktop、VS Code 拡張機能、および Agent SDK を通じて許可リクエストに答えるその他のホストで異なる方法でタイミングします。各通知タイプが発火するタイミング を参照して、両方のタイミングを確認してください。
agent_needs_input および agent_completed マッチャーには Claude Code v2.1.198 以降が必要です。
quota_auto_resume_fired、quota_auto_resume_stale、および quota_auto_resume_disabled マッチャーには Claude Code v2.1.234 以降が必要です。
ターミナルセッションでは、サンドボックス化されたコマンドのネットワークリクエストに対する permission_prompt には Claude Code v2.1.246 以降が必要です。
チームメイトのターミナルセットアップ質問に対する agent_needs_input には Claude Code v2.1.248 以降が必要です。
/hooks と入力して Notification を選択し、hook が登録されていることを確認します。完全なイベントスキーマについては、Notification リファレンス を参照してください。
編集後にコードを自動フォーマットする
Claude が編集するすべてのファイルで Prettier を自動的に実行し、手動操作なしでフォーマットの一貫性を保ちます。
この hook は PostToolUse イベントを Edit|Write マッチャーで使用するため、ファイル編集ツールの後にのみ実行されます。コマンドは jq で編集されたファイルパスを抽出し、Prettier に渡します。これをプロジェクトルートの .claude/settings.json に追加します:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
hook をテストするには、Claude に JavaScript ファイルにシングルクォート文字列を含む行を追加するよう求めてください。その後ファイルを開きます:Prettier のデフォルト設定では、hook はそれらをダブルクォートに書き直します。
hook が成功すると、Claude Code は会話に何も表示しません。hook が実行されたことを確認するには、編集されたファイルが再フォーマットされていることを確認するか、デバッグテクニック を参照してください。
ファイルが Bash コマンドで書き直されるときを含め、特定のファイルがどのように変更されても再フォーマットするには、代わりに FileChanged hook を使用してください。
このページの Bash の例は JSON 解析に jq を使用します。macOS で brew install jq、Debian と Ubuntu で apt-get install jq でインストールするか、jq ダウンロード を参照してください。
保護されたファイルへの編集をブロックする
Claude が .env、package-lock.json、.git/ 内のものなどの機密ファイルを変更するのを防ぎます。Claude は編集がブロックされた理由を説明するフィードバックを受け取るため、アプローチを調整できます。
この例は hook が呼び出す別のスクリプトファイルを使用します。スクリプトはターゲットファイルパスを保護されたパターンのリストに対してチェックし、終了コード 2 で編集をブロックします。
hook スクリプトを作成する
これを .claude/hooks/protect-files.sh に保存します:
#!/bin/bash
# protect-files.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Windows バックスラッシュセパレータを正規化して、以下のパターンがマッチするようにします
FILE_PATH="${FILE_PATH//\\//}"
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
exit 2
fi
done
exit 0
macOS と Linux でスクリプトを実行可能にする
Hook スクリプトが Claude Code で実行されるには、実行可能である必要があります:
chmod +x .claude/hooks/protect-files.sh
hook を登録する
.claude/settings.json に PreToolUse hook を追加して、Edit または Write ツール呼び出しの前にスクリプトを実行します:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}
hook をテストする
Claude に .env ファイルにコメントを追加するよう求めてください。Claude Code は実行前に編集をブロックし、スクリプトの Blocked: メッセージを Claude にフィードバックとして渡します。
圧縮後にコンテキストを再注入する
Claude のコンテキストウィンドウがいっぱいになると、圧縮は会話を要約してスペースを解放します。これは重要な詳細を失う可能性があります。compact マッチャーで SessionStart hook を使用して、すべての圧縮後に重要なコンテキストを再注入します。
Claude Code はコマンドが stdout に書き込むプレーンテキストを Claude のコンテキストに追加します。この例はプロジェクト規約と最近の作業を Claude に思い出させます。これをプロジェクトルートの .claude/settings.json に追加します:
{
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.'"
}
]
}
]
}
}
echo を git log --oneline -5 などの動的出力を生成するコマンドに置き換えて、最近のコミットを表示できます。すべてのセッション開始時にコンテキストを注入する場合は、代わりに CLAUDE.md を使用することを検討してください。環境変数については、リファレンスの CLAUDE_ENV_FILE を参照してください。
設定変更を監査する
セッション中に設定またはスキルファイルが変更されたときを追跡します。ConfigChange イベントは外部プロセスまたはエディタが設定ファイルを変更したときに発火するため、コンプライアンスのために変更をログに記録したり、不正な変更をブロックしたりできます。
この例は各変更を監査ログに追加します。これを ~/.claude/settings.json に追加します:
{
"hooks": {
"ConfigChange": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "jq -c '{timestamp: now | todate, source: .source, file: .file_path}' >> ~/claude-config-audit.log"
}
]
}
]
}
}
マッチャーは設定タイプでフィルタリングします:user_settings、project_settings、local_settings、policy_settings、または skills。変更が有効になるのをブロックするには、終了コード 2 で終了するか、{"decision": "block"} を返します。完全な入力スキーマについては、ConfigChange リファレンス を参照してください。
hook が変更を記録することを確認するには、セッションが実行中に別のエディタで設定ファイルを編集してから、~/claude-config-audit.log を開きます:hook は変更ごとに 1 つの JSON 行をタイムスタンプ、ソース、ファイルパスとともに追加します。
ディレクトリまたはファイルが変更されたときに環境をリロードする
一部のプロジェクトは、どのディレクトリにいるかに応じて異なる環境変数を設定します。direnv などのツールはシェルで自動的にこれを行いますが、Claude の Bash ツールはそれらの変更を自動的に取得しません。
SessionStart hook を CwdChanged hook とペアリングすることでこれを修正します。SessionStart は起動したディレクトリの変数をロードし、CwdChanged は Claude がディレクトリを変更するたびにそれらをリロードします。どちらも CLAUDE_ENV_FILE に書き込み、Claude Code は各 Bash コマンドの前にスクリプトプリアンブルとして実行します。これを ~/.claude/settings.json に追加します:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
}
]
}
],
"CwdChanged": [
{
"hooks": [
{
"type": "command",
"command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
}
]
}
]
}
}
direnv allow をすべてのディレクトリで 1 回実行して、direnv が .envrc をロードすることが許可されるようにします。direnv の代わりに devbox または nix を使用する場合、同じパターンは direnv export bash の代わりに devbox shellenv または devbox global shellenv で機能します。
すべてのディレクトリ変更ではなく、特定のファイルに反応するには、FileChanged を matcher で使用して、監視するファイル名をリストします(パイプで区切られています)。ウォッチリストを構築するために、この値は正規表現として評価されるのではなく、リテラルファイル名に分割されます。FileChanged を参照して、同じ値がファイルが変更されたときにどの hook グループが実行されるかをフィルタリングする方法を確認してください。この例は現在のディレクトリの .envrc と .env を監視します:
{
"hooks": {
"FileChanged": [
{
"matcher": ".envrc|.env",
"hooks": [
{
"type": "command",
"command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
}
]
}
]
}
}
入力スキーマ、watchPaths 出力、および CLAUDE_ENV_FILE の詳細については、CwdChanged および FileChanged リファレンスエントリを参照してください。
特定の許可プロンプトを自動承認する
常に許可するツール呼び出しの承認ダイアログをスキップします。この例は ExitPlanMode を自動承認します。これは Claude がプランの提示を終了して続行するよう求めるときに呼び出すツールです。プランが準備できるたびにプロンプトが表示されることはありません。
上記の終了コード例とは異なり、自動承認には hook が JSON 決定を stdout に書き込む必要があります。Claude Code は PermissionRequest hook を実行します。これは Claude Code が許可ダイアログを表示しようとするときに実行され、hook が "behavior": "allow" を返すと、Claude Code はあなたの代わりにリクエストに答えます。
マッチャーは hook を ExitPlanMode のみにスコープするため、他のプロンプトは影響を受けません。これを ~/.claude/settings.json に追加します:
{
"hooks": {
"PermissionRequest": [
{
"matcher": "ExitPlanMode",
"hooks": [
{
"type": "command",
"command": "echo '{\"hookSpecificOutput\": {\"hookEventName\": \"PermissionRequest\", \"decision\": {\"behavior\": \"allow\"}}}'"
}
]
}
]
}
}
Hook が承認すると、Claude Code は Plan Mode を終了し、Plan Mode に入る前にアクティブだった許可モードを復元します。トランスクリプトは、ダイアログが表示されたはずの場所に「Allowed by PermissionRequest hook」と表示されます。Hook パスは常に現在の会話を保持します:ダイアログができるように、コンテキストをクリアして新しい実装セッションを開始することはできません。
特定の許可モードを設定する代わりに、hook の出力に setMode エントリを含む updatedPermissions 配列を含めることができます。mode 値は default、acceptEdits、または bypassPermissions などの任意の許可モードであり、destination: "session" は現在のセッションのみに適用します。
bypassPermissions は、セッションが既にバイパスモードで起動された場合にのみ適用されます:--dangerously-skip-permissions、--permission-mode bypassPermissions、--allow-dangerously-skip-permissions、または ユーザー、--settings、または管理設定 の permissions.defaultMode: "bypassPermissions"。permissions.disableBypassPermissionsMode で無効化されている場合、または 制限モード でセッションを開始した場合は適用されません。
Claude Code は defaultMode として永続化することはありません。
セッションを acceptEdits に切り替えるには、hook は stdout に次の JSON を書き込みます:
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow",
"updatedPermissions": [
{ "type": "setMode", "mode": "acceptEdits", "destination": "session" }
]
}
}
}
マッチャーをできるだけ狭く保ちます。.* でマッチングするか、マッチャーを空のままにすると、ファイル書き込みやシェルコマンドを含むすべての許可プロンプトが自動承認されます。決定フィールドの完全なセットについては、PermissionRequest リファレンス を参照してください。
hooks の仕組み
Claude Code は、ライフサイクルの特定のポイントで hook イベントを発火させます。イベントが発火すると、Claude Code はすべてのマッチングする hooks を並列で実行します。重複するハンドラーの扱い方については、Hook ハンドラーフィールド を参照してください。以下の表は各イベントとそれがトリガーされるときを示しています:
| イベント | 発火するタイミング |
|---|---|
SessionStart |
セッションが開始または再開されたとき |
Setup |
--init-only で Claude Code を起動するとき、または -p モードで --init または --maintenance を使用するとき。CI またはスクリプトでの 1 回限りの準備用 |
UserPromptSubmit |
プロンプトを送信するとき、Claude が処理する前 |
UserPromptExpansion |
ユーザーが入力したコマンドがプロンプトに展開されるとき、Claude に到達する前。展開をブロックできます |
PreToolUse |
ツール呼び出しが実行される前。ブロックできます |
PermissionRequest |
ツール呼び出しが権限決定を必要とするとき |
PermissionDenied |
オートモードがツール呼び出しを拒否するとき、分類器の判定がない拒否を含みます。JSON hookSpecificOutput.retry: true を使用して、モデルが拒否されたツール呼び出しを再試行できることを伝えます。Claude Code は分類器が判定を出さなかった場合、retry を無視します |
PostToolUse |
ツール呼び出しが成功した後 |
PostToolUseFailure |
ツール呼び出しが失敗した後 |
PostToolBatch |
並列ツール呼び出しの完全なバッチが解決した後、次のモデル呼び出しの前 |
Notification |
Claude Code が通知を送信するとき |
MessageDisplay |
アシスタントメッセージテキストが表示されている間 |
SubagentStart |
サブエージェントがスポーンされるとき |
SubagentStop |
サブエージェントが終了するとき |
TaskCreated |
TaskCreate 経由でタスクが作成されるとき |
TaskCompleted |
タスクが完了としてマークされるとき |
Stop |
Claude が応答を終了するとき |
StopFailure |
API エラーが原因でターンが終了するとき |
TeammateIdle |
エージェントチーム のチームメイトがアイドル状態になろうとするとき |
InstructionsLoaded |
CLAUDE.md または .claude/rules/*.md ファイルがコンテキストに読み込まれるとき。セッション開始時およびセッション中にファイルが遅延読み込みされるときに発火します |
ConfigChange |
セッション中に設定ファイルが変更されるとき |
CwdChanged |
作業ディレクトリが変更されるとき、例えば Claude が cd コマンドを実行するとき。direnv などのツールを使用したリアクティブな環境管理に便利です |
DirectoryAdded |
/add-dir または SDK register_repo_root コントロールリクエスト経由でセッション中盤に作業ディレクトリが追加されるとき |
FileChanged |
監視対象ファイルがディスク上で変更されるとき。matcher フィールドは監視するファイル名を指定します |
WorktreeCreate |
--worktree、isolation: "worktree"、またはバックグラウンドセッション経由で worktree が作成されるとき。デフォルトの git 動作を置き換えます |
WorktreeRemove |
セッション終了時、サブエージェント終了時、またはバックグラウンドセッションを削除するときに worktree が削除されるとき |
PreCompact |
コンテキスト圧縮の前 |
PostCompact |
コンテキスト圧縮が完了した後 |
PreModelSwitch |
Claude Code があなたまたはクライアントがリクエストしたモデルスイッチを適用する前。スイッチをブロックできます |
PostModelSwitch |
セッションのモデルが変更された後、Claude Code が独自に行う変更(セッションを再開するときのモデル復元など)を含みます |
Elicitation |
MCP サーバーがツール呼び出し中にユーザー入力をリクエストするとき |
ElicitationResult |
ユーザーが MCP エリシテーションに応答した後、レスポンスがサーバーに送り返される前 |
SessionEnd |
セッションが終了するとき |
各 hook には、それがどのように実行されるかを決定する type があります。ほとんどの hooks は "type": "command" を使用し、シェルコマンドを実行します。他の 4 つのタイプが利用可能です:
"type": "http":イベントデータを URL に POST します。HTTP hooks を参照してください。"type": "mcp_tool":既に接続されている MCP サーバー上のツールを呼び出します。MCP tool hooks を参照してください。"type": "prompt":シングルターン LLM 評価。プロンプトベースの hooks を参照してください。"type": "agent":ツールアクセス付きマルチターン検証。エージェント hooks は実験的であり、変更される可能性があります。エージェントベースの hooks を参照してください。
複数の hooks からの結果を組み合わせる
複数の hooks が同じイベントにマッチする場合、すべての hook のコマンドが完了してから Claude Code は結果をマージします。1 つの hook が deny を返しても、兄弟 hooks の実行は停止されません。1 つの hook の deny が別の hook の副作用を抑制することに依存しないでください。
すべてのマッチングする hooks が完了した後、Claude Code はそれらの出力を組み合わせます。PreToolUse 許可決定については、最も制限的な答えが適用されます。順序は deny、defer、ask、allow です。additionalContext からのテキストはすべての hook から保持され、Claude に一緒に渡されます。
以下の例は Bash に 2 つの PreToolUse hooks を登録しています。最初のものはすべてのコマンドをログファイルに追加して 0 で終了します。2 番目のものはスクリプトを実行し、コマンドに rm -rf が含まれている場合は 2 で終了して拒否します:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r .tool_input.command >> ~/.claude/bash.log"
},
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-rm-rf.sh"
}
]
}
]
}
}
Claude が rm -rf /tmp/build を実行しようとするとき、両方の hooks が並列で実行されます。ログ hook はコマンドを ~/.claude/bash.log に書き込み、0 で終了します。これは決定がないことを報告します。ガードレール hook は 2 で終了し、ツール呼び出しを拒否します。deny が優先されるため、Claude Code はコマンドをブロックし、Claude にガードレールの stderr を表示します。ログエントリはまだ書き込まれます。なぜなら、ログ hook は既に実行されているからです。
入力を読み取り、出力を返す
Hooks は stdin、stdout、stderr、および終了コードを通じて Claude Code と通信します。イベントが発火すると、Claude Code はイベント固有のデータを JSON としてスクリプトの stdin に渡します。スクリプトはそのデータを読み取り、作業を行い、終了コードを通じて Claude Code に次に何をするかを伝えます。
Hook 入力
すべてのイベントには session_id(セッションの一意の ID)や cwd(イベントが発火したときの作業ディレクトリ)などの共通フィールドが含まれていますが、各イベントタイプは異なるデータを追加します。Claude が Bash コマンドを実行するとき、PreToolUse hook は stdin で次のフィールドを受け取ります:
hook_event_name:hook をトリガーしたイベントtool_name:Claude が使用しようとしているツールtool_input:Claude がツールに渡した引数。Bash の場合、そのcommandフィールドはシェルコマンドを保持します。
たとえば、npm test コマンドの hook 入力は次のようになります:
{
"session_id": "abc123",
"cwd": "/Users/sarah/myproject",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}
スクリプトはその JSON を解析し、これらのフィールドのいずれかに基づいて動作できます。UserPromptSubmit hooks は代わりに prompt テキストを取得し、SessionStart hooks は source(startup、resume、clear、compact、または fork)を取得するなど。リファレンスの 共通入力フィールド で共有フィールドを参照し、各イベントのセクションでイベント固有のスキーマを参照してください。
Hook 出力
スクリプトは stdout または stderr に書き込み、特定のコードで終了することで、Claude Code に次に何をするかを伝えます。以下は、コマンドをブロックする PreToolUse hook の例です:
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q "drop table"; then
echo "Blocked: dropping tables is not allowed" >&2 # stderr は Claude のフィードバックになります
exit 2 # exit 2 = アクションをブロック
fi
exit 0 # exit 0 = 決定なし。通常の許可フローが適用されます
終了コードは次に何が起こるかを決定します:
- 終了 0:hook は異議を報告しません。
PreToolUsehook の場合、これはツール呼び出しを承認しません:通常の 許可フロー が引き続き適用されます。UserPromptSubmit、UserPromptExpansion、SessionStart、およびPostModelSwitchhooks の場合、Claude Code は stdout を プレーンテキストとして扱い Claude のコンテキストに追加します。
- 終了 2:Claude Code はアクションをブロックします。stderr に理由を書き込みます。それがどこに着地するかはイベントによって異なります:一部のイベントはそれを Claude にフィードバックとして供給するため、調整できます。他のイベントはそれをユーザーに表示し、
ConfigChangeやElicitationなどのいくつかはメッセージを表示しません。一部のイベントはブロックできません:SessionStartなどの場合、終了 2 は stderr をユーザーに表示し、実行は続行されます。イベントごとの終了コード 2 の動作 で完全なリストを参照してください。 - その他の終了コード:ほとんどのイベントでは、結果は hook が stdout に出力したものによって異なります:
- スキーマ検証に合格した解析済みオブジェクト:Claude Code は終了コードを無視し、JSON だけが結果を決定し、hook はエラーとして報告されません。
WorktreeCreateが任意の非ゼロ終了で失敗するなどの例外は、リファレンスの 終了コード出力 セクションにリストされています。 - スキーマ検証に失敗した解析済みオブジェクト、または Claude Code が JSON として解析しようとする が有効な JSON ではない stdout:ブロックされないエラー。通知は検証またはパースメッセージを含みます。
- Claude Code が プレーンテキストとして扱う stdout、または空の stdout:アクションはブロックされないエラーとして進行します。トランスクリプトは
<hook name> hook error通知を表示し、その後 stderr の最初の行がFailed with non-blocking status code:で始まります。完全な stderr をキャプチャするには、claude --debugで デバッグログ を有効にするか、セッション中に/debugを実行してください。
- スキーマ検証に合格した解析済みオブジェクト:Claude Code は終了コードを無視し、JSON だけが結果を決定し、hook はエラーとして報告されません。
構造化 JSON 出力
終了コードはブロックするか沈黙するかのみを許可します。より多くの制御のために、終了 0 して stdout に JSON オブジェクトを出力します。
終了 2 で stderr メッセージでブロックするか、終了 0 で JSON で構造化制御を使用します。hook ごとに 1 つのアプローチを選択してください。混在させた場合の動作については、終了コード出力 を参照してください。
たとえば、PreToolUse hook はツール呼び出しを拒否して理由を Claude に伝えたり、ユーザーの承認のためにエスカレートしたりできます:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Use rg instead of grep for better performance"
}
}
"deny" を使用すると、Claude Code はツール呼び出しをキャンセルし、permissionDecisionReason を Claude にフィードバックとして返します。
PreToolUse では、Claude Code は各 permissionDecision 値を次のように処理します:
"allow":インタラクティブな許可プロンプトをスキップします。Deny および ask ルール(エンタープライズ管理 deny リストを含む)は引き続き適用されます。また、requiresUserInteractionとマークされた MCP ツールのプロンプトと、組織がaskに設定した コネクタツールのプロンプトも適用されます。その設定が Claude Code に到達するセッションでも適用されます。"deny":ツール呼び出しをキャンセルし、理由を Claude に送信します"ask":通常どおりユーザーに許可プロンプトを表示します
4 番目の値 "defer" は、-p フラグ付きの 非インタラクティブモード で利用可能です。プロセスを終了し、ツール呼び出しを保持して、Agent SDK ラッパーが入力を収集して再開できるようにします。リファレンスの ツール呼び出しを後で延期する を参照してください。
PreModelSwitch hook は同じ permissionDecision フィールドを返します:"allow" はモデルスイッチを進行させ、"deny" はそれをキャンセルします。"ask" はインタラクティブセッションで /model を実行するときに確認を求めます。他の場所では、Claude Code は "ask" を拒否として扱います。PreModelSwitch 決定制御 を参照してください。
他のイベントは異なる決定パターンを使用します。たとえば、PostToolUse および Stop hooks はトップレベルの decision: "block" フィールドを使用し、PermissionRequest は hookSpecificOutput.decision.behavior を使用します。リファレンスの サマリーテーブル でイベント別の完全な内訳を参照してください。
UserPromptSubmit hooks の場合、代わりに hookSpecificOutput.additionalContext を使用して Claude のコンテキストにテキストを注入します。additionalContext を hookSpecificOutput の内側にネストします。JSON の最上位レベルに配置すると、Claude Code はそれを無視します。たとえば、この出力はすべてのプロンプトに現在のブランチ状態を追加します:
{
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "Current branch: release-42. Deploy freeze until Friday."
}
}
ブロックプロンプトとセッションタイトルの設定を含む完全な出力形状については、UserPromptSubmit 決定制御 を参照してください。
type: "prompt" の Hooks は出力を異なる方法で処理します:プロンプトベースの hooks を参照してください。
マッチャーで hooks をフィルタリングする
マッチャーなしでは、hook はそのイベントのすべての発生で発火します。マッチャーを使用すると、それを絞り込むことができます。たとえば、ファイル編集後にのみフォーマッターを実行したい場合(すべてのツール呼び出しの後ではなく)、PostToolUse hook にマッチャーを追加します:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "prettier --write ..." }
]
}
]
}
}
"Edit|Write" マッチャーは Edit または Write ツール呼び出しでのみ発火し、Bash、Read、または他のツールでは発火しません。カンマもまた同じ方法で代替を区切るため、"Edit, Write" は同等です。マッチャーパターン を参照して、プレーン名と正規表現がどのように評価されるかを確認してください。
Claude はまた、シェルコマンドを実行することでファイルを作成または変更できます。コンプライアンススキャンまたは監査ログなど、hook がすべてのファイル変更を確認する必要がある場合は、ターンごとに 1 回作業ツリーをスキャンする Stop hook を追加してください。呼び出しごとのカバレッジの場合は、Bash|PowerShell もマッチさせ、スクリプトで git status --porcelain を使用して変更されたファイルと追跡されていないファイルをリストアップしてください。PowerShell hook 入力セクション は、Bash だけをマッチさせるのが十分でない理由を説明しています。ディスク上の特定のファイルが変更されたときに hook を実行するには、それを書き込んだものが何であれ、FileChanged hook を使用してください。
各イベントタイプは特定のフィールドでマッチします:
| イベント | マッチャーがフィルタリングするもの | マッチャー値の例 |
|---|---|---|
PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest、PermissionDenied |
ツール名 | Bash、Edit|Write、mcp__.* |
SessionStart |
セッションがどのように開始されたか | startup、resume、clear、compact、fork |
Setup |
どの CLI フラグがセットアップをトリガーしたか | init、maintenance |
SessionEnd |
セッションが終了した理由 | clear、resume、logout、prompt_input_exit、other |
Notification |
通知タイプ | permission_prompt、idle_prompt、auth_success、elicitation_dialog、elicitation_url_dialog、elicitation_complete、elicitation_response、agent_needs_input、agent_completed、quota_auto_resume_fired、quota_auto_resume_stale、quota_auto_resume_disabled |
SubagentStart |
エージェントタイプ | general-purpose、Explore、Plan、またはカスタムエージェント名 |
PreCompact、PostCompact |
圧縮をトリガーしたもの | manual、auto |
PreModelSwitch、PostModelSwitch |
セッションが切り替わるモデルの正規名(PreModelSwitch で説明) | claude-opus-5、claude-opus-4-6|claude-opus-5、.*opus.* |
SubagentStop |
エージェントタイプ | SubagentStart と同じ値 |
ConfigChange |
設定ソース | user_settings、project_settings、local_settings、policy_settings、skills |
DirectoryAdded |
ディレクトリがどのように追加されたか | slash_command、register_repo_root |
StopFailure |
エラータイプ | rate_limit、overloaded、authentication_failed、oauth_org_not_allowed、account_on_hold、billing_error、invalid_request、model_not_found、server_error、max_output_tokens、cloud_credential_error、unknown |
InstructionsLoaded |
ロード理由 | session_start、nested_traversal、path_glob_match、include、compact |
Elicitation |
MCP サーバー名 | 設定した MCP サーバー名 |
ElicitationResult |
MCP サーバー名 | Elicitation と同じ値 |
FileChanged |
リテラルファイル名を監視(FileChanged を参照) | .envrc|.env |
UserPromptExpansion |
コマンド名 | スキルまたはコマンド名 |
UserPromptSubmit、PostToolBatch、Stop、TeammateIdle、TaskCreated、TaskCompleted、WorktreeCreate、WorktreeRemove、CwdChanged、MessageDisplay |
マッチャーサポートなし | すべての発生で常に発火 |
以下のタブは、異なるイベントタイプのいくつかのマッチャーを示しています。
Bash ツール呼び出しのみをマッチし、各コマンドをファイルにログに記録します。PostToolUse イベントはコマンドが完了した後に発火するため、tool_input.command は実行されたものを含みます。Hook は stdin で JSON としてイベントデータを受け取り、jq -r '.tool_input.command' はコマンド文字列のみを抽出し、>> はログファイルに追加します:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt"
}
]
}
]
}
}
MCP ツールは組み込みツールとは異なる命名規則を使用します:mcp__<server>__<tool>。ここで <server> は MCP サーバー名で、<tool> はそれが提供するツールです。たとえば、mcp__github__search_repositories または mcp__filesystem__read_file。プラグインバンドルサーバー からのツールは、mcp__plugin_my-plugin_db__query などのスコープ付きサーバーセグメントを使用します。特定のサーバーからすべてのツールをターゲットするために正規表現マッチャーを使用するか、mcp__.*__write.* のようなパターンでサーバー全体でマッチします。リファレンスの MCP ツールをマッチさせる を参照して、完全な例のリストを確認してください。
以下のコマンドは hook の JSON 入力からツール名を jq で抽出し、stderr に書き込みます。stderr に書き込むことで stdout をクリーンに保ち、メッセージを デバッグログ に送信します:
{
"hooks": {
"PreToolUse": [
{
"matcher": "mcp__github__.*",
"hooks": [
{
"type": "command",
"command": "echo \"GitHub tool called: $(jq -r '.tool_name')\" >&2"
}
]
}
]
}
}
SessionEnd イベントはセッションが終了した理由のマッチャーをサポートします。この hook は clear(/clear を実行するとき)でのみ発火し、通常の終了では発火しません:
{
"hooks": {
"SessionEnd": [
{
"matcher": "clear",
"hooks": [
{
"type": "command",
"command": "rm -f /tmp/claude-scratch-*.txt"
}
]
}
]
}
}
`if` フィールドでツール名と引数でフィルタリングする
if フィールドは 許可ルール構文 を使用して、ツール名と引数の両方で hooks をフィルタリングするため、hook プロセスはツール呼び出しがマッチするときにのみ生成されます。これは matcher を超えており、ツール名のみでグループレベルでフィルタリングします。
たとえば、すべての Bash コマンドではなく、Claude が git コマンドを使用するときにのみ hook を実行するには:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(git *)",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-git-policy.sh"
}
]
}
]
}
}
Hook コマンドが実行されるかどうかは、if パターンの形状と Claude が呼び出している Bash コマンドによって異なります:
if パターン |
Bash コマンド | Hook は実行されるか? | 理由 |
|---|---|---|---|
Bash(git *) |
git push |
はい | コマンド名がマッチします |
Bash(git *) |
npm test && git push |
はい | 各サブコマンドがチェックされます。git push がマッチします |
Bash(git *) |
echo $(git log) |
はい | $() とバッククォート内のコマンドがチェックされます。git log がマッチします |
Bash(git *) |
echo $(date) |
いいえ | サブコマンドが git * にマッチしません |
Bash(git push *) |
echo $(date) |
はい | コマンド名以上を指定するパターンは、$()、バッククォート、または $VAR で hook を実行します |
Claude Code がどのコマンドが Bash 入力を実行するかを判断できない場合、パターンに関係なく hook を実行します。Bash マッチングテーブル は、Claude Code がサブコマンドで絞り込むことができる、またはできないコマンド形状をカバーしています。フィルターはベストエフォートであるため、ハード allow または deny を強制するには、hook ではなく 許可システム を使用してください。
if フィールドは許可ルールと同じパターンを受け入れます:"Bash(git *)"、"Edit(*.ts)" など。複数のツール名をマッチさせるには、それぞれ独自の if 値を持つ別のハンドラーを使用するか、パイプ交替がサポートされている matcher レベルでマッチします。
if はツールイベントでのみ機能します:PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest、および PermissionDenied。他のイベントに追加すると、hook が実行されるのを防ぎます。
hook の場所を設定する
Hook を追加する場所がそのスコープを決定します:
| 場所 | スコープ | 共有可能 |
|---|---|---|
~/.claude/settings.json |
すべてのプロジェクト | いいえ、マシンにローカル |
.claude/settings.json |
単一プロジェクト | はい、リポジトリにコミット可能 |
.claude/settings.local.json |
単一プロジェクト | いいえ、gitignored |
| 管理ポリシー設定 | 組織全体 | はい、管理者制御 |
Plugin hooks/hooks.json |
プラグインが有効なとき | はい、プラグインにバンドル |
| Skill frontmatter | スキルが呼び出されたら、セッションの残り。スキルとエージェントの Hooks を参照してください。 | はい、スキルファイルで定義 |
| Subagent frontmatter | そのサブエージェントが実行されている間 | はい、サブエージェントファイルで定義 |
Claude Code で /hooks を実行して、イベント別にグループ化されたすべての設定済み hooks を参照します。
hooks を無効にするには、設定ファイルで "disableAllHooks": true を設定します。Claude Code は 設定の優先順位 の後に残された値を読み取るため、プロジェクトの設定ファイルはあなたのものをオーバーライドできます。管理設定で設定された Hooks は、disableAllHooks がそこにも設定されていない限り、実行されます。各レベルの完全な範囲については、disableAllHooks を参照してください。
Claude Code が実行中に設定ファイルを直接編集する場合、ファイルウォッチャーは通常、hook の変更を自動的に取得します。
プロンプトベースの hooks
決定論的なルールではなく判断が必要な決定については、type: "prompt" hooks を使用します。シェルコマンドを実行する代わりに、Claude Code はプロンプトと hook の入力データを Claude モデル(デフォルトでは Haiku)に送信して決定を下します。より多くの機能が必要な場合は、model フィールドで異なるモデルを指定できます。
モデルの唯一の仕事は、その決定を JSON として返すことです:
"ok": true:アクションが続行されます"ok": false:何が起こるかはイベントによって異なります:StopおよびSubagentStop:reasonは Claude にフィードバックとして返されるため、作業を続けます。ただし、レスポンスが"impossible": trueも設定している場合は、その条件が決して満たされることのない条件として印付けられ、その場合 Claude Code は停止を許可してターンが終了しますPreToolUse:ツール呼び出しが拒否されます。デフォルトではターンが終了し、拒否のreasonは警告行としてチャットに表示されます。hook にcontinueOnBlock: trueを設定すると、代わりにreasonを Claude にツールエラーとして返すため、調整して続行できます。v2.1.210 より前では、拒否のreasonは Claude にツールエラーとして返され、ターンが続行されていましたPostToolUse:デフォルトではターンが終了し、reasonは警告行としてチャットに表示されます。continueOnBlock: trueを設定すると、reasonを Claude にフィードバックとして返し、代わりにターンを続行しますPostToolBatch、UserPromptSubmit、およびUserPromptExpansion:ターンが終了し、reasonは警告行としてチャットに表示されます
この例は Stop hook を使用して、要求されたすべてのタスクが完了しているかどうかをモデルに尋ねます。モデルが条件がまだ満たされていないため "ok": false を返す場合、Claude は作業を続け、reason を次の指示として使用します:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Check if all tasks are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}."
}
]
}
]
}
}
完全な設定オプションについては、リファレンスの プロンプトベースの hooks を参照してください。
エージェントベースの hooks
エージェント hooks は実験的です。動作と設定は将来のリリースで変更される可能性があります。本番ワークフローについては、コマンド hooks を優先してください。
検証がファイルの検査またはコマンドの実行を必要とする場合、type: "agent" hooks を使用します。プロンプト hooks は単一の LLM 呼び出しを行いますが、エージェント hooks は条件を返す前にファイルを読み取り、コードを検索し、他のツールを使用できる subagent を生成します。
エージェント hooks は "ok" / "reason" 応答形式を使用し、デフォルトのタイムアウトが 60 秒で、最大 50 ツール使用ターンです。プロンプト hooks の impossible フィールドはサポートされていません。ok: false の場合、Claude Code はエージェント hooks を同じイベント上で continueOnBlock: true を持つプロンプト hooks と同じ方法で処理するため、PreToolUse と PostToolUse ではターンが続行されます。エージェント hooks には continueOnBlock フィールドがありません。フィールドを含む エージェント hooks の設定 を参照してください。これには Claude Code が hooks の JSON 入力に置き換える $ARGUMENTS プレースホルダーが含まれます。
この例は Claude が停止することを許可する前にテストが合格することを検証します:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "agent",
"prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
"timeout": 120
}
]
}
]
}
}
Hook 入力データだけで決定を下すのに十分な場合はプロンプト hooks を使用します。コードベースの実際の状態に対して何かを検証する必要がある場合はエージェント hooks を使用します。
完全な設定オプションについては、リファレンスの エージェントベースの hooks を参照してください。
HTTP hooks
type: "http" hooks を使用して、シェルコマンドを実行する代わりに、イベントデータを HTTP エンドポイントに POST します。エンドポイントはコマンド hook が stdin で受け取るのと同じ JSON を受け取り、HTTP レスポンスボディを使用して同じ JSON 形式で結果を返します。
HTTP hooks は、Web サーバー、クラウド関数、または外部サービスに hook ロジックを処理させたい場合に便利です。たとえば、チーム全体のツール使用イベントをログに記録する共有監査サービス。
この例はすべてのツール使用をローカルログサービスに POST します:
{
"hooks": {
"PostToolUse": [
{
"hooks": [
{
"type": "http",
"url": "http://localhost:8080/hooks/tool-use",
"headers": {
"Authorization": "Bearer $MY_TOKEN"
},
"allowedEnvVars": ["MY_TOKEN"]
}
]
}
]
}
}
エンドポイントは、コマンド hooks と同じ 出力形式 を使用して JSON レスポンスボディを返す必要があります。ツール呼び出しをブロックするには、適切な hookSpecificOutput フィールドで 2xx レスポンスを返します。HTTP ステータスコードだけではアクションをブロックできません。
ヘッダー値は $VAR_NAME または ${VAR_NAME} 構文を使用した環境変数補間をサポートします。allowedEnvVars 配列にリストされている変数のみが解決されます。他のすべての $VAR 参照は空のままです。
完全な設定オプションとレスポンス処理については、リファレンスの HTTP hooks を参照してください。
制限とトラブルシューティング
制限
hooks を設計する際は、以下の制約を念頭に置いてください:
- コマンド hooks は stdout、stderr、および終了コードを通じてのみ通信します。これらは
/コマンドまたはツール呼び出しをトリガーできません。additionalContextを通じて返されたテキストは、Claude が平文として読むシステムリマインダーとして注入されます。HTTP hooks はレスポンスボディを通じて通信します。 - Hook タイムアウトはタイプによって異なります。
timeoutフィールド(秒単位)で hook ごとにオーバーライドできます。command、http、mcp_tool:10 分。Claude Code はUserPromptSubmit、PreModelSwitch、およびPostModelSwitchhooks のこのデフォルトを 30 秒に短縮し、MessageDisplayを 10 秒に短縮します。prompt:30 秒。agent:60 秒。SessionEndhooks はすべてのタイプで 1.5 秒の予算を共有します。設定で hook ごとのtimeoutがより長い場合、Claude Code は予算を引き上げて一致させ、最大 60 秒までです。
PostToolUsehooks はツールが既に実行されているため、アクションを元に戻すことはできません。PermissionRequesthooks は Claude Code があなたに許可を求めようとしているときに発火します。- 非インタラクティブモード(
-pフラグ)では、そのプロンプトは Agent SDK のcanUseToolコールバックがそれを提供する場合にのみ存在します。プレーンな-p実行または--permission-prompt-toolでは、自動化された許可決定に代わりにPreToolUsehooks を使用します。 - バックグラウンド subagents は非インタラクティブモードでプロンプトを表示できません。Claude Code は依然としてそれらのツール呼び出しの hooks を実行し、hook が決定を返さない場合は呼び出しを拒否します。インタラクティブセッションでは、バックグラウンド subagent プロンプトはメインセッションに表示され、hooks は通常通り発火します。
- 非インタラクティブモード(
Stophooks はタスク完了時だけでなく、Claude が応答を終了するたびに発火します。ユーザーの割り込みでは発火しません。API エラーは代わりに StopFailure を発火させます。- 複数の
PreToolUsehooks がupdatedInputを返してツールの引数を書き直す場合、最後に完了したものが勝ちます。Hooks は並列で実行されるため、順序は非決定的です。同じツールの入力を変更する複数の hooks を持つことを避けてください。
Hooks と許可モード
PreToolUse hooks は任意の権限モードチェックの前に発火します。すべての 権限モード(dontAsk を含む)で発火します。permissionDecision: "deny" を返す hook は、bypassPermissions モードまたは --dangerously-skip-permissions でもツールをブロックします。これにより、ユーザーが権限モードを変更してバイパスできないポリシーを適用できます。
逆は真ではありません:"allow" を返す hook は、設定からの deny ルールをバイパスしません。また、requiresUserInteraction とマークされた MCP ツールのプロンプトを抑制することもできず、組織が セッションでそのセッティングに到達する Claude Code で ask に設定したコネクタツールも抑制できません。Hooks は制限を厳しくできますが、許可ルールが許可する範囲を超えて緩和することはできません。
Hook が発火しない
Hook は設定されていますが、実行されません。
/hooksを実行し、hook が正しいイベントの下に表示されることを確認します- マッチャーパターンがツール名と正確にマッチすることを確認します。マッチャーは大文字小文字を区別します
- 正しいイベントタイプをトリガーしていることを確認します:
PreToolUseはツール実行前に発火し、PostToolUseは後に発火します。PermissionRequesthook は Claude Code があなたに許可を求めようとしているときに発火します。非インタラクティブケースについては 制限を参照してください
Hook エラーが出力に表示される
トランスクリプトに「PreToolUse hook error: ...」というメッセージが表示されます。
-
スクリプトが予期せずゼロ以外のコードで終了しました。サンプル JSON をパイプして手動でテストします:
echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh echo $? # 終了コードを確認 -
「command not found」が表示される場合は、絶対パスを使用するか、スクリプトを参照するために
${CLAUDE_PROJECT_DIR}を使用します。シェルクォーティングを完全に回避するには、"args": []を追加して exec form に切り替えます。これはシェルなしでスクリプトを直接生成します -
「jq: command not found」が表示される場合は、
jqをインストールするか、JSON 解析に Python/Node.js を使用します -
通知が JSON 検証メッセージを表示する場合、hook の stdout は JSON として解析されましたがスキーマ検証に失敗しました。JSON 解析メッセージを表示する場合、stdout は JSON オブジェクトのように見えましたが有効な JSON ではありませんでした。どちらも終了コード 0 でも発生します。
解析失敗を修正するには、文字列連結の代わりに
jqなどの JSON エンコーダーでペイロードを構築して、値内の引用符とバックスラッシュがエスケープされるようにします。リファレンスの 終了コード出力セクションは終了コードと JSON の組み合わせをカバーしています -
スクリプトがまったく実行されていない場合は、実行可能にします:
chmod +x ./my-hook.sh
`/hooks` に設定された hooks が表示されない
設定ファイルを編集しましたが、hooks がメニューに表示されません。
- ファイル編集は通常自動的に取得されます。数秒後に表示されていない場合、ファイルウォッチャーが変更を見逃した可能性があります:セッションを再開して強制的にリロードします。
- JSON が有効であることを確認します:末尾のコンマとコメントは許可されていません
- 設定ファイルが正しい場所にあることを確認します:プロジェクト hooks の場合は
.claude/settings.json、グローバル hooks の場合は~/.claude/settings.json
Stop hook がブロック上限に達する
Claude は無限ループで作業を続け、停止する代わりに、Stop hook が連続して 8 回ブロックしたという警告でターンを終了します。
Claude Code は Stop hook が進捗なしで 8 回連続でブロックした後、それをオーバーライドします。Hook スクリプトは、それが既にトリガーされたかどうかをチェックする必要があります。JSON 入力から stop_hook_active フィールドを解析し、true の場合は早期に終了します:
#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0 # Claude が停止することを許可
fi
# ... hook ロジックの残り
Hook が収束するために 8 回以上の反復が正当に必要な場合は、CLAUDE_CODE_STOP_HOOK_BLOCK_CAP で上限を引き上げます。
Hook JSON に効果がない
Hook は有効な JSON を出力していますが、決定が有効にならず、トランスクリプトにエラーが表示されません。どの原因が当てはまるかを確認します:
- JSON の前の追加出力:通常、シェルプロファイルの無条件の
echoにより、何か他のものが最初に stdout に書き込まれるため、出力はもはや{で始まらず、Claude Code はそれを JSON として解析しません。原因と修正は以下のリストに従います。 - フィールドが間違ったレベルにある:各フィールドの配置を JSON 出力形式と比較します。例えば、
permissionDecisionはトップレベルではなくhookSpecificOutputの内部に属します。
Claude Code が shell form コマンド hook(args なし)を実行する場合、macOS と Linux では sh -c を、Windows では Git Bash を、Git Bash がデフォルトでインストールされていない場合は PowerShell を生成します。このシェルは非インタラクティブですが、Git Bash と一部の設定(BASH_ENV が ~/.bashrc を指すなど)は依然としてプロファイルをソースします。そのプロファイルに無条件の echo ステートメントが含まれている場合、その出力は hook の JSON に前置されます:
Shell ready on arm64
{"decision": "block", "reason": "Not allowed"}
結合された出力はもはや { で始まらないため、Claude Code は stdout 全体をプレーンテキストとして扱い、JSON を無視します。終了コード 0 ではトランスクリプトに何も報告されません。解析試行は デバッグログにのみ記録されます。これを修正するには、シェルプロファイルの echo ステートメントをラップして、インタラクティブシェルでのみ実行するようにします:
# ~/.zshrc または ~/.bashrc 内
if [[ $- == *i* ]]; then
echo "Shell ready"
fi
$- 変数はシェルフラグを含み、i はインタラクティブを意味します。Hooks は非インタラクティブシェルで実行されるため、echo はスキップされます。
Hook が permissionDecision または additionalContext を hookSpecificOutput の内部ではなくトップレベルに返す場合、JSON は依然として解析され、Claude Code は誤配置されたフィールドを報告なしで無視します。どのフィールドが無視されたかを確認するには、claude --debug で Claude Code を開始し、デバッグログで Hook JSON output had unrecognized keys を検索します。
デバッグ技術
Ctrl+O を押してトランスクリプトビューを開き、hook 実行の結果を確認します:
- 成功した実行:hook の JSON が
systemMessageや Stop hook フィードバックなどのサーフェスを表示しない限り、何も表示されません。- Hook が実行されたことを確認するには、再フォーマットされたファイルなどの効果をチェックするか、以下で説明されているようにデバッグログを有効にして hook を再度トリガーします
- ブロッキングエラー:ほとんどのイベントでは hook のフィードバックが表示されます。Hook の JSON がブロッキング決定を下した場合、フィードバックはその決定からの理由です。そうでない場合は hook の stderr です。
ConfigChangeやElicitationなどのいくつかのイベントでは、ブロックはメッセージを表示しません。 - 非ブロッキングエラー:アクションが進行し、
<hook name> hook error通知が短い説明とともに表示されます。例えば stderr の最初の行に「Failed with non-blocking status code:」というプレフィックスが付いているか、JSON 検証またはパースメッセージです。
どの終了コードと JSON の組み合わせが各結果を生成するか、イベントごとの例外を含めて、リファレンスの 終了コード出力セクションで定義されています。
完全な実行詳細(どの hooks がマッチしたか、それらの終了コード、stdout、stderr など)については、デバッグログを読みます。claude --debug-file /tmp/claude.log で Claude Code を開始して既知のパスに書き込み、別のターミナルで tail -f /tmp/claude.log を実行します。そのフラグなしで開始した場合は、セッション中に /debug を実行してログを有効にし、ログパスを見つけます。
詳細を学ぶ
- Hooks リファレンス:完全なイベントスキーマ、JSON 出力形式、非同期 hooks、および MCP ツール hooks
- セキュリティに関する考慮事項:共有または本番環境に hooks をデプロイする前に確認してください
- Bash コマンドバリデーター例:完全なリファレンス実装