
Claude Code の完了に気づく Hooks 通知設定
Claude Code にタスクを投げている間、資料を読んだり Slack を返したりと、別の作業を並行させている方は多いと思います。戻ってみたら許可を求めて止まっていた、あるいはとっくに終わっていた。どちらも、気づくのが遅れたぶんだけ次の作業が遅れます。この記事では、追加アプリも外部サービスも使わず、macOS に最初から入っているコマンドと Claude Code の Hooks だけで、音・通知・読み上げの3つを設定した手順をまとめます。
止まっているのは Claude ではなく自分の側
Claude Code が権限の確認で待っている間、こちらは別のウィンドウを見ています。画面を切り替えて初めて、Do you want to proceed? のまま数分間止まっていたことに気づく。これが1日に何度も起きると、待ち時間の正体は Claude の処理時間ではなく、こちらが気づくまでの時間になります。
対処の方向は単純で、画面を見ていなくても状態が届くようにすればよいだけです。やることは3つに分かれます。
- 音を鳴らす: 状態ごとに違う音を割り当て、何が起きたかを音だけで区別する
- 通知を表示する: 画面右上のデスクトップ通知に、必要な操作とプロジェクト名を出す
- 読み上げる: 通知すら見ていないとき用に、Claude の回答の中身を音声で伝える
いずれも macOS に最初から入っている機能だけで実現できます。
Hooks は決まった場面で必ずコマンドを実行する
Claude Code には、処理の流れのなかにイベントの起点が定義されています。その起点にフックして任意のコマンドを必ず実行させられるのが Hooks です。Claude が「使おう」と判断するツールと違い、そのイベントが起きれば毎回そのまま走ります。判断が挟まらないので、見落としが起きません。
イベントは全体で30種類以上あり、代表的なものは次のとおりです。
| イベント | 発火する場面 |
|---|---|
SessionStart | セッションを開始した |
UserPromptSubmit | プロンプトを送信した |
PreToolUse | ツールを使う直前 |
PostToolUse | ツールを使い終わった |
Notification | 入力待ち / 権限確認 |
Stop | 応答が終わった |
SubagentStop | サブエージェントが終わった |
SessionEnd | セッションを終了した |
今回使うのは Stop と Notification の2イベント、計3パターンです。
| イベント | matcher | 発火する場面 |
|---|---|---|
Stop | 指定しない | Claude の応答が終わった |
Notification | permission_prompt | コマンドの許可を待っている |
Notification | idle_prompt | 入力を待っている |
Notification はイベントとしては1つで、matcher で待っている理由を絞り込みます。許可待ちと入力待ちをこの時点で分けておくと、あとから音や文言を個別に変えられます。
設定ファイルの形と、手で書かずに済ませる方法
設定は settings.json の hooks に書きます。イベント名の下に matcher と実行したいコマンドを並べる構造です。
JSON
{ "hooks": { "イベント名": [ { "matcher": "絞り込み条件", "hooks": [ { "type": "command", "command": "ここに実行したいコマンドを書く" } ] } ] }}この JSON を手で書く必要はありません。/hooks コマンドから対話的に追加できますし、Claude 自身に「Stop で音を鳴らす Hook を作って」と頼んでしまうのがいちばん早い方法です。
置き場所は3つあり、今回は1つ目を使います。
| ファイル | Git で共有 | 使いどころ |
|---|---|---|
~/.claude/settings.json | されない | 今回はこれ。自分の Mac の全プロジェクトに効く |
.claude/settings.local.json | されない | 1つのプロジェクトだけで試したいとき |
.claude/settings.json | される | チーム共通。全員の環境で動くものだけ |
音・声・通知の許可は、使っている端末と個人の好みに強く依存します。macOS 専用のコマンドをチーム共通の設定にコミットしないのが無難です。 設定したあとは Claude Code を再起動し、/hooks に登録されているかを確認します。表示されない場合は /status で設定ファイルの読み込みと JSON エラーを確認してください。
音を鳴らす: 状態ごとにシステムサウンドを変える
音源を用意する必要はありません。macOS には最初から14種類のシステムサウンドが入っています。
Bash
ls /System/Library/SoundsBasso、Blow、Bottle、Frog、Funk、Glass、Hero、Morse、Ping、Pop、Purr、Sosumi、Submarine、Tink の14個が並びます。afplay で単体再生できるので、まず聞き比べてみます。
Bash
for s in Sosumi Ping Glass; do afplay /System/Library/Sounds/$s.aiff sleep 0.5done今回は許可待ちに Sosumi、入力待ちに Ping、応答終了に Glass を割り当てました。聞き分けられれば何でも構いません。設定は次の形になります。
JSON
{ "hooks": { "Notification": [ { "matcher": "permission_prompt", "hooks": [ { "type": "command", "command": "afplay /System/Library/Sounds/Sosumi.aiff" } ] }, { "matcher": "idle_prompt", "hooks": [ { "type": "command", "command": "afplay /System/Library/Sounds/Ping.aiff" } ] } ], "Stop": [ { "hooks": [ { "type": "command", "command": "afplay /System/Library/Sounds/Glass.aiff" } ] } ] }}これで画面を見ていなくても、鳴った音だけで戻るべきかどうかが分かるようになります。
通知を表示する: 何を待っているかまで出す
音だけでは、どのプロジェクトが何を待っているかまでは分かりません。macOS の osascript を使うと、ターミナルからデスクトップ通知を出せます。
Bash
osascript -e 'display notification "権限の確認が必要です" with title "Claude Code" subtitle "sample-project"'タイトル・サブタイトル・本文の3つを渡せます。サブタイトルにプロジェクト名を入れておくと、どの作業に戻ればいいかまで通知だけで判断できます。
Hooks 側は、前の節と同じ形のまま command の中身が afplay から osascript に変わるだけです。
JSON
{ "hooks": { "Notification": [ { "matcher": "permission_prompt", "hooks": [ { "type": "command", "command": "osascript -e 'display notification \"権限の確認が必要です\" with title \"Claude Code\"'" } ] }, { "matcher": "idle_prompt", "hooks": [ { "type": "command", "command": "osascript -e 'display notification \"入力を待っています\" with title \"Claude Code\"'" } ] } ] }}読み上げる: Stop に届く回答本文を渡す
テキストを音声にする方法はいくつかありますが、ローカルで最も手軽なのは macOS 標準の say です。日本語の声は次のコマンドで探せます。
Bash
say -v '?' | grep ja_JPKyoko、Otoya、Eddy、Flo が出てきます。聞き比べたところ日本語としていちばん自然だったのは Kyoko だったので、これを使います。-v が声、-r が速度(wpm)です。
Bash
for v in Kyoko Otoya Eddy Flo; do say -v $v -r 300 "権限の確認を待っています"doneここまでは固定文を読ませているだけなので、毎回まったく同じことしか言いません。「入力を待っています」と聞こえても、結局は画面を見に行くことになります。Claude Code を並行して何本も回していると、どのタスクの通知なのかが分からず優先順位も付けられません。
やりたいのは、何をやったのかまで聞こえる状態です。そしてその材料は、実は Hook にもう届いています。
Hook で実行したコマンドには、標準入力から JSON が流し込まれます。Stop のときの JSON には last_assistant_message という項目があり、Claude が最後に画面へ出した本文がそのまま入っています。
JSON
{ "hook_event_name": "Stop", "cwd": "/Users/you/sample-project", "last_assistant_message": "テストを3件修正しました。すべて成功しています。", "session_id": "...", "transcript_path": "..."}この項目が入るのは Stop と SubagentStop だけで、ほかのイベントの JSON には含まれません。あとは jq で取り出して say に渡すだけ、と言いたいところですが、そのまま渡すと聞けたものではありませんでした。Claude の回答にはコードブロックと URL が含まれるので、「バッククォート バッククォート バッククォート バッシュ エヌピーエム テスト……エイチティーティーピーエス コロン スラッシュ スラッシュ……」と文字を全部読み上げます。回答が長ければその間ずっと喋り続けます。
入れた処理は2つだけです。コードブロックと URL を落とすことと、先頭180文字で切ることです。
Bash
#!/bin/bash# ~/.claude/hooks/speak.shjq -r '.last_assistant_message' \ | awk '/^```/ { c = !c; next } !c' \ | sed -E 's#https?://[^ ]*# URL #g' \ | cut -c 1-180 \ | say -v Kyoko -r 360これで「テストを3件修正しました。すべて成功しています。詳細は URL を見てください」と読み上げられるようになりました。この2つを入れるだけで実用に耐えます。なお音・通知・読み上げを全部やるなら、Hooks の command に長いワンライナーを書くよりも、このようにスクリプト1本にまとめて呼ぶほうが管理が楽になります。
devcontainer や Codespaces では代わりにターミナルベルを使う
Hook は Claude Code が動いている環境で実行されます。コンテナの中で claude を起動しているなら、Hook もコンテナの中で動きます。
Ubuntu の devcontainer で確認したところ、今回使った3つはどれも存在しませんでした。Linux 用の代替として notify-send や paplay も探しましたが、こちらも入っていません。
Bash
command -v afplay say osascript # 何も出ないcommand -v notify-send paplay # 何も出ないLinux 版のコマンドを入れたとしても、鳴るのはコンテナの中です。代わりに使えるのが Claude Code 組み込みのターミナルベルで、ベル文字はコンテナからホストの端末まで届くため、VS Code や Codespaces でも鳴らせます。
JSON
{ "preferredNotifChannel": "terminal_bell"}VS Code の場合は、アクセシビリティ設定のターミナルベルサウンドが既定では自動になっているため、明示的に有効にする必要があります。
JSON
{ "accessibility.signals.terminalBell": { "sound": "on" }}まとめ
- Hooks は、Claude Code の決まった場面で必ずコマンドを実行する仕組み
StopとNotificationの3パターンに、音・通知・読み上げを割り当てた- 使ったのは
afplayosascriptsayという macOS 標準コマンドだけ Stopのlast_assistant_messageを渡せば、回答の中身まで音声で受け取れる- コンテナ環境では
preferredNotifChannelのターミナルベルで代替する
全部を一度に入れる必要はありません。Stop に afplay を1行足すだけでも、「終わったかな」と見に行く回数は確実に減ります。
ひとつ補足しておくと、Stop は「依頼の完全達成」ではなく「その応答が終わった」合図です。Claude が質問を返して終わることもあるので、通知が来たら結局は画面を見ることになります。権限確認そのものを省略する仕組みではなく、確認に早く戻るための仕組みだと考えてください。どこまでを自動で通し、どこで人間に戻すかという線引きについては、コーディングエージェントの権限設計でも整理しています。
gaipack では、AI 駆動開発を現場で回すための環境づくりを社内で継続的に改善しています。開発体制や運用の相談も承っていますので、お気軽にご相談ください。
※ 本記事の内容は公開時点の情報です。サービスの名称・内容・料金は予告なく改訂されることがあります。




