agentvibes 2.0.6 → 2.0.8

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 (159) hide show
  1. package/.claude/commands/agent-vibes/preview.md +5 -4
  2. package/.claude/commands/agent-vibes/whoami.md +2 -2
  3. package/.claude/hooks/check-output-style.sh +60 -0
  4. package/.claude/hooks/language-manager.sh +50 -48
  5. package/.claude/hooks/personality-manager.sh +34 -2
  6. package/.claude/hooks/play-tts-piper.sh +27 -9
  7. package/.claude/hooks/play-tts.sh +29 -127
  8. package/.claude/hooks/provider-commands.sh +41 -2
  9. package/.claude/hooks/voice-manager.sh +85 -36
  10. package/.claude/personalities/angry.md +2 -1
  11. package/.claude/personalities/annoying.md +2 -1
  12. package/.claude/personalities/crass.md +2 -1
  13. package/.claude/personalities/dramatic.md +2 -1
  14. package/.claude/personalities/dry-humor.md +2 -1
  15. package/.claude/personalities/flirty.md +2 -1
  16. package/.claude/personalities/funny.md +2 -1
  17. package/.claude/personalities/grandpa.md +2 -1
  18. package/.claude/personalities/millennial.md +2 -1
  19. package/.claude/personalities/moody.md +2 -1
  20. package/.claude/personalities/normal.md +2 -1
  21. package/.claude/personalities/pirate.md +2 -1
  22. package/.claude/personalities/poetic.md +2 -1
  23. package/.claude/personalities/professional.md +2 -1
  24. package/.claude/personalities/robot.md +2 -1
  25. package/.claude/personalities/sarcastic.md +2 -1
  26. package/.claude/personalities/sassy.md +2 -1
  27. package/.claude/personalities/surfer-dude.md +2 -1
  28. package/.claude/personalities/zen.md +2 -1
  29. package/.claude/piper-voices-dir.txt +1 -0
  30. package/README.md +2 -2
  31. package/RELEASE_NOTES.md +277 -0
  32. package/agentvibes.org/.claude/commands/agent-vibes/add.md +21 -0
  33. package/agentvibes.org/.claude/commands/agent-vibes/agent-vibes.md +68 -0
  34. package/agentvibes.org/.claude/commands/agent-vibes/commands.json +53 -0
  35. package/agentvibes.org/.claude/commands/agent-vibes/get.md +9 -0
  36. package/agentvibes.org/.claude/commands/agent-vibes/list.md +13 -0
  37. package/agentvibes.org/.claude/commands/agent-vibes/personality.md +79 -0
  38. package/agentvibes.org/.claude/commands/agent-vibes/preview.md +16 -0
  39. package/agentvibes.org/.claude/commands/agent-vibes/provider.md +54 -0
  40. package/agentvibes.org/.claude/commands/agent-vibes/replay.md +19 -0
  41. package/agentvibes.org/.claude/commands/agent-vibes/sample.md +12 -0
  42. package/agentvibes.org/.claude/commands/agent-vibes/sentiment.md +52 -0
  43. package/agentvibes.org/.claude/commands/agent-vibes/set-language.md +47 -0
  44. package/agentvibes.org/.claude/commands/agent-vibes/set-pretext.md +65 -0
  45. package/agentvibes.org/.claude/commands/agent-vibes/switch.md +53 -0
  46. package/agentvibes.org/.claude/commands/agent-vibes/update.md +20 -0
  47. package/agentvibes.org/.claude/commands/agent-vibes/version.md +10 -0
  48. package/agentvibes.org/.claude/commands/agent-vibes/whoami.md +7 -0
  49. package/agentvibes.org/.claude/hooks/bmad-voice-manager.sh +278 -0
  50. package/agentvibes.org/.claude/hooks/language-manager.sh +190 -0
  51. package/agentvibes.org/.claude/hooks/personality-manager.sh +279 -0
  52. package/agentvibes.org/.claude/hooks/piper-download-voices.sh +133 -0
  53. package/agentvibes.org/.claude/hooks/piper-voice-manager.sh +227 -0
  54. package/agentvibes.org/.claude/hooks/play-tts-elevenlabs.sh +201 -0
  55. package/agentvibes.org/.claude/hooks/play-tts-piper.sh +175 -0
  56. package/agentvibes.org/.claude/hooks/play-tts.sh +138 -0
  57. package/agentvibes.org/.claude/hooks/provider-commands.sh +374 -0
  58. package/agentvibes.org/.claude/hooks/provider-manager.sh +196 -0
  59. package/agentvibes.org/.claude/hooks/sentiment-manager.sh +163 -0
  60. package/agentvibes.org/.claude/hooks/voice-manager.sh +349 -0
  61. package/agentvibes.org/.claude/hooks/voices-config.sh +33 -0
  62. package/agentvibes.org/.claude/journal/2025-10-07.html +373 -0
  63. package/agentvibes.org/.claude/journal/index.html +91 -0
  64. package/agentvibes.org/.claude/output-styles/agent-vibes.md +203 -0
  65. package/agentvibes.org/.claude/personalities/angry.md +16 -0
  66. package/agentvibes.org/.claude/personalities/annoying.md +16 -0
  67. package/agentvibes.org/.claude/personalities/crass.md +16 -0
  68. package/agentvibes.org/.claude/personalities/dramatic.md +16 -0
  69. package/agentvibes.org/.claude/personalities/dry-humor.md +52 -0
  70. package/agentvibes.org/.claude/personalities/flirty.md +22 -0
  71. package/agentvibes.org/.claude/personalities/funny.md +16 -0
  72. package/agentvibes.org/.claude/personalities/grandpa.md +34 -0
  73. package/agentvibes.org/.claude/personalities/millennial.md +16 -0
  74. package/agentvibes.org/.claude/personalities/moody.md +16 -0
  75. package/agentvibes.org/.claude/personalities/normal.md +18 -0
  76. package/agentvibes.org/.claude/personalities/pirate.md +16 -0
  77. package/agentvibes.org/.claude/personalities/poetic.md +16 -0
  78. package/agentvibes.org/.claude/personalities/professional.md +16 -0
  79. package/agentvibes.org/.claude/personalities/robot.md +16 -0
  80. package/agentvibes.org/.claude/personalities/sarcastic.md +40 -0
  81. package/agentvibes.org/.claude/personalities/sassy.md +16 -0
  82. package/agentvibes.org/.claude/personalities/surfer-dude.md +16 -0
  83. package/agentvibes.org/.claude/personalities/zen.md +16 -0
  84. package/agentvibes.org/.mcp-minimal.json +60 -0
  85. package/agentvibes.org/CHANGELOG.md +56 -0
  86. package/agentvibes.org/README.md +93 -0
  87. package/agentvibes.org/app/(auth)/layout.tsx +15 -0
  88. package/agentvibes.org/app/(auth)/reset-password/page.tsx +45 -0
  89. package/agentvibes.org/app/(auth)/signin/page.tsx +82 -0
  90. package/agentvibes.org/app/(auth)/signup/page.tsx +104 -0
  91. package/agentvibes.org/app/(default)/layout.tsx +31 -0
  92. package/agentvibes.org/app/(default)/page.tsx +20 -0
  93. package/agentvibes.org/app/api/hello/route.ts +3 -0
  94. package/agentvibes.org/app/css/additional-styles/theme.css +82 -0
  95. package/agentvibes.org/app/css/additional-styles/utility-patterns.css +55 -0
  96. package/agentvibes.org/app/css/style.css +100 -0
  97. package/agentvibes.org/app/layout.tsx +63 -0
  98. package/agentvibes.org/components/cta.tsx +58 -0
  99. package/agentvibes.org/components/features.tsx +256 -0
  100. package/agentvibes.org/components/hero-home.tsx +133 -0
  101. package/agentvibes.org/components/modal-video.tsx +137 -0
  102. package/agentvibes.org/components/page-illustration.tsx +55 -0
  103. package/agentvibes.org/components/spotlight.tsx +77 -0
  104. package/agentvibes.org/components/testimonials.tsx +282 -0
  105. package/agentvibes.org/components/ui/footer.tsx +82 -0
  106. package/agentvibes.org/components/ui/header.tsx +53 -0
  107. package/agentvibes.org/components/ui/logo.tsx +10 -0
  108. package/agentvibes.org/components/workflows.tsx +176 -0
  109. package/agentvibes.org/next.config.js +4 -0
  110. package/agentvibes.org/package-lock.json +1974 -0
  111. package/agentvibes.org/package.json +30 -0
  112. package/agentvibes.org/pnpm-lock.yaml +1141 -0
  113. package/agentvibes.org/postcss.config.js +5 -0
  114. package/agentvibes.org/public/audio/02-sarcastic.mp3 +0 -0
  115. package/agentvibes.org/public/audio/03-angry.mp3 +0 -0
  116. package/agentvibes.org/public/audio/04-grandpa.mp3 +0 -0
  117. package/agentvibes.org/public/audio/05-sarcastic-example2.mp3 +0 -0
  118. package/agentvibes.org/public/audio/french-rachel.mp3 +0 -0
  119. package/agentvibes.org/public/audio/spanish-antoni.mp3 +0 -0
  120. package/agentvibes.org/public/favicon.ico +0 -0
  121. package/agentvibes.org/public/fonts/nacelle-italic.woff2 +0 -0
  122. package/agentvibes.org/public/fonts/nacelle-regular.woff2 +0 -0
  123. package/agentvibes.org/public/fonts/nacelle-semibold.woff2 +0 -0
  124. package/agentvibes.org/public/fonts/nacelle-semibolditalic.woff2 +0 -0
  125. package/agentvibes.org/public/images/blurred-shape-gray.svg +1 -0
  126. package/agentvibes.org/public/images/blurred-shape.svg +1 -0
  127. package/agentvibes.org/public/images/client-logo-01.svg +1 -0
  128. package/agentvibes.org/public/images/client-logo-02.svg +1 -0
  129. package/agentvibes.org/public/images/client-logo-03.svg +1 -0
  130. package/agentvibes.org/public/images/client-logo-04.svg +1 -0
  131. package/agentvibes.org/public/images/client-logo-05.svg +1 -0
  132. package/agentvibes.org/public/images/client-logo-06.svg +1 -0
  133. package/agentvibes.org/public/images/client-logo-07.svg +1 -0
  134. package/agentvibes.org/public/images/client-logo-08.svg +1 -0
  135. package/agentvibes.org/public/images/client-logo-09.svg +1 -0
  136. package/agentvibes.org/public/images/features.png +0 -0
  137. package/agentvibes.org/public/images/footer-illustration.svg +1 -0
  138. package/agentvibes.org/public/images/hero-image-01.jpg +0 -0
  139. package/agentvibes.org/public/images/logo.svg +1 -0
  140. package/agentvibes.org/public/images/page-illustration.svg +1 -0
  141. package/agentvibes.org/public/images/secondary-illustration.svg +1 -0
  142. package/agentvibes.org/public/images/testimonial-01.jpg +0 -0
  143. package/agentvibes.org/public/images/testimonial-02.jpg +0 -0
  144. package/agentvibes.org/public/images/testimonial-03.jpg +0 -0
  145. package/agentvibes.org/public/images/testimonial-04.jpg +0 -0
  146. package/agentvibes.org/public/images/testimonial-05.jpg +0 -0
  147. package/agentvibes.org/public/images/testimonial-06.jpg +0 -0
  148. package/agentvibes.org/public/images/testimonial-07.jpg +0 -0
  149. package/agentvibes.org/public/images/testimonial-08.jpg +0 -0
  150. package/agentvibes.org/public/images/testimonial-09.jpg +0 -0
  151. package/agentvibes.org/public/images/workflow-01.png +0 -0
  152. package/agentvibes.org/public/images/workflow-02.png +0 -0
  153. package/agentvibes.org/public/images/workflow-03.png +0 -0
  154. package/agentvibes.org/public/videos/video.mp4 +0 -0
  155. package/agentvibes.org/tsconfig.json +28 -0
  156. package/agentvibes.org/utils/useMasonry.tsx +67 -0
  157. package/agentvibes.org/utils/useMousePosition.tsx +27 -0
  158. package/package.json +1 -1
  159. package/src/installer.js +145 -171
@@ -1,16 +1,17 @@
1
1
  ---
2
- description: Preview ElevenLabs TTS voices by playing audio samples
2
+ description: Preview TTS voices by playing audio samples (provider-aware)
3
3
  argument-hint: [voice_name|first|last] [N]
4
4
  ---
5
5
 
6
- Preview ElevenLabs TTS voices by playing audio samples.
6
+ Preview TTS voices by playing audio samples from your active provider.
7
7
 
8
8
  Usage examples:
9
9
  - `/agent-vibes:preview` - Preview first 3 voices (default)
10
10
  - `/agent-vibes:preview 5` - Preview first 5 voices
11
- - `/agent-vibes:preview Jessica` - Preview Jessica Anne Bogart voice
11
+ - `/agent-vibes:preview Jessica` - Preview Jessica Anne Bogart voice (ElevenLabs)
12
+ - `/agent-vibes:preview lessac` - Preview Lessac voice (Piper)
12
13
  - `/agent-vibes:preview "Northern Terry"` - Preview Northern Terry voice
13
14
  - `/agent-vibes:preview first 10` - Preview first 10 voices
14
15
  - `/agent-vibes:preview last 5` - Preview last 5 voices
15
16
 
16
- !bash .claude/hooks/voice-manager.sh preview $ARGUMENTS
17
+ !bash .claude/hooks/provider-commands.sh preview $ARGUMENTS
@@ -1,7 +1,7 @@
1
1
  ---
2
- description: Show the current active voice
2
+ description: Show the current active voice and TTS provider
3
3
  ---
4
4
 
5
- Display the currently selected ElevenLabs TTS voice.
5
+ Display the currently selected TTS voice and active provider (ElevenLabs or Piper).
6
6
 
7
7
  !bash .claude/hooks/voice-manager.sh whoami
@@ -0,0 +1,60 @@
1
+ #!/bin/bash
2
+ #
3
+ # @fileoverview Output Style Detection for AgentVibes
4
+ # @context Detects if agent-vibes output style is active
5
+ # @architecture Helper for slash commands to warn users
6
+ # @why Voice commands won't work without agent-vibes output style
7
+ #
8
+
9
+ # Check if we're in a Claude Code session with agent-vibes output style
10
+ # This is a heuristic check - we can't directly detect the output style,
11
+ # but we can check if TTS commands would work
12
+
13
+ check_output_style() {
14
+ # Strategy: Check if this script is being called from within a Claude response
15
+ # If CLAUDECODE env var is set, we're in Claude Code
16
+ # If not, we're running standalone (not in a Claude Code session)
17
+
18
+ if [[ -z "$CLAUDECODE" ]]; then
19
+ # Not in Claude Code at all
20
+ return 1
21
+ fi
22
+
23
+ # We're in Claude Code, but we can't directly detect output style
24
+ # The agent-vibes output style calls our TTS hooks automatically
25
+ # So if this function is called, it means a slash command was invoked
26
+
27
+ # Check if we have the necessary TTS setup
28
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
29
+
30
+ # Check if agent-vibes output style is installed
31
+ if [[ ! -f "$SCRIPT_DIR/../output-styles/agent-vibes.md" ]]; then
32
+ return 1
33
+ fi
34
+
35
+ # All checks passed - likely using agent-vibes output style
36
+ return 0
37
+ }
38
+
39
+ # Show warning if output style is not agent-vibes
40
+ show_output_style_warning() {
41
+ echo ""
42
+ echo "⚠️ Voice commands require the agent-vibes output style"
43
+ echo ""
44
+ echo "To enable voice narration, run:"
45
+ echo " /output-style agent-vibes"
46
+ echo ""
47
+ echo "This will make Claude speak with TTS for all responses."
48
+ echo "You can still use voice commands manually with agent-vibes disabled,"
49
+ echo "but you won't hear automatic TTS narration."
50
+ echo ""
51
+ }
52
+
53
+ # Main execution when called directly
54
+ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
55
+ if ! check_output_style; then
56
+ show_output_style_warning
57
+ exit 1
58
+ fi
59
+ exit 0
60
+ fi
@@ -139,52 +139,54 @@ get_best_voice_for_language() {
139
139
  echo "${LANGUAGE_VOICES[$lang]}"
140
140
  }
141
141
 
142
- # Main command handler
143
- case "${1:-}" in
144
- set)
145
- if [[ -z "$2" ]]; then
146
- echo "Usage: language-manager.sh set <language>"
142
+ # Main command handler - only run if script is executed directly, not sourced
143
+ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
144
+ case "${1:-}" in
145
+ set)
146
+ if [[ -z "$2" ]]; then
147
+ echo "Usage: language-manager.sh set <language>"
148
+ exit 1
149
+ fi
150
+ set_language "$2"
151
+ ;;
152
+ get)
153
+ get_language
154
+ ;;
155
+ code)
156
+ get_language_code
157
+ ;;
158
+ check-voice)
159
+ if [[ -z "$2" ]]; then
160
+ echo "Usage: language-manager.sh check-voice <voice-name>"
161
+ exit 1
162
+ fi
163
+ if is_voice_multilingual "$2"; then
164
+ echo "yes"
165
+ else
166
+ echo "no"
167
+ fi
168
+ ;;
169
+ best-voice)
170
+ get_best_voice_for_language
171
+ ;;
172
+ list)
173
+ echo "Supported languages and recommended voices:"
174
+ echo ""
175
+ for lang in "${!LANGUAGE_VOICES[@]}"; do
176
+ printf "%-15s → %s\n" "$lang" "${LANGUAGE_VOICES[$lang]}"
177
+ done | sort
178
+ ;;
179
+ *)
180
+ echo "AgentVibes Language Manager"
181
+ echo ""
182
+ echo "Usage:"
183
+ echo " language-manager.sh set <language> Set language"
184
+ echo " language-manager.sh get Get current language"
185
+ echo " language-manager.sh code Get language code only"
186
+ echo " language-manager.sh check-voice <name> Check if voice is multilingual"
187
+ echo " language-manager.sh best-voice Get best voice for current language"
188
+ echo " language-manager.sh list List all supported languages"
147
189
  exit 1
148
- fi
149
- set_language "$2"
150
- ;;
151
- get)
152
- get_language
153
- ;;
154
- code)
155
- get_language_code
156
- ;;
157
- check-voice)
158
- if [[ -z "$2" ]]; then
159
- echo "Usage: language-manager.sh check-voice <voice-name>"
160
- exit 1
161
- fi
162
- if is_voice_multilingual "$2"; then
163
- echo "yes"
164
- else
165
- echo "no"
166
- fi
167
- ;;
168
- best-voice)
169
- get_best_voice_for_language
170
- ;;
171
- list)
172
- echo "Supported languages and recommended voices:"
173
- echo ""
174
- for lang in "${!LANGUAGE_VOICES[@]}"; do
175
- printf "%-15s → %s\n" "$lang" "${LANGUAGE_VOICES[$lang]}"
176
- done | sort
177
- ;;
178
- *)
179
- echo "AgentVibes Language Manager"
180
- echo ""
181
- echo "Usage:"
182
- echo " language-manager.sh set <language> Set language"
183
- echo " language-manager.sh get Get current language"
184
- echo " language-manager.sh code Get language code only"
185
- echo " language-manager.sh check-voice <name> Check if voice is multilingual"
186
- echo " language-manager.sh best-voice Get best voice for current language"
187
- echo " language-manager.sh list List all supported languages"
188
- exit 1
189
- ;;
190
- esac
190
+ ;;
191
+ esac
192
+ fi
@@ -37,7 +37,10 @@ get_personality_data() {
37
37
  grep "^description:" "$file" | cut -d: -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'
38
38
  ;;
39
39
  voice)
40
- grep "^voice:" "$file" | cut -d: -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'
40
+ grep "^elevenlabs_voice:" "$file" | cut -d: -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'
41
+ ;;
42
+ piper_voice)
43
+ grep "^piper_voice:" "$file" | cut -d: -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'
41
44
  ;;
42
45
  instructions)
43
46
  sed -n '/^## AI Instructions/,/^##/p' "$file" | sed '1d;$d'
@@ -122,7 +125,33 @@ case "$1" in
122
125
  echo "🎭 Personality set to: $PERSONALITY"
123
126
 
124
127
  # Check if personality has an assigned voice
125
- ASSIGNED_VOICE=$(get_personality_data "$PERSONALITY" "voice")
128
+ # Detect active TTS provider
129
+ PROVIDER_FILE=""
130
+ if [[ -f "$CLAUDE_DIR/tts-provider.txt" ]]; then
131
+ PROVIDER_FILE="$CLAUDE_DIR/tts-provider.txt"
132
+ elif [[ -f "$HOME/.claude/tts-provider.txt" ]]; then
133
+ PROVIDER_FILE="$HOME/.claude/tts-provider.txt"
134
+ fi
135
+
136
+ ACTIVE_PROVIDER="elevenlabs" # default
137
+ if [[ -n "$PROVIDER_FILE" ]]; then
138
+ ACTIVE_PROVIDER=$(cat "$PROVIDER_FILE")
139
+ fi
140
+
141
+ # Get the appropriate voice based on provider
142
+ ASSIGNED_VOICE=""
143
+ if [[ "$ACTIVE_PROVIDER" == "piper" ]]; then
144
+ # Try to get Piper-specific voice first
145
+ ASSIGNED_VOICE=$(get_personality_data "$PERSONALITY" "piper_voice")
146
+ if [[ -z "$ASSIGNED_VOICE" ]]; then
147
+ # Fallback to default Piper voice
148
+ ASSIGNED_VOICE="en_US-lessac-medium"
149
+ fi
150
+ else
151
+ # Use ElevenLabs voice (reads from elevenlabs_voice: field)
152
+ ASSIGNED_VOICE=$(get_personality_data "$PERSONALITY" "voice")
153
+ fi
154
+
126
155
  if [[ -n "$ASSIGNED_VOICE" ]]; then
127
156
  # Switch to the assigned voice (silently - personality will do the talking)
128
157
  VOICE_MANAGER="$SCRIPT_DIR/voice-manager.sh"
@@ -163,6 +192,9 @@ case "$1" in
163
192
 
164
193
  echo ""
165
194
  echo "Note: AI will generate unique ${PERSONALITY} responses - no fixed templates!"
195
+ echo ""
196
+ echo "💡 Tip: To hear automatic TTS narration, enable the agent-vibes output style:"
197
+ echo " /output-style agent-vibes"
166
198
  fi
167
199
  ;;
168
200
 
@@ -32,22 +32,40 @@ DEFAULT_VOICE="en_US-lessac-medium"
32
32
  VOICE_MODEL=""
33
33
 
34
34
  # Get current language setting
35
- CURRENT_LANGUAGE=$(get_current_language)
35
+ CURRENT_LANGUAGE=$(get_language_code)
36
36
 
37
37
  if [[ -n "$VOICE_OVERRIDE" ]]; then
38
38
  # Use override if provided
39
39
  VOICE_MODEL="$VOICE_OVERRIDE"
40
40
  echo "🎤 Using voice: $VOICE_OVERRIDE (session-specific)"
41
41
  else
42
- # Try to get language-specific voice
43
- LANG_VOICE=$(get_voice_for_language "$CURRENT_LANGUAGE" "piper" 2>/dev/null)
42
+ # Try to get voice from voice file (project-local first, then global)
43
+ VOICE_FILE=""
44
+ if [[ -f "$SCRIPT_DIR/../tts-voice.txt" ]]; then
45
+ VOICE_FILE="$SCRIPT_DIR/../tts-voice.txt"
46
+ elif [[ -f "$HOME/.claude/tts-voice.txt" ]]; then
47
+ VOICE_FILE="$HOME/.claude/tts-voice.txt"
48
+ fi
44
49
 
45
- if [[ -n "$LANG_VOICE" ]]; then
46
- VOICE_MODEL="$LANG_VOICE"
47
- echo "🌍 Using $CURRENT_LANGUAGE voice: $LANG_VOICE (Piper)"
48
- else
49
- # Use default voice
50
- VOICE_MODEL="$DEFAULT_VOICE"
50
+ if [[ -n "$VOICE_FILE" ]]; then
51
+ FILE_VOICE=$(cat "$VOICE_FILE" 2>/dev/null)
52
+ # Check if it's a Piper model name (contains underscore and dash)
53
+ if [[ "$FILE_VOICE" == *"_"*"-"* ]]; then
54
+ VOICE_MODEL="$FILE_VOICE"
55
+ fi
56
+ fi
57
+
58
+ # If no Piper voice from file, try language-specific voice
59
+ if [[ -z "$VOICE_MODEL" ]]; then
60
+ LANG_VOICE=$(get_voice_for_language "$CURRENT_LANGUAGE" "piper" 2>/dev/null)
61
+
62
+ if [[ -n "$LANG_VOICE" ]]; then
63
+ VOICE_MODEL="$LANG_VOICE"
64
+ echo "🌍 Using $CURRENT_LANGUAGE voice: $LANG_VOICE (Piper)"
65
+ else
66
+ # Use default voice
67
+ VOICE_MODEL="$DEFAULT_VOICE"
68
+ fi
51
69
  fi
52
70
  fi
53
71
 
@@ -1,138 +1,40 @@
1
1
  #!/bin/bash
2
- # Quick TTS playback script with session-specific voice support
3
- # Usage: play-tts.sh "Text to speak" [voice_name_or_id]
4
2
  #
5
- # Examples:
6
- # play-tts.sh "Hello world" # Uses default voice from voice manager
7
- # play-tts.sh "Hello world" "Sarah" # Uses Sarah voice by name
8
- # play-tts.sh "Hello world" "KTPVrSVAEUSJRClDzBw7" # Uses voice by direct ID
3
+ # @fileoverview TTS Provider Router
4
+ # @context Routes TTS requests to active provider (ElevenLabs or Piper)
5
+ # @architecture Provider abstraction layer - single entry point for all TTS
6
+ # @dependencies provider-manager.sh, play-tts-elevenlabs.sh, play-tts-piper.sh
7
+ # @entrypoints Called by hooks, slash commands, and personality-manager.sh
8
+ # @patterns Provider pattern - delegates to provider-specific implementations
9
+ # @related provider-manager.sh, play-tts-elevenlabs.sh, play-tts-piper.sh
9
10
  #
10
- # This allows different sessions to use different voices for easy identification!
11
11
 
12
12
  # Fix locale warnings
13
13
  export LC_ALL=C
14
14
 
15
15
  TEXT="$1"
16
- VOICE_OVERRIDE="$2" # Optional: voice name or direct voice ID
17
- API_KEY="${ELEVENLABS_API_KEY}"
16
+ VOICE_OVERRIDE="$2" # Optional: voice name or ID
18
17
 
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
18
+ # Get script directory
37
19
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
38
- source "$SCRIPT_DIR/voices-config.sh"
39
-
40
- # Determine which voice to use
41
- VOICE_ID=""
42
-
43
- if [[ -n "$VOICE_OVERRIDE" ]]; then
44
- # Check if override is a voice name (lookup in mapping)
45
- if [[ -n "${VOICES[$VOICE_OVERRIDE]}" ]]; then
46
- VOICE_ID="${VOICES[$VOICE_OVERRIDE]}"
47
- echo "🎤 Using voice: $VOICE_OVERRIDE (session-specific)"
48
- # Check if override looks like a voice ID (alphanumeric string ~20 chars)
49
- elif [[ "$VOICE_OVERRIDE" =~ ^[a-zA-Z0-9]{15,30}$ ]]; then
50
- VOICE_ID="$VOICE_OVERRIDE"
51
- echo "🎤 Using custom voice ID (session-specific)"
52
- else
53
- echo "⚠️ Unknown voice '$VOICE_OVERRIDE', using default"
54
- fi
55
- fi
56
-
57
- # If no override or invalid override, use default from voice manager
58
- if [[ -z "$VOICE_ID" ]]; then
59
- VOICE_MANAGER_SCRIPT="$(dirname "$0")/voice-manager.sh"
60
- if [[ -f "$VOICE_MANAGER_SCRIPT" ]]; then
61
- VOICE_NAME=$("$VOICE_MANAGER_SCRIPT" get)
62
- VOICE_ID="${VOICES[$VOICE_NAME]}"
63
- fi
64
-
65
- # Final fallback to Cowboy Bob default
66
- if [[ -z "$VOICE_ID" ]]; then
67
- echo "⚠️ No voice configured, using Cowboy Bob default"
68
- VOICE_ID="${VOICES[Cowboy Bob]}"
69
- fi
70
- fi
71
-
72
- if [ -z "$TEXT" ]; then
73
- echo "Usage: $0 \"text to speak\""
74
- exit 1
75
- fi
76
-
77
- if [ -z "$API_KEY" ]; then
78
- echo "Error: ELEVENLABS_API_KEY not set"
79
- exit 1
80
- fi
81
-
82
- # Create audio file in project-local storage
83
- # Use project directory if available, otherwise fall back to global
84
- if [[ -n "$CLAUDE_PROJECT_DIR" ]]; then
85
- AUDIO_DIR="$CLAUDE_PROJECT_DIR/.claude/audio"
86
- else
87
- # Fallback: try to find .claude directory in current path
88
- CURRENT_DIR="$PWD"
89
- while [[ "$CURRENT_DIR" != "/" ]]; do
90
- if [[ -d "$CURRENT_DIR/.claude" ]]; then
91
- AUDIO_DIR="$CURRENT_DIR/.claude/audio"
92
- break
93
- fi
94
- CURRENT_DIR=$(dirname "$CURRENT_DIR")
95
- done
96
- # Final fallback to global if no project .claude found
97
- if [[ -z "$AUDIO_DIR" ]]; then
98
- AUDIO_DIR="$HOME/.claude/audio"
99
- fi
100
- fi
101
-
102
- mkdir -p "$AUDIO_DIR"
103
- TEMP_FILE="$AUDIO_DIR/tts-$(date +%s).mp3"
104
-
105
- # Generate audio
106
- curl -s -X POST "https://api.elevenlabs.io/v1/text-to-speech/${VOICE_ID}" \
107
- -H "xi-api-key: ${API_KEY}" \
108
- -H "Content-Type: application/json" \
109
- -d "{\"text\":\"${TEXT}\",\"model_id\":\"eleven_monolingual_v1\",\"voice_settings\":{\"stability\":0.5,\"similarity_boost\":0.75}}" \
110
- -o "${TEMP_FILE}"
111
-
112
- # Add silence padding to prevent WSL audio static
113
- if [ -f "${TEMP_FILE}" ]; then
114
- # Check if ffmpeg is available for adding padding
115
- if command -v ffmpeg &> /dev/null; then
116
- PADDED_FILE="$AUDIO_DIR/tts-padded-$(date +%s).mp3"
117
- # Add 200ms of silence at the beginning to prevent static
118
- ffmpeg -f lavfi -i anullsrc=r=44100:cl=stereo:d=0.2 -i "${TEMP_FILE}" \
119
- -filter_complex "[0:a][1:a]concat=n=2:v=0:a=1[out]" \
120
- -map "[out]" -y "${PADDED_FILE}" 2>/dev/null
121
-
122
- if [ -f "${PADDED_FILE}" ]; then
123
- # Use padded file and clean up original
124
- rm -f "${TEMP_FILE}"
125
- TEMP_FILE="${PADDED_FILE}"
126
- fi
127
- # If padding failed, just use original file
128
- fi
129
20
 
130
- # Play audio (WSL/Linux) in background to avoid blocking
131
- (paplay "${TEMP_FILE}" 2>/dev/null || aplay "${TEMP_FILE}" 2>/dev/null || mpg123 "${TEMP_FILE}" 2>/dev/null) &
132
- # Keep temp files for later review - cleaned up weekly by cron
133
- echo "🎵 Saved to: ${TEMP_FILE}"
134
- echo "🎤 Voice used: ${VOICE_NAME} (${VOICE_ID})"
135
- else
136
- echo "Failed to generate audio"
137
- exit 1
138
- fi
21
+ # Source provider manager to get active provider
22
+ source "$SCRIPT_DIR/provider-manager.sh"
23
+
24
+ # Get active provider
25
+ ACTIVE_PROVIDER=$(get_active_provider)
26
+
27
+ # Route to appropriate provider implementation
28
+ case "$ACTIVE_PROVIDER" in
29
+ elevenlabs)
30
+ exec "$SCRIPT_DIR/play-tts-elevenlabs.sh" "$TEXT" "$VOICE_OVERRIDE"
31
+ ;;
32
+ piper)
33
+ exec "$SCRIPT_DIR/play-tts-piper.sh" "$TEXT" "$VOICE_OVERRIDE"
34
+ ;;
35
+ *)
36
+ echo "❌ Unknown provider: $ACTIVE_PROVIDER"
37
+ echo " Run: /agent-vibes:provider list"
38
+ exit 1
39
+ ;;
40
+ esac
@@ -292,10 +292,49 @@ provider_preview() {
292
292
  ;;
293
293
  piper)
294
294
  # Use the Piper voice manager's list functionality
295
- # For Piper, we'll list and play sample from available voices
296
295
  source "$SCRIPT_DIR/piper-voice-manager.sh"
297
296
 
298
- # Play intro announcement
297
+ # Check if a specific voice was requested
298
+ local voice_arg="$1"
299
+
300
+ if [[ -n "$voice_arg" ]]; then
301
+ # User requested a specific voice - check if it's a valid Piper voice
302
+ # Piper voice names are like: en_US-lessac-medium
303
+ # Try to find a matching voice model
304
+
305
+ # Check if the voice arg looks like a Piper model name (contains underscores/hyphens)
306
+ if [[ "$voice_arg" =~ ^[a-z]{2}_[A-Z]{2}- ]]; then
307
+ # Looks like a Piper voice model name
308
+ if verify_voice "$voice_arg"; then
309
+ echo "🎤 Previewing Piper voice: $voice_arg"
310
+ echo ""
311
+ "$SCRIPT_DIR/play-tts.sh" "Hello, this is the $voice_arg voice. How do you like it?" "$voice_arg"
312
+ else
313
+ echo "❌ Voice model not found: $voice_arg"
314
+ echo ""
315
+ echo "💡 Piper voice names look like: en_US-lessac-medium"
316
+ echo " Run /agent-vibes:list to see available Piper voices"
317
+ fi
318
+ else
319
+ # Looks like an ElevenLabs voice name (like "Antoni", "Jessica")
320
+ echo "❌ '$voice_arg' appears to be an ElevenLabs voice"
321
+ echo ""
322
+ echo "You're currently using Piper TTS (free provider)."
323
+ echo "Piper has different voices than ElevenLabs."
324
+ echo ""
325
+ echo "Options:"
326
+ echo " 1. Run /agent-vibes:list to see available Piper voices"
327
+ echo " 2. Switch to ElevenLabs: /agent-vibes:provider switch elevenlabs"
328
+ echo ""
329
+ echo "Popular Piper voices to try:"
330
+ echo " • en_US-lessac-medium (clear, professional)"
331
+ echo " • en_US-amy-medium (warm, friendly)"
332
+ echo " • en_US-joe-medium (casual, natural)"
333
+ fi
334
+ return
335
+ fi
336
+
337
+ # No specific voice - preview first 3 voices
299
338
  echo "🎤 Piper Preview of 3 people"
300
339
  echo ""
301
340
 
@@ -163,52 +163,77 @@ case "$1" in
163
163
  exit 0
164
164
  fi
165
165
 
166
- # Check if input is a number
167
- if [[ "$VOICE_NAME" =~ ^[0-9]+$ ]]; then
168
- # Get voice array
169
- VOICE_ARRAY=()
170
- for voice in "${!VOICES[@]}"; do
171
- VOICE_ARRAY+=("$voice")
172
- done
166
+ # Detect active TTS provider
167
+ PROVIDER_FILE=""
168
+ if [[ -f "$CLAUDE_DIR/tts-provider.txt" ]]; then
169
+ PROVIDER_FILE="$CLAUDE_DIR/tts-provider.txt"
170
+ elif [[ -f "$HOME/.claude/tts-provider.txt" ]]; then
171
+ PROVIDER_FILE="$HOME/.claude/tts-provider.txt"
172
+ fi
173
173
 
174
- # Sort the array
175
- IFS=$'\n' SORTED_VOICES=($(sort <<<"${VOICE_ARRAY[*]}"))
176
- unset IFS
174
+ ACTIVE_PROVIDER="elevenlabs" # default
175
+ if [[ -n "$PROVIDER_FILE" ]]; then
176
+ ACTIVE_PROVIDER=$(cat "$PROVIDER_FILE")
177
+ fi
178
+
179
+ # If using Piper and voice name looks like a Piper model (contains underscore and dash)
180
+ # then skip ElevenLabs voice validation
181
+ if [[ "$ACTIVE_PROVIDER" == "piper" ]] && [[ "$VOICE_NAME" == *"_"*"-"* ]]; then
182
+ # This is a Piper model name, use it directly
183
+ FOUND="$VOICE_NAME"
184
+ else
185
+ # ElevenLabs voice lookup
186
+ # Check if input is a number
187
+ if [[ "$VOICE_NAME" =~ ^[0-9]+$ ]]; then
188
+ # Get voice array
189
+ VOICE_ARRAY=()
190
+ for voice in "${!VOICES[@]}"; do
191
+ VOICE_ARRAY+=("$voice")
192
+ done
193
+
194
+ # Sort the array
195
+ IFS=$'\n' SORTED_VOICES=($(sort <<<"${VOICE_ARRAY[*]}"))
196
+ unset IFS
177
197
 
178
- # Get voice by number (adjust for 0-based index)
179
- INDEX=$((VOICE_NAME - 1))
198
+ # Get voice by number (adjust for 0-based index)
199
+ INDEX=$((VOICE_NAME - 1))
180
200
 
181
- if [[ $INDEX -ge 0 && $INDEX -lt ${#SORTED_VOICES[@]} ]]; then
182
- VOICE_NAME="${SORTED_VOICES[$INDEX]}"
183
- FOUND="${SORTED_VOICES[$INDEX]}"
201
+ if [[ $INDEX -ge 0 && $INDEX -lt ${#SORTED_VOICES[@]} ]]; then
202
+ VOICE_NAME="${SORTED_VOICES[$INDEX]}"
203
+ FOUND="${SORTED_VOICES[$INDEX]}"
204
+ else
205
+ echo "❌ Invalid number. Please choose between 1 and ${#SORTED_VOICES[@]}"
206
+ exit 1
207
+ fi
184
208
  else
185
- echo "❌ Invalid number. Please choose between 1 and ${#SORTED_VOICES[@]}"
186
- exit 1
209
+ # Check if voice exists (case-insensitive)
210
+ FOUND=""
211
+ for voice in "${!VOICES[@]}"; do
212
+ if [[ "${voice,,}" == "${VOICE_NAME,,}" ]]; then
213
+ FOUND="$voice"
214
+ break
215
+ fi
216
+ done
187
217
  fi
188
- else
189
- # Check if voice exists (case-insensitive)
190
- FOUND=""
191
- for voice in "${!VOICES[@]}"; do
192
- if [[ "${voice,,}" == "${VOICE_NAME,,}" ]]; then
193
- FOUND="$voice"
194
- break
195
- fi
196
- done
197
- fi
198
218
 
199
- if [[ -z "$FOUND" ]]; then
200
- echo "❌ Unknown voice: $VOICE_NAME"
201
- echo ""
202
- echo "Available voices:"
203
- for voice in "${!VOICES[@]}"; do
204
- echo " - $voice"
205
- done | sort
206
- exit 1
219
+ if [[ -z "$FOUND" ]]; then
220
+ echo "❌ Unknown voice: $VOICE_NAME"
221
+ echo ""
222
+ echo "Available voices:"
223
+ for voice in "${!VOICES[@]}"; do
224
+ echo " - $voice"
225
+ done | sort
226
+ exit 1
227
+ fi
207
228
  fi
208
229
 
209
230
  echo "$FOUND" > "$VOICE_FILE"
210
231
  echo "✅ Voice switched to: $FOUND"
211
- echo "🎤 Voice ID: ${VOICES[$FOUND]}"
232
+
233
+ # Show voice ID only for ElevenLabs voices
234
+ if [[ "$ACTIVE_PROVIDER" != "piper" ]] && [[ -n "${VOICES[$FOUND]}" ]]; then
235
+ echo "🎤 Voice ID: ${VOICES[$FOUND]}"
236
+ fi
212
237
 
213
238
  # Have the new voice introduce itself (unless silent mode)
214
239
  if [[ "$SILENT_MODE" != "true" ]]; then
@@ -217,6 +242,10 @@ case "$1" in
217
242
  if [ -x "$PLAY_TTS" ]; then
218
243
  "$PLAY_TTS" "Hi, I'm $FOUND. I'll be your voice assistant moving forward." "$FOUND" > /dev/null 2>&1 &
219
244
  fi
245
+
246
+ echo ""
247
+ echo "💡 Tip: To hear automatic TTS narration, enable the agent-vibes output style:"
248
+ echo " /output-style agent-vibes"
220
249
  fi
221
250
  ;;
222
251
 
@@ -232,6 +261,26 @@ case "$1" in
232
261
  echo "🎤 Current Voice Configuration"
233
262
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
234
263
 
264
+ # Get active TTS provider
265
+ PROVIDER_FILE="$CLAUDE_DIR/tts-provider.txt"
266
+ if [[ ! -f "$PROVIDER_FILE" ]]; then
267
+ PROVIDER_FILE="$HOME/.claude/tts-provider.txt"
268
+ fi
269
+
270
+ if [ -f "$PROVIDER_FILE" ]; then
271
+ ACTIVE_PROVIDER=$(cat "$PROVIDER_FILE")
272
+ if [[ "$ACTIVE_PROVIDER" == "elevenlabs" ]]; then
273
+ echo "Provider: ElevenLabs (Premium AI)"
274
+ elif [[ "$ACTIVE_PROVIDER" == "piper" ]]; then
275
+ echo "Provider: Piper TTS (Free, Offline)"
276
+ else
277
+ echo "Provider: $ACTIVE_PROVIDER"
278
+ fi
279
+ else
280
+ # Default to ElevenLabs if no provider file
281
+ echo "Provider: ElevenLabs (Premium AI)"
282
+ fi
283
+
235
284
  # Get current voice
236
285
  if [ -f "$VOICE_FILE" ]; then
237
286
  CURRENT_VOICE=$(cat "$VOICE_FILE")