この記事の目次
Claude Codeを使い込んでいくと、「フォーマットのかけ忘れ」「危険なコマンドの実行」「作業完了に気づかない」といった細かな運用課題に出会う。hooksはこうした課題を、LLMの判断に頼らず仕組みとして解決するための機能だ。この記事では、hooksの基本概念から設定ファイルの書き方、Exit Codeによる制御、そしてチームで安全に運用するための考え方までを、公式情報で確認できる範囲に絞って解説する。
Claude Code hooksとは何か
hooksとは、Claude Codeの処理の特定のタイミングで、あらかじめ登録したシェルコマンドを実行させる仕組みである。ツールの実行前後やセッションの開始・終了といったイベントに合わせて、決められたコマンドが決められた通りに動く。
ここで重要なのは、hooksが「プロンプトでの指示」とは性質が異なるという点だ。CLAUDE.mdに「保存後は必ずフォーマッタを実行してください」と書いても、それはあくまでLLMへの依頼であり、状況によっては省略されることがある。一方hooksは、対象イベントが発火すれば毎回同じコマンドが実行される。LLMが「今回は省略していいだろう」と判断する余地がない。
言い換えると、CLAUDE.mdは「守ってほしいルールを伝える」仕組みであり、hooksは「システムレベルで強制する」仕組みだ。CLAUDE.mdの書き方についてはこちらの記事で詳しく扱っているので、ルール設計の全体像を掴みたい場合は目を通しておくとよい。Claude Code自体の概要から確認したい場合はClaude Codeとは何かを先に読むと理解しやすい。
hooksが必要になる典型的な場面は、フォーマット漏れの防止、破壊的なコマンドの事前ブロック、通知の自動化など、「毎回確実に行われてほしいが、LLMの気まぐれに委ねたくない処理」だ。
設定ファイルの置き場所とスコープ
hooksは設定ファイル内のhooksキーに記述する。設定ファイルには複数の階層があり、どこに書くかによって影響範囲が変わる。
- ユーザー単位の設定(ホームディレクトリ配下):自分の全プロジェクトに共通して適用したい個人設定を書く場所
- プロジェクト単位の設定(
.claude/settings.json):リポジトリに含めてチーム全員で共有する設定を書く場所 - ローカルの設定(
.claude/settings.local.jsonなど):自分の環境だけに適用したい、gitには含めない設定を書く場所
この階層分けを踏まえると、「フォーマッタの実行」のようにチーム全員が同じ挙動を期待するhookはプロジェクト設定に、個人的な通知先の指定のようなものはローカル設定に置く、という切り分けが自然になる。
プロジェクト設定をgit管理下に置く場合、hooksの中身はシェルコマンドそのものなので、プルリクエストのレビュー対象として扱う必要がある。誰がどう承認するかという運用ルールについてはClaude Code運用ルールの記事で扱っているので、チーム導入時の判断材料になるはずだ。
主要なイベントの種類(確認できる範囲で)
hooksが発火するタイミングにはいくつかの種類がある。ここでは、複数の場面で挙動が一致していて実務上安定して使えるイベントを、発火タイミングと用途の観点で整理する。なお、これ以外にもイベントが存在する可能性があり、全種類や最新の追加分については公式ドキュメントを確認しておこう。
ツール実行前後に発火するイベント
ファイル編集やコマンド実行といった「ツール」が呼び出される直前・直後に発火するイベントがある。実行前のイベントでは、内容を検査してツールの実行自体を止めることができ、実行後のイベントでは、結果を受けて後処理(フォーマット、ログ記録など)を行える。
セッションのライフサイクルに関わるイベント
セッションの開始時、終了時、あるいは会話履歴が圧縮される直前など、セッション全体の節目で発火するイベントがある。作業開始時の環境チェックや、終了時のクリーンアップに使える。
通知・入力に関わるイベント
Claude Codeがユーザーの操作を待っている状態や、応答を終えて処理を終了する場面で発火するイベントがある。作業完了通知や、応答終了時の追加チェックに使える。
hookの書き方(matcher・入出力・環境変数)
hookの設定は、大まかに「イベント名」→「matcher(対象を絞る条件)」→「実行するコマンド」という3層構造になっている。ツール実行に関するイベントでは、matcherでどのツールを対象にするかを指定できる。matcherはツール名に対する正規表現マッチであり、特定のツール名を指定すればそのツールが呼ばれたときだけhookが発火し、空にすればすべてのツールが対象になる。
なお、コマンドの引数レベルまで細かく絞り込む構文については、hooksのmatcher仕様なのか別の権限設定(許可・拒否ルール)の構文なのかが紛らわしい部分がある。この記事では「matcherはツール名ベースの正規表現マッチである」という、確認が取れている範囲の説明にとどめる。
hookとして登録するコマンドには、対象イベントに関する情報がJSON形式で標準入力(stdin)から渡される。ツール実行前後のイベントであれば、どのツールがどんな引数で呼ばれたかといった情報が含まれる。コマンド側はこのJSONをjqなどでパースして、必要な判断を行う。
ハンドラーの種類としては、シェルコマンドを直接実行するcommandタイプが広く使われている。この記事でもこのタイプを前提に解説する。
Exit Codeによる制御
hookとして実行したコマンドの終了コード(Exit Code)によって、Claude Code側の挙動が変わる。
0:正常終了。標準出力の内容は通常のログとして扱われ、処理はそのまま続行する2:ブロックを意味する終了コード。標準エラー出力の内容がClaude側に伝わり、ツール実行前のイベントであればそのツール呼び出し自体を止められる- それ以外の非ゼロ値:ブロックはしないが、エラーとしてユーザーに表示される
「危険な操作を止めたい」場合は、判定ロジックの中で条件に合致したときにexit 2で終了し、標準エラー出力に理由を書く、という形が基本パターンになる。逆に問題がなければexit 0で抜ける。
実践例:まず設定してみる3つのhook
以下はプロジェクト設定(.claude/settings.json)に書く例だ。実際に導入する際は、まず自分のローカル設定で試してから、チーム共有の設定に昇格させるとよい。
保存後の自動フォーマット
ファイルが編集された直後に、対象ファイルをフォーマッタにかける。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs -r npx prettier --write"
}
]
}
]
}
}
編集系ツールが呼ばれるたびに自動でフォーマットがかかるため、「フォーマットのかけ忘れでレビューコメントが増える」という状態を避けやすくなる。
危険なコマンドの実行をブロックする
rm -rf /のような明らかに破壊的なコマンドを、実行前に止める。
#!/bin/bash
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command // empty')
if echo "$command" | grep -qE 'rm -rf /($|[^a-zA-Z0-9_./-])'; then
echo "破壊的な可能性があるコマンドをブロックしました: $command" >&2
exit 2
fi
exit 0
このスクリプトをPreToolUseのBashツール向けhookとして登録しておく。あくまで簡易的なパターンマッチであり、すべての危険なコマンドを検出できるわけではない点には注意してほしい。検知ロジックはプロジェクトの実情に合わせて拡充していく前提で使うものだ。
作業完了時に通知を飛ばす
Claude Codeが応答を終えたタイミングで、デスクトップ通知を出す。
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Codeの応答が完了しました\" with title \"Claude Code\"'"
}
]
}
]
}
}
macOS向けの例だが、OSに応じて通知コマンドを差し替えれば同じ考え方で使える。長時間かかるタスクを別のウィンドウで待っている場合に、切り替えのタイミングを逃しにくくなる。
安全に運用するための注意点
hooksは便利な反面、いくつか気をつけたい点がある。
まず、応答終了時のイベント(Stopなど)をブロックするhookを書く場合、ブロックによってClaude Codeがさらに処理を続け、それがまた同じイベントを発火させる、という循環が起きうる。Claude Code側が内部的にこの循環をどこまで防いでいるか(フラグの有無や判定条件など)は、執筆時点で一次情報から確認できる範囲を超えるため本記事では立ち入らない。その代わり、hookを書く側で実践できる対策を挙げておく。
exit 2でブロックする条件は最初から広く取らず、grepなどで対象を厳密にマッチさせた上で、必要に応じて範囲を広げていく- hookスクリプト内で一時ファイルを使い、同一セッション内で同じ判定が続けて発生した回数を記録しておき、想定より多く繰り返された場合は強制的に
exit 0へフォールバックする実装にしておく - 本番のプロジェクト設定に反映する前に、ローカル設定や単体のテストセッションで実際に何度か発火させ、想定外の繰り返しが起きないかを目視で確かめる
ブロック系のhookは、条件を単純なものから始め、動かしながら検証するという手順を徹底することが、循環を防ぐ最も実務的な方法になる。
次に、hooksと権限設定(許可モードなど)は別のレイヤーで動く安全策だという点を押さえておきたい。権限設定は「どの操作をユーザー確認なしに実行してよいか」を制御するものだが、hooksはそれとは独立に、イベント発火のたびに決まったコマンドを実行する。権限を緩めた運用をする場合ほど、hooksによるチェックの重要性は増す。企業導入時のセキュリティ設計全般についてはClaude Codeのエンタープライズセキュリティガイドで扱っているので、権限設定とhooksを組み合わせた全体設計を検討する際に役立つ。
最後に、hooksはシェルコマンドを直接実行する仕組みである以上、内容次第では意図しない副作用を生む。チームで導入する際は、誰がhookの内容をレビューし、誰が変更を承認するのかという運用体制をあらかじめ決めておく必要がある。より広いチーム展開の設計まで検討したい場合は、Claude Codeのエンタープライズ導入ガイドも参考になる。
動作確認とデバッグの基本
設定したhooksが実際に発火しているかを確認する基本的な流れは次の通りだ。
- 設定ファイルがJSONとして正しい構文になっているかを確認する
- 対象イベントを実際に発生させる操作(対象ツールの実行など)を行う
- hookコマンドの標準出力・標準エラー出力がどう表示されるかを確認する
- 期待通りに動かない場合は、設定ファイルの階層(ユーザー・プロジェクト・ローカルのどこに書いたか)と、matcherの対象ツール名が正しいかを見直す
うまく動かない場合の原因は、複数の設定ファイルで同じイベントに対する設定が競合している、matcherの正規表現が対象ツール名と一致していない、といったケースが多い。まずは一つのシンプルなhookだけを有効にした状態で切り分けると原因を特定しやすい。
よくある質問
既存のCLAUDE.mdのルールをhooksに移行する際に注意すべき点は? CLAUDE.mdの指示文をそのままシェルコマンド化しようとすると、matcherの対象ツールを絞り込みすぎて他の操作まで巻き込んだり、逆に緩すぎて意図しないタイミングで発火したりすることがある。移行する際は、まず該当のCLAUDE.mdの指示を一つだけ選び、ローカル設定でmatcherとExit Codeの挙動を確認してから、チーム共有の設定に反映するという順序を踏むと事故を避けやすい。
チームメンバー全員の環境で同じhookを動かすには何を揃える必要があるか?
hookコマンドが依存するツール(jqやフォーマッタなど)がメンバー全員の環境にインストールされている必要がある。プロジェクト設定として共有する場合は、READMEなどに前提ツールを明記しておくと導入時のつまずきを減らせる。
hooksを使い始める前に最低限読んでおくべき公式情報はどこか? イベントの全種類、matcherの詳細な仕様、そして本記事で触れなかった細かい挙動については、公式ドキュメントを一次情報として確認しておきたい。機能は更新される可能性があるため、設定を書く直前に最新情報を確認する習慣をつけておくと安心だ。
まとめ
Claude Code hooksは、LLMの判断を介さずに特定のタイミングで決まった処理を実行させるための仕組みだ。設定ファイルの階層を理解し、matcherとExit Codeの役割を押さえれば、フォーマットの自動化や危険コマンドのブロックといった実用的なhookを自分の手で書けるようになる。一方で、チームに展開する際はレビュー体制や権限設定との関係を含めた運用設計が欠かせない。この設計は個々の設定例を眺めるだけでは身につきにくく、実際にチームで運用しながら調整していく部分が大きい。hooksを含めたClaude Codeの実践的な設定・運用は独学でも進められるが、チーム全体で型を揃えて定着させたい場合は、研修という選択肢も候補になる。