A voice loop for Claude Code. Your sessions announce themselves out loud when they finish β then you just talk back.
Claude finishes a turn
ββ> π ding + "dayloop: Done β the Stats tab renders and all 14 tests pass."
ββ> ποΈ mic opens (only after speaking β the loop can't hear itself)
ββ> you: "great, now do the same for the horizontal layout"
ββ> transcript lands in that session's prompt and submits
Speech-to-text runs entirely on your Mac via whisper.cpp with Metal. No cloud, no API keys β conch setup installs the engine and downloads the models for you.
You don't pick a session β the mic follows the voice. When a session finishes a turn, it announces itself by name ("dayloop: ..."), and whatever you say next goes back to that session. The announcement is the address. If several sessions finish while you're mid-conversation with one, the newest announcement wins the mic next; the rest you'll have heard by name and can reach by typing (or wait β addressing a session by name is on the roadmap).
Because the loop is turn-based β speak, then listen, never both β the mic never picks up the Mac's own voice, so no feedback loop and no headphones required.
Prompts are injected via tmux send-keys targeted at the exact pane running that session, so it works even when the pane isn't focused. Not a tmux user? With CONCH_KEYSTROKE_FALLBACK=1, conch finds the Terminal window hosting the session (matched by tty), focuses it, and types there. If the window can't be found, your words go to the clipboard instead of being typed into the void β you'll hear "just paste."
macOS. Two commands:
brew install stupart/tap/conch # binary + sox/tmux/whisper-cpp
conch setup # download models + wire hooksbrew install pulls the system dependencies (sox, tmux, whisper-cpp) automatically. conch setup then downloads the two speech models into ~/.cache/conch/models (whisper large-v3-turbo ~1.6 GB, silero VAD ~900 KB), wires the Claude Code hooks, and runs doctor to verify the chain. It's idempotent β re-run it any time; it skips whatever's already there. Already have a whisper.cpp build and models (e.g. a seashell checkout)? Point CONCH_WHISPER_CLI / CONCH_WHISPER_MODEL / CONCH_VAD_MODEL (or CONCH_SEASHELL_ROOT) at them and setup leaves them untouched.
From source (for hacking on conch)
git clone https://github.com/stupart/conch.git && cd conch
bun install
bun link # puts `conch` on your PATH, running from source
conch setup # brew-installs deps, downloads models, wires hooksRequires Bun. Running from source means edits take effect immediately; the brew binary is a frozen bun build --compile build.
Then start it as a background service that launches at login and self-heals within ~15s of a crash:
conch service install # view anytime: tmux attach -t conchPrefer to watch it live? Run the loop in the foreground in any pane instead β you get the full dashboard (session panel + status line):
conch daemonNo daemon running at all? The hooks still work standalone: bell + spoken announcements, no voice-back. That's a perfectly good way to use conch.
By default conch speaks through macOS say. Install mlx-audio and the daemon upgrades itself to Kokoro-82M β dramatically more natural, running warm and local on Apple GPU (~340MB model, auto-downloaded on first use):
brew install uv
uv tool install --with "misaki[en]" "mlx-audio[server]"That's it β the daemon finds mlx_audio.server, spawns it, and every session gets its own voice: labels are hashed onto a ring of 8 Kokoro voices, so dayloop always sounds like dayloop and you can tell sessions apart by ear. Audition the ring with conch voices (or press v in the dashboard to hear each LIVE session in its assigned voice), pin any session with conch voice dayloop bm_george (persisted), customize the ring with CONCH_TTS_VOICES (any of Kokoro's 50+ voices), or force CONCH_TTS=say to opt out. If the server is down, everything degrades to say automatically.
| Command | What it does |
|---|---|
conch setup |
One-command install: brew deps, model downloads, hooks, doctor (run this first) |
conch service [off] |
launchd supervision: start at login, self-heal on crash |
conch install |
Merge hooks into ~/.claude/settings.json (backs up first) |
conch daemon |
Run the voice loop: announce β listen β inject |
conch wake [name] |
Reopen the mic β last announced session, or by name (bind it to a hotkey) |
conch sessions |
List live Claude Code sessions |
conch mute / unmute |
Silence announcements + mic |
conch pause / resume |
Step away: stay quiet but HOLD finished sessions, replay on resume |
conch hook |
Hook entrypoint (Claude Code calls this, not you) |
conch listen |
Mic check: capture one utterance, print the transcript |
conch speak <text> |
TTS check |
conch voices |
Audition the voice ring β each voice introduces itself |
conch voice <s> [v] |
Show or pin a session's voice (persisted) |
conch set <key> <value> |
Save a curated setting and apply it live when possible |
conch get <key> |
Show one effective setting and its source |
conch unset <key> |
Remove a saved value and revert to env/default |
conch settings |
List all curated settings, effective values, and sources |
conch doctor |
Verify external dependencies |
While the mic is open (you'll hear a tink), a bare command word talks to conch instead of the session:
| You say | What happens |
|---|---|
| "stop" / "got it" / "enough" (while it's reading) | stops reading, opens the mic for your reply |
| "no response" / "no response needed" / "cancel" | closes the mic, moves to the next queued session |
| "continue" / "keep going" / "read the rest" | reads more, then listens again |
| "repeat" / "say that again" | re-speaks the last thing conch said |
| anything else | goes to the session as your prompt |
By default conch reads the whole final message aloud (CONCH_READ_FULL=0 for headline-only), pausing briefly between chunks β those pauses are your window to interject: "stop" to cut it short, "no response" to close out, or just start dictating your reply and the rest is skipped.
Commands only match as the entire utterance β "continue working on the login bug" is a prompt, not a command. Filler wrapping is fine ("Oh, continue." works). A soft bottle sound means the window closed on silence.
Came back after the window closed? Press space in the daemon's terminal, or run conch wake (bind it to a global hotkey via Raycast/Shortcuts) β the mic reopens for the last announced session. conch wake dayloop targets any live session by name (conch sessions lists them), and the status line shows exactly who's listening.
Leaving? conch mute silences announcements and the mic until conch unmute; each event only ever speaks once regardless β there is no reminder loop, and a closed window costs nothing (no sox, no whisper). CONCH_AWAY_AFTER_SECS adds opt-in auto-silence after N seconds of keyboard/mouse idle, but note it's off by default for a reason: idle time doesn't count voice activity, so it would mute a fully hands-free session mid-conversation.
Stepping away for a bit? conch pause (or p in the dashboard) is mute's patient sibling: it stays quiet and holds every session that finishes while you're gone, then replays them in order on conch resume. Mute forgets; pause remembers. Joining a meeting, use conch pause first β otherwise an open mic window could pick up meeting audio and inject it into a session.
Want to focus on one thing? Snooze the noisy sessions instead of pausing everything. Point the cursor at a session (ββ) and press enter: it dims to βΈ snoozed and goes silent, while the rest of the loop keeps talking to you. Each snoozed session quietly holds only its latest turn β press enter again and conch catches you up on just that one, current thing rather than a backlog. Snooze a session conch is mid-sentence on, and it stops reading that one right away.
Permission prompts ("dayloop needs you: permission to run npm install") open the mic too, but only accept yes/no: "yes" presses Enter on the highlighted option, "no" presses Escape, anything else is ignored β free text near a permission dialog is deliberately refused. And idle "waiting for your input" nags are filtered: conch checks whether the session's last reply actually asked you something, and stays quiet when the session is just idle ("I'll ping you when it lands").
Run the daemon in a visible terminal (conch daemon) and you get the dashboard from the screenshot above β one line per live Claude Code session, so you can see who needs you at a glance without conch ever nagging you aloud:
π conch
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
boatker β needs a response
honeyb β needs a response
dayloop-feature-work β waiting for you
tokenworks-app β waiting for you
βΈ arch site β recording
conch β workingβ¦
poaster βΈ snoozed
π let's try the horizontal layout next then run the whole suite
ββ select Β· space talk Β· enter snooze Β· m mute Β· p pause Β· l logs Β· ? help Β· q quit
Sessions that need input sort to the top. Each row carries its own state β β needs a response, β waiting for you, β workingβ¦, βΈ snoozed β and the one conch is currently talking to lights up in place as it moves through the turn (βΆ speaking β β mic open β β recording β β¦ transcribing). The reserved line beneath the list streams your words as you speak them, so there's no layout shift when the mic opens.
You don't have to touch it β but you can. ββ move a cursor through the sessions; space talks to the selected one (or the last announcer if you haven't picked); enter snoozes/resumes it; l toggles a live log; ? shows the full key + voice-command help. The play-by-play is always written to /tmp/conch-daemon.log whether or not the log is on screen, so the dashboard stays clean by default.
Windows follow the voice too. When conch starts talking to a session, that session's Terminal window rises to the front without stealing your keyboard focus β you keep typing wherever you are, and the session you're hearing is already there when you look up. (CONCH_REVEAL_ON_TURN=0 to disable.)
The daemon also spawns a warm whisper-server (model stays loaded), which makes every transcription seconds faster and is what powers the live partials β the growing recording is re-transcribed about once a second while you speak. No server binary? Everything still works via the slower cold path, minus partials. CONCH_WHISPER_PORT=0 disables the server.
State is also written to /tmp/conch-state.json ({state, label, partial, ts}) for menu-bar apps or status bars to consume.
Curated settings can be changed without editing shell profiles:
conch settings
conch get end-silence
conch set end-silence 2.75
conch unset end-silence # revert to env/defaultValues are saved in ~/.config/conch/settings.json; writes are atomic for readers and intended for one conch CLI writer at a time. Environment variables take precedence over saved values. set reports whether a value applied live, is masked by an environment variable, waits for the next hook, or was saved for the next daemon start. get and settings ask the daemon for live truth and fall back to local resolution when it is down.
The full environment-variable surface remains available (put overrides in the hook's env or your shell profile):
| Variable | Default | |
|---|---|---|
CONCH_VOICE |
system default | say voice β try Ava (Premium) |
CONCH_SAY_RATE |
210 |
macOS say rate in words per minute; 0 preserves the system default (~175) |
CONCH_SPEAK_SENTENCES |
2 |
how much of the reply to read aloud |
CONCH_SPEAK_MAX_CHARS |
350 |
hard cap on spoken length |
CONCH_BELL / CONCH_SPEAK |
1 |
disable the ding / the voice |
CONCH_BELL_SOUND |
Glass.aiff | any afplay-able file |
CONCH_LISTEN_WINDOW_SECS |
30 |
how long the mic waits for you to start talking |
CONCH_MAX_UTTERANCE_SECS |
120 |
cap on a single utterance once you're talking |
CONCH_END_SILENCE_SECS |
3.5 |
pause length that ends your utterance (drop it for snappier turns) |
CONCH_CONTINUE_SENTENCES |
6 |
sentences per read-aloud / "continue" chunk |
CONCH_GAP_SECS |
0 (none) |
interjection gap between read-aloud chunks |
CONCH_BARGE_THRESHOLD_PCT |
0 (off) |
mic level that interrupts reading mid-chunk; after upgrading an existing supervised install, run conch service install once to shed the old forced env and restart |
CONCH_MIC_CUES |
1 |
tink on mic-open, bottle on silent close |
CONCH_AUTO_SUBMIT |
1 |
press Enter after injecting |
CONCH_HOLD_SUBMIT |
1 |
hold Enter; pauses segment dictation, "send"/"go" or a long pause submits |
CONCH_HOLD_SUBMIT_SECS |
8 |
silence before held dictation auto-submits |
CONCH_KEYSTROKE_FALLBACK |
0 |
allow typing into the frontmost window when no tmux pane is found |
CONCH_TYPING_GRACE_SECS |
2 |
if you touched the keyboard/mouse this recently, a finished turn stays visual (bell + panel) and the mic won't open β so typing can't trigger phantom words; 0 disables |
CONCH_REVEAL_ON_TURN |
1 |
raise a session's window (without stealing focus) when conch starts talking to it |
CONCH_SAY_VOLUME |
0.4 |
say fallback loudness β tuned to match Kokoro (raw say is ~3Γ louder) |
CONCH_SEASHELL_ROOT |
~/whisper-cli |
first place probed for the whisper.cpp build + models; falls back to a brew whisper-cpp install and ~/.cache/conch/models |
CONCH_WHISPER_PORT |
8642 |
warm whisper-server port; 0 = cold cli only |
CONCH_AWAY_AFTER_SECS |
0 (off) |
opt-in: silence everything after N seconds of keyboard idle |
CONCH_TTS |
auto |
voices: auto (Kokoro server if installed, else say) / server / say |
CONCH_TTS_PORT |
8880 |
warm Kokoro server port; 0 disables |
CONCH_TTS_VOICES |
8-voice ring | comma-separated Kokoro voices; sessions hash onto the ring |
CONCH_TTS_SPEED |
1.35 |
Kokoro/voice synthesis speed (conch set voice-speed β¦) |
CONCH_TTS_BATCH_CHARS |
240 |
coalesce later short sentences up to this size; 0 disables (sentence one always stays separate) |
- Name-addressing β "hey dayloop, ..." routes to any session, not just the last announcer
- Haiku summaries β pipe replies through
claude -p --model haikufor a natural spoken one-liner instead of first-two-sentences - Phone remote β hear from and talk back to your sessions over a websocket when you're away from the machine
- Always-listening mode β seashell's concurrent VAD architecture, once extracted from its TUI, replaces the per-window sox capture
- Linux β swap
say/afplayfor espeak/paplay, keystroke fallback for xdotool
Built on seashell's local-first STT engine.
conch is a small open experiment from Blueprint Studio β we build AI products that feel good to use. MIT.
