Claude Code特集2026年10月更新

Claude Code Hooks 使い方|PreToolUse/PostToolUse/Stop/SubagentStop 完全ガイド

公開日: 2026/04/22
更新日: 2026/10/16
Claude Code Hooks 使い方|PreToolUse/PostToolUse/Stop/SubagentStop 完全ガイド

この記事のポイント

Claude Code Hooks の設定方法を、主要4イベント(PreToolUse/PostToolUse/Stop/SubagentStop)の比較表・コピペ用レシピ・exit code の正しい使い方とあわせて解説します。2026年10月時点(v2.1.301)の33イベント、onFailure、mods との違い、企業導入時のセキュリティ統制まで整理しました。

Claude Code Hooks は、rm -rf のような危ないコマンドを実行前に止めたり、ファイル編集のたびに Prettier や型チェックを走らせたりする処理を、Claude の判断に任せず設定ファイルで必ず実行させる仕組みです。2026年10月時点の公式リファレンス(Claude Code v2.1.301)では、フックを掛けられるイベントが33種類、処理の種類(ハンドラー)が5つあります。

この記事では、次の内容を解説します。

  • Claude Code Hooks の全体像と5つのハンドラータイプの使い分け
  • 主要4イベント(PreToolUse / PostToolUse / Stop / SubagentStop)の違いを1枚にまとめた比較表と設定例
  • 33イベントの一覧と、それぞれ止められるかどうか
  • exit 1 では止まらない、JSON の置き場所を間違えるとエラーも出ずに無視される、Stop が止まらなくなる、といったつまずきやすい点と対処法
  • Hooks / Permissions / CLAUDE.md / Skills / mods の使い分け
  • 2026年夏〜秋の主な仕様変更(onFailure: "block"、UserPromptSubmit の実行順など)
  • 企業で使うときのセキュリティ統制(managed settings、-p 実行時の注意)

対象は、Claude Code をすでに使っていて自動化と安全策をもう一段強めたい開発者と、チームに Claude Code を配る前にルールを整えたい技術リーダーです。

Claude Code Hooks とは|30秒で全体像を掴む

Claude Code Hooks は、Claude Code の動作の決まったタイミング(ツール実行の直前・直後、応答の終了時など)に、自分で用意したシェルコマンドや HTTP リクエストを差し込む機能です。公式ガイド(hooks でワークフローを自動化する)は、LLM が実行を選ぶかどうかに頼らず、特定の処理が毎回確実に起きるようにするための仕組みだと説明しています。

CLAUDE.md に「.env は編集しないで」と書くのはお願いで、Claude が守るかどうかはその場の判断次第です。Hooks で .env への Edit をブロックすれば、Claude がどう判断しても編集は通りません。この「お願い」と「強制」の差が、Hooks を使う理由です。

Hooks の主な用途は、安全・品質・運用の3つに分かれます。

  1. 安全: rm -rf、git push --force、本番 DB への書き込みなどを設定側で止める
  2. 品質: ファイル編集のたびに Prettier・ESLint・型チェック・テストを走らせる
  3. 運用: セッション開始時に必要な情報を渡す、作業ログを残す、入力待ちを通知する

できることの代表例

カテゴリ

代表的な使い方

主に使うイベント

事前ブロック

rm -rf、force push、.env の編集、本番環境への接続を止める

PreToolUse

品質チェックの自動化

Prettier / ESLint / 型チェック / テストを編集後に実行

PostToolUse

情報の追加

ブランチ名や git status をプロンプトのたびに渡す

UserPromptSubmit / SessionStart

監査・記録

ツール呼び出しを外部サーバーへ送る、設定変更を記録する

PostToolUse / ConfigChange

完了チェック

テストが通るまで Claude に作業を続けさせる

Stop

サブエージェントの検証

サブエージェントの成果物を検査し、足りなければやり直させる

SubagentStop

通知

権限確認待ち・入力待ちをデスクトップや Slack に通知

Notification

Hooks の提供状況(2026年10月時点)

  • 提供元: Anthropic。Claude Code に標準で入っている機能で、Hooks 単体の追加料金はありません
  • 動く環境: ターミナル版 CLI、Desktop アプリの Code タブ、VS Code 拡張、claude -p / Agent SDK など、設定ファイルを読み込む環境。Windows 向けには PowerShell の指定も公式に用意されています
  • 最新バージョン: v2.1.301(2026年10月15日リリース)。更新が速いため、手元のバージョンは claude --version で確認してください
  • 確認方法: Claude Code の中で /hooks を実行すると、設定済みの hooks をイベントごとに一覧できます(閲覧専用)
Claude Code 公式製品ページのスクリーンショット

出典: Anthropic 公式サイト

関連記事: Claude Code とは|料金・使い方・特徴を徹底解説 / Claude Code 使い方 完全ガイド

設定ファイルの配置場所とスコープ

Claude Code 公式ドキュメント「Settings files and precedence」のページ画像

出典: Claude Code Docs(Settings)

Hooks は複数の設定ファイルに書けます。各ファイルの hooks は上書きではなくマージ(合算)されるため、ユーザー設定とプロジェクト設定の両方に書いた hook はどちらも動きます。まったく同じハンドラーが複数のファイルにある場合は1回だけ実行されます。

配置場所

効く範囲

Git で共有

主な用途

~/.claude/settings.json

自分の全プロジェクト

しない(自分の PC のみ)

個人で常に入れておきたいガード

.claude/settings.json

1つのプロジェクト

する(コミット可)

チーム共通のガード・品質チェック

.claude/settings.local.json

1つのプロジェクト

しない(gitignore)

個人の試験的な hook

管理ポリシー設定(managed settings)

組織全体

管理者が配布

企業としての統制

プラグインの hooks/hooks.json

プラグインを有効にしている間

プラグインに同梱

再利用できる hook の配布

Skill の frontmatter

そのスキルを呼んだ後、セッションの残り

スキルに同梱

特定作業のときだけ効く hook

サブエージェントの frontmatter

そのサブエージェントの実行中

エージェント定義に同梱

サブエージェント専用の検査(frontmatter に書いた Stop は SubagentStop として動く)

運用のコツ: チームで守りたい最低限のガード(rm -rf の防止、.env の保護など)は .claude/settings.json にコミットし、個人的に試している hook は .claude/settings.local.json に置くのが基本です。

設定ファイルを編集すると、通常はファイル監視で自動的に読み込み直されます。反映されないときはセッションを再起動してください。JSON に末尾のカンマやコメントを入れると読み込めないので注意してください。

関連記事: Claude Code Skills 活用ガイド / Claude Code サブエージェントの使い方

設定ファイルの基本構造|3階層ネストを理解する

Hooks の設定は「イベント名 → matcher(対象の絞り込み)→ 実行するハンドラー」の3階層で書きます。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(rm *)",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-rm.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

各階層の役割

  1. イベント名(PreToolUse など): いつ動かすか
  2. matcher: どのツール・どの種類のときに動かすか
  3. hooks: 実際に実行するハンドラー(command / http / mcp_tool / prompt / agent の5タイプ)

ハンドラー共通で使える主なフィールドは、type、if、timeout(秒)、statusMessage(実行中にスピナーに出す文言)です。command 型には args を付けるとシェルを通さずに実行する形式(exec 形式)になり、shell で bash か powershell を選べます。

matcher 構文の評価ルール

matcher は、書いた文字列の中身によって解釈が変わります。

書き方

解釈

例

"*"、""、省略

すべてに一致

すべてのツール

英数字・_・-・空白・,・| だけ

完全一致。| か , で複数指定

Bash / Edit|Write / "Edit, Write"

それ以外の文字を含む

JavaScript の正規表現(前後のアンカーなし)

^Notebook / mcp__github__.*

押さえておきたい点は次のとおりです。

  • 大文字と小文字は区別されます。bash と書くと Bash ツールには一致しません
  • 正規表現は部分一致です。全体で一致させたいときは ^...$ で囲みます
  • MCP ツールは mcp__<サーバー名>__<ツール名> という名前です。サーバー全体を対象にするなら mcp__memory__.* と書きます(mcp__memory だけでは何にも一致しません)
  • UserPromptSubmit、PostToolBatch、Stop、TaskCreated、TaskCompleted、TeammateIdle、WorktreeCreate、WorktreeRemove、MessageDisplay、CwdChanged は matcher に対応していません。書いてもエラーは出ず、そのまま無視されます

if フィールドでツール引数レベルのフィルタ

if を使うと、ツール名に加えて引数の中身で hook を動かすかどうかを決められます。書き方は権限ルールと同じです。

{ "if": "Bash(git push --force*)" }
{ "if": "Edit(*.ts)" }
{ "if": "Bash(rm *)" }

書き方のポイントは次のとおりです。

  • matcher でツール名を絞り、if で引数を絞るのが基本の組み合わせです
  • if に書けるルールは1つだけです。&& や || でつなげないので、*.ts と *.tsx の両方を対象にしたいときはハンドラーを2つに分けます
  • npm test && git push のような複合コマンドは、サブコマンドごとに判定されます。$() の中も対象です。判定できないコマンドは、パターンにかかわらず hook が動きます
  • if が効くのはツール系イベント(PreToolUse / PostToolUse / PostToolUseFailure / PermissionRequest / PermissionDenied)だけです。それ以外のイベントに if を付けると、その hook 自体が動かなくなります
  • 公式は if を「ベストエフォート」と位置付けています。絶対に許可・拒否したいものは permissions の allow / deny ルールで設定してください

5つのハンドラータイプの使い分け

Hook で実行する処理は5種類から選べます。確実さを求めるなら command、外部サービスに送るなら http、判断に文脈の理解が必要なら prompt という分け方が基本です。

タイプ

中身

モデルの使用

既定のタイムアウト

向いている用途

command

シェルコマンドを実行。stdin でイベントの JSON を受け取る

なし

600秒

ブロック、整形、テスト、ログ記録など大半の用途

http

イベントの JSON を指定 URL へ POST

なし

600秒

監査サーバーへの送信、社内システムとの連携

mcp_tool

設定済み MCP サーバーのツールを呼ぶ

なし

600秒

既存の MCP 連携を hook から使う

prompt

Claude モデルに1回だけ判定させ、{"ok": true/false, "reason": ...} を返させる

あり

30秒

「タスクは全部終わったか」など、条件をコードで書きにくい判定

agent

ツールを使えるサブエージェントに検証させる(最大50ツールターン)

あり

60秒

テストを実際に走らせて確かめる検証。公式が「実験的」と明記

タイムアウトの例外として、UserPromptSubmit と PreModelSwitch / PostModelSwitch では command / http / mcp_tool も30秒、MessageDisplay は10秒です。SessionEnd はすべての hook で合計1.5秒しかありません(hook の timeout を長くすると最大60秒まで延びます)。

まずは command から始めるのが安全です。 prompt と agent はモデルを呼ぶぶん、プランの使用量(API 利用なら API 料金)を消費し、判定結果にも揺れが出ます。必ず止めたいガードには向きません。agent 型は公式が実験的と明記しており、本番で使う場合も command を勧めています。

prompt 型は、ok: false を返したときの動きがイベントで異なります。

  • Stop / SubagentStop: 理由を Claude に渡して作業を続けさせます。"impossible": true を返すと停止を許可します
  • PreToolUse: 既定ではツールを拒否してターンを終えます。continueOnBlock: true を付けると、理由をツールのエラーとして返して作業を続けさせます(v2.1.210 で既定の動きが変わりました)
  • PostToolUse: 既定ではターンを終えます。continueOnBlock: true で作業継続

なお v2.1.294 で、「〜するコマンドをブロックせよ」のように命令形で書いた prompt / agent 型の hook が、ブロックすべきものを通してしまう不具合が修正されています。古いバージョンで prompt 型のガードを使っている場合は更新してください。

mcp_tool 型は、プロジェクトですでに MCP サーバーを使っているときに便利です。server / tool / input を指定し、input の中では ${tool_input.file_path} のようにイベントの値を差し込めます。

関連記事: Claude Code MCPの使い方

Exit Code と制御出力|いちばん間違えやすいところ

Hook の結果は終了コード(exit code)か stdout の JSON で Claude Code に伝えます。ここを取り違えると、ガードを書いたつもりで何も止まっていない状態になります。

Exit Code の動作

Exit Code

動作

0

成功。stdout が JSON オブジェクト1つだけなら、制御用の JSON として読まれます。JSON 以外のテキストはデバッグログに残るだけです(例外として UserPromptSubmit / UserPromptExpansion / SessionStart / PostModelSwitch では、そのテキストが Claude への情報として追加されます)。exit 0 のときの stderr は Claude には見えません

2

ブロック。stderr の内容が理由として扱われます。届く先はイベントで異なり、PreToolUse なら Claude にフィードバックされ、ブロックできないイベントではユーザーに表示されるだけです。JSON で許可を返していても exit 2 ならブロックが優先されます

その他(1 を含む)

ブロックしないエラー。処理はそのまま続き、stderr の1行目が通知として表示されるだけです

PreToolUse で exit 0 を返すのは「許可」ではなく「意見なし」です。その後は通常の権限確認の流れに進みます。

最大の落とし穴: exit 1 では止まらない

一般的なシェルの感覚で exit 1 を「失敗=中止」として使うと、警告が出るだけでコマンドは実行されます。止めたいときは必ず exit 2 を使ってください。 日本語の解説記事にも「exit 1 でキャンセルされる」と書かれたものがあるので注意が必要です。

# ❌ 悪い例: 警告は出るが rm -rf は実行される
if echo "$COMMAND" | grep -q "rm -rf"; then
  echo "危険なコマンドです" >&2
  exit 1   # ← これでは止まらない
fi

# ✅ 正しい例: 実行がブロックされ、理由が Claude に返る
if echo "$COMMAND" | grep -q "rm -rf"; then
  echo "rm -rf は禁止されています" >&2
  exit 2
fi

ガード用の hook が動かないと操作が通ってしまう問題と onFailure

もうひとつ見落とされやすいのが、hook 自体が失敗したときの扱いです。スクリプトのパスが間違っている、jq が入っていない、タイムアウトした、といった場合、既定では「その他の終了コード」と同じ扱いになり、ツールはそのまま実行されます。

v2.1.295 で追加された onFailure: "block" を付けると、hook が起動できない・0と2以外で終了した・HTTP エラー・タイムアウト・出力が不正、のいずれでも操作をブロックします(command と http 型が対象。既定値は "continue")。

{
  "type": "command",
  "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/guard-bash.sh",
  "onFailure": "block"
}

ただし Stop / SubagentStop / TaskCompleted / TeammateIdle と、非同期(async)の hook には効きません。

構造化 JSON 出力(exit 0 時)

細かく制御したいときは、exit 0 で stdout に JSON を返します。どの階層に書くかが重要です。

{
  "continue": true,
  "stopReason": "continue が false のときにユーザーに表示する理由",
  "systemMessage": "ユーザーへの警告メッセージ",
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "本番環境への接続は事前承認が必要です",
    "additionalContext": "Claude に渡す補足情報"
  }
}

JSON の書き方で気をつけたい点は次のとおりです。

  • permissionDecision や additionalContext は hookSpecificOutput の中に書きます(hookEventName も必須)。トップレベルに置くと、エラーも出ずに無視されます。claude --debug で起動すると Hook JSON output had unrecognized keys と出るので、効かないときはここを確認します
  • トップレベルの decision: "block" + reason が使えるのは、UserPromptSubmit / UserPromptExpansion / PostToolUse / PostToolUseFailure / PostToolBatch / Stop / SubagentStop / ConfigChange / PreCompact / TaskCreated です。PreToolUse の旧形式(decision: "approve"/"block")は非推奨です
  • continue: false は Claude の処理自体を止め、イベント固有の判断より優先されます
  • suppressOutput は現行リファレンスでは「受け付けるが効果なし」とされています
  • additionalContext は1フィールド10,000文字が上限です。命令口調ではなく事実として書くのが公式の推奨です(プロンプトインジェクション対策に誤検知されにくくなります)
  • 公式は、hook ごとに「exit 2 と stderr」か「exit 0 と JSON」のどちらかに決めて使うことを勧めています

主要4イベントの違いを比較|PreToolUse / PostToolUse / Stop / SubagentStop

Claude Code 公式ドキュメント「Hooks reference」のページ画像

出典: Claude Code Docs(Hooks reference)

主要4イベントの違いを1枚で押さえておくと、どのイベントに何を書くか迷わなくなります。操作を止めたいなら PreToolUse、結果を検査して直させたいなら PostToolUse、終わり方を検査したいなら Stop / SubagentStop です。

項目

PreToolUse

PostToolUse

Stop

SubagentStop

発火タイミング

ツール実行の直前

ツールが成功した直後

Claude が応答を終えるたび

サブエージェントが終了したとき

matcher の対象

ツール名

ツール名

なし(書いても無視)

エージェントの種類(agent_type)

操作を止められるか

止められる

止められない(実行済み)

停止を取り消して作業を続けさせられる

サブエージェントに作業を続けさせられる

主な入力

tool_name / tool_input / tool_use_id

tool_input / tool_response / duration_ms

stop_hook_active / last_assistant_message

stop_hook_active / agent_type / agent_transcript_path / last_assistant_message

返し方

exit 2、または hookSpecificOutput.permissionDecision

decision: "block" + reason、additionalContext

exit 2、または decision: "block" + reason

Stop と同じ

典型的な用途

危険コマンドの阻止、機密ファイルの保護、引数の書き換え

整形、Lint、型チェック、ログ記録

テスト未通過なら終わらせない、作業ログ

サブエージェントの成果物チェック

注意点

allow でも settings の deny / ask は越えられない

Bash 経由のファイル変更は Edit|Write では拾えない

stop_hook_active を見ないと止まらなくなる

親セッションに情報を返したいなら Agent ツールの PostToolUse を使う

全イベント一覧|ライフサイクル上で何が発火するか(33種類)

2026年10月時点の公式リファレンスでは、hook を掛けられるイベントは33種類です。5月時点から、PreModelSwitch / PostModelSwitch、MessageDisplay、DirectoryAdded などが加わっています。

イベント名

発火タイミング

matcher の値

止められるか

SessionStart

セッションの開始・再開

startup / resume / clear / compact / fork

不可

Setup

--init-only、または -p 実行時の --init / --maintenance

init / maintenance

不可

UserPromptSubmit

プロンプト送信直後、処理の前(Claude Code が自分で始めるターンでも発火)

なし

可

UserPromptExpansion

入力したコマンドがプロンプトに展開されるとき

コマンド名

可

PreToolUse

ツール実行の直前

ツール名

可

PermissionRequest

権限の確認が必要になったとき

ツール名

decision オブジェクトで制御(exit 2 は無視)

PermissionDenied

auto mode がツール呼び出しを拒否したとき

ツール名

不可(retry: true で再試行を促せる)

PostToolUse

ツールが成功した直後

ツール名

不可(フィードバックは可)

PostToolUseFailure

ツールが失敗した直後

ツール名

不可

PostToolBatch

並列のツール呼び出しがすべて終わり、次のモデル呼び出しの前

なし

可

Notification

通知を送るとき

permission_prompt / idle_prompt / auth_success など12種類

不可

MessageDisplay

Claude のテキストを表示するとき

なし

不可(表示のみ)

SubagentStart

サブエージェントの生成時

エージェントの種類

不可

SubagentStop

サブエージェントの終了時

エージェントの種類

可

TaskCreated

TaskCreate でタスクを作ったとき

なし

可

TaskCompleted

タスクが完了扱いになったとき

なし

可

Stop

Claude が応答を終えたとき

なし

可(作業を続けさせる)

StopFailure

API エラーでターンが終わったとき

rate_limit / overloaded / billing_error など

不可

TeammateIdle

Agent Teams のチームメイトがアイドルになる前

なし

可

InstructionsLoaded

CLAUDE.md や .claude/rules/*.md を読み込んだとき

session_start / nested_traversal / path_glob_match / include / compact

不可

ConfigChange

セッション中に設定ファイルが変わったとき

user_settings / project_settings / local_settings / policy_settings / skills

可(policy_settings は不可)

CwdChanged

作業ディレクトリが変わったとき

なし

不可

DirectoryAdded

/add-dir などでディレクトリを追加したとき

slash_command / register_repo_root

不可

FileChanged

監視対象のファイルがディスク上で変わったとき

ファイル名(例: .envrc|.env)

不可

WorktreeCreate

worktree を作るとき(既定の git の動作を置き換える)

なし

可(0以外で終了すると作成失敗)

WorktreeRemove

worktree を削除するとき

なし

可

PreCompact

コンテキスト圧縮の前

manual / auto

可

PostCompact

コンテキスト圧縮の後

manual / auto

不可

PreModelSwitch

モデルの切り替えが適用される前

切り替え先のモデル名

可

PostModelSwitch

モデルが切り替わった後

切り替え後のモデル名

不可

Elicitation

MCP サーバーがユーザーに入力を求めたとき

MCP サーバー名

可(辞退)

ElicitationResult

ユーザーが答えた後、サーバーに返す前

MCP サーバー名

可(辞退)

SessionEnd

セッションの終了時

clear / resume / logout / prompt_input_exit / other

不可

環境変数を後続のコマンドに渡す方法: SessionStart / CwdChanged / FileChanged の hook で $CLAUDE_ENV_FILE が指すファイルに書き込むと、その後の Bash コマンドの実行前に読み込まれます。公式ガイドには direnv と組み合わせる例が載っています。

PreToolUse|ツール実行前に挟むガードレール(最重要)

ターミナルのセキュリティガードのイメージ。PreToolUseフックで危険なコマンドをブロックする

PreToolUse は、操作を実行前に止められる唯一の主要イベントで、安全策の中心になります。Claude がツール呼び出しの引数を決めた後、ツールが実際に動く前に発火します。

いつ発火するか

  • Claude がツール呼び出し(Bash / Edit / Write / Read / Agent / WebFetch / MCP ツールなど)を決めた後
  • ツールが実際に動く前
  • すべての権限モード(dontAsk を含む)で、権限チェックより前に発火します

matcher の対象

ツール名で絞り込みます。

  • 組み込みツール: Bash / PowerShell / Edit / Write / Read / Glob / Grep / Agent / WebFetch / WebSearch / ExitPlanMode など
  • MCP ツール: mcp__<server>__<tool> 形式(例: mcp__github__create_issue)。v2.1.274 からは入力に mcp_server も入ります

入力スキーマ

stdin には次のような JSON が渡されます。

{
  "session_id": "abc123",
  "transcript_path": "/path/to/transcript.jsonl",
  "cwd": "/current/directory",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": { "command": "npm test" },
  "tool_use_id": "toolu_01ABC123..."
}

Write / Edit / Read の tool_input.file_path は常に絶対パスです。

制御出力(2通り)

方式A: exit 2 と stderr のメッセージ

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -qE '(rm -rf|git push --force|drop table)'; then
  echo "破壊的なコマンドのためブロックしました: $COMMAND" >&2
  exit 2
fi
exit 0

方式B: JSON で permissionDecision を返す

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "本番環境への接続は事前承認が必要です"
  }
}

permissionDecision の値は4種類です。

値

動き

allow

権限確認のプロンプトを省略する(ただし settings の deny / ask ルールは越えられない)

deny

実行を中止し、理由を Claude に伝える。bypassPermissions や --dangerously-skip-permissions でも効く

ask

ユーザーに確認を求める。理由はユーザーに表示される。auto mode でも必ずプロンプトが出る

defer

-p 実行で、そのターンのツール呼び出しが1件のときだけ有効。プロセスを終了してツール呼び出しを保持し、Agent SDK 側から後で再開する(それ以外の状況では警告付きで無視)

複数の hook が別々の判断を返した場合は、最も厳しいもの(deny > defer > ask > allow)が採用されます。ただし hook は並列に実行されるので、1つが deny しても他の hook は止まらずに動きます(ログの書き込みなどの副作用は起きます)。

引数を書き換える updatedInput

hookSpecificOutput.updatedInput を返すと、ツールの引数を書き換えられます。注意点は3つです。

  • 引数は丸ごと置き換えです。変えない項目も含めて返します
  • 権限ルールは書き換え後の引数で判定され、v2.1.300 からは Claude 自身の入力と同じ検証を受けます
  • 複数の hook が updatedInput を返すと、最後に終わったものが採用されます。並列実行で順序は決まらないため、1つのツールにつき書き換える hook は1つにしてください

設定例: rm -rf と git push --force を全プロジェクトでブロック

~/.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$HOME\"/.claude/hooks/guard-bash.sh",
            "timeout": 10,
            "onFailure": "block"
          }
        ]
      }
    ]
  }
}

~/.claude/hooks/guard-bash.sh:

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

if echo "$COMMAND" | grep -qE '(^|[;&| ])rm -rf( |$)'; then
  echo "rm -rf は禁止されています。対象を確認してから個別に削除してください。" >&2
  exit 2
fi
if echo "$COMMAND" | grep -qE 'git push.*--force'; then
  echo "force push は禁止です。--force-with-lease か PR を使ってください。" >&2
  exit 2
fi
exit 0

onFailure: "block" を付けているので、jq が入っていない環境やスクリプトが見つからない場合も、Bash の実行は止まります。

補足: 許可モードとの優先関係

PreToolUse の hook は権限チェックより前に動くため、ユーザーが bypassPermissions モードや --dangerously-skip-permissions を使っていても、hook が deny を返せば必ずブロックされます。

逆に、hook が allow を返しても settings の deny / ask ルールは越えられません。公式の表現を借りると、hooks は制限を厳しくすることはできても、権限ルール以上に緩めることはできません。

ひとつ例外として、2026年秋に追加された mods(Claude Code のプロセス内で動く JS/TS の拡張)が tool.check を処理すると、PreToolUse の hook がブロックした呼び出しを承認できる場合があります。managed settings に書いた PreToolUse hook はこの上書きを受けません。mods を入れる場合は、インストール前に claude plugin validate で中身を確認しておくと安心です。

関連記事: Claude Code の auto mode とは

PostToolUse|実行後の品質ゲート

PostToolUseフックで編集後に型チェックやLintを自動実行するイメージ

PostToolUse はツールが成功した直後に発火します。編集したファイルの整形、Lint、型チェックなど、品質チェックの中心になるイベントです。

発火タイミングと入力

  • ツールが成功したときに発火します(失敗時は PostToolUseFailure)
  • stdin には tool_input に加えて、ツールの結果 tool_response、所要時間 duration_ms(権限確認と PreToolUse の時間は含まない)が入ります
  • Bash の場合、変更したファイルの情報が tool_response.bashEditDiff に入ることがあります(v2.1.269〜)
{
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": { "file_path": "/path/to/file.ts", "content": "..." },
  "tool_response": { "filePath": "/path/to/file.ts", "success": true },
  "tool_use_id": "toolu_01ABC123..."
}

制御出力とその限界

重要: PostToolUse は実行済みの操作を取り消せません。 できるのは、結果について Claude に伝えて次の行動で直させることです。

出力

効果

decision: "block" + reason

ツールの結果に理由を添えて Claude に渡す。元の出力も Claude に見える

hookSpecificOutput.additionalContext

補足情報を Claude に渡す

hookSpecificOutput.updatedToolOutput

Claude に見せるツールの出力を差し替える(形式が合わないと組み込みツールでは無視される。実行した処理は戻らない)

hookSpecificOutput.classifierContext

auto mode の分類器向けのメモ(2,000文字まで)

Bash でのファイル変更は Edit|Write では拾えない

matcher を Edit|Write にしていても、Claude が sed や echo > など Bash 経由でファイルを書き換えた場合は発火しません。公式ガイドは、Bash|PowerShell も対象にして git status --porcelain で変更を確かめる、Stop で作業ツリーを1回まとめて検査する、FileChanged を使う、といった方法を挙げています。

代表ユースケース

  1. 型チェック: *.ts / *.tsx の編集後に tsc --noEmit を走らせ、エラーを Claude に返す
  2. ESLint の自動修正: eslint --fix を走らせる
  3. Prettier での整形: 編集したファイルをすぐ整形する
  4. テストの実行: 小さなプロジェクトなら編集のたびに pytest / vitest
  5. 変更の記録: 監査用に git diff を保存する
  6. サブエージェントの結果を親に渡す: Agent ツールの PostToolUse で、サブエージェントの結果に補足を付けて親セッションに返す

設定例: TypeScript 編集後の型チェック

if には1ルールしか書けないため、拡張子ごとにハンドラーを分けます。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "if": "Edit(*.ts)",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/tsc-check.sh",
            "timeout": 120
          },
          {
            "type": "command",
            "if": "Edit(*.tsx)",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/tsc-check.sh",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

Write で新規作成したファイルも対象にしたい場合は、Write(*.ts) と Write(*.tsx) のハンドラーを同じように追加します。

.claude/hooks/tsc-check.sh:

#!/bin/bash
cd "$CLAUDE_PROJECT_DIR" || exit 0
OUTPUT=$(npx tsc --noEmit 2>&1)
if [ $? -ne 0 ]; then
  jq -n --arg reason "$OUTPUT" '{
    decision: "block",
    reason: ("型エラーがあります。修正してください:\n" + $reason)
  }'
fi
exit 0

運用のコツは次の3つです。

  • matcher と if で対象を必ず絞ります。すべてのツール呼び出しで型チェックを走らせると、そのたびに待ち時間が積み重なります
  • 大きなプロジェクトでは変更したファイルだけを検査する、重いテストは Stop にまとめる、といった工夫をします
  • 結果を待つ必要がない処理は async: true で裏で動かせます。この場合は Claude の行動を止められず、結果は次のターンで Claude に渡ります

Stop|Claude の応答終了時に割り込む

Stopフックでタスクの完了を確認してから作業を終える流れのイメージ

Stop は Claude が応答を終えたときに発火します。テストが通っていなければ作業を続けさせる、最後に作業ログを残す、といった「終わる前の確認」に使います。

発火タイミングの注意

  • タスクの完了時だけでなく、応答を終えるたびに毎回発火します
  • ユーザーが中断した場合は発火しません。API エラーで終わった場合は StopFailure が発火します

入力と制御出力

  • 入力: stop_hook_active(すでに Stop hook によって続けさせられている最中なら true)、last_assistant_message(最後の応答)、background_tasks、session_crons
  • 最後の応答の内容を見たいときは、transcript ファイルではなく last_assistant_message を使います。Stop の時点では transcript に最後のメッセージが書き込まれている保証がないためです
  • exit 2、または JSON で decision: "block" + reason を返すと、Claude は止まらずに作業を続け、理由が次の指示として渡ります
  • hookSpecificOutput.additionalContext を返すと、エラー表示を出さずに会話を続けさせられます

最重要の注意点: 無限ループ防止と8回上限

Stop hook が条件を見ずに毎回 block を返すと、次のように終わらなくなります。

Claude の応答終了 → Stop hook → block → Claude が作業を続ける
→ また応答終了 → Stop hook → block → …

Claude Code には歯止めがあり、Stop hook がツール呼び出しを挟まずに8回続けて作業を続けさせると、その次の block は無視されてターンが終わります。ツール呼び出しがあれば回数はリセットされ、上限は環境変数 CLAUDE_CODE_STOP_HOOK_BLOCK_CAP で変えられます。ただし上限に頼るのではなく、スクリプト側で stop_hook_active を確認するのが基本です。

#!/bin/bash
INPUT=$(cat)

# 無限ループ対策: Stop hook による継続中なら、停止を許可する
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0
fi

# 本来の判定(例: テストが通らなければ終わらせない)
cd "$CLAUDE_PROJECT_DIR" || exit 0
if ! npm test --silent > /dev/null 2>&1; then
  echo "テストが失敗しています。テストが通るまで修正を続けてください。" >&2
  exit 2
fi
exit 0

この書き方だと、テストが通らないとき Claude に1回だけ作業を続けさせ、次の終了時は止まることを許します。何度か再挑戦させたい場合は、回数を一時ファイルに記録して判定します。

prompt 型で「全部終わったか」を判定する

条件をコードで書きにくいときは、prompt 型で判定させる方法もあります。

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Claude の最後の応答を確認し、ユーザーが依頼した作業がすべて終わっているかを判定してください。終わっていれば ok を true、残っていれば false にして、残っている作業を reason に書いてください。"
          }
        ]
      }
    ]
  }
}

v2.1.294 で Stop / SubagentStop での prompt 型の判定が改善され、早すぎる停止が起きにくくなっています。ただし判定のたびにモデルを呼ぶため、使用量を消費する点は意識しておいてください。

代表ユースケース

  1. 完了ゲート: テストが通らなければ終わらせない
  2. 作業ログ: 応答ごとの記録を残す
  3. 未コミットの変更の警告: git status で残りを見つけて伝える
  4. Bash 経由の変更の検査: 作業ツリー全体を1回だけまとめて Lint する

SubagentStop|サブエージェント終了時のフック

Claude Code 公式ドキュメント「Create custom subagents」のページ画像

出典: Claude Code Docs(Subagents)

SubagentStop は、Agent ツールで起動したサブエージェントが終了したときに発火します。Stop がメインの Claude の終了を扱うのに対し、SubagentStop はサブエージェントごとの終了を扱います。

matcher

エージェントの種類(agent_type)で絞り込みます。

  • 組み込み: general-purpose / Explore / Plan
  • カスタムエージェント: .claude/agents/ などで定義したエージェント名
  • プラグインのエージェントは、^my-plugin:reviewer$ のようにアンカーを付けて指定するのが推奨です

入力

フィールド

内容

stop_hook_active

Stop と同じ。ループ防止に使う

agent_id / agent_type

どのサブエージェントか

agent_transcript_path

サブエージェント自身の会話記録(subagents/ フォルダ内)

transcript_path

メインセッションの会話記録

last_assistant_message

サブエージェントの最後の応答

制御出力

  • Stop と同じく、exit 2 または decision: "block" + reason でサブエージェントに作業を続けさせられます。理由が次の指示になります
  • stop_hook_active のチェックも同じく必要です
  • 親セッションに情報を渡したいときは、SubagentStop ではなく Agent ツールの PostToolUse を使うのが公式の案内です。SubagentStop の出力はサブエージェント側に返ります

関連するイベントとして SubagentStart があります。生成を止めることはできませんが、additionalContext でサブエージェントの会話の冒頭に情報を入れられます。

代表ユースケース

  1. 成果物の検証: レビュー用エージェントの出力に必須の項目がそろっているか確認し、足りなければ続けさせる
  2. 後片付け: サブエージェント用の一時ファイルを削除する
  3. 実行記録: どのエージェントがいつ終わったかを記録する

設定例: カスタムエージェントの出力を検証

{
  "hooks": {
    "SubagentStop": [
      {
        "matcher": "^code-reviewer$",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/validate-review.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

.claude/hooks/validate-review.sh:

#!/bin/bash
INPUT=$(cat)
[ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ] && exit 0

MESSAGE=$(echo "$INPUT" | jq -r '.last_assistant_message // empty')
if ! echo "$MESSAGE" | grep -q "## 指摘事項"; then
  jq -n '{decision: "block", reason: "出力に「## 指摘事項」の見出しがありません。指摘を一覧にしてから終了してください。"}'
fi
exit 0

関連記事: Claude Code サブエージェントの使い方

Notification|デスクトップ通知・外部サービス連携

Notification は、Claude Code が通知を出すとき(権限確認待ち、入力待ちなど)に発火します。操作は止められませんが、長い作業の途中で席を外していても、確認が必要になった時点で気付けるようになります。

matcher の値

現行の公式リファレンスでは12種類あります。よく使うのは次のとおりです。

matcher の値

発火するとき

permission_prompt

ツールの承認待ちで入力を待っているとき

idle_prompt

作業が終わり、次のプロンプトを待っているとき

auth_success

認証が完了したとき

elicitation_* 系

MCP の入力要求に関する通知

agent_needs_input / agent_completed

エージェントが入力を必要とした・完了したとき

quota_auto_resume_* 系

使用量の上限に関する自動再開の通知(v2.1.234〜)

設定例: macOS の通知と Slack への送信

{
  "hooks": {
    "Notification": [
      {
        "matcher": "idle_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code の作業が終わりました\" with title \"Claude Code\"'"
          }
        ]
      },
      {
        "matcher": "permission_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "curl -s -X POST -H 'Content-Type: application/json' -d '{\"text\":\"Claude Code: ツールの承認が必要です\"}' \"$SLACK_WEBHOOK_URL\"",
            "async": true
          }
        ]
      }
    ]
  }
}

Linux なら notify-send、Windows なら PowerShell の通知で同じことができます(公式ガイドに3 OS分の例があります)。http 型はイベントの JSON をそのまま POST するため、Slack の Incoming Webhook のように決まった形式を求める宛先には、この例のように command 型から curl で送るほうが確実です。

注意点は2つです。

  • Notification は止められません。exit 2 を返してもユーザーに表示されるだけです
  • 通知処理で待たせたくない場合は async: true を付けます

補足イベント|知っておくと差がつく4つ

UserPromptSubmit|ユーザー入力直後

プロンプトを送信した直後、Claude が処理を始める前に発火します。情報を追加したり、入力内容を検査してブロックしたりできます。

v2.1.299 から、スキルやカスタムスラッシュコマンドが展開される前に実行されるようになりました。展開後の内容を検査したい場合は UserPromptExpansion を使います。既定のタイムアウトは30秒です。

代表的な使い方は次の2つです。

  • ブランチ名や git status を毎回渡す
  • 機密情報らしき文字列を含むプロンプトを止める
#!/bin/bash
BRANCH=$(git branch --show-current 2>/dev/null || echo "no-git")
STATUS=$(git status --short 2>/dev/null)
jq -n --arg branch "$BRANCH" --arg status "$STATUS" '{
  hookSpecificOutput: {
    hookEventName: "UserPromptSubmit",
    additionalContext: ("現在のブランチ: " + $branch + "\n未コミットの変更:\n" + $status)
  }
}'

SessionStart|セッション開始・再開時

matcher で発火の条件を絞れます。

  • startup: 新しいセッション
  • resume: --resume / --continue / /resume
  • clear: /clear の後
  • compact: コンテキスト圧縮の後
  • fork: セッションを分岐したとき

代表的な使い方: プロジェクトの前提情報を渡す、圧縮で失われがちなルールを compact のときに渡し直す(公式ガイドの例)、$CLAUDE_ENV_FILE で環境変数を設定する。

PostToolUseFailure|ツール失敗時

ツールが失敗したときに発火します。失敗の記録、外部へのアラート、additionalContext で別のやり方を提案する、といった用途に使えます。実行は失敗しているので止める必要はなく、ブロックもできません。

SessionEnd|セッション終了時

終了の理由(clear / resume / logout / prompt_input_exit / other)で絞り込めます。一時ファイルの削除やログの保存に使いますが、持ち時間はすべての hook 合わせて1.5秒です。重い処理は入れないか、timeout を明示して延ばします。

Hooks / Permissions / CLAUDE.md の使い分け

「何を Hooks で書き、何を Permissions で設定し、何を CLAUDE.md に書くか」は迷いやすいところです。単純な許可・拒否は Permissions、必ず起こしたい処理は Hooks、望ましい振る舞いの指示は CLAUDE.md と考えると整理しやすくなります。

手段

性格

向いている用途

向いていない用途

Permissions

宣言的なルール

ツール名やパスでの単純な許可・拒否

条件分岐、後処理、外部連携

CLAUDE.md

Claude が読む指示

コーディング規約、プロジェクトの前提、好ましい進め方

毎回必ず起きてほしい処理

Hooks

イベントで必ず動く処理

Permissions で書けない条件のブロック、整形、検査、記録、通知

単純な許可・拒否(Permissions で足りる)

Skills

必要なときに読み込む手順書

特定作業のやり方を Claude に教える

強制したいルール

mods

Claude Code の中で動く JS/TS の拡張

権限判定やツールの挙動を細かく組み込む

手軽な自動化(Hooks で足りる)

おすすめの組み合わせ: 基本的な拒否は Permissions の deny ルール、Permissions で書けない条件や後処理・通知は Hooks、チームの書き方や進め方は CLAUDE.md、作業手順のまとまりは Skills に置きます。

関連記事: Claude Code の Skills と Hooks の違い / Claude Code スラッシュコマンドの使い方

バージョン別機能追加履歴

Anthropic 公式の Claude Code GitHub リポジトリのページ画像

出典: GitHub(anthropics/claude-code)

Hooks はリリースのたびに細かく変わっています。2026年夏〜秋(v2.1.210〜v2.1.301)の公式 changelog から、使い方に関わる主な変更をまとめました。

バージョン

主な変更

v2.1.301

プラグインやスキルの hook が ask を返したとき、どのプラグイン・スキルからかを確認画面に表示。WorktreeCreate の worktree を、未 push のコミットがあるのに確認なしで削除する不具合を修正

v2.1.300

PreToolUse / PermissionRequest の updatedInput を Claude 自身の入力と同じように検証。agent 型が model の指定を無視する不具合を修正

v2.1.299

UserPromptSubmit がスキル・カスタムスラッシュコマンドの展開前に実行されるよう変更

v2.1.296

--debug で command hook のコマンド・結果・所要時間を記録

v2.1.295

onFailure: "block" を追加。plan mode で PreToolUse が allow した書き込みが実行されてしまう不具合を修正

v2.1.294

命令形で書いた prompt / agent 型 hook がブロックすべきものを通してしまう不具合を修正。Stop / SubagentStop の prompt 型の判定を改善

v2.1.292

hook の出力に含まれる <system-reminder> タグをエスケープ

v2.1.287

ターミナル版で mods が既定で有効に

v2.1.274

PreToolUse の入力に mcp_server を追加

v2.1.269

PostToolUse の Bash の結果に bashEditDiff を追加

v2.1.257

共通の入力に scratchpad_dir を追加

v2.1.236

PostToolUse で classifierContext を返せるように

v2.1.218

プロジェクトのサブエージェント frontmatter の hooks は、フォルダの信頼を承認した後だけ実行

v2.1.214

Edit(src/**) がトップレベルの src だけに一致するよう変更。SessionStart に fork を追加

v2.1.210

prompt 型の PreToolUse で ok: false のときの既定を「ターン終了」に変更(continueOnBlock で以前の動き)

注意: この表は公式 changelog で確認できた範囲です。それより前の変更や各機能の最初のバージョンは載せていません。手元のバージョンは claude --version で確認してください。

実務で即使える 10 のレシピ

Hooksを組み合わせた開発ワークフローの自動化イメージ

公式ガイドの例をもとに、すぐ使える形にまとめました。スクリプトは chmod +x で実行権限を付けてから使ってください。

1. rm -rf / force push のブロック

最初に入れておきたい hook です。~/.claude/settings.json の PreToolUse に matcher Bash で guard-bash.sh を登録し、onFailure: "block" を付けておけば全プロジェクトで効きます。スクリプトは tool_input.command を jq で取り出し、rm -rf や git push --force に一致したら stderr に理由を書いて exit 2 で終わる形にします。正規表現で検出する方式なので、変数展開や別名を使ったコマンドまでは防げません。permissions の deny ルールと併用してください。

2. .env / 秘密鍵の読み取り・編集防止

if は1ルールずつなので、守りたいファイルの種類ごとにハンドラーを並べます。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Read|Edit|Write",
        "hooks": [
          { "type": "command", "if": "Read(*.env)", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/deny-secret.sh" },
          { "type": "command", "if": "Edit(*.env)", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/deny-secret.sh" },
          { "type": "command", "if": "Write(*.env)", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/deny-secret.sh" },
          { "type": "command", "if": "Read(*.pem)", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/deny-secret.sh" }
        ]
      }
    ]
  }
}

.claude/hooks/deny-secret.sh:

#!/bin/bash
FILE=$(jq -r '.tool_input.file_path // empty')
echo "機密ファイル ($FILE) へのアクセスはブロックされています。" >&2
exit 2

公式ガイドには、.env・package-lock.json・.git/ を守るスクリプトの例も載っています。ファイルの読み取りを確実に禁止したいなら、permissions の deny ルールにも同じパターンを入れておくのが安全です。

3. TypeScript 型チェック自動実行

PostToolUse に tsc-check.sh(tsc --noEmit を実行し、エラーがあれば decision: "block" で Claude に返すスクリプト)を登録し、if で Edit(*.ts) と Edit(*.tsx) を別々のハンドラーとして指定します。プロジェクト全体の型チェックが重い場合は、編集のたびではなく Stop で1回だけ実行する形に変えると待ち時間を抑えられます。

4. ESLint 自動修正

#!/bin/bash
FILE=$(jq -r '.tool_input.file_path // empty')
case "$FILE" in
  *.ts|*.tsx|*.js|*.jsx) npx eslint --fix "$FILE" >/dev/null 2>&1 ;;
esac
exit 0

5. Prettier 自動整形

公式ガイドの例です。PostToolUse の matcher を Edit|Write にして、次のコマンドを command に書きます。

{
  "type": "command",
  "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}

6. ブランチ名・git status の自動注入(UserPromptSubmit)

UserPromptSubmit に、git branch --show-current と git status --short の結果を hookSpecificOutput.additionalContext として返すスクリプトを登録します。毎回、今どのブランチで何が変わっているかが Claude に伝わるので、ブランチを取り違えた作業を減らせます。v2.1.299 以降はスキルやスラッシュコマンドの展開前に実行されるため、/ で始まるコマンドを送ったときも同じように情報が付きます。

7. 作業ログの自動記録(Stop)

#!/bin/bash
INPUT=$(cat)
[ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ] && exit 0
echo "$INPUT" | jq -c '{time: (now | todate), session_id, last_message: .last_assistant_message}' \
  >> "$CLAUDE_PROJECT_DIR/.claude/work-log.jsonl"
exit 0

8. 未コミット変更の警告(Stop)

#!/bin/bash
INPUT=$(cat)
[ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ] && exit 0
cd "$CLAUDE_PROJECT_DIR" || exit 0
if [ -n "$(git status --porcelain)" ]; then
  echo "未コミットの変更があります。コミットするか、残す理由をユーザーに説明してください。" >&2
  exit 2
fi
exit 0

9. パッケージ追加の事前確認(PreToolUse)

依存関係を勝手に増やされないよう、npm install / pnpm add / yarn add には ask を返してユーザーに確認させます。

#!/bin/bash
COMMAND=$(jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -qE '(^|[;&| ])(npm install|npm i|pnpm add|yarn add) '; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "ask",
      permissionDecisionReason: "パッケージの追加は確認が必要です"
    }
  }'
fi
exit 0

10. テストを裏で回して結果を次のターンに渡す(async)

async: true を付けた command 型は、Claude を待たせずに裏で動きます。結果の additionalContext は次のターンで Claude に渡ります。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/run-tests-async.sh",
            "async": true
          }
        ]
      }
    ]
  }
}

非同期の hook はブロックも判断もできません。テストが落ちたときにすぐ Claude を起こしたいなら、asyncRewake を使って exit 2 で知らせる方法があります。-p 実行では、プロセスの終了時にまだ動いている非同期 hook は止められます。

よくある5つの落とし穴と回避策

1. exit 1 でブロックしたつもりになっている

症状: ガードを書いたのに危険なコマンドが実行される。警告だけ出て止まらない。

原因: exit 0 と 2 以外はブロックしないエラーとして扱われ、処理が続く。

対処: 止めたいときは必ず exit 2。hook の起動失敗でも止めたいなら onFailure: "block" を付ける。

2. JSON を返しているのに効かない

症状: permissionDecision: "deny" を返しているのに止まらない。additionalContext が Claude に届いていない。

原因: よくあるのは次の3つです。

  • permissionDecision や additionalContext を hookSpecificOutput の外(トップレベル)に書いている
  • .bashrc などのシェルの設定ファイルに echo があり、その出力が JSON の前に混ざって JSON として読めなくなっている(Git Bash や BASH_ENV を設定している環境で起きやすい)
  • exit 0 以外で終了している(JSON は exit 0 のときだけ読まれる)

対処: claude --debug で起動し、Hook JSON output had unrecognized keys などのメッセージを確認する。stdout には JSON 以外を出さない。

3. パスやコマンドが見つからずに動いていない

症状: hook が動いているはずなのに何も起きない。チームの他の人の環境でだけ動かない。

原因: 相対パスでスクリプトを指定している、jq が入っていない、実行権限がない、など。

対処: スクリプトは "$CLAUDE_PROJECT_DIR"/.claude/hooks/... のように絶対パスで指定する(シェル形式ではダブルクォートで囲む。args を使う exec 形式ならクォートは不要)。ガード用の hook には onFailure: "block" を付け、動かなかったときに操作がそのまま通らないようにする。/hooks で登録内容を確認する。

4. matcher や if を書いたのに無視される

症状: Stop に matcher を書いたのに全部で動く。SessionStart に if を付けたら動かなくなった。

原因: matcher に対応していないイベント(Stop、UserPromptSubmit など)では matcher が無視され、ツール系以外のイベントに if を付けるとその hook が動かなくなる。matcher の大文字・小文字の違いも原因になる。

対処: matcher と if が使えるイベントを確認する。bash ではなく Bash のように正確な名前で書く。

5. 処理が重くて作業全体が遅くなる

症状: ファイルを1つ直すたびに待たされ、作業が進まない。

原因: PostToolUse で、すべての編集に対して重い型チェックやテストを走らせている。prompt 型の hook が多く、毎回モデルの判定を待っている。

対処は次のとおりです。

  • matcher と if で対象を絞る
  • 変更したファイルだけを検査する
  • 重い検査は Stop にまとめるか、async: true で裏で動かす
  • timeout を明示し、待ち時間の上限を決めておく

企業導入時のセキュリティ統制

企業でAIツールを使う際のセキュリティ統制のイメージ

チームや組織で Claude Code を使う場合、個人任せの hook 設定では統制が効きません。hooks は「設定ファイルで配られる実行コード」だと考えて、誰が書いたものが、どの環境で動くのかを管理します。

hooks はあなたの権限で何でも実行できる

公式ドキュメントは、command 型の hook はユーザーの権限すべてでシェルコマンドを実行し、アカウントが触れるファイルはすべて変更・削除・参照できると警告しています。追加する前に中身を確認し、テストしてから使うことが前提です。

他人のリポジトリと -p 実行の注意

  • 対話モードでは、フォルダの信頼を承認するまで、~/.claude/settings.json を含むすべての設定ファイルの hooks は実行されません
  • claude -p や Agent SDK では信頼の確認画面が出ず、フォルダは信頼済みとして扱われます。つまり、他人が作ったリポジトリに claude -p をかけると、そのリポジトリの .claude/settings.json に書かれた hooks がそのまま動きます
  • 自分で書いていないリポジトリに -p を使う前に .claude/ の中身を確認する、--bare で起動する、--settings '{"disableAllHooks": true}' で hooks を止める、のいずれかで対策します

実際に、セキュリティ企業 Check Point Research は2026年2月、「.claude/settings.json の hooks が、新しいディレクトリで起動したときに確認なしで実行されてしまう」問題(CVSS 8.7)を公表しています(Check Point Research)。この問題は v1.0.87 で修正済みで、公表前に対応が終わっていましたが、リポジトリを開くだけで hooks が動きうることを示した例です。

関連記事: Claude Code GitHub Actionsの使い方

管理者向けの制御

設定

動作

allowManagedHooksOnly: true(managed settings)

ユーザー・プロジェクト・ローカル・プラグインの hooks を止め、管理者が配った hooks だけを動かす。/hooks に Only hooks from managed settings run here と表示される

enabledPlugins(managed settings)

管理者が強制的に有効にしたプラグインの hooks は、allowManagedHooksOnly の下でも動く

disableAllHooks: true

hooks を止める。ただしユーザー・プロジェクト・ローカルの設定で指定しても、managed settings の hooks は止まらない。設定の優先順位が適用されるため、プロジェクトの false がユーザーの true を上書きすることもある

onFailure: "block"

ガード用の hook が失敗したときに操作を止める

allowedHttpHookUrls / httpHookAllowedEnvVars

HTTP hook の送信先 URL と、ヘッダーに差し込める環境変数を制限する

managed settings に置いた PreToolUse hook は、mods による承認でも上書きされません。組織として必ず止めたいものは managed settings に置くのが確実です。

関連記事: Claude Code のチーム導入ガイド

mods と settings の hooks の違い

mods は、Claude Code のプロセスの中で動く JS/TS のイベントハンドラーで、v2.1.287 からターミナル版で既定で有効です。settings に書く hooks と比べて、次の点に注意します。

  • mods はユーザーの権限で動き、サンドボックスの外で実行されます
  • mod が PreToolUse の hook でブロックされた呼び出しや、ask ルールの対象を承認できる場合があります
  • インストール前に claude plugin validate で、含まれる hooks や呼び出しを確認できます
  • disableAllHooks を設定すると、利用者が入れた mods も止まります(組み込みの mods は止まりません)

mods は新しい機能で、細かい仕様が変わる可能性があります。使う場合は公式の mods の概要 を確認してください。

HTTP hook の環境変数補間

監査サーバーにツールの呼び出しを送る場合、HTTP hook のヘッダーで $VAR / ${VAR} 形式の環境変数を使えます。ただし、allowedEnvVars に書いた変数だけが展開され、それ以外は空になります。認証トークンが意図せず送られるのを防ぐための仕組みです。

{
  "type": "http",
  "url": "https://audit.example.com/hooks",
  "headers": {
    "Authorization": "Bearer ${AUDIT_TOKEN}"
  },
  "allowedEnvVars": ["AUDIT_TOKEN"]
}

スクリプトを書くときの注意

公式のベストプラクティスから、特に大事なものを挙げます。

  1. 入力を検証する: stdin の JSON は jq などで値として取り出し、シェルの文字列にそのまま埋め込まない。変数は "$VAR" とクォートする
  2. パスを確かめる: .. を含むパスで想定外の場所を触られないようにする
  3. 機密ファイルを避ける: .env、.git/、鍵ファイルを hook の処理対象にしない
  4. additionalContext に命令を書かない: 事実として書く。外部から取ってきた文字列をそのまま流し込まない
  5. 裏で動かすプロセスの出力を切り離す: hook の中でバックグラウンドのプロセスを起動するなら、判断の出力を先に済ませ、そのプロセスの stdout / stderr はリダイレクトしておく

社内の業務システムとつなぐとき

Hooks で監査ログを社内サーバーに送ったり、MCP 経由で社内のチケット管理につないだりし始めると、個人の設定ファイルの範囲では管理しきれなくなります。試作した仕組みを社内の実データや既存システムにつないで業務で使う段階では、権限の設計、ログの保管、障害時の対応、保守を誰が引き受けるかを決めておく必要があります。その進め方はAIで作ったアプリを開発会社へ引き継ぐにはで整理しています。

関連記事: Claude Codeのセキュリティと安全な使い方 / Claude Code の auto mode とプロンプトインジェクション対策

Hooks 導入ロードマップ|どこから始めるか

いきなり多くのイベントに hook を入れると、どれが原因で動きがおかしいのか分からなくなります。次の順番で少しずつ増やすのが安全です。

Step 1: 安全ネット(初日)

  • PreToolUse で rm -rf / git push --force / .env の編集を止める
  • exit 2 と onFailure: "block" の使い方に慣れる
  • /hooks で登録内容を確認する

Step 2: 情報の追加(1週間以内)

  • UserPromptSubmit でブランチ名と git status を渡す
  • SessionStart の compact で、圧縮後に必要なルールを渡し直す

Step 3: 品質チェック(2週間以内)

  • PostToolUse で Prettier・ESLint・型チェックを走らせる
  • matcher と if で対象を絞り、待ち時間が気にならない範囲に収める

Step 4: 完了チェック(必要に応じて)

  • Stop でテスト未通過・未コミットの変更を検知する
  • stop_hook_active の確認を必ず入れる

Step 5: チームへの展開

  • .claude/settings.json をコミットしてチーム共通の hooks を配る
  • 組織として統制が必要なら、managed settings と allowManagedHooksOnly を検討する
  • -p や CI で使うリポジトリの .claude/ を確認する運用を決める
  • Notification で Slack に通知し、長い作業の確認待ちを見逃さないようにする

Claude Code Hooks がおすすめの人・そうでない人

こんな人におすすめ

  • Claude に任せた作業での事故が心配な開発者: 危険な操作を設定で確実に止めたい
  • チームで Claude Code を使う技術リーダー: メンバー全員に同じ品質チェックと安全策を効かせたい
  • 既存の Lint・型チェック・テストを AI での開発に組み込みたい人: PostToolUse でそのままつなげられる
  • 監査ログが必要な企業: ツールの呼び出しや設定変更を外部に記録したい
  • 長い作業を任せることが多い人: 確認待ちを通知で受け取り、完了の条件を Stop で決めておきたい

おすすめしないケース

  • Claude Code を使い始めたばかりの段階: まず通常の使い方と permissions に慣れてから足すほうが、問題の切り分けがしやすい
  • 小さな個人スクリプトを書くだけの用途: 設定の手間に見合う効果が出にくい
  • シェルスクリプトに慣れていない人: JSON の扱いや終了コードでつまずきやすい。まずは公式ガイドの例をそのまま使うところから始める
  • 確実に止めたいガードを prompt / agent 型だけで作りたい場合: 判定に揺れがあり、agent 型は公式が実験的としている。command 型か permissions と組み合わせる

よくある質問(FAQ)

Q1. Hooks の利用に追加料金はかかりますか?

Hooks の機能自体に追加料金はありません。ただし Claude Code を使うには Pro(月払い $20)以上の有料プランか API の利用が必要で(2026年10月時点の公式料金ページ)、prompt 型と agent 型の hook はモデルを呼ぶため、そのぶんプランの使用量や API 料金を消費します。command / http / mcp_tool 型はモデルを呼びません。

関連記事: Claude Code料金(日本円換算) / Claude Code のコスト削減術

Q2. 個別の hook だけを一時的に無効にできますか?

個別の hook を無効にするスイッチはありません。止めたい hook のエントリを設定ファイルから削除するか、すべてを止めるなら disableAllHooks: true を使います。ただし managed settings の hooks は、ユーザーやプロジェクトの設定では止まりません。

Q3. Windows でも使えますか?

使えます。command 型で "shell": "powershell" を指定すると PowerShell で実行され、公式ガイドには Windows 向けの通知の例もあります。Bash 向けに書いたスクリプトをそのまま使う場合は、Git Bash などの環境で、シェルの設定ファイルの出力が JSON に混ざらないかを確認してください。

Q4. Hook の中から Claude に追加の指示を出したいのですが、どう書けばいいですか?

additionalContext を使います。命令口調で書くより「テストが3件失敗している」「このファイルは自動生成されている」のように事実として書くほうが、公式の推奨に沿っており、プロンプトインジェクション対策に引っかかりにくくなります。1フィールドの上限は10,000文字で、超えた分はファイルに保存され、2,000文字のプレビューだけが渡ります。

Q5. 複数の hook の実行順は決められますか?

決められません。同じイベントに一致した hook は並列で実行されます。「Aで書き換えてからBで検査する」のような順番に頼る設計は成り立たないので、順番が必要な処理は1つのスクリプトにまとめてください。

Q6. Hook から / コマンドやツールを直接実行できますか?

できません。command 型の hook が Claude に影響を与えられるのは stdout・stderr・終了コードだけです(http 型はレスポンスの本文)。別の作業をしてほしいときは、additionalContext や Stop の reason で Claude に伝え、Claude 自身に実行させます。MCP サーバーのツールを呼ぶだけなら mcp_tool 型が使えます。

Q7. MCP ツールに対する hook は書けますか?

書けます。matcher に mcp__<server>__<tool> の形で指定し、サーバー全体なら mcp__github__.* のように書きます。プラグインに入っている MCP サーバーは mcp__plugin_<プラグイン名>_<サーバー名>__<ツール名> という名前になる点に注意してください。

関連記事: Claude Code MCP連携ガイド

Q8. 非同期(async)の hook ではどこまでできますか?

async: true を付けた command 型の hook は裏で動き、Claude を待たせません。その代わり、ブロックや許可の判断は一切できず、additionalContext などの結果は次のターンで Claude に渡ります。Claude が入力待ちの間は、次の操作があるまで届きません。テストの失敗などですぐ Claude を動かしたい場合は asyncRewake を使い、exit 2 で知らせます。

Q9. agent タイプのハンドラーは本番で使えますか?

2026年10月時点では、公式ドキュメントが agent 型を「実験的」としており、本番では command 型を勧めています。使う場合は検証用途に限定し、ブロックしたい処理は command 型や permissions で別に確実に止まるようにしてください。

Q10. GitHub Actions などの CI 環境でも Hooks は動きますか?

動きます。CI で claude -p を実行する場合も、リポジトリの .claude/settings.json の hooks が読み込まれます。注意したいのは、-p ではフォルダの信頼確認が出ないため、外部からのプルリクエストなどで書き換えられた hooks もそのまま動く点です。外部の変更を扱うワークフローでは --settings '{"disableAllHooks": true}' を付けるか、hooks の変更を必ずレビューする運用にしてください。

まとめ|Hooks は「LLM 任せにしない」ための基盤

Claude Code Hooks は、Claude Code に必ず動く安全策と自動化を組み込む仕組みです。Claude が正しく判断してくれることを期待するのではなく、「この操作は実行させない」「編集したら必ず整形する」を設定ファイルで決められます。

  • 2026年10月時点(v2.1.301)で、33種類のイベントと5タイプのハンドラーが使える
  • 主要4イベントは、止めるなら PreToolUse、結果を検査するなら PostToolUse、終わり方を確かめるなら Stop / SubagentStop
  • 止めたいときは必ず exit 2。exit 1 では止まらない。ガード用の hook には onFailure: "block" を付ける
  • JSON の permissionDecision や additionalContext は hookSpecificOutput の中に書く
  • Stop / SubagentStop では stop_hook_active を必ず確認する
  • hooks は制限を厳しくはできても、権限ルール以上に緩めることはできない
  • -p や SDK では他人のリポジトリの hooks も確認なしで動くため、チームでは managed settings と運用ルールで管理する
  • agent 型は実験的。確実に止めたいものは command 型と permissions で

まずは「rm -rf のブロック」と「UserPromptSubmit でのブランチ情報の追加」の2つから始め、慣れてきたら品質チェックと完了チェックを足していくのがおすすめです。

合わせて読みたい関連記事

公式リソース

このツールを自社のシステムや業務にどう組み込むか、ご提案します

この記事の著者

AI革命

AI革命

編集部

AI革命株式会社の編集部です。最新のAI技術動向から実践的な導入事例まで、企業のデジタル変革に役立つ情報をお届けしています。豊富な経験と専門知識を活かし、読者の皆様にとって価値のあるコンテンツを制作しています。

AI・システム開発は、開始価格を公開しています

既存システムの改修 100万円〜・PoC 300万円〜(税別)。相談内容を送っていただくと、2営業日以内に担当者からご返信します