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つあります。
- 安全性: 破壊的コマンド(
rm -rf、git push --force、本番 DB への書き込みなど)を LLM の判断に委ねず、設定ファイル側で確実にブロックしたい - 品質: ファイル編集後に Prettier・ESLint・型チェック・テストを必ず走らせたい
- 運用: セッション開始時にプロジェクトコンテキストを自動注入したり、作業ログを自動記録したい
これらを Claude Code の settings.json に記述するだけで、セッション再起動や独自ツール不要で即運用できます。
できることの代表例
カテゴリ | 代表ユースケース |
|---|---|
事前ブロック |
|
自動品質担保 | 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 を一覧できる(読み取り専用ブラウザ)
設定ファイルの配置場所とスコープ
Hooks は複数のスコープで多層的に設定できます。優先順位は厳密に決まっており、管理者ポリシー > プロジェクト > ユーザー > プラグイン/スキルの順で評価され、上位設定は下位設定を上書き・制限できます。
配置場所 | スコープ | Git 共有 | 用途 |
|---|---|---|---|
管理ポリシー設定 | 組織全体 | 管理者配布 | エンタープライズ統制 |
| 単一プロジェクト | 可 | チーム共通のガードレール |
| 単一プロジェクト | 不可(gitignore) | 個人の実験的 hook |
| ユーザー全体 | 不可 | 個人の全プロジェクト共通 hook |
プラグイン | プラグイン有効時 | プラグインにバンドル | 再利用可能な 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)"
}
]
}
]
}
}各階層の役割
- イベント名(
PreToolUseなど): ライフサイクル上のどのタイミングで発火するか - matcher: どのツール・条件にマッチしたときに実行するか(次項で詳述)
- hooks: 実際に実行するハンドラー(
command/http/prompt/agentの4タイプ)
matcher 構文の評価ルール
matcher は文字列の中身によって評価方式が変わります。
パターン | 評価方式 | 例 |
|---|---|---|
| 全マッチ(毎回発火) | すべてのツール |
英数字・ | 完全一致 or パイプ区切り |
|
それ以外の文字を含む | JavaScript 正規表現として評価 |
|
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 利用 | コスト | 備考 |
|---|---|---|---|---|
| シェルコマンド実行(最も基本) | なし | なし | stdin で JSON 受信、stdout/stderr/終了コードで応答 |
| 外部 URL へ POST | なし | 通信コストのみ | 監査サーバー連携。 |
| 単一ターン LLM 判定 | あり | API トークン消費 | デフォルト Haiku で |
| サブエージェントによるファイル調査を伴う判定 | あり | API トークン消費大 | デフォルト 60 秒・最大 50 ターン |
最初は command から始めるのが安全です。 prompt / agent は LLM 呼び出しコストが発生し、かつレスポンスに揺らぎが出るため、確実性が求められるガードレールには向きません。agent は公式が実験的と明記しているため、本番運用前に挙動を十分確認してください。
Exit Code と制御出力|最頻出ミス領域
Hook の挙動を決める最重要ポイントが終了コード(Exit Code)です。ここを誤解していると「設定した気になっていたのに何も守られていなかった」状態が起きます。
Exit Code の動作
Exit Code | 動作 |
|---|---|
| 成功。stdout が JSON なら制御出力として解釈。 |
| 非ブロッキングエラー。stderr はトランスクリプトに表示されるのみで、アクションは続行される |
| ブロッキングエラー。アクションがブロックされ、stderr が Claude へフィードバックとして渡される |
その他 | 非ブロッキング。 |
⚠️ 最大の落とし穴: 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 利用時向け)
代表ユースケース
- 破壊的 Bash コマンドのブロック —
rm -rf、git push --force、DROP TABLEなど - 機密ファイルの保護 —
.env、credentials.json、*.pemの編集・読み取り防止 - ツール入力の書き換え —
updatedInputで命令の一部を自動修正 - 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 はそれを読んで自主的に修正を試みる
代表ユースケース
- TypeScript 型チェック —
*.ts/*.tsx編集後にtsc --noEmitを実行し、エラーがあれば Claude に戻す - ESLint 自動修正 —
eslint --fixを走らせ、残ったエラーを Claude にフィードバック - Prettier 自動整形 — 編集ファイルを即フォーマット
- テスト自動実行 — 小規模プロジェクトならファイル編集後に pytest / vitest を走らせる
- 変更差分のログ化 — 監査用に
git diffを記録 - コミット後の差分確認 —
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運用のコツ:
matcherとifでファイル種別を必ず絞る(全ツールで型チェックを走らせると毎回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 0stop_hook_active は「この Stop hook によって既に一度 Claude が継続させられた」ことを示すフラグです。2回目以降は exit 0 で素直に停止させるのが鉄則です。
代表ユースケース
- タスク完了ゲート — テスト未通過なら Claude を止めない
- 作業ログ最終記録 — セッションの最終サマリーを外部 DB に保存
- 未コミット変更の警告 —
git statusで変更残りを検知してフィードバック
SubagentStop|サブエージェント終了時のフック
SubagentStop は サブエージェント(Task / Agent ツールで起動したエージェント)が終了したときに発火します。Stop とは別のイベントで、親セッションではなくサブエージェントのライフサイクルに紐付きます。
matcher
エージェントタイプ名でマッチします。
- 組み込み:
Bash/Explore/Plan/general-purposeなど - カスタムエージェント:
.claude/agents/配下で定義した独自エージェント名
制御出力
- Stop と同様、
exit 2またはdecision: "block"でサブエージェントを継続させられる stop_hook_active対策も同じく必要
代表ユースケース
- サブエージェント成果物の検証 —
Exploreエージェントが調査結果を出した後、JSON スキーマに合致するか検証 - リソースクリーンアップ — エージェント用の一時ワークスペースを削除
- 完了ログ — サブエージェントの実行時間・トークン使用量を記録
- カスタムエージェントの QA —
review-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//resumeclear:/clear後compact: コンテキスト圧縮後
代表用途: プロジェクトコンテキストの自動注入、CLAUDE_ENV_FILE 経由の環境変数永続化、direnv 連携、圧縮後のルール再注入。
PostToolUseFailure|ツール失敗時
ツール実行が失敗したときに発火し、error と is_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 05. Prettier 自動整形
#!/bin/bash
FILE=$(cat | jq -r '.tool_input.file_path')
npx prettier --write "$FILE" 2>/dev/null
exit 06. 現在時刻・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 08. 未コミット変更の警告(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 09. 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 以下を目安にする
matcherとifで実行条件を狭める(ファイル種別・引数パターンで絞る)- 重い処理は差分ファイルだけ検査する
- 必要なら
timeoutを明示
5. コンテキスト消費の監視不足
症状: 45 分作業したら Claude の応答品質が劇的に低下した。
原因: コンテキストウィンドウが 95% に達していることに気付いていない。
回避策: PostToolUse でツール呼び出し数をカウントし、閾値超過時に警告を注入する hook を仕込む。/compact を自動で促す運用にする。
企業導入時のセキュリティ統制
チームや組織に Claude Code を展開する場合、ユーザー任せの hook 設定では統制が効きません。管理者ポリシーを活用します。
管理者向けの3つの制御
オプション | 動作 |
|---|---|
| ユーザー/プロジェクト/プラグインの hooks をすべてブロックし、管理者設定だけを有効化 |
| 特定プラグイン内の hook を強制有効化( |
| 階層を尊重しつつ全 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 で解除する」攻撃経路が塞がれています。
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 を自動実行
matcherとifで対象を絞り、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 2(exit 1では止まらない) - Stop / SubagentStop では
stop_hook_activeを必ずチェック(無限ループの原因No.1) matcherでツール名、ifでツール引数を絞り、1 hook 500ms 以下を目安に- チーム展開時は
.claude/settings.jsonコミット+管理者ポリシーで統制
まずは「rm -rf ブロック」と「UserPromptSubmit で git status 注入」の 2 つから始めて、徐々に品質ゲートとログ記録を追加していく段階的導入がおすすめです。
合わせて読みたい関連記事
- Claude Code とは|料金・使い方・特徴を徹底解説
- Claude Code 使い方 完全ガイド
- Claude Code サブエージェント 使い方ガイド
- Claude Code スラッシュコマンド 作り方
- Claude Code Skills 活用ガイド
- Claude Code MCP連携 ガイド
- Claude Code セキュリティ 安全な使い方
- CLAUDE.md 書き方 ベストプラクティス
公式リソース
この記事の著者

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

CLAUDE.md 書き方ベストプラクティス|設計パターン・300行ルール完全ガイド
2026/04/22

Claude Code Routines 使い方|スケジュール・API・GitHub Event 自動化完全ガイド
2026/04/22

LLM-jp-4とは|国立情報学研究所・約12兆トークン学習の国産LLMを徹底解説
2026/04/21

Google TurboQuantとは?KVキャッシュを最大6倍圧縮する新技術をICLR 2026採択論文から徹底解説
2026/04/21

美容・ヘアサロンのAI活用事例2026|肌診断AI・AIスタイリスト・ECILAまで完全ガイド
2026/04/21

繊維・アパレル業のAI活用事例2026|トレンド予測・AI試着・在庫最適化まで徹底解説
2026/04/21

