Claude Code Hooks入門 ── AIをルールで自動制御する

Claude Codeを毎日使っていると、ある問題に気づく。 「さっきはフォーマットしてくれたのに、今回はしてくれない」 「テスト回してって言い忘れた」 こういう"ムラ"が出るんだよね。

LLMは確率的に動くから、毎回同じ動きをする保証がない。 プロンプトで「必ずフォーマットして」と書いても、忘れるときは忘れる。 個人開発ならまだしも、チームでは品質のバラつきが致命的になる。

そこで登場するのが Claude Code Hooks という仕組み。 設定ファイルにルールを書くだけで、AIの動作に決定論的な制御を組み込める。 LLMの気分に左右されない、自動化されたワークフローが手に入る。

この記事では、Hooksの仕組みから実際の設定例まで解説していく。 読み終わったらすぐに試せるレベルを目指した。

Claude Code Hooksとは何か

Hooksを一言でいうと、Claude Codeのライフサイクルに差し込むコールバック関数。

公式ドキュメントの定義はこう。

Hooks are user-defined shell commands that execute at specific points in Claude Code's lifecycle. They provide deterministic control over Claude Code's behavior.

つまり以下のようなことが設定ファイルだけで実現できる。

  • Claudeがファイルを書いたら自動でフォーマッタを走らせる
  • Bashコマンド実行前に危険なコマンドをブロックする
  • タスク完了前にテストを自動実行する

ポイントは deterministic(決定論的) という言葉。 プロンプトでお願いするのではなく、ルールとして必ず実行される。 ここがHooksの本質的な価値だと思う。

イベントとハンドラ

Hooksには多数のライフサイクルイベントが用意されている。 よく使うのはこのあたり。

  • PreToolUse ── ツール実行前に発火。ブロック可能
  • PostToolUse ── ツール実行成功後に発火
  • Notification ── Claudeが通知を送るとき
  • SessionStart ── セッション開始・再開時
  • Stop ── Claudeの応答完了時。ブロック可能
  • TaskCompleted ── タスク完了時。ブロック可能

ハンドラのタイプは4種類ある。

  • command ── シェルコマンドを実行(最も基本)
  • http ── HTTP POSTリクエストを送信
  • prompt ── LLMに判断を委ねる
  • agent ── サブエージェントで検証する

ほとんどの場面ではcommand型で十分。 高度な判断が必要なときだけprompt型やagent型を使う。

なぜHooksが必要なのか

Hooksの定義がわかったところで、具体的にどんな課題を解決するのか。 僕が特に刺さったのは3つの場面だった。

フォーマット忘れ問題

Claude Codeにコードを書いてもらったあと、フォーマッタを通すかどうかはLLM次第。 CLAUDE.mdに「Prettierを必ず実行して」と書いても、コンテキストが長くなると抜け落ちることがある。

PostToolUseフックなら、ファイル編集のたびに確実にPrettierが走る。 LLMが覚えているかどうかに関係なく、毎回実行される。

危険コマンドの実行リスク

rm -rfDROP TABLE をClaude Codeが実行してしまうリスク。 通常は許可ダイアログが出るけど、自動モードだとすり抜ける可能性がある。

PreToolUseフックで特定パターンをブロックすれば、そもそも実行されない。 exit code 2を返すだけでブロックできるシンプルさも魅力的。

チーム開発での品質バラつき

プロジェクトの .claude/settings.json にHooksを定義すれば、リポジトリにコミットできる。 チーム全員が同じルールでClaude Codeを使うことになる。 「Aさんはフォーマットされてるけど、Bさんはされてない」がなくなる。

これは地味だけどかなり大きいと思う。 AIツールをチームで使うときの標準化は、まだ手探りな状況。 Hooksはその解の一つになりうる。

実践: settings.jsonでHooksを設定する

Computer screen displaying lines of code Photo by Bernd Dittrich on Unsplash

ここからは実際の設定例を紹介していく。 コピペですぐ使えるレベルを意識した。

設定ファイルの場所

Hooksの設定場所は3つある。

  • ~/.claude/settings.json ── 全プロジェクト共通(個人マシン)
  • .claude/settings.json ── プロジェクト単位(コミット可能)
  • .claude/settings.local.json ── プロジェクト単位(gitignore対象)

チームで共有したいルールは .claude/settings.json に置く。 個人的な通知設定などは ~/.claude/settings.json に。 これが基本的な使い分けになる。

例1: ファイル編集後にPrettierを自動実行

ファイルを書いたり編集するたびにPrettierを走らせる設定。 公式ガイドにも掲載されているパターン。 PostToolUseイベントで、Edit/Writeツールにマッチさせる。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

matcherEdit|Write は正規表現パターン。 EditかWriteツールが使われたときだけ発火する。 コマンドは標準入力からJSONを受け取り、jq でファイルパスを抽出してPrettierに渡す仕組み。

例2: 危険なBashコマンドをブロック

rm -rfDROP TABLE を含むコマンドをブロックする設定。 PreToolUseイベントで、Bashツールにマッチさせる。

まずスクリプトを .claude/hooks/block-dangerous.sh として作成する。

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
 
if echo "$COMMAND" | grep -qE 'rm -rf|DROP TABLE|force push'; then
  echo "Blocked: dangerous command detected" >&2
  exit 2
fi
 
exit 0

settings.jsonの設定はこう。

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

exit code 2を返すとツール呼び出しがブロックされる。 stderrに書いたメッセージがClaudeにフィードバックされるので、別のアプローチを試みてくれる。

例3: Claudeが待機中にデスクトップ通知

Claudeが入力待ちになったら通知を送る設定。 ターミナルを見張り続ける必要がなくなる。

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "notify-send 'Claude Code' 'Claude Codeが入力を待っています'"
          }
        ]
      }
    ]
  }
}

macOSなら osascript -e 'display notification ...' に置き換える。 matcherを空文字にするとすべての通知タイプに反応する。

exit codeの意味

Hooksの制御はexit codeで行う。覚えるのは3パターン。

  • exit 0 ── 成功。アクション続行
  • exit 2 ── ブロック。stderrがClaudeに返る
  • その他 ── 非ブロックエラー。verboseモードで確認可能

シンプルなルールだから迷わない。 より細かい制御が必要なら、exit 0でJSON出力する方法もある。

応用パターン: prompt型・agent型フック

command型でカバーできない場面もある。 「ルールでは判断しきれないけど、AIの判断は入れたい」というケース。

prompt型: LLMに判断を委ねる

Stopフック(応答完了時)でタスク完了チェックをする例。 LLMが「まだ残作業がある」と判断したら、Claudeが作業を続ける。

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Check if all tasks are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}."
          }
        ]
      }
    ]
  }
}

prompt型はデフォルトでHaikuモデルが使われる。 高速で安価なので、頻繁に発火するフックでもコスト面で問題ない。

agent型: サブエージェントで検証

agent型はもう一段踏み込んで、ファイルを読んだりコマンドを実行できる。 テストスイートの実行と結果検証に向いている。

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Run the test suite and verify all tests pass. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

command型で npm test を直接叩くのとの違いは、テスト結果を解釈してくれる点。 失敗内容を理解した上で、Claudeにフィードバックを返せる。

ただしagent型はトークン消費が大きい。使いどころは選びたい。

注意点と制限事項

Hooksは強力だけど、万能ではない。 導入前に知っておくべきポイントをまとめる。

PostToolUseは取り消しできない

PostToolUseフックが発火する時点でツールはすでに実行済み。 ファイル書き込みやコマンド実行を「なかったこと」にはできない。 破壊的操作のブロックにはPreToolUseを使うこと。

Stopフックの無限ループに注意

Stopフックで「タスク未完了」と返すと、Claudeが作業を続ける。 でもその応答完了でまたStopフックが発火する。 入力JSONの stop_hook_active フィールドをチェックして、2回目以降は早期リターンする設計が必要になる。

セキュリティリスク

Hooksは任意のシェルコマンドを実行する仕組み。 2025年7月〜2026年1月にかけて、Check Point Researchがプロジェクト設定ファイル経由のRCE脆弱性(CVE-2025-59536)を報告している。 Anthropicは修正済みだが、共有リポジトリのsettings.jsonには信頼できるコマンドだけを記載すべき。

複数hookの競合

同じツールに複数のPreToolUseフックが updatedInput を返すと、最後に完了したフックが優先される。 フックは並列実行のため、結果が非決定論的になりうる。 入力を書き換えるフックは1つのツールに対して1つに絞るのが安全。

シェルプロファイルの干渉

~/.zshrc~/.bashrc に無条件の echo 文があると、フックのJSON出力が壊れることがある。 インタラクティブシェルの場合のみechoする条件分岐で回避できる。

if [[ $- == *i* ]]; then
  echo "Shell ready"
fi

これらは公式ガイドのトラブルシューティングにも記載されている。 事前に知っておけば回避できるものばかり。。

まとめ: AIの動きに「ルール」を入れる時代

Claude Code Hooksの本質は、AIの動作に決定論的なルールを組み込めること。 「お願いする」のではなく「設定する」という発想の転換だと思う。

LLMが確率的に動く以上、品質の担保にはルールベースの仕組みが要る。 これはハーネスエンジニアリングという概念にも通じる考え方。 AIエージェントの動作を「ハーネス(制御装置)」で管理するアプローチが注目されている。

まずは一番シンプルなところから始めてみてほしい。 PostToolUseでフォーマッタを自動実行するだけでも、体験がかなり変わる。 settings.jsonに数行追加するだけだから、今日からでも試せるはず。

Hooksを使いこなすと「AIを使う」から「AIをチームに組み込む」に変わる。 その変化はエンジニアとしてかなりおもしろいんだよね。

参考リンク