AI駆動開発··30min read

Claude Codeのhooks活用ガイド【開発ワークフローを自動化する実践テクニック】

Claude Codehooks自動化AI駆動開発開発ワークフロー
Claude Codeのhooks活用ガイド【開発ワークフローを自動化する実践テクニック】

「Claude Codeでコードを編集するたびに手動でPrettierを実行している」「テストを走らせ忘れてバグを見逃した」「危険なファイルをAIが勝手に書き換えてしまった」——ShiftBの受講生相談会でも、こうした「自動化できるはずの手作業」に関する相談は非常に多いです。

実は、Claude Codeにはhooksというイベント駆動の自動化フレームワークがあり、これを正しく設定するだけでこれらの問題をすべて解決できます。hooksを使えば、ファイル編集後のフォーマット、コミット前のテスト実行、保護ファイルの書き換えブロックなどが100%確実に、毎回自動で実行されます。

僕はShiftBの校長として受講生142名以上のAI駆動開発をサポートし、自社プロダクトもすべてClaude Codeで開発しています。日常的にhooksを活用しており、導入前と比べてフォーマット修正の手作業が完全にゼロになり、保護ファイルの誤編集事故もゼロ件を維持しています。

この記事では、Claude Code hooksの基本概念から、設定方法、全イベント一覧、すぐに使える実践レシピ、設計のベストプラクティス、そしてShiftBで実際に使っている設定まで徹底解説します。記事を読み終えたら、すぐに自分のプロジェクトにhooksを導入できる内容です。

この記事を書いた人:立川修平(ぶべ)

  • ShiftB校長。受講生142名以上のAI駆動開発環境構築をサポート
  • Claude Codeのhooksを日常的に使い、開発ワークフローの自動化を実践・検証中
  • SNSフォロワー計3万人超。AI駆動開発の自動化テクニックを毎日発信

Claude Code hooksとは — イベント駆動の自動化フレームワーク

hooksの役割 — 「確率的」ではなく「確定的」な自動化

Claude Code hooksとは、Claude Codeのライフサイクル上の特定のタイミングで自動的にシェルコマンドやスクリプトを実行する仕組みです。たとえば「ファイルを編集した直後にPrettierを実行する」「危険なコマンドの実行前にブロックする」「Claudeが応答を完了したらデスクトップ通知を送る」といった処理を、設定ファイルに書くだけで実現できます。

ここで重要なのは、hooksはAIの判断に依存しない確定的な自動化だという点です。CLAUDE.mdに「ファイル編集後はPrettierを実行してください」と書いても、Claudeが毎回必ず実行してくれる保証はありません。LLMの出力は本質的に確率的だからです。一方、hooksはイベントが発生すれば100%確実に実行されるため、コードフォーマットやテスト実行などの「絶対にやるべき処理」に最適です。

hooksとCLAUDE.mdの違い — 使い分けの基準

Claude Codeの設定にはCLAUDE.mdもありますが、hooksとは役割が明確に異なります。

項目CLAUDE.mdhooks
実行の確実性確率的(AIが判断)確定的(100%実行)
設定方法Markdownファイルsettings.jsonのJSON設定
適用タイミングセッション全体を通じて常に特定イベント発生時のみ
用途コーディング規約、プロジェクトルールフォーマット実行、テスト実行、ブロック処理
AIの行動への影響「こう書いてください」と依頼「このタイミングでこのコマンドを実行」と強制

わかりやすく言えば、CLAUDE.mdは「AIへのお願い」であり、hooksは「システムへの命令」です。両者を組み合わせることで、AI駆動開発の品質と効率を最大化できます。

hooksが解決する3つの課題

ShiftBの受講生142名の開発データを分析すると、hooks導入前に最も多かった課題は以下の3つでした。

  1. フォーマットの不統一(受講生の74%が経験):Claudeがファイル編集後にPrettierを実行し忘れ、コミット時にlintエラーが発生
  2. 保護ファイルの誤編集(受講生の41%が経験):.envファイルやpackage-lock.jsonをAIが勝手に書き換えてしまう
  3. 通知の見逃し(受講生の68%が経験):Claudeが応答を完了しているのに気づかず、ターミナルを監視し続ける

hooksを導入すれば、これらはすべて設定ファイル数行で自動化できます。

4つのハンドラタイプ — command、http、prompt、agent

Claude Code hooksには4つのハンドラタイプがあります。

タイプ説明主な用途
commandシェルコマンドを実行。stdinでJSON受信、exit codeで結果を返すフォーマット実行、ファイルブロック、通知送信
httpHTTPエンドポイントにPOST送信外部サービスへのログ送信、Webhook連携
promptClaudeモデルに1回のプロンプトで判断させるコード品質チェック、タスク完了判定
agentサブエージェントを起動し、ファイル読み取りやコマンド実行で検証テスト結果の検証、コードベース全体のチェック

最もよく使うのはcommandタイプです。まずはこれを使いこなし、必要に応じて他のタイプを組み合わせていくのがおすすめです。

Claude Code hooksのイベントフロー — PreToolUse・PostToolUseの実行タイミング

hooksの仕組み — settings.jsonでの設定方法と実行タイミング

settings.jsonの基本構造

hooksの設定は、settings.jsonファイルのhooksプロパティに記述します。基本構造は以下のとおりです。

{
  "hooks": {
    "イベント名": [
      {
        "matcher": "フィルタパターン(正規表現)",
        "hooks": [
          {
            "type": "command",
            "command": "実行するシェルコマンド",
            "timeout": 600,
            "statusMessage": "実行中に表示するメッセージ"
          }
        ]
      }
    ]
  }
}

構造は3段階のネストになっています:

  1. イベント名(例:PostToolUse):どのタイミングで発火するか
  2. matcherグループ:そのイベントのうち、さらに条件を絞り込むフィルタ
  3. hookハンドラ:実際に実行するコマンドやスクリプト

設定ファイルの配置場所とスコープ

hooksの設定は複数の場所に配置でき、それぞれスコープが異なります。

配置場所スコープGit共有用途
~/.claude/settings.json全プロジェクト共通不可通知設定、個人的な自動化
.claude/settings.jsonプロジェクト単位可能チーム共通のフォーマッタ、保護ルール
.claude/settings.local.jsonプロジェクト単位不可(gitignore)個人的なプロジェクト設定
マネージドポリシー組織全体管理者制御企業のセキュリティポリシー

最も重要なポイント:チームで共有すべきhooks(フォーマッタ、保護ファイルルール等)は.claude/settings.jsonに配置し、Gitリポジトリにコミットしましょう。個人的な通知設定などは~/.claude/settings.jsonに配置します。

exit codeの仕組み — 0、2、その他の意味

commandタイプのhooksは、exit code(終了コード)で結果をClaude Codeに伝えます。この仕組みを理解することが、hooks活用の鍵です。

exit code意味動作
0成功アクションを続行。stdoutのJSONを解析。UserPromptSubmitやSessionStartでは、stdoutがClaudeのコンテキストに追加される
2ブロックアクションを阻止。stderrのテキストがClaudeにフィードバックされ、AIが行動を修正する
その他非ブロックエラーアクションは続行。stderrはverboseモード(Ctrl+O)でのみ表示

たとえば、PreToolUseフックでexit 2を返すと、そのツール実行がキャンセルされ、stderrに書いた理由がClaudeに伝わります。Claudeはその理由を読んで、別のアプローチを試みます。

入出力のデータフロー

hooksはClaude Codeとstdin、stdout、stderr、exit codeの4つのチャネルで通信します。イベントが発火すると、Claude CodeはイベントデータをJSON形式でhookスクリプトのstdinに渡します。

たとえば、ClaudeがBashコマンドを実行しようとした際のPreToolUseフックには、以下のようなJSONが渡されます:

{
  "session_id": "abc123",
  "cwd": "/Users/yourname/myproject",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test"
  }
}

このJSONをjqなどで解析し、条件に応じてexit codeを返すのがhookスクリプトの基本パターンです。

構造化JSON出力 — より高度な制御

exit codeだけでなく、stdoutにJSONを出力することでより細かい制御が可能です。たとえば、PreToolUseフックでツール実行を拒否しつつ理由を伝える場合:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "grepの代わりにrgを使ってください(パフォーマンス向上のため)"
  }
}

permissionDecisionには"allow"(許可)、"deny"(拒否)、"ask"(ユーザーに確認)の3つの値を指定できます。

AI時代のアプリ開発コース

ShiftBのAIシフトコースは、Claude Codeで本格的なWebアプリケーションを作りながらAI駆動開発を身につけるコースです。ただ作って終わりではなく、コードを読め、セキュリティに責任を持って提案でき、要件定義などの上流工程まで担えるところまで現役エンジニアがサポートします。

AIシフトコースを見る →

対応イベント一覧と使いどころ

主要イベント一覧 — 全26イベント

Claude Code hooksは26種類のイベントに対応しています。以下が全イベントの一覧です。

イベント名発火タイミングブロック可能
SessionStartセッション開始・再開時-
UserPromptSubmitプロンプト送信時(Claude処理前)
PreToolUseツール実行前
PermissionRequest権限ダイアログ表示時
PermissionDeniedツール実行が拒否された時-
PostToolUseツール実行成功後-
PostToolUseFailureツール実行失敗後-
NotificationClaude Codeが通知を送信した時-
SubagentStartサブエージェント起動時-
SubagentStopサブエージェント終了時
TaskCreatedTaskCreate経由でタスク作成時
TaskCompletedタスク完了マーク時
StopClaudeが応答を完了した時
StopFailureAPIエラーでターン終了時-
TeammateIdleエージェントチームのメンバーがアイドル状態になる時
InstructionsLoadedCLAUDE.mdやrulesファイルが読み込まれた時-
ConfigChange設定ファイルが変更された時
CwdChanged作業ディレクトリが変更された時-
FileChanged監視対象ファイルが変更された時-
WorktreeCreateworktreeが作成される時
WorktreeRemoveworktreeが削除される時-
PreCompactコンテキスト圧縮の前-
PostCompactコンテキスト圧縮の後-
ElicitationMCPサーバーがユーザー入力を要求した時
ElicitationResultMCP elicitationへのユーザー応答後
SessionEndセッション終了時-

最もよく使う5つのイベント

26イベントすべてを使う必要はありません。実務でよく使うのは以下の5つです。ShiftBの受講生の利用データでも、これら5つで全hooks設定の約90%を占めています。

  1. PostToolUse:ファイル編集後のフォーマット自動実行。最も利用頻度が高い
  2. PreToolUse:危険な操作のブロック。保護ファイルの書き換え防止など
  3. Notification:デスクトップ通知。Claudeの応答完了を見逃さない
  4. Stop:Claudeの応答完了時の検証。タスク完了チェックなど
  5. SessionStart:セッション開始時のコンテキスト注入。コンパクト後の情報復元など

matcherパターン — イベントをさらに絞り込む

matcherは正規表現パターンで、イベントの発火条件をさらに絞り込みます。イベントごとにmatcherが何にマッチするかが異なるので注意してください。

イベントmatcherの対象
PreToolUse / PostToolUseツール名Edit|WriteBashmcp__.*
SessionStartセッション開始方法startupresumecompact
Notification通知タイプpermission_promptidle_prompt
ConfigChange設定ソースuser_settingsproject_settings
FileChangedファイル名(basename).envrc.env
SessionEnd終了理由clearresumelogout

matcherを空文字列""にすると、そのイベントのすべての発火で実行されます。パフォーマンスのためにも、可能な限りmatcherで絞り込むことをおすすめします。

ifフィールド — ツール名と引数で絞り込む

matcherはツール名でしか絞り込めませんが、ifフィールドを使うとツール名と引数の両方で絞り込めます。 たとえば、Bashツールのうちgitコマンドだけにフックを適用する場合:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(git *)",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/check-git-policy.sh"
          }
        ]
      }
    ]
  }
}

ifフィールドはパーミッションルールと同じ構文を使います。"Bash(git *)"はgitで始まるBashコマンドのみにマッチし、"Edit(*.ts)"はTypeScriptファイルの編集のみにマッチします。

実践!開発ワークフロー自動化レシピ集

レシピ1:ファイル編集後にPrettierを自動実行

最も人気のあるhooks設定です。Claudeがファイルを編集するたびにPrettierが自動実行され、コードフォーマットが常に統一されます。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

この設定を.claude/settings.jsonに配置してGitにコミットすれば、チーム全員のClaude Codeで同じフォーマッタが自動実行されます。jqコマンドでstdinのJSONから編集されたファイルパスを抽出し、Prettierに渡しています。

注意jqがインストールされていない場合は、brew install jq(macOS)またはapt-get install jq(Linux)でインストールしてください。

レシピ2:保護ファイルの書き換えをブロック

.envやpackage-lock.json、.git/ディレクトリ内のファイルなど、AIが触るべきでないファイルへの編集をブロックします。

まず、hookスクリプトを作成します。

#!/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.jsonでこのスクリプトを登録します。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}

exit 2でブロックし、stderrに理由を出力すると、Claudeがその理由を読んで別のアプローチを試みます。たとえば「.envの直接編集はブロックされました」と伝われば、Claudeは.env.exampleの更新を提案するようになります。

レシピ3:デスクトップ通知で応答完了を知らせる

Claudeが応答を完了したり、権限の確認で入力待ちになったりした際に、デスクトップ通知を送ります。ターミナルを監視し続ける必要がなくなるため、マルチタスクが可能になります。

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Codeが入力を待っています\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

上記はmacOSの例です。Linuxの場合はnotify-sendコマンドに置き換えてください。この設定は個人用なので、~/.claude/settings.jsonに配置するのが適切です。

レシピ4:コンパクト後にコンテキストを復元する

Claude Codeのコンテキストウィンドウがいっぱいになると、会話が自動的に要約(コンパクト)されます。このとき、重要な情報が失われることがあります。SessionStartフックのcompact matcherを使えば、コンパクト後に重要な情報を再注入できます。

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'リマインダー: パッケージマネージャはBunを使う。コミット前にbun testを実行。現在のスプリント: 認証リファクタリング。'"
          }
        ]
      }
    ]
  }
}

echoの代わりにgit log --oneline -5などのコマンドを使えば、直近のコミット履歴を動的に注入することも可能です。

レシピ5:Stopフックでタスク完了を検証する

Claudeが「完了しました」と言っても、実際にはタスクが未完了な場合があります。promptタイプのStopフックを使えば、Claudeの応答を別のモデルが検証し、不十分であれば作業を継続させられます。

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "すべてのタスクが完了しているか確認してください。未完了のタスクがある場合は {\"ok\": false, \"reason\": \"残りのタスク内容\"} で応答してください。"
          }
        ]
      }
    ]
  }
}

promptタイプのフックは、Claudeモデル(デフォルトはHaiku)にyes/noの判断をさせる仕組みです。"ok": falseが返された場合、Claudeは作業を継続し、reasonに書かれた内容を次の指示として使います。

レシピ6:Bashコマンドをログに記録する

Claudeが実行したすべてのBashコマンドをログファイルに記録します。セキュリティ監査やデバッグに便利です。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt"
          }
        ]
      }
    ]
  }
}

レシピ7:権限プロンプトの自動承認

特定の権限プロンプト(たとえばExitPlanMode)を自動承認し、毎回の確認を省略できます。

{
  "hooks": {
    "PermissionRequest": [
      {
        "matcher": "ExitPlanMode",
        "hooks": [
          {
            "type": "command",
            "command": "echo '{\"hookSpecificOutput\": {\"hookEventName\": \"PermissionRequest\", \"decision\": {\"behavior\": \"allow\"}}}'"
          }
        ]
      }
    ]
  }
}

注意:matcherは必ず狭い範囲に限定してください。matcherを空にすると、すべての権限プロンプト(ファイル書き込みやシェルコマンドを含む)が自動承認されてしまいます。セキュリティリスクが極めて高いため、必ず特定のツール名を指定しましょう。

開発ワークフロー自動化レシピ — lint・テスト・コミットチェック・セキュリティ検査

hooks設計のベストプラクティスとアンチパターン

ベストプラクティス1:hookは小さく・単一責務に

1つのhookに複数の処理を詰め込まないでください。1つのhookには1つの責務が原則です。

悪い例:1つのhookに複数の処理

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path'); npx prettier --write $FILE && npx eslint --fix $FILE && npm test -- --related $FILE && echo 'All checks passed'"
          }
        ]
      }
    ]
  }
}

良い例:個別のhookに分離

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write",
            "statusMessage": "Formatting..."
          }
        ]
      },
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx eslint --fix",
            "statusMessage": "Linting..."
          }
        ]
      }
    ]
  }
}

分離するメリットは3つあります:デバッグが容易(どのhookが失敗したか特定しやすい)、並列実行(matcherグループ内のhooksは並列実行される)、個別の無効化が可能です。

ベストプラクティス2:タイムアウトを適切に設定する

hookのデフォルトタイムアウトはcommandで600秒(10分)promptで30秒agentで60秒です。ほとんどのhookは数秒で完了するはずなので、必要に応じてtimeoutを短く設定しましょう。

{
  "type": "command",
  "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write",
  "timeout": 30,
  "statusMessage": "Formatting with Prettier..."
}

ベストプラクティス3:statusMessageで進捗を伝える

hookが実行されている間、ユーザーには何が起きているかわかりません。statusMessageを設定すると、hookの実行中にスピナーメッセージが表示されます。

アンチパターン1:Stopフックの無限ループ

Stopフックの最も危険なバグは無限ループです。Claudeが応答を完了するたびにStopフックが発火し、フックが「まだ完了していない」と判定すると、Claudeが再度作業を行い、また応答を完了し、再びStopフックが発火し......という無限ループに陥ります。

対策:フック入力のJSONに含まれるstop_hook_activeフィールドを確認し、trueの場合は早期リターンしてください。

#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0  # Claudeの停止を許可
fi
# ... 通常のhookロジック

アンチパターン2:シェルプロファイルのecho文による出力汚染

Claude CodeはhookスクリプトをBashシェルで実行しますが、その際に~/.zshrc~/.bashrcが読み込まれます。これらのファイルに無条件のecho文が含まれていると、hookのJSON出力の前にその出力が挿入され、JSONのパースに失敗します。

対策:シェルプロファイルのecho文をインタラクティブシェルの場合のみ実行するよう修正してください。

# ~/.zshrc や ~/.bashrc で
if [[ $- == *i* ]]; then
  echo "Shell ready"
fi

アンチパターン3:matcherなしの広範なフック

PreToolUseやPermissionRequestでmatcherを空にするのは危険です。すべてのツール実行に対してフックが発火するため、パフォーマンスが低下し、意図しないブロックが発生する可能性があります。

対策:必ず特定のツール名をmatcherに指定してください。複数のツールを対象にする場合は、パイプ|で区切ります(例:Edit|Write|MultiEdit)。

AI時代のアプリ開発コース

ShiftBのAIシフトコースは、Claude Codeで本格的なWebアプリケーションを作りながらAI駆動開発を身につけるコースです。ただ作って終わりではなく、コードを読め、セキュリティに責任を持って提案でき、要件定義などの上流工程まで担えるところまで現役エンジニアがサポートします。

AIシフトコースを見る →

hooksとCLAUDE.md・カスタムコマンドの組み合わせ技

CLAUDE.md + hooks = 最強の開発環境

CLAUDE.mdとhooksは補完関係にあります。CLAUDE.mdは 「AIがコードを書くときのルール」を定義し、hooksは「AIがアクションを実行したときの自動処理」を定義します。

具体的な役割分担の例:

  • CLAUDE.md:「Tailwindはgap-10の書き方をし、gap-[40px]は使わない」
  • hooks:「ファイル編集後にPrettierとESLintを自動実行する」
  • CLAUDE.md:「APIルートではzodで入力バリデーションを行う」
  • hooks:「.envファイルの直接編集をブロックする」

CLAUDE.mdで「書き方のルール」を伝え、hooksで「絶対に守らせたいこと」を強制する。この組み合わせにより、AIの出力品質と開発プロセスの両方を同時に向上できます。

カスタムスラッシュコマンドとの連携

Claude Codeのカスタムスラッシュコマンド(.claude/commands/とhooksを組み合わせると、さらに強力なワークフローを構築できます。

たとえば、/deployというカスタムコマンドを作成し、デプロイ前のチェックをhooksで自動化するパターン:

# .claude/commands/deploy.md
デプロイ前チェックリストを実行し、問題がなければVercelにデプロイしてください:

1. npm run build でビルドが成功するか確認
2. npm test でテストがすべて通るか確認
3. git status で未コミットの変更がないか確認
4. すべてOKなら vercel --prod を実行

この/deployコマンドと組み合わせて、Stopフックで 「デプロイが完了したらSlackに通知する」といった処理を追加できます。

実践的な組み合わせ例:フルスタック開発ワークフロー

以下は、ShiftBの受講生がフルスタック開発で使っている典型的な組み合わせです。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write",
            "statusMessage": "Formatting..."
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'パッケージマネージャ: bun / テスト: bun test / lint: bun lint'"
          }
        ]
      }
    ],
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Codeが待機中です\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

MCPサーバーのツールにもhooksを適用する

MCPツールはmcp__サーバー名__ツール名という命名規則を使います。matcherで正規表現を使えば、特定のMCPサーバーの全ツールにフックを適用できます。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__github__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"GitHub tool called: $(jq -r '.tool_name')\" >&2"
          }
        ]
      }
    ]
  }
}

これにより、GitHub MCPサーバー経由のすべてのツール呼び出しをログに記録したり、特定の操作をブロックしたりできます。

ShiftBで実際に使っているhooks設定を公開

ShiftBのプロジェクト共通hooks設定

ShiftBではすべてのプロジェクトで以下のhooks設定を共通で使っています。この設定は.claude/settings.jsonに配置し、Gitにコミットしています。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null || true",
            "timeout": 30,
            "statusMessage": "Auto-formatting..."
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}

ポイント:Prettierコマンドの末尾に2>/dev/null || trueを付けています。これにより、Prettierが対応していないファイル形式(画像など)を編集した場合でもエラーにならず、hookが正常に完了します。

ShiftBの保護ファイルスクリプト

ShiftBのプロジェクトで実際に使っている保護ファイルスクリプトです。受講生にもこのスクリプトを共有しており、導入後の保護ファイル誤編集事故はゼロ件を達成しています。

#!/bin/bash
# .claude/hooks/protect-files.sh
# ShiftBプロジェクト共通の保護ファイルスクリプト

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# 空パスの場合はスキップ
if [ -z "$FILE_PATH" ]; then
  exit 0
fi

# 保護対象パターン
PROTECTED_PATTERNS=(
  ".env"
  ".env.local"
  "package-lock.json"
  "bun.lockb"
  "pnpm-lock.yaml"
  "yarn.lock"
  ".git/"
  "node_modules/"
  ".next/"
  "supabase/migrations/"
)

for pattern in "${PROTECTED_PATTERNS[@]}"; do
  if [[ "$FILE_PATH" == *"$pattern"* ]]; then
    echo "BLOCKED: '$FILE_PATH' is a protected file (matched pattern: '$pattern')." >&2
    echo "If you need to modify this file, please ask the user for explicit permission." >&2
    exit 2
  fi
done

exit 0

ShiftBのグローバルhooks設定

僕個人の~/.claude/settings.jsonには、以下のグローバルhooksを設定しています。

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Codeが入力を待っています\" with title \"Claude Code\"'"
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo '=== コンパクト後のリマインダー === \n- パッケージマネージャ: bun\n- テスト: bun test\n- Tailwind: gap-10形式を使い、gap-[40px]は禁止\n- 日本語でコミュニケーション'"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt",
            "async": true
          }
        ]
      }
    ]
  }
}

特にBashコマンドのログ記録は"async": trueにしています。非同期実行にすることで、ログの書き込みを待たずにClaude Codeの処理が続行されるため、パフォーマンスへの影響がゼロです。

hooks導入の効果 — ShiftB受講生のデータ

ShiftBの受講生142名のうち、hooksを導入した67名のデータを分析しました。

指標hooks導入前hooks導入後改善率
フォーマット修正の手作業1セッションあたり平均6.2回0回-100%
保護ファイルの誤編集月平均1.3件0件-100%
Claudeの応答見逃し1日平均3.8回0.2回-95%
lint/テストエラーの検出コミット後に気付く(42%)編集直後に検出(98%)+56pt
1セッションの生産性(体感)--平均+35%向上

特にフォーマット修正と保護ファイルの誤編集がともに完全にゼロになった点が大きいです。これらは「hooksがなければ毎回手動でやるか、事故が起きてから対処するしかなかった」処理であり、hooksの確定的な自動化の威力を示しています。

よくある質問(FAQ)

Q1. hooksの設定を変更したら、Claude Codeの再起動は必要ですか?

通常は不要です。Claude Codeにはファイルウォッチャーが組み込まれており、settings.jsonの変更を自動的に検出します。ただし、まれに検出されない場合があるため、その場合はセッションを再起動してください。変更後は/hooksコマンドで正しく反映されているか確認するのがおすすめです。

Q2. hookスクリプトのデバッグ方法は?

3つの方法があります。まず、Ctrl+Oでverboseモードをオンにすると、hookの出力がトランスクリプトに表示されます。次に、claude --debugで起動すると、どのhookがマッチしたか、exit codeは何だったかの詳細が表示されます。最後に、手動でテストする方法があります。サンプルJSONをパイプでスクリプトに渡して動作を確認します。

echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh
echo $?  # exit codeを確認

Q3. hooksはCLAUDE.mdの代わりになりますか?両方必要ですか?

両方必要です。CLAUDE.mdとhooksは役割が明確に異なります。CLAUDE.mdは「AIがコードを書くときのルールや知識」を定義し、hooksは「特定のイベントで確実に実行する処理」を定義します。たとえば、「Tailwindでgap-10を使う」はCLAUDE.mdに書き、「ファイル編集後にPrettierを実行する」はhooksに設定します。CLAUDE.mdなしでhooksだけ使っても、AIの出力品質は向上しません。

Q4. hooksのcommandが「command not found」になります。どうすればいいですか?

hookスクリプトは非インタラクティブシェルで実行されるため、PATHの設定が通常のターミナルと異なる場合があります。絶対パスを使うのが確実です。たとえばnpx prettierの代わりに/usr/local/bin/npx prettierを使うか、$CLAUDE_PROJECT_DIR環境変数を使ってプロジェクトルートからの相対パスでスクリプトを参照してください。また、スクリプトファイルに実行権限が付与されているかも確認してください(chmod +x ./my-hook.sh)。

Q5. hookを一時的に無効にする方法はありますか?

settings.jsonに"disableAllHooks": trueを追加すると、すべてのhooksを一括で無効化できます。特定のhookだけを無効にしたい場合は、そのhookの設定を削除するか、matcherに絶対にマッチしないパターン(例:"matcher": "DISABLED")を設定する方法があります。

Q6. 複数のPreToolUseフックが同じツールに対して設定されている場合、どの結果が優先されますか?

最も制限的な結果が優先されます。複数のフックが同じツールにマッチした場合、すべてのフックが並列実行され、1つでもdenyを返せばツール実行はキャンセルされます。1つがaskを返せば、他がallowでもユーザーに確認が表示されます。すべてのフックのadditionalContextはマージされてClaudeに渡されます。

Q7. hooks導入で最初に設定すべきものは何ですか?

ShiftBの受講生データでは、以下の3つを最初に設定した受講生の満足度が最も高いという結果でした:①Notification(デスクトップ通知)、②PostToolUseのPrettier自動実行、③PreToolUseの保護ファイルブロック。この3つだけで開発体験が大幅に改善されるため、まずはこの3つから始めることをおすすめします。設定時間も合計10分程度です。

まとめ

Claude Code hooksは、AI駆動開発を「確率的なアシスタント利用」から「確定的な自動化ワークフロー」に進化させるフレームワークです。この記事のポイントをまとめます。

  • hooksはイベント駆動の確定的な自動化:CLAUDE.mdの「お願い」とは異なり、100%確実に実行される
  • settings.jsonで簡単に設定:イベント名・matcher・コマンドの3段階構造
  • まず3つから始める:通知、Prettierの自動実行、保護ファイルのブロック
  • hooksは小さく単一責務に:1つのhookに複数の処理を詰め込まない
  • CLAUDE.mdと組み合わせて最強の環境を構築:「ルール」と「自動化」の両輪で品質を担保
  • .claude/settings.jsonはGitにコミット:チーム全体で自動化ワークフローを共有

ShiftBの受講生データでは、hooksを導入しただけでフォーマット修正の手作業が完全にゼロになり、保護ファイルの誤編集事故もゼロになりました。設定にかかる時間はわずか10分です。

まだhooksを設定していない方は、まずNotificationフック(デスクトップ通知)から始めてみてください。ターミナルを監視する時間がなくなるだけで、AI駆動開発の体験が劇的に変わります。

この記事をAIと深掘りする

要約・疑問の解消に。記事のタイトル・URL・参照元を入れた質問文が自動で入力されます。

AUTHOR

立川修平(ぶべ)

ShiftB 校長 / bubekichi inc. 代表

ShiftBを運営する株式会社bubekichiの代表。経理系SaaSの企業でエンジニアを経験後、独立・起業。複数スタートアップでリードエンジニアを務めながら、SNS発信がきっかけで2024年にShiftBを立ち上げる。現在は自社サービスも複数展開中。

RELATED ARTICLES

関連記事

COURSE

AI時代のアプリ開発コース

ShiftBのAIシフトコースは、Claude Codeで本格的なWebアプリケーションを作りながら学ぶコースです。コードを読め、セキュリティに責任を持って提案でき、要件定義などの上流工程まで担える状態を目指します。まずは無料相談会でご相談ください。