フックガイド
Claude Code フックの基礎から実践まで: 終了コード、JSON出力、非同期コマンド、HTTPエンドポイント、PreToolUseとPostToolUseのマッチャー、本番環境パターン。
設定をやめて、構築を始めよう。
AIオーケストレーション付きSaaSビルダーテンプレート。
問題点: ファイルの書き込みを一件一件手動で承認する。次はコマンド。次はフォーマット処理。20回の中断が積み重なると、作っていた機能の全体像が頭から抜けていく。
すぐに使えるコード: これを .claude/settings.json に追加するだけで、Prettier のフォーマットを二度と手動承認しなくて済む:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\""
}
]
}
]
}
}Claude が書いたファイルはすべて自動フォーマットされる。クリック不要。コンテキストスイッチも不要。
12のフックライフサイクルイベント
Claude Code のすべてのイベントにフックを設定できる。フックはそのイベントが発生したとき、シェルコマンドを実行するか、LLM プロンプトを送信する。完全な一覧:
| フック | 発火タイミング | ブロック可否 | 主な用途 |
|---|---|---|---|
| SessionStart | セッション開始または再開時 | NO | コンテキスト読み込み、環境変数の設定 |
| UserPromptSubmit | Enter キーを押したとき | YES | コンテキスト注入、バリデーション |
| PreToolUse | ツール実行前 | YES | セキュリティブロック、自動承認(パーミッションシステムを拡張) |
| PermissionRequest | パーミッションダイアログ表示時 | YES | 自動承認/拒否 |
| PostToolUse | ツール成功後 | NO* | 自動フォーマット、リント、ログ |
| PostToolUseFailure | ツール失敗後 | NO | エラーハンドリング |
| SubagentStart | サブエージェント起動時 | NO | サブエージェントの初期化 |
| SubagentStop | サブエージェント終了時 | YES | サブエージェントのバリデーション |
| Stop | Claude が応答を終えたとき | YES | タスク完了の強制 |
| PreCompact | コンパクション前 | NO | トランスクリプトのバックアップ |
| Setup | --init/--maintenance 使用時 | NO | 初回セットアップ |
| SessionEnd | セッション終了時 | NO | クリーンアップ、ログ |
| Notification | Claude が通知を送るとき | NO | デスクトップ通知、TTS |
*PostToolUse フックは Claude にメッセージをフィードバックできるが、すでに実行済みのツールを取り消すことはできない。
終了コード: 制御の仕組み
終了コードはフックが Claude と通信する手段:
| 終了コード | 動作 |
|---|---|
| 0 | 成功 - フックが実行され、stdout が JSON として処理される |
| 2 | ブロック - 操作が停止され、stderr が Claude に送られる |
| その他 | エラー - stderr がユーザーに表示され、実行は継続される |
物事をブロックするのは終了コード 2 だ。 これを PreToolUse フックから返せばツールは実行されない。Stop フックから返せば Claude は終了する代わりに処理を続けなければならなくなる。
フックの種類: Command、HTTP、Prompt、Agent
4種類のハンドラーがある。用途に合ったものを選ぶ。
Command フックはシェルスクリプトを実行する:
{
"type": "command",
"command": "python validator.py",
"timeout": 30
}HTTP フックはエンドポイントに POST して JSON を受け取る: 新機能 - 2026年2月
{
"type": "http",
"url": "http://localhost:8080/hooks/pre-tool-use",
"timeout": 30,
"headers": {
"Authorization": "Bearer $MY_TOKEN"
},
"allowedEnvVars": ["MY_TOKEN"]
}Claude Code はイベント JSON をリクエストボディ(Content-Type: application/json)として POST し、レスポンスは Command フックと同じ JSON 出力スキーマに従う。いくつか異なる点がある:
- 非ブロックエラー: 2xx 以外のレスポンス、接続失敗、タイムアウトは実行を止めない。ツール呼び出しをブロックするには、JSON ボディに
decision: "block"を含む 2xx レスポンスを返す必要がある。 - ヘッダーの環境変数: ヘッダー値内で
$VAR_NAMEまたは${VAR_NAME}として変数を参照できる。allowedEnvVarsに列挙された名前のみ解決される。それ以外は空文字列になる。 - 重複排除: HTTP フックは URL で重複排除する。Command フックはコマンド文字列で重複排除する。
- 設定のみ: 設定 JSON を直接編集する必要がある。
/hooksの対話メニューは Command フックのみサポートしている。
Prompt フックは LLM による評価を使う(Stop や SubagentStop に向いている):
{
"type": "prompt",
"prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks complete.",
"timeout": 30
}LLM は {"ok": true} または {"ok": false, "reason": "..."} で返答する。
Agent フックはファイルを読める(Read、Grep、Glob)サブエージェントを起動してより深いチェックを行う:
{
"type": "agent",
"prompt": "Verify all test files have corresponding implementation files",
"timeout": 60
}Agent フックはコードベースを調べてから判断する。Prompt フックより徹底的だが、その分遅い(デフォルトタイムアウト: Prompt の 30 秒に対して 60 秒)。
非同期フック(ノンブロッキング)新機能 - 2026年1月
async: true を設定すると、フックは Claude をブロックせずバックグラウンドで実行される。Anthropic は 2026 年 1 月にこれを提供した:
{
"type": "command",
"command": "node backup-script.js",
"async": true,
"timeout": 30
}最適な用途:
- ログとアナリティクス
- バックアップ作成(PreCompact)
- 通知
- 速度を落とすべきでないサイドエフェクト全般
不向きな用途:
- セキュリティブロック(終了コード 2 を使う PreToolUse)
- 自動承認の判断(PermissionRequest)
- Claude が結果を必要とするフック全般
HTTP フック: エンドポイントへの POST 新機能 - 2026年2月
HTTP フックを自分が所有する Web URL に向けると、イベントがローカルスクリプトではなくそこに流れる。これにより Command フックでは対応しにくかったパターンが使えるようになる:
- チーム全体のポリシーを適用するリモートバリデーションサービス
- 共有監査システムへの集中ログ
- Slack、PagerDuty、カスタムダッシュボードとのWebhook 連携
- フックロジックが API と並行して動くマイクロサービスアーキテクチャ
基本的な HTTP フック
すべての Bash コマンドをバリデーションエンドポイント経由でルーティングする:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "http",
"url": "http://localhost:8080/hooks/pre-tool-use",
"timeout": 30,
"headers": {
"Authorization": "Bearer $MY_TOKEN"
},
"allowedEnvVars": ["MY_TOKEN"]
}
]
}
]
}
}サーバーに届くのは Command フックが stdin で受け取るのと同じペイロードで、POST ボディとして送られる。JSON 出力フォーマットで返答すれば Claude Code が同じように解析する。
HTTP レスポンスの処理
HTTP フックのレスポンスの読み方は異なる。終了コードはなく、HTTP ステータスとレスポンスボディのみ:
| レスポンス | 動作 |
|---|---|
| 2xx + 空のボディ | 成功、出力なしの終了コード 0 と同等 |
| 2xx + プレーンテキストボディ | 成功、テキストが Claude のコンテキストに追加される |
| 2xx + JSON ボディ | 成功、Command フックと同じ JSON 出力スキーマで解析される |
| 2xx 以外のステータス | 非ブロックエラー、実行は継続される |
| 接続失敗 / タイムアウト | 非ブロックエラー、実行は継続される |
注意点: HTTP フックはステータスコードだけでブロックできない。4xx や 5xx を返すとエラーがログに残るが実行は続く。実際にブロックしたり、パーミッションを拒否したりするには、JSON ボディに決定フィールドを含む 2xx レスポンス が必要:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Blocked by security policy"
}
}セキュアなヘッダー認証
headers フィールドは環境変数を展開できるが、allowedEnvVars に変数名が含まれている場合のみ。これにより秘密情報の意図しない漏洩を防ぐ:
{
"type": "http",
"url": "https://hooks.example.com/validate",
"headers": {
"Authorization": "Bearer $API_KEY",
"X-Team-Id": "$TEAM_ID"
},
"allowedEnvVars": ["API_KEY", "TEAM_ID"]
}許可リストにない $VAR は警告もエラーも出さずに空文字列になる。本番環境に出す前に許可リストを二度確認すること。
JSON 出力: 高度な制御
終了コードでは粒度が粗い場合、フックは代わりに構造化された JSON レスポンスを返せる。
PreToolUse の判断
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "Safe read operation",
"updatedInput": { "command": "modified-command" },
"additionalContext": "Context for Claude"
}
}"allow": パーミッションシステムをバイパスする"deny": ツールをブロックし、理由を Claude に伝える"ask": ユーザーに確認を求めるupdatedInput: 実行前にツールのパラメーターを変更する
PermissionRequest の判断
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow",
"updatedInput": { "command": "npm run lint" }
}
}
}Stop/SubagentStop の強制
{
"decision": "block",
"reason": "Tests failing. Fix them before completing."
}フック 1: 保存時の自動フォーマット
ファイル書き込みのたびにフォーマッターを実行する:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\""
},
{
"type": "command",
"command": "npx eslint --fix \"$CLAUDE_TOOL_INPUT_FILE_PATH\""
}
]
}
]
}
}両方のフックは並行して実行される。フォーマットとリントが両方完了してから Claude の返答が表示される。
フック 2: セッションコンテキストの注入
セッション開始時にコンテキストを読み込む:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo '## Git Status' && git status --short && echo '## TODOs' && grep -r 'TODO:' src/ | head -5"
}
]
}
]
}
}環境変数の永続化
SessionStart または Setup フックは、セッション全体で有効な環境変数を定義できる:
#!/bin/bash
if [ -n "$CLAUDE_ENV_FILE" ]; then
echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
echo 'export API_KEY=your-key' >> "$CLAUDE_ENV_FILE"
fi
exit 0フック 3: セキュリティブロック
PreToolUse で危険な操作を止める:
#!/usr/bin/env python3
import json
import sys
import re
DANGEROUS_PATTERNS = [
r'\brm\s+.*-[a-z]*r[a-z]*f',
r'sudo\s+rm',
r'chmod\s+777',
r'git\s+push\s+--force.*main',
]
input_data = json.load(sys.stdin)
if input_data.get('tool_name') == 'Bash':
command = input_data.get('tool_input', {}).get('command', '')
for pattern in DANGEROUS_PATTERNS:
if re.search(pattern, command, re.IGNORECASE):
print("BLOCKED: Dangerous pattern", file=sys.stderr)
sys.exit(2)
sys.exit(0)フック 4: 安全なコマンドの自動承認
PermissionRequest を使って、安全とわかっているコマンドのプロンプトをスキップする:
{
"hooks": {
"PermissionRequest": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python .claude/hooks/auto-approve.py"
}
]
}
]
}
}#!/usr/bin/env python3
import json
import sys
SAFE_PREFIXES = ['npm test', 'npm run lint', 'git status', 'ls']
input_data = json.load(sys.stdin)
command = input_data.get('tool_input', {}).get('command', '')
for prefix in SAFE_PREFIXES:
if command.startswith(prefix):
output = {
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {"behavior": "allow"}
}
}
print(json.dumps(output))
sys.exit(0)
sys.exit(0) # Normal flow for other commandsフック 5: トランスクリプトのバックアップ
PreCompact はトランスクリプトのスナップショットを取るタイミングだ。バックアップは Claude をブロックする必要がないので async: true を付ける:
{
"hooks": {
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "python .claude/hooks/backup-transcript.py",
"async": true
}
]
}
]
}
}マッチャー manual と auto で /compact コマンドと自動コンパクションを区別できる。
フック 6: タスク完了の強制
Stop フックで Claude が早まって完了を宣言するのを防ぐ:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Check if all tasks are complete: $ARGUMENTS. Return {\"ok\": false, \"reason\": \"...\"} if work remains."
}
]
}
]
}
}Stop Hook ガイドにはこのパターンのコマンドベース版が掲載されている。
フック 7: スキルの起動
スキル起動フックは Claude が見る前にプロンプトに割り込み、スキルの推奨事項を末尾に追加する。入力した各キーワードはルールセットと照合されるため、スロークエリに言及した瞬間に Postgres チューニングスキルがコンテキストに入る。Code Kit の SkillActivationHook はすでに 21 種類のスキルカテゴリでこのパターンを使っている:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/SkillActivationHook/skill-activation-prompt.mjs"
}
]
}
]
}
}フック 8: StatusLine を使ったしきい値ベースのバックアップ
StatusLine はライブのコンテキストメトリクスが取得できる唯一の場所だ。そのため、しきい値に達したときにバックアップを開始するのに最適な場所となる:
{
"statusLine": {
"type": "command",
"command": "node .claude/hooks/ContextRecoveryHook/statusline-monitor.mjs"
}
}重要: remaining_percentage フィールドにはすでに固定の 33K トークンの自動コンパクトバッファが含まれている。「自動コンパクトまでの実際の空き容量」を求めるには自分で計算する必要がある:
const AUTOCOMPACT_BUFFER_TOKENS = 33000;
const autocompactBufferPct = (AUTOCOMPACT_BUFFER_TOKENS / windowSize) * 100;
const freeUntilCompact = Math.max(0, pctRemainTotal - autocompactBufferPct);バックアップスクリプト内では 2 つのトリガーシステムが並行して動く。トークンが主要なトリガーで、パーセンテージはセーフティネットとして機能する:
// Token-based triggers (primary - works across all window sizes)
const TOKEN_FIRST_BACKUP = 50000; // First backup at 50k tokens used
const TOKEN_UPDATE_INTERVAL = 10000; // Update every 10k tokens after
if (currentTotalTokens >= TOKEN_FIRST_BACKUP) {
if (lastBackupTokens < TOKEN_FIRST_BACKUP) {
runBackup(
sessionId,
`tokens_${Math.round(currentTotalTokens / 1000)}k_first`,
);
} else if (currentTotalTokens - lastBackupTokens >= TOKEN_UPDATE_INTERVAL) {
runBackup(
sessionId,
`tokens_${Math.round(currentTotalTokens / 1000)}k_update`,
);
}
}
// Percentage-based triggers (safety net, especially for 200k windows)
const THRESHOLDS = [30, 15, 5];
for (const threshold of THRESHOLDS) {
if (state.lastFree > threshold && freeUntilCompact <= threshold) {
runBackup(sessionId, `crossed_${threshold}pct`);
}
}
if (freeUntilCompact < 5 && freeUntilCompact < state.lastFree) {
runBackup(sessionId, "continuous");
}PreCompact はコンパクションの瞬間にのみ発火する。StatusLine ベースのバックアップは、何も壊れていないうちにセッションをスナップショットしておく。1M のような大きなウィンドウではパーセンテージトリガーでは手遅れになるため、トークントリガーが最も重要だ。
アーキテクチャ: 3ファイル構成
バックアップシステムは役割ごとに 3 つのファイルに分かれている:
.claude/hooks/ContextRecoveryHook/
├── backup-core.mjs # Shared backup logic (parsing, formatting, saving)
├── statusline-monitor.mjs # Threshold detection + display (calls backup-core)
└── conv-backup.mjs # PreCompact trigger (calls backup-core)| ファイル | トリガー | 責務 |
|---|---|---|
backup-core.mjs | 他から呼ばれる | トランスクリプトの解析、Markdown フォーマット、ファイル保存、状態更新 |
statusline-monitor.mjs | StatusLine(継続的) | トークン/コンテキスト % の監視、トリガー検知、ステータス表示 |
conv-backup.mjs | PreCompact フック | コンパクション前イベントの処理 |
実際のバックアップ処理は 1 つのファイルが担う。Markdown のレンダリング方法、ファイル名の付け方、状態の記録方法を backup-core.mjs で調整すれば、呼び出し元の両方に変更が反映される。
バックアップファイルの命名
バックアップはタイムスタンプ付きの連番ファイル名を使うため、履歴を確認しやすい:
.claude/backups/1-backup-26th-Jan-2026-4-30pm.md
.claude/backups/2-backup-26th-Jan-2026-5-15pm.md
.claude/backups/3-backup-26th-Jan-2026-5-45pm.mdStatusLine の表示
現在のセッションのバックアップが存在する場合、ステータスラインにそのパスが表示される:
[!] 25.0% free (50.0K/200K)
-> .claude/backups/3-backup-26th-Jan-2026-5-45pm.mdコンパクションが実行された後にどのファイルを読み込めばよいかが一目でわかる。
状態のトラッキング
両方のフックは ~/.claude/claudefast-statusline-state.json にある状態ファイルを共有する:
{
"sessionId": "abc123",
"lastFreeUntilCompact": 25.5,
"currentBackupPath": ".claude/backups/3-backup-26th-Jan-2026-5-45pm.md"
}推奨ワークフロー: コンパクションが発火したら、クリーンな状態のために /clear を実行し、ステータスラインに表示されたバックアップパスを読み込む。そうしないと、自動生成されたサマリーと復元したノートが互いに干渉してしまう。
フック 9: インストールとメンテナンスのための Setup フック
Setup フックはセッション開始前に、特別な CLI フラグで起動されたときのみ実行される:
claude --init # Triggers Setup hook with matcher "init"
claude --init-only # Same as above, but exits after hook (CI-friendly)
claude --maintenance # Triggers Setup hook with matcher "maintenance"settings.json でマッチャーを使って接続する:
{
"hooks": {
"Setup": [
{
"matcher": "init",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/setup_init.py",
"timeout": 120
}
]
},
{
"matcher": "maintenance",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/setup_maintenance.py",
"timeout": 60
}
]
}
]
}
}フラグとプロンプトの組み合わせ: フラグ自体にプロンプトを追加できる。
claude --init "/install"
フックが最初に決定論的に実行される。その後 /install コマンドがエージェント的に発火する。1 回の呼び出しで 2 つの実行モードが使える: スクリプトによるセットアップの後に、それを推論するエージェントが続く。
Setup Hooks ガイドでは、Justfile のセットアップやインタラクティブなオンボーディングフローを含むパターン全体を端から端まで解説している。
設定ファイルの場所
| 場所 | スコープ | 優先度 |
|---|---|---|
| マネージドポリシー | エンタープライズ | 最高 |
.claude/settings.json | プロジェクト(共有) | 高 |
.claude/settings.local.json | プロジェクト(個人) | 中 |
~/.claude/settings.json | すべてのプロジェクト | 最低 |
フックの無効化と制限
すべてのフックを無効にする
フックに問題が発生したり、クリーンなベースラインが必要なときは、設定で disableAllHooks を true にする:
{
"disableAllHooks": true
}すべてのスコープのすべてのフックが停止する。ユーザー、プロジェクト、ローカル。暴走したフックのデバッグや、既知のクリーンなベースラインが必要なときに使う。
マネージドフックの制限
集中管理が必要な組織は、マネージド設定で allowManagedHooksOnly を true に設定できる:
{
"allowManagedHooksOnly": true
}有効にすると、Claude Code が実行するフックはマネージド設定で宣言されたフックと SDK フックのみになる。ユーザースコープ、プロジェクトスコープ、プラグインスコープのすべてが無視される。ローカルフックが組織のセキュリティポリシーを回避できなくなる。
この設定は allowManagedPermissionRulesOnly とセットで使うと効果的だ。これはパーミッションルールを同様に制限する。2 つを組み合わせると、管理者がパーミッション自体とそれを拡張するすべてのフックの全体を管理できるようになる。
マッチャーの構文
| パターン | マッチ対象 |
|---|---|
"" または省略 | すべてのツール |
"Bash" | Bash のみ(完全一致、大文字小文字区別あり) |
| `"Write | Edit"` |
"mcp__memory__.*" | すべてのメモリ MCP ツール |
重要: | の前後にスペースを入れないこと。またマッチャー文字列は大文字小文字を区別する。
イベント固有のマッチャー
SessionStart: startup, resume, clear, compactPreCompact: manual, autoSetup: init, maintenanceNotification: permission_prompt, idle_prompt, auth_success
環境変数
| 変数 | 説明 |
|---|---|
CLAUDE_PROJECT_DIR | プロジェクトルート(すべてのフック) |
CLAUDE_ENV_FILE | 環境変数の永続化(SessionStart、Setup) |
CLAUDE_CODE_REMOTE | Web の場合 "true"、CLI の場合は空 |
デバッグ
フックが発火しない?
- マッチャーの構文を確認する(大文字小文字区別あり、スペースなし)
- 設定ファイルの場所を確認する
- テスト:
echo '{"session_id":"test"}' | python your-hook.py
コマンドが失敗する?
- ログを追加する:
command 2>&1 | tee ~/.claude/hook-debug.log - デバッグモードで実行:
claude --debug - 他の OS でフックが動かない?クロスプラットフォームフックパターンを参照
Stop でループが止まらない?
- 必ず最初に
stop_hook_activeフラグを確認すること
まず1つのフックから始める
最も痛いと感じる摩擦点を選ぶ:
- 毎回手でファイルをフォーマットしている?PostToolUse フォーマッター
- 安全なコマンドを一日中承認している?PermissionRequest 自動承認
- セッションがコンテキストなしで始まる?SessionStart 注入
- コンパクションで進捗が失われる?PreCompact バックアップ
- タスクが早まって完了と判断される?Stop 強制
フック 1 つ。摩擦 1 つ消える。そして繰り返す。セットアップを省略したい場合、Code Kit にはすでに 5 つのフックが設定されている: スキル起動、自動フォーマット、コンテキスト回復、パーミッション自動化、コードバリデーション。これらはすべて上記のパターンで作られている。
設定をやめて、構築を始めよう。
AIオーケストレーション付きSaaSビルダーテンプレート。