Disclosure: I'm Kelly, and I work on Heard (heard.dev), a voice layer for coding agents. Most of this post is the free, do-it-yourself way, because for one session that is all you need. Heard comes up near the end as one option.
The Claude Code CLI listens but does not speak. Its /voice command is dictation: you talk and it types your prompt. The CLI has no spoken output, so if you want to hear when a task is done, you wire it up yourself. It takes one hook.
The Stop hook
The Stop event fires when Claude finishes responding. Its JSON input on stdin includes last_assistant_message, the text of Claude's final reply. Pipe that into macOS say. Add this to ~/.claude/settings.json:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "jq -r '.last_assistant_message // empty' | head -c 600 | say",
"async": true
}
]
}
]
}
}
You need jq (brew install jq). The field name and hook shape are checked against the Stop section of the Claude Code hooks reference (October 2026).
Two details matter:
-
head -c 600caps the length. Final replies can be long reports, andsayreads every word. -
"async": trueruns the hook in the background, so Claude Code carries on while the voice is still talking.
On Linux, replace say with spd-say -e from the speech-dispatcher package.
If you only want a sound, not the words, use afplay /System/Library/Sounds/Glass.aiff as the command instead.
Three small upgrades
-
Say which session finished. Every hook input includes
cwd, sojq -r .cwd | xargs basename | sayspeaks the folder name. -
Hear failures.
Stopdoes not fire when a turn ends on an API error. Claude Code firesStopFailureinstead, so add the same command there. -
Debug a silent hook. Run
/hooksinside Claude Code to see what loaded. A JSON syntax error in settings.json is the usual cause.
The same idea in Codex
Codex has a notify setting in ~/.codex/config.toml. It runs a command with one JSON argument when a turn completes, with type set to agent-turn-complete and the reply in last-assistant-message:
#!/usr/bin/env python3
import json, subprocess, sys
event = json.loads(sys.argv[1])
if event.get("type") == "agent-turn-complete":
text = (event.get("last-assistant-message") or "Codex finished.")[:300]
subprocess.Popen(["say", text])
Save it as ~/.codex/speak.py, then point Codex at it with its absolute path:
notify = ["python3", "/Users/you/.codex/speak.py"]
subprocess.Popen returns straight away, so Codex is not blocked while it speaks.
When several sessions get noisy
For one session, stop here. The hook is free and local. The trouble starts with three or four terminals:
-
It reads, it does not summarise.
sayreads markdown, file paths and code exactly as written. A reply full of paths is hard to follow by ear. - Every event sounds the same. "Done", "tests failed" and "waiting for approval" arrive as one voice reading text.
- Sessions talk over each other, and "finished" does not tell you which one needs you.
A few tools go further. Each description comes from the project's own README, read this week:
-
Aloud (softcane/aloud, MIT) speaks a short gist of each Claude Code and Codex reply on your Mac (about 240 characters by default) with local Kokoro speech, and
aloud fullreads the whole reply. You turn it on per session withaloud on; questions take priority over completion messages and routine tool calls stay silent. It wins if you want something free, local and per-session. It needs Python 3.11 or 3.12 and Homebrew. - VoiceMode (mbailey/voicemode, MIT) is for spoken back-and-forth conversations with Claude Code. It can run locally with Whisper and Kokoro or use OpenAI's cloud, and it runs on Linux, macOS and Windows. It wins if you want to talk with one session, or you are not on a Mac.
- Heard (heard.dev) is the one I work on, a Mac app (macOS 14 or later) for Claude Code and Codex. It speaks a sentence or two about what changed rather than the reply itself. Questions, blockers and failures come through first, and routine progress from parallel sessions is batched into short summaries that start with the project name. Focus mode keeps routine progress quiet and speaks only for questions, blockers and failures. On the Power plan you can answer back by voice. The engine is open source under Apache 2.0 with a local Kokoro voice and no account needed; the downloadable app is a managed build with a 14-day Power trial at signup, no card required. Where the others win: Heard is Mac only, and after the 14-day trial the app needs a paid plan (Pro or Power, on heard.dev/pricing); the free option is building the open-source engine from source.
There is a longer side-by-side of every Claude Code text-to-speech option on heard.dev, and a step-by-step sound-when-done guide with the hook variations.
Which one?
- One session, you just want to know it is done: the Stop hook or the Codex
notifyscript above. - Free, local speech in the sessions you choose, on a Mac: Aloud.
- A spoken conversation with Claude, on any OS: VoiceMode.
- Several Claude Code and Codex sessions, with short summaries and replies by voice: Heard.
If you build your own variation of the hook, I'd like to see it in the comments.
Top comments (0)