agentvibes 2.0.3 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/agent-vibes/provider.md +54 -0
- package/.claude/hooks/piper-download-voices.sh +133 -0
- package/.claude/hooks/piper-voice-manager.sh +194 -0
- package/.claude/hooks/play-tts-elevenlabs.sh +201 -0
- package/.claude/hooks/play-tts-piper.sh +175 -0
- package/.claude/hooks/play-tts.sh.backup-20251005-163851 +138 -0
- package/.claude/hooks/provider-commands.sh +374 -0
- package/.claude/hooks/provider-manager.sh +196 -0
- package/.claude/language-voices.yaml +372 -0
- package/.claude/piper-voices/en_US-lessac-medium.onnx +0 -0
- package/.claude/piper-voices/en_US-lessac-medium.onnx.json +493 -0
- package/.mcp-minimal.json +53 -0
- package/agentvibes.org/.claude/commands/agent-vibes/add.md +21 -0
- package/agentvibes.org/.claude/commands/agent-vibes/agent-vibes.md +68 -0
- package/agentvibes.org/.claude/commands/agent-vibes/commands.json +53 -0
- package/agentvibes.org/.claude/commands/agent-vibes/get.md +9 -0
- package/agentvibes.org/.claude/commands/agent-vibes/list.md +13 -0
- package/agentvibes.org/.claude/commands/agent-vibes/personality.md +79 -0
- package/agentvibes.org/.claude/commands/agent-vibes/preview.md +16 -0
- package/agentvibes.org/.claude/commands/agent-vibes/provider.md +54 -0
- package/agentvibes.org/.claude/commands/agent-vibes/replay.md +19 -0
- package/agentvibes.org/.claude/commands/agent-vibes/sample.md +12 -0
- package/agentvibes.org/.claude/commands/agent-vibes/sentiment.md +52 -0
- package/agentvibes.org/.claude/commands/agent-vibes/set-language.md +47 -0
- package/agentvibes.org/.claude/commands/agent-vibes/set-pretext.md +65 -0
- package/agentvibes.org/.claude/commands/agent-vibes/switch.md +53 -0
- package/agentvibes.org/.claude/commands/agent-vibes/update.md +20 -0
- package/agentvibes.org/.claude/commands/agent-vibes/version.md +10 -0
- package/agentvibes.org/.claude/commands/agent-vibes/whoami.md +7 -0
- package/agentvibes.org/.claude/journal/2025-10-07.html +373 -0
- package/agentvibes.org/.claude/journal/index.html +91 -0
- package/agentvibes.org/.mcp-minimal.json +60 -0
- package/agentvibes.org/CHANGELOG.md +56 -0
- package/agentvibes.org/README.md +93 -0
- package/agentvibes.org/app/(auth)/layout.tsx +15 -0
- package/agentvibes.org/app/(auth)/reset-password/page.tsx +45 -0
- package/agentvibes.org/app/(auth)/signin/page.tsx +82 -0
- package/agentvibes.org/app/(auth)/signup/page.tsx +104 -0
- package/agentvibes.org/app/(default)/layout.tsx +31 -0
- package/agentvibes.org/app/(default)/page.tsx +20 -0
- package/agentvibes.org/app/api/hello/route.ts +3 -0
- package/agentvibes.org/app/css/additional-styles/theme.css +82 -0
- package/agentvibes.org/app/css/additional-styles/utility-patterns.css +55 -0
- package/agentvibes.org/app/css/style.css +100 -0
- package/agentvibes.org/app/layout.tsx +63 -0
- package/agentvibes.org/components/cta.tsx +58 -0
- package/agentvibes.org/components/features.tsx +256 -0
- package/agentvibes.org/components/hero-home.tsx +133 -0
- package/agentvibes.org/components/modal-video.tsx +137 -0
- package/agentvibes.org/components/page-illustration.tsx +55 -0
- package/agentvibes.org/components/spotlight.tsx +77 -0
- package/agentvibes.org/components/testimonials.tsx +282 -0
- package/agentvibes.org/components/ui/footer.tsx +82 -0
- package/agentvibes.org/components/ui/header.tsx +53 -0
- package/agentvibes.org/components/ui/logo.tsx +10 -0
- package/agentvibes.org/components/workflows.tsx +176 -0
- package/agentvibes.org/next.config.js +4 -0
- package/agentvibes.org/package-lock.json +1974 -0
- package/agentvibes.org/package.json +30 -0
- package/agentvibes.org/pnpm-lock.yaml +1141 -0
- package/agentvibes.org/postcss.config.js +5 -0
- package/agentvibes.org/public/audio/02-sarcastic.mp3 +0 -0
- package/agentvibes.org/public/audio/03-angry.mp3 +0 -0
- package/agentvibes.org/public/audio/04-grandpa.mp3 +0 -0
- package/agentvibes.org/public/audio/05-sarcastic-example2.mp3 +0 -0
- package/agentvibes.org/public/audio/french-rachel.mp3 +0 -0
- package/agentvibes.org/public/audio/spanish-antoni.mp3 +0 -0
- package/agentvibes.org/public/favicon.ico +0 -0
- package/agentvibes.org/public/fonts/nacelle-italic.woff2 +0 -0
- package/agentvibes.org/public/fonts/nacelle-regular.woff2 +0 -0
- package/agentvibes.org/public/fonts/nacelle-semibold.woff2 +0 -0
- package/agentvibes.org/public/fonts/nacelle-semibolditalic.woff2 +0 -0
- package/agentvibes.org/public/images/blurred-shape-gray.svg +1 -0
- package/agentvibes.org/public/images/blurred-shape.svg +1 -0
- package/agentvibes.org/public/images/client-logo-01.svg +1 -0
- package/agentvibes.org/public/images/client-logo-02.svg +1 -0
- package/agentvibes.org/public/images/client-logo-03.svg +1 -0
- package/agentvibes.org/public/images/client-logo-04.svg +1 -0
- package/agentvibes.org/public/images/client-logo-05.svg +1 -0
- package/agentvibes.org/public/images/client-logo-06.svg +1 -0
- package/agentvibes.org/public/images/client-logo-07.svg +1 -0
- package/agentvibes.org/public/images/client-logo-08.svg +1 -0
- package/agentvibes.org/public/images/client-logo-09.svg +1 -0
- package/agentvibes.org/public/images/features.png +0 -0
- package/agentvibes.org/public/images/footer-illustration.svg +1 -0
- package/agentvibes.org/public/images/hero-image-01.jpg +0 -0
- package/agentvibes.org/public/images/logo.svg +1 -0
- package/agentvibes.org/public/images/page-illustration.svg +1 -0
- package/agentvibes.org/public/images/secondary-illustration.svg +1 -0
- package/agentvibes.org/public/images/testimonial-01.jpg +0 -0
- package/agentvibes.org/public/images/testimonial-02.jpg +0 -0
- package/agentvibes.org/public/images/testimonial-03.jpg +0 -0
- package/agentvibes.org/public/images/testimonial-04.jpg +0 -0
- package/agentvibes.org/public/images/testimonial-05.jpg +0 -0
- package/agentvibes.org/public/images/testimonial-06.jpg +0 -0
- package/agentvibes.org/public/images/testimonial-07.jpg +0 -0
- package/agentvibes.org/public/images/testimonial-08.jpg +0 -0
- package/agentvibes.org/public/images/testimonial-09.jpg +0 -0
- package/agentvibes.org/public/images/workflow-01.png +0 -0
- package/agentvibes.org/public/images/workflow-02.png +0 -0
- package/agentvibes.org/public/images/workflow-03.png +0 -0
- package/agentvibes.org/public/videos/video.mp4 +0 -0
- package/agentvibes.org/tsconfig.json +28 -0
- package/agentvibes.org/utils/useMasonry.tsx +67 -0
- package/agentvibes.org/utils/useMousePosition.tsx +27 -0
- package/docs/ai-optimized-documentation-standards.md +306 -0
- package/docs/architecture/provider-system.md +574 -0
- package/docs/voice-mapping-format.md +218 -0
- package/package.json +1 -1
- package/scripts/piper-voice/README.md +145 -0
- package/scripts/piper-voice/wsl-install.sh +193 -0
- package/src/installer.js +4 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# @fileoverview Piper TTS Provider Implementation
|
|
4
|
+
# @context Free, offline neural TTS for WSL/Linux
|
|
5
|
+
# @architecture Implements provider contract for Piper binary
|
|
6
|
+
# @dependencies piper (pipx), piper-voice-manager.sh, mpv/aplay
|
|
7
|
+
# @entrypoints Called by play-tts.sh router
|
|
8
|
+
# @patterns Provider contract: text/voice → audio file path
|
|
9
|
+
# @related play-tts.sh, piper-voice-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 model name
|
|
17
|
+
|
|
18
|
+
# Source voice manager and language manager
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
+
source "$SCRIPT_DIR/piper-voice-manager.sh"
|
|
21
|
+
source "$SCRIPT_DIR/language-manager.sh"
|
|
22
|
+
|
|
23
|
+
# Default voice for Piper
|
|
24
|
+
DEFAULT_VOICE="en_US-lessac-medium"
|
|
25
|
+
|
|
26
|
+
# @function determine_voice_model
|
|
27
|
+
# @intent Resolve voice name to Piper model name with language support
|
|
28
|
+
# @why Support voice override, language-specific voices, and default fallback
|
|
29
|
+
# @param Uses global: $VOICE_OVERRIDE
|
|
30
|
+
# @returns Sets $VOICE_MODEL global variable
|
|
31
|
+
# @sideeffects None
|
|
32
|
+
VOICE_MODEL=""
|
|
33
|
+
|
|
34
|
+
# Get current language setting
|
|
35
|
+
CURRENT_LANGUAGE=$(get_current_language)
|
|
36
|
+
|
|
37
|
+
if [[ -n "$VOICE_OVERRIDE" ]]; then
|
|
38
|
+
# Use override if provided
|
|
39
|
+
VOICE_MODEL="$VOICE_OVERRIDE"
|
|
40
|
+
echo "🎤 Using voice: $VOICE_OVERRIDE (session-specific)"
|
|
41
|
+
else
|
|
42
|
+
# Try to get language-specific voice
|
|
43
|
+
LANG_VOICE=$(get_voice_for_language "$CURRENT_LANGUAGE" "piper" 2>/dev/null)
|
|
44
|
+
|
|
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"
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# @function validate_inputs
|
|
55
|
+
# @intent Check required parameters
|
|
56
|
+
# @why Fail fast with clear errors if inputs missing
|
|
57
|
+
# @exitcode 1=missing text, 2=missing piper binary
|
|
58
|
+
if [[ -z "$TEXT" ]]; then
|
|
59
|
+
echo "Usage: $0 \"text to speak\" [voice_model_name]"
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# Check if Piper is installed
|
|
64
|
+
if ! command -v piper &> /dev/null; then
|
|
65
|
+
echo "❌ Error: Piper TTS not installed"
|
|
66
|
+
echo "Install with: pipx install piper-tts"
|
|
67
|
+
echo "Or run: .claude/hooks/piper-installer.sh"
|
|
68
|
+
exit 2
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# @function ensure_voice_downloaded
|
|
72
|
+
# @intent Download voice model if not cached
|
|
73
|
+
# @why Provide seamless experience with automatic downloads
|
|
74
|
+
# @param Uses global: $VOICE_MODEL
|
|
75
|
+
# @sideeffects Downloads voice model files
|
|
76
|
+
# @edgecases Prompts user for consent before downloading
|
|
77
|
+
if ! verify_voice "$VOICE_MODEL"; then
|
|
78
|
+
echo "📥 Voice model not found: $VOICE_MODEL"
|
|
79
|
+
echo " File size: ~25MB"
|
|
80
|
+
echo " Preview: https://huggingface.co/rhasspy/piper-voices"
|
|
81
|
+
echo ""
|
|
82
|
+
read -p " Download this voice model? [y/N]: " -n 1 -r
|
|
83
|
+
echo
|
|
84
|
+
|
|
85
|
+
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
86
|
+
if ! download_voice "$VOICE_MODEL"; then
|
|
87
|
+
echo "❌ Failed to download voice model"
|
|
88
|
+
echo "Fix: Download manually or choose different voice"
|
|
89
|
+
exit 3
|
|
90
|
+
fi
|
|
91
|
+
else
|
|
92
|
+
echo "❌ Voice download cancelled"
|
|
93
|
+
exit 3
|
|
94
|
+
fi
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# Get voice model path
|
|
98
|
+
VOICE_PATH=$(get_voice_path "$VOICE_MODEL")
|
|
99
|
+
if [[ $? -ne 0 ]]; then
|
|
100
|
+
echo "❌ Voice model path not found: $VOICE_MODEL"
|
|
101
|
+
exit 3
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# @function determine_audio_directory
|
|
105
|
+
# @intent Find appropriate directory for audio file storage
|
|
106
|
+
# @why Supports project-local and global storage
|
|
107
|
+
# @returns Sets $AUDIO_DIR global variable
|
|
108
|
+
if [[ -n "$CLAUDE_PROJECT_DIR" ]]; then
|
|
109
|
+
AUDIO_DIR="$CLAUDE_PROJECT_DIR/.claude/audio"
|
|
110
|
+
else
|
|
111
|
+
# Fallback: try to find .claude directory in current path
|
|
112
|
+
CURRENT_DIR="$PWD"
|
|
113
|
+
while [[ "$CURRENT_DIR" != "/" ]]; do
|
|
114
|
+
if [[ -d "$CURRENT_DIR/.claude" ]]; then
|
|
115
|
+
AUDIO_DIR="$CURRENT_DIR/.claude/audio"
|
|
116
|
+
break
|
|
117
|
+
fi
|
|
118
|
+
CURRENT_DIR=$(dirname "$CURRENT_DIR")
|
|
119
|
+
done
|
|
120
|
+
# Final fallback to global if no project .claude found
|
|
121
|
+
if [[ -z "$AUDIO_DIR" ]]; then
|
|
122
|
+
AUDIO_DIR="$HOME/.claude/audio"
|
|
123
|
+
fi
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
mkdir -p "$AUDIO_DIR"
|
|
127
|
+
TEMP_FILE="$AUDIO_DIR/tts-$(date +%s).wav"
|
|
128
|
+
|
|
129
|
+
# @function synthesize_with_piper
|
|
130
|
+
# @intent Generate speech using Piper TTS
|
|
131
|
+
# @why Provides free, offline TTS alternative
|
|
132
|
+
# @param Uses globals: $TEXT, $VOICE_PATH
|
|
133
|
+
# @returns Creates WAV file at $TEMP_FILE
|
|
134
|
+
# @exitcode 0=success, 4=synthesis error
|
|
135
|
+
# @sideeffects Creates audio file
|
|
136
|
+
# @edgecases Handles piper errors, invalid models
|
|
137
|
+
echo "$TEXT" | piper --model "$VOICE_PATH" --output_file "$TEMP_FILE" 2>/dev/null
|
|
138
|
+
|
|
139
|
+
if [[ ! -f "$TEMP_FILE" ]] || [[ ! -s "$TEMP_FILE" ]]; then
|
|
140
|
+
echo "❌ Failed to synthesize speech with Piper"
|
|
141
|
+
echo "Voice model: $VOICE_MODEL"
|
|
142
|
+
echo "Check that voice model is valid"
|
|
143
|
+
exit 4
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# @function add_silence_padding
|
|
147
|
+
# @intent Add silence to prevent WSL audio static
|
|
148
|
+
# @why WSL audio subsystem cuts off first ~200ms
|
|
149
|
+
# @param Uses global: $TEMP_FILE
|
|
150
|
+
# @returns Updates $TEMP_FILE to padded version
|
|
151
|
+
# @sideeffects Modifies audio file
|
|
152
|
+
# AI NOTE: Use ffmpeg if available, otherwise skip padding (degraded experience)
|
|
153
|
+
if command -v ffmpeg &> /dev/null; then
|
|
154
|
+
PADDED_FILE="$AUDIO_DIR/tts-padded-$(date +%s).wav"
|
|
155
|
+
# Add 200ms of silence at the beginning
|
|
156
|
+
ffmpeg -f lavfi -i anullsrc=r=44100:cl=stereo:d=0.2 -i "$TEMP_FILE" \
|
|
157
|
+
-filter_complex "[0:a][1:a]concat=n=2:v=0:a=1[out]" \
|
|
158
|
+
-map "[out]" -y "$PADDED_FILE" 2>/dev/null
|
|
159
|
+
|
|
160
|
+
if [[ -f "$PADDED_FILE" ]]; then
|
|
161
|
+
rm -f "$TEMP_FILE"
|
|
162
|
+
TEMP_FILE="$PADDED_FILE"
|
|
163
|
+
fi
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
# @function play_audio
|
|
167
|
+
# @intent Play generated audio using available player
|
|
168
|
+
# @why Support multiple audio players
|
|
169
|
+
# @param Uses global: $TEMP_FILE
|
|
170
|
+
# @sideeffects Plays audio in background
|
|
171
|
+
# Play audio (WSL/Linux) in background
|
|
172
|
+
(mpv "$TEMP_FILE" 2>/dev/null || aplay "$TEMP_FILE" 2>/dev/null || paplay "$TEMP_FILE" 2>/dev/null) &
|
|
173
|
+
|
|
174
|
+
echo "🎵 Saved to: $TEMP_FILE"
|
|
175
|
+
echo "🎤 Voice used: $VOICE_MODEL (Piper TTS)"
|
|
@@ -0,0 +1,138 @@
|
|
|
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
|
+
#
|
|
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
|
|
9
|
+
#
|
|
10
|
+
# This allows different sessions to use different voices for easy identification!
|
|
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
|
+
|
|
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
|
+
|
|
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
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# @fileoverview Provider management slash commands
|
|
4
|
+
# @context User-facing commands for switching and managing TTS providers
|
|
5
|
+
# @architecture Part of /agent-vibes:* command system
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
# Get script directory
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
source "$SCRIPT_DIR/provider-manager.sh"
|
|
11
|
+
source "$SCRIPT_DIR/language-manager.sh"
|
|
12
|
+
|
|
13
|
+
COMMAND="${1:-help}"
|
|
14
|
+
|
|
15
|
+
# @function provider_list
|
|
16
|
+
# @intent Display all available providers with status
|
|
17
|
+
provider_list() {
|
|
18
|
+
local current_provider
|
|
19
|
+
current_provider=$(get_active_provider)
|
|
20
|
+
|
|
21
|
+
echo "┌────────────────────────────────────────────────────────────┐"
|
|
22
|
+
echo "│ Available TTS Providers │"
|
|
23
|
+
echo "├────────────────────────────────────────────────────────────┤"
|
|
24
|
+
|
|
25
|
+
# ElevenLabs
|
|
26
|
+
if [[ "$current_provider" == "elevenlabs" ]]; then
|
|
27
|
+
echo "│ ✓ ElevenLabs Premium quality ⭐⭐⭐⭐⭐ [ACTIVE] │"
|
|
28
|
+
else
|
|
29
|
+
echo "│ ElevenLabs Premium quality ⭐⭐⭐⭐⭐ │"
|
|
30
|
+
fi
|
|
31
|
+
echo "│ Cost: Free tier + \$5-22/mo │"
|
|
32
|
+
echo "│ Platform: All (Windows, macOS, Linux, WSL) │"
|
|
33
|
+
echo "│ Offline: No │"
|
|
34
|
+
echo "│ │"
|
|
35
|
+
|
|
36
|
+
# Piper
|
|
37
|
+
if [[ "$current_provider" == "piper" ]]; then
|
|
38
|
+
echo "│ ✓ Piper TTS Free, offline ⭐⭐⭐⭐ [ACTIVE] │"
|
|
39
|
+
else
|
|
40
|
+
echo "│ Piper TTS Free, offline ⭐⭐⭐⭐ │"
|
|
41
|
+
fi
|
|
42
|
+
echo "│ Cost: Free forever │"
|
|
43
|
+
echo "│ Platform: WSL, Linux only │"
|
|
44
|
+
echo "│ Offline: Yes │"
|
|
45
|
+
echo "└────────────────────────────────────────────────────────────┘"
|
|
46
|
+
echo ""
|
|
47
|
+
echo "Learn more: agentvibes.org/providers"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# @function provider_switch
|
|
51
|
+
# @intent Switch to a different TTS provider
|
|
52
|
+
provider_switch() {
|
|
53
|
+
local new_provider="$1"
|
|
54
|
+
|
|
55
|
+
if [[ -z "$new_provider" ]]; then
|
|
56
|
+
echo "❌ Error: Provider name required"
|
|
57
|
+
echo "Usage: /agent-vibes:provider switch <provider>"
|
|
58
|
+
echo "Available: elevenlabs, piper"
|
|
59
|
+
return 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# Validate provider
|
|
63
|
+
if ! validate_provider "$new_provider"; then
|
|
64
|
+
echo "❌ Invalid provider: $new_provider"
|
|
65
|
+
echo ""
|
|
66
|
+
echo "Available providers:"
|
|
67
|
+
list_providers
|
|
68
|
+
return 1
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
local current_provider
|
|
72
|
+
current_provider=$(get_active_provider)
|
|
73
|
+
|
|
74
|
+
if [[ "$current_provider" == "$new_provider" ]]; then
|
|
75
|
+
echo "✓ Already using $new_provider"
|
|
76
|
+
return 0
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# Platform check for Piper
|
|
80
|
+
if [[ "$new_provider" == "piper" ]]; then
|
|
81
|
+
if ! grep -qi microsoft /proc/version 2>/dev/null && [[ "$(uname -s)" != "Linux" ]]; then
|
|
82
|
+
echo "❌ Piper is only supported on WSL and Linux"
|
|
83
|
+
echo "Your platform: $(uname -s)"
|
|
84
|
+
echo "See: agentvibes.org/platform-support"
|
|
85
|
+
return 1
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# Check if Piper is installed
|
|
89
|
+
if ! command -v piper &> /dev/null; then
|
|
90
|
+
echo "❌ Piper TTS is not installed"
|
|
91
|
+
echo ""
|
|
92
|
+
echo "Install with: pipx install piper-tts"
|
|
93
|
+
echo "Or run: .claude/hooks/piper-installer.sh"
|
|
94
|
+
echo ""
|
|
95
|
+
echo "Visit: agentvibes.org/install-piper"
|
|
96
|
+
return 1
|
|
97
|
+
fi
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# Check language compatibility
|
|
101
|
+
local current_language
|
|
102
|
+
current_language=$(get_current_language)
|
|
103
|
+
|
|
104
|
+
if [[ "$current_language" != "english" ]]; then
|
|
105
|
+
if ! is_language_supported "$current_language" "$new_provider" 2>/dev/null; then
|
|
106
|
+
echo "⚠️ Language Compatibility Warning"
|
|
107
|
+
echo ""
|
|
108
|
+
echo "Current language: $current_language"
|
|
109
|
+
echo "Target provider: $new_provider"
|
|
110
|
+
echo ""
|
|
111
|
+
echo "❌ Language '$current_language' is not natively supported by $new_provider"
|
|
112
|
+
echo " Will fall back to English when using $new_provider"
|
|
113
|
+
echo ""
|
|
114
|
+
echo "Options:"
|
|
115
|
+
echo " 1. Continue anyway (will use English)"
|
|
116
|
+
echo " 2. Switch language to English"
|
|
117
|
+
echo " 3. Cancel provider switch"
|
|
118
|
+
echo ""
|
|
119
|
+
read -p "Choose option [1-3]: " -n 1 -r
|
|
120
|
+
echo
|
|
121
|
+
|
|
122
|
+
case $REPLY in
|
|
123
|
+
1)
|
|
124
|
+
echo "⏩ Continuing with fallback to English..."
|
|
125
|
+
;;
|
|
126
|
+
2)
|
|
127
|
+
echo "🔄 Switching language to English..."
|
|
128
|
+
"$SCRIPT_DIR/language-manager.sh" set english
|
|
129
|
+
;;
|
|
130
|
+
3)
|
|
131
|
+
echo "❌ Provider switch cancelled"
|
|
132
|
+
return 1
|
|
133
|
+
;;
|
|
134
|
+
*)
|
|
135
|
+
echo "❌ Invalid option, cancelling"
|
|
136
|
+
return 1
|
|
137
|
+
;;
|
|
138
|
+
esac
|
|
139
|
+
fi
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
# Confirm switch
|
|
143
|
+
echo ""
|
|
144
|
+
echo "⚠️ Switch to $(echo $new_provider | tr '[:lower:]' '[:upper:]')?"
|
|
145
|
+
echo ""
|
|
146
|
+
echo "Current: $current_provider"
|
|
147
|
+
echo "New: $new_provider"
|
|
148
|
+
if [[ "$current_language" != "english" ]]; then
|
|
149
|
+
echo "Language: $current_language"
|
|
150
|
+
fi
|
|
151
|
+
echo ""
|
|
152
|
+
read -p "Continue? [y/N]: " -n 1 -r
|
|
153
|
+
echo
|
|
154
|
+
|
|
155
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
156
|
+
echo "❌ Switch cancelled"
|
|
157
|
+
return 1
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
# Perform switch
|
|
161
|
+
set_active_provider "$new_provider"
|
|
162
|
+
|
|
163
|
+
# Test new provider
|
|
164
|
+
echo ""
|
|
165
|
+
echo "🔊 Testing provider..."
|
|
166
|
+
"$SCRIPT_DIR/play-tts.sh" "Provider switched to $new_provider successfully" 2>/dev/null
|
|
167
|
+
|
|
168
|
+
echo ""
|
|
169
|
+
echo "✓ Provider switch complete!"
|
|
170
|
+
echo "Visit agentvibes.org for tips and tricks"
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# @function provider_info
|
|
174
|
+
# @intent Show detailed information about a provider
|
|
175
|
+
provider_info() {
|
|
176
|
+
local provider_name="$1"
|
|
177
|
+
|
|
178
|
+
if [[ -z "$provider_name" ]]; then
|
|
179
|
+
echo "❌ Error: Provider name required"
|
|
180
|
+
echo "Usage: /agent-vibes:provider info <provider>"
|
|
181
|
+
return 1
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
case "$provider_name" in
|
|
185
|
+
elevenlabs)
|
|
186
|
+
echo "┌────────────────────────────────────────────────────────────┐"
|
|
187
|
+
echo "│ ElevenLabs - Premium TTS Provider │"
|
|
188
|
+
echo "├────────────────────────────────────────────────────────────┤"
|
|
189
|
+
echo "│ Quality: ⭐⭐⭐⭐⭐ (Highest available) │"
|
|
190
|
+
echo "│ Cost: Free tier + \$5-22/mo │"
|
|
191
|
+
echo "│ Platform: All (Windows, macOS, Linux, WSL) │"
|
|
192
|
+
echo "│ Offline: No (requires internet) │"
|
|
193
|
+
echo "│ │"
|
|
194
|
+
echo "│ Trade-offs: │"
|
|
195
|
+
echo "│ + Highest voice quality and naturalness │"
|
|
196
|
+
echo "│ + 50+ premium voices available │"
|
|
197
|
+
echo "│ + Multilingual support (30+ languages) │"
|
|
198
|
+
echo "│ - Requires API key and internet │"
|
|
199
|
+
echo "│ - Costs money after free tier │"
|
|
200
|
+
echo "│ │"
|
|
201
|
+
echo "│ Best for: Premium quality, multilingual needs │"
|
|
202
|
+
echo "└────────────────────────────────────────────────────────────┘"
|
|
203
|
+
echo ""
|
|
204
|
+
echo "Full comparison: agentvibes.org/providers"
|
|
205
|
+
;;
|
|
206
|
+
|
|
207
|
+
piper)
|
|
208
|
+
echo "┌────────────────────────────────────────────────────────────┐"
|
|
209
|
+
echo "│ Piper TTS - Free Offline Provider │"
|
|
210
|
+
echo "├────────────────────────────────────────────────────────────┤"
|
|
211
|
+
echo "│ Quality: ⭐⭐⭐⭐ (Very good) │"
|
|
212
|
+
echo "│ Cost: Free forever │"
|
|
213
|
+
echo "│ Platform: WSL, Linux only │"
|
|
214
|
+
echo "│ Offline: Yes (fully local) │"
|
|
215
|
+
echo "│ │"
|
|
216
|
+
echo "│ Trade-offs: │"
|
|
217
|
+
echo "│ + Completely free, no API costs │"
|
|
218
|
+
echo "│ + Works offline, no internet needed │"
|
|
219
|
+
echo "│ + Fast synthesis (local processing) │"
|
|
220
|
+
echo "│ - WSL/Linux only (no macOS/Windows) │"
|
|
221
|
+
echo "│ - Slightly lower quality than ElevenLabs │"
|
|
222
|
+
echo "│ │"
|
|
223
|
+
echo "│ Best for: Budget-conscious, offline use, privacy │"
|
|
224
|
+
echo "└────────────────────────────────────────────────────────────┘"
|
|
225
|
+
echo ""
|
|
226
|
+
echo "Full comparison: agentvibes.org/providers"
|
|
227
|
+
;;
|
|
228
|
+
|
|
229
|
+
*)
|
|
230
|
+
echo "❌ Unknown provider: $provider_name"
|
|
231
|
+
echo "Available: elevenlabs, piper"
|
|
232
|
+
;;
|
|
233
|
+
esac
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
# @function provider_test
|
|
237
|
+
# @intent Test current provider with sample audio
|
|
238
|
+
provider_test() {
|
|
239
|
+
local current_provider
|
|
240
|
+
current_provider=$(get_active_provider)
|
|
241
|
+
|
|
242
|
+
echo "🔊 Testing provider: $current_provider"
|
|
243
|
+
echo ""
|
|
244
|
+
|
|
245
|
+
"$SCRIPT_DIR/play-tts.sh" "Provider test successful. Audio is working correctly with $current_provider."
|
|
246
|
+
|
|
247
|
+
echo ""
|
|
248
|
+
echo "✓ Test complete"
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
# @function provider_get
|
|
252
|
+
# @intent Show currently active provider
|
|
253
|
+
provider_get() {
|
|
254
|
+
local current_provider
|
|
255
|
+
current_provider=$(get_active_provider)
|
|
256
|
+
|
|
257
|
+
echo "🎤 Current Provider: $current_provider"
|
|
258
|
+
echo ""
|
|
259
|
+
|
|
260
|
+
# Show brief info
|
|
261
|
+
case "$current_provider" in
|
|
262
|
+
elevenlabs)
|
|
263
|
+
echo "Quality: ⭐⭐⭐⭐⭐"
|
|
264
|
+
echo "Cost: Free tier + \$5-22/mo"
|
|
265
|
+
echo "Offline: No"
|
|
266
|
+
;;
|
|
267
|
+
piper)
|
|
268
|
+
echo "Quality: ⭐⭐⭐⭐"
|
|
269
|
+
echo "Cost: Free forever"
|
|
270
|
+
echo "Offline: Yes"
|
|
271
|
+
;;
|
|
272
|
+
esac
|
|
273
|
+
|
|
274
|
+
echo ""
|
|
275
|
+
echo "Use /agent-vibes:provider info $current_provider for details"
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
# @function provider_preview
|
|
279
|
+
# @intent Preview voices for the currently active provider
|
|
280
|
+
# @architecture Delegates to provider-specific voice managers
|
|
281
|
+
provider_preview() {
|
|
282
|
+
local current_provider
|
|
283
|
+
current_provider=$(get_active_provider)
|
|
284
|
+
|
|
285
|
+
echo "🎤 Voice Preview ($current_provider)"
|
|
286
|
+
echo ""
|
|
287
|
+
|
|
288
|
+
case "$current_provider" in
|
|
289
|
+
elevenlabs)
|
|
290
|
+
# Use the ElevenLabs voice manager
|
|
291
|
+
"$SCRIPT_DIR/voice-manager.sh" preview "$@"
|
|
292
|
+
;;
|
|
293
|
+
piper)
|
|
294
|
+
# Use the Piper voice manager's list functionality
|
|
295
|
+
# For Piper, we'll list and play sample from available voices
|
|
296
|
+
source "$SCRIPT_DIR/piper-voice-manager.sh"
|
|
297
|
+
|
|
298
|
+
# Play intro announcement
|
|
299
|
+
echo "🎤 Piper Preview of 3 people"
|
|
300
|
+
echo ""
|
|
301
|
+
|
|
302
|
+
# Play first 3 Piper voices as samples
|
|
303
|
+
local sample_voices=(
|
|
304
|
+
"en_US-lessac-medium:Lessac"
|
|
305
|
+
"en_US-amy-medium:Amy"
|
|
306
|
+
"en_US-joe-medium:Joe"
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
for voice_entry in "${sample_voices[@]}"; do
|
|
310
|
+
local voice_name="${voice_entry%%:*}"
|
|
311
|
+
local display_name="${voice_entry##*:}"
|
|
312
|
+
|
|
313
|
+
echo "🔊 ${display_name}..."
|
|
314
|
+
"$SCRIPT_DIR/play-tts.sh" "Hi, my name is ${display_name}" "$voice_name"
|
|
315
|
+
|
|
316
|
+
# Wait for the voice to finish playing before starting next one
|
|
317
|
+
sleep 3
|
|
318
|
+
done
|
|
319
|
+
|
|
320
|
+
echo ""
|
|
321
|
+
echo "✓ Preview complete"
|
|
322
|
+
echo "💡 Use /agent-vibes:list to see all available Piper voices"
|
|
323
|
+
;;
|
|
324
|
+
*)
|
|
325
|
+
echo "❌ Unknown provider: $current_provider"
|
|
326
|
+
;;
|
|
327
|
+
esac
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
# @function provider_help
|
|
331
|
+
# @intent Show help for provider commands
|
|
332
|
+
provider_help() {
|
|
333
|
+
echo "Provider Management Commands"
|
|
334
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
335
|
+
echo ""
|
|
336
|
+
echo "Usage:"
|
|
337
|
+
echo " /agent-vibes:provider list # Show all providers"
|
|
338
|
+
echo " /agent-vibes:provider switch <name> # Switch provider"
|
|
339
|
+
echo " /agent-vibes:provider info <name> # Provider details"
|
|
340
|
+
echo " /agent-vibes:provider test # Test current provider"
|
|
341
|
+
echo " /agent-vibes:provider get # Show active provider"
|
|
342
|
+
echo ""
|
|
343
|
+
echo "Examples:"
|
|
344
|
+
echo " /agent-vibes:provider switch piper"
|
|
345
|
+
echo " /agent-vibes:provider info elevenlabs"
|
|
346
|
+
echo ""
|
|
347
|
+
echo "Learn more: agentvibes.org/docs/providers"
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
# Route to appropriate function
|
|
351
|
+
case "$COMMAND" in
|
|
352
|
+
list)
|
|
353
|
+
provider_list
|
|
354
|
+
;;
|
|
355
|
+
switch)
|
|
356
|
+
provider_switch "$2"
|
|
357
|
+
;;
|
|
358
|
+
info)
|
|
359
|
+
provider_info "$2"
|
|
360
|
+
;;
|
|
361
|
+
test)
|
|
362
|
+
provider_test
|
|
363
|
+
;;
|
|
364
|
+
get)
|
|
365
|
+
provider_get
|
|
366
|
+
;;
|
|
367
|
+
preview)
|
|
368
|
+
shift # Remove 'preview' from args
|
|
369
|
+
provider_preview "$@"
|
|
370
|
+
;;
|
|
371
|
+
help|*)
|
|
372
|
+
provider_help
|
|
373
|
+
;;
|
|
374
|
+
esac
|