Claude Codeでタスク終了時に通知音を鳴らす【Windows版】
目次
はじめに
いつもブログをご覧いただきありがとうございます。
ミジンコに転生したIPUSIRONです😀
MacやLinux環境では、Claude Codeでタスク終了時に通知音を鳴らすのは簡単でした。
しかし、Windows環境の場合、WSL版やDocker版でClaude Codeを実行すると、ホストOSに存在する音声ファイルへのアクセスが難しく、同様の通知機能を実現するのは困難でした。
サーバー・クライアント型の独自プログラムを用意すれば実現できないことはありませんが、かなりの手間がかかっていました。
ところが、2025年7月中旬にWindowsネイティブ版のClaude Codeがリリースされたことで、事情は一変しました。
Windowsネイティブ版であれば、Windowsローカルにある音声ファイルへも簡単にアクセスできますし、hooksを活用することで手軽に通知音を鳴らすことができます。
本記事では、PowerShellを通じて音声ファイルを再生するため、再生ソフトを別途インストールする必要もありません。
方針
・Claudeの設定ファイルである".claude\settings.local.json"ファイル(あるいは".claude\settings.json"ファイル)にhooksの設定を追加する。
・PowerShellのコマンドで音声ファイルを再生する。
解決法<その1> hooks.Stopを利用する【確実ではないが実用上問題なし】
1:音声ファイルを用意する
どのファイル形式でも構いません。鳴らしたい音声ファイルを準備してください。
私は、棒読みちゃんで魔理沙の声で「終わったぜ」と発する音声ファイルを作成しました[1]このツール内で音声ファイルは保存できます。。

音声ファイルの配置場所はどこでも構いません。
私は"C:\Users\owner\OneDrive\Documents\claude"配下に、"marisa_owattaze.wav"ファイル(生成した音声ファイル)を配置しました。
2:cladeコマンドを実行するプロンプト上で、音声ファイルの再生コマンドが正常動作するかを確認する
私の場合はGit Bashで以下のコマンドを実行して、音声ファイルが再生されたことを確認できました。
owner@MHL MINGW64 /d/try100/day032/pigpen-cipherlab (main)
$ powershell -Command "(New-Object Media.SoundPlayer 'C:\\Users\\owner\\OneDrive\\Documents\\claude\\marisa_owattaz
e.wav').PlaySync()"
3:Claudeの設定ファイルにhooksの設定を追加する
後はhooksの設定において、先ほどのPowerShellコマンドを指定するだけです。
以下は私が利用しているhooks.Stopの設定になります。
{
"permissions": {
"allow": [
"Bash(ls:*)",
"Bash(mkdir:*)"
],
"deny": []
},
"hooks": {
"Stop": [
{
"matcher": ".",
"hooks": [
{
"type": "command",
"command": "powershell -Command \"(New-Object Media.SoundPlayer 'C:\\\\Users\\\\owner\\\\OneDrive\\\\Documents\\\\claude\\\\marisa_owattaze.wav').PlaySync()\""
}
]
}
]
}
}
ポイントは以下の2つです。
- JSONでは「\\」が1つの「\」を表す。よって、元のコマンドの「\\」は、JSONファイル内で4重「\\\\」になる。
- JSON文字列の中でPowerShellの"…"を囲むために、\"…\"とする。
Claude Codeのhooks設定におけるマッチング条件(matcher)が正規表現で判定しています。
細かい話をいれば、「"matcher": “"」でも動くでしょう。
その理由はマッチング条件がnew RegExp(matcher)で正規表現している可能性が高いためです。
その場合「""」でも「空文字列にマッチする正規表現」として有効となります。
つまり、多くの出力行には空文字列(=ゼロ文字)も含まれるため、結果として発火することになります。
しかしながら、出力がある限り常に発火させたい場合は、「"."」あるいは「".*"」が推奨されます。
4:Claude Codeでテストする
Claudeの設定ファイルを変更したので、すでにClaude Codeを起動中なら入り直してください。
/reload settingsコマンドで設定を再読み込みできるので、このコマンドだけで十分だと思っていましたが、なぜか私の環境では即反映されなかった(?)ので、一度入り直したところうまくいきました。
owner@MHL MINGW64 /d/try100/day032/pigpen-cipherlab (main)
$ claude
(略)
╭───────────────────────────────────────────────────╮
│ ✻ Welcome to Claude Code! │
│ │
│ /help for help, /status for your current setup │
│ │
│ cwd: D:\try100\day032\pigpen-cipherlab │
╰───────────────────────────────────────────────────╯
Tips for getting started:
1. Run /terminal-setup to set up terminal integration
2. Use Claude to help with file analysis, editing, bash commands and git
3. Be as specific as you would with another engineer for the best results
4. ✔ Run /init to create a CLAUDE.md file with instructions for Claude
> 今日は何日? ←適当に質問する。
● 今日は2025年7月31日です。 ←処理が終わって答えが表示される前に、音声ファイルが再生される。
処理の終了タイミングで音声ファイルが再生されれば成功です。
Claude Code内では「!<コマンド>」という形式でBashやPowerShellのコマンドを直接実行できます。
これはClaude Codeが備える対話型の仮想開発環境(REPL的なUI)の一部で、’!’は外部コマンドの実行を意味します。
たとえば、「!ls」と入力するとファイル一覧が表示されます。
しかし、こうしたコマンドは今回の音声ファイル再生のテストに使えません。
Claude Codeのhooks.Stopは、AIによるツール実行処理が「中断された場合」に呼び出されるコールバック的なフックです。
一方、「!<コマンド>」はあくまでユーザーが手動で入力した直接実行コマンドなので、Claudeの推論プロセスやタスク処理フローには介入しないからです。
5:個人の設定ファイルをGitの管理下から外す【任意】
“.claude\settings.local.json"ファイル内には、自分の環境依存のパスが書かれているので、他人と共有して開発しているような場合はコミットすべきではありません。
“.gitignore"ファイルに、以下の1行を追加しておきます。
.claude/settings.local.json
“.claude/"を指定すると、他の".claude/"配下ファイル(例:場合によってはCLAUDE.md、カスタムコマンドの定義MDファイル)も除外されてしまうため、ファイル単位での指定を推奨します。
なお、すでにGitに追加済みのファイルは".gitignore"ファイルに指定しても無視されないため、次の手順も必要です。
$ git rm --cached .claude/settings.local.json ←Gitの管理対象(インデックス)から外すが、実ファイルはローカルに残す。
$ git commit -m "Ignore local Claude settings"
git rmコマンドは、Gitのインデックス(=ステージングエリア)から削除するコマンドです。そのときに–cachedオプションをつけると、ワーキングツリー(手元のファイル)は削除しません。
以上により、".claude/settings.local.json"はローカルに存在し続ける(削除されない)一方で、コミット対象になりません。
以後、git statusコマンドに表示されません(無視されます)。
hooks.Stopが発動する典型パターン
- Run()やEvaluate()などの実行中に「Stop」ボタンを押したとき
- Claudeが長時間処理を試みていたが、手動で中断されたとき
- ユーザーが自作のフローをclaude.commandsから発火し、中断したとき
- Claudeが自動的なマルチステップの処理を止めたときにも呼ばれる
- この曖昧なトリガーにより、明示的な「Stop」ボタンを押さなくてもClaude側の内部処理が止まると、hooks.Stopが発動する。
hooks.Stopで実現できる理由
前述の設定により、Claudeにコード修正などの実装を指示した場合、何も止めていないのに通知サウンドが鳴ります。
これは、Claudeがコード生成後に「自動的に提案(処理)を終了したとき」にhooks.Stopが発火している可能性が極めて高いです。
Claude内部の挙動的には、以下のとおりです。
1:Claudeがコード生成やファイルの書き換えを実行する。
2:Claudeは「次にすべき処理がない」と判断し、ステップの自動終了に入る。
3:Claude Code側が「ユーザーの明示的な終了ではない中断」と判断し、hooks.Stopを発動。
4:その結果、hooks.Stopに設定したコマンド(=サウンド通知)が発動する。
これは正確な「Stop」じゃなくてもStopとみなされるのに過ぎません。とくに、Claudeの出力が以下のような形になると、Stop判定されやすくなります。
- 完了しました。
- 変更を保存しました。
- この修正で問題が解決するはずです。
- (以降に何もステップ提案が続かない)
Claudeはマルチステップ推論を持っているため、「処理が続かない」と判断されるとステップ完了となり、結果的にStop扱いになることがあるのです。
設定の挙動としては想定どおりですが、使い方としては誤解を招きやすい状況といえます。
グローバル設定にできないか実験してみた <おまけ>
Claudeの設定は、グローバル設定とプロジェクト単位でのローカル設定があります。
もし、hooksの通知音がなるように設定したsettings.local.jsonファイルをグローバル設定に配置することでうまくいけば、プロジェクトを作る度にコピーしたり、.gitignoreファイルを編集したりする手間が省けると考えました。
しかし、実験(2025/8/6時点)の結果は、残念ながら「できない」という結果になりました。
パス | 用途 | settings.local.json読み込まれるか? ※2025/8/6時点 | 備考 | |
---|---|---|---|---|
グローバル設定 | ~/.claude/ | すべてのプロジェクトに共通して適用される設定 | ❌ “~/.claude/settings.local.json"ファイルは読み込まない | ・"~/.claude/commands/<コマンド名>.md"でカスタムスラッシュコマンドを配置すればClaude Code(CLI)で使える。 |
プロジェクト単位でのローカル設定 | <プロジェクトルート>/.claude/ | そのプロジェクトに限って上書きしたい設定 | ✅ <プロジェクトルート>/.claude/settings.local.json"ファイルは読み込まれる。 |
※同一ファイルがあれば、プロジェクト側ファイルが優先されます。
“~/.claude/settings.json"ファイルは、Claudeが内部的に使う状態保存ファイルです。
ユーザーのGUI 設定、フィードバック表示履歴、テーマ設定など。
ここにpermissionsやhooks を書いても反映されません。
別アプローチ
別解としては以下が挙げられます。
- Claudeへの指示に「exec(“play_sound")」を最後に挿入させる。
- 中断と完了を区別したいhooks.Stopとは別に、hooks.Done相当の自作hookを作る。
- 現在Claude Codeには公式なhooks.Doneはないが、自作で組み込める。
- “.claude/commands/"に完了通知コマンドを定義し、Run(…) で呼ばせる。
- 通知を制御したいmatcherに絞り込み条件(例:ステップ中にだけ)をつける。
自作hookによる実現
以下の拡張コマンドを用意します。
{
"name": "Play Notification Sound",
"command": "powershell -Command \"(New-Object Media.SoundPlayer 'C:\\\\Users\\\\owner\\\\OneDrive\\\\Documents\\\\claude\\\\marisa_owattaze.wav').PlaySync()\""
}
そのうえで、Claudeに指示する際に通知サウンドを鳴らしたい場合、以下のように末尾の文でRun()を呼び出すように指示します。なお、Run()には自作コマンド名を指定しています。
このコードを修正してください。
修正が完了したら Run(“Play Notification Sound") を最後に実行してください。
現状hooks.Stopの運用で支障がなく、別解については試していません。
そのため、本当にうまくいくかどうかは検証できていません。
References
↑1 | このツール内で音声ファイルは保存できます。 |
---|