Claude Codeでの開発中、「ファイル保存のたびにフォーマッターを手動で実行している」「危険なコマンドをうっかり許可してしまった」といった経験はないでしょうか。hooks機能を活用すると、こうした反復作業や見落としをシェルコマンドやLLM判定で自動化できます。
Claude Code hooksとは、Claude Codeのライフサイクル上の決まったタイミングで自動実行されるユーザー定義のコマンド群です。LLMの判断に頼らず確定的(deterministic)に動作するため、「必ず実行してほしい処理」に適しています。
hooksの基本構造と設定ファイル
hooksの設定はJSON形式で記述し、3つの階層で構成されます。
- イベント名(いつ発火するか)
- マッチャーグループ(どのツール・条件で絞り込むか)
- ハンドラ配列(何を実行するか)
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
設定ファイルの配置場所とスコープ
設定ファイルの置き場所によって適用範囲が変わります。
| 配置先 | 適用範囲 | リポジトリ共有 |
|---|---|---|
~/.claude/settings.json | 全プロジェクト共通 | 不可(ローカルのみ) |
.claude/settings.json | プロジェクト単位 | 可能(git commit対象) |
.claude/settings.local.json | プロジェクト単位 | 不可(.gitignore推奨) |
| マネージドポリシー | 組織全体 | 管理者が制御 |
プラグイン hooks/hooks.json | プラグイン有効時 | プラグインに同梱 |
| スキル・エージェントのfrontmatter | コンポーネント稼働中のみ | コンポーネントに定義 |
チームで統一したいルール(自動フォーマット等)は .claude/settings.json、個人環境だけに適用する通知設定は ~/.claude/settings.json に置く、という使い分けが実務では効果的です。
/hooksメニューによる対話的な設定
CLIで /hooks と入力すると、GUIライクなメニューが開きます。JSONを直接編集しなくても、イベント選択→マッチャー指定→コマンド入力→保存先選択の流れで設定を追加できます。
メニュー内ではフックごとにソースラベルが表示されます。
[User]:~/.claude/settings.json[Project]:.claude/settings.json[Local]:.claude/settings.local.json[Plugin]: プラグイン同梱のhooks
全hooksを一括で無効化するトグルもこのメニューに用意されています。
全14イベントの一覧と使い分け
2026年2月時点でClaude Code hooksが対応するイベントは14種類です。セッションの開始から終了まで、ライフサイクル順に並べます。
| イベント | 発火タイミング | ブロック可否 |
|---|---|---|
SessionStart | セッション開始・再開時 | 不可 |
UserPromptSubmit | ユーザーがプロンプトを送信した直後(処理前) | 可能 |
PreToolUse | ツール実行の直前 | 可能 |
PermissionRequest | 権限ダイアログ表示時 | 可能 |
PostToolUse | ツール実行成功の直後 | 不可(実行済み) |
PostToolUseFailure | ツール実行失敗の直後 | 不可(失敗済み) |
Notification | 通知送信時 | 不可 |
SubagentStart | サブエージェント起動時 | 不可 |
SubagentStop | サブエージェント完了時 | 可能 |
Stop | メインエージェントの応答完了時 | 可能 |
TeammateIdle | チームメイトがアイドル移行する直前 | 可能 |
TaskCompleted | タスク完了マーク時 | 可能 |
PreCompact | コンテキスト圧縮の直前 | 不可 |
SessionEnd | セッション終了時 | 不可 |
マッチャーの動作と正規表現パターン
マッチャーはイベントごとに異なるフィールドに対して正規表現で照合されます。
| イベント | マッチ対象 | 記述例 |
|---|---|---|
PreToolUse / PostToolUse / PostToolUseFailure / PermissionRequest | ツール名 | Bash、Edit|Write、mcp__.* |
SessionStart | 開始方法 | startup、resume、clear、compact |
SessionEnd | 終了理由 | clear、logout、prompt_input_exit、bypass_permissions_disabled、other |
Notification | 通知タイプ | permission_prompt、idle_prompt、auth_success、elicitation_dialog |
SubagentStart / SubagentStop | エージェント種別 | Bash、Explore、Plan、カスタムエージェント名 |
PreCompact | トリガー | manual、auto |
UserPromptSubmit / Stop / TeammateIdle / TaskCompleted | なし | 全発火時に実行(マッチャー非対応) |
マッチャーを省略するか "*" と書くと、該当イベントの全発火に対してhookが実行されます。
MCPツールへのhook適用
MCPサーバーのツールにもhooksを適用できます。ツール名は mcp__<サーバー名>__<ツール名> の形式です。
{
"hooks": {
"PreToolUse": [
{
"matcher": "mcp__github__.*",
"hooks": [
{
"type": "command",
"command": "echo \"GitHub MCP tool called\" >> ~/mcp-ops.log"
}
]
}
]
}
}
mcp__memory__.* のようにサーバー単位での一括指定や、mcp__.*__write.* のように全サーバーの書き込み系操作を横断して捕捉する書き方も可能です。
hookハンドラの3タイプ:command / prompt / agent
hookが発火したとき実行される処理は3種類から選択できます。
commandタイプ(シェルコマンド)
もっとも基本的な方式です。シェルコマンドを直接実行し、stdin経由でイベントのJSON情報を受け取ります。
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/lint-check.sh",
"timeout": 30
}
| フィールド | 必須 | 説明 |
|---|---|---|
type | ○ | "command" |
command | ○ | 実行するシェルコマンド |
timeout | × | タイムアウト秒数(デフォルト: 600) |
async | × | true で非同期実行(結果は次ターンで配信) |
statusMessage | × | 実行中にスピナーに表示するメッセージ |
once | × | true でセッション中に1回だけ実行(スキル限定) |
promptタイプ(LLM単発判定)
シェルコマンドではなく、Claudeモデル(デフォルトはHaiku)にプロンプトを送って判定させる方式です。$ARGUMENTS プレースホルダにイベントのJSONが展開されます。
{
"type": "prompt",
"prompt": "以下のコンテキストを確認し、全タスクが完了しているか判定してください: $ARGUMENTS",
"timeout": 30
}
モデルは {"ok": true} または {"ok": false, "reason": "理由"} のJSON形式で応答します。
agentタイプ(マルチターン検証)
promptタイプをさらに拡張し、サブエージェントがRead・Grep・Globなどのツールを使いながら最大50ターンにわたって検証する方式です。
{
"type": "agent",
"prompt": "テストスイートを実行し、全テストが通過しているか確認してください。$ARGUMENTS",
"timeout": 120
}
3タイプの選定基準
| 観点 | command | prompt | agent |
|---|---|---|---|
| 判定の性質 | 確定的ルール | LLMの判断 | LLM+ツール調査 |
| 実行速度 | 最速 | 数秒 | 数十秒〜 |
| 非同期対応 | async: true で可能 | 不可 | 不可 |
| デフォルトタイムアウト | 600秒 | 30秒 | 60秒 |
| 適用例 | フォーマッタ実行、ファイル保護 | コード品質の定性チェック | テスト実行結果の包括検証 |
「rm -rf を含むコマンドをブロックする」のような明確なルールにはcommand、「実装が完了しているか総合判定する」のような曖昧な基準にはpromptまたはagentが適しています。
stdin入力とstdout出力の仕様
共通入力フィールド
全イベントで共通して受け取るJSON構造は次の通りです。
{
"session_id": "abc123",
"transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
"cwd": "/home/user/my-project",
"permission_mode": "default",
"hook_event_name": "PreToolUse"
}
イベントごとに tool_name、tool_input、prompt、source などの固有フィールドが追加されます。
終了コードによる制御
| 終了コード | 意味 | 動作 |
|---|---|---|
| 0 | 成功 | stdoutのJSONを解析し、アクション続行 |
| 2 | ブロックエラー | stderrのメッセージをClaudeにフィードバックし、アクションを中止 |
| その他 | 非ブロックエラー | stderrをverboseモードで表示し、アクション続行 |
JSON出力による詳細制御
終了コード0でJSONを出力すると、より細かい振る舞いを指定できます。
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "grep よりも rg を使ってください"
}
}
PreToolUse のパーミッション判定は3段階です。
| 値 | 動作 |
|---|---|
"allow" | パーミッションダイアログをスキップして実行許可 |
"deny" | ツール実行を拒否し、理由をClaudeに通知 |
"ask" | 通常通りユーザーに確認を求める |
全イベント共通で使えるフィールドもあります。
| フィールド | 説明 |
|---|---|
continue | false にするとClaude全体の処理を停止 |
stopReason | continue: false 時にユーザーに表示するメッセージ |
suppressOutput | true でverboseモードへのstdout表示を抑制 |
systemMessage | ユーザーへの警告メッセージ |
実践レシピ:すぐに使えるhook設定5選
1. ファイル編集後の自動フォーマット
開発現場で最も導入効果の高いhookです。Prettierを例にしますが、Black(Python)やrustfmt(Rust)など任意のフォーマッターに差し替え可能です。
.claude/settings.json に追加します。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
PostToolUseイベントはツール実行完了後に発火するため、Claudeが書き込んだファイルに対して即座にフォーマッターが走ります。
2. 危険コマンドのブロック
rm -rf や DROP TABLE のような破壊的コマンドを実行前に検知して拒否するスクリプトです。
.claude/hooks/block-dangerous.sh:
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
BLOCKED_PATTERNS=("rm -rf" "DROP TABLE" "DROP DATABASE" "format c:" "> /dev/sda")
for pattern in "${BLOCKED_PATTERNS[@]}"; do
if echo "$COMMAND" | grep -qi "$pattern"; then
echo "ブロック: '$pattern' を含むコマンドは実行できません" >&2
exit 2
fi
done
exit 0
.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous.sh"
}
]
}
]
}
}
3. デスクトップ通知(OS別)
Claudeが入力待ちや権限確認で停止したとき、デスクトップ通知を送る設定です。ターミナルを常時監視する必要がなくなります。
~/.claude/settings.json に追加します。
macOS:
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Codeが入力を待っています\" with title \"Claude Code\"'"
}
]
}
]
}
}
Linux(notify-send):
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Claude Codeが入力を待っています'"
}
]
}
]
}
}
WSL / Windows:
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "powershell.exe -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Codeが入力を待っています', 'Claude Code')\""
}
]
}
]
}
}
4. センシティブファイルの保護
.env、秘密鍵、ロックファイルなど変更してほしくないファイルへの書き込みをPreToolUseで防ぎます。
.claude/hooks/protect-files.sh:
#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
PROTECTED_PATTERNS=(".env" ".git/" "id_rsa" "id_ed25519" "package-lock.json" "yarn.lock" "Cargo.lock")
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "保護対象: $FILE_PATH は '$pattern' に該当するため編集をブロックしました" >&2
exit 2
fi
done
exit 0
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}
5. コンテキスト圧縮後の情報再注入
Claude Codeはコンテキストウィンドウが逼迫すると自動圧縮(compaction)を実行します。圧縮時にプロジェクト固有の注意事項が失われることがあるため、SessionStartイベントの compact マッチャーで再注入するのが有効です。
{
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "echo '注意: パッケージマネージャーはpnpmを使用。テストはvitest。コミット前にlint必須。'"
}
]
}
]
}
}
stdoutに出力したテキストがClaudeのコンテキストに追加されるため、動的な情報(直近のgitログなど)も注入可能です。
echo "直近のコミット:" && git log --oneline -5
非同期hookの活用
テストスイートの実行やデプロイなど完了まで時間がかかる処理は、"async": true を指定してバックグラウンド実行できます。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/run-tests-async.sh",
"async": true,
"timeout": 300
}
]
}
]
}
}
非同期hookの特性は次の通りです。
- Claudeの処理をブロックせず、バックグラウンドで実行される
decisionやpermissionDecisionによる判定制御は無効(既にアクションが進行済み)- 実行結果は次の会話ターンで
systemMessage/additionalContextとして配信される - commandタイプ限定(prompt / agentは非対応)
環境変数の永続化(CLAUDE_ENV_FILE)
SessionStartイベント限定で利用可能な CLAUDE_ENV_FILE 環境変数を使うと、セッション中に実行されるすべてのBashコマンドに環境変数を引き継がせることができます。
#!/bin/bash
if [ -n "$CLAUDE_ENV_FILE" ]; then
echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi
exit 0
nvmやrbenvなどバージョンマネージャーの切り替え結果を永続化するパターンも有効です。
#!/bin/bash
ENV_BEFORE=$(export -p | sort)
source ~/.nvm/nvm.sh
nvm use 20
if [ -n "$CLAUDE_ENV_FILE" ]; then
ENV_AFTER=$(export -p | sort)
comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi
exit 0
セキュリティのベストプラクティス
hooksはユーザーのシステム権限で動作します。悪意のある設定やミスは深刻な被害に直結するため、以下の対策を徹底してください。
| 対策 | 具体的な実施方法 |
|---|---|
| 入力値の検証 | stdin経由のJSONは必ずバリデーション |
| シェル変数のクォート | "$VAR" の形式で展開(スペース・特殊文字対策) |
| パストラバーサル対策 | ファイルパスに .. が含まれていないか検査 |
| 絶対パスの使用 | $CLAUDE_PROJECT_DIR でプロジェクトルートを参照 |
| センシティブファイル除外 | .env、.git/、秘密鍵を処理対象から除外 |
| 信頼できないプロジェクトへの警戒 | 外部リポジトリの .claude/settings.json にhooksが仕込まれていないか確認 |
hooksの設定はセッション開始時にスナップショットとして読み込まれます。セッション途中で外部からファイルが書き換えられても即座に反映されないため、サプライチェーン攻撃に対する一定の防御が働きます。変更が検知された場合は /hooks メニューでレビューが求められます。
トラブルシューティング
hookが発火しない
/hooksで正しいイベントに登録されているか確認- マッチャーの正規表現がツール名と正確に一致しているか確認(大文字小文字の区別あり)
PermissionRequesthookは非対話モード(-p)では発火しません。PreToolUseを使ってください
Stopイベントの無限ループ
Stopイベントのhookが exit 2 や "decision": "block" を常に返すと、Claudeが停止できず無限ループに陥ります。入力JSONの stop_hook_active フィールドを必ず確認してください。
#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0 # 二重発火を防止
fi
# ... 本来のチェック処理
JSON出力のパースエラー
~/.zshrc や ~/.bashrc に無条件の echo 文があると、hookスクリプトのstdoutにゴミが混入してJSONパースに失敗します。
# ~/.zshrc で対話シェルのみに限定
if [[ $- == *i* ]]; then
echo "Shell ready"
fi
デバッグ方法
claude --debug で起動すると、hookのマッチング結果・終了コード・出力内容が詳細に表示されます。Ctrl+O でverboseモードを切り替えることもできます。
[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Hook command completed with status 0: <出力内容>
スクリプト単体のテストはパイプでJSONを流し込む方法が確実です。
echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh
echo $?
hooksとCLAUDE.md・MCPサーバーの使い分け
Claude Codeには拡張ポイントが複数あり、用途に応じて選択する必要があります。
| 機能 | 性質 | 適した用途 |
|---|---|---|
| hooks | 確定的・自動実行 | フォーマット、ブロック、通知、ログ |
| CLAUDE.md | LLMへの指示テキスト | コーディング規約、プロジェクトルール |
| MCPサーバー | 外部ツール連携 | DB操作、API呼び出し、ファイルシステム拡張 |
| スキル | 呼び出し可能な手順書 | 定型タスクの手順化(hookをfrontmatterで同梱可能) |
| サブエージェント | 隔離コンテキストでの並列実行 | 調査、テスト、独立タスク |
hooksは「LLMが忘れてもルールどおりに動く仕組み」、CLAUDE.mdは「LLMに知っておいてほしい背景情報」と整理すると迷いが減ります。
よくある質問
Q. hooksの実行順序は? 同一イベント・同一マッチャーに複数のhookが登録されている場合、並列に実行されます。完全に同一のコマンドは自動で重複排除されます。
Q. hooksは非対話モード(claude -p)でも動作しますか?
PermissionRequest を除く全イベントが動作します。CI/CDパイプラインでも活用可能です。
Q. hookスクリプトから別のツールを呼び出せますか? hooksの通信手段はstdin / stdout / stderr / 終了コードに限定されます。Claude Codeのツール(Edit、Writeなど)を直接呼び出す仕組みはありません。外部コマンド(git、npm、curlなど)はシェル経由で自由に実行できます。
Q. updatedInput で何ができますか?
PreToolUseまたはPermissionRequestのhookで、ツールに渡される入力パラメータを実行前に書き換えられます。たとえばBashコマンドの引数を安全な値に差し替えるといった使い方が可能です。
まとめ
Claude Code hooksは、全14イベント・3種類のハンドラタイプで開発ワークフローを精密に自動化できる拡張機構です。
セットアップの第一歩としておすすめの順序は次の通りです。
- 通知hook(Notification): ターミナル監視からの解放
- 自動フォーマット(PostToolUse + Edit|Write): コードスタイルの自動統一
- ファイル保護(PreToolUse + Edit|Write): センシティブファイルの誤編集防止
- 危険コマンドブロック(PreToolUse + Bash): 破壊的操作の抑止
公式ドキュメントは以下から確認できます。