CLAUDE.md
CLAUDE.mdCLAUDE.mdroot
Quality
58/100
Scores the file, not the repository.Length
964 words
16 headings · 6 code blocksRepository
15
— · pushed 111 days agoLast changed
3 days ago
First indexed 3 days ago.1# CLAUDE.md23This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.45## What is this67Dictate is a voice-to-text tool for Claude Code on Linux. It records speech using faster-whisper and outputs text via clipboard or stdout. Supports local transcription or forwarding audio to a remote GPU server over TCP. Single Python script (`dictate`), no build system.89## Development1011### Running locally1213```bash14bash install.sh # full install (system deps, venv, launcher)15bash install-service.sh # install and start systemd services16bash update.sh # update after code changes (no venv rebuild)17dictate --serve & # start daemon (keeps Whisper model in memory)18dictate --once # send one request to daemon, print text19dictate # standalone push-to-talk mode (no daemon)2021# Network transcription (remote GPU)22dictate --serve --listen 0.0.0.0:5555 # on GPU machine: headless transcription server23dictate --serve --server GPU_IP:5555 # on laptop: daemon forwarding to remote24dictate --once # client unchanged25```2627After install, reboot or re-login once for `input` group membership (required for evdev access).2829After code changes, run `bash update.sh` to deploy — it copies the script, service files, and restarts services. No venv rebuild needed.3031### Testing3233No test suite. Manual testing:3435```bash36dictate --serve & # start daemon37dictate --once # test transcription38dictate --cpu --model small # test CPU-only inference39dictate --list-devices # verify audio device detection40dictate --stop # stop daemon4142# Network transcription43dictate --serve --listen 0.0.0.0:5555 # start remote server44dictate --serve --server 127.0.0.1:5555 # start forwarding daemon45dictate --once # test end-to-end46```4748Test local daemon, network forwarding, and push-to-talk separately — they share audio and transcription code but have different I/O paths.4950## Architecture5152Single Python script (`dictate`, ~720 lines) with five modes:53541. **Push-to-talk** (default) — loads model, listens for key press via evdev, records, transcribes, copies to clipboard via `wl-copy`552. **Daemon** (`--serve`) — keeps model loaded, listens on Unix socket (`~/.local/share/dictate/dictate.sock`), maintains rolling 1-second pre-buffer, handles one request at a time563. **Daemon forwarding** (`--serve --server H:P`) — same as daemon but skips model loading; after recording, forwards audio over TCP to a remote transcription server574. **TCP transcription server** (`--serve --listen H:P`) — headless, no mic; receives audio over TCP, transcribes with local Whisper model, returns text585. **Client** (`--once`) — connects to daemon socket, sends JSON request with language + hints, reads newline-delimited JSON responses, prints final text to stdout (unchanged by network mode)5960### Daemon ↔ Client protocol (Unix socket)6162```63Client → Daemon: {"language": "en", "initial_prompt": "..."} + shutdown(SHUT_WR)64Daemon → Client: {"status": "recording"}\n65 {"status": "transcribing"}\n66 {"text": "transcribed text here"}\n67```6869### Daemon ↔ TCP server protocol (network transcription)7071```72Daemon → Server:73 4 bytes: header length (uint32 big-endian)74 N bytes: JSON header {"language": "en", "initial_prompt": "...", "audio_length": M}75 M bytes: raw float32 audio (16kHz mono)7677Server → Daemon:78 {"text": "transcribed text here"}\n79```8081### Audio pipeline8283`sounddevice.InputStream` (16kHz mono float32) → RMS-based silence detection → numpy array → `faster-whisper model.transcribe()`. Silence threshold is calibrated from 0.5s ambient measurement on startup: `ambient * 1.5 + 0.01`, capped at 0.05. Calibration retries on silence (rms=0) or suspiciously high ambient (>0.03, e.g. device switching). A background mic monitor thread detects disconnects and re-calibrates on reconnect, sending desktop notifications via `notify-send`.8485### Key functions8687- `calibrate_mic()` — ambient RMS measurement with retry logic, sets speech threshold88- `record_until_silence()` — records until post-speech silence or timeout, respects STOP_FLAG89- `transcribe_audio()` — local transcription via faster-whisper model90- `transcribe_remote()` — forwards audio to TCP server, returns text91- `serve()` — daemon loop: socket listener + pre-buffer, transcribes locally or forwards to remote92- `serve_tcp()` — headless TCP transcription server (for GPU machine)93- `client_once()` — client: connect, send request, read JSON stream94- `push_to_talk()` — standalone: evdev key detection + record + transcribe + clipboard95- `load_hints()` — merges global (`~/.config/dictate/hints.d/`) and project (`.dictate-hints.d/`) hint files96- `find_audio_device()` — prefers pipewire ALSA device for correct Bluetooth routing97- `pick_defaults()` — CUDA auto-detection: GPU → medium/int8, CPU → small/int898- `parse_addr()` — parses HOST:PORT strings for network modes99100### Claude Code integration101102- `/dictate` command (`dictate.claude-command`) — loops `dictate --once`, accumulates utterances103- `/dictate-hints` command (`dictate-hints.claude-command`) — auto-generates project vocabulary hints104- `dictate-editor` — nvim wrapper with F5/F6/F7 voice keybindings, used as `EDITOR=dictate-editor claude`105106## Key design decisions107108- **Wayland only**: `wtype` doesn't work on GNOME Wayland, so clipboard via `wl-copy` is used109- **PipeWire preference**: `default` ALSA device doesn't route Bluetooth mic correctly; must use pipewire device by name110- **`hotwords` removed**: tested but degraded transcription with many terms; `initial_prompt` works better111- **`hallucination_silence_threshold=2`**: prevents Whisper from hallucinating text on silence112- **Threshold cap 0.05**: prevents false "no speech" from noisy calibration (e.g., AirPods connecting/disconnecting)113- **Calibration retry on noise**: ambient RMS > 0.03 triggers retry — catches PipeWire route switching transients114- **Mic health monitor**: background thread checks pre-buffer RMS every 5s, sends `notify-send` on disconnect, re-calibrates on reconnect115- **Hints are per-request**: sent in client JSON, no daemon restart when switching projects116- **Network transcription**: local daemon records and forwards raw audio over TCP; remote server is stateless and handles transcription only. `--once` client is completely unaware of network mode117118## Jetson Orin Nano (aarch64)119120JetPack 6.x ships Python 3.10 and no PyPI ctranslate2 CUDA wheels for aarch64. Two extra steps:1211221. **Build ctranslate2 from source** (once, before `install.sh`):123```bash124 bash build-ctranslate2.sh125```126 This saves a wheel to `~/.local/share/dictate/wheels/`.1272. **Run install.sh** — detects aarch64, installs the pre-built wheel, skips `nvidia-cublas-cu12` (CUDA libs from JetPack), uses `/usr/local/cuda/lib64` in launcher.128129The `tomli` backport is installed automatically for Python < 3.11.130131## Installed file locations132133```134~/.local/bin/dictate # launcher (sets VENV, LD_LIBRARY_PATH)135~/.local/bin/dictate-editor # nvim wrapper136~/.local/share/dictate/venv/ # Python venv137~/.local/share/dictate/dictate.py # main script (copied from repo)138~/.config/dictate/config.toml # user config139~/.config/dictate/hints.d/ # global vocabulary hints140```141
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| Adit-Jain-srm/NightmareNetCLAUDE.md · 45 | CLAUDE.md | buildtestlint-formatstyle+6 | 100/100 | 3 days ago | |
| dotCMS/corecore-web/CLAUDE.md · 949 | CLAUDE.md | teststylearchtesting-strategy+3 | 100/100 | 3 days ago | |
| dotCMS/coreCLAUDE.md · 949 | CLAUDE.md | setupbuildteststyle+7 | 99/100 | today | |
| dotCMS/corecore-web/libs/sdk/react/CLAUDE.md · 949 | CLAUDE.md | setupbuildtestlint-format+9 | 97/100 | 3 days ago | |
| modelcontextprotocol/serversCLAUDE.md · 89k | CLAUDE.md | setupbuildtestlint-format+6 | 97/100 | 3 days ago | |
| luongnv89/claude-howtovi/CLAUDE.md · 41k | CLAUDE.md | setupbuildtestlint-format+8 | 97/100 | 3 days ago | |
| dotCMS/corecore-web/libs/sdk/client/CLAUDE.md · 949 | CLAUDE.md | setupbuildtestlint-format+9 | 97/100 | 3 days ago | |
| supabase/supabase.claude/CLAUDE.md · 108k | CLAUDE.md | testlint-formatstylearch+1 | 97/100 | 3 days ago |
