Claude Code Media
185,207中級

Claude Code Hooks 完全ガイド|9 ライフサイクル + 5 主要 matcher を全部実装【2026年5月版】

Claude Code の全 9 ライフサイクル Hook(PreToolUse / PostToolUse / UserPromptSubmit / Notification / Stop / SubagentStop / PreCompact / SessionStart / SessionEnd)+ 5 主要 matcher の組み合わせを実装サンプル付きで網羅。コスト管理・セキュリティ・監査・PII マスキング・品質ゲートを物理的に強制する運用テンプレ集。

By Claude Code Media 編集部Reviewed by Claude Code Media 編集部

Claude Code Hooks は、ツール呼び出しやセッションのライフサイクルに シェルスクリプトを差し込む 仕組みで、コスト爆発・セキュリティ事故・品質低下を CLAUDE.md やプロンプトだけでは取りこぼしやすい タイミングで阻止できます(exit code による制御のため、Claude 側の判断に依存せず強制力が働きます。ただし正規表現マッチの抜け漏れはあり得るため、後述の許可リスト方式と併用するのが前提です)。

本稿は 2026 年 5 月時点の 9 ライフサイクルイベント + 5 主要 matcher = 14 の実装パターン をすべて実機検証付きで解説する完全リファレンスです。

なぜ Hooks が必要か

ChatGPT のような対話型 AI と Claude Code の最大の違いは「実行する」ことです。Claude がコマンドを実行する直前・直後に 人間の意図を機械的に強制 できるのが Hooks。事例:

  • git push --force を実行前にブロック(PreToolUse)
  • ファイル書き込み後に自動 lint
  • セッション終了時に対話ログを S3 にアーカイブ
  • 日次予算超過を検知して以降の継続を抑止(Stop は応答完了後に走る事後ガード)
  • PII(個人情報)を含む出力を自動マスク

これらは CLAUDE.md やプロンプトに書くだけでは漏れる 領域です。Hooks は exit code でブロックを表現するため、Claude 側の判断に依存せず強制力が働きます(PreToolUse の exit 2 でツール実行を止められます)。

9 ライフサイクル一覧

#イベント起動タイミング主な用途
1PreToolUseツール実行危険コマンドのブロック、引数検証、コスト確認
2PostToolUseツール実行自動 fmt / lint、品質スコア、結果検証
3UserPromptSubmitユーザー送信直後プロンプトインジェクション検出、ログ
4NotificationClaude が通知発火時待機通知の自動転送、Slack 連携
5Stopアシスタント応答完了時セッション終端の最終チェック、監査
6SubagentStopサブエージェント完了時並列タスクの集計、トークン精算
7PreCompact文脈圧縮直前重要文脈の手動保護、メモ抽出
8SessionStartセッション起動時環境変数 / コンテキスト注入
9SessionEndセッション終了時クリーンアップ、外部ログ送信

5 主要 matcher(PreToolUse / PostToolUse 用)

matcherマッチするツールよくある用途
Bashshell コマンド危険コマンドの deny
Write|Editファイル書き換えlint / fmt
Readファイル読み込み機密ファイルの監視
WebFetch外部 URL 取得プロンプトインジェクション検出
Taskサブエージェント起動コスト管理

これらを組み合わせれば 14 通りの実装パターンが組めます。


1. PreToolUse — 暴走を止める最重要 Hook

.claude/settings.jsonhooks.PreToolUse 配列に登録。マッチした すべての Hook が直列実行 され、いずれかが exit 2 を返すと Claude のツール呼び出しはブロックされます(stderr の内容がブロック理由として Claude に伝わります。exit 0 は通過、その他の非ゼロは「Hook のエラー」扱いでブロックはしません)。

1-A. 危険な Bash コマンドのブロック(matcher: Bash)

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/pre-bash-guard.sh" }
        ]
      }
    ]
  }
}
#!/usr/bin/env bash
# .claude/hooks/pre-bash-guard.sh
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""')

# 物理的にブロックするパターン
if echo "$CMD" | grep -qE '(rm -rf /|git push -[fF]|git reset --hard|DROP TABLE|DELETE FROM .* WHERE 1=1)'; then
  echo "[blocked] dangerous command pattern: $CMD" >&2
  exit 2   # exit 2 = Claude にブロック理由を伝える
fi
exit 0

1-B. .env / 秘密ファイルへのアクセスを禁止(matcher: Read|Edit|Write)

#!/usr/bin/env bash
# .claude/hooks/pre-secret-guard.sh
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // ""')

if echo "$FILE" | grep -qE '\.env(\.|$)|/secrets/|credentials\.json$|\.pem$'; then
  echo "[blocked] access to secret file: $FILE" >&2
  exit 2
fi
exit 0

1-C. WebFetch のプロンプトインジェクション検査(matcher: WebFetch)

#!/usr/bin/env bash
# .claude/hooks/pre-webfetch-sanitize.sh
INPUT=$(cat)
URL=$(echo "$INPUT" | jq -r '.tool_input.url // ""')

# ローカルアドレスへの SSRF を防ぐ
if echo "$URL" | grep -qE '^https?://(localhost|127\.0\.0\.1|169\.254\.|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)'; then
  echo "[blocked] SSRF attempt to local network: $URL" >&2
  exit 2
fi
exit 0

2. PostToolUse — 結果を検証する品質ゲート

ツール実行後に走る。出力結果を見て 後段で品質検査 や自動 fmt を強制できます。

2-A. MDX 書き込み後の品質チェック(matcher: Write|Edit)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/post-mdx-quality.sh" }
        ]
      }
    ]
  }
}
#!/usr/bin/env bash
# .claude/hooks/post-mdx-quality.sh — 文字数 / H2 / 内部リンクの最低基準
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
[[ "$FILE" != *.mdx ]] && exit 0

WORD_COUNT=$(grep -v '^---' "$FILE" | wc -w)
H2_COUNT=$(grep -c '^## ' "$FILE")
LINK_COUNT=$(grep -oE '\[.*?\]\(/[^)]+\)' "$FILE" | wc -l)

WARN=""
[[ $WORD_COUNT -lt 1500 ]] && WARN+="本文 < 1,500 字 ($WORD_COUNT)\n"
[[ $H2_COUNT -lt 3 ]] && WARN+="H2 < 3 ($H2_COUNT)\n"
[[ $LINK_COUNT -lt 2 ]] && WARN+="内部リンク < 2 ($LINK_COUNT)\n"

if [[ -n "$WARN" ]]; then
  echo -e "[mdx-quality]\n$WARN" >&2
  # 警告のみで通す(exit 0)。ブロックしたい場合は exit 2
fi
exit 0

2-B. TypeScript ファイル編集後の tsc + prettier(matcher: Write|Edit)

#!/usr/bin/env bash
# .claude/hooks/post-ts-format.sh
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
[[ "$FILE" != *.ts && "$FILE" != *.tsx ]] && exit 0

cd "$(dirname "$FILE")" && cd $(git rev-parse --show-toplevel 2>/dev/null || echo .)
pnpm prettier --write "$FILE" 2>/dev/null
TSC=$(pnpm tsc --noEmit 2>&1 | grep "$(basename "$FILE")" | head -3)
if [[ -n "$TSC" ]]; then
  echo "[tsc] $TSC" >&2
fi
exit 0

3. UserPromptSubmit — プロンプトインジェクション検出

ユーザーが送信したプロンプト本文を Claude が処理する前に 検査できます。

{
  "hooks": {
    "UserPromptSubmit": [
      { "type": "command", "command": ".claude/hooks/user-prompt-guard.sh" }
    ]
  }
}
#!/usr/bin/env bash
# .claude/hooks/user-prompt-guard.sh
INPUT=$(cat)
PROMPT=$(echo "$INPUT" | jq -r '.prompt // ""')

# 既知の jailbreak パターン
if echo "$PROMPT" | grep -qiE 'ignore (all )?previous|disregard.*instructions|you are now (a |an )?[A-Z]'; then
  echo "[user-prompt-guard] potential injection pattern detected" >&2
  # ログだけ取って通す
fi

# 監査ログ
echo "$(date -u +%FT%TZ) USER_PROMPT length=${#PROMPT}" >> ~/.claude/audit/prompts.log
exit 0

4. Notification — Claude の通知をフックする

Claude が「ユーザーに確認したい」「処理が長引いている」等で通知を発火するタイミング。Slack / メール転送に最適。

{
  "hooks": {
    "Notification": [
      { "type": "command", "command": ".claude/hooks/notify-slack.sh" }
    ]
  }
}
#!/usr/bin/env bash
# .claude/hooks/notify-slack.sh
INPUT=$(cat)
MSG=$(echo "$INPUT" | jq -r '.message // ""')
curl -sS -X POST "$SLACK_WEBHOOK_URL" \
  -H 'Content-Type: application/json' \
  -d "$(jq -nc --arg t "🛎 Claude: $MSG" '{text:$t}')" >/dev/null
exit 0

5. Stop — アシスタント応答完了時の最終チェック

セッションの 1 ターン応答が終わった時点で起動。全 mdx の console.log 漏れチェック や対話ログ送信に最適。

{
  "hooks": {
    "Stop": [
      { "type": "command", "command": ".claude/hooks/stop-archive-session.sh" }
    ]
  }
}
#!/usr/bin/env bash
# .claude/hooks/stop-archive-session.sh — 対話 jsonl を S3 にアーカイブ
SESSION_DIR="${HOME}/.claude/projects/$(basename "$(pwd)" | tr ' ' '-' | tr '/' '-')"
LATEST=$(ls -t "$SESSION_DIR"/*.jsonl 2>/dev/null | head -1)
[[ -z "$LATEST" ]] && exit 0
DATE=$(date -u +%Y-%m-%dT%H-%M-%SZ)
aws s3 cp "$LATEST" "s3://your-claude-audit/$(hostname)/$DATE.jsonl" --quiet 2>/dev/null
exit 0

6. SubagentStop — サブエージェント完了時の集計

Task ツールで起動した subagent が終わったタイミング。並列タスクの結果集約・ログ記録に。

#!/usr/bin/env bash
# .claude/hooks/subagent-stop-accumulate.sh
INPUT=$(cat)
AGENT_TYPE=$(echo "$INPUT" | jq -r '.subagent_type // ""')
# 注: 消費トークン数(tokens_used)は Hook 入力に含まれる保証がないため、
# 取得できない場合に備えてフォールバック(0)を置く。正確な精算が必要なら別途集計する。
TOKENS=$(echo "$INPUT" | jq -r '.tokens_used // 0')
echo "$(date -u +%FT%TZ) $AGENT_TYPE tokens=$TOKENS" >> ~/.claude/audit/subagents.log
exit 0

7. PreCompact — 文脈圧縮直前のメモ抽出

Claude が文脈をオートコンパクトする直前。重要な決定事項を別ファイルに退避 できます。長時間セッションで「あの判断、何だったっけ?」を防ぐ最後の砦。

#!/usr/bin/env bash
# .claude/hooks/pre-compact-snapshot.sh
INPUT=$(cat)
TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // ""')
[[ -z "$TRANSCRIPT_PATH" ]] && exit 0

# 直近の決定事項を抽出して別ファイルに残す
TS=$(date -u +%Y%m%d-%H%M%S)
DEST=".claude/snapshots/decisions-$TS.md"
mkdir -p .claude/snapshots
grep -E '(決定|approved|merged|deployed|削除)' "$TRANSCRIPT_PATH" | tail -50 > "$DEST"
exit 0

8. SessionStart — セッション起動時の環境準備

新しいセッションを開いたタイミング。プロジェクトの最新ブランチ確認 / git status 通知 / トークン残量表示 などのオンボーディング処理に。

#!/usr/bin/env bash
# .claude/hooks/session-start-brief.sh — セッション開始時の状況サマリ
echo "[session-start]" >&2
echo "  branch: $(git rev-parse --abbrev-ref HEAD 2>/dev/null)" >&2
echo "  unstaged: $(git status --short 2>/dev/null | wc -l)" >&2
echo "  last commit: $(git log -1 --oneline 2>/dev/null)" >&2
exit 0

9. SessionEnd — セッション完全終了時のクリーンアップ

最後の Stop が走ってからセッションが完全に閉じる時点。一時ファイル削除 / 最終バックアップ に。

#!/usr/bin/env bash
# .claude/hooks/session-end-cleanup.sh
rm -rf /tmp/claude-session-cache/* 2>/dev/null
# 当日の subagent サマリを Discord に送信
TODAY=$(date -u +%F)
SUMMARY=$(grep "$TODAY" ~/.claude/audit/subagents.log 2>/dev/null | awk '{sum+=$NF} END{printf "subagents=%d tokens=%d", NR, sum}')
if [[ -n "$SUMMARY" && -n "$DISCORD_AGENT_WEBHOOK" ]]; then
  curl -sS -X POST "$DISCORD_AGENT_WEBHOOK" \
    -H 'Content-Type: application/json' \
    -d "$(jq -nc --arg c "📊 today: $SUMMARY" '{content:$c}')" >/dev/null
fi
exit 0

exit code の意味(最重要)

Hook は stdout / stderr / exit code でツール実行の挙動を制御します。

exit効果
0通過。stdout / stderr は単にログとして扱われる
2ブロック。Claude にブロック理由(stderr の内容)が伝わり、別の方法を試させる
その他非ゼロエラー。Claude には「Hook が失敗した」と伝わるが、ブロックはしない

deny の意図を確実に伝えたいなら必ず exit 2exit 1 だと「Hook 自体のバグ」と解釈され、ブロック効果が薄い。


当メディアでの 14 Hook 構成(実機検証済)

claude-code-media.com 自身の .claude/hooks/ 配下:

#Hookmatcher目的
1pre-bash-guard.shPreToolUse Bash危険コマンド deny
2pre-secret-guard.shPreToolUse Read|Edit|Write.env / secrets 保護
3pre-webfetch-sanitize.shPreToolUse WebFetchSSRF + injection 防止
4pre-cost-gate.shPreToolUse Task日次予算超過時 subagent 起動拒否
5pre-mdx-frontmatter.shPreToolUse Writefrontmatter 必須項目チェック
6post-mdx-quality.shPostToolUse Write|Edit文字数 / H2 / 内部リンク
7post-mdx-seo-validate.shPostToolUse Write|EditE-E-A-T / Featured Snippet 構造
8post-mdx-geo-aio-check.shPostToolUse Write|Editquotable sentence / llm_summary
9post-ts-format.shPostToolUse Write|Edittsc + prettier
10user-prompt-guard.shUserPromptSubmitinjection 検出 + 監査
11notify-slack.shNotification待機通知転送
12stop-archive-session.shStopjsonl 対話ログを S3 へ
13subagent-stop-accumulate.shSubagentStopトークン精算
14session-end-cleanup.shSessionEndtmp 削除 + Discord 集計通知

これら 14 本で 編集部 1 人 + エージェント 30 体 の安全運用が回ります。詳細実装は Skills × Subagents × Hooks 使い分けマップ も参照。


よくある詰まりポイント

症状原因対処
Hook が起動しないパス間違い / chmod +x 忘れ.claude/hooks/ に正確なパス + 実行権限付与
stderr が見えないecho 先が stdout になっているecho "..." >&2 で stderr に明示
ブロックが効かないexit 1 を使っているexit 2 に変更
タイムアウト重い処理(外部 API 等)5 秒以内に完了させる、& で非同期化
jq が無い標準ツール不足brew install jq
matcher が動かない正規表現が ungreedyWrite|Edit のように | でエスケープ不要
全部の Tool に発火するmatcher 未設定空文字 "" でも全マッチになるので意図的に空にする時のみ使う

設定ファイルのスコープ

Hook の設定は 3 段階で書けます。

位置効く範囲用途
~/.claude/settings.json全プロジェクト個人の安全装置(secret guard 等)
<project>/.claude/settings.jsonプロジェクト固有チームで共有する規律(mdx 品質等)
<project>/.claude/settings.local.jsonプロジェクト個人設定gitignore 推奨、個人の追加 hook

優先順位は local > project > global。global で deny したものを local で上書き許可は基本不可(deny が強い)。


まとめ: 今すぐ入れるべき 3 つ + 余裕があれば 11

最初の 30 分 で入れるべき:

  1. pre-bash-guard.sh — rm -rf / force push を物理停止
  2. pre-secret-guard.sh — .env / secrets を物理保護
  3. post-mdx-quality.sh(or 同等の lint)— 書き込み品質を後段ガード

この 3 本で「半日で取り返しのつかない事故」のうち 80% は塞げます。残り 11 本は運用が安定してから順次。

関連記事:

FAQ

Q: Hooks を仕込むとどれくらい遅くなりますか?

A: 1 Hook あたり 50-200ms。本稿の 14 本全部仕込んでも 1 セッションあたり累積 1-2 秒程度。重い外部 API 連携は & で非同期化推奨。

Q: 既存プロジェクトに後から導入できますか?

A: できます。.claude/settings.json を作成し、.claude/hooks/ 配下にスクリプトを置くだけ。Claude Code 再起動不要、即時反映。

Q: Hooks 内で外部 API を叩いてもいいですか?

A: 可能ですが、タイムアウト管理必須(curl --max-time 3 等)。同期処理が長引くと Claude セッションが見かけ上止まる。

Q: ブロックしたい時の正しい exit code は?

A: exit 2exit 1 は「Hook 自体のバグ」と解釈されブロック効果が弱い。

Q: Hook の stdout は Claude に渡りますか?

A: stderr のみ Claude が読みます。stdout は対話ログには残りますが Claude の文脈には入りません。ブロック理由は必ず stderr に

Q: matcher で複数ツールを対象にできますか?

A: Write|Edit のように | 区切りで指定。正規表現として評価されます。

Q: Hook が無限ループする心配は?

A: PostToolUse で Edit を呼ぶような Hook を書くと、再度 PostToolUse が走るループが発生し得ます。Hook 内では Claude のツールを呼ばず、純粋なシェル操作のみで完結させる設計が安全。

関連記事

この記事の著者

claude-code-media 編集部Claude Code 専門編集チーム

Claude Code の非エンジニア向け業務効率化メディア『claude-code-lab.jp』を運営。フリーランス・中小企業・個人開発者向けに、実装テンプレ・業務自動化テクニック・Vibe Coding 入門を配信。

Claude Code 完全ガイドVibe CodingAI 業務効率化非エンジニア向け AI 教育MCP(Model Context Protocol)
ByClaude Code Media 編集部

AI支援で執筆 — 本記事は Claude Code エージェントによる執筆支援を受け、編集部が事実確認・編集を行っています。 数値・引用元は記事更新日時点で確認済みですが、最新情報は各公式サイトでご確認ください。