Voice Relay

Voice relay lets you dictate into a Claude Code session instead of typing. It captures audio from your microphone, transcribes it locally using a Whisper model, and injects the resulting text into the attended pane of any workspace. All audio processing happens on your machine, so nothing leaves the local network except the transcribed text.

Voice relay works with every workspace type: local Zellij sessions, Podman containers, Compose environments, SSH remotes, and Kubernetes deployments. The same command, the same TUI, regardless of where the session runs.

Prerequisites

You need two things before voice relay can run: the whisper.cpp server binary and a Whisper model file.

Install whisper.cpp via Homebrew:

brew install whisper-cpp

This provides whisper-server, which cc-deck starts and manages automatically. You do not need to run it yourself.

Then run the setup command to download a model and verify dependencies:

cc-deck ws voice --setup

Setup checks for whisper-server in your PATH, downloads the default model (base.en, about 141 MB), and reports whether everything is ready.

If you want a different model, pass --model during setup: cc-deck ws voice --setup --model small.en. See Whisper Models for a comparison of available models.

Quick Start

The fastest path from zero to voice dictation takes three commands.

Create a workspace (if you do not already have one):

cc-deck ws new my-project --type local

In one terminal, attach to the workspace and start a Claude Code session:

cc-deck ws attach my-project

In a second terminal, start voice relay:

cc-deck ws voice my-project

The voice TUI opens with an audio level meter and a scrollable transcript. Start speaking and your words appear in the Claude Code input of the attended pane.

To submit the prompt, say "send" as a standalone word. cc-deck recognizes this as a command word and sends a newline instead of the literal text. To cycle to the next session needing attention, say "next" as a standalone word. This triggers the same tiered attend logic as Alt+a. Both command words can be configured in ~/.config/cc-deck/config.yaml.

Press q to quit the voice TUI and shut down the whisper server.

Voice Activity Detection

Voice relay uses VAD (voice activity detection) mode. The microphone listens continuously, and cc-deck detects when you start and stop speaking based on audio energy levels. Each utterance is automatically segmented, transcribed, and delivered.

The threshold setting controls how much ambient noise is tolerated before speech is detected. Lower values make detection more sensitive (quieter speech is captured), while higher values require louder input. Use the + and - keys in the TUI to adjust the threshold in real time.

Silence of 2.5 seconds ends an utterance and triggers transcription (configurable with --silence or defaults.voice.silence). Utterances are capped at 30 seconds, which is the maximum window Whisper can process in a single pass.

Sidebar Integration

When voice relay is running, a ♫ indicator appears in the sidebar header.

  • Bright green ♫: Voice relay is connected and listening.

  • Dim ♫: Voice relay is connected but muted.

  • No ♫: Voice relay is not connected.

Mute Toggle

You can mute and unmute voice relay from multiple locations:

From the sidebar (any pane):

  • Alt+m: Toggle mute (configurable via voice_key in plugin config).

  • m in navigation mode: Toggle mute while navigating sessions.

  • Click the ♫ indicator: Toggle mute with the mouse.

From the voice TUI:

  • m: Toggle mute.

Mute state synchronizes bidirectionally between the sidebar and voice TUI. When you mute from the sidebar, the TUI reflects the change within about one second. When you mute from the TUI, the sidebar updates immediately.

Whisper Models

cc-deck supports five Whisper models for dictation and recording. The .en models are English-optimized; medium and large-v3-turbo also handle other languages.

Model Size Speed Accuracy Notes

tiny.en

74 MB

Fastest

Low

Good for testing. Not recommended for real dictation.

base.en

141 MB

Fast

Decent

The default. Reasonable accuracy for clear speech in a quiet room.

small.en

465 MB

Moderate

Good

Best tradeoff on Apple Silicon. Noticeably better with technical terms.

medium

1.5 GB

Slower

High

Multilingual support. Accurate, but higher latency per utterance.

large-v3-turbo

1.6 GB

Like medium on Apple Silicon

Highest

Multilingual support. The most accurate model; a pruned large model that transcribes about as fast as medium.

Speech Filter

Whisper models sometimes turn non-speech audio into words. Keyboard clicks, breathing, or room noise can come out as "Thank you.", "Okay.", or "Bye.", and the relay would deliver those phantom words to the attended session. The large models are more prone to this than the small ones.

To prevent it, the relay runs a speech filter before transcription. It uses the Silero voice activity model (ggml-silero-v6.2.0.bin, under 1 MB), which cc-deck ws voice --setup installs together with the Whisper model. Audio without speech is skipped instead of transcribed, so it costs almost no time and produces no text. The filter applies to dictation, to recordings, and to separating speakers by voice.

The filter is on by default. If it is not installed yet, the voice TUI shows a reminder at startup. To turn it off, set defaults.voice.speech_filter: false in ~/.config/cc-deck/config.yaml.

On an Apple Silicon Mac, small.en is the recommended upgrade from the default. It runs comfortably in real time and handles code-related vocabulary (function names, library names, technical jargon) much better than base.en.

To switch models:

cc-deck ws voice --setup --model small.en
cc-deck ws voice my-project --model small.en

Models are cached at ~/.cache/cc-deck/models/ and only downloaded once.

The Voice TUI

When voice relay starts, it opens a terminal interface that shows the current state of the audio pipeline.

What You See

The header displays the target workspace name, the active audio device, the current mode (VAD or MUTED), and a real-time audio level meter. The level meter uses a braille-block visualization with a threshold indicator, so you can see at a glance whether your voice is loud enough to trigger detection.

The main area shows a scrollable transcript history. Each entry includes a timestamp, delivery status icon, and the transcribed text:

  • + (green): Text was delivered to the attended pane.

  • ! (red): Delivery failed.

  • ~ (grey): Transcription is in progress.

The footer shows available keyboard shortcuts.

Keyboard Controls

Key Action

q

Quit voice relay and stop the whisper server.

m

Toggle mute/unmute.

+ / -

Increase or decrease VAD sensitivity by 2% per press.

d

Open the audio device picker.

r

Start recording, pause, or resume transcript recording.

R (Shift+R)

Stop transcript recording and close the file.

v

Open the reading view (only while recording or paused).

s

Choose how the next recording separates speakers: by pause or by voice (only when idle).

PgUp / PgDn

Scroll through transcript history.

Transcript Recording

Voice relay can record everything you say to a plain text file. Each transcribed utterance is written as a single line, with no timestamps or metadata.

To start recording, press r. A filename prompt appears at the bottom of the TUI with a default name based on the current timestamp (for example, transcript-2026-05-04T15-04-05.txt). Type a custom name or press Enter to accept the default. Press Escape to cancel without starting a recording.

While recording, a red REC indicator appears in the header next to the mode display. Every transcription is appended to the file as it arrives.

To pause recording, press r again. The indicator changes to a yellow pause symbol. Transcriptions continue to appear in the TUI history but are not written to the file. Press r once more to resume recording.

To stop recording and close the file, press R (Shift+R). The indicator disappears and the file is finalized. Quitting the TUI with q also closes the file properly.

Relative filenames are stored in ~/.local/share/cc-deck/transcripts/. Absolute paths are used as-is.

Recording While Muted

When the relay is muted and a recording is active, voice relay continues to transcribe speech and write it to the transcript file. The transcribed text is not sent to the attended pane. This lets you capture notes or commentary that you do not want delivered to the active session.

When no recording is active and the relay is muted, audio is discarded without transcription, preserving the existing mute behavior.

Reading View and Speaker Separation

While recording a transcript, press v to open the reading view. The view shows the ongoing conversation as blocks, one block for each contribution in the call. Each block shows the time the contribution started, followed by the spoken text. The gutter bar alternates between two colors, so adjacent contributions are easy to tell apart. The view follows new speech as it arrives.

Navigating the Reading View

Scroll with ↑/↓ or j/k (one line) and PgUp/PgDn (one page). When you scroll up, auto-follow pauses and the footer shows how many new blocks have arrived below. Press G or End to jump back to the bottom and resume following. Press esc or v again to close the reading view and return to the normal TUI.

The header line keeps the audio level meter and the threshold (T:) visible. Press + or - to adjust the threshold while you read, for example when quiet speakers are missing from the transcript. During a pause in the conversation, the level bar should drop below the threshold marker; otherwise silence is never detected and passages only end at the maximum chunk length.

Separating Speakers

The relay decides in one of two ways where one speaker stops and the next one starts. The header shows the current choice after Speakers:.

By pause (the default)

A new block starts after a silence longer than the configured pause break, or where Whisper marks a speaker change with a leading dash. This mode works with every Whisper model and every language and needs no setup. A quick exchange without a pause stays in one block.

By voice

A speech model listens for a change of voice and starts a new block at each change, even without a pause. This mode uses the tinydiarize model through whisper-cli and recognizes English only. The recording is transcribed with that model instead of your configured one, while dictation keeps using your configured model.

Press s in the idle TUI to switch between the two modes. The choice applies to the next recording, and the status line briefly confirms it. While a recording runs, s has no effect, so choose the mode before you press r.

Installing the Voice Model

Separating speakers by voice needs the tinydiarize model (small.en-tdrz, 488 MB). When you press s and the model is missing, the TUI asks whether to download it and shows the progress in the footer. Dictation and recording keep working during the download, and quitting the relay with q cancels it.

To install the model ahead of time, use the setup command:

cc-deck ws voice --setup --model small.en-tdrz

Tuning Recording Sensitivity

Recording passages arrive faster than dictation utterances because the recording VAD uses a shorter silence window (1 second by default, compared to 2.5 seconds for dictation). This means the reading view updates more frequently during recording.

For quiet remote voices or noisy environments, adjust the recording-specific VAD parameters in the config file:

defaults:
  voice:
    recording:
      threshold: 20
      silence: 0.8
      max_chunk: 10.0
      pause_break: 1.5

Lower threshold values capture softer speech. Shorter silence values produce faster updates but may split mid-sentence pauses into separate passages. The pause_break value (2 seconds by default) sets how many seconds of real silence between two passages start a new block. The relay measures this silence from the last loud audio of one passage to the first loud audio of the next, so the --pre-roll setting does not affect it.

See Configuration Reference for the full list of recording parameters and their ranges.

Speaker Breaks in the Transcript File

The transcript file separates contributions with a single blank line. Each segment is written on its own line, and a blank line appears before each segment that starts a new contribution. The first line of a recording never has a blank line in front of it. Both modes write the file the same way.

Troubleshooting Speaker Separation

whisper-cli not found

Separating speakers by voice requires whisper-cli, which is part of the whisper-cpp package. Install it with brew install whisper-cpp. Without it, pressing s shows an error and the relay keeps separating speakers by pause.

Voice model download failed

Network errors during the download appear in the TUI footer. Press s again to retry, or install the model with cc-deck ws voice --setup --model small.en-tdrz.

Pause-based fallback during a recording

If the voice model fails on a passage or needs longer than 30 seconds for it, the relay transcribes that passage with your configured model and separates speakers by pause for it. The footer shows the reason, and the recording continues. Run the relay with --verbose to see the details in ~/.local/state/cc-deck/voice.log.

No new block while one person talks

The voice model reacts to a change of speaker, not to pauses. A long monologue therefore stays in one block, except where a silence exceeds the pause break.

Configuration

Config File

The VAD threshold can be set in ~/.config/cc-deck/config.yaml so you do not have to pass --threshold every time:

defaults:
  voice:
    threshold: 45

The threshold uses a logarithmic scale from 0 to 100. A value around 30 to 50 works well for most quiet indoor environments.

CLI Flags

All voice settings can be overridden per invocation:

Flag Default Description

--model

base.en

Whisper model name.

--port

8234

Port for the local whisper-server.

--threshold

(from config)

VAD sensitivity (0-100, logarithmic). Overrides the config file value.

--verbose

false

Write detailed diagnostic logs to ~/.local/state/cc-deck/voice.log.

Command Words

Voice relay recognizes two default command words:

Word Action Description

send

submit

Submits the current prompt (sends a newline to the attended pane)

next

attend

Cycles to the next session needing attention (same as Alt+a)

A command word only fires when the entire utterance, after stripping filler words, equals the trigger word. Saying "the next step is to refactor" is treated as normal dictation because "next" is not standalone.

You can configure additional trigger words or define new actions in the config file:

defaults:
  voice:
    commands:
      submit:
        - send
        - done
        - enter
      attend:
        - next
        - switch

Each key under commands is an action name, and the list contains trigger words for that action. When you configure an action, your word list replaces the default words for that action. Actions you do not configure keep their defaults. Filler words (um, uh, hmm) are stripped before matching, so "um, send" still triggers the command.

Diagnostic Logging

Pass --verbose to enable per-utterance latency logging and backend health information. Logs are written to ~/.local/state/cc-deck/voice.log, not to the terminal. This is useful when debugging transcription accuracy or delivery failures.

Troubleshooting

whisper-server not found

Install whisper.cpp: brew install whisper-cpp. Then run cc-deck ws voice --setup to verify the installation.

Timed out waiting for whisper-server

The server takes a few seconds to load the model into memory. Larger models (especially medium at 1.5 GB and large-v3-turbo at 1.6 GB) need more time. If this persists, check whether another process is using port 8234 and try a different port with --port.

Phantom words such as "Thank you." appear without anyone speaking

Run cc-deck ws voice --setup to install the speech filter, and check that defaults.voice.speech_filter is not set to false. With --verbose, the log shows speech filter: on at startup when the filter is active.

Transcription misses quiet speech

Lower the VAD threshold with the - key in the TUI, or set a lower value in the config file. A threshold of 20 to 30 captures softer speech at the cost of picking up more background noise.

Filler words appear in output

cc-deck strips common fillers (um, uh, hmm, ah, er) automatically. If you see them, the issue may be that the filler was part of a longer phrase rather than standalone.

Text does not appear in Claude Code

Verify that the workspace has an active Zellij session and that a pane is attended. Check the transcript history in the TUI for delivery errors (the ! indicator).

Next Steps

  • The Sidebar for understanding session states and the attended pane

  • Smart Attend for how cc-deck picks which pane receives voice input

  • Configuration for custom keybindings and layout variants

  • CLI Reference for the complete ws voice flag reference