Claude Code2026年4月更新

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

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

この記事のポイント

Claude Code の Hooks 機能の全体像と、主要4イベント(PreToolUse / PostToolUse / Stop / SubagentStop)の仕様・設定例・つまずきやすい落とし穴を公式仕様ベースで整理。rm -rf の事前ブロック、編集後の型チェック自動実行、無限ループ回避まで実務で使えるレシピ付き。

Claude Code Hooks は、Claude Code のライフサイクル上の特定イベント(ツール実行前後、セッション開始・終了、ユーザー入力送信時など)でユーザー定義のシェルコマンドや LLM 判定を自動実行できる仕組みです。rm -rf のような破壊的コマンドの事前ブロック、編集後の型チェック自動実行、作業ログの自動記録など、LLM の判断に依存せず「必ず発生する」処理を設定ファイルで定義できるのが最大の特徴です。

この記事でわかること:

  • Claude Code Hooks の全体像と4つのハンドラータイプ
  • 主要4イベント(PreToolUse / PostToolUse / Stop / SubagentStop)の仕様・設定例・制御出力の違い
  • 実務で即使える10個のレシピ(破壊的コマンド防止・型チェック自動化・コンテキスト注入ほか)
  • exit 1 では止まらない・無限ループに陥るなど、よくある5つの落とし穴と回避策
  • 企業導入時のセキュリティ統制(管理者ポリシー・hook 無効化)

対象読者は、Claude Code を導入済みで「自動化とガードレールをもう一段階深めたい」中級〜上級の開発者、およびチームに Claude Code を展開する前にガバナンスを整えたい技術リーダーです。本記事は公式リファレンス(v2.1.x 系)に基づいて整理しています。

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

Claude Code Hooks は、Claude Code の内部イベントに対してフックスクリプトを登録し、決定論的なガードレールと自動化を実現する仕組みです。Anthropic の公式ドキュメント(hooks-guide)では、「LLM が自分でそう判断するのに依存するのではなく、特定のアクションが常に発生することを保証する」ための機能と説明されています。

Hooks が解く課題は3つあります。

  1. 安全性: 破壊的コマンド(rm -rfgit push --force、本番 DB への書き込みなど)を LLM の判断に委ねず、設定ファイル側で確実にブロックしたい
  2. 品質: ファイル編集後に Prettier・ESLint・型チェック・テストを必ず走らせたい
  3. 運用: セッション開始時にプロジェクトコンテキストを自動注入したり、作業ログを自動記録したい

これらを Claude Code の settings.json に記述するだけで、セッション再起動や独自ツール不要で即運用できます。

できることの代表例

カテゴリ

代表ユースケース

事前ブロック

rm -rf /git push --force.env 編集、本番環境への接続

自動品質担保

Prettier / ESLint / TypeScript 型チェック / pytest / go test の自動実行

コンテキスト注入

現在時刻・git status・ブランチ名・最近の issue を毎ターン自動追加

監査・ロギング

全ツール呼び出しを外部サーバーに POST、セッションコスト記録

停止ゲート

テスト未通過なら Claude を停止させない、未コミット時に警告

サブエージェント検証

Task ツール完了後の成果物検証・リソースクリーンアップ

Hooks の提供状況

  • 提供元: Anthropic(Claude Code に標準同梱、追加料金不要)
  • 対応環境: macOS / Linux / Windows(Claude Code が動く全環境)
  • 設定箇所: ~/.claude/settings.json(ユーザー全体)、.claude/settings.json(プロジェクト)、.claude/settings.local.json(Git 非共有)など複数スコープ
  • 確認コマンド: Claude Code 内で /hooks を実行すると設定済み hooks を一覧できる(読み取り専用ブラウザ)

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

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

Hooks は複数のスコープで多層的に設定できます。優先順位は厳密に決まっており、管理者ポリシー > プロジェクト > ユーザー > プラグイン/スキルの順で評価され、上位設定は下位設定を上書き・制限できます。

配置場所

スコープ

Git 共有

用途

管理ポリシー設定

組織全体

管理者配布

エンタープライズ統制

.claude/settings.json

単一プロジェクト

チーム共通のガードレール

.claude/settings.local.json

単一プロジェクト

不可(gitignore)

個人の実験的 hook

~/.claude/settings.json

ユーザー全体

不可

個人の全プロジェクト共通 hook

プラグイン hooks/hooks.json

プラグイン有効時

プラグインにバンドル

再利用可能な hook 集

スキル / エージェント frontmatter

コンポーネント有効時

コンポーネント内

特定スキル限定 hook

運用のコツ: チームで共有したい最低限のガードレール(rm -rf 防御、.env 保護など)は .claude/settings.json にコミットし、個人ごとの実験用 hook は .claude/settings.local.json に置くのが基本パターンです。

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

Hooks 設定は 3 階層のネスト構造で記述します。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh",
            "timeout": 600,
            "if": "Edit(*.env)"
          }
        ]
      }
    ]
  }
}

各階層の役割

  1. イベント名PreToolUse など): ライフサイクル上のどのタイミングで発火するか
  2. matcher: どのツール・条件にマッチしたときに実行するか(次項で詳述)
  3. hooks: 実際に実行するハンドラー(command / http / prompt / agent の4タイプ)

matcher 構文の評価ルール

matcher は文字列の中身によって評価方式が変わります。

パターン

評価方式

"*"""、省略

全マッチ(毎回発火)

すべてのツール

英数字・_| のみ

完全一致 or パイプ区切り

Bash / Edit|Write

それ以外の文字を含む

JavaScript 正規表現として評価

^Notebook / mcp__.*__write.*

if フィールド(v2.1.85 以降)でツール引数レベルのフィルタ

Claude Code v2.1.85 以降では、if フィールドで許可ルール構文を使ったツール引数レベルの条件指定ができます。

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

ポイント:

  • matcher でツール名を絞り、if で引数を絞るのが推奨パターン
  • 複合コマンド(npm test && git push)はサブコマンドごとに評価される
  • if が機能するのはツール系イベント(PreToolUse / PostToolUse / PostToolUseFailure / PermissionRequest / PermissionDenied)のみ

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

Hook が実行する処理は 4 つのタイプから選べます。用途で使い分けるのが基本です。

タイプ

主な用途

LLM 利用

コスト

備考

"type": "command"

シェルコマンド実行(最も基本)

なし

なし

stdin で JSON 受信、stdout/stderr/終了コードで応答

"type": "http"

外部 URL へ POST

なし

通信コストのみ

監査サーバー連携。allowedEnvVars でヘッダー補間

"type": "prompt"

単一ターン LLM 判定

あり

API トークン消費

デフォルト Haiku で {"ok": true/false} を返させる

"type": "agent"(実験的)

サブエージェントによるファイル調査を伴う判定

あり

API トークン消費大

デフォルト 60 秒・最大 50 ターン

最初は command から始めるのが安全です。 prompt / agent は LLM 呼び出しコストが発生し、かつレスポンスに揺らぎが出るため、確実性が求められるガードレールには向きません。agent公式が実験的と明記しているため、本番運用前に挙動を十分確認してください。

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

Exit Code と制御出力|最頻出ミス領域

Hook の挙動を決める最重要ポイントが終了コード(Exit Code)です。ここを誤解していると「設定した気になっていたのに何も守られていなかった」状態が起きます。

Exit Code の動作

Exit Code

動作

0

成功。stdout が JSON なら制御出力として解釈。UserPromptSubmit / SessionStart では stdout がそのままコンテキストに追加される

1

非ブロッキングエラー。stderr はトランスクリプトに表示されるのみで、アクションは続行される

2

ブロッキングエラー。アクションがブロックされ、stderr が Claude へフィードバックとして渡される

その他

非ブロッキング。<hook name> hook error として通知のみ

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

セキュリティゲートを作ったつもりで exit 1 を使ってしまうと、警告は表示されるが実行自体はブロックされません。ポリシー強制目的では必ず exit 2 を使ってください。

# ❌ 悪い例: 警告は出るが 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

構造化 JSON 出力(exit 0 時)

より柔軟な制御をしたい場合は、stdout に JSON を返します。

{
  "continue": true,
  "stopReason": "...",
  "suppressOutput": false,
  "systemMessage": "...",
  "decision": "block",
  "reason": "...",
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "...",
    "updatedInput": { "field": "value" },
    "additionalContext": "...",
    "retry": true
  }
}

注意点:

  • コンテキスト系フィールド(additionalContext など)は 10,000 文字上限
  • PreToolUse の許可判定は hookSpecificOutput.permissionDecision を使う(トップレベル decision ではない)
  • exit 2 と JSON 制御を混在させない(Claude Code は exit 2 時に JSON を無視する)
  • 複数 hook の決定は最も制限的なものが採用される(deny > defer > ask > allow

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

Claude Code は 20 以上のイベントで hook を発火できます。まずは全体像を一覧で把握し、主要イベントを次章で詳しく解説します。

イベント名

発火タイミング

ブロック可否

代表用途

PreToolUse

ツール実行直前

破壊的コマンド防止

PostToolUse

ツール成功直後

フィードバックのみ

型チェック・Lint

PostToolUseFailure

ツール失敗直後

コンテキスト注入可

失敗ログ・代替提案

UserPromptSubmit

ユーザー入力送信直後

✅(破棄)

時刻・git status 注入

Stop

Claude 応答終了時

✅(継続強制)

完了ゲート

SubagentStop

サブエージェント終了時

✅(継続強制)

成果物検証

SessionStart

セッション開始・再開時

コンテキスト注入

プロジェクト情報注入

SessionEnd

セッション終了時

不可(クリーンアップ)

一時ファイル削除

Notification

許可プロンプト表示・アイドル時

デスクトップ通知

PermissionRequest

許可ダイアログ表示時

自動承認・自動拒否

PermissionDenied

Auto モードで分類器が拒否時

retry 指示可

再試行促進

UserPromptExpansion

スラッシュコマンド展開時

コマンド展開検証

PreCompact / PostCompact

コンテキスト圧縮前後

ルール再注入

FileChanged

監視ファイル変更時

direnv 連携

CwdChanged

作業ディレクトリ変更時

環境切替

ConfigChange

設定変更時

リロード通知

InstructionsLoaded

CLAUDE.md 読込時

ルール検証

WorktreeCreate / Remove

git worktree 作成・削除時

ワークスペース管理

TaskCreated / TaskCompleted

タスク作成・完了時

タスク監査

SubagentStart

サブエージェント起動時(観測専用)

ロギング

StopFailure

API エラーでターン終了時

出力無視

エラー監視

TeammateIdle

Agent Team のチームメイトがアイドル時

チーム協調

Elicitation / ElicitationResult

MCP サーバーが入力要求する前後

MCP 統合

以降、タイトルに掲げた主要4イベント(PreToolUse / PostToolUse / Stop / SubagentStop)を深掘りします。

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

PreToolUse は Hooks の中で最も重要なイベントです。Claude がツール呼び出しのパラメータを生成した直後、実際のツール実行の直前に発火します。ここでブロックすれば、破壊的な操作を確実に止められます。

いつ発火するか

  • Claude がツール呼び出し(Bash / Edit / Write / Read / Agent / WebFetch / MCP ツールなど)を決定した後
  • 実際のツール実行の直前(ツールはまだ動いていない)

matcher の対象

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

  • 組み込みツール: Bash / Edit / Write / Read / Glob / Grep / Agent / WebFetch / WebSearch / AskUserQuestion / ExitPlanMode など
  • MCP ツール: mcp__<server>__<tool> 形式(例: mcp__github__create_issue

入力スキーマ

stdin から以下の JSON が渡されます。

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

制御出力(2通り)

方式A: 終了コード 2 で stderr にメッセージ

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
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: 許可して即実行
  • deny: ブロック(bypassPermissions モードや --dangerously-skip-permissions でも効く)
  • ask: ユーザーに確認
  • defer: 外部 UI に判断を委ねる(v2.1.89 以降、SDK 利用時向け)

代表ユースケース

  1. 破壊的 Bash コマンドのブロックrm -rfgit push --forceDROP TABLE など
  2. 機密ファイルの保護.envcredentials.json*.pem の編集・読み取り防止
  3. ツール入力の書き換えupdatedInput で命令の一部を自動修正
  4. MCP 書き込みの検証mcp__.*__write.* にマッチさせて書き込み系 MCP ツールだけを検証

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

~/.claude/settings.json:

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

.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 は禁止されています。--dry-run で確認してください。" >&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

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

PreToolUse hook は任意の許可モード判定より前に発火します。つまり、ユーザーが bypassPermissions モードや --dangerously-skip-permissions を使っても、hook が deny を返せば必ずブロックされます。これによりユーザーがモード変更で回避できないポリシーを適用できます。

ただし逆は真ではなく、hook で allow を返しても設定の deny ルールはバイパスできません。hook は制限を厳しくはできても、緩和はできない設計です。

PostToolUse|実行後の品質ゲート

PostToolUse はツール呼び出しが成功した直後に発火します。ファイル編集後に Prettier / ESLint / 型チェックを自動実行するなど、品質ゲートの中核を担うイベントです。

発火タイミングと入力

  • ツール呼び出しが成功したとき(失敗時は PostToolUseFailure が発火)
  • stdin には tool_input に加えて tool_response が含まれる
{
  "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 }
}

制御出力とその限界

⚠️ 重要な制約: PostToolUse はツールがすでに実行された後に発火するため、アクション自体は取り消せません。できるのは「結果を Claude にフィードバックし、次のアクションに活かす」ことだけです。

  • トップレベル decision: "block" + reason を返すと Claude にフィードバックが戻る
  • Claude はそれを読んで自主的に修正を試みる

代表ユースケース

  1. TypeScript 型チェック*.ts / *.tsx 編集後に tsc --noEmit を実行し、エラーがあれば Claude に戻す
  2. ESLint 自動修正eslint --fix を走らせ、残ったエラーを Claude にフィードバック
  3. Prettier 自動整形 — 編集ファイルを即フォーマット
  4. テスト自動実行 — 小規模プロジェクトならファイル編集後に pytest / vitest を走らせる
  5. 変更差分のログ化 — 監査用に git diff を記録
  6. コミット後の差分確認git commit 後に git log -1 を Claude に見せる

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

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

.claude/hooks/tsc-check.sh:

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

運用のコツ:

  • matcherif でファイル種別を必ず絞る(全ツールで型チェックを走らせると毎回2〜3秒以上かかる)
  • 実行時間の目安は 1 hook あたり 500ms 以下。大規模プロジェクトでは差分ファイルだけ検査する工夫が必要
  • 重い処理は timeout を明示し、失敗時の挙動(open / closed)を決めておく

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

Stop イベントは Claude が応答を終えたタイミングで発火します。タスク未完了なら Claude を継続させる、作業ログを最終記録する、といった「最後の仕上げ」に使います。

発火タイミングの注意

  • Claude が応答を終了したときに毎回発火する(タスク完了時だけではない)
  • ユーザーが割り込みで停止させた場合は発火しない

制御出力

  • 終了コード2 または JSON で decision: "block" を返すと、Claude は停止せず作業を継続する
  • stderr の内容が「継続すべき理由」として Claude に渡る

⚠️ 最重要の注意点: 無限ループ防止

Stop hook で何も考えずに block を返すと、以下の無限ループに陥ります。

Claude 応答終了 → Stop hook 発火 → block → Claude 継続
→ また応答終了 → Stop hook 発火 → block → …(延々)

これを防ぐため、Stop hook では 必ず stop_hook_active フィールドをチェックしてください。

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

# ❗ 無限ループ対策: 2回目以降は素直に停止を許可
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0
fi

# ここに本来のロジック(例: テスト未通過なら止めない)
cd "$CLAUDE_PROJECT_DIR"
if ! npm test --silent > /dev/null 2>&1; then
  echo "テストが通るまで継続してください" >&2
  exit 2
fi
exit 0

stop_hook_active は「この Stop hook によって既に一度 Claude が継続させられた」ことを示すフラグです。2回目以降は exit 0 で素直に停止させるのが鉄則です。

代表ユースケース

  1. タスク完了ゲート — テスト未通過なら Claude を止めない
  2. 作業ログ最終記録 — セッションの最終サマリーを外部 DB に保存
  3. 未コミット変更の警告git status で変更残りを検知してフィードバック

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

SubagentStop は サブエージェント(Task / Agent ツールで起動したエージェント)が終了したときに発火します。Stop とは別のイベントで、親セッションではなくサブエージェントのライフサイクルに紐付きます。

matcher

エージェントタイプ名でマッチします。

  • 組み込み: Bash / Explore / Plan / general-purpose など
  • カスタムエージェント: .claude/agents/ 配下で定義した独自エージェント名

制御出力

  • Stop と同様、exit 2 または decision: "block" でサブエージェントを継続させられる
  • stop_hook_active 対策も同じく必要

代表ユースケース

  1. サブエージェント成果物の検証Explore エージェントが調査結果を出した後、JSON スキーマに合致するか検証
  2. リソースクリーンアップ — エージェント用の一時ワークスペースを削除
  3. 完了ログ — サブエージェントの実行時間・トークン使用量を記録
  4. カスタムエージェントの QAreview-agent 完了後に生成物の必須項目チェック

設定例: Explore エージェントの結果を検証

{
  "hooks": {
    "SubagentStop": [
      {
        "matcher": "Explore",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/validate-explore-output.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

関連記事: Claude Code サブエージェント 使い方ガイド / Claude Code Agent Teams 使い方

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

主要4イベント以外で、実務で頻繁に使われるイベントを4つ紹介します。

UserPromptSubmit|ユーザー入力直後

ユーザーがプロンプトを送信した瞬間に発火します。Claude が処理を始める前にコンテキストを注入したり、入力内容を検証できます。

代表用途:

  • 現在時刻・git status・ブランチ名を毎ターン自動注入
  • 機密情報を含むプロンプトの自動ブロック
  • sessionTitle でセッションの自動命名
#!/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変更: " + $status)
  }
}'

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

matcher で細かく発火条件を絞れます。

  • startup: 新規セッション
  • resume: --resume / --continue / /resume
  • clear: /clear
  • compact: コンテキスト圧縮後

代表用途: プロジェクトコンテキストの自動注入、CLAUDE_ENV_FILE 経由の環境変数永続化、direnv 連携、圧縮後のルール再注入。

PostToolUseFailure|ツール失敗時

ツール実行が失敗したときに発火し、erroris_interrupt を受け取ります。失敗ログの収集、代替アプローチの提案を additionalContext で注入、外部アラート連携などに使います。

SessionEnd|セッション終了時

終了のきっかけ(clear / resume / logout / prompt_input_exit など)を matcher で判定できます。一時ファイル削除、セッションコスト記録、監査ログへのアーカイブに使います。制御出力は持たずクリーンアップ専用です。

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

ここからは、実際のプロジェクトでそのまま組み込める設定例を 10 個紹介します。

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

すでに PreToolUse 節で掲載済み。最初に入れるべき hook No.1

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

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

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

PostToolUse 節で掲載済み。Edit|Write + if*.ts / *.tsx に限定する。

4. ESLint 自動修正

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

5. Prettier 自動整形

#!/bin/bash
FILE=$(cat | jq -r '.tool_input.file_path')
npx prettier --write "$FILE" 2>/dev/null
exit 0

6. 現在時刻・git status の自動注入(UserPromptSubmit)

前節で掲載済み。プロンプトに「今どこにいるか」の文脈が毎回入るため、Claude の出力精度が体感で改善する。

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

#!/bin/bash
INPUT=$(cat)
[ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ] && exit 0
echo "[$(date +%F_%T)] session_id=$(echo "$INPUT" | jq -r '.session_id')" \
  >> "$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"
if [ -n "$(git status --porcelain)" ]; then
  echo "未コミット変更があります。コミットするか stash してください。" >&2
  exit 2
fi
exit 0

9. npm install 系コマンドの事前警告(PreToolUse)

依存関係を勝手に追加されないよう、npm install / pnpm add 系に対して ask 判定を返す。

10. サブエージェント完了時のリソース解放(SubagentStop)

#!/bin/bash
INPUT=$(cat)
AGENT_NAME=$(echo "$INPUT" | jq -r '.agent_name // empty')
rm -rf "$CLAUDE_PROJECT_DIR/.claude/tmp/${AGENT_NAME}_"*
exit 0

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

Hooks 導入で頻出する失敗パターンをまとめます。DEV.to の yurukusa 氏記事の整理がわかりやすいので参考にしています。

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

症状: セキュリティゲートを書いたのに破壊的コマンドが実行される。警告だけ出て止まらない。

原因: exit 1 は非ブロッキング扱い。アクションは続行される。

回避策: ポリシー強制目的では必ず exit 2/hooks コマンドで設定済み hook の挙動を確認する。

2. JSON 内で $HOME が展開されないまま放置

症状: hook が呼ばれているはずなのに何も動かない(サイレント失敗)。

原因: settings.json 内の $HOME~ は展開されないケースがある。相対パスも NG。

回避策: "$CLAUDE_PROJECT_DIR" 環境変数を使う、または絶対パスを記述する。/hooks で実パスを検証する。

3. jq 依存を前提にしてしまう

症状: 開発機では動くが CI や他チームメンバーの環境でサイレント失敗する。

原因: jq 未インストール環境での挙動を決めていない。

回避策: スクリプト冒頭で command -v jq をチェックし、未インストール時の動作(安全側は closed = 全ブロック、利便性優先は open = 通過)を明示する。

4. 実行時間が長すぎてフロー全体を遅くする

症状: 50 ファイル編集で 2.5 分以上のオーバーヘッドが発生する。

原因: PostToolUse で全ファイルを対象にした重い型チェックを無条件実行。

回避策:

  • 1 hook あたり 500ms 以下を目安にする
  • matcherif で実行条件を狭める(ファイル種別・引数パターンで絞る)
  • 重い処理は差分ファイルだけ検査する
  • 必要なら timeout を明示

5. コンテキスト消費の監視不足

症状: 45 分作業したら Claude の応答品質が劇的に低下した。

原因: コンテキストウィンドウが 95% に達していることに気付いていない。

回避策: PostToolUse でツール呼び出し数をカウントし、閾値超過時に警告を注入する hook を仕込む。/compact を自動で促す運用にする。

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

チームや組織に Claude Code を展開する場合、ユーザー任せの hook 設定では統制が効きません。管理者ポリシーを活用します。

管理者向けの3つの制御

オプション

動作

allowManagedHooksOnly: true

ユーザー/プロジェクト/プラグインの hooks をすべてブロックし、管理者設定だけを有効化

enabledPlugins

特定プラグイン内の hook を強制有効化(allowManagedHooksOnly の例外として)

disableAllHooks: true

階層を尊重しつつ全 hooks を無効化(緊急停止)

HTTP hook の環境変数補間

外部監査サーバーに全ツール呼び出しを POST する場合、HTTP hook のヘッダー値で $VAR / ${VAR} 形式の補間ができますが、allowedEnvVars に列挙した変数のみ解決され、それ以外は空文字に置換されます。認証トークンの誤流出を防ぐ仕組みです。

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

Hooks が制限を緩和することはできない

重要な原則: Hooks は制限を厳しくはできるが緩和はできない設計です。permissionDecision: "allow" を返しても、設定ファイルの deny ルールはバイパスできません。これにより「管理者が決めた禁止事項をユーザーが hook で解除する」攻撃経路が塞がれています。

関連記事: Claude Code セキュリティ 安全な使い方 / AIコーディング セキュリティ リスク

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

いきなり全イベントを設定しようとすると破綻します。以下の順番で段階的に導入するのが安全です。

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

  • PreToolUse で rm -rf / git push --force / .env 編集をブロック
  • exit 2 の使い方に慣れる
  • /hooks コマンドで設定反映を確認

Step 2: コンテキスト強化(1週間以内)

  • UserPromptSubmit で git status・ブランチ名・現在時刻を自動注入
  • SessionStart でプロジェクトの README や最近の issue を注入

Step 3: 品質ゲート(2週間以内)

  • PostToolUse で型チェック・Lint・Prettier を自動実行
  • matcherif で対象を絞り、500ms 以下を維持

Step 4: タスク完了制御(必要に応じて)

  • Stop で未コミット・テスト未通過を検知
  • stop_hook_active 対策を必ず入れる

Step 5: チーム展開(組織レベル)

  • .claude/settings.json をコミットしてチーム共通の hook を配布
  • 管理者ポリシーで allowManagedHooksOnly を検討

Claude Code Hooks が向いている人・向いていない人

こんな人におすすめ

  • LLM 任せの自動化に不安がある開発者 — 決定論的なガードレールで事故を事前に防ぎたい
  • チームで Claude Code を展開する技術リーダー — メンバー間で一貫した品質・セキュリティ基準を適用したい
  • 既存の Lint / 型チェック / テストを AI 開発フローに組み込みたい人 — PostToolUse でそのまま連携できる
  • 監査ログ要件がある企業 — 全ツール呼び出しを外部サーバーに記録したい
  • OSS / セキュリティ重視プロジェクトのメンテナ.env 誤操作や force push を物理的にブロックしたい

向いていない・慎重になるべきケース

  • Claude Code 初日・初導入の段階 — まず基本機能を使いこなしてから hook を追加するのが効率的
  • 小さな個人スクリプト用途 — オーバーヘッドの割にメリットが薄い
  • シェルスクリプトに慣れていない人 — JSON 操作や jq 依存の扱いにつまずきやすい
  • 確実性が絶対的に必要な場面で agent ハンドラーを使いたい場合 — 公式が実験的と明記しているため、本番運用前に挙動検証を徹底する

よくある質問(FAQ)

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

A. Hooks 機能自体は Claude Code に標準同梱で追加料金はかかりません。ただし type: "prompt" / type: "agent" ハンドラーは内部で LLM を呼び出すため、API トークン消費が発生します。type: "command" / type: "http" ならトークン消費はありません。

Q2. 設定を変更したら再起動が必要ですか?

A. セッション中に設定ファイルを編集すると、ファイルウォッチャーが通常は自動で変更を取り込みます。反映されない場合はセッション再起動で強制リロードしてください。

Q3. どのスコープに設定を置くのがベストですか?

A. 目的によります。チームで共有したい最低限のガードレール(rm -rf 防御など)は .claude/settings.json にコミット、個人の実験は .claude/settings.local.json、全プロジェクト共通の個人設定は ~/.claude/settings.json が基本パターンです。

Q4. PermissionRequest hook で自動承認したいのですが、-p 非インタラクティブモードで動きません。

A. 公式仕様として、PermissionRequest hooks は -p 非インタラクティブモードでは発火しません。自動化したい場合は PreToolUse hook で permissionDecision: "allow" を返してください。

Q5. 複数の PreToolUse hook が updatedInput を返したらどうなりますか?

A. 最後に完了したものが勝ちます。並列実行されるため順序は非決定的です。複数 hook で入力を書き換えるのは避け、1 つの hook に統合するのが安全です。

Q6. Stop hook で無限ループになりました。どう直せばいいですか?

A. スクリプトの冒頭で stop_hook_active フラグをチェックし、true なら即 exit 0 で停止を許可してください。本記事の Stop 節にサンプルコードがあります。

Q7. Hook から / コマンドやツールをトリガーできますか?

A. できません。Hook が Claude に影響を与えられるのは stdout / stderr / 終了コード(HTTP hook はレスポンスボディ)だけです。別のツールを呼びたい場合は additionalContext でその旨を Claude に伝え、Claude 自身にツールを呼ばせる設計にします。

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

A. 書けます。matcher に mcp__<server>__<tool> 形式の正規表現を指定してください。例えば書き込み系 MCP ツールだけを絞るなら mcp__.*__write.* のように書けます。

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

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

Claude Code Hooks は、Claude Code に決定論的なガードレールを敷くための仕組みです。LLM が正しく判断してくれることを祈るのではなく、「この操作は絶対に実行されない」「この場合は必ず型チェックが走る」を設定ファイルで保証できます。

この記事の要点を改めて整理します。

  • 主要 4 イベント(PreToolUse / PostToolUse / Stop / SubagentStop)を理解すれば 8 割の用途はカバーできる
  • ポリシー強制目的では必ず exit 2exit 1 では止まらない)
  • Stop / SubagentStop では stop_hook_active を必ずチェック(無限ループの原因No.1)
  • matcher でツール名、if でツール引数を絞り、1 hook 500ms 以下を目安に
  • チーム展開時は .claude/settings.json コミット+管理者ポリシーで統制

まずは「rm -rf ブロック」と「UserPromptSubmit で git status 注入」の 2 つから始めて、徐々に品質ゲートとログ記録を追加していく段階的導入がおすすめです。

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

公式リソース

この記事の著者

AI革命

AI革命

編集部

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

AI活用ならAI革命にお任せ。サービスを見てみる
AI Revolution Growth Arrow

AIでビジネスを革新しませんか?

あなたのビジネスにAIがどのような価値をもたらすかをご提案いたします。