Never miss when Claude finishes. Get sound + banner notifications on any terminal.
One command install · Works on every terminal · No dependencies
Quick Start · Manual Setup · Sound Profiles · Troubleshooting
Claude Code uses terminal escape sequences for notifications. They don't work.
| Terminal / Setup | Result |
|---|---|
Ghostty (even with desktop-notifications = true) |
❌ Silent |
| Any terminal + Zellij or tmux | ❌ Multiplexer strips escape codes |
| Alacritty, Warp, VS Code, Terminal.app | ❌ No notification support |
skipDangerousModePermissionPrompt: true |
❌ Notification event never fires |
You leave to grab coffee. You come back. Claude finished 5 minutes ago. No sound. No banner. Nothing.
This repo fixes that — permanently, on every terminal.
macOS:
curl -fsSL https://raw.githubusercontent.com/GkyEla/claude-code-sound-notifications/main/install.sh | bashLinux:
curl -fsSL https://raw.githubusercontent.com/GkyEla/claude-code-sound-notifications/main/install.sh | bashThe installer will:
- Auto-detect your OS
- Let you pick a notification sound
- Preview the sound before applying
- Add or update only this project's
Stophook - Preserve other tools' hooks and back up
settings.jsonbefore changes
curl -fsSL https://raw.githubusercontent.com/GkyEla/claude-code-sound-notifications/main/uninstall.sh | bashThe uninstaller removes only this project's notification hook and preserves unrelated Stop hooks.
Add the hooks block to ~/.claude/settings.json:
macOS
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code finished\" with title \"Claude Code\" sound name \"Glass\"'"
}
]
}
]
}
}Linux
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Claude Code finished' --urgency=normal"
}
]
}
]
}
}Linux (with sound)
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Claude Code finished' --urgency=normal && paplay /usr/share/sounds/freedesktop/stereo/complete.oga"
}
]
}
]
}
}Full settings.json example
{
"alwaysThinkingEnabled": true,
"effortLevel": "high",
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code finished\" with title \"Claude Code\" sound name \"Glass\"'"
}
]
}
]
}
}Restart Claude Code after saving. Run /hooks inside Claude Code to verify.
Claude Code has a built-in Notification event — but it's broken for most users:
| Event | When It Fires | Reliability |
|---|---|---|
Notification |
Permission prompts, idle state, auth | ❌ Never fires with skipDangerousModePermissionPrompt |
Stop |
Every time Claude finishes responding | ✅ Always fires |
Most power users enable dangerous mode. If you do, the Notification event is completely dead.
Stop is the only reliable event.
Claude Code finishes responding
│
▼
"Stop" hook fires
│
▼
osascript / notify-send
│
▼
OS Notification Center
│
▼
Sound + Banner 🔔
No terminal escape sequences. No dependency on Ghostty, Kitty, or any terminal feature. Direct OS-level notification that always works.
| # | Sound | Vibe | Try it |
|---|---|---|---|
| 1 | Glass | Clean, subtle | osascript -e 'display notification "test" with title "test" sound name "Glass"' |
| 2 | Ping | Classic notification | osascript -e 'display notification "test" with title "test" sound name "Ping"' |
| 3 | Pop | Quick, light | osascript -e 'display notification "test" with title "test" sound name "Pop"' |
| 4 | Hero | Bold, triumphant | osascript -e 'display notification "test" with title "test" sound name "Hero"' |
| 5 | Purr | Soft, gentle | osascript -e 'display notification "test" with title "test" sound name "Purr"' |
| 6 | Submarine | Deep, distinct | osascript -e 'display notification "test" with title "test" sound name "Submarine"' |
| 7 | Frog | Fun, unmissable | osascript -e 'display notification "test" with title "test" sound name "Frog"' |
| 8 | Sosumi | The OG Mac sound | osascript -e 'display notification "test" with title "test" sound name "Sosumi"' |
| 9 | Tink | Minimal, delicate | osascript -e 'display notification "test" with title "test" sound name "Tink"' |
Drop any .aiff file into ~/Library/Sounds/ and use its filename (without extension) as the sound name.
| Terminal | Direct | + Zellij | + tmux |
|---|---|---|---|
| Ghostty | ✅ | ✅ | ✅ |
| Kitty | ✅ | ✅ | ✅ |
| iTerm2 | ✅ | ✅ | ✅ |
| Alacritty | ✅ | ✅ | ✅ |
| Warp | ✅ | ✅ | ✅ |
| macOS Terminal | ✅ | ✅ | ✅ |
| VS Code Terminal | ✅ | ✅ | ✅ |
Works everywhere — because it doesn't use the terminal for notifications.
No sound or notification
- macOS: Check System Settings → Notifications → Script Editor — make sure it's allowed
- Make sure Do Not Disturb / Focus mode is off
- Validate JSON:
python3 -m json.tool ~/.claude/settings.json - Restart Claude Code after editing settings
- Test manually:
# macOS osascript -e 'display notification "Test" with title "Test" sound name "Glass"' # Linux notify-send "Test" "Test"
Hook not firing
- Run
/hooksinside Claude Code — verifyStopappears - Hooks must be inside
settings.json, not a separatehooks.jsonfile - Check for JSON syntax errors (trailing commas, missing brackets)
Want to change the sound
Re-run the installer or manually edit ~/.claude/settings.json — replace the sound name in the osascript command.
Found a better approach? Support another OS? Improvements welcome.
- Fork this repo
- Make your changes
- Open a PR
MIT — use it however you want.
If this saved you from notification silence, give it a ⭐