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.
The shortest honest version.
From first prompt to sound.
What you usually experience.
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.
Boot, idle, sleep, and shutdown, the full loop.
Closing everything
| Situation | Story |
|---|---|
| Quit Claude Code or Codex | The hooks stop firing because there is no turn to listen to anymore. |
| Close the terminal | The voice layer has nothing to react to. It just waits until the next real session starts. |
| Restart the computer | Everything is gone again. The next time you open an agent, the same sequence starts over from the beginning. |
Interrupts, and what happens when two projects talk.
When you interrupt
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
Only when more than one project has spoken in the last ten minutes. Solo work stays clean and unprefixed.
Why everything is not spoken verbatim.
none for the deterministic fallback.The dashboard, if you want to look under the hood.
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.
Who does the talking.
Silence that sticks.
.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.Simple confidence checks.
./voice statusprintf '%s' '{"hook_event_name":"Stop","last_assistant_message":"voice test"}' | ./.venv/bin/python hook.pyclaude -p 'Reply with exactly: voice test'codex --dangerously-bypass-hook-trust exec 'Reply with exactly: voice test'.voice-disabled, run a turn, confirm silence. Delete it, confirm the voice comes back.
touch .voice-disabled.voice.log, with what was stripped, condensed, and prefixed.
tail -f .voice.log