Hooksを活用してClaude Codeの処理制御に正解を出してみる

はじめに:Hooks の重要性
「開発チームに Claude Code を配布したが、AIが勝手に .env を読み取らないか心配だ」
「コーディング規約を CLAUDE.md に書いても、AIが守ったり守らなかったりで品質がブレる」
「監査要件として、AIが行った操作のログを残したい」
情シス部長、DX推進リード、開発チームリーダーがいま向き合っているのは、こうした「AIに任せたあとのリスク」をどう抑えるかという問いです。生成AIによる開発支援が広がる一方、「AIの動作のうち、どこをどうやって決定論的に制御するか」が新たな課題として浮上しています。
この課題に対する有力な選択肢のひとつが Claude Code Hooks です。Hooks は「LLMへのお願い」ではなく、設定ファイルに定義したシェルコマンドやスクリプトが、Claude Code の特定のタイミングで必ず実行される仕組みです。プロンプトでは「やってください」としか言えなかったところを、Hooks なら「コード側で実行前後の処理を強制する」ことができます。
本記事では、Anthropic 公式ドキュメント(Claude Code Hooks Reference)を基に、情シス・開発リーダー視点で導入を検討したい実用レシピを、コピペ可能な settings.json とスクリプト付きで解説します。
この記事を読むことで得られること:
- Hooks の全体像(4つの実行レイヤー × 主要イベント)とイベント選定の判断軸
- 「破壊的コマンド遮断」「秘密情報保護」「品質ゲート自動化」「監査ログ取得」の即戦力レシピ
- 組織統制機能
allowManagedHooksOnlyによる Hooks ポリシーの一元管理 - 導入時に陥りやすい落とし穴(exit code、レイテンシ、JSON破損)の回避方法
基礎知識:Claude Code Hooks とは何か
Hooks が解決する「プロンプトの限界」
CLAUDE.md やシステムプロンプトに「危険なコマンドは実行しないこと」と書いても、それはAIへの「お願い」にすぎません。プロンプトインジェクションや解釈の揺らぎで、ルールが破られる可能性は常に残ります。
Hooks は、この課題に対して、設計レベルで補強できます。
| アプローチ | 仕組み | 確実性 |
|---|---|---|
| プロンプト指示(CLAUDE.md 等) | LLMにテキストで指示 | 確率的(破られる可能性あり) |
| Claude Code Hooks | イベント発火時にスクリプトを実行 | 決定論的(必ず実行される) |
💡 ポイント: Hooks は AI の判断を介さず、設定ファイルに書いたとおりに必ず動きます。これが、プロンプトでは得られない「決定論的な制御」の正体です。
ライフサイクルと4つの実行レイヤー
Hooks を「どこで何をするか」設計するには、まず Claude Code の1セッションがどう進むかを把握しておく必要があります。セッションは以下のような流れで進行し、各タイミングで Hooks が発火します。

セッションが始まると SessionStart が一度だけ発火し、その後ユーザーが入力を送るたびに「ターンループ」が回ります。1ターンは UserPromptSubmit(プロンプト受付)で始まり、AIがツールを呼ぶたびに PreToolUse(実行前)→ PermissionRequest(権限確認)→ PostToolUse(実行後)の3点セットが回り、ターンの最後に Stop が発火します。セッションを閉じると SessionEnd が一度だけ走ります。Notification や FileChanged はこの流れとは独立に、必要なときだけ発火する非同期イベントです。
「いつ実行されるか」で4つのレイヤーに分けて捉えると、自分が作りたい仕組みに必要なイベントが選びやすくなります。
| レイヤー | 代表イベント | 主な用途 | 発火頻度 |
|---|---|---|---|
| セッション層 | SessionStart / SessionEnd | コンテキスト動的注入、最終処理、監査ログ | セッションごとに1回 |
| ターン層 | UserPromptSubmit / Stop | ルール注入、完了通知、強制テスト実行 | 1ターンに1回 |
| ツール層 | PreToolUse / PostToolUse | 品質ゲート、危険コマンド遮断 | ツール呼び出しごと(最多) |
| 非同期層 | Notification / FileChanged | 通知連携、設定ホットリロード | イベント発生時のみ |
設計の判断軸はシンプルです。「セッション1回分やれば足りる処理」はセッション層、「ターンごとに必要な処理」はターン層、「個別のツール呼び出しを止めたい・チェックしたい処理」はツール層、「Claude Code 本体の進行とは別の通知や設定追従」は非同期層に置きます。
特に注意したいのがツール層の発火頻度です。AIが1ターンに10回ツールを呼び出せば、ツール層の Hooks も10回実行されます。ここに重い処理を書きますと体感速度が一気に落ちますので、ツール層には軽量チェックのみを置き、重い処理は1ターン1回のターン層やセッション層に配置するという棲み分けが運用上の重要なポイントとなります(具体的な時間基準は §3.2 で扱います)。
設定ファイルの階層
Hooks の設定は1つのファイルに集約するのではなく、置き場所のスコープが違う4つのファイルに分けて記述できます。これら4つは「上書き」ではなく「重ね合わせ」で動作します。つまり、複数の階層で同じイベント用の Hooks を定義していれば、そのすべてが順に実行されます。
| ファイル | スコープ | リポジトリ共有 | 用途 |
|---|---|---|---|
~/.claude/settings.json | 全プロジェクト | 不可 | 個人共通の Hooks |
.claude/settings.json | 単一プロジェクト | 可(コミット推奨) | チーム共有の Hooks |
.claude/settings.local.json | 単一プロジェクト | 不可(gitignore) | 個人の上書き |
| Managed Policy(組織配布) | 全配布対象 | 管理者配布 | 組織統制の Hooks |
それぞれの使い分けは次のように整理できます。
~/.claude/settings.json(個人ホーム) は、すべてのプロジェクトで自分が使いたい Hooks(例:作業ログを残す、待ち時間を通知する)を置く場所です。他のメンバーには共有されません。.claude/settings.json(プロジェクト直下) は、リポジトリにコミットしてチーム全員に同じ Hooks を配布するための場所です。本記事の各レシピは原則ここに置きます。.claude/settings.local.json(gitignore 対象) は、特定の開発者が自分の環境だけで一時的に Hooks を差し替えたいときに使います。(例:CIで動かない Hooks をローカルで無効化する)- Managed Policy(組織配布) は、企業がOS/MDM経由でClaude Codeに配布するポリシーで、上記3つに優先して効きます。詳細は §3.1 で扱います。
💡 開発チームリーダー向けポイント:
.claude/settings.jsonをリポジトリにコミットすれば、リポジトリを clone(GitHub 等から自身のPCに取得する操作)した開発者の Claude Code に同じ Hooks が既定で適用されます。これにより「規約を読んでもらう」段階から「規約のチェックを Claude Code が自動で行う」段階へ前進できます。ただし、ユーザーが
.claude/settings.local.jsonで個別に上書きしたり、フックスクリプト自体を削除することは技術的には可能です。組織レベルで上書きを禁じたい場合は、後述の Managed Policy(§3.1)と組み合わせる必要があります。
終了コードによるブロック制御
フックスクリプトと Claude Code は、終了コード(exit code)と標準エラー出力(stderr)の組み合わせで意思疎通します。スクリプト側がどんな終了コードで終わるかで、Claude Code の次の挙動が決まります。
なお「終了コード」とは、UNIX 系 OS で古くから使われている標準的な仕組みで、プログラムが終了する際に親プロセスへ返す整数値のことです。慣習として 0 が成功、それ以外がエラーを示しますが、具体的な数値の意味付けは各プログラムに委ねられています。Claude Code はこの値を読み取り、独自のルールで次の動作を決めるという形で、UNIX の標準機能の上に Hooks の制御フローを構築しています。
| 終了コード | 意味 | Claude Code の挙動 |
|---|---|---|
0 | 成功 | JSON出力をパース処理、何もなければそのまま継続 |
2 | ブロッキングエラー | ツール実行を阻止、stderr をエラーとして Claude に伝達 |
その他(1 等) | 非ブロッキングエラー | 実行継続、stderr を1行表示するのみ |
ポイントは、exit 2 のときだけ「実行をブロックし、その理由を Claude に伝える」というモードに入る点です。exit 1 や他のコードはエラー表示こそされますが、後続の処理は止まりません。ブロックを意図して書いた Hooks が exit 1 で終わっていますと、見た目はエラーが出ているにもかかわらずツールはそのまま実行されてしまうという、最も気付きにくい誤動作を招きます。
また exit 2 のときに stderr へ書き出した文字列は、単にエラーログとして消費されるのではなく、Claude 本体に「なぜブロックされたか」というメッセージとして渡されます。これにより AI は別の手段を試したり、ユーザーへ状況を説明したりできるようになります。Hooks を設計する際は「止める/止めない」だけでなく、「Claude に何を伝えるか」までを併せて設計することが重要です。
⚠️ 最大の落とし穴: よく
exit 1でブロックさせようとして失敗します。ブロックにはexit 2を使う必要があります。
実装のステップ:情シス・開発リーダーが今すぐ仕込む5つのレシピ
優先度の高い5つのレシピを、設定ファイルとシェルスクリプトをそのまま貼り付ければ動作する形で紹介します。それぞれのレシピの概要としては、レシピ①と②が「事故を防ぐ仕組み」、③が「品質を底上げする仕組み」、④が「規約をAIに伝える運用上の仕組み」、⑤が「監査・コンプライアンス対応のための仕組み」という構成です。優先順位を付ける場合は、まずレシピ①②から導入するのが効果と工数のバランスとして最適です。
前提条件チェックリスト(コードを動かす前に)
以降のレシピはすべて、POSIX シェル(bash) と jq(JSON処理ツール) を前提としています。コピー前に下記の環境条件を満たす必要があります。
| 項目 | 対応方法 |
|---|---|
jq のインストール | macOS: brew install jq / Linux: apt install jq 等 / Windows: winget install jqlang.jq |
| bash 実行環境の準備(Windowsの場合) | Claude Code on Windows でシェルスクリプトを動かすには WSL2(Ubuntu等) または Git Bash が必要。PowerShell ネイティブでは .sh は動作しません |
| スクリプトの実行権限の付与(macOS / Linux / WSL) | chmod +x .claude/hooks/*.sh を1回実行 |
| 配置ディレクトリ | プロジェクト直下に .claude/hooks/ ディレクトリを作成し、設定したい Hooks の .sh ファイルを保存 |
| 改行コードの変更(Windowsで作成する場合) | LF(Unix形式)で保存。Windows標準の CRLF のままだと、bash が行末の \r を制御文字として誤認してエラーとなる |
| コード品質ツール(任意) | eslint(lint チェック)、prettier(コード整形)、typescript(型チェック)を Node.js プロジェクトにインストール。(AI が書いた JavaScript / TypeScript コードを編集直後に自動で検査・整形するために使用) |
| Slack通知ツール(任意) | 環境変数 $SLACK_WEBHOOK_URL を ~/.bashrc 等に設定 |
補足: コード品質ツールは、本記事ではレシピ③(編集後の自動品質ゲート)で利用しています。
⚠️ Windows ユーザーへの重要な注意: Claude Code 自体は Windows でネイティブ動作しますが、本記事のフックスクリプトは bash 前提です。Windows 環境で運用する場合、Claude Code を WSL2 内で起動するか、Git Bash を
shellフィールドで明示指定する必要があります。組織配布する場合は、この前提を運用標準に含める必要があります。
レシピ①:破壊的コマンドの強制遮断(PreToolUse)
AI に Bash 操作を任せる際にもっとも避けたいのが、rm -rf / や DROP TABLE のように一度実行してしまうと取り返しのつかない破壊的コマンドです。プロンプトで「危険なコマンドは実行しないでください」とお願いするだけでは、解釈の揺らぎや誤動作によって実行されてしまうリスクが残ります。コードレビューを通せばよいというものでもなく、リアルタイムに動くエージェントの操作を後追いで止めることはできません。
このレシピでは、PreToolUse Hooks を利用して、Bash ツールが起動する直前にコマンド文字列を検査し、危険なパターンに該当した場合は exit 2 で実行をブロックする構成を取ります。Bash 以外のツールには影響しないため、matcher: "Bash" で対象を絞り込みます。
.claude/settings.json への登録
設定ファイル側では、PreToolUse のうち Bash ツール呼び出し時にスクリプトを起動するよう指定いたします。コマンド文字列の正規表現マッチに重い処理は不要ですので、timeout は10秒で十分です。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-dangerous.sh",
"timeout": 10
}
]
}
]
}
}
.claude/hooks/block-dangerous.sh
スクリプト本体では、危険なコマンドのパターンを正規表現の配列として列挙し、Claude Code から標準入力で渡されるコマンド文字列と1つずつ突き合わせます。マッチしたパターンがあれば、stderr にブロック理由を出力したうえで exit 2 で終了します。
#!/bin/bash
# Claude Code から JSON が標準入力で渡される
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
# 破壊的コマンドのパターン
DANGEROUS_PATTERNS=(
'rm[[:space:]]+-rf?[[:space:]]+(/|~|\*|\$HOME)' # 広域削除
'DROP[[:space:]]+(TABLE|DATABASE|SCHEMA)' # DBスキーマ破壊
'mkfs\.' # ファイルシステム初期化
'dd[[:space:]]+if=.*of=/dev/' # デバイス上書き
':\(\)\{.*\|:&.*\};:' # フォークボム
'curl[[:space:]].*\|[[:space:]]*(bash|sh)' # curl パイプ実行
'terraform[[:space:]]+destroy' # IaC 全削除
)
for pattern in "${DANGEROUS_PATTERNS[@]}"; do
if echo "$COMMAND" | grep -Eq "$pattern"; then
# stderr に出した内容は Claude に「ブロック理由」として伝わる
echo "BLOCKED: 破壊的コマンドのため実行を拒否しました(pattern: $pattern)" >&2
exit 2 # ★ 必ず 2。1 ではブロックされない
fi
done
exit 0
ここで列挙しているパターンは「特に致命的なもの」に意図的に限定しています。rm を含む通常のコマンド(例:rm tmp.txt)まで止めてしまうと業務に支障が出るため、-rf 付きの広域削除や、対象が /、~、*、$HOME といった広範な範囲を指す場合に限って遮断しています。実運用ではチームのリスク許容度に応じて、パターンの追加・削減をご検討ください。
💡 ポイント:
exit 2でstderrに書き出した内容は、Claude に「なぜブロックされたか」というメッセージとして伝わります。これにより AI は別の手段を試したりユーザーに状況を説明したりできるため、単に止めるだけでなく、その後の作業を継続させる設計になっています。
レシピ②:秘密情報ファイルの読み取り遮断(PreToolUse)
.env や credentials.json などの秘密情報ファイルを AI に読み取らせてしまうと、その内容がチャットログ、AI のコンテキスト、さらに LLM プロバイダ側のログまで、複数の場所に残存します。一度でも読み込まれてしまうと、後から「なかったこと」にすることは現実的に困難です。プロンプトで「.env は読まないでください」と書く程度ではどうしても抜けが生じるため、決定論的なブロック機構を入れておくことを推奨します。
このレシピでは、Read / Edit / Write 系のツールが起動する直前に、対象ファイルパスを検査する Hooks を設けます。ファイルパスの文字列マッチで判定するためレイテンシは小さく、timeout: 5 秒で十分対応できます。
.claude/settings.json への追加
matcher には Read / Edit / Write の3つを縦棒区切りで指定し、これらのツール呼び出し時にのみ Hooks が発火するように構成します。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read|Edit|Write",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/protect-secrets.sh",
"timeout": 5
}
]
}
]
}
}
.claude/hooks/protect-secrets.sh
スクリプト側では、秘密情報を含む可能性が高いファイル名のパターンを列挙し、対象ファイルパスがいずれかに該当した時点でブロックします。
#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
# 秘密情報を含む可能性が高いファイルパターン
SECRET_PATTERNS=(
'\.env(\..*)?$' # .env, .env.production など
'credentials\.json$' # 各種サービスの認証情報ファイル
'service-account.*\.json$' # GCP サービスアカウント鍵
'id_rsa$|id_ed25519$' # SSH 秘密鍵のみ($終端で id_rsa.pub などの公開鍵は除外)
'\.pem$|\.p12$|\.pfx$' # 証明書・鍵
'secrets?\.ya?ml$' # secrets.yaml / secret.yml(Kubernetes Secret など)
)
for pattern in "${SECRET_PATTERNS[@]}"; do
if echo "$FILE_PATH" | grep -Eq "$pattern"; then
echo "BLOCKED: 秘密情報ファイルへのアクセスを拒否しました: $FILE_PATH" >&2
exit 2
fi
done
exit 0
列挙しているパターンは、.env 系のドット記法、credentials.json などの明示的な命名、*.pem / *.p12 といった証明書類、secrets.yaml 形式の設定ファイル、SSH 秘密鍵までを含めています。自社の運用で別パターンの秘密情報ファイルが存在する場合は、配列に追加してください。
⚠️ 情シス担当者向けの注意点: この Hooks を
.claude/settings.json(リポジトリ管理)に登録すると、リポジトリを clone(取得)した開発者の環境に既定で適用され、.env等の読み取りをブロックできます。事故を減らす目的では強力な手段になりますが、これだけで秘密情報管理が完結するわけではありません。シークレットマネージャや権限管理など、他の対策との併用を推奨します。
レシピ③:編集後の自動品質ゲート(PostToolUse)
AI にコードを書いてもらった直後、そのコードが本当に動作するかを AI 自身が確認していないケースが少なからずあります。型エラーや lint エラーを含んだまま「完了しました」と報告してくる場面に心当たりがある方も多いのではないでしょうか。これらを人間のレビューで毎回発見するのは非効率ですので、編集の直後に型チェックと lint を自動で走らせ、エラーが見つかった場合は AI に差し戻す仕組みを入れておくことが有効です。
このレシピでは、PostToolUse のうち Edit / Write ツール実行後にスクリプトを起動し、対象ファイルへ ESLint と Prettier を自動適用したうえで、プロジェクト全体の型チェックを実行します。型エラーが検出された場合は、stderr 経由で AI に通知し、再修正を促す構成です。
.claude/settings.json への追加
PostToolUse は重めの検証も許容できる位置にありますが、Edit / Write 以外のツールには不要ですので、matcher で対象を絞り込みます。timeout は120秒に設定し、規模の大きいプロジェクトの型チェックにも余裕を持って対応できるようにしています。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/quality-gate.sh",
"timeout": 120
}
]
}
]
}
}
.claude/hooks/quality-gate.sh
スクリプトはまず対象ファイルが TypeScript / JavaScript 系であるかを確認し、該当しなければ即座に終了します。続いて ESLint の自動修正、Prettier の整形、tsc による型チェックを順に実行し、型エラーが見つかった場合のみ AI に差し戻します。
#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
# TypeScript / JavaScript ファイルのみ対象
if [[ ! "$FILE_PATH" =~ \.(ts|tsx|js|jsx)$ ]]; then
exit 0
fi
# 1. ESLint 自動修正(失敗してもブロックしない)
npx eslint --fix "$FILE_PATH" 2>&1 || true
# 2. Prettier 整形
npx prettier --write "$FILE_PATH" 2>&1 || true
# 3. 型チェック(プロジェクト全体)
if ! TYPECHECK_OUTPUT=$(npx tsc --noEmit 2>&1); then
# 型エラーがあれば Claude に差し戻し(PostToolUse は exit 2 でも実行は取り消せないが、stderr の内容で Claude に再修正を促せる)
echo "型エラーが検出されました。修正してください:" >&2
echo "$TYPECHECK_OUTPUT" | tail -30 >&2
exit 2
fi
exit 0
ESLint と Prettier については、「自動修正で済む指摘は修正し、失敗してもブロックしない」運用にしています。一方、型エラーだけは exit 2 で差し戻す扱いとし、AI に再修正を促します。PostToolUse の段階ではファイル変更そのものは取り消せませんが、stderr の内容が Claude に伝わるため、修正の指示を返す形で品質を担保できます。
💡 PostToolUse の重要な制約: PostToolUse はツール実行後に走るため、ファイル変更そのものは取り消せません。ただし
stderrの内容が Claude に渡るため、AI に「修正してください」と差し戻す形でリカバリーが可能です。
レシピ④:セッション開始時のコンテキスト自動注入(SessionStart)
チームで定めたコーディング規約、現在進行中のスプリント情報、最新のブランチ状態や未コミットの変更状況など、毎セッションの開始時に AI へ伝えたい情報は意外と多くあります。ユーザーが毎回手入力するのは現実的ではなく、CLAUDE.md に書いておくだけでは情報の鮮度が保てません。
SessionStart Hooks を利用しますと、Claude Code がセッションを開始する時点で、外部から取得した最新情報を自動的に AI のコンテキストへ注入できます。本レシピでは、git の状態とチーム規約を組み合わせて注入する構成例を紹介します。
.claude/settings.json への追加
matcher には startup(新規セッション開始時)と resume(再開時)を縦棒区切りで指定します。スクリプト内で git コマンドを実行するため、timeout は10秒を確保しています。
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/inject-context.sh",
"timeout": 10
}
]
}
]
}
}
.claude/hooks/inject-context.sh
スクリプトは、現在時刻・現在ブランチ・未コミットの変更状況を git コマンドで取得し、チーム規約と併せて1つのテキストブロックにまとめ、JSON 形式で Claude Code に返します。hookSpecificOutput.additionalContext フィールドに格納された内容は、セッション冒頭で AI のコンテキストに追加されます。
#!/bin/bash
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
STATUS=$(git status --short 2>/dev/null | head -20)
NOW=$(date '+%Y-%m-%d %H:%M:%S')
# JSON 形式で additionalContext を返すと、Claude に「追加コンテキスト」として渡る
jq -n \
--arg now "$NOW" \
--arg branch "$BRANCH" \
--arg status "$STATUS" '
{
hookSpecificOutput: {
hookEventName: "SessionStart",
additionalContext: ("現在時刻: " + $now + "\n" +
"現在ブランチ: " + $branch + "\n" +
"未コミット変更:\n" + $status + "\n\n" +
"■ チーム規約\n" +
"・PR は必ず `feat/`, `fix/`, `chore/` プレフィックス\n" +
"・本番DBへの直接アクセス禁止\n" +
"・秘密情報は AWS Secrets Manager 経由")
}
}'
注入する内容は、チームの運用に合わせて自由に組み替えることができます。例えば Jira API から現在の Sprint ID を取得して追加する、Linear や Notion から課題リストを差し込む、リリースカレンダーの状態を入れる、といった応用が可能です。外部システムから取得した動的情報を、AI に自動で共有するための基盤として活用できます。
💡 DX推進リード向けポイント: Confluence や Notion に書いた規約がそもそも読まれていないというパターンに対して、本レシピは「セッション開始時に規約テキストを Claude のコンテキストに必ず流し込む」段階までを保証します。
注入したルールを AI が遵守するかどうかは、最終的にはプロンプトの問題に戻るため確率的です。決定論的に止めたいルール(破壊的コマンド、秘密情報アクセス等)につきましては、PreToolUse 系フックとの併用をお勧めします。
レシピ⑤:監査ログの自動取得(SessionEnd + PostToolUse)
AI がいつ・どのような操作を行ったかを後追いで確認できる状態にしておくことは、特にエンタープライズ環境では監査・コンプライアンスの観点から事実上の必須要件となっています。Claude Code 標準の動作ログだけでは粒度が粗かったり、保管期限が短かったりするため、自社管理のログ基盤に取り込める形で別途記録を残しておくことに価値があります。
本レシピでは、PostToolUse でツール呼び出しごとに1行の JSON(JSONL 形式)を書き出し、SessionEnd でセッション終了サマリを追記する構成を取ります。出力先は月単位で分割した JSONL ファイルとしており、Splunk・Datadog・Elastic といった主要なログ基盤への取り込みを想定したフォーマットです。
.claude/settings.json への追加
PostToolUse は matcher: "*" ですべてのツール呼び出しを捕捉し、軽量な JSON 1行追記のみを行うため timeout: 5 秒で動作します。SessionEnd は matcher の指定なしで常時実行とし、終了時のサマリを書き出します。
{
"hooks": {
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/audit-log.sh",
"timeout": 5
}
]
}
],
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/session-end-log.sh",
"timeout": 10
}
]
}
]
}
}
.claude/hooks/audit-log.sh
このスクリプトは、Claude Code から渡される JSON 入力を読み込み、タイムスタンプ・ユーザー名・セッションID・イベント名・ツール名・入力概要を抽出して、月単位のログファイルに1行の JSON として追記します。tool_input_summary は意図的に200文字で切っており、長大なツール入力でログが肥大化することを防いでいます。
#!/bin/bash
INPUT=$(cat)
LOG_DIR="${CLAUDE_PROJECT_DIR}/.claude/audit-logs"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/$(date '+%Y-%m').jsonl"
# 1行 = 1イベントの JSON Lines 形式で追記(SIEM 取り込み前提)
echo "$INPUT" | jq -c \
--arg ts "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
--arg user "${USER:-unknown}" '
{
timestamp: $ts,
user: $user,
session_id: .session_id,
event: .hook_event_name,
tool: .tool_name,
tool_input_summary: (.tool_input | tostring | .[0:200]),
cwd: .cwd
}' >> "$LOG_FILE"
exit 0
.claude/hooks/session-end-log.sh
セッション終了時のスクリプトは、その時点でのブランチ情報を加えたうえで、同じ月のログファイルへサマリ行を追記します。
#!/bin/bash
INPUT=$(cat)
LOG_DIR="${CLAUDE_PROJECT_DIR}/.claude/audit-logs"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/$(date '+%Y-%m').jsonl"
# セッション終了時のサマリを記録
echo "$INPUT" | jq -c \
--arg ts "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
--arg user "${USER:-unknown}" \
--arg branch "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'none')" '
{
timestamp: $ts,
user: $user,
session_id: .session_id,
event: "SessionEnd",
branch: $branch,
cwd: .cwd
}' >> "$LOG_FILE"
exit 0
💡 情シス部長向けポイント: JSONL は Splunk、Datadog、Elastic など主要な SIEM/ログ基盤が標準で取り込める形式です(フィールドマッピング等の設定は別途必要)。これにより、AIの操作履歴を SIEM に集約するための「データソース」が整う段階まで進められます。
監査体制として完成させるには、(1) ログのリテンション期間設定、(2) PII マスキング(
tool_input_summaryのサニタイズ)、(3) 異常検知ルール、(4) インシデント発生時のエスカレーション手順、までを別途設計する必要があります。本レシピはあくまでその「最初のデータ取得層」を担う位置付けです。
応用・発展:エンタープライズ向け高度活用
Managed Policy
個人や開発チームの設定上書きを許さず、組織が定めた Hooks ポリシーのみを有効化したい場合に使うのが Managed Policy です。
allowManagedHooksOnly: true を設定することで、おおむね以下の挙動になります。
- ユーザーの
~/.claude/settings.jsonの Hooks → 無効化 - プロジェクトの
.claude/settings.jsonの Hooks → 無効化 - プラグインの Hooks → 無効化
- 管理者が指定した Hooks のみが有効
これにより、「現場で .claude/settings.local.json を使って Hooks を上書きする」「プロジェクトの設定を改変してチェックを外す」といった、設定レイヤーでの回避を制限できます。
⚠️ 注意: Managed Policy の細部仕様はバージョンによって変動する可能性があるので、本番導入前には最新の公式ドキュメントで動作を確認することを推奨します。
過剰実行を防ぐ2つの軸 ― マッチャーとタイムアウト
フック設定の matcher フィールドは、「どのツール呼び出しに対してこの Hooks を動かすか」を決めるフィルターです。指定しなかったり "*" にすると全ツール呼び出しに対して発火するため、特に PostToolUse で重い処理を書いていると処理速度が一気に落ちます。マッチャーで対象を絞り込むことが、運用上もっとも効くチューニング箇所です。
| マッチャー記法 | 動作 | 用途 |
|---|---|---|
"*" または省略 | 全マッチ | 監査ログ(軽量処理) |
"Bash" | 完全一致 | Bash 限定の検査 |
"Edit|Write" | リスト一致 | ファイル編集系のみ |
"mcp__.*" | 正規表現 | 全MCPツールに適用 |
書き方の使い分けは「狙う粒度」で決まります。完全一致は単一ツールを狙うとき、リスト一致(パイプ区切り)は2〜3個の関連ツールをまとめたいとき、正規表現はMCPサーバーの全ツールやプレフィックスでまとめたい複数ツールを一括指定したいときに使います。「とりあえず * にしておく」は監査ログのような全捕捉が目的のときだけにして、ブロック系・チェック系は必ず必要なツールだけに絞り込むのが原則です。
もうひとつのチューニング軸が timeout です。Hooks の実行中は Claude Code 本体の処理が止まるため、設定した timeout がそのまま「ユーザーが待たされる最大時間」になります。Claude Code 全体の処理速度との兼ね合いを考えると、発火頻度が高い Hooks ほど短く、発火範囲を絞り込める Hooks ほど長く とるのが基本方針です。この観点から PreToolUse / PostToolUse については次のラインに収めるのを目安とします。
- PreToolUse: 5〜10秒 — 毎ツール呼び出し前に走るので、ここに置くのは「文字列パターンマッチ」「ファイルパス検査」など、ミリ秒〜秒で終わる軽量処理だけにします(本記事の §2-1・§2-2 のレシピもこのラインで
timeout: 5〜10を設定しています)。 - PostToolUse: 60〜120秒 — ターン中に走るとはいえ、編集後の型チェックやテスト実行など重めの検証を許容できます。公式の agent Hooks のデフォルトが 60 秒であることを踏まえ、
tsc --noEmitのようにプロジェクト規模に依存する処理を含める場合は 120 秒程度を上限として確保しておくと安全です。ただしツール呼び出しのたびに走るので、マッチャーでEdit|Writeのように絞ることとセットです。
上記はあくまで目安ですので、自社のプロジェクト規模や対象タスクに応じて調整いただくのが安全です。
通知連携 ― AI待ち時間のロス削減
Claude Code は、危険なコマンドの実行や外部APIアクセスなど、ユーザーの承認が必要な場面で permission_prompt(権限承認待ち)の状態に入って一時停止します。問題は、開発者がそのときターミナルから離れていると、AIが何分も待たされていることに気付かない点です。AIの稼働コストと開発者の時間の両方が無駄になるため、待ち状態に入った瞬間に Slack 等の常時見ているツールへ通知を飛ばす仕組みを入れておくと、生産性が変わります。
仕組みは比較的シンプルで、Notification イベントに対する Hooks を1つ登録し、その中から Slack の Incoming Webhook を呼び出すだけで構築できます。まず .claude/settings.json 側で、permission_prompt の通知をスクリプトへ接続します。
{
"hooks": {
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/notify-slack.sh"
}
]
}
]
}
}
📝 通知タイプの注意: 公式ドキュメントで確認できる Notification の matcher 例は
permission_promptとauth_successです。idle_promptのような他のタイプを目にすることもありますが、公式仕様で常に提供されているとは限らないため、本記事では公式に明記されているpermission_promptのみを採用しています。アイドル検知通知を入れたい場合は、利用環境の最新ドキュメントで提供状況を確認する必要があります。
スクリプト側は、Claude Code から渡される通知メッセージを取り出し、Slack Webhook に投げます。
#!/bin/bash
INPUT=$(cat)
MESSAGE=$(echo "$INPUT" | jq -r '.message // "Claude Code が入力待ちです"')
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "$(jq -n --arg text "$MESSAGE" '{text: $text}')"
Slack 以外への応用も難しくありません。curl の宛先を Microsoft Teams の Incoming Webhook URL に差し替えれば Teams に、Discord の Webhook に差し替えれば Discord に飛ばせます。社内のチャットツールが何であれ、Webhook 受信に対応していれば数行の変更で繋がります。さらに踏み込めば、特定の高リスクツール(例:DB 操作系MCP)だけ別チャンネルに飛ばす、メンションを付ける、といった出し分けもこの中で書くことが可能です。
落とし穴・運用上の注意
Hooks を導入しますと、動かない・止まらない・誤発火するといった事象が必ず一度は発生します。原因の多くは公式仕様の細部を見落としていることに起因しますので、頻発するつまずきポイントをパターンとして把握しておくことで、トラブルシュート時間を大きく短縮できます。
最も多いのが exit 1 でブロックしようとして失敗するケース です。exit 1 は「エラーは出たが、後続処理は止めない」という意味になるため、stderr にメッセージが出ていてもツールはそのまま実行されてしまいます。スクリプトを書いたあとは、実際に検知パターンを踏ませて「本当にツール呼び出しが止まったか」を確認する必要があります。
次に多いのが 標準入力の読み忘れ です。Claude Code はフックスクリプトに対し、ツール名や引数を含むJSONを 標準入力(stdin) で渡します。これを jq 等で直接読もうとすると、二度読みできずに空文字になることがあります。スクリプト冒頭で INPUT=$(cat) のようにいったん変数へ取り込み、その変数を echo "$INPUT" | jq ... で読むという定石を守る必要があります。
PostToolUse 全マッチで重い処理を走らせて応答が遅くなる ケースも頻発します。AIが1ターンに10回ツールを呼べば Hooks も10回走るため、matcher: "*" のまま型チェックやテスト実行を入れると体感的に作業が止まります。重い処理は Edit|Write 等で対象を絞り、timeout を明示して暴走を防ぎます。
HTTP / async Hooks のサイレント失敗 も気付きにくい類の落とし穴です。HTTP Hooks は仕様上、Non-2xx ステータス・接続失敗・タイムアウトのいずれも non-blocking error として扱われ、Webhook 受信側が落ちていても Claude Code 側は何も知らずに先へ進みます。同様に async: true を指定した Command Hooks はバックグラウンド実行となるため、内部でエラー終了してもメインフローには通知されません。これらを使う場合は、Hook 側でログ出力を残して別経路で監視するか、asyncRewake: true を併用して exit 2 時に Claude を呼び戻す設計にする必要があります。
別の代表例として、Hooks が登録されているか確認しないまま動作確認するケース があります。.claude/settings.json を直したつもりが、スクリプトに chmod +x を付け忘れていたり、matcher の正規表現が実際のツール名(例:MultiEdit の大文字違い)と一致していなかったり、上位スコープの設定で上書きされていたりすると、そもそも Hook は呼ばれません。Claude Code 内で /hooks コマンドを打つと、現在ロードされている全 Hooks を読み取り専用ビューで一覧でき、各 Hook の出所(User / Project / Local / Plugin / Session / Built-in)まで表示されますので、デバッグの最初の一手として習慣化すると早くなります。
環境変数の補間ミスは、command フィールドにシェル変数(例:$PROJECT_ROOT)をそのまま書いて、それが空文字に展開されてしまうケースです。フック設定の中で使えるパス展開は ${CLAUDE_PROJECT_DIR} や ${CLAUDE_PLUGIN_ROOT} といった公式プレースホルダに限られるので、独自の環境変数に頼らず公式記法を使うのが安全です。
最後に、設定変更後の再起動忘れもありがちです。settings.json を編集してもセッション中の Claude Code は古い設定で動き続けるため、動作確認をするときは Claude Code 自体を一度再起動してから行う必要があります。
| 落とし穴 | 症状 | 対処 |
|---|---|---|
exit 1 でブロックしようとした | ブロックされず、stderr が表示されるだけ | 必ず exit 2 |
| stdin の JSON を読まずに処理 | tool_input が取れず誤判定 | INPUT=$(cat) で全読み込みしてから jq |
| 全 PostToolUse で重い処理 | 応答が10秒以上遅延 | マッチャーで絞る、timeout を明示 |
| HTTP / async Hooks のサイレント失敗 | 失敗してもメインフローに通知されず気付かない | HTTP は 2xx + JSON で decision を返す、async は asyncRewake を併用 |
| Hooks が登録されていない | 設定を変えたのに動かない | /hooks コマンドで現在ロード中の Hooks を確認 |
| 環境変数の補間ミス | コマンドが空文字に展開 | ${CLAUDE_PROJECT_DIR} 等の公式プレースホルダを使用 |
| 設定変更後に再起動忘れ | 古い設定のまま | Claude Code を必ず再起動 |
導入効果として期待できる領域
Hooks は決定論的に動作するため、効果が現れる領域そのものは予想しやすいですが、改善度の数値はチーム規模・既存の自動化レベル・対象プロジェクトによって大きく変動します。公開されている独立調査は2026年5月時点で乏しいため、ここでは断定的な数値は提示せず、効果が出やすい領域を整理します。
まず期待できるのが、コード品質のばらつき低減です。レシピ③で導入する PostToolUse の自動 lint・型チェックは、AIが書いたコードがエラーを含んだまま次のステップに進むことを防ぎます。これにより、レビューの場で「型が合っていない」「lint が通らない」といった機械的な指摘が事前に潰れ、品質の下限が Hooks で固定されます。
次に来るのが、レビュー工数の削減です。レシピ③で機械的な指摘が消え、レシピ④で SessionStart に規約が注入されることで、フォーマットや命名規則のような「決まりごと」レベルの指摘がレビューに残りにくくなります。結果としてレビュアーは、設計の妥当性やロジックの正しさといった人間にしか判断できない領域に集中しやすくなります。
セキュリティ面では、インシデント発生確率の低下が見込めます。レシピ①の破壊的コマンド遮断とレシピ②の秘密情報保護は、過去にAI起因で起きやすかった事故パターン(rm -rf 系の広域削除、.env 流出)を、Hooks が捉えるパターンの範囲で物理的にブロックします。「うっかり」「指示の解釈違い」での事故を減らす効果が大きい領域です。
最後に、監査対応工数の削減があります。レシピ⑤の監査ログ(JSONL)が稼働していれば、「いつ・誰が・何を AI 経由で実行したか」を後追いで集計できる状態になります。監査の都度ヒアリングや手作業の調査をしなくて済む土台ができる、という意味での工数削減です。
| 領域 | 該当レシピ | 期待される効果の方向性 |
|---|---|---|
| コード品質のばらつき低減 | レシピ③(PostToolUse 自動 lint / 型チェック) | レビュー時の lint / 型エラー指摘の機械的削減 |
| レビュー工数の削減 | レシピ③、レシピ④(SessionStart コンテキスト注入) | フォーマット・規約系の指摘が事前に消えるため、レビュアーは設計・ロジックに集中可能 |
| インシデント発生確率の低下 | レシピ①(破壊的コマンド遮断)、レシピ②(秘密情報保護) | rm -rf 系・.env 流出系の操作を、Hooks が捉えたパターンの範囲でブロック可能 |
| 監査対応工数の削減 | レシピ⑤(監査ログ JSONL) | 「いつ・誰が・何を AI 経由で実行したか」を後追いで集計可能 |
💡 数値での効果測定の進め方: 導入前後の「PR(Pull Request:開発者がコード変更を本流ブランチに取り込むよう提案する単位)コメント件数」「レビュー所要時間」「インシデント件数」を自社で4〜8週間計測し、自組織の母集団に基づく数値で評価することを推奨します。市場で見かける「○○%改善」系の数字は前提条件が異なる組織の数値であることがほとんどなので、自社の改善目標としてそのまま採用するのは避けるのが安全です。
参考までに、公開されている近接事例の数値を挙げます。
- [査読前論文]AI 支援コードレビューを 300 名規模・約 1 年導入した企業内研究: PR レビュー所要時間 31.8% 短縮 との報告(DeputyDev 関連研究 / arXiv 2508.09676)。
- [ブログのケーススタディ]30 名規模の開発組織で Claude Code を全面導入した事例(Hooks 11 個 + 共有スキル 22 個を整備): 90 日継続計測で生産性 35% 向上 との報告(digitalapplied.com 2026 ケーススタディ)。
- [ベンダーレポート]OSS の GitHub PR 470 件(AI 共著 320 件、人間のみ 150 件)を独立解析した調査: AI 共著 PR には人間のみと比較して 約 1.7 倍の問題 が検出されたとの報告(CodeRabbit “State of AI vs Human Code Generation Report” 2025)。
まとめと運用チェックリスト
要点の整理
- Hooks はプロンプトと違い「決定論的に走る」: AIの判断に依存しないため、ガバナンス・監査要件の データ取得層/実行制御層 として活用できる。(ただし Hooks だけで監査要件全体が満たされるわけではなく、リテンション設定や PII マスキング等は別途設計が必要)
- 4レイヤー(セッション/ターン/ツール/非同期)でイベントを選ぶ: 用途に応じた最小限の Hooks を設計する。
exit 2だけがブロックする: ここを間違えると「動いているように見えて動いていない」状態になる。.claude/settings.jsonのコミットでチーム既定適用: ユーザー側の上書きを許さず統一したい場合は、Managed Policy(§3.1)の併用を検討する。- マッチャーと timeout で過剰実行を防ぐ:
matcherで対象ツールを絞り込み、参考レイテンシ予算(Pre: 5〜10秒 / Post: 60〜120秒)を起点にtimeoutを自社環境で調整する。
情シス部長・開発リーダー向け 導入前チェックリスト
- 破壊的コマンド遮断フック(rm -rf、DROP TABLE 等)を
.claude/settings.jsonに登録したか - 秘密情報ファイル(.env、SSH鍵)保護フックを登録したか
- 編集後の自動 lint / 型チェック(PostToolUse)を有効化したか
- SessionStart で動的コンテキスト(チーム規約・ブランチ情報)を注入しているか
- 監査ログ(JSONL)の出力先と保管期間を定めたか
- Notification Hooks で Slack / Teams 連携を構築したか
- Managed Policy で組織統制が必要かを評価したか
- Hooks の
timeout値を全件設定したか(デフォルト依存にしない) exit 2がブロック、exit 1は非ブロックである点をチーム内で周知したか- レッドチーミング(フック回避テスト)を実施したか
「ただ AI を使う」から「事故リスクを設計で減らす」へ
Claude Code Hooks は、AI コーディングのリスクを設計レベルで扱えるようにする仕組みです。ブロック系の Hooks(PreToolUse + exit 2)は決定論的に効く一方、コンテキスト注入で AI にルールを伝える運用は最終的に AI の判断に委ねられることに留意は必要ですが、それでも次の3つの実用的な価値があります。
- PreToolUse による決定論的なブロック: 破壊的コマンド・秘密情報アクセスを、実装レベルで止められる
- PostToolUse による品質ゲート: AI が書いたコードに対して lint / 型チェックを必ず走らせ、レビュー段階に進む前にエラーを差し戻せる
- SessionEnd / PostToolUse による監査ログ: AI の操作履歴を JSONL で残し、SIEM 連携の起点を作れる
情シス部長にとっては AI ガバナンスの一構成要素、DX推進リードにとっては 品質と速度を両立させるレバーのひとつ、開発チームリーダーにとっては チームのコード品質を底上げする手段。立場によって価値の見え方は違いますが、いずれも「Hooks ですべてが解決する」のではなく「組織の AI 統制全体の中で、Hooks がカバーできる領域を見極めて実装する」という姿勢が成果に直結します。
最後に
私たちは、単にシステムを組むだけの開発会社ではありません。低コストで高品質なAIツールの構築から、ROI(投資対効果)を最大化する導入ロードマップの策定、社内スタッフが自らAIを運用・改善できる体制の構築まで、AI導入の成功に必要なすべてを最初から最後まで丸ごと支援いたします。
実は、ご相談いただく方のほとんどが「何が分からないかも分からない」という状態からのスタートです。構想段階でも、ただのアイデアベースでも構いません。
まずは、あなたのお困りごとをそのまま聞かせていただけませんか?貴社のビジネスを加速させるパートナーとして伴走いたします。
参考文献
- Anthropic. “Hooks reference – Claude Code Docs.” https://code.claude.com/docs/en/hooks
- AI Lab OISHI. “Claude Code Hooks 完全実装ガイド|29イベントで品質ゲート・安全運用を仕組み化【2026年5月版】.” https://www.oishillc.jp/claude-code-hooks-implementation-guide-2026-05/
- kawabe0201. “Claude Code Hooks で開発フローを全自動化した話 ─ 実戦で使っている15の hook.” Qiita. https://qiita.com/kawabe0201/items/3fcf698abe60d57b211b
- karanb192. “claude-code-hooks: A growing collection of useful Claude Code hooks.” GitHub. https://github.com/karanb192/claude-code-hooks
- Uravation. “【2026年最新】Claude Code Hooksガイド|自動化設定.” https://uravation.com/media/claude-code-hooks-guide-2026/
- Uravation. “Claude Code 法人導入完全ガイド|IT管理者向け【2026】.” https://uravation.com/media/claude-code-enterprise-it-admin-guide-2026/
- DataCamp. “Claude Code Hooks: A Practical Guide to Workflow Automation.” https://www.datacamp.com/tutorial/claude-code-hooks
- Blake Crosley. “Claude Code Hooks Tutorial: 5 Production Hooks From Scratch.” https://blakecrosley.com/blog/claude-code-hooks-tutorial
- aiorg.dev. “Claude Code Hooks: Complete Guide with 20+ Ready-to-Use Examples (2026).” https://aiorg.dev/blog/claude-code-hooks
- AI革命株式会社. “Claude Code Hooks 使い方|PreToolUse/PostToolUse/Stop/SubagentStop 完全ガイド.” https://ai-revolution.co.jp/media/claude-code-hooks-guide/
- Paul Schick. “Claude Code Hooks: PreToolUse & PostToolUse Tutorial.” Paul’s Development Blog. https://paul-schick.com/posts/claude-code-hooks-pretooluse-posttooluse/
- “DeputyDev — AI Powered Developer Assistant: Breaking the Code Review Logjam through Contextual AI to Boost Developer Productivity.” arXiv. https://arxiv.org/pdf/2508.09676
- Digital Applied. “Case Study: Claude Code Adoption at a 30-Dev Shop 2026.” https://www.digitalapplied.com/blog/case-study-claude-code-team-adoption-30-dev-shop-2026
- CodeRabbit. “State of AI vs Human Code Generation Report.” 2025. https://www.coderabbit.ai/blog/state-of-ai-vs-human-code-generation-report
