Real-time voice conversations for the Claude CLI.
Talk to Claude naturally from your terminal β speak, and hear it think back.
claude-voice is a native voice layer on top of the Claude CLI, not a
speech-to-text wrapper. You speak; your words are transcribed and sent to
claude; the streamed response is printed live and spoken aloud sentence by
sentence as it is generated. Start talking again at any time and it stops,
listens, and hands your interruption straight to Claude β like pair programming
out loud.
- π€ Speak to Claude β continuous listening with silence detection, or push-to-talk.
- π§ Wraps the Claude CLI β uses your existing
claudeinstall and session; no Claude API key required. - π Speaks as it thinks β responses are streamed to TTS one complete sentence at a time, so audio starts almost immediately.
- β Barge-in / interruption β cut in mid-sentence; playback and generation stop instantly and it starts listening again.
- π Pluggable providers β Groq Whisper (default) or any OpenAI-compatible STT; ElevenLabs or Sarvam TTS. Add your own.
- π©Ί
doctorβ one command verifies your CLI, keys, mic, speaker, and network. - π§© Extensible β an event-driven core with a plugin API for wake-words, clipboard, memory, notifications, MCP, and more.
- π¨ Polished, branded TUI β an Ink-powered Claude Voice AI interface: a gradient-branded header with a persistent watermark, a spinner that shimmers through the brand palette, a scrollback transcript, live token streaming, colored role labels (
β― You,β¦ Claude), and a status bar with a mic meter + timings. - β¨οΈ Talk or type β press
tto type a message instead of speaking, so you can use it even without a working mic.
# 1. Install globally
npm install -g @aayushdebugging/claude-voice
# 2. Make sure the Claude CLI is installed and logged in
# https://claude.com/claude-code
claude --version
# 3. Set your provider keys (see "Configuration" below)
export GROQ_API_KEY="β¦" # speech-to-text
export ELEVENLABS_API_KEY="β¦" # text-to-speech
# 4. Check everything is wired up
claude-voice doctor
# 5. Talk to Claude
claude-voiceYou'll also need a microphone recorder backend and audio output:
| Platform | Install |
|---|---|
| macOS | brew install sox (and brew install ffmpeg for gapless streaming) |
| Debian/Ubuntu | sudo apt-get install sox libsox-fmt-all alsa-utils ffmpeg |
| Windows | Install SoX and add it to PATH |
ffmpeg(itsffplay) is optional but recommended: it enables gapless streamed speech. Without it, playback falls back toafplay/aplayclips (still works, with a tiny seam between sentences).
See the Installation guide for details and troubleshooting.
claude-voice # start a conversation (continuous mode)
claude-voice --model opus # pick a Claude model (opus | sonnet | fable)
claude-voice --voice river # pick a TTS voice
claude-voice --stt groq # choose the speech-to-text provider
claude-voice --tts sarvam # choose the text-to-speech provider (elevenlabs | sarvam)
claude-voice --push-to-talk # press SPACE to talk instead of continuous
claude-voice --no-speak # text only, no spoken responses
claude-voice --language hi # set STT + TTS language (ISO-639-1) or "auto"
claude-voice --speed 1.5 # speech rate multiplier, 0.5β3.0 (1 = natural)
claude-voice --no-stream # speak the whole reply at once (default: as it streams)
claude-voice --fast-speech # lowest latency: start after the first clause (may sound choppier)
claude-voice --local # fully-local, free stack (whisper.cpp + Kokoro)In-session keys (default push-to-talk):
| Key | Action |
|---|---|
| SPACE | tap to start talking β it auto-stops when you finish speaking and transcribes. Tap again to send immediately, or to interrupt while Claude is replying. |
t |
type a message instead of speaking (Enter sends, Esc cancels, β/β recalls previous prompts) |
/ |
open the command palette |
q / Ctrl-C |
quit |
Command palette (press /):
| Command | Action |
|---|---|
/help |
show keys & commands |
/clear |
clear the transcript |
/mute Β· /speak |
toggle spoken responses on the fly |
/voice <name> |
switch the TTS voice live (e.g. /voice karun) |
/model <name> |
switch the Claude model (opus/sonnet/fable) for the next turn |
/speed <rate> |
set the speech rate live β 0.5β3.0 (e.g. /speed 1.5 or /speed 2x) |
/lang <code> |
switch language for STT + TTS (e.g. /lang hi, /lang auto) |
/stream |
toggle speaking-as-Claude-writes vs speaking the whole reply at once |
/quit |
exit |
While listening, a live mic-level meter in the status bar fills green as it hears you, so you always know the mic is working. Claude's answer streams to the screen as it's written and is spoken as it streams β text is chunked one sentence at a time and each sentence is synthesized ahead of playback, so audio starts moments after the first sentence and tracks the text as it appears. Sentences are the unit (rather than clauses) so the TTS engine voices internal punctuation β commas, dashes, colons β as natural pauses: splitting on every comma makes engines like Kokoro pad each fragment with a beat of trailing silence, i.e. an audible stop at each mark. For the lowest possible first-word latency at the cost of choppier prosody, --fast-speech chunks at clause boundaries instead. Generation, synthesis, and playback all run in parallel and never block each other.
For gapless streaming, install ffmpeg (brew install ffmpeg): the reply is fed sentence-by-sentence into a single persistent audio stream (ffplay), which waits during pauses rather than reopening the device β so it sounds like one continuous answer. Without a streaming player it falls back to playing a few back-to-back clips via afplay (a small seam between clips), and --no-stream (or /stream) speaks the whole reply at once with no seams at all. sox play is intentionally not used for streaming β it cuts off on the first pause.
While Claude generates, the status bar shows a live word count and words/sec, and each reply is tagged with its time and length (e.g. Claude Β· 2.4s Β· 45w).
The rich UI needs a real terminal. When output is piped or
CLAUDE_VOICE_PLAIN=1is set, it falls back to a plain streaming UI.
| Command | What it does |
|---|---|
claude-voice / claude-voice chat |
Start a live voice conversation. |
claude-voice serve |
Host a remote voice client on your Wi-Fi β talk to Claude from your phone. |
claude-voice say "<text>" |
Speak a phrase aloud with the configured voice (no mic needed). |
claude-voice config |
View or edit configuration. |
claude-voice local |
Set up / check the fully-local, free stack (whisper.cpp + Kokoro). |
claude-voice doctor |
Diagnose your setup (CLI, keys, mic, speaker, network). |
claude-voice update |
Check for and install the latest version. |
claude-voice version |
Print the installed version. |
Already installed it? Upgrade to the latest published version:
# global install:
npm install -g @aayushdebugging/claude-voice@latest
# or, if it's a project dependency:
npm update @aayushdebugging/claude-voiceFrom v0.1.2 onward you can also self-update in place:
claude-voice update # checks npm and installs the newest version
claude-voice update --check # just check, don't install
claude-voice version # what you're on nowNote:
npm updatetakes a bare package name (no@latest); usenpm install β¦@latestto force the newest, ornpm update β¦to move within your saved range.
Run entirely offline with open-source models β no cloud, no keys, $0:
- STT: whisper.cpp (Metal-accelerated on Apple Silicon)
- TTS: Kokoro (
kokoro-onnx, CPU, no PyTorch)
It's a few one-time steps. claude-voice local downloads the models and reports
status; you install the native runtimes once and start the two local servers
(npm can't install compiled binaries, and the servers are long-running, so those
stay in your hands).
1. Install the runtimes (one-time β native/Python, not from npm):
| Platform | Command |
|---|---|
| macOS | brew install sox ffmpeg whisper-cpp uv |
| Debian/Ubuntu | sudo apt-get install sox ffmpeg Β· then build whisper.cpp and install uv |
2. Download the models + check what's needed:
claude-voice localThis auto-downloads the whisper model (~150 MB, with a progress bar), writes the
Kokoro server script to ~/.claude-voice/kokoro-server.py, and prints a β/β status
report with the exact command for anything still missing. (--no-download prints the
model's curl command instead of fetching it.)
3. Start the two servers β each in its own terminal, since they keep running:
# speech-to-text (whisper.cpp)
whisper-server -m ~/.claude-voice/models/ggml-base.en.bin --host 127.0.0.1 --port 8081
# text-to-speech (Kokoro β downloads its ~340 MB model on first run)
uv run --with kokoro-onnx --with numpy python ~/.claude-voice/kokoro-server.py --port 88804. Talk, pointed at the local stack:
claude-voice --local
# β¦or make it the permanent default:
claude-voice config --set stt=whispercpp --set tts=kokoro --set voice=af_heartRe-run
claude-voice localany time to see live status (which servers are up, which models exist). If you seewhisper.cpp request failed: fetch failed, a server just isn't running yet β start it with step 3.
Any OpenAI-compatible STT/TTS server works too (point providers.whispercpp.baseUrl /
providers.kokoro.baseUrl at it).
Run a token-protected voice session on your machine and drive it from your phone on the same Wi-Fi β hands-free with headphones:
claude-voice serveIt prints a link and a QR code. Scan it (or open the link) on your phone:
- Accept the one-time certificate warning (it's a self-signed cert β needed so the phone's browser will allow microphone access on the LAN).
- Allow microphone access.
- Hold the button, speak, release. Your words are transcribed on your machine, sent to Claude, and the reply streams back as text and plays aloud on the phone. Tap Stop to cut off a reply, or type instead.
Safe by default. A remote session runs with all of Claude's tools disabled β it can talk, but it can't run shell commands, edit files, or read your disk. The link carries a secret token, but anyone on your network who has it can talk to Claude on your machine, so only share it with yourself.
claude-voice serve --host 127.0.0.1 # keep it on this machine only
claude-voice serve --port 8080 # change the port
claude-voice serve --max-clients 2 # cap simultaneous devices
claude-voice serve --allow-tools # DANGER: let remote prompts run toolsPlease read SECURITY.md before using --allow-tools or exposing the server on an untrusted network.
Configuration lives at ~/.claude-voice/config.json. API keys are never
stored there β they are read from environment variables:
| Provider | Env var |
|---|---|
| Groq (STT, default) | GROQ_API_KEY |
| OpenAI (STT) | OPENAI_API_KEY |
| ElevenLabs (TTS, default) | ELEVENLABS_API_KEY |
| Sarvam (TTS) | SARVAM_API_KEY |
Manage it from the CLI:
claude-voice config # print current config + path
claude-voice config --edit # open in $EDITOR
claude-voice config --set voice=river --set model=sonnet
claude-voice config --get providers.groq.model
claude-voice config --reset # restore defaultsπ€ Microphone β π Speech-to-Text β π€ Claude CLI (stream-json)
β β
barge-in π Sentence parser
β β β
π Speakers β π£οΈ TTS queue ββββββββββββ π₯οΈ Terminal (live tokens)
Claude's output is streamed token-by-token. A streaming sentence parser
accumulates tokens until a sentence completes (., ?, !, or a paragraph
break), then hands each finished sentence to an interruptible speech queue
so audio begins while Claude is still generating. Every stage communicates over
a typed event bus, so the UI, plugins, and providers stay decoupled.
Read the full Architecture guide.
claude-voice is also a library. The CLI is a thin layer over these primitives:
import { createSession, loadConfig } from '@aayushdebugging/claude-voice';
const config = await loadConfig({ model: 'opus', voice: 'river' });
const { conversation, bus } = createSession({ config });
bus.on('SpeechRecognized', ({ text }) => console.log('You said:', text));
bus.on('ClaudeToken', ({ text }) => process.stdout.write(text));
await conversation.start(); // Ctrl-C to stopSee the API documentation for the full surface, including how to write custom STT/TTS providers and plugins.
Run claude-voice doctor first β it pinpoints most issues. Common ones:
- "Claude CLI not found" β install it from https://claude.com/claude-code and ensure
claudeis on yourPATH. - "recording backend not found" / "Microphone capture needs sox" β install
sox(brew install sox; see the table above). - "No microphone audio detected" β either
soxisn't installed, or your terminal lacks mic permission. Installsox, then grant access under System Settings β Privacy & Security β Microphone (to the terminal app you launched from β e.g. Terminal, iTerm, or VS Code) and restart it. - "whisper.cpp request failed: fetch failed" (or the same for
kokoro) β the local STT/TTS server isn't running. Start it (see Fully local & free, step 3) or runclaude-voice localto see which servers are up.fetch failed= nothing is listening on the configured port. - "speaker module unavailable" β the optional native audio module failed to build; install your platform's build tools and reinstall.
--no-speakworks meanwhile. - 401 from a provider β double-check the corresponding
*_API_KEY.
claude-voice drives the Claude CLI β a capable agent β from your voice. Local
sessions have full tool access (like your terminal); remote (serve) sessions
disable all tools by default so a spoken or injected prompt can't run commands
or touch your files. Before exposing a server or using --allow-tools, read
SECURITY.md for the threat model and safe-use guidance. To
report a vulnerability, see the reporting section there (please don't file a
public issue).
Contributions are very welcome! See the Contributing guide for the dev setup, coding standards, and how to add a provider or plugin.
MIT Β© claude-voice contributors
{ "stt": "groq", // "groq" | "openai" "tts": "elevenlabs", // "elevenlabs" | "sarvam" "voice": "aria", // TTS voice name "model": "opus", // Claude model alias "pushToTalk": false, // continuous vs push-to-talk "autoSpeak": true, // speak responses aloud "streamSpeech": true, // speak as Claude writes (vs the whole reply at once) "voicePrompt": "β¦", // steers Claude to answer in a voice-friendly way ("" disables) "language": "auto", // STT + TTS language (ISO-639-1) or "auto" "speechRate": 1.0, // speech rate multiplier, 0.5β3.0 (1 = natural) "silenceTimeoutMs": 1500, // silence that ends an utterance "micSensitivity": 150, // lower = picks up quieter mics; higher = ignores noise "sampleRate": 16000 }