agentvibes 5.12.0 → 5.13.1

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.
Files changed (73) hide show
  1. package/.agentvibes/install-manifest.json +111 -91
  2. package/.claude/commands/agent-vibes/commands.json +0 -20
  3. package/.claude/commands/agent-vibes/unmute.md +6 -2
  4. package/.claude/config/audio-effects.cfg +6 -6
  5. package/.claude/github-star-reminder.txt +1 -1
  6. package/.claude/hooks/agentvibes-session-id.sh +69 -0
  7. package/.claude/hooks/bmad-party-speak.sh +20 -4
  8. package/.claude/hooks/bmad-speak.sh +60 -2
  9. package/.claude/hooks/bmad-tts-injector.sh +20 -1
  10. package/.claude/hooks/bmad-voice-manager.sh +25 -3
  11. package/.claude/hooks/clawdbot-receiver-SECURE.sh +21 -2
  12. package/.claude/hooks/clawdbot-receiver.sh +19 -1
  13. package/.claude/hooks/elevenlabs-voices.sh +62 -0
  14. package/.claude/hooks/kokoro-installer.sh +20 -10
  15. package/.claude/hooks/language-manager.sh +10 -3
  16. package/.claude/hooks/party-set-room.sh +71 -0
  17. package/.claude/hooks/party-stage-roster.py +328 -0
  18. package/.claude/hooks/personality-manager.sh +19 -2
  19. package/.claude/hooks/piper-voice-manager.sh +3 -2
  20. package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +24 -5
  21. package/.claude/hooks/play-tts-elevenlabs.sh +38 -118
  22. package/.claude/hooks/play-tts-kokoro.sh +33 -6
  23. package/.claude/hooks/play-tts-soprano.sh +3 -2
  24. package/.claude/hooks/play-tts-ssh-remote.sh +37 -29
  25. package/.claude/hooks/play-tts-termux-ssh.sh +5 -4
  26. package/.claude/hooks/play-tts.sh +66 -61
  27. package/.claude/hooks/provider-catalog.json +352 -0
  28. package/.claude/hooks/provider-catalog.sh +161 -0
  29. package/.claude/hooks/provider-commands.sh +2 -1
  30. package/.claude/hooks/provider-manager.sh +47 -9
  31. package/.claude/hooks/python-resolver.sh +117 -0
  32. package/.claude/hooks/session-id.sh +56 -0
  33. package/.claude/hooks/session-start-tts.sh +39 -0
  34. package/.claude/hooks/speed-manager.sh +1 -1
  35. package/.claude/hooks/translate-manager.sh +3 -2
  36. package/.claude/hooks/translator.py +1 -1
  37. package/.claude/hooks/voice-manager.sh +242 -10
  38. package/.claude/hooks-windows/language-manager.ps1 +7 -1
  39. package/.claude/hooks-windows/personality-manager.ps1 +16 -1
  40. package/.claude/hooks-windows/play-tts-kokoro.ps1 +20 -4
  41. package/.claude/hooks-windows/play-tts.ps1 +32 -3
  42. package/.claude/hooks-windows/provider-catalog.ps1 +140 -0
  43. package/.claude/hooks-windows/provider-manager.ps1 +63 -8
  44. package/.claude/hooks-windows/tts-watcher.ps1 +33 -12
  45. package/.claude/hooks-windows/voice-manager-windows.ps1 +49 -0
  46. package/.mcp.json +0 -7
  47. package/README.md +12 -3
  48. package/RELEASE_NOTES.md +61 -0
  49. package/mcp-server/server.py +146 -49
  50. package/mcp-server/test_mcp_correctness.py +20 -2
  51. package/mcp-server/test_windows_script_parity.py +0 -2
  52. package/package.json +2 -2
  53. package/src/cli/list-voices.js +218 -114
  54. package/src/console/bling.js +71 -0
  55. package/src/console/music-preview.js +79 -0
  56. package/src/console/tabs/music-tab.js +16 -39
  57. package/src/console/tabs/settings-tab.js +195 -13
  58. package/src/console/tabs/setup-tab.js +9 -34
  59. package/src/console/tabs/voices-tab.js +83 -14
  60. package/src/console/widgets/track-picker.js +82 -0
  61. package/src/installer.js +136 -18
  62. package/src/services/provider-catalog.js +412 -0
  63. package/src/services/provider-voice-catalog.js +52 -73
  64. package/src/services/tts-engine-service.js +29 -0
  65. package/src/utils/provider-validator.js +62 -12
  66. package/.claude/commands/agent-vibes/language.md +0 -23
  67. package/.claude/commands/agent-vibes/learn.md +0 -67
  68. package/.claude/commands/agent-vibes/replay-target.md +0 -14
  69. package/.claude/commands/agent-vibes/target-voice.md +0 -26
  70. package/.claude/commands/agent-vibes/target.md +0 -30
  71. package/.claude/hooks/learn-manager.sh +0 -492
  72. package/.claude/hooks/replay-target-audio.sh +0 -95
  73. package/.claude/hooks-windows/learn-manager.ps1 +0 -241
@@ -0,0 +1,352 @@
1
+ {
2
+ "_generated": "DO NOT EDIT — generated from src/services/provider-catalog.js",
3
+ "_contentHash": "sha256:5e717024f1250a8f54d07291256e532606dba7b4a15ab9589305d1af6bba7f35",
4
+ "version": "5e717024f1250a8f",
5
+ "providers": [
6
+ {
7
+ "id": "soprano",
8
+ "engineId": "soprano",
9
+ "aliases": [],
10
+ "displayName": "Soprano TTS",
11
+ "voiceModel": "single",
12
+ "defaultVoice": "soprano-default",
13
+ "requires": "pip",
14
+ "runtime": {
15
+ "unix": "play-tts-soprano.sh",
16
+ "windows": "play-tts-soprano.ps1",
17
+ "darwinOnly": false
18
+ },
19
+ "voices": [
20
+ "soprano-default"
21
+ ]
22
+ },
23
+ {
24
+ "id": "piper",
25
+ "engineId": "piper",
26
+ "aliases": [],
27
+ "displayName": "Piper TTS",
28
+ "voiceModel": "discovered",
29
+ "defaultVoice": "en_US-lessac-medium",
30
+ "requires": "pip",
31
+ "runtime": {
32
+ "unix": "play-tts-piper.sh",
33
+ "windows": null,
34
+ "darwinOnly": false
35
+ },
36
+ "voices": null
37
+ },
38
+ {
39
+ "id": "kokoro",
40
+ "engineId": "kokoro",
41
+ "aliases": [],
42
+ "displayName": "Kokoro TTS",
43
+ "voiceModel": "static",
44
+ "defaultVoice": "af_heart",
45
+ "requires": "pip",
46
+ "runtime": {
47
+ "unix": "play-tts-kokoro.sh",
48
+ "windows": "play-tts-kokoro.ps1",
49
+ "darwinOnly": false
50
+ },
51
+ "voices": [
52
+ "af_heart",
53
+ "af_alloy",
54
+ "af_aoede",
55
+ "af_bella",
56
+ "af_jessica",
57
+ "af_kore",
58
+ "af_nicole",
59
+ "af_nova",
60
+ "af_river",
61
+ "af_sarah",
62
+ "af_sky",
63
+ "am_adam",
64
+ "am_echo",
65
+ "am_eric",
66
+ "am_fenrir",
67
+ "am_liam",
68
+ "am_michael",
69
+ "am_onyx",
70
+ "am_puck",
71
+ "bf_alice",
72
+ "bf_emma",
73
+ "bf_isabella",
74
+ "bf_lily",
75
+ "bm_daniel",
76
+ "bm_fable",
77
+ "bm_george",
78
+ "bm_lewis",
79
+ "jf_alpha",
80
+ "jf_gongitsune",
81
+ "jf_nezumi",
82
+ "jf_tebukuro",
83
+ "jm_kumo",
84
+ "zf_xiaobei",
85
+ "zf_xiaoni",
86
+ "zf_xiaoxiao",
87
+ "zf_xiaoyi",
88
+ "zm_yunxi",
89
+ "zm_yunxia",
90
+ "zm_yunyang",
91
+ "ef_dora",
92
+ "em_alex",
93
+ "em_santa",
94
+ "ff_siwis",
95
+ "hf_alpha",
96
+ "hm_omega",
97
+ "if_sara",
98
+ "im_nicola",
99
+ "pf_dora",
100
+ "pm_alex",
101
+ "pm_santa",
102
+ "kf_alpha",
103
+ "km_hyunsu"
104
+ ]
105
+ },
106
+ {
107
+ "id": "elevenlabs",
108
+ "engineId": "elevenlabs",
109
+ "aliases": [],
110
+ "displayName": "ElevenLabs",
111
+ "voiceModel": "name-to-id",
112
+ "defaultVoice": "Sarah",
113
+ "requires": "api-key",
114
+ "runtime": {
115
+ "unix": "play-tts-elevenlabs.sh",
116
+ "windows": null,
117
+ "darwinOnly": false
118
+ },
119
+ "voices": [
120
+ {
121
+ "id": "EXAVITQu4vr4xnSDxMaL",
122
+ "name": "Sarah",
123
+ "gender": "Female",
124
+ "lang": "en-US",
125
+ "desc": "Mature, reassuring"
126
+ },
127
+ {
128
+ "id": "CwhRBWXzGAHq8TQ4Fs17",
129
+ "name": "Roger",
130
+ "gender": "Male",
131
+ "lang": "en-US",
132
+ "desc": "Laid-back, casual"
133
+ },
134
+ {
135
+ "id": "FGY2WhTYpPnrIDTdsKH5",
136
+ "name": "Laura",
137
+ "gender": "Female",
138
+ "lang": "en-US",
139
+ "desc": "Enthusiast, quirky"
140
+ },
141
+ {
142
+ "id": "IKne3meq5aSn9XLyUdCD",
143
+ "name": "Charlie",
144
+ "gender": "Male",
145
+ "lang": "en-AU",
146
+ "desc": "Deep, confident"
147
+ },
148
+ {
149
+ "id": "JBFqnCBsd6RMkjVDRZzb",
150
+ "name": "George",
151
+ "gender": "Male",
152
+ "lang": "en-GB",
153
+ "desc": "Warm storyteller"
154
+ },
155
+ {
156
+ "id": "N2lVS1w4EtoT3dr4eOWO",
157
+ "name": "Callum",
158
+ "gender": "Male",
159
+ "lang": "en-US",
160
+ "desc": "Husky trickster"
161
+ },
162
+ {
163
+ "id": "SAz9YHcvj6GT2YYXdXww",
164
+ "name": "River",
165
+ "gender": "",
166
+ "lang": "en-US",
167
+ "desc": "Relaxed, neutral"
168
+ },
169
+ {
170
+ "id": "SOYHLrjzK2X1ezoPC6cr",
171
+ "name": "Harry",
172
+ "gender": "Male",
173
+ "lang": "en-US",
174
+ "desc": "Fierce warrior"
175
+ },
176
+ {
177
+ "id": "TX3LPaxmHKxFdv7VOQHJ",
178
+ "name": "Liam",
179
+ "gender": "Male",
180
+ "lang": "en-US",
181
+ "desc": "Energetic creator"
182
+ },
183
+ {
184
+ "id": "Xb7hH8MSUJpSbSDYk0k2",
185
+ "name": "Alice",
186
+ "gender": "Female",
187
+ "lang": "en-GB",
188
+ "desc": "Clear educator"
189
+ },
190
+ {
191
+ "id": "XrExE9yKIg1WjnnlVkGX",
192
+ "name": "Matilda",
193
+ "gender": "Female",
194
+ "lang": "en-US",
195
+ "desc": "Knowledgable, pro"
196
+ },
197
+ {
198
+ "id": "bIHbv24MWmeRgasZH58o",
199
+ "name": "Will",
200
+ "gender": "Male",
201
+ "lang": "en-US",
202
+ "desc": "Relaxed optimist"
203
+ },
204
+ {
205
+ "id": "cgSgspJ2msm6clMCkdW9",
206
+ "name": "Jessica",
207
+ "gender": "Female",
208
+ "lang": "en-US",
209
+ "desc": "Playful, bright"
210
+ },
211
+ {
212
+ "id": "cjVigY5qzO86Huf0OWal",
213
+ "name": "Eric",
214
+ "gender": "Male",
215
+ "lang": "en-US",
216
+ "desc": "Smooth, trustworthy"
217
+ },
218
+ {
219
+ "id": "hpp4J3VqNfWAUOO0d1Us",
220
+ "name": "Bella",
221
+ "gender": "Female",
222
+ "lang": "en-US",
223
+ "desc": "Professional, warm"
224
+ },
225
+ {
226
+ "id": "iP95p4xoKVk53GoZ742B",
227
+ "name": "Chris",
228
+ "gender": "Male",
229
+ "lang": "en-US",
230
+ "desc": "Charming, down-to-earth"
231
+ },
232
+ {
233
+ "id": "nPczCjzI2devNBz1zQrb",
234
+ "name": "Brian",
235
+ "gender": "Male",
236
+ "lang": "en-US",
237
+ "desc": "Deep, comforting"
238
+ },
239
+ {
240
+ "id": "onwK4e9ZLuTAKqWW03F9",
241
+ "name": "Daniel",
242
+ "gender": "Male",
243
+ "lang": "en-GB",
244
+ "desc": "Steady broadcaster"
245
+ },
246
+ {
247
+ "id": "pFZP5JQG7iQjIQuC4Bku",
248
+ "name": "Lily",
249
+ "gender": "Female",
250
+ "lang": "en-GB",
251
+ "desc": "Velvety actress"
252
+ },
253
+ {
254
+ "id": "pNInz6obpgDQGcFmaJgB",
255
+ "name": "Adam",
256
+ "gender": "Male",
257
+ "lang": "en-US",
258
+ "desc": "Dominant, firm"
259
+ },
260
+ {
261
+ "id": "pqHfZKP75CvOlQylNhV4",
262
+ "name": "Bill",
263
+ "gender": "Male",
264
+ "lang": "en-US",
265
+ "desc": "Wise, mature"
266
+ }
267
+ ]
268
+ },
269
+ {
270
+ "id": "macos",
271
+ "engineId": "macos",
272
+ "aliases": [
273
+ "macos-say",
274
+ "say"
275
+ ],
276
+ "displayName": "macOS Say",
277
+ "voiceModel": "discovered",
278
+ "defaultVoice": "Samantha",
279
+ "requires": "builtin",
280
+ "runtime": {
281
+ "unix": "play-tts-macos.sh",
282
+ "windows": null,
283
+ "darwinOnly": true
284
+ },
285
+ "voices": null
286
+ },
287
+ {
288
+ "id": "windows-sapi",
289
+ "engineId": "sapi",
290
+ "aliases": [
291
+ "sapi"
292
+ ],
293
+ "displayName": "Windows SAPI",
294
+ "voiceModel": "discovered",
295
+ "defaultVoice": "",
296
+ "requires": "builtin",
297
+ "runtime": {
298
+ "unix": null,
299
+ "windows": "play-tts-sapi.ps1",
300
+ "darwinOnly": false
301
+ },
302
+ "voices": null
303
+ },
304
+ {
305
+ "id": "windows-piper",
306
+ "engineId": "piper",
307
+ "aliases": [
308
+ "piper"
309
+ ],
310
+ "displayName": "Piper TTS",
311
+ "voiceModel": "discovered",
312
+ "defaultVoice": "en_US-lessac-medium",
313
+ "requires": "exe",
314
+ "runtime": {
315
+ "unix": null,
316
+ "windows": "play-tts-windows-piper.ps1",
317
+ "darwinOnly": false
318
+ },
319
+ "voices": null
320
+ }
321
+ ],
322
+ "platforms": {
323
+ "unix": [
324
+ "soprano",
325
+ "piper",
326
+ "kokoro",
327
+ "elevenlabs"
328
+ ],
329
+ "darwin": [
330
+ "soprano",
331
+ "piper",
332
+ "kokoro",
333
+ "elevenlabs",
334
+ "macos"
335
+ ],
336
+ "windows": [
337
+ "soprano",
338
+ "kokoro",
339
+ "windows-sapi",
340
+ "windows-piper"
341
+ ]
342
+ },
343
+ "displayNames": {
344
+ "soprano": "Soprano TTS",
345
+ "piper": "Piper TTS",
346
+ "kokoro": "Kokoro TTS",
347
+ "elevenlabs": "ElevenLabs",
348
+ "macos": "macOS Say",
349
+ "windows-sapi": "Windows SAPI",
350
+ "windows-piper": "Piper TTS"
351
+ }
352
+ }
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # provider-catalog.sh — GENERATED FILE. DO NOT EDIT.
4
+ # DO NOT EDIT — generated from src/services/provider-catalog.js
5
+ # Regenerate with: node scripts/generate-provider-catalog.mjs
6
+ # content-hash: sha256:5e717024f1250a8f54d07291256e532606dba7b4a15ab9589305d1af6bba7f35
7
+ #
8
+ # Bash-3.2-safe catalog accessors (macOS ships bash 3.2 — no associative
9
+ # arrays, no case-modifying parameter expansions). Source this file; do not execute it.
10
+
11
+ AGENTVIBES_CATALOG_VERSION="5e717024f1250a8f"
12
+
13
+ # Guard against double-sourcing.
14
+ [ -n "${_AGENTVIBES_PROVIDER_CATALOG_LOADED:-}" ] && return 0
15
+ _AGENTVIBES_PROVIDER_CATALOG_LOADED=1
16
+
17
+ # Lowercase a string without case-modifying expansions (unavailable in bash 3.2).
18
+ _catalog_lc() { printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]'; }
19
+
20
+ # Resolve an id-or-alias to its canonical provider id (echoes it; 1 if unknown).
21
+ catalog_canonical_provider() {
22
+ case "$(_catalog_lc "${1:-}")" in
23
+ soprano) printf '%s' 'soprano' ;;
24
+ piper) printf '%s' 'piper' ;;
25
+ kokoro) printf '%s' 'kokoro' ;;
26
+ elevenlabs) printf '%s' 'elevenlabs' ;;
27
+ macos|macos-say|say) printf '%s' 'macos' ;;
28
+ windows-sapi|sapi) printf '%s' 'windows-sapi' ;;
29
+ windows-piper) printf '%s' 'windows-piper' ;;
30
+ *) return 1 ;;
31
+ esac
32
+ }
33
+
34
+ # Echo the default voice for a provider (empty string is valid — system default).
35
+ catalog_default_voice() {
36
+ local _p; _p="$(catalog_canonical_provider "${1:-}")" || return 1
37
+ case "$_p" in
38
+ soprano) printf '%s' 'soprano-default' ;;
39
+ piper) printf '%s' 'en_US-lessac-medium' ;;
40
+ kokoro) printf '%s' 'af_heart' ;;
41
+ elevenlabs) printf '%s' 'Sarah' ;;
42
+ macos) printf '%s' 'Samantha' ;;
43
+ windows-sapi) printf '%s' '' ;;
44
+ windows-piper) printf '%s' 'en_US-lessac-medium' ;;
45
+ esac
46
+ }
47
+
48
+ # Echo a human display name for a provider.
49
+ catalog_display_name() {
50
+ local _p; _p="$(catalog_canonical_provider "${1:-}")" || { printf '%s' "${1:-}"; return 0; }
51
+ case "$_p" in
52
+ soprano) printf '%s' 'Soprano TTS' ;;
53
+ piper) printf '%s' 'Piper TTS' ;;
54
+ kokoro) printf '%s' 'Kokoro TTS' ;;
55
+ elevenlabs) printf '%s' 'ElevenLabs' ;;
56
+ macos) printf '%s' 'macOS Say' ;;
57
+ windows-sapi) printf '%s' 'Windows SAPI' ;;
58
+ windows-piper) printf '%s' 'Piper TTS' ;;
59
+ esac
60
+ }
61
+
62
+ # Print the provider ids playable on a platform (unix|darwin|windows), one per line.
63
+ catalog_providers_for_platform() {
64
+ case "$(_catalog_lc "${1:-}")" in
65
+ unix)
66
+ printf '%s\n' 'soprano' 'piper' 'kokoro' 'elevenlabs'
67
+ ;;
68
+ darwin)
69
+ printf '%s\n' 'soprano' 'piper' 'kokoro' 'elevenlabs' 'macos'
70
+ ;;
71
+ windows)
72
+ printf '%s\n' 'soprano' 'kokoro' 'windows-sapi' 'windows-piper'
73
+ ;;
74
+ *) return 1 ;;
75
+ esac
76
+ }
77
+
78
+ # Print a provider's listable voices (ids for kokoro, names for elevenlabs), one per line.
79
+ # Discovered providers (piper/macos/sapi) enumerate at runtime and print nothing here.
80
+ catalog_list_voices() {
81
+ local _p; _p="$(catalog_canonical_provider "${1:-}")" || return 1
82
+ case "$_p" in
83
+ soprano)
84
+ printf '%s\n' 'soprano-default'
85
+ ;;
86
+ piper)
87
+ : # (none — enumerated at runtime)
88
+ ;;
89
+ kokoro)
90
+ printf '%s\n' 'af_heart' 'af_alloy' 'af_aoede' 'af_bella' 'af_jessica' 'af_kore' 'af_nicole' 'af_nova' 'af_river' 'af_sarah' 'af_sky' 'am_adam' 'am_echo' 'am_eric' 'am_fenrir' 'am_liam' 'am_michael' 'am_onyx' 'am_puck' 'bf_alice' 'bf_emma' 'bf_isabella' 'bf_lily' 'bm_daniel' 'bm_fable' 'bm_george' 'bm_lewis' 'jf_alpha' 'jf_gongitsune' 'jf_nezumi' 'jf_tebukuro' 'jm_kumo' 'zf_xiaobei' 'zf_xiaoni' 'zf_xiaoxiao' 'zf_xiaoyi' 'zm_yunxi' 'zm_yunxia' 'zm_yunyang' 'ef_dora' 'em_alex' 'em_santa' 'ff_siwis' 'hf_alpha' 'hm_omega' 'if_sara' 'im_nicola' 'pf_dora' 'pm_alex' 'pm_santa' 'kf_alpha' 'km_hyunsu'
91
+ ;;
92
+ elevenlabs)
93
+ printf '%s\n' 'Sarah' 'Roger' 'Laura' 'Charlie' 'George' 'Callum' 'River' 'Harry' 'Liam' 'Alice' 'Matilda' 'Will' 'Jessica' 'Eric' 'Bella' 'Chris' 'Brian' 'Daniel' 'Lily' 'Adam' 'Bill'
94
+ ;;
95
+ macos)
96
+ : # (none — enumerated at runtime)
97
+ ;;
98
+ windows-sapi)
99
+ : # (none — enumerated at runtime)
100
+ ;;
101
+ windows-piper)
102
+ : # (none — enumerated at runtime)
103
+ ;;
104
+ esac
105
+ }
106
+
107
+ # Validate a voice for a provider. Echoes the canonical voice and returns 0 on
108
+ # success; returns 1 (no output) on rejection. Mirrors validateVoice() in the JS SSOT.
109
+ catalog_validate_voice() {
110
+ local _p _v _lc; _p="$(catalog_canonical_provider "${1:-}")" || return 1
111
+ _v="${2:-}"; _lc="$(_catalog_lc "$_v")"
112
+ case "$_p" in
113
+ kokoro)
114
+ case "$_lc" in
115
+ 'af_heart'|'af_alloy'|'af_aoede'|'af_bella'|'af_jessica'|'af_kore'|'af_nicole'|'af_nova'|'af_river'|'af_sarah'|'af_sky'|'am_adam'|'am_echo'|'am_eric'|'am_fenrir'|'am_liam'|'am_michael'|'am_onyx'|'am_puck'|'bf_alice'|'bf_emma'|'bf_isabella'|'bf_lily'|'bm_daniel'|'bm_fable'|'bm_george'|'bm_lewis'|'jf_alpha'|'jf_gongitsune'|'jf_nezumi'|'jf_tebukuro'|'jm_kumo'|'zf_xiaobei'|'zf_xiaoni'|'zf_xiaoxiao'|'zf_xiaoyi'|'zm_yunxi'|'zm_yunxia'|'zm_yunyang'|'ef_dora'|'em_alex'|'em_santa'|'ff_siwis'|'hf_alpha'|'hm_omega'|'if_sara'|'im_nicola'|'pf_dora'|'pm_alex'|'pm_santa'|'kf_alpha'|'km_hyunsu')
116
+ printf '%s' "$_lc"; return 0 ;;
117
+ esac
118
+ return 1 ;;
119
+ elevenlabs)
120
+ case "$_lc" in
121
+ 'sarah') printf '%s' 'EXAVITQu4vr4xnSDxMaL'; return 0 ;;
122
+ 'roger') printf '%s' 'CwhRBWXzGAHq8TQ4Fs17'; return 0 ;;
123
+ 'laura') printf '%s' 'FGY2WhTYpPnrIDTdsKH5'; return 0 ;;
124
+ 'charlie') printf '%s' 'IKne3meq5aSn9XLyUdCD'; return 0 ;;
125
+ 'george') printf '%s' 'JBFqnCBsd6RMkjVDRZzb'; return 0 ;;
126
+ 'callum') printf '%s' 'N2lVS1w4EtoT3dr4eOWO'; return 0 ;;
127
+ 'river') printf '%s' 'SAz9YHcvj6GT2YYXdXww'; return 0 ;;
128
+ 'harry') printf '%s' 'SOYHLrjzK2X1ezoPC6cr'; return 0 ;;
129
+ 'liam') printf '%s' 'TX3LPaxmHKxFdv7VOQHJ'; return 0 ;;
130
+ 'alice') printf '%s' 'Xb7hH8MSUJpSbSDYk0k2'; return 0 ;;
131
+ 'matilda') printf '%s' 'XrExE9yKIg1WjnnlVkGX'; return 0 ;;
132
+ 'will') printf '%s' 'bIHbv24MWmeRgasZH58o'; return 0 ;;
133
+ 'jessica') printf '%s' 'cgSgspJ2msm6clMCkdW9'; return 0 ;;
134
+ 'eric') printf '%s' 'cjVigY5qzO86Huf0OWal'; return 0 ;;
135
+ 'bella') printf '%s' 'hpp4J3VqNfWAUOO0d1Us'; return 0 ;;
136
+ 'chris') printf '%s' 'iP95p4xoKVk53GoZ742B'; return 0 ;;
137
+ 'brian') printf '%s' 'nPczCjzI2devNBz1zQrb'; return 0 ;;
138
+ 'daniel') printf '%s' 'onwK4e9ZLuTAKqWW03F9'; return 0 ;;
139
+ 'lily') printf '%s' 'pFZP5JQG7iQjIQuC4Bku'; return 0 ;;
140
+ 'adam') printf '%s' 'pNInz6obpgDQGcFmaJgB'; return 0 ;;
141
+ 'bill') printf '%s' 'pqHfZKP75CvOlQylNhV4'; return 0 ;;
142
+ esac
143
+ [[ "$_v" =~ ^[A-Za-z0-9]{20}$ ]] && { printf '%s' "$_v"; return 0; }
144
+ return 1 ;;
145
+ soprano)
146
+ case "$_lc" in
147
+ ''|soprano|soprano-default) printf '%s' 'soprano-default'; return 0 ;;
148
+ esac
149
+ return 1 ;;
150
+ piper|windows-piper)
151
+ if [[ "$_v" =~ ^[a-z]{2,3}_[A-Z]{2}- ]] || [[ "$_v" == *"::"* ]]; then
152
+ printf '%s' "$_v"; return 0
153
+ fi
154
+ return 1 ;;
155
+ macos|windows-sapi)
156
+ if [ -n "$_v" ]; then printf '%s' "$_v"; return 0; fi
157
+ [ "$_p" = windows-sapi ] && { printf '%s' ''; return 0; }
158
+ return 1 ;;
159
+ esac
160
+ return 1
161
+ }
@@ -44,6 +44,7 @@
44
44
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
45
45
  source "$SCRIPT_DIR/provider-manager.sh"
46
46
  source "$SCRIPT_DIR/language-manager.sh"
47
+ source "$SCRIPT_DIR/python-resolver.sh"
47
48
 
48
49
  COMMAND="${1:-help}"
49
50
 
@@ -101,7 +102,7 @@ provider_list() {
101
102
 
102
103
  # Check installation status of optional providers
103
104
  local kokoro_installed=false
104
- python3 -c "import importlib.util,sys; sys.exit(0 if importlib.util.find_spec('kokoro') else 1)" 2>/dev/null && kokoro_installed=true
105
+ [[ -n "$PYTHON_BIN" ]] && "$PYTHON_BIN" -c "import importlib.util,sys; sys.exit(0 if importlib.util.find_spec('kokoro') else 1)" 2>/dev/null && kokoro_installed=true
105
106
 
106
107
  local elevenlabs_key_set=false
107
108
  if [[ -n "${ELEVENLABS_API_KEY:-}" ]]; then
@@ -73,6 +73,23 @@ get_provider_config_path() {
73
73
  echo "$provider_file"
74
74
  }
75
75
 
76
+ # @function _load_provider_catalog
77
+ # @intent Source the generated Provider Catalog (bash-3.2-safe SSOT accessors)
78
+ # @why Single source of truth for per-provider defaults (AVI-S9.4) — mirrors the
79
+ # identical pattern in voice-manager.sh (AVI-S9.3). FAIL-SAFE: callers probe
80
+ # `type catalog_* >/dev/null 2>&1` and fall back to legacy hardcoded literals
81
+ # when the artifact is missing (installed-tree skew). Defaults never break.
82
+ # @returns None (sources catalog_* functions into the current shell when present)
83
+ # @sideeffects None (double-sourcing is guarded inside provider-catalog.sh)
84
+ _load_provider_catalog() {
85
+ local _script_dir
86
+ _script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
87
+ if [[ -f "$_script_dir/provider-catalog.sh" ]]; then
88
+ # shellcheck source=/dev/null
89
+ source "$_script_dir/provider-catalog.sh" 2>/dev/null || true
90
+ fi
91
+ }
92
+
76
93
  # @function get_active_provider
77
94
  # @intent Read currently active TTS provider from config file
78
95
  # @why Central function for determining which provider to use
@@ -187,12 +204,24 @@ migrate_voice_to_provider() {
187
204
  "en_US-danny-low:Alex"
188
205
  )
189
206
 
190
- # Default voices by provider
191
- local piper_default="en_US-lessac-medium"
192
- local macos_default="Samantha"
193
- local soprano_default="soprano-default" # Single voice — no selection needed
194
- local elevenlabs_default="Rachel"
195
- local kokoro_default="af_heart"
207
+ # Default voices by provider — sourced from the generated Provider Catalog SSOT
208
+ # (AVI-S9.4, catalog_default_voice). FAIL-SAFE: legacy literals if the catalog
209
+ # artifact is missing (installed-tree skew) — mirrors AVI-S9.3's pattern.
210
+ _load_provider_catalog
211
+ local piper_default macos_default soprano_default elevenlabs_default kokoro_default
212
+ if type catalog_default_voice >/dev/null 2>&1; then
213
+ piper_default="$(catalog_default_voice piper)"
214
+ macos_default="$(catalog_default_voice macos)"
215
+ soprano_default="$(catalog_default_voice soprano)" # Single voice — no selection needed
216
+ elevenlabs_default="$(catalog_default_voice elevenlabs)"
217
+ kokoro_default="$(catalog_default_voice kokoro)"
218
+ else
219
+ piper_default="en_US-lessac-medium"
220
+ macos_default="Samantha"
221
+ soprano_default="soprano-default" # Single voice — no selection needed
222
+ elevenlabs_default="Sarah"
223
+ kokoro_default="af_heart"
224
+ fi
196
225
 
197
226
  # Single-voice providers: migration is straightforward
198
227
  case "$target_provider" in
@@ -214,7 +243,15 @@ migrate_voice_to_provider() {
214
243
  return 0
215
244
  fi
216
245
 
217
- # If migrating FROM a single-voice provider, return default for target provider
246
+ # If migrating FROM a single-voice/fixed-shape provider, return default for target.
247
+ # NOTE ("Rachel" sentinel, AVI-S9.4): "Rachel" was ElevenLabs' hardcoded default
248
+ # before the 21-voice catalog trim (AVI-S8.1) — it is NOT a name-to-id match in
249
+ # today's catalog. This is READ-SIDE TOLERANCE ONLY (CLAUDE.md non-destructive
250
+ # rule): a user config that still says "Rachel" degrades gracefully to the
251
+ # target provider's default instead of erroring; we never rewrite the user's file.
252
+ if [[ "$current_voice" == "Rachel" ]]; then
253
+ echo "⚠️ '$current_voice' is a legacy ElevenLabs default no longer in the voice catalog; migrating to the $target_provider default." >&2
254
+ fi
218
255
  if [[ "$current_voice" == "soprano-default" || "$current_voice" == "Rachel" || "$current_voice" =~ ^[a-z]{2}_[a-z0-9_]+$ ]]; then
219
256
  case "$target_provider" in
220
257
  piper) echo "$piper_default" ;;
@@ -372,6 +409,7 @@ get_provider_script_path() {
372
409
  # @returns Echoes LLM name (e.g. "claude-code") or empty string if no remote routing configured
373
410
  # @exitcode 0=always
374
411
  detect_routing_llm() {
412
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/python-resolver.sh"
375
413
  # Priority 1: AGENTVIBES_LLM_KEY env var (set during active TTS sessions)
376
414
  if [[ -n "${AGENTVIBES_LLM_KEY:-}" ]] && [[ "$AGENTVIBES_LLM_KEY" =~ ^llm:([a-zA-Z0-9][a-zA-Z0-9_-]*)$ ]]; then
377
415
  echo "${BASH_REMATCH[1]}"
@@ -380,9 +418,9 @@ detect_routing_llm() {
380
418
 
381
419
  # Priority 2: First mode=remote entry in transport-config.json
382
420
  local _transport_cfg="$HOME/.agentvibes/transport-config.json"
383
- if [[ -f "$_transport_cfg" ]] && command -v python3 &>/dev/null; then
421
+ if [[ -f "$_transport_cfg" ]] && [[ -n "$PYTHON_BIN" ]]; then
384
422
  local _remote_llm
385
- _remote_llm=$(AGENTVIBES_CFG="$_transport_cfg" python3 - <<'PYEOF'
423
+ _remote_llm=$(AGENTVIBES_CFG="$_transport_cfg" "$PYTHON_BIN" - <<'PYEOF'
386
424
  import json, os, sys
387
425
  try:
388
426
  d = json.load(open(os.environ['AGENTVIBES_CFG'], encoding='utf-8'))