Claude Codeを始めたばかりの方へ: hookを既に使っている方向けの記事です。hookの基本を知らない場合は、先に「5分で完了・初心者ガイド」を読むのがおすすめです。
Claude Code v2.1.85で、hookにifフィールドが追加された。
:::details 初心者向け: matcherとは
matcherは「このhookをどのツールに対して実行するか」を指定するフィルタです。"matcher": "Bash"と書くと、Claude CodeがBashコマンドを実行するときだけそのhookが動きます。ただし従来はこれだけだったので、lsでもgitでも全てのBashコマンドで発火していました。
:::
これまで、hookはmatcherで指定したツール(例: Bash)が呼ばれるたびに全て実行されていた。lsでもgit専用のhookが起動し、echoでもnpm専用のhookが起動していた。
ifフィールドを使えば、特定のコマンドパターンの時だけhookを実行できる。
:::details 初心者向け: ifフィールドとは
ifフィールドは、hookの「発動条件」を細かく指定できる新機能です。従来のmatcherが「Bashコマンド全てに反応」だったのに対し、ifフィールドは「gitコマンドの時だけ反応」のように絞り込めます。宅配便の受け取りに例えると、matcherは「誰が来てもインターホンが鳴る」、ifフィールドは「宅配便の人が来た時だけ鳴る」です。
:::
Before: 全Bashコマンドでhookが起動
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "~/.claude/hooks/branch-guard.sh"
}]
}]
}
}
branch-guard.shはgit push/git reset等をブロックするhookだ。だがlsでもechoでもcatでも起動する。hookの中で「gitコマンドかどうか」を判定して、git以外ならexit 0している。
無駄なプロセス生成が毎回発生する。hookが10個あれば、ls1回で10プロセス。
After: gitコマンドの時だけhookが起動
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"if": "Bash(git *)",
"command": "~/.claude/hooks/branch-guard.sh"
}]
}]
}
}
if: "Bash(git *)" — Bashツールが呼ばれ、かつコマンドがgitで始まる場合だけhookが実行される。ifは個々のhookハンドラ内(typeやcommandと同じ階層)に書く。
ls、echo、npm testではhookが起動しない。branch-guard.shの中での「gitかどうか」判定も不要になる。
従来のmatcherだけの方式は「玄関に来た人全員に身分証チェック」、ifフィールド付きは「配達員が来たときだけ荷物を確認」のようなものだ。
構文
ifフィールドはClaude Codeの権限ルール構文(permissions.allow/permissions.denyと同じ)を使う。
Bash(git *) → gitで始まるBashコマンド
Bash(npm install *) → npm install で始まるコマンド
Bash(rm *) → rmで始まるコマンド
Edit(*.py) → .pyファイルへのEdit
Write(*.json) → .jsonファイルへのWrite
Read(*) → 全てのRead(matcherと同じ効果)
ワイルドカード*が使える。
実用例
1. git操作専用hook
{
"matcher": "Bash",
"hooks": [{ "type": "command", "if": "Bash(git *)", "command": "~/.claude/hooks/branch-guard.sh" }]
}
2. npm/pip installだけブロック
{
"matcher": "Bash",
"hooks": [{ "type": "command", "if": "Bash(npm install *)", "command": "~/.claude/hooks/dependency-install-guard.sh" }]
}
3. Pythonファイルの編集だけ構文チェック
{
"matcher": "Edit",
"hooks": [{ "type": "command", "if": "Edit(*.py)", "command": "~/.claude/hooks/python-syntax-check.sh" }]
}
4. 複数条件は複数エントリ
1つのifに複数パターンは書けない。複数条件が必要な場合はエントリを分ける。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{ "type": "command", "if": "Bash(git *)", "command": "~/.claude/hooks/branch-guard.sh" }]
},
{
"matcher": "Bash",
"hooks": [{ "type": "command", "if": "Bash(npm *)", "command": "~/.claude/hooks/npm-guard.sh" }]
}
]
}
}
パフォーマンス効果
hookが10個ある場合:
| 操作 | Before (全hook実行) | After (ifフィルタ) |
|---|---|---|
ls |
10プロセス起動 | 0プロセス |
git push |
10プロセス起動 | git用のhookだけ起動 |
npm test |
10プロセス起動 | npm用のhookだけ起動 |
自律モードで数百のBashコマンドを実行するセッションでは、この差は大きい。
注意点
-
ifはv2.1.85以降で使える。古いバージョンでは無視される -
ifを指定しない場合、従来通りmatcherだけでフィルタされる(後方互換性あり) -
ifの構文が間違っているとhookは実行されない(フェイルクローズド=「判断できないなら安全側に倒す」という設計思想)。構文ミスに注意
:::details 初心者向け: フェイルクローズドとは
フェイルクローズドとは、「エラーが起きたら安全側に倒す」という設計思想です。自動ドアに例えると、停電したときに「開きっぱなし」になるのがフェイルオープン、「閉まったまま」になるのがフェイルクローズドです。セキュリティではフェイルクローズドが基本——判断できないなら、許可するより拒否する方が安全です。
:::
まとめ
v2.1.85のifフィールドにより:
- hookの実行回数が大幅に減る
- hook内の条件分岐コードが不要になる
- 設定ファイルで意図が明確になる
既存のhookをif対応にするのは簡単。matcherはそのまま、ifを1行追加するだけ。
📌 関連記事:
- Claude Codeのhookが動かない——5分で原因を特定する診断ガイド
- OpenAIが発表した「ハーネスエンジニアリング」を700時間の自律運用で実践している話
-
cc-safe-setup v2.0——610個のexample hookで守備範囲を5倍にした
📘 hookの設計思想から運用まで体系的にまとめた本: Claude Code 実戦ガイド
「AIに任せて大丈夫なのか」という不安を持ったまま800時間使い続けた記録は非エンジニアがClaude Codeを800時間走らせた——失敗と学びの全記録(¥800・第2章まで無料)に書いた。
🛡 637例をワンコマンドで: npx cc-safe-setup — ifフィールド対応例も収録。13,955テストで検証済み。
あなたのhook、全Bashコマンドで起動していませんか? ifフィールドで必要なときだけ動くようにしてみてください。
📖 トークン消費に困っているなら → Claude Codeのトークン消費を半分にする——800時間の運用データから見つけた実践テクニック(¥2,500・はじめに+第1章 無料)
⚠️ CVE-2026-21852(2026年4月公開): プロジェクト内.claude/settings.json経由でAPIキー窃盗。対策: npx cc-safe-setup(ユーザーレベル設定で免疫)→ 詳細
⚠️ Opus 4.7をお使いの方へ(2026年4月)
Opus 4.7で安全分類器の不具合・トークン消費急増が報告されています。Safety Scannerで設定を無料チェック。対策はSurvival Guideを参照。