Claude Code

Claude Codeのフック(Hooks)とは?設定方法と使い方・例を解説

INTERCREATE JAPAN 編集部公開 2026.10.15

この記事でわかること

  • フックとは何か、CLAUDE.md・スキル・権限設定との違い
  • 最初のフック(通知)を設定する手順と、設定ファイルの置き場所
  • イベント・matcher・if・終了コードなど、フックのしくみ
  • 自動整形・ファイルの保護・危険なコマンドの阻止など、よく使う例
  • Windowsでの書き方(PowerShell・パスの区切り・jqの代わり)
  • 安全に使うための注意と、フックが動かないときの確かめ方
目次
  1. Claude Codeのフックとは
  2. ひとことで言うと
  3. CLAUDE.mdとの違い
  4. スキル・サブエージェント・権限設定との使い分け
  5. フックでできること
  6. フックでできないこと
  7. 設定のしかた・置き場所
  8. 設定ファイルの形
  9. 最初のフック:Claudeが待っているときに通知する
  10. 設定ファイルの置き場所
  11. Claudeに書いてもらう
  12. 設定したフックを確かめる
  13. フックのしくみ
  14. イベント:動くタイミング
  15. matcher:どんなときに動かすか
  16. ifフィールド:引数まで見て絞る
  17. フックが受け取る情報
  18. フックの返し方:終了コード
  19. JSONで細かく返す
  20. Claudeに情報を足す
  21. 複数のフックが動くとき
  22. タイムアウト
  23. よく使う例
  24. 例1:編集のたびに自動整形する
  25. 例2:重要なファイルを書き換えさせない
  26. 例3:危険なコマンドを止める
  27. 例4:圧縮のあとも前提を思い出させる
  28. 例5:プロンプトに今の状況を足す
  29. 例6:実行したコマンドを記録する
  30. 例7:設定の変更を記録する
  31. 例8:終わる前にテストを確認する
  32. 例9:決まった許可だけを自動で承認する
  33. Windowsでの書き方
  34. どのシェルで動くか
  35. PowerShellでフックを書く
  36. 例:PowerShellで書いたファイル保護
  37. パスの区切りに注意する
  38. jqが入っていないとき
  39. .cmdや.batのコマンドを呼ぶとき
  40. Git Bashの設定ファイルが出力を邪魔する
  41. Windowsでよくあるつまずき
  42. フックの種類・非同期実行
  43. promptフック:判断をモデルに任せる
  44. agentフック:確かめてから判断する
  45. httpフック:外部のサービスに任せる
  46. mcp_toolフック:MCPサーバーの処理を呼ぶ
  47. バックグラウンドで動かす(async)
  48. スキルやサブエージェントに入れる
  49. 安全に使うための注意
  50. フックは自分の権限で動く
  51. 他人のリポジトリのフックに注意する
  52. 権限設定との関係
  53. フックが失敗したとき、動作を止めるか
  54. フックを無効にする・制限する
  55. 動かないときの確かめ方
  56. よくある質問
  57. まとめ

「テストを実行してからコミットして」と毎回頼んでいるのに、たまに忘れられる。「.envだけは絶対に書き換えてほしくない」。「作業が終わったら、ほかの作業をしていても気づきたい」。Claude Codeを使い込むほど、こうした悩みが出てきます。

これらを解決するのが、Claude Codeの「フック(Hooks)」です。フックは、Claude Codeの動きの決まったタイミングで、自分で用意したコマンドを自動的に実行させる機能です。Claudeに「お願い」するのではなく、条件が合えば毎回必ず動くところが、CLAUDE.mdなどの指示との大きな違いです。

この記事では、フックの考え方から、最初のフックを作る手順、イベントや条件の指定のしかた、よく使う例、Windowsでの書き方、安全に使うための注意までを順に解説します。内容は2026年10月時点の公式ドキュメントをもとにしています。機能は更新が続いているため、細かい仕様は公式ページもあわせて確認してください。

Claude Codeのフックとは

ノートPCの画面を見ながら、作業の流れの途中に確認の関所を置く設計をメモに書く開発担当者

Claude Codeには、Claudeが作業を進める途中で、「プロンプトを受け取ったとき」「ファイルを編集する直前」「ファイルを編集した直後」「応答を終えたとき」といった節目があります。フックは、こうした節目に自分で決めた処理をはさみ込むための仕組みです。

ひとことで言うと

公式ドキュメントでは、フックを「ユーザーが定義したシェルコマンド、HTTPエンドポイント、MCPツールの呼び出し、LLMへのプロンプト、サブエージェントを、Claude Codeの動作の決まったタイミングで自動実行するもの」と説明しています。いちばんよく使うのは、シェルコマンド(パソコン上で動かすコマンド)です。

公式ガイドの言葉を借りると、フックは「LLMが実行するかどうかを選ぶのではなく、特定の処理が必ず行われる」という決まった動作を作るものです。ファイルを書き換えたら整形ツールを動かす、特定のファイルへの書き込みを止める、といった「毎回同じことをしたい」場面に向いています。

なお、フックは端末(ターミナル)だけの機能ではありません。公式ドキュメントでは、IDE拡張、デスクトップアプリ、クラウド上のセッションでも、同じイベントでフックが動くと説明されています。

CLAUDE.mdとの違い

「お願い」と「仕組み」の違いを整理します。CLAUDE.mdに「コミット前にテストを実行する」と書いておけば、Claudeはたいていその通りに動きます。ただし、守るかどうかはClaudeの判断に任されています。フックは、判断を挟まずに実行されます。

CLAUDE.mdフック
性質Claudeへの指示・前提(読んで、従うかをClaudeが判断する)条件が合えば、必ず実行されるコマンドなど
書くもの文章設定(JSON)とスクリプト
向いている内容プロジェクトの前提、コーディングの方針、用語整形・通知・ファイルの保護・記録など、例外なく行いたい処理
Claudeに見えるか毎回読み込まれて、Claudeの文脈に入る出力の多くは画面にも出ない。ブロックの理由や、追加した情報などだけがClaudeに渡る

公式ガイドでも、毎回のセッションで同じ前提を渡したいだけなら、フックではなくCLAUDE.mdを使うよう案内されています。フックは、「前提を伝える」仕組みではなく、「動作そのものを決める」仕組みです。

あわせて読みたいCLAUDE.mdとは?Claude Codeに覚えさせる指示書の書き方・置き場所・例CLAUDE.md(クロード エムディー)は、Claude Codeが作業のたびに読み込む指示書のファイルです。置き場所と読み込まれる順番、/initでの作り方、書くべき内容とテンプレート、長くなったときの分け方、AGENTS.mdとの関係、効かないときの確認方法を公式ドキュメントをもとに解説します。

スキル・サブエージェント・権限設定との使い分け

Claude Codeには、フックのほかにも動作をカスタマイズする仕組みがあります。迷ったときは、次の表で「何をしたいか」から選びます。

やりたいこと向いている仕組み理由
プロジェクトの前提や方針を毎回伝えたいCLAUDE.md文章で書くだけでよく、スクリプトが要らない
特定の作業の手順書を持たせたいスキル必要なときだけ読み込まれる手順書。Claudeが判断して使う
調査などを別の担当に任せたいサブエージェント独立した文脈で作業し、結果の要約だけが戻る
特定の操作を例外なく止めたい・必ず実行したいフックClaudeの判断に関係なく動く
ツールの許可・拒否の基本ルールを決めたい権限設定(permissions)公式は、確実な許可・拒否にはフックより権限設定を使うよう案内している

最後の行は見落としやすい点です。公式ドキュメントでは、フックのifによる絞り込みは「ベストエフォート(できる範囲での絞り込み)」とされており、厳格に許可・拒否を決めたいなら、フックではなく権限の仕組みを使うように書かれています。フックは、その上で「権限設定では表現できない条件」や「実行の前後の処理」を足すものと考えると整理しやすくなります。

フックでできること

公式ガイドで紹介されている用途を、目的別にまとめます。

やりたいこと使うイベント具体例
通知するNotificationClaudeが許可待ちになったとき、デスクトップに通知を出す
コードを自動で整えるPostToolUseファイルを編集するたびに整形ツール(Prettierなど)を実行する
特定のファイルを守るPreToolUse.envやpackage-lock.jsonの編集を止める
危険なコマンドを止めるPreToolUserm -rfを含むコマンドを実行前に拒否する
情報をClaudeに足すSessionStart・UserPromptSubmit現在のブランチや「今週はデプロイ凍結」などをプロンプトに添える
圧縮後に前提を思い出させるSessionStart(compact)会話の圧縮で消えかねない規約を、圧縮のたびに再注入する
操作を記録するPostToolUse・ConfigChange実行したコマンドや、設定ファイルの変更をログに残す
終わる前に確認させるStop全部のタスクが終わっているか、テストが通るかを確かめてから止まらせる
許可の確認を省くPermissionRequest決まった種類の許可ダイアログだけを自動で承認する

フックでできないこと

フックは強力ですが、できないこともあります。公式ドキュメントに書かれている制約を、先に押さえておきましょう。

  • スラッシュコマンドやツール呼び出しは起動できない:コマンド型のフックは、標準出力・標準エラー出力・終了コードでやりとりします。/で始まるコマンドや、ツールの呼び出しを起こすことはできません
  • PostToolUseは取り消せない:ツールが実行された後に動くため、実行済みの操作を元に戻すことはできません。止めたいなら、実行前のPreToolUseで止めます
  • Stopはいつでも動くわけではない:Claudeの応答が終わるたびに動きますが、ユーザーが中断したときは動きません。APIエラーで終わったときは、別のStopFailureというイベントが動きます
  • 追加した順番に動くわけではない:同じイベントに合致するフックは並列で実行されます。順序に依存する処理は、1つのスクリプトにまとめます

「フック」と呼ばれる別の仕組みについて

公式ドキュメントには、プラグインが「JavaScriptの関数としてフックを登録する」仕組み(mod)も出てきますが、これは本記事で扱う設定ファイルのフックとは別の機能です。この記事で説明するのは、設定ファイルに書くコマンド型などのフックです。

設定のしかた・置き場所

PCで設定ファイルを開き、通知の設定を書き込んでいる会社員の手元

フックは、settings.jsonという設定ファイルに書きます。専用のアプリや画面はありません。まず設定ファイルの形を理解し、いちばん簡単な「通知」のフックを実際に動かしてみましょう。

設定ファイルの形

フックの設定は、3段の入れ子になっています。公式ドキュメントでは、次の3つを選ぶ形だと説明されています。

  1. イベントを選ぶどのタイミングで動かすか。PreToolUse(ツールの実行前)やStop(応答の終了時)などです
  2. matcher(条件)を決めるそのイベントのうち、どんな場合だけ動かすか。「Bashツールのときだけ」のように絞ります
  3. ハンドラー(実行する処理)を書く動かすコマンドなどを指定します。1つの条件に、複数を並べられます

形にすると、次のようになります。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "実行したいコマンド"
          }
        ]
      }
    ]
  }
}

この例では、PostToolUse(ツールが成功した後)というイベントの、EditかWrite(ファイルの編集・書き込み)のときだけ、commandに書いたコマンドを実行します。"type": "command"は「シェルコマンドを実行する」という意味です。

JSONの書き間違いに注意

設定ファイルはJSONという形式で、最後の項目の後ろにカンマを付けたり、コメントを書いたりするとエラーになります。公式ガイドでも、フックが一覧に出てこないときの確認項目として「末尾のカンマやコメントは使えない」と挙げられています。すでにhooksがある場合は、全体を置き換えるのではなく、イベント名を同じ階層に追加します。

最初のフック:Claudeが待っているときに通知する

公式ガイドの最初の例も、通知のフックです。Claudeが許可や入力を待っているときに通知が出るので、ターミナルを見張らなくても済みます。リスクが小さく、動いたかどうかも分かりやすいため、最初の1本に向いています。

  1. 設定ファイルを開くユーザー全体の設定ファイル~/.claude/settings.jsonを開きます。なければ作ります。Windowsでは、ユーザーフォルダの中の.claudeフォルダにあるファイルです
  2. Notificationフックを書く下のJSONを、自分のOSに合わせて追記します
  3. /hooksで確認するClaude Codeの入力欄に/hooksと入力し、Notificationの下に追加したフックが出ていることを確かめます
  4. 試すClaudeに許可が必要な作業(ファイルの編集など)を頼み、別のウィンドウに切り替えます。許可待ちになると通知が出ます

Macの場合の設定は次のとおりです(公式ガイドの例です)。

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

Windows(PowerShell)の場合は、commandの中身が次のように変わります。通知の形は、画面の隅に出る通知ではなく、ダイアログボックス(小さな確認窓)になります。

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')\""
          }
        ]
      }
    ]
  }
}

Linuxの場合はnotify-send 'Claude Code' 'Claude Code needs your attention'を使います。

OS使うコマンド通知が出ないときの確認
Macosascript通知は標準の「スクリプトエディタ」経由で出る。そのアプリに通知の許可がないと、エラーも出ずに失敗する。システム設定の「通知」で許可する
Linuxnotify-sendデスクトップの通知デーモンが必要。サーバーやSSH接続、多くのコンテナでは使えない。入っていなければ、ディストリビューションに合ったパッケージを入れる
Windowspowershell.exeダイアログが、ターミナルの後ろに開くことがある。PowerShellで同じコマンドを直接実行して確かめる。WSLで動かす場合は、powershell.exeがPATHに通っている必要がある

"matcher": ""と空にしているため、あらゆる種類の通知で動きます。許可待ちのときだけにしたいならpermission_prompt、60秒ほど放置されたときだけならidle_promptを指定します。

matcherの値動くタイミング
permission_promptツールの使用許可などを求められ、その確認が約6秒待たれているとき
idle_promptClaudeの応答が終わって約60秒たち、その間に入力がないとき
auth_success認証が完了したとき
elicitation_dialogMCPサーバーが入力フォームを開き、約6秒入力がないとき

ターミナルの画面では、許可待ちの通知は「しばらく入力していないとき」だけ出ます。許可のダイアログが出た瞬間に動かしたい場合は、後述するPermissionRequestを使います。

設定ファイルの置き場所

フックをどの設定ファイルに書くかで、効く範囲と、チームで共有できるかが変わります。

置き場所効く範囲共有
~/.claude/settings.json自分のすべてのプロジェクトできない(自分のパソコンだけ)
.claude/settings.jsonそのプロジェクトだけできる(リポジトリにコミットして共有)
.claude/settings.local.jsonそのプロジェクトだけできない(Claude Codeが保存する際はgitの管理対象外にされる)
管理者のポリシー設定組織全体できる(管理者が制御)
プラグインの hooks/hooks.jsonプラグインを有効にしている間できる(プラグインに同梱)
スキルのフロントマターそのスキルを呼び出した後、セッションの間できる(スキルのファイルに書く)
サブエージェントのフロントマターそのサブエージェントが動いている間できる(サブエージェントのファイルに書く)

使い分けの目安は、次のとおりです。通知のように自分の好みの問題は~/.claude/settings.json、整形やファイルの保護のようにチームで共通のルールは.claude/settings.jsonに書いてコミットします。

設定ファイルを直接書き換えた場合は、通常は自動で反映されます。数秒たっても/hooksに出てこないときは、セッションを開き直します。

クラウド上のセッションでは、手元のユーザー設定は使われない

公式ドキュメントによると、クラウド上のセッションは、パソコンの~/.claude/settings.jsonを読みません。クラウドでも同じフックを使いたい場合は、プロジェクトの.claude/settings.jsonに書いてリポジトリに入れておきます。

Claudeに書いてもらう

JSONやスクリプトを自分で書かなくても、Claude Codeに頼んで作ってもらうことができます。公式ガイドにも「やりたいことを説明すれば、Claudeがフックを書いてくれる」と書かれています。たとえば次のように頼みます。

ファイルを編集するたびに、Prettierで自動整形するフックを、このプロジェクトの.claude/settings.jsonに追加してください。

Claudeが書いた設定は、そのまま信用せずに/hooksで内容を確かめ、実際に動かして試してから使い始めます。フックはあなたのユーザー権限でコマンドを実行するため、中身を自分で理解してから有効にするのが基本です(詳しくは後述の「安全に使う」の節で説明します)。

設定したフックを確かめる

Claude Codeで/hooksと入力すると、設定されているフックを一覧で見られます。この画面は読み取り専用で、フックの追加や変更はできません。主な用途は次の3つです。

  • 各フックがどこから来ているか(ユーザー設定・プロジェクト設定・ローカル設定・プラグインなど)の確認
  • 選んだフックの実行内容と、定義されているファイルの確認
  • 末尾の「All events」から、設定のないものも含めた全イベントの確認

一覧に出てくれば、設定ファイルの読み込みと書式は正しいということです。実際に動いたかどうかは、フックの効果(ファイルが整形された、通知が出た、など)で確かめます。成功したフックは、通常は会話の画面に何も表示しません。

フックのしくみ

ホワイトボードに作業の流れと関所を書き込みながら、同僚に説明する担当者

ここからは、設定を自分で書いたり、動かないときに原因を探したりするために必要な、フックの内部の考え方を説明します。「いつ動くか(イベント)」「どれを動かすか(matcher・if)」「何を受け取るか(入力)」「どう返すか(終了コードと出力)」の4つが押さえどころです。

イベント:動くタイミング

公式の一覧には、2026年10月時点で30を超えるイベントがあります。動く頻度で見ると、次の3つに分かれます。

  • セッションごと:SessionStart(開始・再開)、SessionEnd(終了)
  • ターン(1回のやりとり)ごと:UserPromptSubmit(プロンプトの受け取り)、Stop(応答の終了)、StopFailure(APIエラーでの終了)
  • ツールの呼び出しごと:PreToolUse(実行前)、PostToolUse(実行後)

まず、よく使うイベントを表にまとめます。「ブロックできるか」は、終了コード2を返したときに、その動作を止められるかどうかです。

イベント動くタイミングブロックできるか主な使い道
SessionStartセッションの開始・再開時できない前提や現在の状況をClaudeに渡す、環境変数の設定
UserPromptSubmitプロンプトを送信した直後、Claudeが処理する前できるプロンプトの検査、情報の追加
PreToolUseツールを実行する直前できる危険な操作・保護したいファイルの阻止、入力の書き換え
PermissionRequestツールの使用許可を求める直前終了コードではできない(JSONで拒否する)許可ダイアログの自動承認・自動拒否
PostToolUseツールが成功した直後できない(実行済み)自動整形、ログ、結果への注釈
PostToolUseFailureツールが失敗した直後できない失敗の記録、Claudeへの補足
StopClaudeが応答を終えたときできる(続きの作業をさせる)完了条件の確認、テストの実行
NotificationClaude Codeが通知を出すときできないデスクトップ通知、外部サービスへの転送
PreCompact会話の圧縮の直前できる圧縮を止める
SessionEndセッションが終わるときできない後片付け、記録

そのほかのイベントは次のとおりです。必要になったときに引けるよう、一覧で載せておきます。

イベント動くタイミングブロックできるか
Setup--init-onlyでの起動、または-pモードでの--init・--maintenance。CIなどでの1回限りの準備用できない
UserPromptExpansion入力したコマンドがプロンプトに展開されるときできる
PermissionDenied自動モードでツールの呼び出しが拒否されたときできない
PostToolBatch並列のツール呼び出しがすべて終わった後、次のモデル呼び出しの前できる
MessageDisplayClaudeのメッセージの文章が表示されるときできない(表示だけ)
SubagentStart / SubagentStopサブエージェントの起動時・終了時Startはできない、Stopはできる
TaskCreated / TaskCompletedタスクの作成時・完了扱いにするときどちらもできる
StopFailureAPIエラーでターンが終わったときできない
TeammateIdleエージェントチームのメンバーが待機に入るときできる
InstructionsLoadedCLAUDE.mdや.claude/rules/のファイルが読み込まれたときできない
ConfigChangeセッション中に設定ファイルが変わったときできる(管理者設定を除く)
CwdChanged作業ディレクトリが変わったときできない
DirectoryAdded/add-dirなどで作業ディレクトリが追加されたときできない
FileChanged監視しているファイルがディスク上で変わったときできない
WorktreeCreate / WorktreeRemoveワークツリーの作成時・削除時どちらも、失敗させることはできる
PostCompact会話の圧縮が終わった後できない
PreModelSwitch / PostModelSwitchモデルの切り替えの前後Preはできる、Postはできない
Elicitation / ElicitationResultMCPサーバーが入力を求めたとき、ユーザーが応答した後どちらもできる

新しいイベントは追加されていくため、公式の「Hooks reference」の一覧を確認するのが確実です。

matcher:どんなときに動かすか

matcherを書かない(空にする)と、そのイベントが起きるたびにフックが動きます。matcherを書くと、「特定のツールのときだけ」「特定の始まり方のときだけ」のように絞れます。

何に対して照合されるかは、イベントごとに決まっています。

イベントmatcherが絞り込む対象値の例
PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest、PermissionDeniedツールの名前Bash、Edit|Write、mcp__.*
SessionStartセッションの始まり方startup(新規)、resume(再開)、clear、compact(圧縮後)、fork
SessionEnd終わった理由clear、resume、logout、prompt_input_exit、other
Notification通知の種類permission_prompt、idle_prompt
SubagentStart、SubagentStopサブエージェントの種類Explore、Plan、自作の名前
PreCompact、PostCompact圧縮のきっかけmanual、auto
ConfigChange設定ファイルの種類user_settings、project_settings、local_settings、policy_settings、skills
FileChanged監視するファイル名.envrc|.env
UserPromptSubmit、Stop、PostToolBatch などmatcherには対応していない(常にすべての発生で動く)

matcherの値の書き方には、次のルールがあります。

matcherの値どう評価されるか例
"*"、空文字、または省略すべてに一致そのイベントのすべての発生で動く
英数字・_・-・空白・,・|だけ完全一致の文字列、または|か,で区切った複数の候補BashはBashツールだけ。Edit|WriteとEdit, Writeは、どちらも2つのツールのどちらか
それ以外の文字を含むJavaScriptの正規表現(部分一致)^Notebookは名前がNotebookで始まるツール。mcp__memory__.*はmemoryサーバーの全ツール

注意点が2つあります。まず、matcherは大文字・小文字を区別します。bashと書いてもBashツールには一致しません。次に、正規表現になる場合は部分一致のため、Edit.*はEditとNotebookEditの両方に一致します。完全一致にしたいときは^Edit$のように書きます。

MCPサーバーのツールは、mcp__サーバー名__ツール名という名前になります。サーバーの全ツールを対象にするには、mcp__memory__.*のように.*を付けます。mcp__memoryだけでは、完全一致の文字列として扱われ、どのツールにも一致しません。

matcherに対応していないイベントでは、無視される

UserPromptSubmitやStopのようにmatcherに対応していないイベントにmatcherを書いても、エラーにはならず、黙って無視されます。意図した絞り込みが効いていないことに気づきにくいので、イベントの対応表を確認してください。

ifフィールド:引数まで見て絞る

matcherはツールの名前だけを見ます。「Bashツールのうち、gitで始まるコマンドのときだけ」のように、ツールの引数まで見て絞りたいときは、各フックにifを書きます。ifには、権限設定と同じ書き方(Bash(git *)、Edit(*.ts)など)を使います。

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

こうしておくと、Bashツールがgit系のコマンドを実行するときだけスクリプトが起動します。ほかのコマンドでは、スクリプトのプロセス自体が起動しないため、無駄な待ち時間も生まれません。

Bashのコマンドに対するifの判定は、次のようになります(公式の表より)。

ifの書き方Claudeが実行するコマンド動くか理由
Bash(git *)git push動くコマンド名が一致する
Bash(git *)npm test && git push動くつながったコマンドを個別に調べ、git pushが一致する
Bash(git *)FOO=bar git push動く先頭の環境変数の代入は無視して照合する
Bash(git *)echo $(git log)動く$()やバッククォートの中も調べ、git logが一致する
Bash(git *)echo $(date)動かないどのコマンドもgit *に一致しない
Bash(git push *)echo $(date)動くコマンド名以上を指定したパターンは、$()などがあると安全側に倒して動かす

最後の行のように、Claude Codeが「どのコマンドが実行されるか判断できない」場合は、パターンに関係なくフックを動かします。ifはあくまで「ベストエフォートの絞り込み」です。確実な拒否・許可は、権限設定で行います。

そのほか、ifには次の制約があります。

  • 書けるルールは1つだけです。&&や||、リスト形式はありません。複数の条件がある場合は、フックを条件ごとに分けて書きます
  • 使えるのは、ツール系のイベント(PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest、PermissionDenied)だけです。それ以外のイベントに書くと、そのフックは動きません
  • ファイルツールのEdit(src/**)は、作業ディレクトリ直下のsrcだけに一致します。どの深さのsrcにも一致させたいときはEdit(**/src/**)と書きます

フックが受け取る情報

コマンド型のフックは、起動時に、イベントの情報をJSONで標準入力から受け取ります。たとえばClaudeがnpm testを実行しようとしたとき、PreToolUseのフックには次のような情報が渡されます。

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

どのイベントにも共通して入っている主な項目と、イベントごとに増える項目は次のとおりです。

項目内容入っているイベント
session_idセッションのIDすべて
cwdフックが呼ばれたときの作業ディレクトリすべて
hook_event_name起きたイベントの名前すべて
transcript_path会話の記録ファイルのパスすべて
permission_mode現在の権限モード(default、plan、acceptEdits、auto、dontAsk、bypassPermissions)多くのイベント
tool_name、tool_input使うツールの名前と、その引数PreToolUse、PostToolUseなど
tool_responseツールが返した結果PostToolUse
prompt送信されたプロンプトの文UserPromptSubmit
last_assistant_message、stop_hook_activeClaudeの最後の応答の文、Stopフックによって続行中かどうかStop
sourcestartup・resume・clear・compact・forkSessionStart
message、notification_type通知の文と種類Notification

ファイルを扱うツール(Write・Edit・Read)のtool_input.file_pathは、常に絶対パスで渡されます。~や相対パスはClaude Code側で展開されてから渡されるため、「~と書いて検査をすり抜ける」ことはできません。

Bashのスクリプトからこれらを取り出すには、jqというJSON整形コマンドをよく使います。jq -r '.tool_input.command'と書けば、実行しようとしているコマンドを取り出せます。jqがない環境では、PythonやNode.jsで読むこともできます(Windowsの場合は後述します)。

フックの返し方:終了コード

フックが処理を終えるときの終了コード(プログラムが終わるときに返す数字)で、Claude Codeに次の動作を伝えます。

終了コード意味起きること
0成功(異議なし)動作はそのまま進む。PreToolUseで0を返しても「許可した」ことにはならず、通常の許可の流れが続く。UserPromptSubmit・SessionStartなどでは、標準出力の文字がClaudeの文脈に追加される
2ブロックブロックできるイベントでは、その動作を止める。標準エラー出力に書いた文が、止めた理由になる
それ以外(1など)非ブロックのエラー動作はそのまま進む。「hook error」という通知が会話の記録に出る

終了コード1ではブロックされない

Unixの慣習では、1は「失敗」を表す数字です。しかしClaude Codeでは、1は非ブロックのエラーとして扱われ、動作は止まりません。公式ドキュメントも「ポリシーを強制するフックにはexit 2を使う」と警告しています。スクリプトの途中でエラーが起きて1が返ると、「止めるつもりが素通り」になるので注意してください。

ブロックするスクリプトの形は、次のようになります。

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q "drop table"; then
  echo "Blocked: dropping tables is not allowed" >&2  # 標準エラー出力 = Claudeへのフィードバック
  exit 2                                              # 2 = ブロック
fi

exit 0  # 0 = 判断なし。通常の許可の流れに進む

標準エラー出力に書いた文は、Claudeに「ツールが拒否された理由」として返ります。Claudeはその文を読んで、別のやり方を探します。理由は具体的に書くほど、Claudeが次の行動を選びやすくなります。

終了コード2の効果はイベントによって違います。止められないイベントでは、2を返しても動作は止まりません。

イベント終了コード2を返したときの動き
PreToolUseツールの呼び出しを止める。理由はClaudeに渡る
UserPromptSubmitプロンプトを止める。Claudeには届かない。理由はユーザーに表示される
StopClaudeが止まるのを防ぎ、会話を続けさせる。理由はClaudeへの指示になる
PostToolUse止められない(ツールは実行済み)。標準エラー出力がClaudeに渡る
PermissionRequest効果がない。拒否するにはJSONのdecisionを使う
Notification、SessionEnd止められない。標準エラー出力は無視、またはユーザーにのみ表示される
SessionStart止められない。標準エラー出力はユーザーにのみ表示される
ConfigChange設定の変更を止める(管理者設定を除く)

JSONで細かく返す

終了コードだけでは、「止める」か「何もしない」かしか表せません。もう少し細かく制御したいときは、終了コード0のまま、標準出力にJSONを1つだけ出力します。公式ドキュメントは、1つのフックで「終了コードで伝える」か「JSONで伝える」かのどちらかに統一するよう案内しています。

たとえばPreToolUseでは、次のようにして拒否し、理由をClaudeに返せます。

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "grepではなくrgを使ってください"
  }
}

PreToolUseのpermissionDecisionには、4つの値があります。

値動き
allow許可のダイアログを省略する。ただし、権限設定のdenyやaskのルールは引き続き評価される
denyツールの呼び出しを取り消し、理由をClaudeに返す
ask通常どおり、ユーザーに許可を確認する
defer非対話モード(-p)で、呼び出しを保留して後で再開する。Agent SDKなどを使う連携向け

PreToolUseのupdatedInputを使うと、ツールの引数を実行前に書き換えることもできます。このとき、入力オブジェクト全体が置き換わるため、変更しない項目も一緒に返す必要があります。

JSONで使える、全イベントに共通のフィールドもあります。

フィールド内容
continuefalseにすると、フックの後でClaudeが処理を完全に止める。イベント固有の判断より優先される
stopReasoncontinueがfalseのとき、ユーザーに表示するメッセージ
systemMessageユーザーに表示する警告メッセージ
terminalSequenceデスクトップ通知・ウィンドウのタイトル・ベルなどのために、Claude Codeに代わりに出力させる端末の制御文字列

イベントによって、判断を返す方法が違います。使うイベントのものを選びます。

イベント判断の返し方主なフィールド
UserPromptSubmit、PostToolUse、PostToolUseFailure、Stop、SubagentStop、ConfigChange、PreCompact最上位のdecision"decision": "block"とreason
PreToolUsehookSpecificOutputpermissionDecision、permissionDecisionReason
PermissionRequesthookSpecificOutputdecision.behavior(allow / deny)
SessionStart、SubagentStart文脈の追加だけhookSpecificOutput.additionalContext
Notification、SessionEnd など判断はできないログや後片付けなど、副作用の用途

フィールドを置く階層を間違えると、黙って無視される

たとえばpermissionDecisionは、最上位ではなくhookSpecificOutputの中に置きます。階層を間違えるとJSONとしては読めるため、エラーが出ずに効果だけがなくなります。additionalContextも同じです。確かめるにはclaude --debugで起動し、ログに「Hook JSON output had unrecognized keys」という行がないかを探します。

Claudeに情報を足す

UserPromptSubmit・SessionStart・PostToolUseなどでは、additionalContextに文字列を入れて、Claudeに情報を渡せます。画面には表示されず、Claudeが次のリクエストで読みます。

{
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "Current branch: release-42. Deploy freeze until Friday."
  }
}

公式ドキュメントは、ここに書く文を「事実の記述にする」よう勧めています。「デプロイ先は本番です」「このリポジトリはbun testを使います」のような書き方です。システムへの命令のような書き方をすると、Claudeがプロンプトインジェクション(外部の文章に混ざった不正な指示)を疑い、内容をユーザーに見せるだけで文脈として使わないことがある、という説明です。

また、additionalContext・systemMessage・標準出力の文字列は、それぞれ10,000文字が上限です。超えた分はファイルに保存され、Claudeには保存先のパスと先頭2,000文字ほどの抜粋だけが渡されます。Claudeに必ず見せたい内容は、上限の範囲に収めます。

複数のフックが動くとき

同じイベントで複数のフックが一致した場合は、すべて並列に実行され、全員が終わってから結果が合成されます。1つがdenyを返しても、ほかのフックの実行は止まりません。

PreToolUseで判断が食い違った場合は、deny > defer > ask > allowの順で、より制限の強い方が採用されます。additionalContextは、すべてのフックの分がまとめてClaudeに渡されます。

たとえば、Bashの実行前に「コマンドをログに追記するフック」と「rm -rfを止めるフック」を並べて書いたとします。rm -rf /tmp/buildが実行されようとすると、両方が並列で動きます。ログには記録され、ブロックするフックが2を返すので、コマンド自体は実行されません。「止めたものも記録に残る」わけです。

もう1つ重要なのは、同じ引数を書き換えるフックを複数置かないことです。複数のPreToolUseがupdatedInputで同じツールの入力を書き換えると、最後に終わったものが採用されます。並列で動くため、どれが最後になるかは決まっていません。

タイムアウト

フックには実行時間の上限があります。timeoutフィールドに秒数を書いて、フックごとに変更できます。

種類既定の上限
command・http・mcp_tool10分(UserPromptSubmitなど一部のイベントは30秒、MessageDisplayは10秒)
prompt30秒
agent60秒
SessionEnd(種類を問わず)全フックで合計1.5秒(timeoutを長く設定した場合は、最長60秒まで延びる)

上限に達したフックは取り消され、その出力は捨てられます。PreToolUseでタイムアウトしても、ツールの呼び出しはブロックされず、通常の許可の流れで続行されます。「止めるフックが固まったら止まるだろう」とは期待できないので、止めることが目的のフックは、確実に素早く終わるように作ります。

よく使う例

PCの画面に並んだ複数の設定例を見比べて、自分の環境に合うものを選ぶ会社員

ここでは、公式ガイドで紹介されている例を中心に、実際の業務でも使いやすいものを集めました。設定は、そのまま.claude/settings.json(そのプロジェクトだけ)か、~/.claude/settings.json(自分の全プロジェクト)に追記して使えます。

Bashスクリプトの例は、Mac・Linux、またはWindowsのGit Bashを前提にしています。Windowsでの書き方は、次の節で改めて説明します。

Windowsでは、matcherに注意

Git for Windowsを入れていないWindowsでは、Claude CodeがBashツールを登録せず、シェルコマンドはPowerShellツールで実行されます。公式ドキュメントによると、この環境でmatcherがBashだけのフックは、一度も動きません。コマンドを調べるフックは、Bash|PowerShellと書くと両方に対応できます。詳しくは次の節で説明します。

例イベント目的難しさ
1. 編集のたびに自動整形するPostToolUseコードの書式をそろえるやさしい
2. 重要なファイルを書き換えさせないPreToolUse事故の防止ふつう
3. 危険なコマンドを止めるPreToolUse事故の防止ふつう
4. 圧縮後に前提を思い出させるSessionStart前提の維持やさしい
5. プロンプトに今の状況を足すUserPromptSubmit情報の追加やさしい
6. 実行したコマンドを記録するPostToolUse履歴の確保やさしい
7. 設定の変更を記録するConfigChange監査やさしい
8. 終わる前にテストを確認するStop品質の確保ふつう
9. 特定の許可を自動で承認するPermissionRequest確認の手間を減らすやや難しい

例1:編集のたびに自動整形する

Claudeがファイルを編集するたびに、整形ツールのPrettierを実行して、書式をそろえます。手作業で整える手間と、Claudeに「整形して」と毎回頼む手間がなくなります。

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

matcherにEdit|Writeを指定しているため、ファイルを書き換えるツールのときだけ動きます。コマンドの流れは、(1)jqで標準入力のJSONから編集されたファイルのパスを取り出し、(2)xargsでそのパスをPrettierに渡して整形する、というものです。

試すには、Claudeにシングルクォートの文字列を含む行をJavaScriptのファイルに追加してもらい、ファイルを開きます。Prettierの標準設定なら、ダブルクォートに書き換わっています。成功したフックは会話に何も表示しないので、整形されたかどうかはファイルで確認します。

Bashコマンドで書き換えられたファイルは対象外

Claudeは、シェルコマンド(sedやリダイレクトなど)でもファイルを変更できます。Edit|Writeに絞ったフックは、Bashコマンド経由の変更を検知できません。ファイルの変更を漏れなく見たい場合は、1ターンの終わりに作業ツリーを調べるStopフックにするか、FileChangedイベントを使います。

例2:重要なファイルを書き換えさせない

パスワードなどを書いた.envや、勝手に変わっては困るpackage-lock.json、.git/の中身を、Claudeが編集できないようにします。止めた理由はClaudeに返るので、Claudeは別の方法を探してくれます。

スクリプトは別ファイルに書きます。.claude/hooks/protect-files.shとして、次の内容を保存します。

#!/bin/bash
# protect-files.sh

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

# Windowsのバックスラッシュ区切りを、下のパターンに合うよう置き換える
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

MacやLinuxでは、スクリプトに実行権限を付けます。

chmod +x .claude/hooks/protect-files.sh

そして、設定ファイルに登録します。$CLAUDE_PROJECT_DIRは、プロジェクトのルートを指す変数です。作業ディレクトリが変わっても、同じスクリプトを指せます。

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

試すには、Claudeに.envへコメントを1行足すよう頼みます。編集は実行されず、Blocked:で始まるメッセージがClaudeに返ります。

パターンを書き換えれば、仕事のファイルにも使えます。たとえば「確定済み」というフォルダの中を書き換えさせたくないなら、PROTECTED_PATTERNSに"/確定済み/"のように追加します。ファイルの場所が、プログラムのコードではなく文書や表計算データであっても、同じ考え方で守れます。

この種のフックは、権限モードを変えても効く

PreToolUseフックは、権限モードの確認よりも前に動きます。公式ドキュメントによると、denyを返すフックは、bypassPermissionsモードや--dangerously-skip-permissionsで起動した場合でもツールをブロックします。利用者が権限モードを変えても突破できない、組織のルールを作れるということです。

例3:危険なコマンドを止める

Bashツールでrm -rfが実行されようとしたら、止めます。matcherでBashツールに絞り、さらにifでrmで始まるコマンドに絞ると、関係のないコマンドでスクリプトが起動しません。

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

スクリプトは、標準入力のコマンドにrm -rfが含まれていたら、JSONでdenyを返します。

#!/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

1つ目の例との違いは、終了コード2ではなく、JSONでdenyを返している点です。どちらでも止められます。理由を細かく返したい、updatedInputなども組み合わせたい、といった場合はJSONが便利です。

気をつけたいのは、rm -rfという文字列だけを探す方法では、rm -frやrm -r -fのような書き方をすり抜けてしまうことです。確実に守りたいときは、スクリプトの判定を丁寧に作るか、権限設定の拒否ルールと併用します。

例4:圧縮のあとも前提を思い出させる

会話が長くなると、Claude Codeは古いやりとりを要約して文脈を空ける「圧縮」を行います。この際に、大事な前提が抜け落ちることがあります。SessionStartにcompactのmatcherを付けると、圧縮のたびに前提を再び渡せます。

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.'"
          }
        ]
      }
    ]
  }
}

SessionStartフックの標準出力は、そのままClaudeの文脈に追加されます。上の例は固定の文を出していますが、git log --oneline -5のように、実行のたびに変わる出力にもできます。

毎回のセッション開始時に渡したいだけの内容は、フックではなくCLAUDE.mdに書くのが標準的な方法です。フックが向いているのは、「圧縮の直後」のようにタイミングが決まっているものです。

例5:プロンプトに今の状況を足す

プロンプトを送るたびに、現在のブランチ名など、実行時点でしか分からない情報を添えます。UserPromptSubmitフックの標準出力は、Claudeの文脈に追加されるため、echoだけで実現できます。

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "echo \"Current branch: $(git branch --show-current)\""
          }
        ]
      }
    ]
  }
}

このイベントには、matcherがありません。書かなくても、プロンプトのたびに動きます。実行時間が長いと、プロンプトの処理全体が遅くなります。公式の既定では、UserPromptSubmitのタイムアウトは30秒です。軽いコマンドだけを置きます。

UserPromptSubmitは、自分が入力したプロンプトだけでなく、スケジュールされたタスクの実行や、バックグラウンドのサブエージェントからの報告のときにも動きます。

例6:実行したコマンドを記録する

Claudeが実行したBashコマンドを、すべてログファイルに追記します。あとから「何を実行させたのか」を確認できます。

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

PostToolUseは実行後に動くので、記録されるのは実際に実行されたコマンドです。PreToolUseに置けば、実行前の記録になり、ブロックされたコマンドも残ります。

記録のためのフックは、async(後述)にしておくと、Claudeの作業を待たせずに済みます。

例7:設定の変更を記録する

ConfigChangeイベントは、セッション中に設定ファイルやスキルのファイルが変更されたときに動きます。外部のエディタやプロセスによる変更を、記録できます。コンプライアンス(法令・社内規則への対応)で、設定の変更履歴が必要な場合などに使います。

{
  "hooks": {
    "ConfigChange": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "jq -c '{timestamp: now | todate, source: .source, file: .file_path}' >> ~/claude-config-audit.log"
          }
        ]
      }
    ]
  }
}

matcherで、user_settings・project_settings・local_settings・policy_settings・skillsのどれかに絞れます。変更を止めたいときは、終了コード2を返すか、{"decision": "block"}を返します。ただし、管理者のポリシー設定(policy_settings)の変更は、止められません。

例8:終わる前にテストを確認する

Stopは、Claudeが応答を終えるたびに動きます。ここで終了コード2を返すと、Claudeは止まらずに作業を続けます。標準エラー出力の文が、続きの作業の指示になります。テストが通るまで終わらせない、という使い方ができます。

#!/bin/bash
# .claude/hooks/check-tests.sh
INPUT=$(cat)

# すでにStopフックによる続行中なら、そのまま終わらせる(無限ループを防ぐ)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0
fi

if ! npm test --silent >/dev/null 2>&1; then
  echo "npm test fails. Check the failures and fix them before finishing." >&2
  exit 2
fi

exit 0

設定は、Stopにスクリプトを登録します。matcherには対応していません。

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check-tests.sh",
            "args": []
          }
        ]
      }
    ]
  }
}

スクリプトの最初でstop_hook_activeを確認しているのは、止める判断が堂々巡りにならないようにするためです。公式ドキュメントによると、Claude Codeは、Stopフックが8回続けて続行を指示すると、次のブロックを無視してターンを終わらせます。この回数は、Claudeがツールを呼び出すたびにリセットされます。フック自身が確認を入れておけば、無駄な繰り返しを避けられます。

回数の上限は、環境変数CLAUDE_CODE_STOP_HOOK_BLOCK_CAPで変更できます。

例9:決まった許可だけを自動で承認する

Claudeは、計画を示し終えるとExitPlanModeというツールを呼び出して、実行に進んでよいか尋ねます。この確認を毎回省きたい場合は、PermissionRequestフックで自動承認します。終了コードではなく、JSONでallowを返します。

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

承認されると、ダイアログが表示される代わりに、会話の記録に「Allowed by PermissionRequest hook」と出ます。

matcherはできるだけ狭くする

公式ドキュメントは「matcherを.*にするか空にすると、ファイルの書き込みやシェルコマンドを含む、すべての許可を自動承認してしまう」と警告しています。自動承認のフックは、必ずツール名を1つに絞って書いてください。

同じ仕組みで、権限モードを切り替えることもできます。updatedPermissionsにsetModeを入れると、そのセッションだけacceptEditsなどに切り替えられます。bypassPermissionsは、そのセッションがもともとバイパスモードを使える状態で始まっている場合にだけ有効です。

あわせて読みたいClaude Code Skillsとは?作り方・使い方と、業務で役立つ例を解説Claude Code Skills(スキル)は、よく使う手順や決まりごとをSKILL.mdにまとめ、Claudeに必要なときだけ読み込ませる仕組みです。CLAUDE.mdとの違い、最初のスキルの作り方、置き場所、設定項目、チームでの共有方法、うまく動かないときの直し方、安全に使う注意点まで、2026年10月時点の公式ドキュメントをもとに解説します。

Windowsでの書き方

Windowsのノートパソコンで、PowerShellの画面を見ながらスクリプトを確認する会社員

フックの解説は、MacやLinuxのBashを前提にしたものが多く、そのままWindowsに貼り付けると動かないことがあります。公式ドキュメントにはWindows向けの書き方も載っているので、Windowsで使う場合に引っかかりやすい点をまとめます。

どのシェルで動くか

コマンド型のフックのcommandは、シェル(コマンドを解釈して実行するソフト)に渡されて動きます。Windowsでは、次のように選ばれます。

状況フックのシェルシェルコマンドを実行するツール
Git for Windowsを入れているGit Bash(shellに"powershell"を指定すれば、PowerShellにもできる)Bashツール(PowerShellツールが有効な場合は、PowerShellツールも)
Git for Windowsを入れていないPowerShellPowerShellツールのみ(Bashツールは登録されない)

PowerShellは、PowerShell 7以降のpwsh.exeが見つかればそれを使い、なければWindows PowerShell 5.1のpowershell.exeを使います。

2行目の「Bashツールが登録されない」ことが、見落としやすいポイントです。この環境で、matcherにBashだけを書いたフックは、一度も動きません。コマンドの内容を調べるフックでは、Bash|PowerShellと書いて、両方のツールをカバーします。

PowerShellでフックを書く

個別のフックをPowerShellで動かすには、shellに"powershell"を指定します。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "shell": "powershell",
            "command": "Write-Host 'File written'"
          }
        ]
      }
    ]
  }
}

PowerShellのスクリプトファイル(.ps1)を実行させる場合は、次の2通りの書き方があります。

書き方設定の例特徴
引数リスト(args)で書く"command": "powershell.exe" と "args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "${CLAUDE_PROJECT_DIR}/.claude/hooks/x.ps1"]シェルを介さず直接起動する。引用符やスペースの問題が起きにくい。公式の例もこの形
シェル形式で書く"shell": "powershell" と "command": "& \"$env:CLAUDE_PROJECT_DIR\\.claude\\hooks\\x.ps1\""パイプなどシェルの機能が使える

引数リストの例にある-NoProfileは、PowerShellのプロファイル(起動時に読み込まれる設定)を読まないことを指定する引数です。起動が速くなります。-ExecutionPolicy Bypassは、ローカルのスクリプトファイルを実行できるようにする指定です。

PowerShellでは、変数の書き方に注意

プロジェクトのルートを指す変数は、PowerShellのシェル形式では${CLAUDE_PROJECT_DIR}か$env:CLAUDE_PROJECT_DIRと書きます。Bashと同じ$CLAUDE_PROJECT_DIRと書くと、PowerShellは「未定義のローカル変数」と解釈して空になり、スクリプトのパスが途中で切れてしまいます。また、${CLAUDE_PROJECT_DIR}はダブルクォートの中では使えますが、シングルクォートの中では展開されません。

例:PowerShellで書いたファイル保護

前の節の「重要なファイルを書き換えさせない」フックを、PowerShellで書くと次のようになります。jqは使わず、PowerShellが標準で持つConvertFrom-JsonでJSONを読みます。.claude/hooks/protect-files.ps1として保存します。

# .claude/hooks/protect-files.ps1
$callInput = [Console]::In.ReadToEnd() | ConvertFrom-Json
$path = $callInput.tool_input.file_path -replace '\\', '/'

$patterns = @('.env', 'package-lock.json', '/.git/')

foreach ($pattern in $patterns) {
  if ($path -like "*$pattern*") {
    [Console]::Error.WriteLine("Blocked: $path matches protected pattern '$pattern'")
    exit 2
  }
}

exit 0

設定ファイルには、次のように登録します。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe",
            "args": [
              "-NoProfile",
              "-ExecutionPolicy",
              "Bypass",
              "-File",
              "${CLAUDE_PROJECT_DIR}/.claude/hooks/protect-files.ps1"
            ]
          }
        ]
      }
    ]
  }
}

危険なコマンドを止めるフックでは、BashツールとPowerShellツールの両方を対象にします。ifは1つのルールしか書けないため、ツールごとにハンドラーを分けます。公式ドキュメントの例を簡略にすると、次の形です。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash|PowerShell",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(rm *)",
            "command": "powershell.exe",
            "args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1"]
          },
          {
            "type": "command",
            "if": "PowerShell(Remove-Item *)",
            "command": "powershell.exe",
            "args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1"]
          }
        ]
      }
    ]
  }
}

パスの区切りに注意する

WindowsではClaude Codeが渡すファイルのパスが、C:\project\src\index.tsのようにバックスラッシュ区切りになります。Git Bashでフックを動かしていても同じです。

そのため、スクリプトの中で/src/のようにスラッシュ区切りで比較すると、どのパスにも一致せず、ブロックしたいつもりの操作が素通りします。公式ドキュメントでも、この点が警告されています。比較の前に区切りをそろえます。

言語書き方
BashFILE_PATH="${FILE_PATH//\\//}"
PowerShell$path = $callInput.tool_input.file_path -replace '\\', '/'
Pythonfile_path.replace("\\", "/")

また、パスは絶対パスで渡されます。^で先頭を固定して比較するのではなく、/src/のようにパスの途中の区切りを含めた部分一致で見るのが公式の推奨です。

jqが入っていないとき

公式のBashの例は、JSONの読み取りにjqを使います。jqが入っていない環境では、「jq: command not found」というエラーになります。対処は次の3つです。

  • jqをインストールして、PATHの通った場所に置く
  • PowerShellのConvertFrom-Jsonで読む(前の例)
  • PythonやNode.jsで読む(公式ガイドもこの方法を挙げています)

社内の複数のパソコンに配る予定なら、追加のインストールが要らないPowerShellで書くのが、手間が少なくて済みます。

.cmdや.batのコマンドを呼ぶとき

引数リスト(args)を使う形式では、commandに実行ファイル(.exeなど)を指定する必要があります。npm・npx・eslintなどがnode_modules/.binに置く.cmdや.batのファイルは、シェルなしでは起動できません。公式ドキュメントは、次のどちらかを勧めています。

  • nodeと、その下のスクリプトのパスを指定する(例:"command": "node", "args": ["…/eslint/bin/eslint.js"])。node.exeは本物の実行ファイルなので、全OSで動きます
  • argsを使わず、シェル形式で.cmdの名前をそのまま書く

Git Bashの設定ファイルが出力を邪魔する

Git Bashなどでは、非対話モードでもプロファイル(~/.bashrcなど)が読み込まれる場合があります。プロファイルにechoが書かれていると、その文字がフックの標準出力の先頭に混ざり、JSONとして読まれなくなります。しかも終了コードは0のため、エラーも出ません。

確認するにはclaude --debugで起動し、ログに「Hook output does not start with {」という行がないかを探します。直すには、echoを対話モードのときだけ動くように囲みます。

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

Windowsでよくあるつまずき

症状原因対処
フックがまったく動かないGit for Windowsがなく、Bashツールが登録されていない。matcherがBashだけBash|PowerShellにする
ブロックしたいのに通ってしまうパスがバックスラッシュ区切りで、スラッシュ区切りの比較に一致しない比較の前に区切りをそろえる
jq: command not foundjqが入っていないjqを入れるか、PowerShell・Python・Node.jsで書く
スクリプトのパスが途中で切れるPowerShellで、$CLAUDE_PROJECT_DIRと書いている${CLAUDE_PROJECT_DIR}か$env:CLAUDE_PROJECT_DIRにする
.cmdを起動できない引数リスト形式では、シェルなしで起動できないnodeでスクリプトを直接指定するか、シェル形式にする
JSONを返したのに効かないプロファイルのechoが標準出力に混ざっているechoを対話モードのときだけ動くようにする
通知のダイアログが見えないダイアログがターミナルの後ろに開いているPowerShellで同じコマンドを直接実行して確かめる
SessionStartで環境変数が反映されないPowerShellで動くフックは、CLAUDE_ENV_FILEを使えないBashで動かすか、別の方法で環境を整える
あわせて読みたいClaude Codeのセキュリティ対策|権限・サンドボックス・学習設定を解説Claude Codeは手元のファイルを書き換え、コマンドを実行できるため、使い方を決めずに導入すると誤操作や情報漏えいが心配です。標準の安全機能、権限モードと権限ルール、サンドボックス、データの扱いと学習設定、脆弱性やMCPへの備え、会社で決めるルールを公式情報でまとめました。

フックの種類・非同期実行

複数の部品を並べて、用途ごとに組み合わせを考えているエンジニアと事務担当

ここまでは、シェルコマンドを実行する「コマンド型」を中心に説明してきました。フックには、全部で5つの種類があります。

type何をするか向いている場面
commandシェルコマンドを実行する整形・通知・ファイルの保護など、手元のパソコンでの処理
httpイベントの情報をURLにPOSTで送る社内の監査サービスなど、外部サービスに判断や記録を任せる
mcp_tool設定済みのMCPサーバーのツールを呼び出すセキュリティスキャンなど、MCPサーバーに用意された処理を使う
promptClaudeのモデルに1回だけ判断させる決まったルールでは書けない、文脈による判断
agentツールを使えるサブエージェントに調べさせて判断させる(実験的)ファイルやテスト結果を実際に確かめてからの判断

promptフック:判断をモデルに任せる

「すべてのタスクが完了しているか」のように、ルールでは書きにくい判断は、prompt型で行います。シェルコマンドの代わりに、プロンプトとフックの入力情報がClaudeのモデルに送られ、モデルが{"ok": true}か{"ok": false, "reason": "…"}で答えます。

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Check if all tasks are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}."
          }
        ]
      }
    ]
  }
}

okがfalseのときの動きは、イベントによって違います。

イベント"ok": falseのときの動き
Stop、SubagentStopreasonがClaudeに返り、作業が続く。"impossible": trueを添えると、満たせない条件として止まることを許可する
PreToolUseツールの呼び出しが拒否される。既定ではターンが終わり、理由は警告として表示される。continueOnBlock: trueを付けると、理由がツールのエラーとしてClaudeに返り、作業が続く
PostToolUse既定ではターンが終わり、理由が警告として表示される。continueOnBlock: trueで、理由をClaudeに返して続行する
UserPromptSubmit などターンが終わり、理由が警告として表示される

使うモデルは、modelフィールドで指定できます。省略すると、Claude Codeがバックグラウンドの処理に使うモデルが使われます。タイムアウトの既定は30秒です。

なお、公式ドキュメントでは、/goalコマンドが「セッション内で有効なpromptフック(Stop)の組み込みのショートカット」と説明されています。設定を書かずに「この条件を満たすまで作業を続けて」と指示したいときは、こちらが手軽です。

agentフック:確かめてから判断する

promptフックはフックの入力情報だけで判断します。実際のファイルやテストの結果を確かめる必要があるときは、agent型を使います。サブエージェントが起動し、ファイルの読み込みや検索、コマンドの実行をしてから、okで答えます。

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

$ARGUMENTSは、フックの入力JSONに置き換えられるプレースホルダーです。タイムアウトの既定は60秒、ツールを使えるターン数は最大50です。

agentフックは実験的な機能

公式ドキュメントでは、agentフックは実験的で、動作や設定が今後のリリースで変わる可能性があると明記されています。本番の運用では、コマンド型のフックを優先するよう案内されています。

httpフック:外部のサービスに任せる

httpフックは、コマンド型が標準入力で受け取るのと同じJSONを、指定したURLへPOSTで送ります。チーム全体の操作を1つの監査サービスに集めたい、といった場合に使います。

{
  "hooks": {
    "PostToolUse": [
      {
        "hooks": [
          {
            "type": "http",
            "url": "http://localhost:8080/hooks/tool-use",
            "headers": {
              "Authorization": "Bearer $MY_TOKEN"
            },
            "allowedEnvVars": ["MY_TOKEN"]
          }
        ]
      }
    ]
  }
}

応答は、コマンド型のJSON出力と同じ形式で返します。ステータスコードの扱いは、次のとおりです。

  • 2xx:本文のJSONを解釈する。ブロックしたいときは、2xxの応答の本文に判断のフィールドを入れる
  • 2xx以外・接続失敗:非ブロックのエラー。動作はそのまま進む。ステータスコードだけではブロックできない
  • リクエストに失敗したときに動作を止めたい場合は、onFailure: "block"を指定する

ヘッダーの値には環境変数を入れられますが、allowedEnvVarsに名前を書いたものだけが展開されます。書いていない変数は空文字になります。なお、イベントのJSONには、実行するコマンドやプロンプトの内容が含まれます。送信先は、信頼できる自社のサービスに限定してください。組織側でallowedHttpHookUrlsによって、送信を許可するURLを制限することもできます。

mcp_toolフック:MCPサーバーの処理を呼ぶ

設定済みのMCPサーバーのツールを、フックから呼び出せます。たとえば、ファイルを編集するたびに、サーバーのセキュリティスキャンのツールにファイルのパスを渡す設定は次のとおりです。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "my_server",
            "tool": "security_scan",
            "input": { "file_path": "${tool_input.file_path}" }
          }
        ]
      }
    ]
  }
}

注意点は、セッションの開始直後(起動時のSessionStartとSetup)には、MCPサーバーがまだ使えないため、mcp_toolのフックは実行されずにスキップされることです。起動時に必要な処理は、コマンド型で書きます。

あわせて読みたいClaude CodeのMCPとは?設定方法・スコープ・認証・権限の管理を解説Claude CodeのMCPは、課題管理やデータベースなどの外部ツールにClaude Codeをつなぐ仕組みです。claude mcp addでの追加、スコープと.mcp.json、認証、権限と管理者の制限、うまくいかないときの対処を2026年10月時点の公式情報でまとめました。

バックグラウンドで動かす(async)

通常のフックは、終わるまでClaudeの作業を待たせます。時間のかかる処理(テストの実行、外部サービスへの送信など)は、"async": trueを付けると、バックグラウンドで動かせます。コマンド型のフックだけで使えます。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/run-tests.sh",
            "async": true
          }
        ]
      }
    ]
  }
}

非同期のフックには、次の特徴と制約があります。

  • 止められない:実行が終わった後に結果が出るため、decisionやpermissionDecision、continueは効きません
  • 結果は次のターンで届く:フックが終わると、JSONのadditionalContextとsystemMessageが、次の会話のターンでClaudeに渡されます。セッションが待機中のときは、次にユーザーが操作するまで待ちます
  • タイムアウトは効かない:バックグラウンドで動いている間、timeoutは適用されません
  • 非対話モードでは中断される:-pで動かしている場合、終了時に実行中の非同期フックは取り消されます
  • 実行ごとにプロセスができる:呼ばれるたびに、別のバックグラウンドのプロセスが起動します

「失敗したときだけClaudeを呼び起こしたい」場合は、asyncRewakeを使います。バックグラウンドで動かし、終了コード2で終わったときに、標準エラー出力(なければ標準出力)をClaudeに渡して目を覚まさせます。

スキルやサブエージェントに入れる

フックは、設定ファイルだけでなく、スキルやサブエージェントの定義ファイルの冒頭(フロントマター)にも書けます。そのスキルを使っている間、またはそのサブエージェントが動いている間だけ有効にしたいときに便利です。

---
name: secure-operations
description: Perform operations with security checks
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/security-check.sh"
---

スキルのフックは、スキルが呼び出されたときに登録され、セッションの残りの間動き続けます。最初に1回成功したら外したい場合は、once: trueを付けます。サブエージェントのフックは、そのサブエージェントが動いている間だけ有効で、終わると外れます。サブエージェント内のStopフックは、SubagentStopとして扱われます。

安全に使うための注意

盾と鍵のアイコンの横で、フックの設定内容を一行ずつ確認している担当者

フックは便利な反面、パソコンの上でコマンドを自動実行する仕組みです。設定を間違えたり、中身を確かめずに他人のフックを取り込んだりすると、被害が大きくなります。使い始める前に、次の点を確認しておきましょう。

フックは自分の権限で動く

公式ドキュメントは、コマンド型のフックについて、ユーザーの全権限でシェルコマンドを実行すると警告しています。ユーザーのアカウントがアクセスできるファイルなら、変更も削除もできるということです。追加するフックは、必ず自分で中身を確認し、先に試してから有効にします。

公式ドキュメントは、フックを書く際の基本姿勢として、次の5つを挙げています。

基本具体的には
入力を鵜呑みにしない受け取ったJSONの値を検証・無害化してから使う
シェル変数は必ず引用符で囲む$VARではなく"$VAR"と書く。ファイル名にスペースや特殊な文字が入っていても壊れにくくなる
パスの..を拒否するファイルのパスに..が含まれていないかを確認し、想定外の場所へ出ていくのを防ぐ
絶対パスを使うスクリプトの場所は、${CLAUDE_PROJECT_DIR}などで絶対パスにする
機密のファイルを避ける.env、.git/、鍵のファイルなどを、フックの処理対象にしない

他人のリポジトリのフックに注意する

プロジェクトの.claude/settings.jsonに書かれたフックは、そのプロジェクトを開くと動きます。他人が作ったリポジトリを開くときは、フックにも注意が必要です。

実行のしかたフックが動くまで
対話モード(ふつうの使い方)フォルダの「ワークスペースの信頼」ダイアログを承認するまで、あなた自身の~/.claude/settings.jsonを含め、すべての設定ファイルのフックが保留される
-pやAgent SDKでの実行ダイアログは表示されず、フォルダは信頼されたものとして扱われる。リポジトリにコミットされたフックが、確認なしに動く

見知らぬリポジトリにclaude -pを向ける前には、.claude/の設定ファイルを確認してください。公式ドキュメントは、--bareを付けて起動するか、--settings '{"disableAllHooks": true}'でフックを止めて実行するよう案内しています。

プロジェクトのスキルやサブエージェントにもフックが入っている

フックは、設定ファイルだけでなく、プロジェクトのスキルやサブエージェントの冒頭にも書けます。公式ドキュメントによると、プロジェクトのサブエージェントのフックは、そのフォルダの信頼を承認した後でなければ動きません。ただし、プロジェクトのスキルのフックは、スキルが呼び出された時点で登録されます。取り込んだスキルやサブエージェントの中身も、確認の対象に含めてください。

権限設定との関係

フックと、ふだんの許可ダイアログや権限設定(allow・deny)は、次のように組み合わさります。

場面動き
フックがdenyを返す権限モードに関係なく、ツールがブロックされる。bypassPermissionsや--dangerously-skip-permissionsでもブロックされる
フックがallowを返す許可ダイアログは省略されるが、権限設定のdenyやaskのルールは引き続き有効。組織が設定したdenyリストも効く
フックが何も返さない(終了コード0)「許可した」ことにはならない。通常の許可の流れに進む
設定ファイルのフックで緩めようとする設定ファイルやプラグインのフックは、権限ルールが許す範囲を超えて制限を緩めることはできない。厳しくするほうにだけ働く

つまり、フックは「制限を足す」ことには強く、「制限を外す」ことは苦手です。権限設定のdenyを回避するフックは作れません。基本のルールは権限設定で決め、権限設定では表現できない条件をフックで足す、という役割分担になります。

フックが失敗したとき、動作を止めるか

通常、フックが起動に失敗したり、タイムアウトしたりしても、ツールの呼び出し自体は実行されます。ポリシーのためのフックで、スクリプトのパスを間違えていると、何も止まらないままになるということです。

これを防ぐために、コマンド型とhttp型のフックには"onFailure": "block"を指定できます。失敗やタイムアウトが起きたときに、そのイベントで終了コード2を返したのと同じ動き(PreToolUseならツールの呼び出しのブロック)になります。この指定には、Claude Code v2.1.295以降が必要です。

{
  "type": "command",
  "command": "node",
  "args": ["${CLAUDE_PROJECT_DIR}/.claude/hooks/check-command.js"],
  "onFailure": "block"
}

ただし、Stop・SubagentStop・TaskCompleted・TeammateIdleのフックと、バックグラウンド(async)のフックには効果がありません。また、公式ドキュメントは、ポリシーのフックを設定したあとは、最初の実行で「hook error」の通知が出ていないかを必ず確かめるよう勧めています。パスの書き間違いが、いちばん多い原因です。

フックを無効にする・制限する

フックを一時的にすべて止めたいときは、設定ファイルに"disableAllHooks": trueを書きます。次の点に注意してください。

  • 個別に無効にする方法はない:1つだけ止めたい場合は、設定からその項目を削除します
  • 設定の優先順位に従う:プロジェクトの.claude/settings.jsonにfalseがあると、ユーザー設定のtrueを上書きできます
  • 管理者のフックは止められない:管理者のポリシー設定で定義されたフックは、ユーザー・プロジェクト・ローカルの設定のdisableAllHooksでは止められません
  • 1回の実行だけ止めたい:--settings '{"disableAllHooks": true}'で起動します

管理者は、管理設定のallowManagedHooksOnlyで、動かせるフックを制限できます。有効にすると、ユーザー・プロジェクト・ローカル・プラグインのフックは動かなくなります。

動かないときの確かめ方

フックが期待どおりに動かないときは、症状から原因を絞ります。

症状確かめること
フックが動かない/hooksで、正しいイベントの下に出ているか。matcherの大文字・小文字(区別される)。イベントの選択(PreToolUseは実行前、PostToolUseは実行後)。ifを、ツール系以外のイベントに書いていないか
/hooksに出てこないJSONの文法(末尾のカンマやコメントは不可)。設定ファイルの場所。数秒たっても反映されなければセッションを開き直す。「Only hooks from managed settings run here」と出ているなら、組織がallowManagedHooksOnlyを設定している
「hook error」と表示されるスクリプトが想定外のコードで終わっている。サンプルのJSONをパイプで渡して、手で試す(下記)。「command not found」なら絶対パスか${CLAUDE_PROJECT_DIR}を使う。実行権限(chmod +x)
JSONを返しても効かないプロファイルのechoが先頭に混ざっていないか。フィールドの階層(hookSpecificOutputの内側か)。claude --debugで確認する
Stopフックが止まらないstop_hook_activeを確認しているか。8回続けてブロックすると、Claude Codeが上書きして終わらせる

スクリプトを手で試すには、サンプルのJSONを標準入力で渡し、終了コードを見ます。

echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh
echo $?

Claude Codeの画面では、Ctrl+Oでトランスクリプト(会話の記録の画面)を開くと、フックの結果を確認できます。成功したフックは、通常は何も表示されません。ブロックされた場合は、理由のメッセージが見えます。

さらに詳しい実行内容(終了コード・標準出力・標準エラー出力)は、デバッグログに記録されます。claude --debug-file /tmp/claude.logのようにログの場所を指定して起動するか、起動後に/debugを実行してログを有効にします。--debugだけでは端末に出力されず、ログは~/.claude/debug/の下のファイルに書かれます。

遅いフックを探すには

デバッグログには、フックごとに「Hooks: PostToolUse:Write [コマンド] finished with status 0 (31ms)」のような、かかった時間つきの行が残ります。公式ドキュメントによると、この形式はClaude Code v2.1.296以降です。毎回のやりとりを遅くしているフックは、この時間から見つけられます。

よくある質問

フックとCLAUDE.mdは、どちらを使えばいいですか?

Claudeに前提や方針を伝えたいだけなら、CLAUDE.mdが向いています。毎回例外なく実行したい処理(整形、通知、特定のファイルの保護など)は、フックにします。迷ったときは、まずCLAUDE.mdに書いてみて、守られないことがあれば、その部分をフックにする進め方が安全です。

フックは、どの画面で使えますか?

公式ドキュメントでは、ターミナル、IDE拡張、デスクトップアプリ、クラウド上のセッションのいずれでも、同じイベントでフックが動くと説明されています。ただし、クラウド上のセッションは手元の~/.claude/settings.jsonを読みません。クラウドでも使いたいフックは、プロジェクトの.claude/settings.jsonに書きます。

フックの設定を変えたら、Claude Codeの再起動は必要ですか?

設定ファイルを直接編集した場合は、通常はファイルの監視によって自動で反映されます。反映されないときは、セッションを開き直してください。/hooksで、設定が読み込まれているかを確認できます。

1つだけ一時的に止めることはできますか?

個別のフックを無効にする仕組みは、公式にはありません。設定ファイルからその項目を削除するか、すべてのフックを止めるdisableAllHooksを使います。

複数のフックを書いたら、順番に動きますか?

順番には動きません。同じイベントに一致するフックは、並列で実行されます。順序に依存する処理は、1つのスクリプトにまとめてください。判断が食い違った場合は、PreToolUseではdenyが最も優先されます。

フックで、Claudeに指示を出して作業を続けさせられますか?

できます。Stopフックで終了コード2を返すか、"decision": "block"とreasonを返すと、Claudeは止まらずに、その理由を次の指示として作業を続けます。additionalContextで、エラーではない形の補足を渡すこともできます。いずれも、8回続けて繰り返すと上書きされる点に注意します。

Windowsでも使えますか?

使えます。Git for Windowsを入れていない場合は、PowerShellでフックが動き、Bashツールもありません。matcherはBash|PowerShellと書き、スクリプトはPowerShellで書くのが確実です。パスがバックスラッシュ区切りで渡される点にも注意してください。詳しくは「Windowsでの書き方」の節にまとめています。

フックを書くのが難しそうです。Claudeに作ってもらえますか?

公式ガイドに「やりたいことを説明すれば、Claudeがフックを書いてくれる」と書かれています。やりたいことと、設定する場所(プロジェクトか、自分の全体か)を伝えて頼み、できた設定は/hooksと実際の動作で確かめてから使い始めます。

フックで、Claudeに渡る内容を書き換えられますか?

イベントによって異なります。PreToolUseでは、updatedInputでツールの引数を実行前に書き換えられます。PostToolUseでは、updatedToolOutputで、Claudeが見るツールの結果を置き換えられます。ただし、結果の書き換えは、Claudeに見える内容が変わるだけで、実行済みの処理は取り消せません。UserPromptSubmitでは、プロンプトそのものは書き換えられず、additionalContextで情報を添えることだけができます。

チームで同じフックを使いたいときは?

プロジェクトの.claude/settings.jsonに書いてリポジトリにコミットすると、全員に共有されます。スクリプトも.claude/hooks/に置いて、一緒にコミットします。複数のプロジェクトに配りたい場合は、プラグインのhooks/hooks.jsonにまとめる方法もあります。組織全体に強制したい場合は、管理者がポリシー設定に書きます。

ファイルを編集した後のフックが、Bashで書き換えたファイルでは動きません。

仕様です。Edit|Writeに一致するPostToolUseフックは、Bashコマンドや、Claude Code以外のプロセスが書き換えたファイルでは動きません。漏れなく検知したいときは、ファイルの変更を検知するFileChangedイベントを使うか、Stopフックで、1ターンの終わりに作業ツリーをまとめて調べます。

フックに渡る情報には、何が含まれますか?

イベントごとに違いますが、作業ディレクトリ、セッションID、ツールの名前と引数(実行しようとしているコマンドや、書き込もうとしているファイルのパス、内容)、送信したプロンプトの文などが含まれます。httpフックでは、それらがPOSTの本文として外部のURLに送られるため、送信先は信頼できるサービスに限ります。

ブロックしたプロンプトは、記録に残りますか?

残る場合があります。UserPromptSubmitのフックでブロックしても、プロンプトの文は、セッションの記録ファイルや入力履歴などに残ることがあると公式ドキュメントに書かれています。ブロックは、機密情報をディスクに残さないための手段ではありません。

まとめ

この記事のまとめ

  • フックは、Claude Codeの動作の決まったタイミングでコマンドなどを自動実行する仕組み。Claudeの判断に関係なく、条件が合えば毎回動く
  • 設定はsettings.jsonに「イベント → matcher → 実行する処理」の3段で書く。最初はNotificationの通知フックで試すと分かりやすい
  • ブロックするときは終了コード2か、JSONのdenyを使う。終了コード1では止まらない
  • 整形・ファイルの保護・危険なコマンドの阻止・記録・終了前の確認など、「例外なく行いたい処理」に向く。基本の許可・拒否のルールは権限設定で決める
  • Windowsでは、Bashツールがない環境ではmatcherにBash|PowerShellと書き、パスのバックスラッシュとjqの有無に注意する
  • フックはユーザーの権限で動く。中身を確認してから有効にし、他人のリポジトリは、動かす前に.claude/を確かめる

フックは、Claude Codeを「お願いして使うAI」から「決まったルールのもとで動くAI」に変える設定です。最初は通知のような影響の小さいものから始めて、動きを確かめながら、ファイルの保護や記録などに広げていくのがおすすめです。

チームや社内でClaude Codeを使うときは、どの操作を止めるか、どこまでを自動化するか、設定をどう共有するかを決めておくと、運用が安定します。INTERCREATE JAPANでは、Claude Code・Codexの導入から、社内ルールに合わせたフックやスキルの設計、運用の定着までをお手伝いしています。

この記事を書いた人

INTERCREATE JAPAN 編集部

Web制作、業務システムの開発、Claude Code・Codex の導入支援を行う INTERCREATE JAPAN の編集部です。実際の制作や導入の現場で確かめた手順をもとに、AIツールの使い方やホームページの運用について解説しています。

参考・出典