01

One Line

The shortest honest version.

What it is You finish a turn in Claude Code or Codex, the final reply gets handed to a tiny local voice layer, and your Mac speaks it out loud unless you interrupt it first.
02

Journey

From first prompt to sound.

1. You open an agentClaude Code or Codex starts as usual. Nothing extra in your workflow.
2. You ask for workThe agent thinks, writes, and answers the way it already does.
3. A hook notices the endWhen the turn finishes, the final reply is handed off automatically.
4. The voice layer wakesIf the speaker service is asleep, it starts quietly in the background.
5. You hear the answerThe reply becomes speech, unless it is too long or gets interrupted.
03

Normal Day

What you usually experience.

Open Claude Code or CodexThe hook system is already attached, so the voice layer waits in the background for a completed turn.
Write a promptNothing special happens yet. The agent just behaves like a normal terminal agent.
Agent finishesThe final reply is captured and sent onward. That is the only moment speech starts.
Speech playsThe reply is spoken locally. If you keep reading, the next line is already ready for the next turn.

What feels different

You do not launch a separate app. You just use Claude or Codex normally, and the voice follows the conversation instead of sitting in front of it.

04

Lifecycle

Boot, idle, sleep, and shutdown, the full loop.

Fresh bootNothing is running. No daemon, no loaded speaker, machine just sitting there.
First hook firesIt checks for the daemon, starts it if needed, and hands over the text. That first turn wakes everything.
SpeakingThe daemon stays warm for as long as turns keep completing.
Quiet stretchThe daemon exits on its own after being idle, keeping memory use low.
Next turn wakes itThe cycle repeats. Nothing is restarted manually.
Forcing it awakeA restart button in the controller respawns the daemon on demand, useful right after editing the daemon itself. It is separate from sleep: it is a deliberate kick, not the idle exit.

Closing everything

SituationStory
Quit Claude Code or CodexThe hooks stop firing because there is no turn to listen to anymore.
Close the terminalThe voice layer has nothing to react to. It just waits until the next real session starts.
Restart the computerEverything is gone again. The next time you open an agent, the same sequence starts over from the beginning.
05

Attention

Interrupts, and what happens when two projects talk.

When you interrupt

You type againThe act of entering a new prompt is the stop signal. The current speech should be cut off immediately.
The old voice stopsThe current playback gets killed so it does not keep talking over the new turn.
The new turn takes overOnly the latest response matters. Old speech should never come back after you have moved on.

What this means in practice

If you interrupt the agent, the old speech is treated as obsolete. The stop is scoped to the project you typed in, so a second project speaking at the same time keeps talking. The system respects your attention, not stale output.

When two projects talk

Two agents, two reposClaude in one project, Codex in another, both finishing turns within minutes of each other. This is the normal evening, not the edge case.
The voice names the sourceReplies get a spoken prefix: "In client-site", "From voice-layer", "Update from docs". The wording rotates so it never sounds robotic.
Interrupts stay localTyping in one project silences only that project's speech. The other one keeps talking.

When prefixes appear

Only when more than one project has spoken in the last ten minutes. Solo work stays clean and unprefixed.

06

Long Answers

Why everything is not spoken verbatim.

Short repliesThese can be spoken almost directly, so the answer stays crisp and human.
Long repliesThese get condensed into a short spoken summary by local Ollama, so the speaker does not ramble through dense prose. Set the provider to none for the deterministic fallback.
Behavior and historyAttributable diffs become behavioral facts, while a compact local timeline connects earlier failures to later fixes. It stores outcomes and evidence, never prompt, reply, or source text.
TablesA reply with a table is never read row by row. The full reply goes to the condenser first, and the voice speaks the short table summary. If that fails, it simply tells you to look.
07

Controller

The dashboard, if you want to look under the hood.

Open the pageThe daemon serves a control panel in your browser. It updates live as the voice speaks, no refreshing.
The stageWhen a reply is spoken, you see its real waveform draw itself with a playhead sweeping along, and the text scrolling by.
The mixing deskDrag a line across a chart of your past replies to set how long an answer can be before it gets summarized. You aim it by eye against real data.
Media duckingIf enabled, Spotify and Browser YouTube fade down only while TTS is playing. A restoration lease repairs Spotify after an interrupted daemon, and the browser adapter restores from daemon state.
The shootoutCompare installed TTS engines on the same sentence, replay each clip, run blind A/B, and keep the winner without the page making decisions for you.
The ledgerEvery reply is a dot on a timeline, one lane per project. Click any dot to hear it again. Several projects finishing together can collapse into one spoken radio bulletin.
Attention cuesShort repo and intent sounds tell you where a line came from and whether it is a success, warning, blocker, or request. The spoken summary stays brief.
Voice inboxEnable it when stepping away. Agent replies accumulate locally without speaking; press I'm back for one concise return briefing.
BackchannelRepeat, slow down, speed up, or switch to brief mode from the controller or CLI.

Why it exists

The taste knobs turned out to be the interesting part. The controller tunes them, but it never decides anything: it is a remote, and the daemon stays the brain.

08

Voices

Who does the talking.

Engine catalogsSpeech is generated fresh every time. Pocket stays default with its catalog, and Kokoro exposes its own 54 voices when it is the active engine.
One per agentClaude, Codex, and notifications each get their own voice, so your ear knows who is talking before you read a word.
Your own voiceDrop in an audio clip or record yourself, and the system can speak in that voice. This waits on a one-time setup with the model's makers before it wakes up.
09

Shut Up

Silence that sticks.

SHUT UPPress Space or drop a file named .voice-disabled in the repo root. The hook sees it before anything else and refuses to wake the daemon or speak. No restart, no config.
CHILLUse this when the model is already speaking and you only want the current sentence to stop. The next completed turn can still speak.
10

How To Test

Simple confidence checks.

HealthAsk the daemon if it is alive. That only tells you the speaker service is ready, not that hooks are wired.
./voice status
Manual hookFeed a fake completed turn to the hook and listen for speech. That tests the path end to end.
printf '%s' '{"hook_event_name":"Stop","last_assistant_message":"voice test"}' | ./.venv/bin/python hook.py
Media duckEnable Spotify or Browser YouTube ducking, trigger speech, and listen for fade down during TTS plus exact restore after. Chill should restore too.
Claude turnRun a normal Claude Code reply and let the real Stop hook fire.
claude -p 'Reply with exactly: voice test'
Codex turnRun a normal Codex reply once the hook is trusted, and confirm the same behavior there.
codex --dangerously-bypass-hook-trust exec 'Reply with exactly: voice test'
Shut UpCreate .voice-disabled, run a turn, confirm silence. Delete it, confirm the voice comes back.
touch .voice-disabled
The logEvery spoken or skipped reply lands in .voice.log, with what was stripped, condensed, and prefixed.
tail -f .voice.log