1. その瞬間 · 「AIにお願いする」だけでは足りないと気づくとき
AIを使うどのプロジェクトにも、モデルの善意を当てにするのをやめる瞬間が訪れます。AGENTS.md に、コードをフォーマットせよ、機密ファイルは触るな、テストを走らせよ、と書きました。そして90%はその通りにやってくれます。ですが残りの10%こそがあなたを噛むのです。本番の .env を編集してしまった回、フォーマットをすっ飛ばしてコミットが汚れた回、「一時的っぽい」という理由で間違ったフォルダに rm -rf を実行してしまった回です。
hooks の瞬間とは、こう考えたときです。「これがモデルの良い判断に依存しなかったら? ただ、いつでも、その判断に頼らず起きるとしたら?」。それこそが、Claude Code の公式ドキュメントが hooks のページの中心に据えている約束です。hooks はエージェントの振る舞いに対して 「決定論的な制御」 を与え、「LLM がそれを実行することを選ぶかどうかに頼るのではなく、特定のアクションが必ず起きることを保証する」(英語原文の忠実な訳)。この一文がこの資料全体のテーゼです。
2. その痛み · どこから来て、なぜ起きるのか
その痛みは黙示録ではなく、浸食です。AIに何かを「いつも」やってと頼んでも、時々やってくれない。なぜならLLMは決定論的ではないからです。毎ターン、あらためて判断し直すのであり、コンテキストが詰まったターンでは、あなたのフォーマットのリマインダーが他の何千ものトークンの下に埋もれてしまうこともある。AIが反抗的なのではありません。確率的なシステムに一貫性を求めている のです。優秀だけれど注意散漫な人に、ガスの元栓を締めるのを一度も忘れないでとお願いするようなものです。ほとんどの夜は締めてくれる。だからこそあなたは油断してしまうのです。
- 毎セッション同じ指示を繰り返している(「フォーマットして」「Xは触らないで」「テストを走らせて」)のに、それでも 時々すっぽ抜ける。
- AIが絶対に触ってはいけないファイルを編集していたことに 後から 気づいた:
.env、package-lock.json、.git/の中身など。 - フォーマットが不揃いなコミットが紛れ込み、チーム全体の diff を汚した。
- AIに広い権限を渡すのが少し怖い。なぜなら AI自身に依存しない安全網が存在しない から。
- AGENTS.md に重要なルールが文章で書かれているが、「書いてある」は「保証されている」と同じではない。
そしてここに、この資料を上級者向けたらしめている切り口があります。もしあなたが既にエージェント用の良い契約書(AGENTS.md)の書き方について読んだことがあるなら、hooks は 次のステップ です。「契約書を書く」(宣言的:何を望むかを伝える)から、「契約書が自分で実行されるようにする」(決定論的:機械が代わりに守る)へと進むのです。紙に書かれた法律と、角に立つ警官の違いです。
3. hook とは何か(煙に巻かず、一文で)
hook とは、Claude Code がそのライフサイクルの特定の時点で自動的に実行するシェルコマンドのことです。あなたは settings.json ファイルに一度だけ宣言します。それ以降、そのライフサイクルの時点が来るたびに、あなたのコマンドが走ります。モデルがその判断で発火させるのではありません。イベント が発火させるのです。そこに魔法があります。決定論的で、議論の余地がないのです。
4. ライフサイクルのイベント(引っかける先の地点)
公式ドキュメントには たくさんの ライフサイクルイベントが列挙されています。エージェントの作業のほぼどの瞬間にも対応するものがあります。数やリスト全体を暗記しないでください。考え方を身につけ、95%の時間で本当に使うものだけを覚えておきましょう。以下が、ドキュメントに載っている実際の名前そのままです。
- `SessionStart` — セッションが起動または再開したとき。新鮮なコンテキスト(例:あなたの規約、直近のコミット)を注入するのに最適。その標準出力(stdout)は Claude のコンテキストに追加される。
- `UserPromptSubmit` — あなたがプロンプトを送信した直後、Claude がそれを処理する前。コンテキストを追加したり、プロンプトをブロックすることさえできる。
- `PreToolUse` — ツールが実行される 前。これはアクションをブロックできる。 あなたのボディガードです。ここで
rm -rfや保護ファイルへの編集を止める。 - `PostToolUse` — ツールが成功した 後。ここで編集されたばかりのファイルをフォーマットしたり、lint を走らせたり、好きなことをする。
- `Notification` — Claude があなたを必要とするとき(許可を求める、または待っている)。デスクトップ通知に最適。
- `Stop` / `SubagentStop` — Claude(またはサブエージェント)が返答を終えたとき。ターンを閉じる前の最終チェックに役立つ。
- `PreCompact`(および matcher が
compactのSessionStart)— コンテキストの圧縮の前後で、メモリが圧縮されるときに重要なものを失わないようにする。 - `SessionEnd` — セッションが終了したとき。一時ファイルの掃除や後片付けに。
Pre に引っかける。すでに起きたことに 反応したい(フォーマット、通知、記録)なら Post に引っかける。5. hook はどう宣言するか(JSON の構造)
すべての hook は、settings.json の "hooks" ブロックの中に存在します。構造は常に同じマトリョーシカです。イベント名 → matcher(何に適用するか)→ type: "command" と走らせる command を持つ hooks のリスト。一度見れば、もう忘れません。
{
"hooks": {
"PostToolUse": [ // 1. ライフサイクルの EVENTO(イベント)
{
"matcher": "Edit|Write", // 2. どのツールに適用するか
"hooks": [
{
"type": "command", // 3. シェルコマンドである
"command": "...tu comando..." // 4. 起きたときに何を走らせるか
}
]
}
]
}
}PreToolUse、PostToolUse)ではツール名でフィルタします。"Edit|Write" は「Edit または Write を使うときだけ」、"Bash" はシェルコマンドだけ、を意味します。空 の matcher("")は常にすべてに対して発火します。縦棒 | は選択肢を区切ります(最近のバージョンの Claude Code ではカンマも有効です:"Edit, Write" は等価)。6. hook はどこに存在するか(そして場所がなぜ重要か)
hook を置く場所が、その適用範囲を定義します。これは肝心です。プロジェクトの hook はチームと共有されます(リポジトリにコミットされる)。グローバルな hook はあなただけのものです。公式の表が、それぞれどこに置くかをまとめています。
- `~/.claude/settings.json` — グローバル。あなたのすべてのプロジェクト に適用される。共有されず、あなたのマシンのもの。ここには個人的な hooks(例:デスクトップ通知)を置く。
- `.claude/settings.json`(リポジトリ内)— その プロジェクト だけ。そして コミットできる:チーム全員がそのルールを継承する。ここにはプロジェクトのルール(フォーマット、保護ファイル)を置く。
- `.claude/settings.local.json` — そのプロジェクトだけ、プライベート:Claude Code が作成時に gitignore する。共有したくないローカルの設定用。
.claude/settings.json に置いて コミットする。そうすれば契約書は一人の頭の中に住むのをやめ、コードと一緒に旅するようになります。それが本当の力です。規律がリポジトリの一部になり、誰かが思い出さないといけない付箋ではなくなるのです。7. コピペできる、金に値する3つの hook
理論はもう十分。ここに、公式ドキュメントから取ってきた、貼るだけで使える3つの実際の hook があります。今日あなたが一番痛いものから始めてください。
a) 編集のたびに自動フォーマット(PostToolUse)
定番です。Claude がファイルを編集または書き込むたびに、自動で Prettier をかけます。もう二度とフォーマットが不揃いなコミットはありません。プロジェクトの .claude/settings.json に置きます。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}jq -r '.tool_input.file_path' はその JSON から 編集されたファイルのパスを抽出 し、xargs npx prettier --write がそれを Prettier に渡します。jq はコマンドライン用のJSONリーダーです。macOS なら brew install jq、Debian/Ubuntu なら apt-get install jq でインストールしてください。b) rm -rf のような危険なコマンドをブロックする(PreToolUse)
これが恐怖を取り除いてくれるものです。各 Bash コマンドを実行 前 にチェックし、破壊的なものが含まれていれば 拒否する PreToolUse の hook です。ここではドキュメントが明快に述べている技術的な細部が本当に重要です。終了コード2で終了するとブロックされ、標準エラー出力(stderr)に書いたものが Claude に説明として届き、修正のヒントになります。まずスクリプト。
#!/bin/bash # .claude/hooks/block-rm-rf.sh INPUT=$(cat) COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command') if echo "$COMMAND" | grep -q "rm -rf"; then echo "ブロックされました: 'rm -rf' は許可されていません。再帰・強制ではなく、具体的なパスを削除してください。" >&2 exit 2 # exit 2 = アクションをブロック; stderr がフィードバックとして Claude に届く fi exit 0 # exit 0 = 異議なし; 通常の権限フローが続く
実行可能にして、スクリプトを指すように settings に登録します。
chmod +x .claude/hooks/block-rm-rf.sh
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-rm-rf.sh"
}
]
}
]
}
}grep -q "rm -rf" は明白なケースは捕まえますが、rm -fr、変な空白の入った rm -rf、rm --recursive --force はすり抜けます。目に見える安全網、そしてAIへの教育的フィードバックとしては役立ちますが、防弾装備と混同してはいけません。ドキュメント自身が遠回しなく述べています。hook のフィルタは best-effort(ベストエフォート)であり「フェイルオープン」する(コマンドをパースできなければ通してしまう)。だから、誰も回避できない ハードな禁止 には、hook ではなく 権限システム(permissions.deny)を使ってください。正しいパターンはこうです:反応して知らせるための hook、本物の錠前のための permission rules。PreToolUse は、`bypassPermissions` でも `--dangerously-skip-permissions` でも、ツールを拒否します。つまり、あなた(またはAI)が権限の壁をすべて下げていても、あなたのブロック用 hook は立ったままなのです。このルールは非対称であり、意図的にそう設計されています。hooks はポリシーを 強化 することはできても、権限ルールが許す範囲を超えて緩めることは決してできません。ブロック用 hook は、あなた自身ですら誤って回避できないルールなのです。c) AIがあなたを必要とするときのデスクトップ通知(Notification)
ターミナルを見張るのをやめるためのものです。Claude が終わる、または許可を必要とするとき、デスクトップ通知が飛んで、あなたは別のことをしに行けます。グローバルな ~/.claude/settings.json に置きます(osascript を使った macOS の例)。
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code があなたの注意を必要としています\" with title \"Claude Code\"'"
}
]
}
]
}
}notify-send 'Claude Code' 'Claude Code があなたの注意を必要としています' を使い、Windows(PowerShell) では System.Windows.Forms の MessageBox を使います。JSONの構造は同じです — 変わるのは command の行だけ。ガイド自身が書いている macOS の小技:通知が出ない場合は、一度 osascript -e 'display notification "test"' を走らせ、その後システム設定 → 通知 で Script Editor に権限を与えてください。8. 習慣 · いつ hook を考えるべきか(そしていつ考えないべきか)
hooks は何にでも使うものではありません。頭の中のルールはシンプルです。何かが 常に、機械的に、判断なしで起きるべき なら、それは hook です。何かが ケースごとの判断を要する なら、決定論的な hook で無理に縛らないでください。モデルに判断させる(あるいは、ドキュメントがサポートしている prompt/エージェント型の hook を使う。ただしそれは別次元の話です)。
- AGENTS.md に「〜を忘れないで…」を3回目に書いていると気づいたとき。繰り返すなら、それを hook に変える。
- 絶対に起きてはいけないアクションがあるとき(
.envを触る、再帰削除、.git/への書き込み):それはブロック用のPreToolUse— ハードな錠前としては権限ルールで裏打ちする。 - 編集後に必ず起きるべきアクションがあるとき(フォーマット、import の並べ替え、lint の実行):それは
PostToolUse。 - 圧縮のたびに重要なコンテキストを再注入させ、AIがセッションの途中であなたの規約を「忘れない」ようにしたいとき。
PostToolUse に注意:すでに起きたことは取り消せません — 反応するだけです。阻止する には常に PreToolUse。複雑なロジックを詰め込みすぎると hook はもろくなります。短く、単一目的に保ってください。9. 使い方のプロトコル · 最初の役立つ hook を組む
ここで実践的な細部です。ドキュメント自身が、hook を説明して Claude に書かせることを勧めています。ですが、うまく — そして安全に — いくには、「hook を入れて」ではなく、構造化された依頼を与えるのがよいでしょう。これがこの資料の唯一のマスタープロンプトです。ひとつだけ、強力で、安全ブレーキ付きであなたの最初の hook を設計します。
自分のワークフローのルールを自動化するために、Claude Code の最初の hook を組みたいです。私が常に保証したいルールはこれです:[ルールを記述する。例:「編集したファイルを毎回 prettier でフォーマットする」/「ファイルを再帰的に削除するコマンドをブロックする」/「.env や .git/ の中身が編集されるのを阻止する」/「終わったら通知で知らせる」]。 何かを書き始める前に、これを分かりやすい言葉で答えて、うまく設計するのを手伝ってください(私はプログラミングをしません): 1. このルールに正しいライフサイクルの EVENTO(イベント)。ルールが何かを阻止するものなら、PreToolUse であるべき(前に起き、ブロックできる)。すでに行われたことに反応する(フォーマット、通知、記録)なら、PostToolUse か Notification であるべき。どれを選び、なぜかを教えてください。 2. 正確な MATCHER(どのツールに適用するか:Edit|Write、Bash、または全部なら空)と、なぜ他ではなくそれかの理由。できるだけ狭く保ってください:matcher が広すぎると、hook が本来ではない場所で発火します。 3. settings.json が存在すべき場所: - このリポジトリのチーム全員が守るべきルールなら → .claude/settings.json(コミットされる)。 - 私のマシンとすべてのプロジェクト用だけなら → ~/.claude/settings.json。 - ローカルでプライベートなら → .claude/settings.local.json。 どれか、そしてなぜかを教えてください。 4. 貼るだけで使える、完成した JSON ブロック。hook を組んだ状態で。別スクリプトが必要なら(PreToolUse でのブロックでは典型的)、完全な .sh スクリプトも渡し、chmod +x で実行可能にするのを私に思い出させてください。 5. ブロック用の hook の場合:exit 2 と、なぜブロックされたかを説明する明確な stderr メッセージを使い、あなた自身(Claude)がその理由を受け取って修正できるようにしてください。無言にしないこと。そして、このルールがハードな錠前として権限ルール(permissions.deny)も必要かどうかを私に知らせてください。hook の中の grep は best-effort であり回避されうるからです。 6. セキュリティ、必須:この hook がどんなシェルコマンドを走らせるのかを一文で説明し、破壊的なことは何もせず、どこにもデータを送らないことを私に確認してください。hook はあなたの権限でシェルを走らせること、そして私が理解しているコマンドだけを保存すべきことを私に思い出させてください。 7. どうテストするか:hook が登録されていることを確認する方法(Claude Code 内の /hooks コマンド)と、本物を壊さずに実際に発火することをテストする方法を教えてください。 まず設計だけ(イベント、matcher、場所)を見せて、次に JSON と必要ならスクリプト、最後にセキュリティの説明とテストを見せてください。私の settings.json はまだ変更しないでください:自分で貼る前にブロックを見直したいのです。
このパターンに注目してください。まず設計し、セキュリティを見直し、そしてあなた自身がブロックを貼る。hook を理解せずに設定に紛れ込ませることは決してありません。あらゆる強力な自動化を守る、同じ2段階の規律です。機械が提案し、あなたが承認する。
10. 最も簡単な道 · 何をチャットで、何をファイルでやるか
こんがらがらないように、チャットでAIに任せられることと、あなたの仕事のままであることを整理します。
- ルールを説明すれば、完成した hook の JSON ブロックを書く(公式ドキュメントも明示的に勧めている)。
- ブロック用 hook の
.shスクリプトを、jq、exit 2、stderr メッセージ付きで生成する。 - あなたのルールにどのイベントとどの matcher が合うか、そしてどの
settings.jsonに置くべきかを説明する。 - インストール手順を思い出させる:スクリプトの
chmod +x、足りなければbrew install jq。
- ブロックを貼る — 読んで理解した上で、あなたの
settings.jsonに。安全網が見直していないコードであってはならない。 - 場所 を選ぶ(共有リポジトリ vs. グローバル vs. ローカル):それが誰がルールを継承するかを決める。
- hook のコマンドが 安全 であることを確認する(あなたの権限でシェルを走らせる:理解できないなら保存しない)。
- Claude Code 内の `/hooks` で登録されたことを確認し、本物を壊さずに発火することをテストする。
まとめ · あなたの hooks チェックリスト
- 正しいイベント を選んだ:阻止には
PreToolUse、反応にはPostToolUse/Notification。 - matcher はできるだけ狭い(本来ではない場所で発火しない)。
- 正しい場所 にある:共有リポジトリ、自分のグローバル、またはローカルのプライベート。
- ブロックするなら、AIが修正できるように 明確な stderr メッセージ付きの `exit 2` を使う — そしてハードな禁止なら、権限ルールで裏打ちした。
- 走らせる コマンドを理解している(自分の権限でのシェル):見直していないものは保存しなかった。
- `/hooks` で確認し、本物を壊さずに発火することをテストした。
- AGENTS.md の文章からルールを取り出し、常に起きる ものに変えた。
PreToolUse のように、常に起きるのです。それは hook と同じ哲学を、ターミナル1台ではなくチーム全体に適用したものです。品質が誰かの記憶頼みの約束ではなく、回避できないベルト上のセンサーになる。あなたはルールを一度置く。システムが永遠にそれを守るのです。