softvoice 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SPra03
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,308 @@
1
+ Metadata-Version: 2.4
2
+ Name: softvoice
3
+ Version: 0.1.0
4
+ Summary: On-device voice in and out for Claude Code and Codex: replies read aloud, push-to-talk and dictation, nothing leaves your Mac
5
+ Keywords: claude-code,codex,text-to-speech,speech-to-text,voice,dictation,macos
6
+ Author: SPra03
7
+ Author-email: SPra03 <11s11p11sp@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: MacOS X
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: MacOS
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
16
+ Requires-Dist: numpy>=2.5.4
17
+ Requires-Dist: parakeet-mlx>=0.5.3
18
+ Requires-Dist: pocket-tts>=4.0.1
19
+ Requires-Dist: pyobjc-framework-applicationservices>=12.2.2
20
+ Requires-Dist: pyobjc-framework-cocoa>=12.2.2
21
+ Requires-Dist: pyobjc-framework-quartz>=12.2.2
22
+ Requires-Dist: rumps>=0.4.0
23
+ Requires-Dist: silero-vad>=6.2.3
24
+ Requires-Dist: sounddevice>=0.5.6
25
+ Requires-Dist: soundfile>=0.14.0
26
+ Requires-Python: >=3.12
27
+ Project-URL: Homepage, https://github.com/SPra03/softvoice
28
+ Project-URL: Issues, https://github.com/SPra03/softvoice/issues
29
+ Description-Content-Type: text/markdown
30
+
31
+ # softvoice
32
+
33
+ **Talk to Claude Code and Codex, and hear them talk back. Everything stays on your Mac.**
34
+
35
+ ![macOS Apple Silicon](https://img.shields.io/badge/macOS%2014%2B-Apple%20Silicon-black?logo=apple)
36
+ ![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-3776AB?logo=python&logoColor=white)
37
+ ![License: MIT](https://img.shields.io/badge/license-MIT-green)
38
+ [![PyPI](https://img.shields.io/pypi/v/softvoice?color=blue)](https://pypi.org/project/softvoice/)
39
+ ![On-device](https://img.shields.io/badge/your%20voice%20%26%20text-never%20leave%20your%20Mac-blue)
40
+ [![CI](https://github.com/SPra03/softvoice/actions/workflows/ci.yml/badge.svg)](https://github.com/SPra03/softvoice/actions/workflows/ci.yml)
41
+
42
+ Your coding agent finishes a turn and reads its reply aloud, underlining each sentence on the
43
+ page as it goes. You answer by talking: tap a key, say what you want, press Enter. A small glass
44
+ pill floats above your windows and shows what is happening. No accounts, no API keys, no audio
45
+ sent anywhere: after a one-time model download it works with the network unplugged.
46
+
47
+ - **Speaking**: [Pocket TTS](https://github.com/kyutai-labs/pocket-tts) by Kyutai, a 100M-parameter voice that runs on the CPU and starts talking about 200 ms after the text arrives
48
+ - **Listening**: [Parakeet TDT 0.6B v3](https://huggingface.co/nvidia/parakeet-tdt-0.6b-v3) by NVIDIA, run through [parakeet-mlx](https://github.com/senstella/parakeet-mlx) on Apple Silicon; transcribes faster than you speak
49
+ - **Integration**: the agents' own `Stop`, `Notification` and `UserPromptSubmit` hooks. No MCP server, no wrapping the terminal, no changes to how you work
50
+
51
+ English first (the listener understands 24 other European languages; the voice has English, French, German, Spanish, Italian, Portuguese and Dutch). Apple Silicon Mac, macOS 14 or newer. MIT licence. Early, and in daily use by its author.
52
+
53
+ ## Install
54
+
55
+ Three commands, with [uv](https://docs.astral.sh/uv/) doing the Python work:
56
+
57
+ ```bash
58
+ brew install uv
59
+ uv tool install softvoice
60
+ softvoice setup
61
+ ```
62
+
63
+ (Or the newest commit straight from GitHub: `uv tool install git+https://github.com/SPra03/softvoice`.)
64
+
65
+ `setup` downloads the two models once (Pocket TTS, about 230 MB; Parakeet, 2.5 GB) and asks
66
+ macOS for the permissions below. Then:
67
+
68
+ ```bash
69
+ softvoice install # hooks into Claude Code (and Codex, if ~/.codex exists)
70
+ softvoice autostart on # the pill, now and at every login, with no terminal left open
71
+ ```
72
+
73
+ `install` writes three hooks into `~/.claude/settings.json` (a backup is kept next to it) and,
74
+ if `~/.codex` exists, into `~/.codex/hooks.json`. Restart your Claude Code sessions so they pick
75
+ the hooks up; Codex needs a one-time `/hooks` to trust them. Upgrade later with
76
+ `uv tool upgrade softvoice`; `softvoice uninstall` removes the hooks, `softvoice autostart off`
77
+ the login item, and everything softvoice owns lives in `~/.softvoice`.
78
+
79
+ Needs about 4 GB of disk for the models and the Python environment, and about 2 GB of memory
80
+ while running. The models load once at start; idle, it uses no CPU.
81
+
82
+ ### Permissions
83
+
84
+ macOS grants these to the app that owns the terminal you run softvoice from (Terminal, iTerm,
85
+ the Claude desktop app, VS Code), or to Python itself when the login item starts it. Each is
86
+ asked for once.
87
+
88
+ | permission | needed for | asked |
89
+ | --- | --- | --- |
90
+ | Microphone | listening | the first time the mic opens |
91
+ | Accessibility | pasting your words into the prompt; reading the page for the underline | by `setup`, or the first paste |
92
+ | Input Monitoring | seeing the talk key and scroll events | by `setup`, or the first `listen` or `app` |
93
+
94
+ If you grant Accessibility after the app has started, it restarts itself (under the login item)
95
+ so the underline can read the page.
96
+
97
+ ## Everyday use
98
+
99
+ The talk key is **right Option** by default: the ⌥ key to the right of the space bar, chosen
100
+ because nothing else uses it on its own. Prefer a combination? One line in the config:
101
+ `hotkey = "alt+space"` (or `ctrl+shift+d`, `fn+space`, ...). A combination is swallowed, so the
102
+ app in front never sees it.
103
+
104
+ | when | you | softvoice |
105
+ | --- | --- | --- |
106
+ | any time | **tap** the talk key, or click the mic | opens a dictation session: talk as long as you like, your words appear in the pill |
107
+ | dictating | tap again, press **Enter**, or click **✓** | pastes everything into your prompt, where you can still edit it |
108
+ | dictating | press **Escape**, click **✕**, or say "cancel" | discards it |
109
+ | dictating | end with "send it" | pastes and presses Enter for you |
110
+ | any time | **hold** the talk key, speak, release | push-to-talk: a quick take, pasted where your cursor is |
111
+ | while it speaks | tap the talk key, or click the speaker | stops the voice |
112
+ | while it is quiet | click the speaker | reads the text you have selected in any app, or replays the last reply |
113
+ | hands-free on | just talk after the beep | after every spoken reply the mic opens by itself, pauses send |
114
+
115
+ Enter and Escape are only taken while a dictation session is open, and only on their own: Cmd,
116
+ Ctrl and Option combinations still reach your app. The second Enter, after the paste, sends your
117
+ prompt as usual.
118
+
119
+ ### What is on device, and what is not
120
+
121
+ - **Audio** is recorded, transcribed and synthesised on your Mac. It is never written to disk and never sent anywhere.
122
+ - **The agent's replies** are read from the hook payload on your Mac and spoken locally. Anything that looks like a key or token is replaced with the word "redacted" before it is spoken; code blocks, tables and URLs are skipped.
123
+ - **The network** is used once, to download the models from Hugging Face. After that you can work offline.
124
+ - **Nothing is collected**: no analytics, no accounts, no telemetry.
125
+
126
+ ## Hearing replies
127
+
128
+ Nothing else to run. When an agent finishes a turn, its Stop hook calls `softvoice hook`, which
129
+ hands the reply to a small daemon (started on demand, kept warm) that cleans and speaks it:
130
+
131
+ - the whole turn is spoken, not just the last paragraph: every text block of at least
132
+ `interim_min_chars` (200) since your message, so a long answer written before a tool call is not
133
+ lost, while short progress notes between tool calls are skipped
134
+ - code blocks, tables, URLs and long paths are dropped or shortened: look at the screen for those
135
+ - markdown is flattened; bullets become sentences so the voice pauses
136
+ - anything that looks like a key or token is replaced with "redacted"
137
+ - the whole reply is spoken, however long; set `max_chars` to cut long replies at a sentence boundary with "More on screen."
138
+ - permission prompts are spoken too; sending a new prompt interrupts speech
139
+
140
+ ```bash
141
+ softvoice say "hello there" # speak anything
142
+ softvoice stop # shut up
143
+ softvoice mute / unmute # stay silent across turns
144
+ softvoice voice jane # try another voice (see `softvoice voices`)
145
+ softvoice status | log | restart
146
+ ```
147
+
148
+ ### Following along
149
+
150
+ While a reply is read, the pill shows the sentence being spoken on one line: the words already
151
+ said and the current one are white, the rest grey, and long sentences slide left so the current
152
+ word stays in view. The daemon speaks one sentence at a time and learns its own speaking rate, so
153
+ the timing stays close.
154
+
155
+ On the page itself, a thin light grey underline is drawn under that sentence in the front window,
156
+ line by line, found through macOS accessibility. It works where the app exposes its text: the
157
+ Claude desktop app, Terminal, VS Code and most native apps. The underline follows the page: every
158
+ scroll event makes it re-read its position at once, and a slow safety read twice a second covers
159
+ keyboard scrolling and scrollbar drags. When there is nothing to underline, the app log says why.
160
+ Turn it off with the pill's menu or `highlight_on_page = false`.
161
+
162
+ ## Talking
163
+
164
+ The transcript is pasted wherever your cursor is: the Claude Code prompt in a terminal, the
165
+ desktop app, anything. Dictation and push-to-talk paste into whatever app is in front, since you
166
+ started them on purpose. Hands-free, where the mic opens by itself, is more careful: it pastes
167
+ only into terminals and agent apps (`paste_targets`) or an app you have dictated into before, and
168
+ otherwise puts the text on the clipboard and tells you.
169
+
170
+ Without the pill, from a terminal: `softvoice listen` (hold the key, speak, release) and
171
+ `softvoice listen --hands-free`. `--auto-submit` presses Enter for you; `--no-paste` only prints.
172
+
173
+ ## The pill and the menu bar
174
+
175
+ ```bash
176
+ softvoice app --hands-free # by hand; `softvoice autostart on` does this at every login
177
+ ```
178
+
179
+ A small dark glass pill (36 points tall) floats above every window, bottom right by default.
180
+ Drag it anywhere; it remembers the spot. Clicking it never steals focus, so your cursor stays in
181
+ the prompt. It uses one accent colour, and the accent always means "live": the waveform while the
182
+ mic listens, the speaker while the voice talks. Everything else is grey. Whenever text shows, the
183
+ capsule becomes a rounded rectangle, and the text is a single line that scrolls, newest words at
184
+ the right.
185
+
186
+ | button | click | shows |
187
+ | --- | --- | --- |
188
+ | mic | start a dictation session | coral at rest (talk is the next thing to do), a live coral waveform while it listens, grey while the voice speaks or it transcribes |
189
+ | speaker | stop the voice while it speaks; when quiet, read the text you have selected in the front app, or replay the last reply if nothing is selected | coral stop while speaking, grey play otherwise, slashed when muted |
190
+
191
+ Exactly one thing on the pill is coral at any time, and it is the thing to tap next.
192
+
193
+ **Dictation session**: click the mic (or tap the key) and the pill grows to the left into
194
+ **waveform · live text · ✕ · ✓**. Committed sentences are white; the sentence still forming, and
195
+ the hint text, are grey; the line scrolls so the newest words are always visible. Each time you
196
+ pause for `dictation_pause_ms` (0.7 s) the sentence is committed; the one you are still saying is
197
+ re-transcribed about once a second so you see it forming. `dictation_silence_s` (8 s) of silence
198
+ finishes the session by itself. The preview lives in the pill rather than in the prompt on
199
+ purpose: live text keeps revising itself, and the prompt only gets one clean paste that you can
200
+ edit before sending.
201
+
202
+ **Press and hold** either button, or right-click, for the options: Hands-free, Auto-send, Mute,
203
+ Underline, Voice, Hide pill, config file, Quit. `panel = false` in the config turns the pill off.
204
+
205
+ The menu bar gets one small waveform, the same in every state (the pill is where state shows);
206
+ `menu_bar_icon = false` hides it. Its first line says what softvoice is doing right now, and the
207
+ menu has **Hide pill** / **Show pill**, **Talk**, **Stop speaking**, **Mute**, **Hands-free** and
208
+ **Auto-send**, the config file, and Quit.
209
+
210
+ ### Running without a terminal
211
+
212
+ `softvoice autostart on` registers the app as a macOS login item: it starts when you log in, needs
213
+ no terminal, and comes back by itself if it ever crashes (not after you quit it on purpose).
214
+ `softvoice autostart off` removes the login item and stops the app; `softvoice autostart` shows
215
+ the state. The app's output goes to `~/.softvoice/app.log`.
216
+
217
+ Only one copy of the app runs at a time: `softvoice app` while it is already running just shows
218
+ the pill of the running one. **Hide pill** tucks the pill away while the key, the voice and the
219
+ menu bar keep working; **Show pill** brings it back. Quit from the menu stops the app until the
220
+ next login or the next `softvoice app`.
221
+
222
+ ## Hands-free
223
+
224
+ A conversation loop, on with `--hands-free`, the menu, or `hands_free = true`. Each time the
225
+ daemon finishes speaking a reply:
226
+
227
+ 1. a short high beep: the mic is open
228
+ 2. you talk; Silero VAD waits until you pause for `end_silence_ms` (0.9 s)
229
+ 3. a short low beep: the mic is closed; Parakeet transcribes
230
+ 4. the text is pasted into the agent's prompt, where you can read and edit it, then you press Enter
231
+
232
+ To send without touching the keyboard, end what you say with "send it" (`send_words`), or turn
233
+ on **Auto-send** (`auto_submit = true`), which presses Enter for you. Say nothing for
234
+ `listen_timeout_s` (10 s) and the mic closes. Say "cancel" or "never mind" to discard a take.
235
+ Hold the talk key at any moment to interrupt the speaker and take over. If you typed a prompt
236
+ yourself, or interrupted the speaker, the mic stays closed.
237
+
238
+ ## Configuration
239
+
240
+ `~/.softvoice/config.toml` (`softvoice config` prints it) lists every setting with its default,
241
+ all commented out; remove the `# ` from a line to change it. Restart the daemon after changes
242
+ (`softvoice restart`) and relaunch the app (`softvoice autostart on` again, or Quit and `softvoice app`).
243
+
244
+ | key | default | meaning |
245
+ | --- | --- | --- |
246
+ | `voice` | `alba` | a predefined voice, or a path to a short WAV to clone |
247
+ | `language` | `english` | Pocket TTS model: `english`, `french`, `german`, `spanish`, `italian`, `portuguese`, `dutch`, or the tiny `english_18m_2026-10` |
248
+ | `max_chars` | `0` | cut long replies here; 0 speaks the whole reply |
249
+ | `speak_whole_turn` | `true` | speak every substantial text block of the turn, not only the last |
250
+ | `interim_min_chars` | `200` | shorter blocks before tool calls count as progress notes and are skipped |
251
+ | `speak_notifications` | `["permission_prompt"]` | add `"idle_prompt"` to be nagged |
252
+ | `interrupt_on_prompt` | `true` | stop speaking when you send a prompt |
253
+ | `announce_project` | `false` | prefix replies with the project folder name |
254
+ | `hotkey` | `alt_r` | the talk key: `alt_r`, `alt_l`, `cmd_r`, `ctrl_r`, `shift_r`, `fn`, `f1`…`f20`, or a combination such as `alt+space`, `ctrl+shift+d` (modifiers `alt`, `cmd`, `ctrl`, `shift`, `fn` plus a letter, digit, `space`, `tab` or F-key) |
255
+ | `stt_model` | `mlx-community/parakeet-tdt-0.6b-v3` | any Parakeet model in the mlx-community collection |
256
+ | `auto_submit` | `false` | press Enter after pasting; off means the transcript waits in your prompt for review |
257
+ | `send_words` | `["send it", ...]` | say one at the end of a take to send it hands-free |
258
+ | `tap_threshold_ms` | `350` | shorter press is tap-to-talk, longer is push-to-talk |
259
+ | `dictation_pause_ms` | `700` | pause that commits a sentence to the preview |
260
+ | `dictation_silence_s` | `8` | silence that finishes a dictation session |
261
+ | `panel` / `menu_bar_icon` | `true` | show the floating pill / the menu bar icon |
262
+ | `highlight_on_page` | `true` | underline the sentence being read in the front window |
263
+ | `clear_selection` | `true` | after you select text and press play, ask the app to drop the selection (native apps and editors; the Claude app ignores the request) |
264
+ | `accent` | `"#E8735A"` | the pill's live colour (waveform, speaker) |
265
+ | `underline` | `"#AAAAAA"` | the colour of the underline on the page |
266
+ | `hands_free` | `false` | hands-free by default |
267
+ | `end_silence_ms` | `900` | pause that ends your utterance |
268
+ | `listen_timeout_s` | `10` | close the mic when you say nothing |
269
+ | `earcon` | `true` | beeps when the mic opens and closes |
270
+ | `cancel_words` | `["cancel", ...]` | say one of these to discard a take |
271
+ | `paste_targets` | terminals, Claude, VS Code, Cursor, JetBrains, Codex | bundle ids hands-free may paste into (dictation you start yourself pastes anywhere) |
272
+
273
+ Set `SOFTVOICE_DISABLED=1` in an agent's environment to silence it without uninstalling.
274
+
275
+ ## How it fits together
276
+
277
+ ```
278
+ Claude Code / Codex ──Stop hook (stdin JSON)──▶ softvoice hook ──unix socket──▶ daemon
279
+ (40 ms, no ML imports) │ clean markdown
280
+ │ Pocket TTS stream
281
+ ▼ speakers
282
+ you ──tap or hold the key──▶ softvoice app ──Parakeet──▶ paste into the focused prompt
283
+ ▲ hands-free: the daemon's "speech_end" event opens the mic,
284
+ Silero VAD ends the take, the transcript is pasted (+ Enter)
285
+ ```
286
+
287
+ The hook command is tiny and imports no ML code, so it never slows the agent down. The daemon
288
+ holds the voice model warm between turns. The app holds the listener: the key watcher, the mic,
289
+ Parakeet, the pill and the underline.
290
+
291
+ ## Status
292
+
293
+ Early, and in daily use by its author on an M4 MacBook Air. Verified live: the hands-free loop,
294
+ the pill, dictation sessions with Enter and Escape, the Option+Space talk key, follow-along text
295
+ and the underline on the page. Verified without a human: hook latency, speech from Stop and
296
+ Notification payloads, interruption on new prompts, the daemon's event stream, VAD end-of-utterance
297
+ timing on recorded speech, and a Pocket TTS to Parakeet round trip. Not yet: languages other than
298
+ English, a signed app bundle, Windows or Linux (the listener needs Apple's MLX).
299
+
300
+ Found a rough edge? Open an issue with the lines from `softvoice log` and `~/.softvoice/app.log`.
301
+
302
+ ## Credits
303
+
304
+ [Pocket TTS](https://github.com/kyutai-labs/pocket-tts) by Kyutai. [Parakeet TDT 0.6B v3](https://huggingface.co/nvidia/parakeet-tdt-0.6b-v3)
305
+ by NVIDIA (CC-BY-4.0), converted for MLX by the [mlx-community](https://huggingface.co/mlx-community/parakeet-tdt-0.6b-v3)
306
+ and run with [parakeet-mlx](https://github.com/senstella/parakeet-mlx) by senstella. [Silero VAD](https://github.com/snakers4/silero-vad)
307
+ for end-of-utterance detection. [rumps](https://github.com/jaredks/rumps) for the menu bar. See
308
+ [LANDSCAPE.md](LANDSCAPE.md) for the survey of voice tools this started from.
@@ -0,0 +1,278 @@
1
+ # softvoice
2
+
3
+ **Talk to Claude Code and Codex, and hear them talk back. Everything stays on your Mac.**
4
+
5
+ ![macOS Apple Silicon](https://img.shields.io/badge/macOS%2014%2B-Apple%20Silicon-black?logo=apple)
6
+ ![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-3776AB?logo=python&logoColor=white)
7
+ ![License: MIT](https://img.shields.io/badge/license-MIT-green)
8
+ [![PyPI](https://img.shields.io/pypi/v/softvoice?color=blue)](https://pypi.org/project/softvoice/)
9
+ ![On-device](https://img.shields.io/badge/your%20voice%20%26%20text-never%20leave%20your%20Mac-blue)
10
+ [![CI](https://github.com/SPra03/softvoice/actions/workflows/ci.yml/badge.svg)](https://github.com/SPra03/softvoice/actions/workflows/ci.yml)
11
+
12
+ Your coding agent finishes a turn and reads its reply aloud, underlining each sentence on the
13
+ page as it goes. You answer by talking: tap a key, say what you want, press Enter. A small glass
14
+ pill floats above your windows and shows what is happening. No accounts, no API keys, no audio
15
+ sent anywhere: after a one-time model download it works with the network unplugged.
16
+
17
+ - **Speaking**: [Pocket TTS](https://github.com/kyutai-labs/pocket-tts) by Kyutai, a 100M-parameter voice that runs on the CPU and starts talking about 200 ms after the text arrives
18
+ - **Listening**: [Parakeet TDT 0.6B v3](https://huggingface.co/nvidia/parakeet-tdt-0.6b-v3) by NVIDIA, run through [parakeet-mlx](https://github.com/senstella/parakeet-mlx) on Apple Silicon; transcribes faster than you speak
19
+ - **Integration**: the agents' own `Stop`, `Notification` and `UserPromptSubmit` hooks. No MCP server, no wrapping the terminal, no changes to how you work
20
+
21
+ English first (the listener understands 24 other European languages; the voice has English, French, German, Spanish, Italian, Portuguese and Dutch). Apple Silicon Mac, macOS 14 or newer. MIT licence. Early, and in daily use by its author.
22
+
23
+ ## Install
24
+
25
+ Three commands, with [uv](https://docs.astral.sh/uv/) doing the Python work:
26
+
27
+ ```bash
28
+ brew install uv
29
+ uv tool install softvoice
30
+ softvoice setup
31
+ ```
32
+
33
+ (Or the newest commit straight from GitHub: `uv tool install git+https://github.com/SPra03/softvoice`.)
34
+
35
+ `setup` downloads the two models once (Pocket TTS, about 230 MB; Parakeet, 2.5 GB) and asks
36
+ macOS for the permissions below. Then:
37
+
38
+ ```bash
39
+ softvoice install # hooks into Claude Code (and Codex, if ~/.codex exists)
40
+ softvoice autostart on # the pill, now and at every login, with no terminal left open
41
+ ```
42
+
43
+ `install` writes three hooks into `~/.claude/settings.json` (a backup is kept next to it) and,
44
+ if `~/.codex` exists, into `~/.codex/hooks.json`. Restart your Claude Code sessions so they pick
45
+ the hooks up; Codex needs a one-time `/hooks` to trust them. Upgrade later with
46
+ `uv tool upgrade softvoice`; `softvoice uninstall` removes the hooks, `softvoice autostart off`
47
+ the login item, and everything softvoice owns lives in `~/.softvoice`.
48
+
49
+ Needs about 4 GB of disk for the models and the Python environment, and about 2 GB of memory
50
+ while running. The models load once at start; idle, it uses no CPU.
51
+
52
+ ### Permissions
53
+
54
+ macOS grants these to the app that owns the terminal you run softvoice from (Terminal, iTerm,
55
+ the Claude desktop app, VS Code), or to Python itself when the login item starts it. Each is
56
+ asked for once.
57
+
58
+ | permission | needed for | asked |
59
+ | --- | --- | --- |
60
+ | Microphone | listening | the first time the mic opens |
61
+ | Accessibility | pasting your words into the prompt; reading the page for the underline | by `setup`, or the first paste |
62
+ | Input Monitoring | seeing the talk key and scroll events | by `setup`, or the first `listen` or `app` |
63
+
64
+ If you grant Accessibility after the app has started, it restarts itself (under the login item)
65
+ so the underline can read the page.
66
+
67
+ ## Everyday use
68
+
69
+ The talk key is **right Option** by default: the ⌥ key to the right of the space bar, chosen
70
+ because nothing else uses it on its own. Prefer a combination? One line in the config:
71
+ `hotkey = "alt+space"` (or `ctrl+shift+d`, `fn+space`, ...). A combination is swallowed, so the
72
+ app in front never sees it.
73
+
74
+ | when | you | softvoice |
75
+ | --- | --- | --- |
76
+ | any time | **tap** the talk key, or click the mic | opens a dictation session: talk as long as you like, your words appear in the pill |
77
+ | dictating | tap again, press **Enter**, or click **✓** | pastes everything into your prompt, where you can still edit it |
78
+ | dictating | press **Escape**, click **✕**, or say "cancel" | discards it |
79
+ | dictating | end with "send it" | pastes and presses Enter for you |
80
+ | any time | **hold** the talk key, speak, release | push-to-talk: a quick take, pasted where your cursor is |
81
+ | while it speaks | tap the talk key, or click the speaker | stops the voice |
82
+ | while it is quiet | click the speaker | reads the text you have selected in any app, or replays the last reply |
83
+ | hands-free on | just talk after the beep | after every spoken reply the mic opens by itself, pauses send |
84
+
85
+ Enter and Escape are only taken while a dictation session is open, and only on their own: Cmd,
86
+ Ctrl and Option combinations still reach your app. The second Enter, after the paste, sends your
87
+ prompt as usual.
88
+
89
+ ### What is on device, and what is not
90
+
91
+ - **Audio** is recorded, transcribed and synthesised on your Mac. It is never written to disk and never sent anywhere.
92
+ - **The agent's replies** are read from the hook payload on your Mac and spoken locally. Anything that looks like a key or token is replaced with the word "redacted" before it is spoken; code blocks, tables and URLs are skipped.
93
+ - **The network** is used once, to download the models from Hugging Face. After that you can work offline.
94
+ - **Nothing is collected**: no analytics, no accounts, no telemetry.
95
+
96
+ ## Hearing replies
97
+
98
+ Nothing else to run. When an agent finishes a turn, its Stop hook calls `softvoice hook`, which
99
+ hands the reply to a small daemon (started on demand, kept warm) that cleans and speaks it:
100
+
101
+ - the whole turn is spoken, not just the last paragraph: every text block of at least
102
+ `interim_min_chars` (200) since your message, so a long answer written before a tool call is not
103
+ lost, while short progress notes between tool calls are skipped
104
+ - code blocks, tables, URLs and long paths are dropped or shortened: look at the screen for those
105
+ - markdown is flattened; bullets become sentences so the voice pauses
106
+ - anything that looks like a key or token is replaced with "redacted"
107
+ - the whole reply is spoken, however long; set `max_chars` to cut long replies at a sentence boundary with "More on screen."
108
+ - permission prompts are spoken too; sending a new prompt interrupts speech
109
+
110
+ ```bash
111
+ softvoice say "hello there" # speak anything
112
+ softvoice stop # shut up
113
+ softvoice mute / unmute # stay silent across turns
114
+ softvoice voice jane # try another voice (see `softvoice voices`)
115
+ softvoice status | log | restart
116
+ ```
117
+
118
+ ### Following along
119
+
120
+ While a reply is read, the pill shows the sentence being spoken on one line: the words already
121
+ said and the current one are white, the rest grey, and long sentences slide left so the current
122
+ word stays in view. The daemon speaks one sentence at a time and learns its own speaking rate, so
123
+ the timing stays close.
124
+
125
+ On the page itself, a thin light grey underline is drawn under that sentence in the front window,
126
+ line by line, found through macOS accessibility. It works where the app exposes its text: the
127
+ Claude desktop app, Terminal, VS Code and most native apps. The underline follows the page: every
128
+ scroll event makes it re-read its position at once, and a slow safety read twice a second covers
129
+ keyboard scrolling and scrollbar drags. When there is nothing to underline, the app log says why.
130
+ Turn it off with the pill's menu or `highlight_on_page = false`.
131
+
132
+ ## Talking
133
+
134
+ The transcript is pasted wherever your cursor is: the Claude Code prompt in a terminal, the
135
+ desktop app, anything. Dictation and push-to-talk paste into whatever app is in front, since you
136
+ started them on purpose. Hands-free, where the mic opens by itself, is more careful: it pastes
137
+ only into terminals and agent apps (`paste_targets`) or an app you have dictated into before, and
138
+ otherwise puts the text on the clipboard and tells you.
139
+
140
+ Without the pill, from a terminal: `softvoice listen` (hold the key, speak, release) and
141
+ `softvoice listen --hands-free`. `--auto-submit` presses Enter for you; `--no-paste` only prints.
142
+
143
+ ## The pill and the menu bar
144
+
145
+ ```bash
146
+ softvoice app --hands-free # by hand; `softvoice autostart on` does this at every login
147
+ ```
148
+
149
+ A small dark glass pill (36 points tall) floats above every window, bottom right by default.
150
+ Drag it anywhere; it remembers the spot. Clicking it never steals focus, so your cursor stays in
151
+ the prompt. It uses one accent colour, and the accent always means "live": the waveform while the
152
+ mic listens, the speaker while the voice talks. Everything else is grey. Whenever text shows, the
153
+ capsule becomes a rounded rectangle, and the text is a single line that scrolls, newest words at
154
+ the right.
155
+
156
+ | button | click | shows |
157
+ | --- | --- | --- |
158
+ | mic | start a dictation session | coral at rest (talk is the next thing to do), a live coral waveform while it listens, grey while the voice speaks or it transcribes |
159
+ | speaker | stop the voice while it speaks; when quiet, read the text you have selected in the front app, or replay the last reply if nothing is selected | coral stop while speaking, grey play otherwise, slashed when muted |
160
+
161
+ Exactly one thing on the pill is coral at any time, and it is the thing to tap next.
162
+
163
+ **Dictation session**: click the mic (or tap the key) and the pill grows to the left into
164
+ **waveform · live text · ✕ · ✓**. Committed sentences are white; the sentence still forming, and
165
+ the hint text, are grey; the line scrolls so the newest words are always visible. Each time you
166
+ pause for `dictation_pause_ms` (0.7 s) the sentence is committed; the one you are still saying is
167
+ re-transcribed about once a second so you see it forming. `dictation_silence_s` (8 s) of silence
168
+ finishes the session by itself. The preview lives in the pill rather than in the prompt on
169
+ purpose: live text keeps revising itself, and the prompt only gets one clean paste that you can
170
+ edit before sending.
171
+
172
+ **Press and hold** either button, or right-click, for the options: Hands-free, Auto-send, Mute,
173
+ Underline, Voice, Hide pill, config file, Quit. `panel = false` in the config turns the pill off.
174
+
175
+ The menu bar gets one small waveform, the same in every state (the pill is where state shows);
176
+ `menu_bar_icon = false` hides it. Its first line says what softvoice is doing right now, and the
177
+ menu has **Hide pill** / **Show pill**, **Talk**, **Stop speaking**, **Mute**, **Hands-free** and
178
+ **Auto-send**, the config file, and Quit.
179
+
180
+ ### Running without a terminal
181
+
182
+ `softvoice autostart on` registers the app as a macOS login item: it starts when you log in, needs
183
+ no terminal, and comes back by itself if it ever crashes (not after you quit it on purpose).
184
+ `softvoice autostart off` removes the login item and stops the app; `softvoice autostart` shows
185
+ the state. The app's output goes to `~/.softvoice/app.log`.
186
+
187
+ Only one copy of the app runs at a time: `softvoice app` while it is already running just shows
188
+ the pill of the running one. **Hide pill** tucks the pill away while the key, the voice and the
189
+ menu bar keep working; **Show pill** brings it back. Quit from the menu stops the app until the
190
+ next login or the next `softvoice app`.
191
+
192
+ ## Hands-free
193
+
194
+ A conversation loop, on with `--hands-free`, the menu, or `hands_free = true`. Each time the
195
+ daemon finishes speaking a reply:
196
+
197
+ 1. a short high beep: the mic is open
198
+ 2. you talk; Silero VAD waits until you pause for `end_silence_ms` (0.9 s)
199
+ 3. a short low beep: the mic is closed; Parakeet transcribes
200
+ 4. the text is pasted into the agent's prompt, where you can read and edit it, then you press Enter
201
+
202
+ To send without touching the keyboard, end what you say with "send it" (`send_words`), or turn
203
+ on **Auto-send** (`auto_submit = true`), which presses Enter for you. Say nothing for
204
+ `listen_timeout_s` (10 s) and the mic closes. Say "cancel" or "never mind" to discard a take.
205
+ Hold the talk key at any moment to interrupt the speaker and take over. If you typed a prompt
206
+ yourself, or interrupted the speaker, the mic stays closed.
207
+
208
+ ## Configuration
209
+
210
+ `~/.softvoice/config.toml` (`softvoice config` prints it) lists every setting with its default,
211
+ all commented out; remove the `# ` from a line to change it. Restart the daemon after changes
212
+ (`softvoice restart`) and relaunch the app (`softvoice autostart on` again, or Quit and `softvoice app`).
213
+
214
+ | key | default | meaning |
215
+ | --- | --- | --- |
216
+ | `voice` | `alba` | a predefined voice, or a path to a short WAV to clone |
217
+ | `language` | `english` | Pocket TTS model: `english`, `french`, `german`, `spanish`, `italian`, `portuguese`, `dutch`, or the tiny `english_18m_2026-10` |
218
+ | `max_chars` | `0` | cut long replies here; 0 speaks the whole reply |
219
+ | `speak_whole_turn` | `true` | speak every substantial text block of the turn, not only the last |
220
+ | `interim_min_chars` | `200` | shorter blocks before tool calls count as progress notes and are skipped |
221
+ | `speak_notifications` | `["permission_prompt"]` | add `"idle_prompt"` to be nagged |
222
+ | `interrupt_on_prompt` | `true` | stop speaking when you send a prompt |
223
+ | `announce_project` | `false` | prefix replies with the project folder name |
224
+ | `hotkey` | `alt_r` | the talk key: `alt_r`, `alt_l`, `cmd_r`, `ctrl_r`, `shift_r`, `fn`, `f1`…`f20`, or a combination such as `alt+space`, `ctrl+shift+d` (modifiers `alt`, `cmd`, `ctrl`, `shift`, `fn` plus a letter, digit, `space`, `tab` or F-key) |
225
+ | `stt_model` | `mlx-community/parakeet-tdt-0.6b-v3` | any Parakeet model in the mlx-community collection |
226
+ | `auto_submit` | `false` | press Enter after pasting; off means the transcript waits in your prompt for review |
227
+ | `send_words` | `["send it", ...]` | say one at the end of a take to send it hands-free |
228
+ | `tap_threshold_ms` | `350` | shorter press is tap-to-talk, longer is push-to-talk |
229
+ | `dictation_pause_ms` | `700` | pause that commits a sentence to the preview |
230
+ | `dictation_silence_s` | `8` | silence that finishes a dictation session |
231
+ | `panel` / `menu_bar_icon` | `true` | show the floating pill / the menu bar icon |
232
+ | `highlight_on_page` | `true` | underline the sentence being read in the front window |
233
+ | `clear_selection` | `true` | after you select text and press play, ask the app to drop the selection (native apps and editors; the Claude app ignores the request) |
234
+ | `accent` | `"#E8735A"` | the pill's live colour (waveform, speaker) |
235
+ | `underline` | `"#AAAAAA"` | the colour of the underline on the page |
236
+ | `hands_free` | `false` | hands-free by default |
237
+ | `end_silence_ms` | `900` | pause that ends your utterance |
238
+ | `listen_timeout_s` | `10` | close the mic when you say nothing |
239
+ | `earcon` | `true` | beeps when the mic opens and closes |
240
+ | `cancel_words` | `["cancel", ...]` | say one of these to discard a take |
241
+ | `paste_targets` | terminals, Claude, VS Code, Cursor, JetBrains, Codex | bundle ids hands-free may paste into (dictation you start yourself pastes anywhere) |
242
+
243
+ Set `SOFTVOICE_DISABLED=1` in an agent's environment to silence it without uninstalling.
244
+
245
+ ## How it fits together
246
+
247
+ ```
248
+ Claude Code / Codex ──Stop hook (stdin JSON)──▶ softvoice hook ──unix socket──▶ daemon
249
+ (40 ms, no ML imports) │ clean markdown
250
+ │ Pocket TTS stream
251
+ ▼ speakers
252
+ you ──tap or hold the key──▶ softvoice app ──Parakeet──▶ paste into the focused prompt
253
+ ▲ hands-free: the daemon's "speech_end" event opens the mic,
254
+ Silero VAD ends the take, the transcript is pasted (+ Enter)
255
+ ```
256
+
257
+ The hook command is tiny and imports no ML code, so it never slows the agent down. The daemon
258
+ holds the voice model warm between turns. The app holds the listener: the key watcher, the mic,
259
+ Parakeet, the pill and the underline.
260
+
261
+ ## Status
262
+
263
+ Early, and in daily use by its author on an M4 MacBook Air. Verified live: the hands-free loop,
264
+ the pill, dictation sessions with Enter and Escape, the Option+Space talk key, follow-along text
265
+ and the underline on the page. Verified without a human: hook latency, speech from Stop and
266
+ Notification payloads, interruption on new prompts, the daemon's event stream, VAD end-of-utterance
267
+ timing on recorded speech, and a Pocket TTS to Parakeet round trip. Not yet: languages other than
268
+ English, a signed app bundle, Windows or Linux (the listener needs Apple's MLX).
269
+
270
+ Found a rough edge? Open an issue with the lines from `softvoice log` and `~/.softvoice/app.log`.
271
+
272
+ ## Credits
273
+
274
+ [Pocket TTS](https://github.com/kyutai-labs/pocket-tts) by Kyutai. [Parakeet TDT 0.6B v3](https://huggingface.co/nvidia/parakeet-tdt-0.6b-v3)
275
+ by NVIDIA (CC-BY-4.0), converted for MLX by the [mlx-community](https://huggingface.co/mlx-community/parakeet-tdt-0.6b-v3)
276
+ and run with [parakeet-mlx](https://github.com/senstella/parakeet-mlx) by senstella. [Silero VAD](https://github.com/snakers4/silero-vad)
277
+ for end-of-utterance detection. [rumps](https://github.com/jaredks/rumps) for the menu bar. See
278
+ [LANDSCAPE.md](LANDSCAPE.md) for the survey of voice tools this started from.