Skip to main content

handsfree

Hands-free voice loop for driving Claude by voice. Combines the say skill (Gemini TTS audio out), a focus-pull back to the caller's cmux prompt, Wispr Flow dictation (voice in), and an appendix-stop listener so the user ENDS and SUBMITS each turn by saying the word appendix. When active, every reply is spoken aloud and the mic is auto-armed, so the user answers and sends without touching the keyboard. Use when the user asks for hands-free mode, to control Claude by voice, to start the voice flow, or voice mode on. Turn it off with hands-free off, stop voice mode, or stop the voice flow.

来源信息

仓库
Vesely/skills
最近来源活动
2026年6月28日 20:15
检测到的 SKILL.md 语言
英语
星标
28
分支
4

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

文件资源管理器
4 个文件

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
handsfree
description
Hands-free voice loop for driving Claude by voice. Combines the say skill (Gemini TTS audio out), a focus-pull back to the caller's cmux prompt, Wispr Flow dictation (voice in), and an appendix-stop listener so the user ENDS and SUBMITS each turn by saying the word appendix. When active, every reply is spoken aloud and the mic is auto-armed, so the user answers and sends without touching the keyboard. Use when the user asks for hands-free mode, to control Claude by voice, to start the voice flow, or voice mode on. Turn it off with hands-free off, stop voice mode, or stop the voice flow.
# handsfree A super-lightweight wrapper around two existing skills — **`say`** (Gemini TTS, voice Charon) and **`wispr`** (Wispr Flow dictation) — plus a cmux focus-pull and a small **appendix-stop** keyword listener, so the user can run a whole Claude session by voice while away from the keyboard (e.g. driving). One script, `say-listen.sh`, does a full turn; `appendix-stop.sh` closes it by voice. ## What one turn does `say-listen.sh "<short reply text>"`: 1. **Speaks** the text via `~/.claude/skills/say/gemini-say.ts` (blocks until the audio finishes — `afplay` is synchronous). Playback is sped up slightly with ffmpeg `atempo` (pitch preserved; default ~1.07, tune via `GEMINI_SAY_RATE`). The `say` helper also ducks background audio (Spotify / Music / browser) for that span. 2. **Arms Wispr** hands-free listening (`stop-hands-free` then `start-hands-free`, both via `open -g`) so the user can immediately reply by voice. 3. **Pulls focus** back to the **caller's** cmux prompt (window by UUID, pane by ref, resolved live from `cmux identify`) so dictation lands in the prompt that invoked it even if the user wandered off to another app while Claude was thinking. 4. **Ensures the appendix-stop listener is running** (idempotent) so the user can end and submit the turn by voice. Opt out with `HANDSFREE_NO_APPENDIX=1`. ## Ending a turn by voice — the appendix-stop listener Wispr's only built-in stop is the **Fn** key, impossible while driving. The `appendix-stop.sh` listener closes that gap: **the user just speaks, then says "appendix"** — the listener stops Wispr, the dictation pastes into the prompt, and it presses Return to submit. No keyboard, no "press enter". ```bash ~/.claude/skills/handsfree/appendix-stop.sh start # arm (say-listen does this for you) ~/.claude/skills/handsfree/appendix-stop.sh status # running? recent triggers ~/.claude/skills/handsfree/appendix-stop.sh stop # disarm ~/.claude/skills/handsfree/appendix-stop.sh selftest # speaker->mic check (dry-run, safe) ``` ### How it actually works (and why) The listener is its own local speech-to-text on the mic. Several hard-won design choices are baked in, each fixing a real failure: - **ffmpeg capture, not SDL.** `whisper-stream`'s built-in SDL mic capture opens the device but on some macOS builds delivers **pure silence** (verified: SDL dead while `ffmpeg` read the same mic at −23 dB). So the engine is **ffmpeg (avfoundation) → rolling WAV segments → `whisper-cli`**. `APPENDIX_ENGINE=auto` uses ffmpeg when the binary is present, else falls back to SDL. - **Built-in mic, explicitly.** A connected iPhone Continuity mic can hijack the "default" input and read as silence. The ffmpeg engine picks the first avfoundation audio device that is **not** an iPhone/iPad/Continuity mic. - **Multilingual `small` model + `-l cs`.** A Czech speaker saying the English word "appendix" transcribes as garbage under the English `tiny.en` ("upend it", "(upbeat music)"). The multilingual **`small`** model (~480 MB, auto-downloaded) with Czech reads it cleanly as "Appendix". `-sns` (suppress non-speech tokens) stops "(zvuk)"/"(hudba)" hallucinations from road noise, and `--prompt` primes decoding with the trigger word (`APPENDIX_PROMPT`) so a quiet/noisy utterance still locks onto it (verified not to turn normal speech into a false fire). - **Warm model server.** Reloading the ~480 MB model per segment was the dominant latency. A **persistent `whisper-server`** loads it once and answers each segment over HTTP (~0.5 s), so word→stop drops to ~1 s and segments shrink to 1 s. It survives the one-shot listener restarts and is killed only by `stop`. `APPENDIX_SERVER=0` forces the per-call `whisper-cli` fallback (no regression if the server can't start). - **Pronunciation-tolerant match.** `APPENDIX_REGEX` matches "appendix" plus the common mis-hears (`a pendix`, `habendix`, `pandix`, `opendiks`, `a bendej`…) without firing on normal Czech speech. Overlapping windows catch the word even when it straddles a segment boundary. - **Repeated Return via cmux, not a global keystroke.** Wispr pastes the dictated text an unpredictable moment after `stop-hands-free`, so a single timed Return races the paste and hits an empty prompt. The listener presses Return several times across a ~4 s window (`APPENDIX_ENTER_TRIES` / `APPENDIX_ENTER_EVERY`); the press after the paste submits, the rest are no-ops. It submits with **`cmux send-key`** into the caller's workspace (written to `caller.target` by `say-listen.sh`) — focus-independent, so Wispr stealing focus during the paste cannot misdirect it — with an `osascript` keystroke as fallback. `open -g` for the stop deeplink keeps focus on the prompt. **Why not read Wispr's own transcript?** Tempting (Wispr's ASR is excellent), but Wispr writes the text — to the prompt and to its `flow.sqlite` `History` table — only **when the dictation ends**, not live. So there is no on-the-fly signal to trigger the stop; an independent local STT on the mic is required. **One-shot.** The listener self-stops after a real fire; `say-listen.sh` re-arms both Wispr and the listener next turn. `APPENDIX_WATCH_WISPR=1` additionally stops it when Wispr leaves hands-free (Escape/Fn), but it is **default off**: in hands-free Wispr's `activeDictationSession` also goes null between utterances, so the watchdog would kill the listener during a natural pause. **Trigger word leaks into the text.** Wispr also hears "appendix", so it lands in the dictated text. Strip it with a Wispr **Snippet** mapping `appendix` → a single space (Settings → Dictionary / Snippets; they live in Wispr's sqlite `Dictionary` table). `add-wispr-snippet.sh` can insert it as a guarded fallback (refuses while Wispr runs, backs up the DB first). First `start` auto-downloads the model (~480 MB) to `~/.cache/whisper-cpp/`. Tunables (env): `APPENDIX_ENGINE` (auto/ffmpeg/sdl), `APPENDIX_MODEL`, `APPENDIX_LANG` (cs), `APPENDIX_REGEX`, `APPENDIX_PROMPT` (hotword bias, default `appendix`), `APPENDIX_SERVER` (warm whisper-server, 1) / `APPENDIX_SERVER_PORT` (8771), `APPENDIX_CAPTURE` (mic index, default 0), `APPENDIX_SEG_SEC` (1), `APPENDIX_COOLDOWN` (6), `APPENDIX_PRESS_ENTER` (1), `APPENDIX_ENTER_TRIES` (6), `APPENDIX_ENTER_EVERY` (0.7), `APPENDIX_WATCH_WISPR` (0), `APPENDIX_DEBUG` (logs every transcription to `heard.log` for tuning), `APPENDIX_DRY_RUN`. ### Want true "Hey Siri" robustness? For bullet-proof, low-latency keyword spotting in noisy environments, a dedicated wake-word engine (Picovoice Porcupine / openWakeWord) beats general STT — but it needs a one-time setup (a custom "appendix" keyword from the vendor console + an access key) that can't be done hands-free. Offer it as an upgrade for when the user is at a desk. ## When ACTIVE (the loop) While hands-free mode is on, deliver **every** reply to the user by running: ```bash zsh ~/.claude/skills/handsfree/say-listen.sh "<spoken reply>" ``` Spoken-reply rules (same as the `say` skill): - Short: 1–4 sentences. Summarize; don't read everything. - Write the reply **in whatever language the user is using in this session** (the `say` helper auto-detects the language from the text — do not hardcode one). - Plain text, **no markdown, no code, no URLs, no symbols** — it's read aloud. - **Never say "appendix" in a spoken reply** — the mic hears your own TTS and would fire the listener. Refer to it as "the trigger word" / "the ending word". - If a real decision is needed, end with a clear spoken question; the mic is already armed for the answer. - The on-screen text reply can stay fuller (the user can read it later); the spoken part is the summary. ## Activating / deactivating - **On:** invoked via `/handsfree` (or the trigger phrases). Acknowledge by speaking through the script, then keep using it for subsequent replies. The appendix-stop listener auto-starts on the first turn. - **Off:** `/handsfree off`, "vypni hands-free", etc. → stop calling the script, disarm the mic once with `open "wispr-flow://stop-hands-free"`, stop the listener with `appendix-stop.sh stop`, and confirm in text (no need to speak it). ## Gotchas - **Focus is intentional.** The script yanks cmux to the foreground on purpose so dictation can't leak into another app. Turn the mode off if it gets in the way. - **Caller-relative.** The focus target is whoever invoked the script. Run it from the session you want the user talking back into (normally the orchestrator session). - **Requirements:** Wispr Flow running (Microphone permission), `say` skill + Vertex creds, cmux CLI, `bun`, `python3`, **`ffmpeg`** (capture + atempo), and **`whisper-cpp`** (`whisper-cli`); plus Accessibility/Automation permission for the terminal so the auto-Return works. `whisper-cpp` is auto-installed via Homebrew on first use; the model auto-downloads. Verified against Wispr Flow 1.5.980.
在 GitHub 查看