@domdhi/claude-code-tts 1.0.0
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.
- package/INSTALL.md +335 -0
- package/README.md +133 -0
- package/bin/install.js +36 -0
- package/daemon.py +370 -0
- package/install.py +269 -0
- package/package.json +35 -0
- package/repeat.py +108 -0
- package/stop.py +244 -0
- package/task-hook.py +171 -0
- package/voices.json +14 -0
package/INSTALL.md
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
# claude-code-tts — Installation Reference
|
|
2
|
+
|
|
3
|
+
Full reference for install options, voice configuration, hooks wiring, and troubleshooting.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Stack
|
|
8
|
+
|
|
9
|
+
| Package | Role |
|
|
10
|
+
|---------|------|
|
|
11
|
+
| `edge-tts` | Primary TTS — Microsoft neural voices, free, cloud, ~0 RAM |
|
|
12
|
+
| `miniaudio` | Decodes edge-tts MP3 output to PCM for playback |
|
|
13
|
+
| `sounddevice` | Audio playback |
|
|
14
|
+
| `cffi` | sounddevice's C backend (not auto-installed by pip) |
|
|
15
|
+
| `kokoro-onnx` | Optional offline fallback — activates if edge-tts fails |
|
|
16
|
+
| `onnxruntime` | ONNX runtime (auto-installed with kokoro-onnx) |
|
|
17
|
+
|
|
18
|
+
**Engine priority:** edge-tts (primary) → kokoro-onnx (fallback, if installed)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
### Option A — installer script (recommended)
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git clone https://github.com/domdhi/claude-code-tts
|
|
28
|
+
cd claude-code-tts
|
|
29
|
+
pip install edge-tts miniaudio sounddevice cffi
|
|
30
|
+
python install.py
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The installer:
|
|
34
|
+
1. Checks Python version (3.10+ required)
|
|
35
|
+
2. Installs required packages
|
|
36
|
+
3. Copies hook files to `~/.claude/hooks/tts/`
|
|
37
|
+
4. Creates the `on` file (TTS enabled immediately)
|
|
38
|
+
5. Optionally installs kokoro-onnx offline fallback (~82MB)
|
|
39
|
+
6. Prints the `settings.json` snippet to add
|
|
40
|
+
|
|
41
|
+
### Option B — manual
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Install packages
|
|
45
|
+
pip install edge-tts miniaudio sounddevice cffi
|
|
46
|
+
|
|
47
|
+
# Create install dir
|
|
48
|
+
mkdir -p ~/.claude/hooks/tts
|
|
49
|
+
|
|
50
|
+
# Copy files
|
|
51
|
+
cp daemon.py stop.py task-hook.py repeat.py voices.json ~/.claude/hooks/tts/
|
|
52
|
+
|
|
53
|
+
# Enable TTS
|
|
54
|
+
touch ~/.claude/hooks/tts/on
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Then add the settings.json snippet below manually.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Claude Code Settings
|
|
62
|
+
|
|
63
|
+
Add to `~/.claude/settings.json`. If you already have a `"hooks"` key, merge these entries — don't replace the whole object.
|
|
64
|
+
|
|
65
|
+
**Mac/Linux:**
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"hooks": {
|
|
69
|
+
"Stop": [
|
|
70
|
+
{
|
|
71
|
+
"hooks": [
|
|
72
|
+
{
|
|
73
|
+
"type": "command",
|
|
74
|
+
"command": "python \"$HOME/.claude/hooks/tts/stop.py\""
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
"PostToolUse": [
|
|
80
|
+
{
|
|
81
|
+
"matcher": "Task",
|
|
82
|
+
"hooks": [
|
|
83
|
+
{
|
|
84
|
+
"type": "command",
|
|
85
|
+
"command": "python \"$HOME/.claude/hooks/tts/task-hook.py\""
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
"UserPromptSubmit": [
|
|
91
|
+
{
|
|
92
|
+
"hooks": [
|
|
93
|
+
{
|
|
94
|
+
"type": "command",
|
|
95
|
+
"command": "python \"$HOME/.claude/hooks/tts/repeat.py\""
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Windows** (replace `C:\Users\YourName` with your actual home path):
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"hooks": {
|
|
108
|
+
"Stop": [
|
|
109
|
+
{
|
|
110
|
+
"hooks": [
|
|
111
|
+
{
|
|
112
|
+
"type": "command",
|
|
113
|
+
"command": "python \"C:\\Users\\YourName\\.claude\\hooks\\tts\\stop.py\""
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
],
|
|
118
|
+
"PostToolUse": [
|
|
119
|
+
{
|
|
120
|
+
"matcher": "Task",
|
|
121
|
+
"hooks": [
|
|
122
|
+
{
|
|
123
|
+
"type": "command",
|
|
124
|
+
"command": "python \"C:\\Users\\YourName\\.claude\\hooks\\tts\\task-hook.py\""
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
],
|
|
129
|
+
"UserPromptSubmit": [
|
|
130
|
+
{
|
|
131
|
+
"hooks": [
|
|
132
|
+
{
|
|
133
|
+
"type": "command",
|
|
134
|
+
"command": "python \"C:\\Users\\YourName\\.claude\\hooks\\tts\\repeat.py\""
|
|
135
|
+
}
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Offline Fallback (kokoro-onnx)
|
|
146
|
+
|
|
147
|
+
kokoro-onnx is an optional local TTS engine. It activates automatically if edge-tts fails (no internet, rate limit, etc.).
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
pip install kokoro-onnx
|
|
151
|
+
|
|
152
|
+
# Download model files (~82MB total)
|
|
153
|
+
# Mac/Linux:
|
|
154
|
+
mkdir -p ~/.claude/hooks/tts/models
|
|
155
|
+
curl -L "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/kokoro-v1.0.onnx" \
|
|
156
|
+
-o ~/.claude/hooks/tts/models/kokoro-v1.0.onnx
|
|
157
|
+
curl -L "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/voices-v1.0.bin" \
|
|
158
|
+
-o ~/.claude/hooks/tts/models/voices-v1.0.bin
|
|
159
|
+
|
|
160
|
+
# Windows (PowerShell):
|
|
161
|
+
New-Item -ItemType Directory -Force "$env:USERPROFILE\.claude\hooks\tts\models"
|
|
162
|
+
Invoke-WebRequest "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/kokoro-v1.0.onnx" `
|
|
163
|
+
-OutFile "$env:USERPROFILE\.claude\hooks\tts\models\kokoro-v1.0.onnx"
|
|
164
|
+
Invoke-WebRequest "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/voices-v1.0.bin" `
|
|
165
|
+
-OutFile "$env:USERPROFILE\.claude\hooks\tts\models\voices-v1.0.bin"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Voice Configuration
|
|
171
|
+
|
|
172
|
+
Edit `~/.claude/hooks/tts/voices.json`.
|
|
173
|
+
|
|
174
|
+
### Available voices
|
|
175
|
+
|
|
176
|
+
| Key | Edge TTS voice | Style |
|
|
177
|
+
|-----|----------------|-------|
|
|
178
|
+
| `af_heart` | en-US-AriaNeural | warm, natural female (default) |
|
|
179
|
+
| `af_bella` | en-US-MichelleNeural | polished female |
|
|
180
|
+
| `af_sarah` | en-US-SaraNeural | professional female |
|
|
181
|
+
| `af_sky` | en-US-JennyNeural | friendly, conversational |
|
|
182
|
+
| `af_nova` | en-US-MonicaNeural | energetic female |
|
|
183
|
+
| `am_michael` | en-US-GuyNeural | natural, authoritative male |
|
|
184
|
+
| `am_adam` | en-US-DavisNeural | deep male |
|
|
185
|
+
| `am_echo` | en-US-TonyNeural | casual male |
|
|
186
|
+
| `am_eric` | en-US-EricNeural | confident male |
|
|
187
|
+
| `am_liam` | en-US-RyanNeural | young, energetic male |
|
|
188
|
+
| `am_onyx` | en-US-ChristopherNeural | deep, authoritative male |
|
|
189
|
+
|
|
190
|
+
### Voice priority (highest → lowest)
|
|
191
|
+
|
|
192
|
+
1. `[AgentName]:` prefix in response text → agent voice from `voices.json`
|
|
193
|
+
2. Project key match → `voices.json` `"projects"` section
|
|
194
|
+
3. `"default"` entry in `voices.json`
|
|
195
|
+
|
|
196
|
+
### Per-agent voices (task-hook.py)
|
|
197
|
+
|
|
198
|
+
`task-hook.py` reads `subagent_type` from the Task tool input and looks up the agent by name:
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"default": {"voice": "af_heart", "speed": 1.0},
|
|
203
|
+
"general-purpose": {"voice": "am_michael", "speed": 1.0},
|
|
204
|
+
"code-reviewer": {"voice": "am_onyx", "speed": 0.9}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Per-agent prefix (stop.py)
|
|
209
|
+
|
|
210
|
+
Any agent that begins its response with `[AgentName]:` gets routed to that voice. Add to the agent's system prompt:
|
|
211
|
+
```
|
|
212
|
+
Always begin your response with [AgentName]:
|
|
213
|
+
```
|
|
214
|
+
Add to `voices.json`:
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"MyAgent": {"voice": "am_adam", "speed": 0.9}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
The hook strips `[AgentName]:` before speaking.
|
|
221
|
+
|
|
222
|
+
### Per-project voices
|
|
223
|
+
|
|
224
|
+
Add a `"projects"` section. Keys are matched as case-insensitive substrings of the encoded project path under `~/.claude/projects/`:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
ls ~/.claude/projects/ # shows encoded dir names like c--Users-me-Repos-MyProject
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
```json
|
|
231
|
+
{
|
|
232
|
+
"projects": {
|
|
233
|
+
"MyProject": {"voice": "am_onyx", "speed": 0.95},
|
|
234
|
+
"another-repo": {"voice": "af_sarah", "speed": 1.0}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Enable / Disable
|
|
242
|
+
|
|
243
|
+
TTS is gated on the presence of `~/.claude/hooks/tts/on`:
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
# Disable
|
|
247
|
+
rm ~/.claude/hooks/tts/on
|
|
248
|
+
|
|
249
|
+
# Re-enable
|
|
250
|
+
touch ~/.claude/hooks/tts/on # Mac/Linux
|
|
251
|
+
echo. > %USERPROFILE%\.claude\hooks\tts\on # Windows cmd
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Commands
|
|
257
|
+
|
|
258
|
+
Type in the Claude Code prompt:
|
|
259
|
+
|
|
260
|
+
| Prompt | Effect |
|
|
261
|
+
|--------|--------|
|
|
262
|
+
| `/voice:stop` or `/stop` | Stop speech immediately, clear queue |
|
|
263
|
+
| `/repeat` | Replay last spoken response |
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Daemon Protocol
|
|
268
|
+
|
|
269
|
+
The daemon runs on `localhost:6254` and accepts JSON lines:
|
|
270
|
+
|
|
271
|
+
| Command | Effect |
|
|
272
|
+
|---------|--------|
|
|
273
|
+
| `{"cmd": "speak", "text": "...", "voice": "af_heart", "speed": 1.0, "project": "repo"}` | Queue speech |
|
|
274
|
+
| `{"cmd": "stop"}` | Stop immediately, clear queue |
|
|
275
|
+
| `{"cmd": "ping"}` | Health check → `{"ok": true, "pid": N}` |
|
|
276
|
+
| `{"cmd": "quit"}` | Shut down daemon |
|
|
277
|
+
|
|
278
|
+
**Queue behavior:** at most one item per `project` key. New message from the same project replaces its queued slot. Messages from different projects line up. Omit `project` for single-project use.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Performance Tuning
|
|
283
|
+
|
|
284
|
+
The daemon runs at below-normal process priority and limits ONNX threads by default. To adjust:
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
# Top of daemon.py, before any imports
|
|
288
|
+
os.environ.setdefault('OMP_NUM_THREADS', '4') # lower = less CPU spike
|
|
289
|
+
os.environ.setdefault('ONNXRUNTIME_NUM_THREADS', '4') # higher = faster synthesis
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
After editing daemon.py, restart the daemon:
|
|
293
|
+
```bash
|
|
294
|
+
# Mac/Linux
|
|
295
|
+
pkill -f daemon.py
|
|
296
|
+
|
|
297
|
+
# Windows
|
|
298
|
+
taskkill /F /IM python.exe # kills all python processes
|
|
299
|
+
```
|
|
300
|
+
The daemon auto-restarts on the next response.
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Troubleshooting
|
|
305
|
+
|
|
306
|
+
### No audio output
|
|
307
|
+
- Check that the `on` file exists: `ls ~/.claude/hooks/tts/on`
|
|
308
|
+
- Check that settings.json hooks are wired correctly
|
|
309
|
+
- Check `~/.claude/hooks/tts/daemon.log` for errors
|
|
310
|
+
|
|
311
|
+
### edge-tts fails silently
|
|
312
|
+
- Requires internet access — check connectivity
|
|
313
|
+
- If offline, install kokoro-onnx fallback (see above)
|
|
314
|
+
- Check `~/.claude/hooks/tts/debug.log` for synthesis errors
|
|
315
|
+
|
|
316
|
+
### kokoro-onnx not found at startup
|
|
317
|
+
- This is expected if you skipped the offline fallback install
|
|
318
|
+
- The daemon will log: `kokoro-onnx not installed — edge-tts only`
|
|
319
|
+
- Install it if you need offline support: `pip install kokoro-onnx` + download models
|
|
320
|
+
|
|
321
|
+
### cffi not found / sounddevice import error
|
|
322
|
+
- Run: `pip install cffi`
|
|
323
|
+
- sounddevice doesn't always pull in cffi automatically
|
|
324
|
+
|
|
325
|
+
### Daemon keeps restarting / won't stay up
|
|
326
|
+
- Check for port conflict: `lsof -i :6254` (Mac/Linux) or `netstat -ano | findstr 6254` (Windows)
|
|
327
|
+
- Check `~/.claude/hooks/tts/daemon.log`
|
|
328
|
+
|
|
329
|
+
### Audio cuts off mid-sentence (kokoro fallback)
|
|
330
|
+
- kokoro-onnx has a 510-token (~1500 char) hard limit
|
|
331
|
+
- The daemon chunks text at sentence boundaries automatically — if you're hitting this, check `debug.log` for `IndexError`
|
|
332
|
+
|
|
333
|
+
### Windows: DETACHED_PROCESS causes silence
|
|
334
|
+
- Do not add `DETACHED_PROCESS` to the subprocess flags — it breaks the Windows audio session
|
|
335
|
+
- `CREATE_NO_WINDOW` only is correct (already set in the hook files)
|
package/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# claude-code-tts
|
|
2
|
+
|
|
3
|
+
Neural TTS hook system for [Claude Code](https://claude.ai/code). Reads Claude's responses aloud as they finish.
|
|
4
|
+
|
|
5
|
+
**Engines:** Edge TTS (Microsoft neural voices, free, requires internet) with automatic offline fallback to kokoro-onnx.
|
|
6
|
+
**Platform:** Windows, macOS, Linux
|
|
7
|
+
**Install:** one Python script, no build tools required
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @domdhi/claude-code-tts
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
That's it. The installer copies the hook files, enables TTS, optionally installs the offline fallback, and prints the `settings.json` snippet to add to Claude Code.
|
|
18
|
+
|
|
19
|
+
**Requirements:** Node.js 16+ and Python 3.10+ must both be installed. The hooks run in Python — Node is only used for the install command.
|
|
20
|
+
|
|
21
|
+
**Or install manually:**
|
|
22
|
+
```bash
|
|
23
|
+
git clone https://github.com/domdhi/claude-code-tts
|
|
24
|
+
cd claude-code-tts
|
|
25
|
+
pip install edge-tts miniaudio sounddevice cffi
|
|
26
|
+
python install.py
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## What It Does
|
|
32
|
+
|
|
33
|
+
Three Claude Code hooks work together:
|
|
34
|
+
|
|
35
|
+
| Hook | File | When it fires |
|
|
36
|
+
|------|------|---------------|
|
|
37
|
+
| `Stop` | `stop.py` | After every Claude response — reads it aloud |
|
|
38
|
+
| `PostToolUse:Task` | `task-hook.py` | After a subagent finishes — reads its output |
|
|
39
|
+
| `UserPromptSubmit` | `repeat.py` | On `/repeat` or `/voice:stop` commands |
|
|
40
|
+
|
|
41
|
+
A persistent daemon (`daemon.py`) keeps the TTS model loaded in the background. Hook files connect to it via TCP on `localhost:6254`, starting it automatically if needed.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Voice Configuration
|
|
46
|
+
|
|
47
|
+
Edit `~/.claude/hooks/tts/voices.json` to customize voices per agent or per project.
|
|
48
|
+
|
|
49
|
+
**Available voices:**
|
|
50
|
+
|
|
51
|
+
| Key | Edge TTS | Style |
|
|
52
|
+
|-----|----------|-------|
|
|
53
|
+
| `af_heart` | AriaNeural | warm female (default) |
|
|
54
|
+
| `af_bella` | MichelleNeural | polished female |
|
|
55
|
+
| `af_sarah` | SaraNeural | professional female |
|
|
56
|
+
| `af_sky` | JennyNeural | friendly female |
|
|
57
|
+
| `af_nova` | MonicaNeural | energetic female |
|
|
58
|
+
| `am_michael` | GuyNeural | natural male |
|
|
59
|
+
| `am_adam` | DavisNeural | deep male |
|
|
60
|
+
| `am_echo` | TonyNeural | casual male |
|
|
61
|
+
| `am_eric` | EricNeural | confident male |
|
|
62
|
+
| `am_liam` | RyanNeural | energetic male |
|
|
63
|
+
| `am_onyx` | ChristopherNeural | authoritative male |
|
|
64
|
+
|
|
65
|
+
**Per-agent voices** (add to `voices.json`):
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"default": {"voice": "af_heart", "speed": 1.0},
|
|
69
|
+
"general-purpose": {"voice": "am_michael", "speed": 1.0}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
The `task-hook.py` reads `subagent_type` from the Task tool input to look up the agent's voice automatically.
|
|
73
|
+
|
|
74
|
+
**Per-project voices** (add a `"projects"` section):
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"projects": {
|
|
78
|
+
"my-project": {"voice": "am_onyx", "speed": 0.95}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
Project keys are matched as case-insensitive substrings of the encoded project path under `~/.claude/projects/`.
|
|
83
|
+
|
|
84
|
+
**Per-agent prefix** — any agent that begins its response with `[AgentName]:` gets routed to that voice:
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"MyAgent": {"voice": "am_adam", "speed": 0.9}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
See [INSTALL.md](INSTALL.md) for full configuration reference.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Commands
|
|
96
|
+
|
|
97
|
+
Type these in the Claude Code prompt:
|
|
98
|
+
|
|
99
|
+
| Command | Effect |
|
|
100
|
+
|---------|--------|
|
|
101
|
+
| `/voice:stop` | Stop speech immediately |
|
|
102
|
+
| `/repeat` | Replay last response |
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Enable / Disable
|
|
107
|
+
|
|
108
|
+
TTS is controlled by the presence of an `on` file in the install directory:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Disable
|
|
112
|
+
rm ~/.claude/hooks/tts/on
|
|
113
|
+
|
|
114
|
+
# Re-enable
|
|
115
|
+
touch ~/.claude/hooks/tts/on # Mac/Linux
|
|
116
|
+
echo. > %USERPROFILE%\.claude\hooks\tts\on # Windows
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Requirements
|
|
122
|
+
|
|
123
|
+
- Python 3.10+
|
|
124
|
+
- Claude Code
|
|
125
|
+
- Internet connection (for Edge TTS primary engine)
|
|
126
|
+
- `edge-tts`, `miniaudio`, `sounddevice`, `cffi`
|
|
127
|
+
- Optional: `kokoro-onnx` + model files (~82MB) for offline fallback
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
MIT
|
package/bin/install.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
const { execFileSync } = require('child_process')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
|
|
7
|
+
// Find Python — try python3 first on Mac/Linux, python first on Windows
|
|
8
|
+
const candidates = process.platform === 'win32'
|
|
9
|
+
? ['python', 'python3']
|
|
10
|
+
: ['python3', 'python']
|
|
11
|
+
|
|
12
|
+
let python = null
|
|
13
|
+
for (const candidate of candidates) {
|
|
14
|
+
try {
|
|
15
|
+
execFileSync(candidate, ['--version'], { stdio: 'ignore' })
|
|
16
|
+
python = candidate
|
|
17
|
+
break
|
|
18
|
+
} catch {
|
|
19
|
+
// not found, try next
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!python) {
|
|
24
|
+
console.error('Error: Python 3.10+ is required but was not found.')
|
|
25
|
+
console.error('Install Python from https://python.org and try again.')
|
|
26
|
+
process.exit(1)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const script = path.join(__dirname, '..', 'install.py')
|
|
30
|
+
const args = process.argv.slice(2) // pass through --dir and any other flags
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
execFileSync(python, [script, ...args], { stdio: 'inherit' })
|
|
34
|
+
} catch (e) {
|
|
35
|
+
process.exit(e.status ?? 1)
|
|
36
|
+
}
|