Claude Codeのフックは、ライフサイクルの決まった地点でClaude Codeが実行するコマンドです。モデルが選んでも選ばなくても、通知、フォーマッタ、遮断が起きます。文書にある通知は~/.claude/settings.jsonのNotificationコマンドです。macOSはosascript、Linuxはnotify-send、WindowsはPowerShellのMessageBoxです。読み取り専用の/hooksメニューで確認します。このファイルはこのマシンだけで、クラウドセッションは読みません。共有するPrettierフックは、プロジェクトの.claude/settings.jsonでPostToolUse、matcherはEdit|Writeに置きます。成功してもチャットには何も出ず、Bashコマンドが書き換えたファイルは見えません。編集を止めるには、Anthropicのprotect-files.shがEditまたはWriteの前に理由をstderrへ書き、終了コード2で抜けます。破壊的なシェルコマンドを止めるには、block-rm.shが終了コード0で抜け、permissionDecision denyを出力します。終了コード2は、JSONがallowでもPreToolUseを止めます。終了コード0だけでは、その呼び出しを承認しません。PermissionRequestとNotificationは終了コード2を無視します。起動できないcommandフック、またはPreToolUseでタイムアウトしたcommand、HTTP、MCP toolフックは、ツール呼び出しを通常の権限フローに残します。コマンド自体は、ユーザーアカウントのファイル権限で動きます。
フックはモデルが選ばなくても実行されます
Claude Codeのフックは、Claude Codeのライフサイクルの一点に付けたシェルコマンドです。その時点が来るたびにClaude Codeがコマンドを実行するので、モデルが実行を選んでも選ばなくても、通知、フォーマッタ、遮断が起きます。Anthropicのフックガイドがレシピ集です。フックスリファレンスには、イベントのスキーマ、終了コードの表、JSONフィールドがあります。
ハンドラの種類はcommand、http、mcp_tool、prompt、agentの五つです。下の三つのレシピはcommandを使います。判断が必要な決定では、promptフックがフック入力をClaudeモデルへ送ります。ガイドの既定はHaikuです。agentフックは実験的で変わることがあり、ガイドは本番のワークフローではcommandフックを優先するよう述べています。
Anthropicは、GitHubのBashコマンド検証実装も完全な参照実装としてリンクしています。
文書にあるデスクトップ通知から始めます
~/.claude/settings.jsonを開き、Notificationフックを追加します。ファイルがなければ作ります。Anthropicの最初の例はmacOSのosascriptによる表示通知で、matcherは空、“type”は“command”です。
ファイルにすでにhooksキーがあるときは、ほかのイベントキーの横にNotificationを足します。各イベント名は、その一つのhooksオブジェクトの中のキーです。オブジェクトを置き換えると、そのファイルに設定済みのフックがすべて消えます。
/hooksと入力して登録を確認します。メニューは各イベントと件数を出します。フックを選ぶと、matcher、type、ソースファイル、コマンドが見えます。メニューは読み取り専用です。フックの追加、変更、削除はJSONを編集するか、Claudeに編集させます。
試すにはEscを押し、ステータスバーにmanual mode onと出るまでShift+Tabを押します。権限が必要なことを頼み、ターミナルから離れます。macOSではosascriptがScript Editorを通ります。Script Editorに通知権限がないと、コマンドは黙って失敗し、macOSは許可を求めません。osascript -e ’display notification “test”’を一度実行し、System SettingsでScript Editorの通知を許可します。
~/.claude/settings.json
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}
LinuxとWindowsも同じNotificationイベントを使います
LinuxとWindowsの~/.claude/settings.jsonにも、同じNotificationフック、空のmatcher、commandタイプを置きます。変わるのはコマンド文字列だけです。AnthropicのLinuxコマンドはnotify-sendです。notify-sendにはデスクトップ通知デーモンが必要で、ヘッドレスサーバー、SSHセッション、ほとんどのコンテナにはそれがありません。notify-send ‘Claude Code’ ’test’で試します。DebianまたはUbuntuでコマンドがなければ、パッケージはlibnotify-binです。
AnthropicのWindowsコマンドはPowerShellのMessageBoxを開きます。ダイアログなので、ターミナルの後ろに隠れることがあります。先にPowerShellでコマンドを試します。WSLの中では、Windows相互運用を通してpowershell.exeがPATHにある必要があります。Notificationフックは遮断できません。終了コードとstderrは無視されます。
プラットフォームごとのAnthropicのNotificationコマンドです。macOSの例と同じcommandフィールドに置きます。
| プラットフォーム | コマンド |
| — | — |
| macOS | osascript -e ‘display notification “Claude Code needs your attention” with title “Claude Code”’ |
| Linux | notify-send ‘Claude Code’ ‘Claude Code needs your attention’ |
| Windows | powershell.exe -Command “[System.Reflection.Assembly]::LoadWithPartialName(‘System.Windows.Forms’); [System.Windows.Forms.MessageBox]::Show(‘Claude Code needs your attention’, ‘Claude Code’)” |
受け取りたい通知だけに合わせます
空のmatcherは、すべての通知タイプで発火します。permission_promptは、Claudeがツール、またはサンドボックス化されたコマンドのネットワーク要求の承認を必要とするときに発火します。idle_promptは、Claudeが応答を終えて約60秒たち、そのあいだ入力がなければ発火します。elicitationのダイアログは、permission_promptと同じ約6秒のゲートを使います。
ターミナルでは、プロンプトが出たときにpermission_promptのタイマーが始まり、約6秒待ちます。キー入力のたびに遅れます。Claude Desktop、VS Code拡張、そしてAgent SDK経由で権限要求に答えるほかのホストでは、permission_promptは要求の約6秒後で、入力はその時刻を遅らせません。もっと早く答えるか、PermissionRequestフックがもっと早く答えると、そのNotificationフックは動きません。権限の瞬間そのものにはPermissionRequestを使います。
agent_needs_inputは、agent viewが開いた状態でバックグラウンドセッションが入力待ちを始めたとき、または現在のセッションがチームメイトのターミナル設定の質問をし、約6秒入力がないときに発火します。agent_completedは、バックグラウンドセッションが終わるか失敗したとき、そしてagent viewが開いているあいだだけ発火します。quota_auto_resume_firedは、claude.aiの利用上限が作業を止めたあと、Claude Codeがその作業を再開するときに発火します。時点は上限のリセット時か、待ち時間中にしたことが利用を再び可能にしたときで、その場合はリセットより早くなります。quota_auto_resume_staleは、コンピュータが約30分を超えてスリープしているあいだにその上限がリセットされたときに発火します。Claude CodeはEnterを待ちます。より短いスリープのあとは作業を続け、代わりにquota_auto_resume_firedを発火します。quota_auto_resume_disabledは、Claude Codeが作業を続けずに待ちを終えるときに発火します。EscかCtrl+Cを押したとき、またはDon’t continue automaticallyを選んだときには発火しません。
agent_needs_inputとagent_completedにはClaude Code v2.1.198以降が必要です。agent_needs_inputのチームメイトのターミナル設定の場合にはv2.1.248以降が必要です。三つのquota_auto_resumeマッチャにはv2.1.234以降が必要です。ターミナルで、サンドボックス化されたコマンドのネットワーク要求に対するpermission_promptにはv2.1.246以降が必要です。
個人の通知はユーザーファイル、共有フックはプロジェクトファイルへ置きます
~/.claude/settings.jsonはこのマシンのすべてのプロジェクトに適用され、共有できません。.claude/settings.jsonは一つのプロジェクトに適用され、コミットできます。.claude/settings.local.jsonは一つのプロジェクトに適用され、Claude Codeがそこへ設定を保存するとgitignoreされます。フック項目はユーザー、プロジェクト、ローカルの設定をまたいでマージされます。各ファイルが自分のフックを足します。プロジェクトのフォーマッタがユーザーの通知フックを消すことはありません。同じハンドラを複数の設定ファイルに定義しても、一度だけ実行されます。プラグインまたはスキルにあるそのハンドラの複製は別のままです。一つのファイルの中でhooksオブジェクトを置き換えると、そのファイルに設定したほかのイベントはやはり落ちます。
クラウドセッションはローカルの~/.claude/settings.jsonを読みません。そのファイルの個人通知フックは、このマシンに残ります。
commandフックはユーザーアカウントの権限で動きます。そのアカウントがアクセスできるファイルは、変更、削除、読み取りができます。共有プロジェクトファイルへコミットする前に、コマンドを確認して試します。対話セッションでは、ワークスペース信頼ダイアログを受け入れるまで、Claude Codeは~/.claude/settings.jsonを含むすべての設定ファイルのフックを保留します。claude -pまたはSDKセッションでは、Claude Codeはそのフォルダを信頼済みとして扱うので、.claude/settings.jsonにコミットされたフックが実行されます。
Anthropicが示す、フック設定の適用範囲です。
| ファイル | 範囲 | 共有 |
| — | — | — |
| ~/.claude/settings.json | このマシンのすべてのプロジェクト | いいえ |
| .claude/settings.json | 一つのプロジェクト | はい。コミットできます |
| .claude/settings.local.json | 一つのプロジェクト | いいえ。Claude Codeがそこへ設定を保存するとgitignoreされます |
EditまたはWriteのあとでファイルを整形します
Anthropicの編集後フォーマットのレシピは、プロジェクトの.claude/settings.jsonにあるPostToolUseフックで、matcherはEdit|Writeです。コマンドはjqでtool_input.file_pathを読み、npx prettier –writeへ渡します。そのファイルにすでにあるイベントキーの横へPostToolUseを足します。
フックガイドのBash例はjqを使います。macOSではbrew install jq、DebianとUbuntuではapt-get install jqで入れます。フックが成功すると、Claude Codeは会話に何も出しません。Anthropicの確認方法は、JavaScriptファイルに単一引用符の文字列を一行足すようClaudeに頼み、ファイルを開くことです。Prettierの既定設定なら、フックはそれを二重引用符へ書き換えます。
PostToolUseはツールが成功したあとに動くので、編集をやり直せません。Edit|Writeは、Bashコマンドが書き換えたファイルも見逃します。何が書いたかにかかわらず特定のファイルを整形し直すなら、AnthropicはFileChangedフックを示します。.envrc|.envのようなFileChangedのmatcherは、|で分けたリテラルなファイル名の一覧で、FileChangedは書き込みを止められません。一つのターンのすべての変更について、ガイドはStopでの走査か、git status –porcelainで変更済みと未追跡のファイルを列挙するスクリプトのBash|PowerShellマッチャを示します。PowerShellツールが主シェルであるWindowsでは、Bashだけに一致するフックは発火しません。
.claude/settings.json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
終了コード2で編集を止めます
Anthropicのprotect-filesの例は、Edit|Writeに対するPreToolUseフックです。スクリプトはtool_input.file_pathを読み、.env、package-lock.json、.git/を保護対象の部分文字列として扱い、Blockedメッセージをstderrへ書いて終了コード2で抜けます。Claude CodeはそのEditまたはWriteを止め、メッセージをClaudeへ渡します。スクリプト末尾の終了コード0は決定なしの報告なので、通常の権限フローが続きます。編集は承認しません。
Windowsでは、Write、Edit、Readがfile_pathをバックスラッシュで渡します。スクリプトの置換は、パターン検査の前にそれをスラッシュへ変えます。スラッシュの比較をバックスラッシュのパスに当てると、編集は通過します。
スクリプトを.claude/hooks/protect-files.shとして保存します。macOSとLinuxではchmod +x .claude/hooks/protect-files.shを実行します。このコマンドはshell formなので、パスを引用符で囲み、プロジェクトの.claude/settings.jsonから登録します。Anthropicの試験は、.envへコメントを足すようClaudeに頼むことです。編集は実行前に止まります。
PreToolUseの終了コード2は、スクリプトがallowというJSONを出しても遮断します。stderrと終了コード2か、終了コード0とJSONかのどちらかを使い、フックごとに一つの方式を選びます。
.claude/hooks/protect-files.sh
#!/bin/bash
# protect-files.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Normalize Windows backslash separators so the patterns below match
FILE_PATH="${FILE_PATH//\\//}"
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
exit 2
fi
done
exit 0
EditまたはWriteの前にファイルガードを登録します
このPreToolUseグループを、プロジェクトの.claude/settings.jsonへ、すでにあるイベントキーの横に追加します。$CLAUDE_PROJECT_DIRは、セッションが始まったプロジェクトルートです。shell formでは、空白を含むパスが残るよう引用符が要ります。
起動できないcommandフック、つまりファイルがないか実行可能でないスクリプトは、編集を止めません。シェルは127のようなコードで終わり、トランスクリプトには遮断しないフックエラーが出て、ツール呼び出しは進みます。PreToolUseでタイムアウトに達したcommand、HTTP、MCP toolフックも、呼び出しを通常の権限フローに残します。これらのフックの既定は600秒です。起動に失敗するかタイムアウトするガードは門を開けたままにするので、硬い遮断は権限システムに残します。リファレンスが述べる例外は、PreToolUseでタイムアウトを超えたAgent SDKのコールバックフックです。そのコールバックはツール呼び出しを止めます。
.claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}
JSONで破壊的なシェルコマンドを拒否します
コマンドが動く前に止めるには、BashにPreToolUseを使います。Anthropicのリファレンス例は、プロジェクトにblock-rm.shを保存し、chmod +xで実行可能にして、ifルールBash(rm *)で登録します。スクリプトは、コマンドにrm -rfが含まれるとpermissionDecision denyを出力します。それ以外は終了コード0で抜け、通常の権限フローを残し、コマンドは承認しません。
ifフィールドは権限ルールを一つ持ち、照合はbest-effortです。Claude CodeがBash入力のどのコマンドが動くか判断できないときは、パターンにかかわらずフックを実行します。リファレンスは、硬いallowまたはdenyにはフックではなく権限システムを使うよう述べています。フックが実際に返したpermissionDecision denyは、bypassPermissionsモードと–dangerously-skip-permissionsでもツールを止めます。allowの決定は、設定のdenyルールを緩めません。
PreToolUseでは、denyが呼び出しを取り消し、permissionDecisionReasonをClaudeへ送ります。askは通常のプロンプトを出します。allowは対話プロンプトを飛ばしますが、denyとaskのルールは適用されたままです。deferは非対話のclaude -p実行にあります。兄弟のPreToolUseフックは、その一つがdenyを返したあとも実行されます。マージされた決定の順序はdeny、defer、ask、allowです。
この登録はargsを空配列にします。これはexec formです。Claude Codeはシェルなしでスクリプトを直接起動します。Prettierフックのようにパイプや&&が必要ならargsを省略します。PowerShellが主シェルのWindowsでは、Bash|PowerShellにも一致させます。Bashだけのフックはそこで発火しません。リファレンスには、Remove-Item用の別のPowerShellハンドラがあります。
.claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm *)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
"args": []
}
]
}
]
}
}
denyを出力するスクリプトです
これを.claude/hooks/block-rm.shとして保存し、macOSとLinuxでchmod +xします。jqを使うので、jqがPATHにある必要があります。rm -rfではJSONを出力して終了コード0で抜けるので、呼び出しを拒否するのはJSONです。rm file.txtのようなより安全なコマンドでは、出力なしで終了コード0になります。この方式では終了コード0のままにします。終了コード2を足すと、あとでJSONをallowに直しても遮断されます。
PreToolUseはEndConversationも飛ばし、@で参照したファイルは見えません。Claude Codeはプロンプトを組み立てるあいだにその内容を入れるので、それらに対してReadフックは発火しません。
.claude/hooks/block-rm.sh
#!/bin/bash
# .claude/hooks/block-rm.sh
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Destructive command blocked by hook"
}
}'
else
exit 0
fi
終了コードの意味はイベントごとに違います
終了コード0は、フックが終了コードでは異議を出さないという意味です。PreToolUseでは通常の権限フローが残ります。フックがJSONの決定を出力するときも、使うのは終了コード0です。終了コード2は、止められるイベントを止めます。理由は、そのJSON決定が遮断するなら決定のreason、そうでなければstderrです。PermissionRequestは終了コード2を無視します。denyはhookSpecificOutput.decision.behaviorから来ます。Notificationフックは終了コードとstderrを無視します。PostToolUseは、すでに実行されたツールをやり直せません。そこでの終了コード2は、事後にstderrをClaudeへ見せます。
ほとんどのイベントで、コードだけで遮断するのは終了コード2だけです。有効な決定JSONがなければ、終了コード1は遮断しないエラーで、動作は進みます。WorktreeCreateは例外です。ゼロ以外の終了はワークツリー作成を失敗させます。止められるイベントで終了コード2とJSONを混ぜると、終了コード2は依然として遮断し、permissionDecisionのallowはそれを覆せません。Claude Code v2.1.214より前は、終了コード2とスキーマ検証に失敗したstdout JSONの組み合わせは遮断しませんでした。現在のリファレンスは、その組み合わせも依然として遮断し、Claude Codeはstderrを理由にすると述べています。
シェルプロファイルがJSONより先に文字を出すと、stdoutは{で始まらなくなり、Claude Codeはそれをプレーンテキストとして扱い、終了コード0では決定が無視されます。ガイドの修正は、対話シェルのときだけechoすることです。起動できないcommandフックと、PreToolUseでタイムアウトしたcommand、HTTP、MCP toolフックは、ツール呼び出しを通常の権限フローに残します。
- PreToolUseの終了コード0は、通常の権限フローを残します。
- 終了コード0とJSONは構造的な制御です。PreToolUseのpermissionDecision、PermissionRequestのdecision.behavior、StopやPostToolUseのようなイベントのトップレベルのdecision blockです。
- 終了コード2は、stdoutのJSONがallowでもPreToolUseを止めます。
- PostToolUseの終了コード2は、ツールがすでに実行されたあとにstderrをClaudeへ見せます。
- PermissionRequestとNotificationは終了コード2を無視します。
- 有効な決定JSONがない終了コード1は遮断しません。
これらのレシピのイベントについて、Anthropicのフックスリファレンスが述べる終了コード2とJSONの効果です。
| イベント | 終了コード2の効果 | 決定するJSONフィールド |
| — | — | — |
| PreToolUse | ツール呼び出しを止める | hookSpecificOutput.permissionDecision: allow、deny、ask、defer |
| PostToolUse | stderrをClaudeへ見せる。ツールはすでに実行済み | トップレベルのdecisionをblockに設定 |
| PermissionRequest | 無視される。権限フローは続く | hookSpecificOutput.decision.behavior: allowまたはdeny |
| Notification | 無視される | なし。このイベントに決定制御はない |
| Stop | ターンを終える代わりにClaudeの作業を続ける | トップレベルのdecisionをblockに設定 |
三つの周期がセッションの大半を覆います
フックスリファレンスは、よく使うイベントを三つの周期に分けます。SessionStartとSessionEndはセッションごとです。UserPromptSubmit、Stop、StopFailureはターンごとです。PreToolUseとPostToolUseはエージェントループ内のツール呼び出しで動き、EndConversationは両方を飛ばします。フォーマッタはPostToolUseに置きます。遮断はPreToolUseに置きます。デスクトップ通知はNotificationに置きます。NotificationはClaude Codeが通知を送るときに発火し、上で述べた遅延するpermission_promptも含みます。
同じイベントに一致するフックは、並列に最後まで実行されます。一つのdenyが兄弟のロガーを止めることはありません。複数のPreToolUseフックが同じツールへupdatedInputを返すと、最後に終わったものが効き、その順序は決定的ではありません。
AnthropicのNotificationコマンドを~/.claude/settings.jsonに追加し、/hooksで確認します。Linuxではnotify-send、WindowsではPowerShellのMessageBoxコマンドを使います。jqを入れ、プロジェクトの.claude/settings.jsonにPostToolUseのEdit|Write Prettierコマンドを追加します。protect-files.shを保存し、macOSまたはLinuxでchmod +xしてから、引用符で囲んだ$CLAUDE_PROJECT_DIRのパスでPreToolUseに登録します。block-rm.shも同じように保存し、ifをBash(rm *)、argsを空配列にして登録すると、permissionDecision denyを出力します。起動に失敗したりタイムアウトしたりするガードスクリプトはツール呼び出しを止めないので、権限システムにもハードなdenyを残します。