Claude Code Hooks実践ガイド|整形・通知の自動化【2026】
結論:Claude Code の Hooks は、ツール実行の前後やセッションの節目に任意のシェルコマンドを「確実に」走らせる仕組みです。LLM の気まぐれに頼らず、保存時フォーマット・lint・テスト・通知・ガードレールを settings.json に宣言するだけで自動化できます。
- 要点1:主要イベントは
PreToolUse/PostToolUse/UserPromptSubmit/Notification/Stop/SubagentStop/SessionStart/SessionEnd/PreCompactなど。matcherでツール名(Edit|Write等)を絞り込む。 - 要点2:hook は標準入力(stdin)で JSON を受け取り、終了コード 2 で操作をブロック、または stdout に JSON を返して挙動を制御する。
- 要点3:hook は信頼できる設定にだけ書く。任意のシェルを自動実行するため、機微情報を吐かず、危険なコマンドを安易に許可しないこと。
対象読者:Claude Code を日常的に回している開発者・エンジニア・PM。CLAUDE.md だけでは守りきれない「必ず実行したいルール」を仕組み化したい人。
今日できること:この記事のサンプルを ~/.claude/settings.json か .claude/settings.json に貼って、保存時フォーマットと通知を即座に有効化する。
正直に言うと、自分が最初に Hooks を触ったきっかけは「Claude Code に CLAUDE.md で『編集後は必ず prettier をかけて』と書いても、3 回に 1 回くらい忘れる」というイライラでした。指示はあくまで指示で、モデルが従うかどうかは確率の問題です。Hooks はここを決定論的(deterministic)に変えてくれる。プロンプトで「お願い」していたことを、設定で「強制」できるようになる。これが Hooks の本質です。
この記事では、公式ドキュメント(Hooks reference)で確認した現行仕様をベースに、保存時フォーマット・テスト・Slack 通知・保護ファイルのガードレールまで、実際に動く settings.json のサンプルを並べて解説します。

Claude Code Hooks とは|「お願い」を「強制」に変える仕組み
Hooks は、Claude Code のライフサイクルの特定のタイミングで実行される、ユーザー定義のシェルコマンドです。公式ドキュメントの表現を借りると「LLM が実行を選ぶことに頼るのではなく、特定のアクションが常に起きることを保証する」ためのもの。プロジェクトのルール強制、繰り返し作業の自動化、既存ツールとの統合に使います。
CLAUDE.md(メモリ)との違いは明確です。CLAUDE.md は「モデルへの追加指示」で、従うかどうかはモデル次第。Hooks は「ハーネス側が実行する処理」で、モデルの判断を介さずに必ず走ります。たとえば「.env を絶対に編集させたくない」というルールは、CLAUDE.md に書くより PreToolUse hook で終了コード 2 を返してブロックするほうが確実です。
設定が正しく登録されているかは、CLI で /hooks と打てば確認できます。/hooks はイベント一覧と各イベントの hook 件数を表示するブラウザで、選ぶと event・matcher・type・source file・command の詳細が見えます。ただし公式ドキュメント上、この /hooks メニューは読み取り専用です。追加・変更・削除は settings の JSON を直接編集する(または Claude に書いてもらう)必要があります。
主要なフックイベント一覧|どこで何が発火するか
イベント名・発火タイミングは現行版で頻繁に拡張されています。ここでは実務でまず使う、安定した主要イベントに絞って整理します(網羅的な最新リストは必ず公式リファレンスで確認してください)。
- PreToolUse:ツール実行の直前。許可制御やブロックに使う。
matcherはツール名。 - PostToolUse:ツール実行の直後。フォーマット・lint・テストなど後処理に使う。すでに実行済みなのでブロックはできない。
- UserPromptSubmit:ユーザーがプロンプトを送信した時。コンテキスト注入やバリデーションに使う。
- Notification:Claude が入力や許可を待っている時。デスクトップ通知などに使う。
- Stop:Claude が応答を終えようとした時。完了通知や「終わらせない」制御に使う。
- SubagentStop:サブエージェント(Task)が終了した時。
- SessionStart:セッション開始時。
matcherにcompact等を指定でき、コンテキスト注入に使う。 - SessionEnd:セッション終了時。後片付けやログに使う。
- PreCompact:コンテキスト圧縮(compaction)の直前。
ポイントは「ブロックできるイベント」と「できないイベント」が分かれていることです。公式ドキュメントによれば、終了コード 2 で実際に操作を止められるのは PreToolUse / UserPromptSubmit / Stop / SubagentStop など。PostToolUse や SessionStart / SessionEnd は「すでに起きてしまった or 関係ない」ためブロック不可です。整形やテストは PostToolUse、ガードレールは PreToolUse、と役割で使い分けるのが鉄則です。
設定ファイルの場所とJSON構造|matcherの書き方
Hooks は settings.json の "hooks" キーに定義します。配置場所はスコープごとに分かれており、後勝ち・マージのルールがあります。主に使うのは次の 3 つです。
~/.claude/settings.json— ユーザー全体(全プロジェクトで有効).claude/settings.json— プロジェクト共有(リポジトリにコミットしてチームで共有).claude/settings.local.json— プロジェクトローカル(通常 gitignore して個人設定に使う)
JSON の構造は「イベント名 → matcher グループ → hook ハンドラ」の 3 階層です。一番外側の "hooks" オブジェクトの中に、イベント名をキーとして配列を置きます。同じファイルに複数イベントを書く場合は、"hooks" オブジェクトを丸ごと置き換えるのではなく、イベント名を兄弟キーとして並べるのが正解です。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
],
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}
matcher の評価ルールは公式に定義されています。実務で覚えておくべきは次の 3 パターンです。
"*"・""・省略 → すべてにマッチ(毎回発火)。- 英数字・
_・|だけ → 完全一致、または|区切りのリスト。例:Bash/Edit|Write。 - それ以外の文字を含む → JavaScript の正規表現として評価。例:
^Notebook/mcp__memory__.*。
MCP サーバー由来のツールは mcp__<server>__<tool> という名前になるので、"mcp__memory__.*" のように正規表現で束ねて絞り込めます。matcher が対象にするのは PreToolUse / PostToolUse 系ではツール名、Notification や SessionStart 系ではイベント固有の文字列(compact など)である点に注意してください。
保存時フォーマット・lint・テストを自動化する設定例
一番リターンが大きいのが PostToolUse による後処理の自動化です。Claude がファイルを編集するたびに、フォーマッタ・lint・テストを「必ず」走らせる。ここを Hooks に任せると、レビューで「整形してください」と指摘する回数が体感で激減します。
以下は、編集系ツール(Edit|Write)の直後に Prettier をかける公式サンプルです。hook は stdin で JSON を受け取るので、jq で tool_input.file_path を取り出してフォーマッタに渡します。プロジェクト直下の .claude/settings.json に置きます。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
Python なら Prettier の代わりに ruff、Go なら gofmt に差し替えるだけです。lint やテストもまとめて回したいなら、コマンドをスクリプトに切り出すと読みやすくなります。次のスクリプトは、編集されたファイルの拡張子を見てフォーマッタと lint を出し分け、最後にテストを走らせる例です。
#!/bin/bash
# .claude/hooks/post-edit.sh
set -euo pipefail
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
case "$FILE_PATH" in
*.py)
ruff format "$FILE_PATH"
ruff check --fix "$FILE_PATH" || true
;;
*.ts|*.tsx|*.js)
npx prettier --write "$FILE_PATH"
npx eslint --fix "$FILE_PATH" || true
;;
*.go)
gofmt -w "$FILE_PATH"
;;
esac
exit 0
登録は同じく PostToolUse に置きます。スクリプトのパスには環境変数 $CLAUDE_PROJECT_DIR(全 hook プロセスで設定されるプロジェクトルート)を使うと、どこから起動しても解決できます。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/post-edit.sh"
}
]
}
]
}
}
テストを「編集のたび」に全部走らせると重いので、自分は重いテストは Stop イベント(応答が終わるタイミング)に寄せています。set -euo pipefail は入れていますが、フォーマッタや lint の失敗で編集まで止めたくない箇所は || true で握りつぶしている点に注意してください。PostToolUse は終了コード 2 を返しても操作はブロックできない(すでに編集済み)ので、ここで非ゼロを返すと「失敗情報がトランスクリプトに出る」だけになります。
Slack・デスクトップ通知をフックで飛ばす
長いタスクを Claude に任せて別作業に戻ると、いつ入力待ちになったか分かりません。Notification イベントを使うと、Claude が入力や許可を待った瞬間に通知を飛ばせます。macOS なら osascript、Linux なら notify-send を使う公式サンプルがそのまま動きます。
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}
チームで共有するなら Slack の Incoming Webhook に投げるのが定番です。タスク完了時に通知したいので、Stop イベント(Claude が応答を終えようとした時)に curl を仕込みます。Webhook URL は機微情報なので、コマンドに直書きせず環境変数経由で読むのが安全です。
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "curl -s -X POST -H 'Content-Type: application/json' -d '{\"text\":\"Claude Code: タスクが完了しました\"}' \"$SLACK_WEBHOOK_URL\""
}
]
}
]
}
}
セキュリティ上の注意として、hook のコマンドはあなたのシェル権限でそのまま実行されます。Webhook URL・API トークン・パスワードといった秘密情報を settings.json に平文で書かないこと。チームで共有する .claude/settings.json は特に危険です。秘密情報はシェルの環境変数や .env に置き、hook からは変数名で参照する。これは公式が繰り返し強調しているガードレールでもあります。
exit 2でブロックする|保護ファイルのガードレール実装
「.env や package-lock.json、.git/ 配下を Claude に絶対触らせたくない」というケースは、PreToolUse hook で終了コード 2を返してブロックします。終了コード 2 は「ブロッキングエラー」で、操作を中止しつつ stderr の内容を Claude に渡します。Claude はその理由を読んで方針を変えられるので、ただ止めるより建設的です。
公式サンプルは、保護対象パターンに一致したら exit 2 する独立スクリプト方式です。次の 3 ステップで導入できます。
- 保護スクリプトを
.claude/hooks/protect-files.shとして保存する。 chmod +x .claude/hooks/protect-files.shで実行権限を付与する(実行可能でないと Claude Code は走らせられない)。.claude/settings.jsonのPreToolUseにEdit|Writematcher で登録する。
スクリプト本体です。stdin の JSON から tool_input.file_path を取り出し、保護パターンに含まれていれば理由を stderr に書いて exit 2 します。
#!/bin/bash
# .claude/hooks/protect-files.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
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
登録側の settings はこうなります。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}
終了コードによる制御を整理しておきます。公式リファレンスの定義は明快です。
- 0(成功):正常に続行。stdout の JSON があれば解析される。
- 2(ブロッキングエラー):対象操作をブロック(
PreToolUse/UserPromptSubmit/Stop/SubagentStop等)。stderr が Claude に渡る。 - その他(1, 3 …):非ブロッキングエラー。実行は続行し、stderr はトランスクリプトに表示される。
より細かい制御をしたい場合は、stdout に JSON を返す方法もあります。PreToolUse なら hookSpecificOutput の中に permissionDecision(allow / deny / ask)を返して許可判断を上書きできます。たとえば次のように返すと、終了コードに頼らず「拒否+理由」を明示できます。
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "本番DBへの書き込みは禁止です"
}
}
このとき hook 側は終了コード 0で返す必要があります(exit 0 のときだけ stdout の JSON が解析されるため)。exit 2 のブロックは「素早く止める」、JSON 出力は「理由つきで丁寧に制御する」と覚えておくと使い分けが楽です。
compactionや誤発火でハマらないための注意点
実装していて自分が踏んだ/同僚がよく踏む落とし穴をまとめます。
- ❌ PostToolUse で exit 2 すれば編集を取り消せると思っている → ⭕ 取り消せない。PostToolUse は編集後に走るのでブロック不可。止めたいなら PreToolUse に書く。
- ❌
"matcher": ""を全イベントで使い回す → ⭕ ツール名で絞る。空 matcher は毎回発火するので、PostToolUse のフォーマッタが Bash 実行のたびに無駄に走る。Edit|Writeで限定する。 - ❌ compaction でプロジェクト規約が消えて Claude が npm を使い出す → ⭕ SessionStart の
compactmatcher で再注入。圧縮後に重要コンテキストを stdout で戻せる。 - ❌ Webhook URL を
settings.jsonに直書きしてコミット → ⭕ 環境変数経由。共有設定に秘密を置かない。
compaction 対策の SessionStart hook は地味に効きます。コンテキストが溢れて圧縮されると規約が抜け落ちることがあるので、圧縮後に毎回リマインドを差し込みます。stdout に書いた文字列がそのまま Claude のコンテキストに追加される仕組みです。
{
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "echo 'Reminder: npmではなくBunを使う。コミット前にbun testを実行する。現在のスプリント: 認証リファクタ。'"
}
]
}
]
}
}
設定変更そのものを監査したい場合は ConfigChange イベントが使えます。外部プロセスやエディタが設定・スキルファイルを変更した時に発火するので、変更内容を監査ログに追記しておくとコンプライアンス用途に便利です。
{
"hooks": {
"ConfigChange": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "jq -c '{timestamp: now | todate, source: .source, file: .file_path}' >> ~/claude-config-audit.log"
}
]
}
]
}
}
最後に運用面のコツ。hook を追加したら必ず /hooks で登録を確認し、わざと条件を発生させて動作テストする。フォーマッタが見つからない・jq が未インストール、といった環境差で静かに失敗することが多いので、stderr をトランスクリプトで一度は目視してください。Hooks は「確実に動く」のが価値なので、確実に動いているかの確認だけはサボらないのが結局いちばん速い、というのが正直なところです。
まとめ|Hooksで「守れるルール」を仕組みにする
Claude Code Hooks は、プロンプトでお願いしていた品質ルールを設定で強制できるレイヤーです。PostToolUse で保存時フォーマット・lint・テスト、Notification / Stop で通知、PreToolUse の exit 2 で保護ファイルのガードレール、SessionStart の compact matcher でコンテキスト再注入。この 4 つを押さえれば、日々の開発のブレがかなり減ります。
Hooks 単体でも強力ですが、MCP でツールを増やし、サブエージェントで並列化し、その全体に Hooks でガードレールを敷く、という組み合わせで真価を発揮します。次のステップとして、関連する実装ガイドもあわせてどうぞ。
- Claude Code MCP実践ガイド|設定から自作まで
- Claude Codeサブエージェント並列開発入門
- Claude Code法人導入セキュリティ完全チェックリスト
- Claude Code業務導入完全ガイド|エンジニア向け実装手順
著者プロフィール
佐藤傑(さとう・すぐる)。株式会社Uravation代表取締役。X(@SuguruKun_ai)フォロワー約10万人。100社以上の企業向けAI研修・導入支援。著書『AIエージェント仕事術』(SBクリエイティブ)。
出典
- Claude Code Hooks reference(Anthropic 公式ドキュメント)
- Automate actions with hooks(Anthropic 公式ガイド)
- Claude Code settings(Anthropic 公式ドキュメント)