SpyBara
Go Premium

hooks-guide.md 2026-09-08 20:00 UTC to 2026-09-09 22:58 UTC

This page contains 135 additions and 67 deletions.

2026
Wed 9 22:58 Sat 12 03:02 Fri 25 23:58

hooks でアクションを自動化する

Claude Code がファイルを編集したり、タスクを完了したり、入力が必要になったりしたときに、シェルコマンドを自動的に実行します。コードをフォーマットし、通知を送信し、コマンドを検証し、プロジェクトルールを適用します。

Hooks はユーザー定義のシェルコマンドです。Claude Code はそのライフサイクルの特定のポイントで実行され、決定論的な制御を提供します。LLM が実行を選択するのに依存するのではなく、特定のアクションが常に発生します。Hooks を使用して、プロジェクトルールを適用し、反復的なタスクを自動化し、Claude Code を既存のツールと統合します。

判断が必要な決定については、決定論的なルールではなく、Claude モデルを使用して条件を評価する プロンプトベースの hooks または エージェントベースの hooks を使用することもできます。

Claude Code を拡張する他の方法については、Claude に追加の指示と実行可能なコマンドを与えるための skills、分離されたコンテキストでタスクを実行するための subagents、プロジェクト全体で共有する拡張機能をパッケージ化するための plugins を参照してください。

最初の hook をセットアップする

Hook を作成するには、設定ファイル に hooks ブロックを追加します。このチュートリアルではデスクトップ通知 hook を作成するため、Claude があなたの入力を待っているときにアラートを受け取ることができます。ターミナルを監視する代わりに。

1

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 を書いてもらうこともできます。

2

設定を確認する

/hooks と入力して hooks ブラウザを開きます。利用可能なすべての hook イベントのリストが表示され、hooks が設定されているイベントの横に数が表示されます。Notification を選択して、新しい hook がリストに表示されることを確認します。Hook を選択すると、その詳細が表示されます:イベント、マッチャー、タイプ、ソースファイル、およびコマンド。

3

hook をテストする

Esc を押して CLI に戻ります。Shift+Tab を押してステータスバーに ⏸ manual mode on が表示されるまで続け、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 をオンにします。コマンドを再度実行して、テスト通知が表示されることを確認します。

空の 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 を使用してください。

保護されたファイルへの編集をブロックする

Claude が .env、package-lock.json、.git/ 内のものなどの機密ファイルを変更するのを防ぎます。Claude は編集がブロックされた理由を説明するフィードバックを受け取るため、アプローチを調整できます。

この例は hook が呼び出す別のスクリプトファイルを使用します。スクリプトはターゲットファイルパスを保護されたパターンのリストに対してチェックし、終了コード 2 で編集をブロックします。

1

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
2

macOS と Linux でスクリプトを実行可能にする

Hook スクリプトが Claude Code で実行されるには、実行可能である必要があります:

chmod +x .claude/hooks/protect-files.sh
3

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"
}
]
}
]
}
}
4

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" は現在のセッションのみに適用します。

セッションを 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 ハンドラーフィールド を参照してください。以下の表は各イベントとそれがトリガーされるときを示しています:

Event When it fires
SessionStart When a session begins or resumes
Setup When you start Claude Code with --init-only, or with --init or --maintenance in -p mode. For one-time preparation in CI or scripts
UserPromptSubmit When you submit a prompt, before Claude processes it
UserPromptExpansion When a user-typed command expands into a prompt, before it reaches Claude. Can block the expansion
PreToolUse Before a tool call executes. Can block it
PermissionRequest When a tool call needs a permission decision
PermissionDenied When auto mode denies a tool call, including denials without a classifier verdict. Use JSON hookSpecificOutput.retry: true to tell the model it may retry the denied tool call. Claude Code ignores retry when the classifier produced no verdict
PostToolUse After a tool call succeeds
PostToolUseFailure After a tool call fails
PostToolBatch After a full batch of parallel tool calls resolves, before the next model call
Notification When Claude Code sends a notification
MessageDisplay While assistant message text is displayed
SubagentStart When a subagent is spawned
SubagentStop When a subagent finishes
TaskCreated When a task is being created via TaskCreate
TaskCompleted When a task is being marked as completed
Stop When Claude finishes responding
StopFailure When the turn ends due to an API error
TeammateIdle When an agent team teammate is about to go idle
InstructionsLoaded When a CLAUDE.md or .claude/rules/*.md file is loaded into context. Fires at session start and when files are lazily loaded during a session
ConfigChange When a configuration file changes during a session
CwdChanged When the working directory changes, for example when Claude executes a cd command. Useful for reactive environment management with tools like direnv
DirectoryAdded When a working directory is added mid-session via /add-dir or the SDK register_repo_root control request
FileChanged When a watched file changes on disk. The matcher field specifies which filenames to watch
WorktreeCreate When a worktree is being created via --worktree, isolation: "worktree", or for a background session. Replaces default git behavior
WorktreeRemove When a worktree is being removed at session exit, when a subagent finishes, or when you delete a background session
PreCompact Before context compaction
PostCompact After context compaction completes
PreModelSwitch Before Claude Code applies a model switch that you or a client requested. Can block the switch
PostModelSwitch After the session's model changes, including changes Claude Code makes on its own, such as restoring the model when you resume a session
Elicitation When an MCP server requests user input during a tool call
ElicitationResult After a user responds to an MCP elicitation, before the response is sent back to the server
SessionEnd When a session terminates

各 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 は異議を報告しません。
    • PreToolUse hook の場合、これはツール呼び出しを承認しません:通常の 許可フロー が引き続き適用されます。
    • UserPromptSubmit、UserPromptExpansion、SessionStart、および PostModelSwitch hooks の場合、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 を実行してください。

構造化 JSON 出力

終了コードはブロックするか沈黙するかのみを許可します。より多くの制御のために、終了 0 して stdout に JSON オブジェクトを出力します。

たとえば、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、または他のツールでは発火しません。Claude Code v2.1.191 以降では、カンマもまた同じ方法で代替を区切るため、"Edit, Write" は同等です。マッチャーパターン を参照して、プレーン名と正規表現がどのように評価されるかを確認してください。

各イベントタイプは特定のフィールドでマッチします:

イベント マッチャーがフィルタリングするもの マッチャー値の例
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、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"
}
]
}
]
}
}

`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

検証がファイルの検査またはコマンドの実行を必要とする場合、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、および PostModelSwitch hooks のこのデフォルトを 30 秒に短縮し、MessageDisplay を 10 秒に短縮します。
    • prompt:30 秒。
    • agent:60 秒。
    • SessionEnd hooks はすべてのタイプで 1.5 秒の予算を共有します。設定で hook ごとの timeout がより長い場合、Claude Code は予算を引き上げて一致させ、最大 60 秒までです。
  • PostToolUse hooks はツールが既に実行されているため、アクションを元に戻すことはできません。
  • PermissionRequest hooks は Claude Code があなたに許可を求めようとしているときに発火します。
    • 非インタラクティブモード(-p フラグ)では、そのプロンプトは Agent SDK の canUseTool コールバックがそれを提供する場合にのみ存在します。プレーンな -p 実行または --permission-prompt-tool では、自動化された許可決定に代わりに PreToolUse hooks を使用します。
    • バックグラウンド subagents は非インタラクティブモードでプロンプトを表示できません。Claude Code は依然としてそれらのツール呼び出しの hooks を実行し、hook が決定を返さない場合は呼び出しを拒否します。インタラクティブセッションでは、バックグラウンド subagent プロンプトはメインセッションに表示され、hooks は通常通り発火します。
  • Stop hooks はタスク完了時だけでなく、Claude が応答を終了するたびに発火します。ユーザーの割り込みでは発火しません。API エラーは代わりに StopFailure を発火させます。
  • 複数の PreToolUse hooks が 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 は後に発火します。PermissionRequest hook は 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 を出力していますが、決定が有効にならず、トランスクリプトにエラーが表示されません。

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 はスキップされます。

デバッグ技術

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 を実行してログを有効にし、ログパスを見つけます。

詳細を学ぶ