agentvibes 2.0.20 → 2.0.22

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 (233) hide show
  1. package/.claude/agents/health-coach.md +154 -0
  2. package/.claude/agents/motivator.md +171 -0
  3. package/.claude/agents/negotiator.md +97 -0
  4. package/.claude/commands/agent-vibes/agent-health-coach.md +15 -0
  5. package/.claude/commands/agent-vibes/agent-motivator.md +15 -0
  6. package/.claude/commands/agent-vibes/agent-negotiator.md +15 -0
  7. package/.claude/commands/agent-vibes/agent.md +79 -0
  8. package/.claude/commands/agent-vibes/commands.json +4 -0
  9. package/.claude/commands/agent-vibes/set-favorite-voice.md +84 -0
  10. package/.claude/github-star-reminder.txt +1 -1
  11. package/.claude/hooks/download-extra-voices.sh +244 -0
  12. package/.claude/hooks/personality-manager.sh +81 -0
  13. package/.claude/hooks/piper-download-voices.sh +3 -93
  14. package/.claude/hooks/piper-multispeaker-registry.sh +165 -0
  15. package/.claude/hooks/play-tts-elevenlabs.sh +5 -3
  16. package/.claude/hooks/play-tts-piper.sh +27 -44
  17. package/.claude/hooks/provider-manager.sh +24 -5
  18. package/.claude/hooks/speed-manager.sh +10 -12
  19. package/.claude/hooks/voice-manager.sh +94 -56
  20. package/README.md +0 -3
  21. package/RELEASE_NOTES.md +40 -247
  22. package/docs/agents.md +485 -0
  23. package/docs/commands.md +1 -21
  24. package/docs/extra-voices-implementation-summary.md +235 -0
  25. package/docs/voice-registration-fix.md +254 -0
  26. package/fix-vscode-colors.sh +88 -0
  27. package/fixcolors +88 -0
  28. package/generate-all-agent-voices.sh +174 -0
  29. package/generate-missing-elevenlabs-complete.sh +236 -0
  30. package/generate-missing-elevenlabs.sh +110 -0
  31. package/generate-new-voices.sh +108 -0
  32. package/generate-piper-agent-intros.sh +85 -0
  33. package/generate-provider-and-agent-intros.sh +136 -0
  34. package/logo/fav_icon_128x128.png +0 -0
  35. package/logo/fav_icon_128x128.png:Zone.Identifier +4 -0
  36. package/logo/logo1.webp +0 -0
  37. package/logo/logo1.webp:Zone.Identifier +4 -0
  38. package/logo/social.png +0 -0
  39. package/logo/social.png:Zone.Identifier +4 -0
  40. package/mcp-server/agentvibes.db +0 -0
  41. package/mcp-server/server.py +47 -14
  42. package/package.json +3 -3
  43. package/regenerate-agent-voices.sh +79 -0
  44. package/test/unit/provider-manager.bats +8 -5
  45. package/test/unit/speed-manager.bats +4 -4
  46. package/agentvibes.org/.claude/audio/tts-padded-1760744118.mp3 +0 -0
  47. package/agentvibes.org/.claude/audio/tts-padded-1760748535.mp3 +0 -0
  48. package/agentvibes.org/.claude/audio/tts-padded-1760748676.mp3 +0 -0
  49. package/agentvibes.org/.claude/audio/tts-padded-1760750748.mp3 +0 -0
  50. package/agentvibes.org/.claude/audio/tts-padded-1760750947.mp3 +0 -0
  51. package/agentvibes.org/.claude/audio/tts-padded-1760752718.mp3 +0 -0
  52. package/agentvibes.org/.claude/audio/tts-padded-1760752907.mp3 +0 -0
  53. package/agentvibes.org/.claude/audio/tts-padded-1760753017.mp3 +0 -0
  54. package/agentvibes.org/.claude/audio/tts-padded-1760753045.mp3 +0 -0
  55. package/agentvibes.org/.claude/audio/tts-padded-1760753241.mp3 +0 -0
  56. package/agentvibes.org/.claude/audio/tts-padded-1760753315.mp3 +0 -0
  57. package/agentvibes.org/.claude/audio/tts-padded-1760753382.mp3 +0 -0
  58. package/agentvibes.org/.claude/audio/tts-padded-1760753408.mp3 +0 -0
  59. package/agentvibes.org/.claude/audio/tts-padded-1760753426.mp3 +0 -0
  60. package/agentvibes.org/.claude/audio/tts-padded-1760753446.mp3 +0 -0
  61. package/agentvibes.org/.claude/audio/tts-padded-1760753541.mp3 +0 -0
  62. package/agentvibes.org/.claude/audio/tts-padded-1760753553.mp3 +0 -0
  63. package/agentvibes.org/.claude/audio/tts-padded-1760753577.mp3 +0 -0
  64. package/agentvibes.org/.claude/commands/agent-vibes/add.md +0 -21
  65. package/agentvibes.org/.claude/commands/agent-vibes/agent-vibes.md +0 -68
  66. package/agentvibes.org/.claude/commands/agent-vibes/bmad.md +0 -196
  67. package/agentvibes.org/.claude/commands/agent-vibes/commands.json +0 -77
  68. package/agentvibes.org/.claude/commands/agent-vibes/get.md +0 -9
  69. package/agentvibes.org/.claude/commands/agent-vibes/language.md +0 -23
  70. package/agentvibes.org/.claude/commands/agent-vibes/learn.md +0 -67
  71. package/agentvibes.org/.claude/commands/agent-vibes/list.md +0 -13
  72. package/agentvibes.org/.claude/commands/agent-vibes/personality.md +0 -79
  73. package/agentvibes.org/.claude/commands/agent-vibes/preview.md +0 -17
  74. package/agentvibes.org/.claude/commands/agent-vibes/provider.md +0 -54
  75. package/agentvibes.org/.claude/commands/agent-vibes/replay-target.md +0 -14
  76. package/agentvibes.org/.claude/commands/agent-vibes/replay.md +0 -19
  77. package/agentvibes.org/.claude/commands/agent-vibes/sample.md +0 -12
  78. package/agentvibes.org/.claude/commands/agent-vibes/sentiment.md +0 -52
  79. package/agentvibes.org/.claude/commands/agent-vibes/set-language.md +0 -47
  80. package/agentvibes.org/.claude/commands/agent-vibes/set-pretext.md +0 -65
  81. package/agentvibes.org/.claude/commands/agent-vibes/set-speed.md +0 -41
  82. package/agentvibes.org/.claude/commands/agent-vibes/switch.md +0 -53
  83. package/agentvibes.org/.claude/commands/agent-vibes/target-voice.md +0 -26
  84. package/agentvibes.org/.claude/commands/agent-vibes/target.md +0 -30
  85. package/agentvibes.org/.claude/commands/agent-vibes/update.md +0 -20
  86. package/agentvibes.org/.claude/commands/agent-vibes/version.md +0 -10
  87. package/agentvibes.org/.claude/commands/agent-vibes/whoami.md +0 -7
  88. package/agentvibes.org/.claude/hooks/bmad-tts-injector.sh +0 -386
  89. package/agentvibes.org/.claude/hooks/bmad-voice-manager.sh +0 -375
  90. package/agentvibes.org/.claude/hooks/check-output-style.sh +0 -60
  91. package/agentvibes.org/.claude/hooks/github-star-reminder.sh +0 -94
  92. package/agentvibes.org/.claude/hooks/language-manager.sh +0 -360
  93. package/agentvibes.org/.claude/hooks/learn-manager.sh +0 -443
  94. package/agentvibes.org/.claude/hooks/personality-manager.sh +0 -324
  95. package/agentvibes.org/.claude/hooks/piper-download-voices.sh +0 -133
  96. package/agentvibes.org/.claude/hooks/piper-installer.sh +0 -144
  97. package/agentvibes.org/.claude/hooks/piper-voice-manager.sh +0 -227
  98. package/agentvibes.org/.claude/hooks/play-tts-elevenlabs.sh +0 -376
  99. package/agentvibes.org/.claude/hooks/play-tts-piper.sh +0 -281
  100. package/agentvibes.org/.claude/hooks/play-tts.sh +0 -69
  101. package/agentvibes.org/.claude/hooks/provider-commands.sh +0 -505
  102. package/agentvibes.org/.claude/hooks/provider-manager.sh +0 -248
  103. package/agentvibes.org/.claude/hooks/replay-target-audio.sh +0 -64
  104. package/agentvibes.org/.claude/hooks/sentiment-manager.sh +0 -163
  105. package/agentvibes.org/.claude/hooks/speed-manager.sh +0 -259
  106. package/agentvibes.org/.claude/hooks/voice-manager.sh +0 -477
  107. package/agentvibes.org/.claude/hooks/voices-config.sh +0 -33
  108. package/agentvibes.org/.claude/journal/2025-10-07.html +0 -373
  109. package/agentvibes.org/.claude/journal/index.html +0 -91
  110. package/agentvibes.org/.claude/output-styles/agent-vibes.md +0 -203
  111. package/agentvibes.org/.claude/personalities/angry.md +0 -17
  112. package/agentvibes.org/.claude/personalities/annoying.md +0 -17
  113. package/agentvibes.org/.claude/personalities/crass.md +0 -17
  114. package/agentvibes.org/.claude/personalities/dramatic.md +0 -17
  115. package/agentvibes.org/.claude/personalities/dry-humor.md +0 -53
  116. package/agentvibes.org/.claude/personalities/flirty.md +0 -23
  117. package/agentvibes.org/.claude/personalities/funny.md +0 -17
  118. package/agentvibes.org/.claude/personalities/grandpa.md +0 -35
  119. package/agentvibes.org/.claude/personalities/millennial.md +0 -17
  120. package/agentvibes.org/.claude/personalities/moody.md +0 -17
  121. package/agentvibes.org/.claude/personalities/normal.md +0 -19
  122. package/agentvibes.org/.claude/personalities/pirate.md +0 -17
  123. package/agentvibes.org/.claude/personalities/poetic.md +0 -17
  124. package/agentvibes.org/.claude/personalities/professional.md +0 -17
  125. package/agentvibes.org/.claude/personalities/robot.md +0 -17
  126. package/agentvibes.org/.claude/personalities/sarcastic.md +0 -41
  127. package/agentvibes.org/.claude/personalities/sassy.md +0 -17
  128. package/agentvibes.org/.claude/personalities/surfer-dude.md +0 -17
  129. package/agentvibes.org/.claude/personalities/zen.md +0 -17
  130. package/agentvibes.org/.claude/piper-voices-dir.txt +0 -1
  131. package/agentvibes.org/.claude/plugins/bmad-voices.md +0 -42
  132. package/agentvibes.org/.claude/tts-provider.txt +0 -1
  133. package/agentvibes.org/.mcp-minimal.json +0 -60
  134. package/agentvibes.org/CHANGELOG.md +0 -56
  135. package/agentvibes.org/README.md +0 -93
  136. package/agentvibes.org/app/(auth)/layout.tsx +0 -15
  137. package/agentvibes.org/app/(auth)/reset-password/page.tsx +0 -45
  138. package/agentvibes.org/app/(auth)/signin/page.tsx +0 -82
  139. package/agentvibes.org/app/(auth)/signup/page.tsx +0 -104
  140. package/agentvibes.org/app/(default)/blog/[slug]/page.tsx +0 -128
  141. package/agentvibes.org/app/(default)/blog/page.tsx +0 -95
  142. package/agentvibes.org/app/(default)/layout.tsx +0 -31
  143. package/agentvibes.org/app/(default)/page.tsx +0 -20
  144. package/agentvibes.org/app/api/hello/route.ts +0 -3
  145. package/agentvibes.org/app/css/additional-styles/theme.css +0 -82
  146. package/agentvibes.org/app/css/additional-styles/utility-patterns.css +0 -55
  147. package/agentvibes.org/app/css/style.css +0 -100
  148. package/agentvibes.org/app/layout.tsx +0 -63
  149. package/agentvibes.org/components/code-block.tsx +0 -27
  150. package/agentvibes.org/components/cta.tsx +0 -58
  151. package/agentvibes.org/components/features.tsx +0 -256
  152. package/agentvibes.org/components/hero-home.tsx +0 -133
  153. package/agentvibes.org/components/mdx-components.tsx +0 -128
  154. package/agentvibes.org/components/modal-video.tsx +0 -137
  155. package/agentvibes.org/components/page-illustration.tsx +0 -55
  156. package/agentvibes.org/components/spotlight.tsx +0 -77
  157. package/agentvibes.org/components/testimonials.tsx +0 -282
  158. package/agentvibes.org/components/ui/footer.tsx +0 -82
  159. package/agentvibes.org/components/ui/header.tsx +0 -68
  160. package/agentvibes.org/components/ui/logo.tsx +0 -10
  161. package/agentvibes.org/components/workflows.tsx +0 -176
  162. package/agentvibes.org/content/blog/discovering-new-piper-voices.mdx +0 -253
  163. package/agentvibes.org/content/blog/getting-started-agentvibes.mdx +0 -228
  164. package/agentvibes.org/content/blog/introducing-agentvibes-v2.mdx +0 -250
  165. package/agentvibes.org/content/blog/language-learning-with-agentvibes.mdx +0 -142
  166. package/agentvibes.org/content/blog/voice-personalities-guide.mdx +0 -119
  167. package/agentvibes.org/lib/blog.ts +0 -73
  168. package/agentvibes.org/next.config.js +0 -6
  169. package/agentvibes.org/package-lock.json +0 -4285
  170. package/agentvibes.org/package.json +0 -40
  171. package/agentvibes.org/pnpm-lock.yaml +0 -1141
  172. package/agentvibes.org/postcss.config.js +0 -5
  173. package/agentvibes.org/public/audio/02-sarcastic.mp3 +0 -0
  174. package/agentvibes.org/public/audio/03-angry.mp3 +0 -0
  175. package/agentvibes.org/public/audio/04-grandpa.mp3 +0 -0
  176. package/agentvibes.org/public/audio/05-sarcastic-example2.mp3 +0 -0
  177. package/agentvibes.org/public/audio/french-rachel.mp3 +0 -0
  178. package/agentvibes.org/public/audio/piper-voices/speaker_0_Cori_Samuel.wav +0 -0
  179. package/agentvibes.org/public/audio/piper-voices/speaker_10_Steve_C.wav +0 -0
  180. package/agentvibes.org/public/audio/piper-voices/speaker_11_Owlivia.wav +0 -0
  181. package/agentvibes.org/public/audio/piper-voices/speaker_12_Paul_Hampton.wav +0 -0
  182. package/agentvibes.org/public/audio/piper-voices/speaker_13_Jennifer_Dorr.wav +0 -0
  183. package/agentvibes.org/public/audio/piper-voices/speaker_14_Emily_Cripps.wav +0 -0
  184. package/agentvibes.org/public/audio/piper-voices/speaker_15_Martin_Clifton.wav +0 -0
  185. package/agentvibes.org/public/audio/piper-voices/speaker_1_Kara_Shallenberg.wav +0 -0
  186. package/agentvibes.org/public/audio/piper-voices/speaker_2_Kristin_Hughes.wav +0 -0
  187. package/agentvibes.org/public/audio/piper-voices/speaker_3_Maria_Kasper.wav +0 -0
  188. package/agentvibes.org/public/audio/piper-voices/speaker_4_Mike_Pelton.wav +0 -0
  189. package/agentvibes.org/public/audio/piper-voices/speaker_5_Mark_Nelson.wav +0 -0
  190. package/agentvibes.org/public/audio/piper-voices/speaker_6_Michael_Scherer.wav +0 -0
  191. package/agentvibes.org/public/audio/piper-voices/speaker_7_James_K_White.wav +0 -0
  192. package/agentvibes.org/public/audio/piper-voices/speaker_8_Rose_Ibex.wav +0 -0
  193. package/agentvibes.org/public/audio/piper-voices/speaker_9_progressingamerica.wav +0 -0
  194. package/agentvibes.org/public/audio/spanish-antoni.mp3 +0 -0
  195. package/agentvibes.org/public/favicon.ico +0 -0
  196. package/agentvibes.org/public/fonts/nacelle-italic.woff2 +0 -0
  197. package/agentvibes.org/public/fonts/nacelle-regular.woff2 +0 -0
  198. package/agentvibes.org/public/fonts/nacelle-semibold.woff2 +0 -0
  199. package/agentvibes.org/public/fonts/nacelle-semibolditalic.woff2 +0 -0
  200. package/agentvibes.org/public/images/blurred-shape-gray.svg +0 -1
  201. package/agentvibes.org/public/images/blurred-shape.svg +0 -1
  202. package/agentvibes.org/public/images/client-logo-01.svg +0 -1
  203. package/agentvibes.org/public/images/client-logo-02.svg +0 -1
  204. package/agentvibes.org/public/images/client-logo-03.svg +0 -1
  205. package/agentvibes.org/public/images/client-logo-04.svg +0 -1
  206. package/agentvibes.org/public/images/client-logo-05.svg +0 -1
  207. package/agentvibes.org/public/images/client-logo-06.svg +0 -1
  208. package/agentvibes.org/public/images/client-logo-07.svg +0 -1
  209. package/agentvibes.org/public/images/client-logo-08.svg +0 -1
  210. package/agentvibes.org/public/images/client-logo-09.svg +0 -1
  211. package/agentvibes.org/public/images/features.png +0 -0
  212. package/agentvibes.org/public/images/footer-illustration.svg +0 -1
  213. package/agentvibes.org/public/images/hero-image-01.jpg +0 -0
  214. package/agentvibes.org/public/images/logo.svg +0 -1
  215. package/agentvibes.org/public/images/page-illustration.svg +0 -1
  216. package/agentvibes.org/public/images/secondary-illustration.svg +0 -1
  217. package/agentvibes.org/public/images/testimonial-01.jpg +0 -0
  218. package/agentvibes.org/public/images/testimonial-02.jpg +0 -0
  219. package/agentvibes.org/public/images/testimonial-03.jpg +0 -0
  220. package/agentvibes.org/public/images/testimonial-04.jpg +0 -0
  221. package/agentvibes.org/public/images/testimonial-05.jpg +0 -0
  222. package/agentvibes.org/public/images/testimonial-06.jpg +0 -0
  223. package/agentvibes.org/public/images/testimonial-07.jpg +0 -0
  224. package/agentvibes.org/public/images/testimonial-08.jpg +0 -0
  225. package/agentvibes.org/public/images/testimonial-09.jpg +0 -0
  226. package/agentvibes.org/public/images/workflow-01.png +0 -0
  227. package/agentvibes.org/public/images/workflow-02.png +0 -0
  228. package/agentvibes.org/public/images/workflow-03.png +0 -0
  229. package/agentvibes.org/public/videos/video.mp4 +0 -0
  230. package/agentvibes.org/tsconfig.json +0 -28
  231. package/agentvibes.org/utils/useMasonry.tsx +0 -67
  232. package/agentvibes.org/utils/useMousePosition.tsx +0 -27
  233. package/docs/bryce-beattie-voice-licensing.md +0 -131
@@ -1,227 +0,0 @@
1
- #!/bin/bash
2
- #
3
- # @fileoverview Piper Voice Model Management
4
- # @context Manages downloading, caching, and validating Piper ONNX voice models
5
- # @architecture Voice model lifecycle management for Piper provider
6
- # @dependencies curl, piper binary
7
- # @entrypoints Sourced by play-tts-piper.sh and provider management commands
8
- # @patterns HuggingFace model repository integration, file-based caching
9
- # @related play-tts-piper.sh, provider-manager.sh, GitHub Issue #25
10
- #
11
-
12
- # Base URL for Piper voice models on HuggingFace
13
- PIPER_VOICES_BASE_URL="https://huggingface.co/rhasspy/piper-voices/resolve/main"
14
-
15
- # @function get_voice_storage_dir
16
- # @intent Determine directory for storing Piper voice models
17
- # @why Voice models are large (~25MB each) and should be shared globally across all projects
18
- # @returns Echoes path to voice storage directory (~/.claude/piper-voices)
19
- # @sideeffects Creates directory if it doesn't exist
20
- # @architecture Supports custom path via PIPER_VOICES_DIR env var, defaults to global storage
21
- get_voice_storage_dir() {
22
- local voice_dir
23
-
24
- # Check for custom path in environment or config file
25
- if [[ -n "$PIPER_VOICES_DIR" ]]; then
26
- voice_dir="$PIPER_VOICES_DIR"
27
- else
28
- # Check for config file (project-local first, then global)
29
- local config_file
30
- if [[ -n "$CLAUDE_PROJECT_DIR" ]] && [[ -f "$CLAUDE_PROJECT_DIR/.claude/piper-voices-dir.txt" ]]; then
31
- config_file="$CLAUDE_PROJECT_DIR/.claude/piper-voices-dir.txt"
32
- else
33
- # Search up directory tree for .claude/
34
- local current_dir="$PWD"
35
- while [[ "$current_dir" != "/" ]]; do
36
- if [[ -f "$current_dir/.claude/piper-voices-dir.txt" ]]; then
37
- config_file="$current_dir/.claude/piper-voices-dir.txt"
38
- break
39
- fi
40
- current_dir=$(dirname "$current_dir")
41
- done
42
-
43
- # Check global config
44
- if [[ -z "$config_file" ]] && [[ -f "$HOME/.claude/piper-voices-dir.txt" ]]; then
45
- config_file="$HOME/.claude/piper-voices-dir.txt"
46
- fi
47
- fi
48
-
49
- if [[ -n "$config_file" ]]; then
50
- voice_dir=$(cat "$config_file" | tr -d '[:space:]')
51
- fi
52
- fi
53
-
54
- # Fallback to default global storage
55
- if [[ -z "$voice_dir" ]]; then
56
- voice_dir="$HOME/.claude/piper-voices"
57
- fi
58
-
59
- mkdir -p "$voice_dir"
60
- echo "$voice_dir"
61
- }
62
-
63
- # @function verify_voice
64
- # @intent Check if voice model files exist locally
65
- # @why Avoid redundant downloads, detect missing models
66
- # @param $1 {string} voice_name - Voice model name (e.g., en_US-lessac-medium)
67
- # @returns None
68
- # @exitcode 0=voice exists, 1=voice missing
69
- # @sideeffects None
70
- verify_voice() {
71
- local voice_name="$1"
72
- local voice_dir
73
- voice_dir=$(get_voice_storage_dir)
74
-
75
- local onnx_file="$voice_dir/${voice_name}.onnx"
76
- local json_file="$voice_dir/${voice_name}.onnx.json"
77
-
78
- [[ -f "$onnx_file" ]] && [[ -f "$json_file" ]]
79
- }
80
-
81
- # @function get_voice_path
82
- # @intent Get absolute path to voice model ONNX file
83
- # @why Piper binary requires full path to model file
84
- # @param $1 {string} voice_name - Voice model name
85
- # @returns Echoes path to .onnx file
86
- # @exitcode 0=success, 1=voice not found
87
- # @sideeffects None
88
- get_voice_path() {
89
- local voice_name="$1"
90
- local voice_dir
91
- voice_dir=$(get_voice_storage_dir)
92
-
93
- local onnx_file="$voice_dir/${voice_name}.onnx"
94
-
95
- if [[ ! -f "$onnx_file" ]]; then
96
- echo "❌ Voice model not found: $voice_name" >&2
97
- return 1
98
- fi
99
-
100
- echo "$onnx_file"
101
- }
102
-
103
- # @function parse_voice_components
104
- # @intent Extract language, locale, speaker, quality from voice name
105
- # @why HuggingFace uses directory structure: lang/locale/speaker/quality
106
- # @param $1 {string} voice_name - Voice name (e.g., en_US-lessac-medium)
107
- # @returns Sets global variables: LANG, LOCALE, SPEAKER, QUALITY
108
- # @sideeffects Sets global variables
109
- # AI NOTE: Voice name format is: lang_LOCALE-speaker-quality
110
- parse_voice_components() {
111
- local voice_name="$1"
112
-
113
- # Extract components from voice name
114
- # Format: en_US-lessac-medium
115
- # lang_LOCALE-speaker-quality
116
-
117
- local lang_locale="${voice_name%%-*}" # en_US
118
- local speaker_quality="${voice_name#*-}" # lessac-medium
119
-
120
- LANG="${lang_locale%%_*}" # en
121
- LOCALE="${lang_locale#*_}" # US
122
- SPEAKER="${speaker_quality%%-*}" # lessac
123
- QUALITY="${speaker_quality#*-}" # medium
124
- }
125
-
126
- # @function download_voice
127
- # @intent Download Piper voice model from HuggingFace
128
- # @why Provide free offline TTS voices
129
- # @param $1 {string} voice_name - Voice model name
130
- # @param $2 {string} lang_code - Language code (optional, inferred from voice_name)
131
- # @returns None
132
- # @exitcode 0=success, 1=download failed
133
- # @sideeffects Downloads .onnx and .onnx.json files
134
- # @edgecases Handles network failures, validates file integrity
135
- download_voice() {
136
- local voice_name="$1"
137
- local lang_code="${2:-}"
138
-
139
- local voice_dir
140
- voice_dir=$(get_voice_storage_dir)
141
-
142
- # Check if already downloaded
143
- if verify_voice "$voice_name"; then
144
- echo "✅ Voice already downloaded: $voice_name"
145
- return 0
146
- fi
147
-
148
- # Parse voice components
149
- parse_voice_components "$voice_name"
150
-
151
- # Construct download URLs
152
- # Path format: {language}/{language}_{locale}/{speaker}/{quality}/{speaker}-{quality}.onnx
153
- local model_path="${LANG}/${LANG}_${LOCALE}/${SPEAKER}/${QUALITY}/${voice_name}"
154
- local onnx_url="${PIPER_VOICES_BASE_URL}/${model_path}.onnx"
155
- local json_url="${PIPER_VOICES_BASE_URL}/${model_path}.onnx.json"
156
-
157
- echo "📥 Downloading Piper voice: $voice_name"
158
- echo " Source: HuggingFace (rhasspy/piper-voices)"
159
- echo " Size: ~25MB"
160
- echo ""
161
-
162
- # Download ONNX model
163
- echo " Downloading model file..."
164
- if ! curl -L --progress-bar -o "$voice_dir/${voice_name}.onnx" "$onnx_url"; then
165
- echo "❌ Failed to download voice model"
166
- rm -f "$voice_dir/${voice_name}.onnx"
167
- return 1
168
- fi
169
-
170
- # Download JSON config
171
- echo " Downloading config file..."
172
- if ! curl -L -s -o "$voice_dir/${voice_name}.onnx.json" "$json_url"; then
173
- echo "❌ Failed to download voice config"
174
- rm -f "$voice_dir/${voice_name}.onnx" "$voice_dir/${voice_name}.onnx.json"
175
- return 1
176
- fi
177
-
178
- # Verify file integrity (basic check - file size > 0)
179
- if [[ ! -s "$voice_dir/${voice_name}.onnx" ]]; then
180
- echo "❌ Downloaded file is empty or corrupt"
181
- rm -f "$voice_dir/${voice_name}.onnx" "$voice_dir/${voice_name}.onnx.json"
182
- return 1
183
- fi
184
-
185
- echo "✅ Voice downloaded successfully: $voice_name"
186
- echo " Location: $voice_dir/${voice_name}.onnx"
187
- }
188
-
189
- # @function list_downloaded_voices
190
- # @intent Show all locally cached voice models
191
- # @why Help users see what voices they have available
192
- # @returns Echoes voice names (one per line)
193
- # @exitcode 0=success
194
- # @sideeffects None
195
- list_downloaded_voices() {
196
- local voice_dir
197
- voice_dir=$(get_voice_storage_dir)
198
-
199
- echo "📦 Downloaded Piper Voices:"
200
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
201
-
202
- local count=0
203
- shopt -s nullglob
204
- for onnx_file in "$voice_dir"/*.onnx; do
205
- if [[ -f "$onnx_file" ]]; then
206
- local voice_name
207
- voice_name=$(basename "$onnx_file" .onnx)
208
- local file_size
209
- file_size=$(du -h "$onnx_file" | cut -f1)
210
- echo " • $voice_name ($file_size)"
211
- ((count++))
212
- fi
213
- done
214
- shopt -u nullglob
215
-
216
- if [[ $count -eq 0 ]]; then
217
- echo " (No voices downloaded yet)"
218
- fi
219
-
220
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
221
- echo "Total: $count voices"
222
- }
223
-
224
- # AI NOTE: This file manages the lifecycle of Piper voice models
225
- # Voice models are ONNX files (~20-30MB each) downloaded from HuggingFace
226
- # Files are cached locally to avoid repeated downloads
227
- # Project-local storage preferred over global for isolation
@@ -1,376 +0,0 @@
1
- #!/bin/bash
2
- #
3
- # @fileoverview ElevenLabs TTS Provider Implementation
4
- # @context Provider-specific implementation for ElevenLabs API integration
5
- # @architecture Part of multi-provider TTS system - implements provider interface
6
- # @dependencies Requires ELEVENLABS_API_KEY, curl, ffmpeg, paplay/aplay/mpg123, jq
7
- # @entrypoints Called by play-tts.sh router with ($1=text, $2=voice_name)
8
- # @patterns Follows provider contract: accept text/voice, output audio file path
9
- # @related play-tts.sh, provider-manager.sh, GitHub Issue #25
10
- #
11
-
12
- # Fix locale warnings
13
- export LC_ALL=C
14
-
15
- TEXT="$1"
16
- VOICE_OVERRIDE="$2" # Optional: voice name or direct voice ID
17
- API_KEY="${ELEVENLABS_API_KEY}"
18
-
19
- # Check for project-local pretext configuration
20
- CONFIG_DIR="${CLAUDE_PROJECT_DIR:-.}/.claude/config"
21
- CONFIG_FILE="$CONFIG_DIR/agentvibes.json"
22
-
23
- if [[ -f "$CONFIG_FILE" ]] && command -v jq &> /dev/null; then
24
- PRETEXT=$(jq -r '.pretext // empty' "$CONFIG_FILE" 2>/dev/null)
25
- if [[ -n "$PRETEXT" ]]; then
26
- TEXT="$PRETEXT: $TEXT"
27
- fi
28
- fi
29
-
30
- # Limit text length to prevent API issues (max 500 chars for safety)
31
- if [ ${#TEXT} -gt 500 ]; then
32
- TEXT="${TEXT:0:497}..."
33
- echo "⚠️ Text truncated to 500 characters for API safety"
34
- fi
35
-
36
- # Source the single voice configuration file
37
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
38
- source "$SCRIPT_DIR/voices-config.sh"
39
- source "$SCRIPT_DIR/language-manager.sh"
40
-
41
- # @function determine_voice_and_language
42
- # @intent Resolve voice name/ID and language for multilingual support
43
- # @why Supports both voice names and direct IDs, plus language-specific voices
44
- # @param $VOICE_OVERRIDE {string} Voice name or ID (optional)
45
- # @returns Sets $VOICE_ID and $LANGUAGE_CODE global variables
46
- # @sideeffects None
47
- # @edgecases Handles unknown voices, falls back to default
48
- VOICE_ID=""
49
- LANGUAGE_CODE="en" # Default to English
50
-
51
- # Get current language setting
52
- CURRENT_LANGUAGE=$(get_language_code)
53
-
54
- # Get language code for API
55
- # ElevenLabs uses 2-letter ISO codes
56
- case "$CURRENT_LANGUAGE" in
57
- spanish) LANGUAGE_CODE="es" ;;
58
- french) LANGUAGE_CODE="fr" ;;
59
- german) LANGUAGE_CODE="de" ;;
60
- italian) LANGUAGE_CODE="it" ;;
61
- portuguese) LANGUAGE_CODE="pt" ;;
62
- chinese) LANGUAGE_CODE="zh" ;;
63
- japanese) LANGUAGE_CODE="ja" ;;
64
- korean) LANGUAGE_CODE="ko" ;;
65
- russian) LANGUAGE_CODE="ru" ;;
66
- polish) LANGUAGE_CODE="pl" ;;
67
- dutch) LANGUAGE_CODE="nl" ;;
68
- turkish) LANGUAGE_CODE="tr" ;;
69
- arabic) LANGUAGE_CODE="ar" ;;
70
- hindi) LANGUAGE_CODE="hi" ;;
71
- swedish) LANGUAGE_CODE="sv" ;;
72
- danish) LANGUAGE_CODE="da" ;;
73
- norwegian) LANGUAGE_CODE="no" ;;
74
- finnish) LANGUAGE_CODE="fi" ;;
75
- czech) LANGUAGE_CODE="cs" ;;
76
- romanian) LANGUAGE_CODE="ro" ;;
77
- ukrainian) LANGUAGE_CODE="uk" ;;
78
- greek) LANGUAGE_CODE="el" ;;
79
- bulgarian) LANGUAGE_CODE="bg" ;;
80
- croatian) LANGUAGE_CODE="hr" ;;
81
- slovak) LANGUAGE_CODE="sk" ;;
82
- english|*) LANGUAGE_CODE="en" ;;
83
- esac
84
-
85
- if [[ -n "$VOICE_OVERRIDE" ]]; then
86
- # Check if override is a voice name (lookup in mapping)
87
- if [[ -n "${VOICES[$VOICE_OVERRIDE]}" ]]; then
88
- VOICE_ID="${VOICES[$VOICE_OVERRIDE]}"
89
- echo "🎤 Using voice: $VOICE_OVERRIDE (session-specific)"
90
- # Check if override looks like a voice ID (alphanumeric string ~20 chars)
91
- elif [[ "$VOICE_OVERRIDE" =~ ^[a-zA-Z0-9]{15,30}$ ]]; then
92
- VOICE_ID="$VOICE_OVERRIDE"
93
- echo "🎤 Using custom voice ID (session-specific)"
94
- else
95
- echo "⚠️ Unknown voice '$VOICE_OVERRIDE', trying language-specific voice"
96
- fi
97
- fi
98
-
99
- # If no override or invalid override, use language-specific voice
100
- if [[ -z "$VOICE_ID" ]]; then
101
- # Try to get voice for current language
102
- LANG_VOICE=$(get_voice_for_language "$CURRENT_LANGUAGE" "elevenlabs" 2>/dev/null)
103
-
104
- if [[ -n "$LANG_VOICE" ]] && [[ -n "${VOICES[$LANG_VOICE]}" ]]; then
105
- VOICE_ID="${VOICES[$LANG_VOICE]}"
106
- echo "🌍 Using $CURRENT_LANGUAGE voice: $LANG_VOICE"
107
- else
108
- # Fall back to voice manager
109
- VOICE_MANAGER_SCRIPT="$(dirname "$0")/voice-manager.sh"
110
- if [[ -f "$VOICE_MANAGER_SCRIPT" ]]; then
111
- VOICE_NAME=$("$VOICE_MANAGER_SCRIPT" get)
112
- VOICE_ID="${VOICES[$VOICE_NAME]}"
113
- fi
114
-
115
- # Final fallback to default
116
- if [[ -z "$VOICE_ID" ]]; then
117
- echo "⚠️ No voice configured, using default"
118
- VOICE_ID="${VOICES[Aria]}"
119
- fi
120
- fi
121
- fi
122
-
123
- # @function validate_inputs
124
- # @intent Check required parameters and API key
125
- # @why Fail fast with clear errors if inputs missing
126
- # @exitcode 1=missing text, 2=missing API key
127
- if [ -z "$TEXT" ]; then
128
- echo "Usage: $0 \"text to speak\" [voice_name_or_id]"
129
- exit 1
130
- fi
131
-
132
- if [ -z "$API_KEY" ]; then
133
- echo "Error: ELEVENLABS_API_KEY not set"
134
- echo "Set your API key: export ELEVENLABS_API_KEY=your_key_here"
135
- exit 2
136
- fi
137
-
138
- # @function determine_audio_directory
139
- # @intent Find appropriate directory for audio file storage
140
- # @why Supports project-local and global storage
141
- # @returns Sets $AUDIO_DIR global variable
142
- # @sideeffects None
143
- # @edgecases Handles missing directories, creates if needed
144
- # AI NOTE: Check project dir first, then search up tree, finally fall back to global
145
- if [[ -n "$CLAUDE_PROJECT_DIR" ]]; then
146
- AUDIO_DIR="$CLAUDE_PROJECT_DIR/.claude/audio"
147
- else
148
- # Fallback: try to find .claude directory in current path
149
- CURRENT_DIR="$PWD"
150
- while [[ "$CURRENT_DIR" != "/" ]]; do
151
- if [[ -d "$CURRENT_DIR/.claude" ]]; then
152
- AUDIO_DIR="$CURRENT_DIR/.claude/audio"
153
- break
154
- fi
155
- CURRENT_DIR=$(dirname "$CURRENT_DIR")
156
- done
157
- # Final fallback to global if no project .claude found
158
- if [[ -z "$AUDIO_DIR" ]]; then
159
- AUDIO_DIR="$HOME/.claude/audio"
160
- fi
161
- fi
162
-
163
- mkdir -p "$AUDIO_DIR"
164
- TEMP_FILE="$AUDIO_DIR/tts-$(date +%s).mp3"
165
-
166
- # @function synthesize_with_elevenlabs
167
- # @intent Call ElevenLabs API to generate speech
168
- # @why Encapsulates API call with error handling
169
- # @param Uses globals: $TEXT, $VOICE_ID, $API_KEY
170
- # @returns Creates audio file at $TEMP_FILE
171
- # @exitcode 0=success, 3=API error
172
- # @sideeffects Creates MP3 file in audio directory
173
- # @edgecases Handles network failures, API errors, rate limiting
174
- # Choose model based on language
175
- if [[ "$LANGUAGE_CODE" == "en" ]]; then
176
- MODEL_ID="eleven_monolingual_v1"
177
- else
178
- MODEL_ID="eleven_multilingual_v2"
179
- fi
180
-
181
- # @function get_speech_speed
182
- # @intent Read speed config and map to ElevenLabs API range (0.7-1.2)
183
- # @why ElevenLabs only supports 0.7 (slower) to 1.2 (faster), must map user scale
184
- # @returns Speed value for ElevenLabs API (clamped to 0.7-1.2)
185
- get_speech_speed() {
186
- local config_dir=""
187
-
188
- # Determine config directory
189
- if [[ -n "$CLAUDE_PROJECT_DIR" ]] && [[ -d "$CLAUDE_PROJECT_DIR/.claude" ]]; then
190
- config_dir="$CLAUDE_PROJECT_DIR/.claude/config"
191
- else
192
- # Try to find .claude in current path
193
- local current_dir="$PWD"
194
- while [[ "$current_dir" != "/" ]]; do
195
- if [[ -d "$current_dir/.claude" ]]; then
196
- config_dir="$current_dir/.claude/config"
197
- break
198
- fi
199
- current_dir=$(dirname "$current_dir")
200
- done
201
- # Fallback to global
202
- if [[ -z "$config_dir" ]]; then
203
- config_dir="$HOME/.claude/config"
204
- fi
205
- fi
206
-
207
- local main_speed_file="$config_dir/tts-speech-rate.txt"
208
- local target_speed_file="$config_dir/tts-target-speech-rate.txt"
209
-
210
- # Legacy file paths for backward compatibility
211
- local legacy_main_speed_file="$config_dir/piper-speech-rate.txt"
212
- local legacy_target_speed_file="$config_dir/piper-target-speech-rate.txt"
213
-
214
- local user_speed="1.0"
215
-
216
- # If this is a non-English voice and target config exists, use it
217
- if [[ "$CURRENT_LANGUAGE" != "english" ]]; then
218
- if [[ -f "$target_speed_file" ]]; then
219
- user_speed=$(cat "$target_speed_file" 2>/dev/null || echo "1.0")
220
- elif [[ -f "$legacy_target_speed_file" ]]; then
221
- user_speed=$(cat "$legacy_target_speed_file" 2>/dev/null || echo "1.0")
222
- else
223
- user_speed="0.5" # Default slower for learning
224
- fi
225
- else
226
- # Otherwise use main config if available
227
- if [[ -f "$main_speed_file" ]]; then
228
- user_speed=$(grep -v '^#' "$main_speed_file" 2>/dev/null | grep -v '^$' | tail -1 || echo "1.0")
229
- elif [[ -f "$legacy_main_speed_file" ]]; then
230
- user_speed=$(grep -v '^#' "$legacy_main_speed_file" 2>/dev/null | grep -v '^$' | tail -1 || echo "1.0")
231
- fi
232
- fi
233
-
234
- # Map user scale (0.5=slower, 1.0=normal, 2.0=faster, 3.0=very fast)
235
- # to ElevenLabs range (0.7=slower, 1.0=normal, 1.2=faster)
236
- # Formula: elevenlabs_speed = 0.7 + (user_speed - 0.5) * 0.2
237
- # This maps: 0.5→0.7, 1.0→0.8, 2.0→1.0, 3.0→1.2
238
- # Actually, let's use a better mapping:
239
- # 0.5x → 0.7 (slowest ElevenLabs)
240
- # 1.0x → 1.0 (normal)
241
- # 2.0x → 1.15
242
- # 3.0x → 1.2 (fastest ElevenLabs)
243
-
244
- if command -v bc &> /dev/null; then
245
- local eleven_speed
246
- if (( $(echo "$user_speed <= 0.5" | bc -l) )); then
247
- eleven_speed="0.7"
248
- elif (( $(echo "$user_speed >= 3.0" | bc -l) )); then
249
- eleven_speed="1.2"
250
- elif (( $(echo "$user_speed <= 1.0" | bc -l) )); then
251
- # Map 0.5-1.0 to 0.7-1.0
252
- eleven_speed=$(echo "scale=2; 0.7 + ($user_speed - 0.5) * 0.6" | bc -l)
253
- else
254
- # Map 1.0-3.0 to 1.0-1.2
255
- eleven_speed=$(echo "scale=2; 1.0 + ($user_speed - 1.0) * 0.1" | bc -l)
256
- fi
257
- echo "$eleven_speed"
258
- else
259
- # Fallback without bc: just clamp to safe values
260
- if (( $(awk 'BEGIN {print ("'$user_speed'" <= 0.5)}') )); then
261
- echo "0.7"
262
- elif (( $(awk 'BEGIN {print ("'$user_speed'" >= 2.0)}') )); then
263
- echo "1.2"
264
- else
265
- echo "1.0"
266
- fi
267
- fi
268
- }
269
-
270
- SPEECH_SPEED=$(get_speech_speed)
271
-
272
- # Build JSON payload with jq for proper escaping
273
- PAYLOAD=$(jq -n \
274
- --arg text "$TEXT" \
275
- --arg model "$MODEL_ID" \
276
- --arg lang "$LANGUAGE_CODE" \
277
- --argjson speed "$SPEECH_SPEED" \
278
- '{
279
- text: $text,
280
- model_id: $model,
281
- language_code: $lang,
282
- voice_settings: {
283
- stability: 0.5,
284
- similarity_boost: 0.75,
285
- speed: $speed
286
- }
287
- }')
288
-
289
- curl -s -X POST "https://api.elevenlabs.io/v1/text-to-speech/${VOICE_ID}" \
290
- -H "xi-api-key: ${API_KEY}" \
291
- -H "Content-Type: application/json" \
292
- -d "$PAYLOAD" \
293
- -o "${TEMP_FILE}"
294
-
295
- # @function add_silence_padding
296
- # @intent Add silence to beginning of audio to prevent WSL static
297
- # @why WSL audio subsystem cuts off first ~200ms, causing static/clipping
298
- # @param Uses global: $TEMP_FILE
299
- # @returns Updates $TEMP_FILE to padded version
300
- # @sideeffects Modifies audio file, removes original
301
- # @edgecases Gracefully falls back to unpadded if ffmpeg unavailable
302
- # Add silence padding to prevent WSL audio static
303
- if [ -f "${TEMP_FILE}" ]; then
304
- # Check if ffmpeg is available for adding padding
305
- if command -v ffmpeg &> /dev/null; then
306
- PADDED_FILE="$AUDIO_DIR/tts-padded-$(date +%s).mp3"
307
- # Add 200ms of silence at the beginning to prevent static
308
- # Note: ElevenLabs returns mono audio, so we use mono silence
309
- ffmpeg -f lavfi -i anullsrc=r=44100:cl=mono:d=0.2 -i "${TEMP_FILE}" \
310
- -filter_complex "[0:a][1:a]concat=n=2:v=0:a=1[out]" \
311
- -map "[out]" -c:a libmp3lame -b:a 128k -y "${PADDED_FILE}" 2>/dev/null
312
-
313
- if [ -f "${PADDED_FILE}" ]; then
314
- # Use padded file and clean up original
315
- rm -f "${TEMP_FILE}"
316
- TEMP_FILE="${PADDED_FILE}"
317
- fi
318
- # If padding failed, just use original file
319
- fi
320
-
321
- # @function play_audio
322
- # @intent Play generated audio file using available player with sequential playback
323
- # @why Support multiple audio players and prevent overlapping audio in learning mode
324
- # @param Uses global: $TEMP_FILE, $CURRENT_LANGUAGE
325
- # @sideeffects Plays audio with lock mechanism for sequential playback
326
- # @edgecases Falls through players until one works
327
- LOCK_FILE="/tmp/agentvibes-audio.lock"
328
-
329
- # Wait for previous audio to finish (max 30 seconds)
330
- for i in {1..60}; do
331
- if [ ! -f "$LOCK_FILE" ]; then
332
- break
333
- fi
334
- sleep 0.5
335
- done
336
-
337
- # Track last target language audio for replay command
338
- if [[ "$CURRENT_LANGUAGE" != "english" ]]; then
339
- TARGET_AUDIO_FILE="${CLAUDE_PROJECT_DIR:-.}/.claude/last-target-audio.txt"
340
- echo "${TEMP_FILE}" > "$TARGET_AUDIO_FILE"
341
- fi
342
-
343
- # Create lock and play audio
344
- touch "$LOCK_FILE"
345
-
346
- # Get audio duration for proper lock timing
347
- DURATION=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "${TEMP_FILE}" 2>/dev/null)
348
- DURATION=${DURATION%.*} # Round to integer
349
- DURATION=${DURATION:-1} # Default to 1 second if detection fails
350
-
351
- # Convert to 48kHz stereo WAV for better SSH tunnel compatibility
352
- # ElevenLabs returns 44.1kHz mono MP3, which causes static over SSH audio tunnels
353
- # Converting to 48kHz stereo (Windows/PulseAudio native format) eliminates the static
354
- if [[ -n "$SSH_CONNECTION" ]] || [[ -n "$SSH_CLIENT" ]] || [[ -n "$VSCODE_IPC_HOOK_CLI" ]]; then
355
- CONVERTED_FILE="${TEMP_FILE%.mp3}.wav"
356
- if ffmpeg -i "${TEMP_FILE}" -ar 48000 -ac 2 "${CONVERTED_FILE}" -y 2>/dev/null; then
357
- TEMP_FILE="${CONVERTED_FILE}"
358
- fi
359
- fi
360
-
361
- # Play audio (WSL/Linux) in background to avoid blocking, fully detached
362
- (paplay "${TEMP_FILE}" || aplay "${TEMP_FILE}" || mpg123 "${TEMP_FILE}") >/dev/null 2>&1 &
363
- PLAYER_PID=$!
364
-
365
- # Wait for audio to finish, then release lock
366
- (sleep $DURATION; rm -f "$LOCK_FILE") &
367
- disown
368
-
369
- # Keep temp files for later review - cleaned up weekly by cron
370
- echo "🎵 Saved to: ${TEMP_FILE}"
371
- echo "🎤 Voice used: ${VOICE_NAME} (${VOICE_ID})"
372
- else
373
- echo "❌ Failed to generate audio - API may be unavailable"
374
- echo "Check your API key and network connection"
375
- exit 3
376
- fi