Ever walked away from your terminal while Claude Code was working, only to come back and find it’s been waiting for your input for ages? Or maybe you’re working on something else and want to know when Claude finishes a long task?
This guide shows you how to make Claude Code speak to you when it needs attention or completes work—using built-in OS speech synthesis and Claude Code’s powerful hooks system.
What Are Claude Code Hooks?
Hooks are custom shell commands that Claude Code executes at specific lifecycle events. You can trigger actions when:
- Notification — When Claude needs your attention
- Stop — When Claude finishes or pauses
- SubagentStop — When a background sub-agent completes
- PreToolUse — Before Claude uses a tool
- PostToolUse — After Claude uses a tool
We’ll use Notification, Stop, and SubagentStop hooks to play voice alerts.
Quick Start: The Settings File
Claude Code stores its configuration in settings.json. The location varies by OS:
| OS | Path |
|---|---|
| macOS | ~/.claude/settings.json |
| Linux | ~/.claude/settings.json |
| Windows | %USERPROFILE%\.claude\settings.json |
If the file doesn’t exist, create it. Here’s how to find/create it:
# macOS/Linux - Open or create the settings file
mkdir -p ~/.claude && nano ~/.claude/settings.json
# Windows PowerShell - Open or create the settings file
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.claude"
notepad "$env:USERPROFILE\.claude\settings.json"
Windows: Complete Working Configuration
Here’s a tested, working configuration for Windows using the SAPI COM object:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "powershell -Command \"(New-Object -ComObject SAPI.SpVoice).Speak('Claude has finished')\"",
"timeout": 10
}
]
}
],
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "powershell -Command \"(New-Object -ComObject SAPI.SpVoice).Speak('Claude needs your attention')\"",
"timeout": 10
}
]
}
],
"SubagentStop": [
{
"hooks": [
{
"type": "command",
"command": "powershell -Command \"(New-Object -ComObject SAPI.SpVoice).Speak('Sub agent has completed')\"",
"timeout": 10
}
]
}
]
}
}
Key Points
timeout— Prevents hooks from hanging; 10 seconds is usually plentySAPI.SpVoice— Windows Speech API COM object; works out of the boxSubagentStop— Fires when background agents (launched via Task tool) complete
Customizing Windows Voices
List available voices in PowerShell:
# List all installed voices
$voice = New-Object -ComObject SAPI.SpVoice
$voice.GetVoices() | ForEach-Object { $_.GetDescription() }
Use a specific voice:
{
"hooks": {
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "powershell -Command \"$v = New-Object -ComObject SAPI.SpVoice; $v.Voice = $v.GetVoices() | Where-Object { $_.GetDescription() -like '*Zira*' } | Select-Object -First 1; $v.Speak('Claude needs your attention')\"",
"timeout": 10
}
]
}
]
}
}
Common Windows Voices
| Voice | Description |
|---|---|
Microsoft David | American male |
Microsoft Zira | American female |
Microsoft Mark | American male |
Microsoft Hazel | British female |
Tip: Install additional voices via Settings → Time & Language → Speech → Add voices.
macOS: Using the say Command
macOS has a built-in say command that converts text to speech.
Basic Configuration
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "say 'Claude has finished'",
"timeout": 10
}
]
}
],
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "say 'Claude needs your attention'",
"timeout": 10
}
]
}
],
"SubagentStop": [
{
"hooks": [
{
"type": "command",
"command": "say 'Sub agent has completed'",
"timeout": 10
}
]
}
]
}
}
Customizing the Voice
macOS has dozens of voices. List them with:
say -v '?'
Use a specific voice by adding the -v flag:
{
"hooks": {
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "say -v 'Samantha' 'Hey! Claude needs your input'",
"timeout": 10
}
]
}
]
}
}
Popular macOS Voices
| Voice | Description |
|---|---|
Samantha | Clear American female (default) |
Alex | American male, very natural |
Daniel | British male |
Karen | Australian female |
Moira | Irish female |
Tessa | South African female |
Whisper | Whispering voice |
Adjusting Speech Rate
Speed up or slow down with the -r flag (words per minute):
# Faster (250 wpm)
say -r 250 'Claude needs attention'
# Slower (150 wpm)
say -r 150 'Claude has completed'
Linux: Using espeak or festival
Linux doesn’t have built-in speech, but you can install lightweight TTS tools.
Using espeak
Install espeak:
# Ubuntu/Debian
sudo apt install espeak
# Fedora
sudo dnf install espeak
# Arch
sudo pacman -S espeak
Configure Claude Code:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "espeak 'Claude has finished'",
"timeout": 10
}
]
}
],
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "espeak 'Claude needs your attention'",
"timeout": 10
}
]
}
],
"SubagentStop": [
{
"hooks": [
{
"type": "command",
"command": "espeak 'Sub agent has completed'",
"timeout": 10
}
]
}
]
}
}
Using festival
Festival sounds more natural than espeak:
# Ubuntu/Debian
sudo apt install festival
# Usage
echo "Claude needs your attention" | festival --tts
{
"hooks": {
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "echo 'Claude needs your attention' | festival --tts",
"timeout": 10
}
]
}
]
}
}
Advanced: Conditional Notifications with Matchers
Use the matcher field to trigger hooks only for specific tools or conditions.
Only Notify on Specific Tools
Play a sound only when Claude wants to use the Bash tool:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "say 'Claude wants to run a command'",
"timeout": 10
}
]
}
]
}
}
Combining Multiple Hook Types
{
"hooks": {
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "say -v 'Samantha' 'Hey! I need your help'",
"timeout": 10
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "say -v 'Samantha' 'All done!'",
"timeout": 10
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "say -v 'Whisper' 'Running command'",
"timeout": 5
}
]
}
]
}
}
Playing Sound Effects Instead
Don’t want voice? Play an audio file instead.
macOS
{
"hooks": {
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "afplay /System/Library/Sounds/Glass.aiff",
"timeout": 5
}
]
}
]
}
}
Built-in macOS sounds are in /System/Library/Sounds/:
Glass.aiffPing.aiffPop.aiffSubmarine.aiffHero.aiff
Windows
{
"hooks": {
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "powershell -Command \"(New-Object Media.SoundPlayer 'C:\\Windows\\Media\\chimes.wav').PlaySync()\"",
"timeout": 5
}
]
}
]
}
}
Linux
{
"hooks": {
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "aplay /usr/share/sounds/alsa/Front_Center.wav",
"timeout": 5
}
]
}
]
}
}
Install aplay if needed:
sudo apt install alsa-utils
Troubleshooting
Hook Not Firing?
- Check JSON syntax — Use a JSON validator (jsonlint.com)
- Check file location — Must be in
~/.claude/settings.json(or Windows equivalent) - Restart Claude Code — Changes require restart
- Test command manually — Run the command in terminal first
- Check timeout — If command takes too long, it may be killed
Voice Not Playing?
macOS:
# Test say command
say "test"
# Check audio output
system_profiler SPAudioDataType
Windows:
# Test SAPI speech
(New-Object -ComObject SAPI.SpVoice).Speak("test")
Linux:
# Test espeak
espeak "test"
# Check audio
aplay -l
Common Issues
| Issue | Solution |
|---|---|
| No sound on Windows | Check volume, run (New-Object -ComObject SAPI.SpVoice).Speak("test") in PowerShell |
| Hook times out | Increase timeout value or use async playback |
| JSON parse error | Validate JSON syntax, escape backslashes on Windows |
| Command not found | Use full path to executable |
Complete Cross-Platform Configuration
Here’s a complete configuration that works well in practice:
Windows
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "powershell -Command \"(New-Object -ComObject SAPI.SpVoice).Speak('Claude has finished')\"",
"timeout": 10
}
]
}
],
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "powershell -Command \"(New-Object -ComObject SAPI.SpVoice).Speak('Claude needs your attention')\"",
"timeout": 10
}
]
}
],
"SubagentStop": [
{
"hooks": [
{
"type": "command",
"command": "powershell -Command \"(New-Object -ComObject SAPI.SpVoice).Speak('Sub agent has completed')\"",
"timeout": 10
}
]
}
]
}
}
macOS
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "say 'Claude has finished'",
"timeout": 10
}
]
}
],
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "say 'Claude needs your attention'",
"timeout": 10
}
]
}
],
"SubagentStop": [
{
"hooks": [
{
"type": "command",
"command": "say 'Sub agent has completed'",
"timeout": 10
}
]
}
]
}
}
Conclusion
With a few lines of configuration, you can turn Claude Code into a vocal assistant that keeps you informed without constant terminal monitoring. The timeout field ensures hooks never hang, and SubagentStop catches those background agent completions you might otherwise miss.
Now you’ll never miss when Claude needs your input—or when it’s finished working its magic.
Next steps:
- Explore other Claude Code tips in our guides
- Check out the official Claude Code documentation for more hook options
