Skip to content

Hooks ​

05. CLAUDE.md とメモリ と 06. Skills で、CLAUDE.md やスキルに書いた指示は、Claude が読んで「従おうとする」もので、必ず守られるとは限らない、と書きました。フックは、その反対の性質を持つ仕組みです。決まったタイミングで、決まったコマンドを、Claude の判断を通らずに、必ず実行します。このページでは、フックの仕組みと使い道を整理し、「Claude が入力を待っているときに、Mac の通知を出す」フックを、実際に作って試します。

この記事で分かること ​

  • フックとは何か。誰が提供し、誰が実行するのか
  • フックが動く場所と、動かせるタイミング
  • フックで何ができるか。権限の仕組みとの関係
  • 入力待ちの通知のフックを作って、ターミナル版とデスクトップアプリで試した結果

用語の整理 ​

用語意味
フック(hook)Claude Code の中で何かが起きたときに、決めておいたコマンドを自動で実行する仕組み
イベント(event)フックが動くきっかけになる出来事。「道具を使う直前」「Claude が入力を待っている」など
マッチャー(matcher)イベントの中で、さらにどの場合に動かすかを絞り込む条件。たとえば「道具の名前が Bash のときだけ」
通知(notification)画面の右上に出る、Mac の知らせ

フックとは ​

提供するのも、実行するのも、Claude Code ​

フックは、Claude Code の機能の 1 つです。何を実行するかは、利用者やプロジェクト、組織の管理者が、設定ファイルに書きます。書いたコマンドを、決まったタイミングで実行するのは、Claude(モデル)ではなく、Claude Code の本体です。

CLAUDE.md、スキルフック
誰が扱うかClaude が読んで、従おうとするClaude Code の本体が、実行する
必ず守られるか守られないことがある決まったタイミングで、必ず実行される
向いていること方針や、手順の説明毎回、必ず行わせたいこと

同じ名前の、別の仕組み ​

「フック」という言葉は、ほかの道具でも使われています。

名前提供しているところ動くタイミングの例
Claude Code のフック(このページで扱うもの)Claude CodeClaude が道具を使う前後、Claude が入力を待っているとき など
Git のフックGitコミットの前、push の前 など。Claude を使っていなくても、人がコミットしたときにも動く
GitHub の WebhookGitHubプルリクエストが作られたとき など。別のサーバーに知らせを送る

考え方は似ていますが、別々の仕組みです。Claude Code のフックは、Claude Code の中での出来事にだけ反応します。

フックが動く場所 ​

公式ドキュメントには、フックの仕組みは、ターミナル版、エディターの拡張機能、デスクトップアプリ、クラウドのセッションの、どこで Claude Code を使っても同じように働く、と書かれています。

Claude Code が動いている場所フックのコマンドが動く場所
ターミナル版、デスクトップアプリ、VS Code の拡張機能(自分の PC での会話)自分の PC
クラウドのセッションクラウドの環境の中。自分の PC の ~/.claude/settings.json は読まれない

自分の PC で動く場合、公式ドキュメントは、次のように警告しています。

Command hooks execute shell commands with your full user permissions. (コマンドのフックは、利用者のすべての権限で、シェルのコマンドを実行する)

フックに書いたコマンドは、利用者が自分でターミナルに入力したのと同じ力を持ちます。03. 権限モードと、確認を減らす方法 で見た権限の確認も、通りません。自分で中身を理解したコマンドだけを書きます。ほかの人のリポジトリの .claude/settings.json にフックが書かれていたら、中身を確かめてから使います。

フックを動かせるタイミング ​

フックを動かせるイベントは、数多くあります。主なものを挙げます。

イベント動くタイミング
SessionStart会話が始まったとき、再開したとき
UserPromptSubmit利用者が依頼を送って、Claude が読む前
PreToolUseClaude が道具を使う直前。止めることもできる
PermissionRequestClaude Code が、利用者に許可を求めようとしたとき
PostToolUseClaude が道具を使い終わった直後
NotificationClaude Code が、利用者に知らせを出すとき(許可を待っている、入力を待っている、など)
StopClaude が答え終わったとき
FileChanged指定したファイルが、変更されたとき
ConfigChange会話の途中で、設定ファイルが変わったとき

Claude のファイルの読み書きを、監視できるか ​

フックで、ファイルの読み書きを監視することもできます。ただし、範囲があります。

知りたいことフックでできるか
Claude が、道具でどのファイルを読み書きしたかできる。道具を使う前後(PreToolUse、PostToolUse)のフックで記録できる。直前のフックなら、止めることもできる
指定したファイルが、誰かに変更されたかできる(FileChanged)。Claude 以外のアプリが変えた場合も分かる。ただし、作業フォルダーの中の、名前を指定したファイルだけで、分かるのは変更だけ
Claude が動かしたプログラムの中で、どのファイルが読まれたかできない。フックに渡るのは、実行したコマンドの文字だけ
PC 全体のファイルの読み書きできない。フックは、PC 全体を見張る道具ではない

フックで何ができるか ​

公式ドキュメントの使い方の手引きには、次の使用例が挙げられています。

使用例使うイベント内容
入力待ちを通知するNotificationClaude が入力や許可を待っているときに、Mac の通知を出す
編集のあとに、自動で整形するPostToolUseClaude が編集したファイルに、書式を整える道具を自動で実行する
大事なファイルの編集を止めるPreToolUse.env などの編集を止める。止めた理由が Claude に伝わる
会話の要約のあとに、大事な情報を入れ直すSessionStart会話が要約されたあとに、プロジェクトの決まりを Claude に伝え直す
設定ファイルの変更を記録するConfigChange会話の途中での設定の変更を、記録に残す
特定の確認を、自動で許可するPermissionRequest決めた種類の確認だけ、利用者の代わりに「許可」と答える

止める力は強く、許す力には限りがある ​

公式ドキュメントには、権限の仕組みとの関係が、次のように書かれています。

  • 道具を使う直前のフックで「拒否」と答えると、権限モードが何であっても止まる。確認をすべて省くモードでも止まる。利用者がモードを変えても抜けられない決まりを作れる。
  • フックで「許可」と答えても、設定ファイルの禁止のルールは破れない。組織が「確認する」と決めた道具の確認なども、省けない。

フックの書き方 ​

フックは、04. 設定ファイルの仕組み で扱った設定ファイルに、hooks という項目で書きます。

json
{
  "hooks": {
    "イベントの名前": [
      {
        "matcher": "絞り込みの条件",
        "hooks": [
          { "type": "command", "command": "実行するコマンド" }
        ]
      }
    ]
  }
}

フックを書く場所によって、効く範囲が決まります。考え方は、ほかの設定と同じです。

書く場所効く範囲ほかの人に届くか
~/.claude/settings.json自分の、すべてのプロジェクト届かない
.claude/settings.jsonそのプロジェクト届く。コミットすれば
.claude/settings.local.jsonそのプロジェクトの、自分だけ届かない
管理設定組織の全員届く。管理者が配る

フックに渡される情報は、標準入力から、JSON の形で受け取れます。たとえば、道具を使い終わった直後のフックには、使った道具の名前、入力(実行したコマンドなど)、結果などが渡されます。

実際にやってみる:入力待ちを通知する ​

Claude に長い作業を任せている間に、別のことをしていると、Claude が確認や入力を待っていることに気づかないことがあります。そこで、公式ドキュメントの使用例にある「入力待ちを通知する」フックを作り、試しました。作ったもの、準備したこと、試したこと、確かめずに区切ったこと、の順に書きます。

1. 作ったフック ​

~/.claude/settings.json に、次のフックを書きました。通知は個人の好みなので、プロジェクトの設定ではなく、自分の設定に書いています。

json
{
  "theme": "dark",
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude が入力を待っています\" with title \"Claude Code\" sound name \"Glass\"'"
          }
        ]
      }
    ]
  }
}
部分意味
NotificationClaude Code が、利用者に知らせを出すときに動く
"matcher": ""知らせの種類を問わず、すべてで動く
commandAppleScript で、Mac の通知を出す。公式ドキュメントの例の本文を日本語にし、音(Glass)を加えた

公式ドキュメントによると、Notification のフックが動く主な場面は、次の 2 つです。

場面動くタイミング
許可を待っている(permission_prompt)確認の画面が出てから、約 6 秒たっても答えがないとき
入力を待っている(idle_prompt)Claude が答え終わってから、約 60 秒たっても入力がないとき

~/.claude/settings.json は、最初からありました。ターミナル版を初めて起動したときに、画面の色合い(テーマ)を選んだので、そのときに作られたものです。中身は "theme": "dark" の 1 行だけでした。この 1 行を残したまま、フックを書き足しています。

2. 準備:通知が画面に出るようにする ​

フックを書く前に、通知のコマンドだけを試しました。ここで、2 つのことが分かりました。

通知の許可と、表示のしかた

AppleScript の display notification で出す通知は、macOS では「スクリプトエディタ」から出たものとして扱われます。公式ドキュメントにも、スクリプトエディタに通知の許可がないと、何も表示されず、エラーも出ない、と書かれています。

この Mac では、許可はすでにオンでした。ただし、通知の表示のしかたが「一時的」になっていて、数秒で消えてしまいました。「システム設定」→「通知」→「スクリプトエディタ」で、表示のしかたを「持続的」に変えると、自分で閉じるまで残るようになりました。

Claude のコマンドの環境からは、通知が出ない

Claude が、自分のコマンドの道具(Bash)で、通知のコマンドを直接実行しても、通知は画面に出ませんでした。コマンドはエラーなく終わります。同じコマンドを、利用者がターミナルで実行すると、通知が出ました。Claude のコマンドを OS の仕組みで囲うサンドボックス(03. 権限モードと、確認を減らす方法)は、このとき有効になっていなかったので、それが原因ではありません。理由は分かっていません。

3. 試したことと、結果 ​

フックを設定したあと、ターミナル版とデスクトップアプリで試しました。

試したこと使う場所結果
答え終わってから、約 60 秒待つターミナル版通知が出た
答え終わってから、約 60 秒待つデスクトップアプリ通知が出なかった(2 回試して、2 回とも)
確認の画面に答えずに、約 10 秒待つ(権限モードを一時的に Manual にした)デスクトップアプリ通知が出なかった

デスクトップアプリで出なかった理由を調べるため、フックのコマンドに、「動いた時刻と、渡された情報を、ファイルに書き残す」処理を加えました。

試したこと結果分かること
デスクトップアプリで、答え終わってから約 60 秒待つ(記録の処理を加えて、2 回)記録のファイルが、作られなかったNotification のフックが、動いていない
デスクトップアプリを背面にして、答え終わってから約 60 秒待つ(記録の処理を加えて、1 回)記録のファイルが、作られなかったアプリが背面にあっても、動かない
記録の処理そのものを、Claude が直接実行する記録のファイルが作られ、正しく書き残された記録の処理には、問題がない

さらに、デスクトップアプリで、フックそのものが動くかどうかを、別のイベントで確かめました。道具を使い終わった直後(PostToolUse)のフックに、記録と通知の処理を書き、Claude がコマンドを実行しました。

試したこと結果
道具を使い終わった直後のフックで、記録を書く記録が残った。使った道具の名前、実行したコマンド、作業フォルダー、権限モードなどが、フックに渡されていた
同じフックで、通知を出す(題名「study-lab のフック」、本文「フックからの通知(テスト)」。ほかの通知と区別できる文面にした)通知が出た

このフックは、会話の途中で設定ファイルに書き足したものですが、会話を始め直さなくても、すぐに動きました。

ここまでで分かったことを、まとめます。

確かめたこと結果
デスクトップアプリで、フックは動くか動く
デスクトップアプリで動いたフックから、Mac の通知を出せるか出せる
デスクトップアプリで、答え終わって約 60 秒の Notification のフックは動くか動かなかった
ターミナル版で、答え終わって約 60 秒の Notification のフックは動くか動いた

答え終わって約 60 秒のきっかけ(idle_prompt)について、公式ドキュメントの説明は、ターミナルでの会話を前提に書かれています。デスクトップアプリで起きるかどうかは、書かれていませんでした。

確かめ終わったあと、道具を使い終わった直後のテスト用のフックと、記録の処理は外しました。Notification のフックは、最初の形のまま残しています。

4. 確かめずに区切ったこと ​

デスクトップアプリでの入力待ちの通知は、次の 2 点を確かめないまま、ここで区切りにしました。普段はデスクトップアプリを Auto で使っていて確認の画面がめったに出ないこと、代わりに作った記録のフック(下の「実際にやってみる:Claude が触ったファイルを記録する」)が実用に足りていることが理由です。

きっかけ公式ドキュメントに書かれていることこのサイトで分かっていること
確認の画面が出て約 6 秒(permission_prompt)デスクトップアプリでも、確認を求めてから約 6 秒で動く(v2.1.233 から)。ターミナル版と違い、入力中でも遅れない。約 6 秒より前に答えると、動かない試したときは通知が出なかった。ただし、記録の処理を加える前だったので、フックが動いたかどうかは分からない
答え終わって約 60 秒(idle_prompt)約 60 秒で動くという説明は、ターミナルでの会話についてのもの。デスクトップアプリでの動きとして書かれているのは、permission_prompt だけ前面で 2 回、背面で 1 回試して、3 回とも動かなかった。理由は分かっていない

実際にやってみる:Claude が触ったファイルを記録する ​

入力待ちの通知は、デスクトップアプリでは、きっかけが起きませんでした。一方で、道具を使い終わった直後(PostToolUse)のフックは、デスクトップアプリでも動くことを確かめました。そこで、このイベントを使って、「Claude が、いつ、どのファイルに触れたか」を記録するフックを作りました。上の「Claude のファイルの読み書きを、監視できるか」で整理した内容を、実際に確かめるものです。作ったもの、試したこと、分かったこと、の順に書きます。

1. 作ったもの ​

記録の処理

フックから渡された情報から、必要な項目だけを取り出して、1 件 1 行で記録のファイルに書き足す処理を、~/.claude/hooks/log-tool-use.sh に書きました。情報の取り出しには、JSON を扱うコマンド jq を使っています(この Mac には、最初から入っていました)。

sh
#!/bin/sh
# Claude Code の PostToolUse フックから呼ばれる。
# 時刻、使う場所、セッション、道具の名前、触れたファイルの場所(またはコマンドの文字)を、1 件 1 行で記録する。
# ファイルの中身や、コマンドの結果は記録しない。
jq -r --arg entry "${CLAUDE_CODE_ENTRYPOINT:-unknown}" '
  def oneline: tostring | gsub("\n"; " ⏎ ");
  [ (now | localtime | strftime("%Y-%m-%d %H:%M:%S")),
    $entry,
    ((.session_id // "") | .[0:8]),
    .tool_name,
    ( if .tool_name == "Bash" then (.tool_input.command // "")
      elif (.tool_name == "Grep" or .tool_name == "Glob")
        then ((.tool_input.path // ".") + "  " + (.tool_input.pattern // ""))
      else (.tool_input.file_path // "") end | oneline ),
    ( if .tool_input.description then "(" + (.tool_input.description | oneline) + ")" else empty end )
  ] | join("  ")
' >> "$HOME/.claude/tool-use.log"

記録する項目と、記録しない項目は、次のように決めました。

記録する記録しない
時刻ファイルの中身
使う場所(デスクトップアプリか、ターミナル版か)コマンドの結果
セッション(識別番号の先頭 8 文字)
道具の名前
触れたファイルの場所。コマンドの場合は、コマンドの文字と、Claude が付けた説明

ファイルの中身やコマンドの結果を記録しないのは、記録が大きくなることと、記録のファイルに、読んだファイルの中身が残ってしまうことを避けるためです。

使う場所は、Claude Code が設定する環境変数(CLAUDE_CODE_ENTRYPOINT)から、セッションは、フックに渡される情報(session_id)から取っています。

フックの設定

このフックは、まずこのサイトの作業だけで試すために、プロジェクトの、自分だけの設定(.claude/settings.local.json)に書きました。このファイルは、Git の管理の対象外で、公開されません。

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Read|Write|Edit|Grep|Glob|Bash",
        "hooks": [
          { "type": "command", "command": "\"$HOME/.claude/hooks/log-tool-use.sh\"" }
        ]
      }
    ]
  }
}

matcher で、ファイルを読む(Read)、書く(Write)、編集する(Edit)、探す(Grep、Glob)道具と、コマンドの実行(Bash)に絞っています。

記録の処理は、フックに組み込む前に、作りものの情報(ファイルを読む、複数行のコマンド、ファイルを探す)を渡して、正しい形で書かれることを確かめました。

2. 試したこと ​

このプロジェクトで始めた、3 つのセッションで試しました。どのセッションの記録かを見分けられるように、それぞれ、違うファイルについて尋ねました。

セッション頼んだこと
デスクトップアプリの、このページを書いているセッションCLAUDE.md を読む。Python のコマンドで README.md の 1 行目を表示する
ターミナル版の、新しいセッションREADME.md の 1 行目を教えて
デスクトップアプリの、別の新しいセッションpackage.json の name を教えて

3 つとも、記録が残りました。記録の例です(ホームフォルダーの場所は ~ に置き換えています)。

text
2026-10-18 11:02:52  claude-desktop  344a479d  Bash  tail -n 2 ~/.claude/tool-use.log | ...  (Show the newest log lines ...)
2026-10-18 11:08:19  cli  60322502  Bash  ls README* 2>/dev/null  (Find README files)
2026-10-18 11:08:20  cli  60322502  Read  ~/python-projects/study-lab/README.md
項目読み方
claude-desktop、cli使う場所。デスクトップアプリは claude-desktop、ターミナル版は cli と記録された
344a479d、60322502セッション。セッションごとに、別の値になった

なお、最初は、使う場所とセッションを記録していませんでした。3 つのセッションの記録が混ざって、どれがどのセッションのものかを、頼んだファイルの名前と時刻から見分ける必要がありました。そこで、この 2 つの項目を足しました。

3. 分かったこと ​

確かめたこと結果
プロジェクトの設定に書いたフックは、どのセッションで動くかこのプロジェクトで始めた、デスクトップアプリの別のセッションでも、ターミナル版のセッションでも動いた
フックを、会話の途中で書き足したとき会話を始め直さなくても、すぐに記録が始まった
1 つの依頼で、何件の記録になるか依頼によって違う。ターミナル版の Claude は、先にコマンドで README のファイルを探し、そのあとで読む道具で読んだ。1 つの依頼が、2 件の記録になった
同じ依頼でも、使う道具は変わるか変わる。ターミナル版の Claude は、読む道具で README.md を読み、デスクトップアプリの別のセッションの Claude は、コマンド(grep)で package.json を調べた。どちらも記録された

記録の限界も、実際に確かめられました。Python のコマンドで README.md を読んだときの記録は、次のとおりです。

text
2026-10-18 10:47:50  Bash  venv/bin/python -c "print(open('README.md').read().splitlines()[0])"  (Print the first line of the README using a Python one-liner)

記録の上では、道具は Bash で、README.md を読んだことは、コマンドの文字を読み解かないと分かりません。別のファイルに書いたプログラムを動かした場合は、何を読んだのかは、記録からは分かりません。

このプロジェクト以外で始めたセッションや、クラウドのセッションでは、このフックは動きません。設定を、プロジェクトの設定に書いているためです。すべてのプロジェクトで記録したい場合は、利用者の設定(~/.claude/settings.json)に書きます。

まとめ ​

  • フックは、Claude Code の本体が、決まったタイミングで、決まったコマンドを必ず実行する仕組み。Claude の判断を通らない。CLAUDE.md やスキルと違って、必ず実行される。
  • 自分の PC での会話では、フックのコマンドは、自分の PC の上で、利用者のすべての権限で動く。中身を理解したコマンドだけを書く。
  • 道具を使う直前のフックは、権限モードに関係なく、操作を止められる。フックで許可しても、禁止のルールは破れない。
  • 入力待ちの通知のフックは、ターミナル版では、答え終わって約 60 秒で動いた。デスクトップアプリでは、同じきっかけでは動かなかった。ただし、デスクトップアプリでも、フックの仕組みそのものは動き、フックから通知も出せた。
  • 道具を使い終わった直後のフックで、Claude が触ったファイルとコマンドを記録できた。プロジェクトの設定に書くと、そのプロジェクトで始めた、デスクトップアプリとターミナル版のどのセッションでも動いた。ただし、コマンドで動かしたプログラムの中で読み書きされたファイルは、記録に残らない。

確認の記録 ​

項目内容
最終確認日2026 年 10 月 18 日(「4. 確かめずに区切ったこと」は 2026 年 10 月 24 日)
Claude Code のバージョンデスクトップアプリ 2.1.295、ターミナル版 2.1.300(macOS)
検証の状況2 つの「実際にやってみる」は、この Mac で、ターミナル版とデスクトップアプリを使って確かめた結果。それ以外の節は、公式ドキュメントの内容をもとに書いている
確かめていない内容入力待ちの通知の「4. 確かめずに区切ったこと」の 2 点。入力待ちの通知と、ファイルの記録以外の使用例(整形、編集を止める、自動で許可する など)は、実際には試していない