agentvibes 5.12.0 → 5.13.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.
Files changed (72) hide show
  1. package/.claude/commands/agent-vibes/commands.json +0 -20
  2. package/.claude/commands/agent-vibes/unmute.md +6 -2
  3. package/.claude/config/audio-effects.cfg +6 -6
  4. package/.claude/github-star-reminder.txt +1 -1
  5. package/.claude/hooks/agentvibes-session-id.sh +69 -0
  6. package/.claude/hooks/bmad-party-speak.sh +20 -4
  7. package/.claude/hooks/bmad-speak.sh +60 -2
  8. package/.claude/hooks/bmad-tts-injector.sh +20 -1
  9. package/.claude/hooks/bmad-voice-manager.sh +25 -3
  10. package/.claude/hooks/clawdbot-receiver-SECURE.sh +21 -2
  11. package/.claude/hooks/clawdbot-receiver.sh +19 -1
  12. package/.claude/hooks/elevenlabs-voices.sh +62 -0
  13. package/.claude/hooks/kokoro-installer.sh +20 -10
  14. package/.claude/hooks/language-manager.sh +10 -3
  15. package/.claude/hooks/party-set-room.sh +71 -0
  16. package/.claude/hooks/party-stage-roster.py +328 -0
  17. package/.claude/hooks/personality-manager.sh +19 -2
  18. package/.claude/hooks/piper-voice-manager.sh +3 -2
  19. package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +24 -5
  20. package/.claude/hooks/play-tts-elevenlabs.sh +38 -118
  21. package/.claude/hooks/play-tts-kokoro.sh +33 -6
  22. package/.claude/hooks/play-tts-soprano.sh +3 -2
  23. package/.claude/hooks/play-tts-ssh-remote.sh +37 -29
  24. package/.claude/hooks/play-tts-termux-ssh.sh +5 -4
  25. package/.claude/hooks/play-tts.sh +66 -61
  26. package/.claude/hooks/provider-catalog.json +352 -0
  27. package/.claude/hooks/provider-catalog.sh +161 -0
  28. package/.claude/hooks/provider-commands.sh +2 -1
  29. package/.claude/hooks/provider-manager.sh +47 -9
  30. package/.claude/hooks/python-resolver.sh +117 -0
  31. package/.claude/hooks/session-id.sh +56 -0
  32. package/.claude/hooks/session-start-tts.sh +39 -0
  33. package/.claude/hooks/speed-manager.sh +1 -1
  34. package/.claude/hooks/translate-manager.sh +3 -2
  35. package/.claude/hooks/translator.py +1 -1
  36. package/.claude/hooks/voice-manager.sh +242 -10
  37. package/.claude/hooks-windows/language-manager.ps1 +7 -1
  38. package/.claude/hooks-windows/personality-manager.ps1 +16 -1
  39. package/.claude/hooks-windows/play-tts-kokoro.ps1 +20 -4
  40. package/.claude/hooks-windows/play-tts.ps1 +32 -3
  41. package/.claude/hooks-windows/provider-catalog.ps1 +140 -0
  42. package/.claude/hooks-windows/provider-manager.ps1 +63 -8
  43. package/.claude/hooks-windows/tts-watcher.ps1 +33 -12
  44. package/.claude/hooks-windows/voice-manager-windows.ps1 +49 -0
  45. package/.mcp.json +0 -7
  46. package/README.md +12 -3
  47. package/RELEASE_NOTES.md +43 -0
  48. package/mcp-server/server.py +146 -49
  49. package/mcp-server/test_mcp_correctness.py +20 -2
  50. package/mcp-server/test_windows_script_parity.py +0 -2
  51. package/package.json +1 -1
  52. package/src/cli/list-voices.js +218 -114
  53. package/src/console/bling.js +71 -0
  54. package/src/console/music-preview.js +79 -0
  55. package/src/console/tabs/music-tab.js +16 -39
  56. package/src/console/tabs/settings-tab.js +195 -13
  57. package/src/console/tabs/setup-tab.js +9 -34
  58. package/src/console/tabs/voices-tab.js +83 -14
  59. package/src/console/widgets/track-picker.js +82 -0
  60. package/src/installer.js +124 -10
  61. package/src/services/provider-catalog.js +412 -0
  62. package/src/services/provider-voice-catalog.js +52 -73
  63. package/src/services/tts-engine-service.js +29 -0
  64. package/src/utils/provider-validator.js +62 -12
  65. package/.claude/commands/agent-vibes/language.md +0 -23
  66. package/.claude/commands/agent-vibes/learn.md +0 -67
  67. package/.claude/commands/agent-vibes/replay-target.md +0 -14
  68. package/.claude/commands/agent-vibes/target-voice.md +0 -26
  69. package/.claude/commands/agent-vibes/target.md +0 -30
  70. package/.claude/hooks/learn-manager.sh +0 -492
  71. package/.claude/hooks/replay-target-audio.sh +0 -95
  72. package/.claude/hooks-windows/learn-manager.ps1 +0 -241
@@ -31,13 +31,13 @@
31
31
  #
32
32
  # ---
33
33
  #
34
- # @fileoverview TTS Provider Router with Translation and Language Learning Support
34
+ # @fileoverview TTS Provider Router with Translation Support
35
35
  # @context Routes TTS requests to active provider (Piper or macOS) with optional translation
36
- # @architecture Provider abstraction layer - single entry point for all TTS, handles translation and learning mode
37
- # @dependencies provider-manager.sh, play-tts-piper.sh, translator.py, translate-manager.sh, learn-manager.sh
36
+ # @architecture Provider abstraction layer - single entry point for all TTS, handles translation
37
+ # @dependencies provider-manager.sh, play-tts-piper.sh, translator.py, translate-manager.sh
38
38
  # @entrypoints Called by hooks, slash commands, personality-manager.sh, and all TTS features
39
39
  # @patterns Provider pattern - delegates to provider-specific implementations, auto-detects provider from voice name
40
- # @related provider-manager.sh, play-tts-piper.sh, learn-manager.sh, translate-manager.sh
40
+ # @related provider-manager.sh, play-tts-piper.sh, translate-manager.sh
41
41
  #
42
42
  # **IMPORTANT: This script should be called inline (NOT in background) in Bash tool**
43
43
  # Wait for TTS playback to complete before continuing.
@@ -113,6 +113,12 @@ elif [[ -f "$GLOBAL_MUTE_FILE" ]]; then
113
113
  exit 0
114
114
  fi
115
115
 
116
+ # Resolve a working Python interpreter once (translator and transport config
117
+ # both need it). Sourced AFTER the mute exits so muted calls
118
+ # pay nothing. Windows git-bash frequently has no python3 on PATH — see
119
+ # python-resolver.sh; $PYTHON_BIN is empty when none is usable.
120
+ source "$SCRIPT_DIR/python-resolver.sh"
121
+
116
122
  # Parse named flags (e.g. --llm) before positional arguments.
117
123
  # This allows callers to pass: play-tts.sh --llm claude-code "text to speak"
118
124
  # Named args are extracted; remaining positional args are shifted into $1/$2/$3.
@@ -241,6 +247,53 @@ if [[ -z "$_PRETEXT" ]]; then
241
247
  fi
242
248
  done
243
249
  fi
250
+ # Dynamic session self-ID: a per-LLM PRETEXT containing the {{session}} token
251
+ # expands to "<LLM> on <Project> in <Terminal>" so multi-session users can tell
252
+ # which tab just spoke. Announced once per session (best-effort — see the key
253
+ # note below); on later utterances the token is dropped so it doesn't prefix
254
+ # every line. Opt-in — only fires when the pretext actually contains the token.
255
+ if [[ "$_PRETEXT" == *"{{session}}"* ]]; then
256
+ # Session key. There is no true Claude-session id available to a plain Bash
257
+ # command, so this is best-effort: a terminal-session id if the emulator
258
+ # exports one (stable for the tab's life), else the parent PID. Worst case the
259
+ # self-ID repeats or is skipped — cosmetic, and only when a pretext opts in.
260
+ _SID_RAW="${WT_SESSION:-${TERM_SESSION_ID:-${TMUX:-$PPID}}}"
261
+ _SID_KEY="$(printf '%s' "${LLM_PROVIDER:-}|$(basename "${CLAUDE_PROJECT_DIR:-$PROJECT_ROOT}")|$_SID_RAW" | tr -c 'A-Za-z0-9' '_')"
262
+ # Announce-marker dir: an OWNED subdir, per the project's secure-temp rule.
263
+ # If the dir exists but isn't ours (pre-created symlink attack on a shared
264
+ # host), skip the marker entirely and just strip the token — never chmod or
265
+ # write through someone else's path.
266
+ _ANN_BASE="${XDG_RUNTIME_DIR:-/tmp/agentvibes-$(id -u 2>/dev/null || echo 0)}"
267
+ _ANN_DIR="$_ANN_BASE/agentvibes-session"
268
+ _ANN_OK=0
269
+ if mkdir -p "$_ANN_DIR" 2>/dev/null && chmod 700 "$_ANN_DIR" 2>/dev/null; then
270
+ _ANN_OWNER="$(stat -c '%u' "$_ANN_DIR" 2>/dev/null || stat -f '%u' "$_ANN_DIR" 2>/dev/null || echo '')"
271
+ [[ -z "$_ANN_OWNER" || "$_ANN_OWNER" == "$(id -u 2>/dev/null || echo 0)" ]] && _ANN_OK=1
272
+ fi
273
+ _ANN_FILE="$_ANN_DIR/announced-$_SID_KEY"
274
+ # First utterance → replacement is the self-ID; later utterances → empty (drop).
275
+ if [[ "$_ANN_OK" == "1" && -f "$_ANN_FILE" ]]; then
276
+ _SESSION_ID=""
277
+ else
278
+ _SESSION_ID="$(bash "$SCRIPT_DIR/agentvibes-session-id.sh" "${LLM_PROVIDER:-claude-code}" "${CLAUDE_PROJECT_DIR:-$PROJECT_ROOT}" 2>/dev/null || true)"
279
+ if [[ "$_ANN_OK" == "1" ]]; then
280
+ touch "$_ANN_FILE" 2>/dev/null || true
281
+ # Bound growth: drop announce-markers older than a day (find is optional).
282
+ find "$_ANN_DIR" -maxdepth 1 -name 'announced-*' -mtime +1 -delete 2>/dev/null || true
283
+ fi
284
+ fi
285
+ if [[ -n "$_SESSION_ID" ]]; then
286
+ _PRETEXT="${_PRETEXT//"{{session}}"/$_SESSION_ID}"
287
+ else
288
+ # Empty replacement: also swallow one adjacent ", " so "Hey {{session}}, ready"
289
+ # becomes "Hey ready", not "Hey , ready"; then drop any bare token.
290
+ _PRETEXT="${_PRETEXT//"{{session}}, "/}"
291
+ _PRETEXT="${_PRETEXT//", {{session}}"/}"
292
+ _PRETEXT="${_PRETEXT//"{{session}}"/}"
293
+ fi
294
+ # Final tidy: collapse doubled/edge commas and runs of whitespace.
295
+ _PRETEXT="$(printf '%s' "$_PRETEXT" | sed -E 's/,[[:space:]]*,/,/g; s/^[[:space:]]*,[[:space:]]*//; s/[[:space:]]*,[[:space:]]*$//; s/[[:space:]]{2,}/ /g; s/^[[:space:]]+//; s/[[:space:]]+$//')"
296
+ fi
244
297
  if [[ -n "$_PRETEXT" ]]; then
245
298
  TEXT="${_PRETEXT}, ${TEXT}"
246
299
  fi
@@ -332,7 +385,7 @@ case "$ACTIVE_PROVIDER" in
332
385
  if [[ "$VOICE_OVERRIDE" =~ ^(af|am|bf|bm|jf|jm|kf|km|zf|zm|ff|fm|hf|hm|if|im|pf|pm|ef|em|nf|nm)_[a-zA-Z0-9]+$ ]]; then
333
386
  # Kokoro-shaped voice override (e.g. af_heart) must win over the LLM row's
334
387
  # engine column, else it is synthesized as a Piper voice, 404s downloading
335
- # a model that doesn't exist, and stays silent. (Grafted from master WIP.)
388
+ # a model that doesn't exist, and stays silent.
336
389
  ACTIVE_PROVIDER="kokoro"
337
390
  elif [[ -n "$_LLM_ENGINE" ]]; then
338
391
  ACTIVE_PROVIDER="$_LLM_ENGINE"
@@ -356,8 +409,8 @@ fi
356
409
  _TRANSPORT_CFG="$HOME/.agentvibes/transport-config.json"
357
410
  if [[ "$ACTIVE_PROVIDER" != "ssh-remote" && "$ACTIVE_PROVIDER" != "agentvibes-receiver" && "$ACTIVE_PROVIDER" != "termux-ssh" ]] \
358
411
  && [[ -n "$LLM_PROVIDER" && "$LLM_PROVIDER" != "default" ]] \
359
- && [[ -f "$_TRANSPORT_CFG" ]] && command -v python3 &>/dev/null; then
360
- _LLM_SSH_MODE=$(AGENTVIBES_CFG="$_TRANSPORT_CFG" AGENTVIBES_KEY="$LLM_PROVIDER" python3 - <<'PYEOF'
412
+ && [[ -f "$_TRANSPORT_CFG" ]] && [[ -n "$PYTHON_BIN" ]]; then
413
+ _LLM_SSH_MODE=$(AGENTVIBES_CFG="$_TRANSPORT_CFG" AGENTVIBES_KEY="$LLM_PROVIDER" "$PYTHON_BIN" - <<'PYEOF'
361
414
  import json, os, sys
362
415
  try:
363
416
  d = json.load(open(os.environ['AGENTVIBES_CFG'], encoding='utf-8'))
@@ -368,7 +421,7 @@ PYEOF
368
421
  )
369
422
  if [[ "$_LLM_SSH_MODE" == "remote" ]]; then
370
423
  # Redirect this LLM's audio through ssh-remote using its own SSH config
371
- _llm_remote_data=$(AGENTVIBES_CFG="$_TRANSPORT_CFG" AGENTVIBES_KEY="$LLM_PROVIDER" python3 - <<'PYEOF'
424
+ _llm_remote_data=$(AGENTVIBES_CFG="$_TRANSPORT_CFG" AGENTVIBES_KEY="$LLM_PROVIDER" "$PYTHON_BIN" - <<'PYEOF'
372
425
  import json, os, sys
373
426
  try:
374
427
  d = json.load(open(os.environ['AGENTVIBES_CFG'], encoding='utf-8'))
@@ -536,50 +589,8 @@ speak_text() {
536
589
  esac
537
590
  }
538
591
 
539
- # Note: learn-manager.sh and translate-manager.sh are sourced inside their
540
- # respective handler functions to avoid triggering their main handlers
541
-
542
- # @function handle_learning_mode
543
- # @intent Speak in both main language and target language for learning
544
- # @why Issue #51 - Auto-translate and speak twice for immersive language learning
545
- # @returns 0 if learning mode handled, 1 if not in learning mode
546
- handle_learning_mode() {
547
- # Source learn-manager for learning mode functions
548
- source "$SCRIPT_DIR/learn-manager.sh" 2>/dev/null || return 1
549
-
550
- # Check if learning mode is enabled
551
- if ! is_learn_mode_enabled 2>/dev/null; then
552
- return 1
553
- fi
554
-
555
- local target_lang
556
- target_lang=$(get_target_language 2>/dev/null || echo "")
557
- local target_voice
558
- target_voice=$(get_target_voice 2>/dev/null || echo "")
559
-
560
- # Need both target language and voice for learning mode
561
- if [[ -z "$target_lang" ]] || [[ -z "$target_voice" ]]; then
562
- return 1
563
- fi
564
-
565
- # 1. Speak in main language (current voice)
566
- speak_text "$TEXT" "$VOICE_OVERRIDE" "$ACTIVE_PROVIDER"
567
-
568
- # 2. Auto-translate to target language
569
- local translated
570
- # SECURITY: Add timeout to prevent hanging (#134)
571
- translated=$(timeout 5 python3 "$SCRIPT_DIR/translator.py" "$TEXT" "$target_lang" 2>/dev/null) || translated="$TEXT"
572
-
573
- # Small pause between languages
574
- sleep 0.5
575
-
576
- # 3. Speak translated text with target voice
577
- local target_provider
578
- target_provider=$(detect_voice_provider "$target_voice")
579
- speak_text "$translated" "$target_voice" "$target_provider"
580
-
581
- return 0
582
- }
592
+ # Note: translate-manager.sh is sourced inside its handler function to avoid
593
+ # triggering its main handler
583
594
 
584
595
  # @function handle_translation_mode
585
596
  # @intent Translate and speak in target language (non-learning mode)
@@ -604,7 +615,7 @@ handle_translation_mode() {
604
615
  # Translate text
605
616
  local translated
606
617
  # SECURITY: Add timeout to prevent hanging (#134)
607
- translated=$(timeout 5 python3 "$SCRIPT_DIR/translator.py" "$TEXT" "$translate_to" 2>/dev/null) || translated="$TEXT"
618
+ translated=$(timeout 5 "${PYTHON_BIN:-python3}" "$SCRIPT_DIR/translator.py" "$TEXT" "$translate_to" 2>/dev/null) || translated="$TEXT"
608
619
 
609
620
  # Get voice for target language if no override specified
610
621
  local voice_to_use="$VOICE_OVERRIDE"
@@ -625,14 +636,8 @@ handle_translation_mode() {
625
636
  }
626
637
 
627
638
  # Mode priority:
628
- # 1. Learning mode (speaks twice: main + translated)
629
- # 2. Translation mode (speaks translated only)
630
- # 3. Normal mode (speaks as-is)
631
-
632
- # Try learning mode first (Issue #51)
633
- if handle_learning_mode; then
634
- exit 0
635
- fi
639
+ # 1. Translation mode (speaks translated only)
640
+ # 2. Normal mode (speaks as-is)
636
641
 
637
642
  # Try translation mode (Issue #50)
638
643
  if handle_translation_mode; then
@@ -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
+ }