claude-speaks
Listen to your Claude Code agents.
A Claude Code plugin that turns each session's response into a one- or two-sentence verbal status line so you can supervise many background agents by ear — local, over an SSH tunnel to your Mac, or as a push notification to your phone.
Built for the "I'm watching a tmux dashboard of half a dozen background claude sessions and I want to hear when each one finishes or asks me something" scenario.
How it works
Claude finishes a turn
└── Stop hook fires (also Notification)
└── scripts/on-stop.sh
├── extracts last assistant message from the transcript JSONL
├── scripts/summarize.sh → Claude Haiku 4.5
│ "Tests pass, ready for review."
│ "Needs you: blocked on missing API key."
│ "Failed: build is red, migration not applied."
└── scripts/dispatch.sh
├── local → say / piper / espeak-ng (flock-serialized)
├── relay → POST to your Mac via SSH -R tunnel
└── ntfy → push notification to your phone
The summarizer's system prompt is tuned for TTS: plain English, no markdown, no paths, no tool names, max ~25 words, and Needs you: / Failed: prefixes on anything that warrants your attention.
Sessions are serialized so five agents finishing at once speak one after another, not on top of each other. Each utterance is prefixed with <project> agent <short-id>: so you know who's talking.
Install
Via marketplace (recommended)
/plugin marketplace add coflounder/claude-speaks
/plugin install claude-speaks
Local development
git clone https://github.com/coflounder/claude-speaks ~/claude-speaks
claude --plugin-dir ~/claude-speaks
Configure
Set these in your shell profile (or per-tmux-pane for ad-hoc routing):
| Variable | Purpose | Default |
|---|
CLAUDE_SPEAKS_BACKEND | local, relay, ntfy, or a comma-separated combination. | local |
CLAUDE_SPEAKS_RELAY_URL | HTTP endpoint of the relay daemon. | http://localhost:8765/speak |
CLAUDE_SPEAKS_NTFY_TOPIC | ntfy.sh topic name. | — |
CLAUDE_SPEAKS_NTFY_SERVER | Self-hosted ntfy server. | https://ntfy.sh |
CLAUDE_SPEAKS_DISABLED | Set to 1 to mute this session. | 0 |
Summarization uses the claude CLI in headless mode (claude -p --model claude-haiku-4-5-20251001), which reuses your existing Claude Code authentication — no separate ANTHROPIC_API_KEY is required. The plugin sets CLAUDE_SPEAKS_SUMMARIZING=1 around that call and the Stop hook bails out when it sees that variable, so the nested invocation does not recurse.
You can also drop a marker file at $CLAUDE_PLUGIN_DATA/disabled to mute every session on this machine until you remove it.
Deployment scenarios
Mac, local agents
export CLAUDE_SPEAKS_BACKEND=local
That's it — uses macOS say directly.
Linux box, local audio
sudo apt-get install -y jq espeak-ng # or: pip install piper-tts (better voice)
export CLAUDE_SPEAKS_BACKEND=local
Phone (Termux) + remote server, SSH reverse-tunnel
You're SSH'd into the server from your phone and want the phone itself to speak summaries aloud — hands-free, without tapping notifications.
# On the phone, in Termux:
pkg install python termux-api git
# Also install the "Termux:API" app from F-Droid (separate Android package
# that exposes Android's TTS to termux-tts-speak).
git clone https://github.com/coflounder/claude-speaks.git
python claude-speaks/scripts/relay-daemon.py
# -> "claude-speaks relay listening on http://127.0.0.1:8765 (engine: termux-tts-speak)"
# In another Termux session, SSH to the server with a reverse tunnel:
ssh -R 8765:localhost:8765 user@server
# On the server (or in your background-agent env):
export CLAUDE_SPEAKS_BACKEND=relay
export CLAUDE_SPEAKS_RELAY_URL=http://localhost:8765/speak
The daemon auto-detects termux-tts-speak and uses Android's built-in TTS — no separate voice install, no audio routing. Combine with ntfy as a fallback when the SSH session drops:
export CLAUDE_SPEAKS_BACKEND=relay,ntfy
Remote server + Mac, SSH tunnel
The most common case for a tmux dashboard.
# On your Mac, in a long-running terminal:
python3 ~/claude-speaks/scripts/relay-daemon.py
# Connect to your server with a reverse tunnel:
ssh -R 8765:localhost:8765 your-server
# On the server (or in your background-agent env):
export CLAUDE_SPEAKS_BACKEND=relay
export CLAUDE_SPEAKS_RELAY_URL=http://localhost:8765/speak
Every Claude session on the server pipes summaries through the tunnel and out your Mac's speakers, in arrival order across all sessions.
Per-project voices keep things distinguishable:
export CLAUDE_SPEAKS_VOICE_MAP="codetrotter=Daniel,danka=Karen,groundfish=Moira"
Phone push