Claude Code Hooks は 27 のライフサイクルイベントで agent loop に介入できる仕組み — 危険コマンドのブロック、Slack通知、フォーマッタ実行、ポリシー強制、全ツールコールのロギング。本記事は 完全本番リファレンス:全イベント、全ハンドラ、全exit code ルール を Anthropic 公式 Claude Code docs(2026年5月版、v2.1.141+)で検証して並べた一枚。
Hooks を本番投入するなら、このページをブックマークしてほしい。
27 Hook Events 全カタログ
Hooks は5つのライフサイクル cadence で発火する。
セッションレベル(1セッション1回)
| Event | 発火タイミング |
|---|---|
SessionStart | セッション開始 or 再開時 |
Setup | --init-only、--init、-pモードの--maintenance 起動時 |
SessionEnd | セッション終了時 |
ターンレベル(1ユーザー prompt 1回)
| Event | 発火タイミング | ブロック可? |
|---|---|---|
UserPromptSubmit | 送信後、Claude処理前 | ✅ |
UserPromptExpansion | 入力コマンドが prompt に展開、Claude到達前 | ✅ |
Stop | Claudeが応答終了時 | ✅ |
StopFailure | APIエラーでturn終了時 | ❌ 観測のみ |
Agentic Loop(毎ツールコール)
| Event | 発火タイミング | ブロック可? |
|---|---|---|
PreToolUse | ツールコール実行前 | ✅ |
PostToolUse | ツールコール成功後 | ❌ 既実行 |
PostToolUseFailure | ツールコール失敗後 | ❌ 既失敗 |
PostToolBatch | 並列ツールコールバッチ全完了後、次モデル呼び出し前 | ✅ loop停止 |
PermissionRequest | 権限ダイアログ表示時 | ✅ 拒否可 |
PermissionDenied | autoモード分類器がツールコール拒否時 | ❌ 既拒否 |
Agent / Team イベント
| Event | 発火タイミング | ブロック可? |
|---|---|---|
SubagentStart | サブエージェント spawn時 | ❌ |
SubagentStop | サブエージェント終了時 | ✅ stop防止 |
TeammateIdle | エージェントチーム teammate がアイドル直前 | ✅ |
TaskCreated | TaskCreate でタスク作成時 | ✅ ロールバック |
TaskCompleted | タスクが completed マーク時 | ✅ 完了防止 |
ファイル & 環境イベント
| Event | 発火タイミング | ブロック可? |
|---|---|---|
InstructionsLoaded | CLAUDE.md or .claude/rules/*.md が context にロード時 | ❌ |
ConfigChange | セッション中に設定ファイル変更時 | ✅(policy_settings 除く) |
CwdChanged | 作業ディレクトリ変更時 | ❌ |
FileChanged | 監視ファイルがディスク上で変更時 | ❌ |
WorktreeCreate | --worktree で worktree 作成時 | ❌ |
WorktreeRemove | worktree 削除時 | ❌ |
コンテキスト & 通知イベント
| Event | 発火タイミング | ブロック可? |
|---|---|---|
PreCompact | context compaction 前 | ✅ compaction blocks |
PostCompact | compaction 完了後 | ❌ |
Notification | Claude Code が通知送信時 | ❌ |
Elicitation | MCP server が tool call中にユーザー入力要求時 | ❌ |
ElicitationResult | ユーザーが MCP elicitation に応答後 | ❌ |
合計 27 distinct events(タイトルの「32+」はサブタイプ含む — SessionStart は matcher startup/resume/clear/compact で別トリガー扱い)。
5 Handler Types
{
"type": "command" | "http" | "mcp_tool" | "prompt" | "agent",
"if": "Bash(git *)", // optional:permission rule フィルタ
"timeout": 600, // seconds before キャンセル
"statusMessage": "Checking...", // optional:spinner メッセージ
"once": true // optional:1セッション1回
}
1. command — シェルスクリプト
主力。シェルコマンドを spawn、stdin で JSON、stdout JSON / stderr で結果。
{
"type": "command",
"command": "/path/to/secret-guard.sh",
"args": ["${tool_input.file_path}"],
"shell": "bash",
"async": false
}
- shell form(
argsなし):full tokenization、pipes、glob 利用可 - exec form(
argsあり):no shell、各 arg そのまま、path placeholder 置換 async: true:バックグラウンド実行、agent loop ブロックなしasyncRewake: true:async コマンドが exit 2 で終了時に wake
2. http — Webhook
HTTP エンドポイントに JSON POST。中央ポリシーサービスや Slack/Discord 通知向け。
{
"type": "http",
"url": "http://localhost:8080/hook",
"headers": { "Authorization": "Bearer $MY_TOKEN" },
"allowedEnvVars": ["MY_TOKEN"]
}
- 2xx 空 body:成功(= exit 0 相当)
- 2xx + JSON:hook output として parse
- 非2xx:non-blocking error、実行継続
3. mcp_tool — MCP サーバーに委譲
接続済み MCP server の tool に hook 判断を委譲。
{
"type": "mcp_tool",
"server": "my_server",
"tool": "security_scan",
"input": { "file_path": "${tool_input.file_path}" }
}
server 切断 or tool が isError: true 返した場合、hook は non-blocking error で実行継続。ネットワーク失敗下で policy 強制が必要なら mcp_tool に依存しない。
4. prompt — Single-Turn Claude 判定
{
"type": "prompt",
"prompt": "Is this $ARGUMENTS safe? Reply JSON with {decision, reason}.",
"model": "claude-haiku-4-5"
}
$ARGUMENTS が hook input JSON。モデルが JSON 返し → Claude Code が hook output として parse。判断境界明確なら Haiku で速度優先。
5. agent — サブエージェント spawn
ツールアクセス付きフルサブエージェント。最重いが多段推論可。
{
"type": "agent",
"prompt": "Investigate why this command failed: $ARGUMENTS",
"model": "claude-opus-4-7"
}
Matcher Patterns
matcher フィールドで event 内発火条件を絞る。
| Matcher 値 | 評価 | 例 |
|---|---|---|
"*"、""、省略 | 全マッチ | 全発火 |
英数字/_/| のみ | exact 文字列 or |-区切りリスト | Bash or Edit|Write |
| その他文字含む | JavaScript regex | ^Notebook or mcp__memory__.* |
MCP Tool マッチング
MCP tool は mcp__<server>__<tool> 形式:
{ "matcher": "mcp__memory__.*" } // memory server 全ツール
{ "matcher": "mcp__.*__write.*" } // 全サーバの write 系
Exit Code Semantics(セキュリティ最重要)
公式 docs の最重要ルール:
「ほとんどの hook event で、exit code 2 だけが action をブロックする。Claude Code は exit code 1 を non-blocking error として扱い、action を続行する(1 は Unix 慣習の失敗コードにも関わらず)。hook がポリシー強制目的なら
exit 2を使うこと。」
| Exit code | 意味 | 挙動 |
|---|---|---|
| 0 | 成功 | stdout JSON を parse、JSON がブロックしなければ続行 |
| 2 | Blocking error | stdout 無視、stderr をエラーメッセージとして使用。capable event で action をブロック |
| その他 | Non-blocking error | stdout 無視、stderr 先頭行を transcript 表示。実行継続 |
これが Hook 実装の半分を壊している gotcha:開発者は exit 1 を反射的に使う、hook は silent fail、危険な action は通過する。policy 強制には必ず exit 2。
設定ロケーション
| ロケーション | スコープ | 共有可? |
|---|---|---|
~/.claude/settings.json | 全プロジェクト | No、local |
.claude/settings.json | 単プロジェクト | Yes、committed |
.claude/settings.local.json | 単プロジェクト | No、gitignored |
| Managed policy settings | 組織全体 | Yes、admin制御 |
Plugin hooks/hooks.json | 有効化中 | Yes、bundled |
| Skill/agent frontmatter | コンポーネント寿命 | Yes、ファイル内 |
3階層 ネスト構造
{
"hooks": {
"PreToolUse": [ // Level 1: event
{
"matcher": "Bash", // Level 2: matcher グループ
"hooks": [
{
"type": "command", // Level 3: handler
"command": "/path/to/script.sh"
}
]
}
]
}
}
JSON Input / Output
共通 stdin Input(全イベント)
{
"session_id": "abc123",
"transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
"cwd": "/home/user/my-project",
"permission_mode": "default",
"hook_event_name": "PreToolUse"
}
subagent context では追加で agent_id と agent_type。tool-use context では effort.level($CLAUDE_EFFORT 環境変数でも参照可)。
JSON stdout Output
{
"continue": true, // false で Claude 完全停止
"stopReason": "Build failed", // continue=false 時のメッセージ
"suppressOutput": false, // debug log から除外
"systemMessage": "Warning", // ユーザー警告
"terminalSequence": "\033]...", // ターミナル escape (v2.1.141+)
"decision": "block", // ほとんどの event 用
"reason": "Not allowed",
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"additionalContext": "...",
"permissionDecision": "deny",
"permissionDecisionReason": "..."
}
}
Decision Control パターン
event ごとに output schema が異なる。
ほとんどの event — top-level decision:
{ "decision": "block", "reason": "Test suite must pass" }
PreToolUse — ネストの hookSpecificOutput.permissionDecision:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Database writes not allowed"
}
}
本番パターン6選
Pattern 1: 危険コマンドブロック(PreToolUse + exit 2)
#!/bin/bash
# block-dangerous.sh — PreToolUse、matcher: Bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command')
if echo "$CMD" | grep -qE 'rm -rf /|git push --force.*main'; then
echo "Refusing dangerous command: $CMD" >&2
exit 2 # Critical: NOT exit 1
fi
exit 0
Pattern 2: 編集後フォーマット(PostToolUse)
#!/bin/bash
# format-after-edit.sh — PostToolUse、matcher: Edit|Write
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path')
case "$FILE" in
*.ts|*.tsx) npx prettier --write "$FILE" 2>/dev/null ;;
*.py) black "$FILE" 2>/dev/null ;;
*.go) gofmt -w "$FILE" 2>/dev/null ;;
esac
exit 0
Pattern 3: シークレットガード(PreToolUse Edit)
#!/bin/bash
INPUT=$(cat)
CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // .tool_input.new_string')
if echo "$CONTENT" | grep -qE 'sk-[a-zA-Z0-9]{20,}|ghp_[a-zA-Z0-9]{36}|AKIA[A-Z0-9]{16}'; then
echo "Refusing to write potential secret to file" >&2
exit 2
fi
exit 0
Pattern 4: Telegram 通知 on Stop
#!/bin/bash
# telegram-notify.sh — Stop hook、matcher なし
MSG="Claude Code session ended at $(date)"
curl -s "https://api.telegram.org/bot$TG_TOKEN/sendMessage" \
-d "chat_id=$TG_CHAT_ID" -d "text=$MSG" > /dev/null
exit 0
Pattern 5: コストロガー(PostToolUse + PostToolBatch)
#!/bin/bash
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
echo "$(date -u +%FT%TZ) $TOOL" >> ~/.claude/cost-log.tsv
exit 0
日次集計に流して、プロジェクト別 tool 使用解析。
Pattern 6: main 直接 push ブロック
#!/bin/bash
# matcher: Bash, "if": "Bash(git push*)"
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command')
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null)
if echo "$CMD" | grep -qE 'git push.*origin (main|master)' && [ "$BRANCH" = "main" ]; then
echo "Push to main blocked. Use a feature branch + PR." >&2
exit 2
fi
exit 0
Hooks vs Subagents vs Skills(使い分け)
| 必要なもの | 使うべき |
|---|---|
| ライフサイクルイベント(pre-commit、post-edit、on-stop)で自動発火 | Hook |
| 再利用ワークフローを名前でオンデマンド呼び出し | Skill |
| コンテキスト隔離で side task | Subagent |
| 常時ロードの指示 | CLAUDE.md |
設定ファイル全体比較は AGENTS.md vs CLAUDE.md vs .cursor/rules を、subagent 詳細は Claude Code Subagents 完全リファレンス を参照。
よくある落とし穴
- policy 強制に
exit 1を使う。Claude Code は non-blocking として扱い、危険な action は通過する。block には必ずexit 2。 - decision schema の混同。ほとんどの event は top-level
decisionだが、PreToolUseはhookSpecificOutput.permissionDecision。間違ったフィールドを書いてもblockが発火しない。 chmod +x忘れ。hook コマンド silent fail(exit code は存在しないも同然)。$HOOK_LOGを仕込んでログで確認。- matcher 大文字小文字ミス。
Edit|Write|multiEdit(小文字m)はMultiEditにマッチしない。正確にEdit|Write|MultiEdit。 mcp_toolをセキュリティ強制に使う。MCP server 切断時に non-blocking で通過。ハードpolicy にはcommandhook を。disableAllHooksで managed hook を無効化できない。組織配備の managed policy hook は user/project/local のdisableAllHooks: trueでも生き残る。managed level のdisableAllHooksだけが無効化可能。- HTTP hook タイムアウト。デフォルト 600秒。Webhook ハンドラが遅いと agent loop が止まる。非クリティカルなら
async: true。
FAQ
Claude Code hooks の exit code 最重要ルールは?
exit 2 がブロック、それ以外の非ゼロは non-blocking。実装バグの最大の原因。exit 1(Unix の失敗慣習)を反射で書くが、Claude Code が policy 強制で尊重するのは exit 2 のみ。
Claude Code は何種類の hook event をサポート?
2026年5月(v2.1.141+)時点で 27 distinct events。詳細は本文の「27 Hook Events 全カタログ」セクション。
PreToolUse と PostToolUse の違いは?
PreToolUse はツール実行 前 に発火、exit 2 で ブロック可。PostToolUse はツール成功 後 に発火、観測専用 — 起きたことは undo できないが、反応(フォーマット、ログ、通知)はできる。
危険な Bash コマンドをブロックする hook はどう書く?
PreToolUse hook + matcher: "Bash"。stdin JSON 読み、tool_input.command 抽出、deny パターンチェック、マッチしたら stderr メッセージ + exit 2。exit 1 ではない。本文「Pattern 1」参照。
hook はシェルスクリプトの代わりに HTTP エンドポイント呼べる?
呼べる — type: "http"。URL に JSON POST、2xx レスポンスを hook output として parse。中央ポリシーサービスや Slack/Discord 通知に。ただし非2xx は non-blocking なので、ネットワーク失敗下で hard policy 強制が必要なら HTTP hook は不向き。
hookSpecificOutput とは?
一部の event は decision output を top-level decision ではなく nested hookSpecificOutput オブジェクトで期待する。PreToolUse は hookSpecificOutput.permissionDecision、PermissionRequest は hookSpecificOutput.decision。それ以外のほとんどの event はフラットな top-level decision。
hook はチームメンバー間で共有される?
ロケーション次第。.claude/settings.json(project、commit)は 共有。~/.claude/settings.json(user、machine-local)は 個人。.claude/settings.local.json(gitignored)は プロジェクト別個人。Plugin hook は plugin に同梱。Managed policy hook は admin 配備で組織全体。
hook で Claude を完全に停止できる?
できる — JSON output で {"continue": false, "stopReason": "..."} を返す。指定したメッセージでセッション停止。慎重に使う — credentials 漏洩、build 致命的破壊などの catastrophic 状態に予約。
動かない hook をどうデバッグする?
3つ確認: (1) chmod +x。(2) matcher regex が tool 名と完全一致(大文字小文字、MultiEdit vs multiEdit)。(3) スクリプト先頭にデバッグログ:echo "[$(date)] hook called" >> ~/.claude/hook-debug.log。ログ空なら hook が invoke されていない → /hooks メニューで登録確認。
Async hook のタイムアウトは?
デフォルト 600秒(10分)。"timeout": 60 でハンドラ設定オーバーライド可。async: true の場合、タイムアウトは総実行時間に適用 — agent loop は待たずに継続。asyncRewake: true を付けると async hook が exit 2 で後から Claude を wake できる。
関連記事
- Claude Code Subagents 完全リファレンス 2026
- AGENTS.md vs CLAUDE.md vs .cursor/rules 3方比較
- Career-Ops 完全リファレンス 2026
- Career-Opsの内部構造:14モードSkillsアーキテクチャ
- 2026年インストールすべきClaude Code Skillsベスト15
- 165以上のAIコーディングルール実例集