Claude Code Task Alerts: Bells & Desktop Notifications
To get desktop notifications and bell sounds for Claude Code task completion, you use a combination of shell hooks and the terminal’s built-in notification system. The most direct method is adding a Stop hook in your settings that triggers a desktop notification and plays a sound when a task finishes.
Background: Why Task Alerts Matter
Claude Code is Anthropic’s command-line AI coding assistant. It runs tasks that can take anywhere from a few seconds to several minutes, especially when generating code, running tests, or performing multi-file edits. When you’re working in another window or waiting for a long operation, there’s no obvious visual cue that the task has completed. This is where hooks and desktop notifications come in—they let you step away from the terminal and still know exactly when Claude Code is ready for your input.
The official way to customize this behavior is through hooks, which are shell commands that run at specific points in the Claude Code lifecycle. The most relevant hooks are Stop (fires when a task finishes) and SubagentStop (fires when a subagent finishes). These hooks are defined in a JSON settings file, and they can call any command your system supports—including notification utilities and sound players.
How Hooks Work in Claude Code
Hooks are configured in a JSON file. For user-level settings, the file is typically at ~/.claude/settings.json on macOS/Linux or %USERPROFILE%\.claude\settings.json on Windows. Project-level settings live in .claude/settings.json inside your project folder. The structure looks like this:
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "your-command-here"
}
}
}
]
}
}
The matcher field lets you filter which tasks trigger the hook—an empty string matches all tasks. The command field is a shell command that runs when the hook fires. You can add multiple commands by adding more objects to the hooks array.
When the Stop hook fires, Claude Code passes a JSON object to the command via stdin. That object contains details like the session ID, transcript path, and the reason the task stopped. Most notification commands don’t need this data, but it’s available if you want to include task specifics in your alert.
Setting Up Desktop Notifications
The exact command depends on your operating system and desktop environment. Here are the most common approaches:
- macOS: Use
osascript -e 'display notification "Task complete" with title "Claude Code"'. This shows a standard macOS notification banner. - Linux (with libnotify): Use
notify-send "Claude Code" "Task complete". If you’re using a minimal window manager, you may need to installlibnotify-binor equivalent. - Windows: Use PowerShell:
powershell -Command "New-BurntToastNotification -Text 'Claude Code', 'Task complete'". The BurntToast module must be installed first. Alternatively, you can use a simple VBScript message box, but that requires extra setup.
To combine a notification with a bell sound, you can chain commands with ; or &&. For example, on macOS:
osascript -e 'display notification "Task complete" with title "Claude Code"' ; afplay /System/Library/Sounds/Glass.aiff
On Linux, you might use paplay or aplay depending on your audio system. On Windows, you can use [console]::beep(800,300) in PowerShell.
Adding a Bell Sound
If you only want a bell sound without a desktop notification, you can skip the notification command and just play a sound. The terminal bell itself is the simplest option: you can output the bell character \a using echo -e "\a". However, many modern terminals have the bell disabled or set to visual mode only, so you may not hear anything.
For a more reliable sound, use a system audio file. On macOS, the afplay command can play any of the built-in sounds in /System/Library/Sounds/. On Linux, paplay (PulseAudio) or aplay (ALSA) work. On Windows, PowerShell’s [console]::beep() is the simplest built-in option, but it’s limited to a single tone.
You can also use a tool like spd-say on Linux to speak a phrase like “Task complete,” which is useful if you’re not looking at the screen.
How to Choose the Right Approach
Your choice depends on your workflow and environment:
- If you’re always at the computer but in another app: A desktop notification is best because it’s unobtrusive and doesn’t require sound.
- If you’re away from the desk: A loud bell or spoken message is more effective. Consider using a sound that’s distinct from other system sounds.
- If you’re in a quiet office: A visual notification might be sufficient. You can also make the terminal window flash using
printf '\e[?5h'(reverse video) and reset it withprintf '\e[?5l', but this can be annoying. - If you run long tasks overnight: You might want a sound that’s loud enough to wake you, but also consider logging to a file so you can check the result later.
You can also set different hooks for different matchers. For example, you could have a quiet notification for quick edits and a loud bell for long-running test suites. The matcher uses regex to match against the task’s tool call, so you can filter by tool name or other criteria.
Common Problems and Troubleshooting
Here are the most frequent issues people run into:
- Hook not firing: Make sure the JSON is valid and the file is in the correct location. Check the Claude Code logs (usually in
~/.claude/logs/) for errors. Also, ensure the hook command is in your PATH—use absolute paths if unsure. - Notification not showing: On macOS, notifications may be muted if your terminal app is in Focus mode or if notifications are disabled for that app. On Linux,
notify-sendrequires a notification daemon likedunstormako. - Sound not playing: Check that the audio file path is correct and that your system’s audio isn’t muted. On Windows, PowerShell’s
Beepmethod doesn’t work in all terminal hosts—try usingWScript.Shellinstead. - Command runs but nothing happens: Some commands (like
notify-send) need a display environment. If you’re running Claude Code over SSH, the notification will appear on the remote machine, not your local one. In that case, you’ll need to forward the display or use a different method like sending a message to your phone. - Multiple notifications for one task: If you have both
StopandSubagentStophooks, you’ll get a notification for each subagent that finishes. You can avoid this by only usingStopor by filtering with matchers.
Alternatives to Hooks
If you don’t want to edit JSON files, there are other ways to get alerts:
- Watch the terminal manually: This is the default, but it’s not practical for long tasks.
- Use a terminal multiplexer like tmux: You can enable monitoring in tmux to highlight the window when output changes, but this doesn’t give a desktop notification.
- Use a third-party tool: Some terminal emulators (like iTerm2 on macOS) have built-in triggers that can run a script when a specific string appears (e.g., “Task complete”). This can be more flexible than hooks because you can match any output, not just the stop event.
- Write a wrapper script: Instead of running
claudedirectly, you can create a script that runs the command and then plays a sound or sends a notification when it exits. This gives you full control but requires you to remember to use the script.
Hooks are the most integrated approach because they’re built into Claude Code and don’t require external tools. However, they’re limited to the predefined lifecycle events.
Frequently Asked Questions
Can I get a notification only when a task fails?
Yes, but not directly. The Stop hook fires regardless of success or failure. You can inspect the JSON passed to the hook to check the stop_hook_active or other fields, but it’s simpler to have the hook run a script that checks the exit code of the last command. For example, you could wrap your Claude Code command in a script that checks the exit code and only then plays a sound.
Do hooks work with Claude Code in VS Code or other editors?
Hooks are part of the CLI, so they work regardless of where you invoke Claude Code. If you’re using an editor extension that runs Claude Code in the background, the hooks still fire, but the notification may appear on your desktop. Some editors have their own notification systems that you can use instead.
Can I use a custom sound file?
Yes. Just replace the system sound path with the path to your own audio file. The command must be able to play the file format (e.g., afplay supports many formats on macOS, paplay supports OGG/WAV on Linux, and Windows PowerShell can play WAV files with System.Media.SoundPlayer).
Will the hook slow down Claude Code?
No. Hooks are executed asynchronously—Claude Code doesn’t wait for the command to finish before continuing. However, if your command is slow (like a long sound file), the notification might be delayed slightly. It’s best to keep the command lightweight.
How do I test a hook without running a full task?
You can manually trigger a hook by sending a test message to Claude Code. For example, you can run claude -p "echo test" (if you have the CLI) which will start a task and stop it immediately. Alternatively, you can run the hook command directly in your terminal to verify it works.
What if I’m using Claude Code on a remote server?
Desktop notifications won’t appear on your local machine unless you set up X11 forwarding (on Linux) or use a tool like ssh -X. A more portable solution is to use a notification service like ntfy or pushover that sends a message to your phone. You can call their HTTP APIs from the hook command.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.