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 -rf や DROP TABLE をClaude Codeが実行してしまうリスク。
通常は許可ダイアログが出るけど、自動モードだとすり抜ける可能性がある。
PreToolUseフックで特定パターンをブロックすれば、そもそも実行されない。 exit code 2を返すだけでブロックできるシンプルさも魅力的。
チーム開発での品質バラつき
プロジェクトの .claude/settings.json にHooksを定義すれば、リポジトリにコミットできる。
チーム全員が同じルールでClaude Codeを使うことになる。
「Aさんはフォーマットされてるけど、Bさんはされてない」がなくなる。
これは地味だけどかなり大きいと思う。 AIツールをチームで使うときの標準化は、まだ手探りな状況。 Hooksはその解の一つになりうる。
実践: settings.jsonでHooksを設定する
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"
}
]
}
]
}
}matcher の Edit|Write は正規表現パターン。
EditかWriteツールが使われたときだけ発火する。
コマンドは標準入力からJSONを受け取り、jq でファイルパスを抽出してPrettierに渡す仕組み。
例2: 危険なBashコマンドをブロック
rm -rf や DROP 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 0settings.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をチームに組み込む」に変わる。 その変化はエンジニアとしてかなりおもしろいんだよね。
参考リンク
- Hooks reference - Claude Code Docs ── 公式リファレンス。全イベントのスキーマ
- Automate workflows with hooks - Claude Code Docs ── 公式ガイド。セットアップとユースケース集
- Claude Code Hooks Tutorial: 5 Production Hooks From Scratch ── 本番向け5つのhook設定チュートリアル
- Check Point Research - CVE-2025-59536 ── セキュリティ脆弱性レポート