# CLI Reference The `stt-daemon` CLI provisions the virtual sink, runs the daemon, and lists devices. Every subcommand accepts the common overrides `--config`, `--device`, `--backend`, `--model`, `--language`, and `-v/--verbose`. ## Subcommands The `stt-daemon` CLI supports these subcommands. Every subcommand also accepts the common overrides `--config`, `--device`, `--backend`, `--model`, `--language`, and `-v/--verbose` (for example `stt-daemon setup --device ...`). ### `setup` — Create virtual sink and loopbacks Interactive setup (shows device picker): ```bash uv run stt-daemon setup ``` Non-interactive setup (specify sources directly): ```bash uv run stt-daemon setup --sources "alsa_input.usb-Foo,alsa_output.pci-0000.monitor" ``` This creates: - A null sink (by default `VirtualMicSink`) - A remap-source to expose the sink's monitor as a microphone (by default `VirtualMic`) - Loopback modules routing each selected source into the sink - A state file tracking all module IDs for later cleanup ### `run` — Start the daemon (auto-provisions if needed) ```bash uv run stt-daemon run ``` If no saved provisioning exists and the sink is missing, the daemon will: 1. Show the interactive device picker 2. Create the sink and loopbacks (if sources are selected) 3. Start listening and transcribing You can also override settings: ```bash uv run stt-daemon run --device "alsa_input.usb-..." --language es --model base uv run stt-daemon run -v # Verbose logging uv run stt-daemon run --check # Test audio capture + model, then exit ``` ### `list` — Show devices and provisioning status ```bash uv run stt-daemon list ``` Output shows: - Input devices (real microphones) - Output devices (speakers/headphones; captured via their `.monitor` source) - Current provisioning state (if a sink has been created) ### `clear` — Remove virtual sink and loopbacks ```bash uv run stt-daemon clear # or without confirmation prompt: uv run stt-daemon clear --yes ``` This unloads all tracked modules and removes the state file. If `audio/audio.json` is already gone (reboot, manual delete, crash), `clear --yes` still finds orphaned modules by name (`VirtualMicSink`/`VirtualMic` via `pactl list short modules`) and unloads them in reverse order (loopbacks → remap-source → null-sink), so the command is idempotent — safe to re-run and to recover from `Sink 'VirtualMicSink' already exists but is not tracked`. ### `up` — Launch daemon, assistant, and optionally TUI ```bash uv run stt-daemon up # Start daemon + assistant (separate processes) uv run stt-daemon up --with-tui # Also launch the interactive TUI viewer ``` This subcommand runs the entire system as a supervised set of child processes: - **daemon** — transcription engine (listens to audio, publishes transcriptions) - **assistant** — controller (subscribes to transcriptions, serves the control socket) - **TUI** (optional) — viewer/controller UI (connects to the assistant via control socket) The supervisor manages their lifetimes: if any child exits or Ctrl+C is pressed, all remaining children are cleanly shut down. Without `--with-tui`, you can still run a separate `uv run stt-tui` in another terminal to view and control the assistant. With `--with-tui`, the daemon and assistant output is discarded so it cannot corrupt the screen, while the TUI keeps the terminal. Without `--with-tui`, all output goes to the terminal. The supervisor writes nothing to disk. Like all subcommands, `up` also accepts `--config`, `--device`, `--backend`, `--model`, `--language`, and `-v` (verbose) flags. Example workflow: ```bash # One-command startup with TUI (everything stops when you quit the TUI) uv run stt-daemon up --with-tui # Or, start daemon + assistant only uv run stt-daemon up # Then in another terminal, launch the TUI whenever you want uv run stt-tui ``` ### `speaker-enroll` — Pre-enroll a speaker from WAV files ```bash uv run stt-daemon speaker-enroll --name Efren --wav audio/2026-09-28/143022-123-pause.wav ``` Registers a known speaker up front, without waiting for the daemon to mint a provisional id from a live voice. `--name` is required; pass `--wav` once per clip, and each file contributes one prototype (capped at `max_prototypes`, oldest evicted). On success the command prints something like `enrolled 'Efren' as spk_0002 (2 clip(s), 7.4s)`. Each WAV must be 16 kHz mono 16-bit PCM; the clips the daemon saves under `//audio/` are already in that format, so they need no conversion, and each must be at least `[speaker].min_enroll_s` seconds. The store is single-writer and owned by the daemon, so stop it before enrolling, or pass `--force` to bypass the refusal. Like every subcommand, `speaker-enroll` also accepts `--config`. `--dedupe-threshold F` (default `0.98`) sets the cosine at or above which two prototypes of the same speaker count as duplicates. The command strips near-duplicate prototypes, including any pre-existing ones for the named speaker, before it finishes, and reports it as `removed N duplicate prototype(s) from the ...`. Example with several clips: ```bash uv run stt-daemon speaker-enroll --name Efren \ --wav audio/2026-09-28/143022-123-pause.wav \ --wav audio/2026-09-28/150210-456-end_of_turn.wav ``` ### `speaker-enroll-seed` — Assisted enrollment from a seed clip ```bash uv run stt-daemon speaker-enroll-seed --name "Efren Yevale Varela" --seed audio/efren-seed.wav ``` Enrolls from ONE clip you are certain is the speaker. The command scans `--audio-dir` (default `[output].session_dir`, the session root `transcripts/`, searched recursively for `*.wav`), embeds each `*.wav`, ranks them by cosine similarity to the seed, and offers the ones at or above `--min-score` (default `0.55`) and at least `[speaker].min_enroll_s` seconds for review. `--name` and `--seed` are required; the seed must be 16 kHz mono 16-bit PCM. Review is interactive: each candidate shows `[i/N] (score X.XX, Y.Ys)` and you choose Accept, Skip, Play, or Quit. `--count N` (default `5`) caps how many reviewed clips are accepted; `--yes` skips the prompts and auto-accepts the top candidates (required when stdin is not a TTY); `--no-play` disables playback; `--max-scan N` caps how many files are embedded. Quit or Ctrl+C writes nothing and prints the equivalent `stt-daemon speaker-enroll --wav ...` command. Like `speaker-enroll`, this refuses to run while a daemon appears to be running unless `--force` is passed, and accepts `--config`. Each candidate is flagged during review: `duplicate` when its embedding is already covered by one of that speaker's prototypes (cosine at or above `--dedupe-threshold`, default `0.98`), and `other-speaker:` when it scores highly against a different enrolled speaker (cosine at or above `--other-threshold`, default `0.45`), a contamination hint. Accepting a `duplicate` is refused with `skipping : already covered by this profile`, without consuming the accept budget. The incoming set (seed plus accepted clips) is de-duplicated before enrolling and pre-existing near-duplicate prototypes of that speaker are removed afterward, both reported on stdout. Example, accepting three reviewed clips non-interactively: ```bash uv run stt-daemon speaker-enroll-seed --name "Efren Yevale Varela" \ --seed audio/efren-seed.wav --count 3 --yes ``` **Tuning and rebuilding.** Running either enrollment command for a name that already exists **appends** prototypes (the oldest is evicted once `max_prototypes` is reached) rather than resetting the profile, so you tune by enrolling more diverse clips and flush a bad prototype by adding enough new ones. To start clean, stop the daemon, delete `speakers/`, and re-enroll. After any enrollment, tune-up, or wipe, **restart the daemon** so it reloads the store. See the [Tuning an existing profile](SPEAKERS.md#tuning-an-existing-profile) and [Wipe and rebuild](SPEAKERS.md#wipe-and-rebuild) sections of [docs/SPEAKERS.md](SPEAKERS.md). ### `speaker-eval` — Measure accuracy against labeled clips ```bash uv run stt-daemon speaker-eval --labeled labeled/ ``` Scores the store against a directory of labeled clips. Each immediate subdirectory of `--labeled` is a speaker display name and holds that person's `*.wav` clips (16 kHz mono 16-bit PCM). The command embeds each clip, labels it with the same matcher the daemon uses, and prints a plain-text report: `total`, `accuracy`, `correct`, `wrong`, `uncertain (top-1 correct/total)`, `unknown`, a `per-speaker` table (`name | n | correct | acc`), the top `confusions` (`true -> predicted (count)`, up to 10), and `margins` (`count`, `mean`, `p10` of top1 - top2). The run is read-only, so it never writes the store and the daemon does not need to be stopped. It requires the model and `uv sync --extra speaker`, and fails clearly if the store was built with a different embedding model (dimension mismatch). `--min-accuracy F` (optional) exits non-zero when measured accuracy is below `F`, so it works as a tuning gate. Example, using it as a gate: ```bash uv run stt-daemon speaker-eval --labeled labeled/ --min-accuracy 0.9 ``` ### (Legacy) `--list-sources` flag ```bash uv run stt-daemon --list-sources # Equivalent to: uv run stt-daemon list ``` ### (Legacy) `--check` flag ```bash uv run stt-daemon --check # Equivalent to: uv run stt-daemon run --check ``` ## Interactive Device Picker When you run `setup` or `run` without `--sources`, the daemon shows an interactive picker: - **TTY mode** (terminal): Uses `questionary` to show a checkbox UI. Pre-checked items are from the previously saved selection. - **Non-TTY mode** (piped input): Shows a numbered menu; enter space/comma-separated indices, `all`, `none`, or blank (to keep previous selection). Device labels: - `[IN] ()` — Input device (real microphone) - `[OUT] ()` — Output device (monitor of a sink) ## Running the daemon The simplest workflow: ```bash # Create/setup the sink (interactive) uv run stt-daemon setup # Start the daemon in another terminal # (auto-uses the provisioned sink) uv run stt-daemon run # Stop and remove the sink when done uv run stt-daemon clear --yes ```