>

Claude Code Hooks 入門編|CLAUDE.mdだけでは足りない理由と決定論的制御

Claude Code Hooks 入門編|CLAUDE.mdだけでは足りない理由と決定論的制御

要約

Claude Code Hooks は、CLAUDE.md や Skills では実現できない「確実に実行される制御」を提供する仕組みである。CLAUDE.md に書いたルールは LLM の判断に依存するため 100% の遵守が保証されないが、Hooks はシステムレベルで動作し、ツール実行の前後やセッションのライフサイクルに合わせてシェルスクリプトや HTTP リクエストを必ず実行する。

本記事では Hooks の基本概念を理解した上で、異なるパターンを示す3つの実例をコード付きで紹介する。

対象読者: Claude Code を日常的に使っている開発者で、CLAUDE.md だけでは制御しきれない場面に課題を感じている方。

この記事を読むことで得られるメリット

この記事を読むことで以下のことが分かる:

  • Hooks と CLAUDE.md / Skills の根本的な違いが分かる
  • 「いつ Hooks を使うべきか」の判断基準が明確になる
  • settings.json の書き方とイベント・Exit Code の仕組みを理解できる
  • 3つの実例で Stop + agentPreToolUse + commandStop + command のパターンを把握できる
  • フックのデバッグ方法とよくある間違いを把握できる

この記事を読むのにかかる時間

約15分

環境

  • Claude Code 2.0.65+
  • macOS / Linux(シェルスクリプトベース)
  • jq(JSON パーサー)がインストール済みであること

Hooks とは何か — なぜ必要なのか

CLAUDE.md の限界:「お願い」は 100% 守られない

CLAUDE.md はセッション開始時に読み込まれる静的テキストであり、Claude にとっては「参考情報」に過ぎない。LLM が最終的にその指示に従うかどうかは確率的であり、100% の実行保証がない。

実際に報告されている失敗シナリオを挙げる。

  • 長時間プロジェクトで複雑なデバッグチェーンがコンテキストウィンドウの大半を消費した場合、Claude がリンターを実行せずにファイルを書き込む(出典: Dotzlaw Consulting
  • CLAUDE.md に「.env ファイルを絶対に編集するな」と書いていたにもかかわらず、Claude が .env を読み込み、認証情報を env.example にコピーして GitHub にコミットした(出典: paddo.dev
  • CLAUDE.md に「pnpm を使って」と指定しても、コンテキストの圧縮後に npm install を実行してしまう

これらの問題の根本原因は、CLAUDE.md の指示がコンテキスト圧縮(Compaction)時に要約・希薄化されることにある。長いセッションほどこのリスクは高まる。

Hooks の決定論的実行保証

公式ドキュメントでは、Hooks を「LLM に実行を選択させるのではなく、確実に特定のアクションが常に発生することを保証する決定論的制御を提供する」と定義している。

Hooks は LLM の推論チェーンの外、システムレベルで実行される。Claude がどれだけ「実行したい」と判断しても、Hooks による制御は回避できない。

4つのハンドラータイプ

Hooks には用途に応じた4つのタイプがある。

タイプ

用途

説明

command

シェルコマンド実行

シェルスクリプトを実行し、exit code で許可/ブロックを制御する。最も基本的

http

HTTP POST 送信

イベントデータを外部 Webhook エンドポイントに POST する。Slack 通知等に使用

prompt

単一ターン LLM 判定

Claude モデルにプロンプトを送り、ok: true/false の判定を得る。ツール使用不可・低コスト

agent

サブエージェント起動

ツール使用可能なサブエージェントを生成し、複数ターンの検証を行う。最も強力だがトークン消費大。model 指定可

判断基準:「提案」なら CLAUDE.md、「ルール」なら Hooks

判断の目安は「それが提案なのかルールなのか」である。提案なら CLAUDE.md、絶対に破られてはならないルールなら Hooks を使う(出典: Medium - Mustafa Morbel)。

設定レイヤーの全体像

Claude Code には複数の設定レイヤーがあり、上にいくほど柔軟で気軽に使える(が、確実性は低い)、下にいくほど厳格で回避不能(が、設定コストが高い)。

             CLAUDE.md
          (提案・ガイドライン)
        ─────────────────────
           Skills / Commands
        (タスク特化の知識・手順)
        ─────────────────────
           permissions.deny
         (組み込みの拒否ルール)
        ─────────────────────
               Hooks
        (決定論的な強制ルール)

レイヤー

性質

確実性

CLAUDE.md

確率的(参考情報)

70〜90%

Skills / Commands

確率的(構造化)

70〜90%

permissions.deny

決定論的(簡易)

90%+

Hooks

決定論的(完全)

100%

確実性を重視するなら、決定論的な Hooks に寄せた方が望む結果を得られる確率は当然高くなる。一方で、Hooks はシェルスクリプトの作成や設定のメンテナンスといった運用コストがかかるため、コストと確実性のバランスでどのレイヤーを使うかを判断するのが現実的である。

settings.json の書き方

設定ファイルの場所

Hooks は .claude/settings.json(または .claude/settings.local.json)に記述する。

ファイル

スコープ

Git 管理

~/.claude/settings.json

ユーザー全体

通常しない

プロジェクト/.claude/settings.json

プロジェクト全体

チームで共有する場合

プロジェクト/.claude/settings.local.json

個人のローカル設定

しない(.gitignore 推奨)

基本構造

{
  "hooks": {
    "イベント名": [
      {
        "matcher": "ツール名の正規表現(省略で全マッチ)",
        "hooks": [
          {
            "type": "command",
            "command": "実行するシェルコマンド"
          }
        ]
      }
    ]
  }
}

以下の設定は「Bash ツール実行前check.sh を実行する」という意味である。スクリプトが exit 2 を返せばブロックされる。

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{ "type": "command",
        "command": "./check.sh" }]
    }]
  }
}

Claude Code はイベント発火時にJSON コンテキスト(ツール名・引数等)を stdin でフックに渡す。フック内で jq を使って解析するのが基本パターンである。

主要なイベント

イベント

発火タイミング

ブロック可能

PreToolUse

ツール実行

はい(exit 2 でブロック)

PostToolUse

ツール実行成功

いいえ

Stop

ターンの終了時(Claude が応答テキストを出力し終わった時)

はい(exit 2 で継続を強制)

Notification

通知発生時(権限確認、アイドル状態等)

いいえ

SessionStart

セッション開始/再開時

いいえ

exit code の意味

exit code

意味

exit 0

成功。ツール実行を許可

exit 1

エラー。ログに記録されるがブロックしない

exit 2

ブロック。ツール実行を阻止し、Claude にブロック理由を伝える

重要: ブロックするには exit 2 が必要。exit 1 ではブロックされない。 これは最もよくある間違いの一つである。

3つの実例で理解する Hooks

Hooks の仕組みを3つの異なるパターンで理解する。

実例1: 目次と本文の整合性チェック(Stop + agent)

Stop イベントと agent ハンドラーの組み合わせ。Claude の作業完了時にサブ AI が自動で品質検証を行う。

仕組み:

  1. Claude が記事やドキュメントの編集を完了する
  2. Stop イベントが発火し、agent ハンドラーが起動
  3. サブエージェントが目次(アジェンダ)と本文の見出しを照合
  4. 不一致があれば Claude にフィードバックし、修正を促す

.claude/settings.json:

{
  "hooks": {
    "Stop": [{
      "hooks": [{ "type": "agent",
        "prompt": "目次・アジェンダに記載された項目が本文で全てカバーされているか検証してください。不一致があれば報告してください。$ARGUMENTS" }]
    }]
  }
}

ポイント:

  • agent タイプ = サブ AI が自動検証。シェルスクリプト不要で、自然言語でルールを記述できる
  • Stop イベント = 作業完了時に発火。記事・スライド・ドキュメント等の完成品を検証するのに最適
  • 目次と本文の見出し不一致を自動検出し、品質を担保する

実例2: 危険コマンドのブロック(PreToolUse + command)

PreToolUse イベントと command ハンドラーの組み合わせ。Claude が rm -rf /git push --force maingit reset --hard などの破壊的コマンドを実行しようとした場合に、実行前にシステムレベルでブロックする。

仕組み:

  1. Claude が Bash ツールでコマンドを実行しようとする
  2. PreToolUse フックが発火し、コマンド文字列を正規表現でチェック
  3. 危険パターンに該当 → exit 2 でブロック
  4. 該当しない → exit 0 で実行を許可

.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous-commands.sh"
          }
        ]
      }
    ]
  }
}

.claude/hooks/block-dangerous-commands.sh:

#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

DANGEROUS_PATTERNS=(
  'rm\s+-rf\s+/'
  'git\s+push\s+(-f|--force)\s+(origin\s+)?main'
  'git\s+reset\s+--hard'
  'DROP\s+TABLE'
  ':\(\)\{ :\|:& \};:'
)

for pattern in "${DANGEROUS_PATTERNS[@]}"; do
  if echo "$CMD" | grep -qE "$pattern"; then
    echo "BLOCKED: Dangerous command detected: $CMD" >&2
    exit 2
  fi
done

exit 0

パターン

ブロック対象

理由

rm\s+-rf\s+/

rm -rf /

ルートからの再帰的強制削除

git\s+push\s+(-f|--force)

git push --force main

main ブランチへのフォースプッシュ

git\s+reset\s+--hard

git reset --hard

コミットされていない変更の全破棄

DROP\s+TABLE

SQL の DROP TABLE

データベーステーブルの削除

:\(\)\{ :|:& \};:

フォーク爆弾

システムリソース枯渇攻撃

ポイント:

  • command タイプ = シェルスクリプトで判定。exit 2 を返すと実行を阻止
  • PreToolUse イベント = ツール実行に発火するため、危険な操作を未然に防げる
  • permissions.deny でもコマンドの拒否は可能だが、パイプやマルチラインコマンドで回避されるケースがある(出典: DEV Community)。PreToolUse フックはコマンド文字列全体を解析できるため、より堅牢

危険コマンドブロックの実装パターンは Blake Crosley のチュートリアルAI Hero の解説 も参考になる。

実例3: Markdown → PDF 自動生成(Stop + command)

Stop イベントと command ハンドラーの組み合わせ。Claude の作業完了時にビルド成果物を自動更新する。

仕組み:

  1. Claude が Markdown ファイル(スライド等)を編集する
  2. 作業完了時に Stop フックが発火
  3. スクリプトが変更されたスライドファイルを検出し、slidev export で PDF を自動生成
  4. 変更がなければスキップ

.claude/settings.json:

{
  "hooks": {
    "Stop": [{
      "hooks": [{ "type": "command",
        "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/export-pdf.sh",
        "timeout": 120000 }]
    }]
  }
}

.claude/hooks/export-pdf.sh:

#!/bin/bash
cd "$CLAUDE_PROJECT_DIR"

# 変更されたスライドファイルを検出
changed=$(git diff --name-only HEAD -- 'projects/*/slides.md')
[ -z "$changed" ] && exit 0

for slide in $changed; do
  dir=$(dirname "$slide")
  npx slidev export \
    --output "$dir/slides-export.pdf" \
    "$slide"
done

ポイント:

  • Markdown を編集 → Stop で自動 PDF 化。ドキュメント・レポート・提案書などあらゆる MD → 成果物変換に応用可能
  • timeout: 120000(120秒)で PDF 生成に十分な時間を確保
  • git diff で変更されたファイルだけを対象にすることで、不要なビルドを回避

フックスクリプトには実行権限を付与しておくこと。

chmod +x .claude/hooks/*.sh

デバッグの基本

/hooks コマンドで設定一覧を確認

Claude Code のプロンプトで /hooks と入力すると、イベント別に設定済みフックの一覧を読み取り専用で閲覧できる。設定ミスの確認に便利である。

Ctrl+O で verbose モード切替

verbose モードを有効にすると、トランスクリプトにフック実行の進捗が表示される。

よくある間違い

1. exit 1 を使ってしまう(出典: DEV Community

ブロックするには exit 2 が必要。exit 1 はエラーとしてログに記録されるが、ツール実行はブロックされない

# NG: ブロックされない
exit 1

# OK: ブロックされる
exit 2

2. $HOME 等の環境変数がパス展開されない

JSON 設定ファイル内で $HOME を使うと、変数展開されずにそのまま文字列として渡される。フックがサイレントにロードされなくなる。

// NG: $HOME は展開されない
"command": "$HOME/.claude/hooks/my-hook.sh"

// OK: $CLAUDE_PROJECT_DIR を使う
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/my-hook.sh"

$CLAUDE_PROJECT_DIR は Hooks 実行時に Claude Code が自動で設定する環境変数であり、正しく展開される。これが唯一の例外である。

まとめ

  • Hooks は CLAUDE.md / Skills とは根本的に異なる決定論的制御を提供する
  • 「提案」は CLAUDE.md、「絶対に破られてはならないルール」は Hooks に書く
  • ブロックするには exit 2 を使う(exit 1 ではブロックされない)
  • 3つの実例で異なるパターンを理解:
    1. 目次と本文の整合性チェック(Stop + agent)
    2. 危険コマンドのブロック(PreToolUse + command)
    3. Markdown → PDF 自動生成(Stop + command)

基礎編では、Chrome 拡張の自動ビルド、TDD の強制、API スモークテスト、Slack 通知、重複コード検出、Skill 自動選択など、日常開発を支えるより実践的なフックのレシピを紹介する。

関連記事

参考リンク