541 lines
19 KiB
Bash
Executable File
541 lines
19 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# File: .claude/hooks/provider-commands.sh
|
|
#
|
|
# AgentVibes - Finally, your AI Agents can Talk Back! Text-to-Speech WITH personality for AI Assistants!
|
|
# Website: https://agentvibes.org
|
|
# Repository: https://github.com/paulpreibisch/AgentVibes
|
|
#
|
|
# Co-created by Paul Preibisch with Claude AI
|
|
# Copyright (c) 2025 Paul Preibisch
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
# DISCLAIMER: This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
# express or implied, including but not limited to the warranties of
|
|
# merchantability, fitness for a particular purpose and noninfringement.
|
|
# In no event shall the authors or copyright holders be liable for any claim,
|
|
# damages or other liability, whether in an action of contract, tort or
|
|
# otherwise, arising from, out of or in connection with the software or the
|
|
# use or other dealings in the software.
|
|
#
|
|
# ---
|
|
#
|
|
# @fileoverview Provider management slash commands
|
|
# @context User-facing commands for switching and managing TTS providers
|
|
# @architecture Part of /agent-vibes:* command system with language compatibility checking
|
|
# @dependencies provider-manager.sh, language-manager.sh, voice-manager.sh, piper-voice-manager.sh
|
|
# @entrypoints Called by /agent-vibes:provider slash commands (list, switch, info, test, get, preview)
|
|
# @patterns Interactive confirmations, platform detection, language compatibility validation
|
|
# @related provider-manager.sh, play-tts.sh, voice-manager.sh, piper-voice-manager.sh
|
|
#
|
|
|
|
# Get script directory
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "$SCRIPT_DIR/provider-manager.sh"
|
|
source "$SCRIPT_DIR/language-manager.sh"
|
|
|
|
COMMAND="${1:-help}"
|
|
|
|
# @function is_language_supported
|
|
# @intent Check if a language is supported by a provider
|
|
# @param $1 {string} language - Language code (e.g., "spanish", "french")
|
|
# @param $2 {string} provider - Provider name (e.g., "elevenlabs", "piper")
|
|
# @returns 0 if supported, 1 if not
|
|
is_language_supported() {
|
|
local language="$1"
|
|
local provider="$2"
|
|
|
|
# English is always supported
|
|
if [[ "$language" == "english" ]] || [[ "$language" == "en" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
case "$provider" in
|
|
elevenlabs)
|
|
# ElevenLabs supports all languages via multilingual voices
|
|
return 0
|
|
;;
|
|
piper)
|
|
# Piper only supports English natively
|
|
return 1
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# @function provider_list
|
|
# @intent Display all available providers with status
|
|
provider_list() {
|
|
local current_provider
|
|
current_provider=$(get_active_provider)
|
|
|
|
echo "┌────────────────────────────────────────────────────────────┐"
|
|
echo "│ Available TTS Providers │"
|
|
echo "├────────────────────────────────────────────────────────────┤"
|
|
|
|
# ElevenLabs
|
|
if [[ "$current_provider" == "elevenlabs" ]]; then
|
|
echo "│ ✓ ElevenLabs Premium quality ⭐⭐⭐⭐⭐ [ACTIVE] │"
|
|
else
|
|
echo "│ ElevenLabs Premium quality ⭐⭐⭐⭐⭐ │"
|
|
fi
|
|
echo "│ Cost: Free tier + \$5-22/mo │"
|
|
echo "│ Platform: All (Windows, macOS, Linux, WSL) │"
|
|
echo "│ Offline: No │"
|
|
echo "│ │"
|
|
|
|
# Piper
|
|
if [[ "$current_provider" == "piper" ]]; then
|
|
echo "│ ✓ Piper TTS Free, offline ⭐⭐⭐⭐ [ACTIVE] │"
|
|
else
|
|
echo "│ Piper TTS Free, offline ⭐⭐⭐⭐ │"
|
|
fi
|
|
echo "│ Cost: Free forever │"
|
|
echo "│ Platform: WSL, Linux only │"
|
|
echo "│ Offline: Yes │"
|
|
echo "└────────────────────────────────────────────────────────────┘"
|
|
echo ""
|
|
echo "Learn more: agentvibes.org/providers"
|
|
}
|
|
|
|
# @function provider_switch
|
|
# @intent Switch to a different TTS provider
|
|
provider_switch() {
|
|
local new_provider="$1"
|
|
local force_mode=false
|
|
|
|
# Check for --force or --yes flag
|
|
if [[ "$2" == "--force" ]] || [[ "$2" == "--yes" ]] || [[ "$2" == "-y" ]]; then
|
|
force_mode=true
|
|
fi
|
|
|
|
# Auto-enable force mode if running non-interactively (e.g., from MCP)
|
|
# Check multiple conditions for MCP/non-interactive context
|
|
if [[ ! -t 0 ]] || [[ -n "$CLAUDE_PROJECT_DIR" ]] || [[ -n "$MCP_SERVER" ]]; then
|
|
force_mode=true
|
|
fi
|
|
|
|
if [[ -z "$new_provider" ]]; then
|
|
echo "❌ Error: Provider name required"
|
|
echo "Usage: /agent-vibes:provider switch <provider> [--force]"
|
|
echo "Available: elevenlabs, piper"
|
|
return 1
|
|
fi
|
|
|
|
# Validate provider
|
|
if ! validate_provider "$new_provider"; then
|
|
echo "❌ Invalid provider: $new_provider"
|
|
echo ""
|
|
echo "Available providers:"
|
|
list_providers
|
|
return 1
|
|
fi
|
|
|
|
local current_provider
|
|
current_provider=$(get_active_provider)
|
|
|
|
if [[ "$current_provider" == "$new_provider" ]]; then
|
|
echo "✓ Already using $new_provider"
|
|
return 0
|
|
fi
|
|
|
|
# Platform check for Piper
|
|
if [[ "$new_provider" == "piper" ]]; then
|
|
if ! grep -qi microsoft /proc/version 2>/dev/null && [[ "$(uname -s)" != "Linux" ]]; then
|
|
echo "❌ Piper is only supported on WSL and Linux"
|
|
echo "Your platform: $(uname -s)"
|
|
echo "See: agentvibes.org/platform-support"
|
|
return 1
|
|
fi
|
|
|
|
# Check if Piper is installed
|
|
if ! command -v piper &> /dev/null; then
|
|
echo "❌ Piper TTS is not installed"
|
|
echo ""
|
|
echo "Install with: pipx install piper-tts"
|
|
echo "Or run: .claude/hooks/piper-installer.sh"
|
|
echo ""
|
|
echo "Visit: agentvibes.org/install-piper"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Check language compatibility
|
|
local current_language
|
|
current_language=$(get_language_code)
|
|
|
|
if [[ "$current_language" != "english" ]]; then
|
|
if ! is_language_supported "$current_language" "$new_provider" 2>/dev/null; then
|
|
echo "⚠️ Language Compatibility Warning"
|
|
echo ""
|
|
echo "Current language: $current_language"
|
|
echo "Target provider: $new_provider"
|
|
echo ""
|
|
echo "❌ Language '$current_language' is not natively supported by $new_provider"
|
|
echo " Will fall back to English when using $new_provider"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " 1. Continue anyway (will use English)"
|
|
echo " 2. Switch language to English"
|
|
echo " 3. Cancel provider switch"
|
|
echo ""
|
|
|
|
# Skip prompt in force mode
|
|
if [[ "$force_mode" == true ]]; then
|
|
echo "⏩ Force mode: Continuing with fallback to English..."
|
|
else
|
|
read -p "Choose option [1-3]: " -n 1 -r
|
|
echo
|
|
|
|
case $REPLY in
|
|
1)
|
|
echo "⏩ Continuing with fallback to English..."
|
|
;;
|
|
2)
|
|
echo "🔄 Switching language to English..."
|
|
"$SCRIPT_DIR/language-manager.sh" set english
|
|
;;
|
|
3)
|
|
echo "❌ Provider switch cancelled"
|
|
return 1
|
|
;;
|
|
*)
|
|
echo "❌ Invalid option, cancelling"
|
|
return 1
|
|
;;
|
|
esac
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Confirm switch (skip in force mode)
|
|
if [[ "$force_mode" != true ]]; then
|
|
echo ""
|
|
echo "⚠️ Switch to $(echo $new_provider | tr '[:lower:]' '[:upper:]')?"
|
|
echo ""
|
|
echo "Current: $current_provider"
|
|
echo "New: $new_provider"
|
|
if [[ "$current_language" != "english" ]]; then
|
|
echo "Language: $current_language"
|
|
fi
|
|
echo ""
|
|
read -p "Continue? [y/N]: " -n 1 -r
|
|
echo
|
|
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
echo "❌ Switch cancelled"
|
|
return 1
|
|
fi
|
|
else
|
|
echo "⏩ Force mode: Switching to $new_provider..."
|
|
fi
|
|
|
|
# Perform switch
|
|
set_active_provider "$new_provider"
|
|
|
|
# Update target voice if language learning mode is active
|
|
local target_lang_file=""
|
|
local target_voice_file=""
|
|
|
|
# Check project-local first, then global
|
|
if [[ -d "$SCRIPT_DIR/../.." ]]; then
|
|
local project_dir="$SCRIPT_DIR/../.."
|
|
if [[ -f "$project_dir/.claude/tts-target-language.txt" ]]; then
|
|
target_lang_file="$project_dir/.claude/tts-target-language.txt"
|
|
target_voice_file="$project_dir/.claude/tts-target-voice.txt"
|
|
fi
|
|
fi
|
|
|
|
# Fallback to global
|
|
if [[ -z "$target_lang_file" ]]; then
|
|
if [[ -f "$HOME/.claude/tts-target-language.txt" ]]; then
|
|
target_lang_file="$HOME/.claude/tts-target-language.txt"
|
|
target_voice_file="$HOME/.claude/tts-target-voice.txt"
|
|
fi
|
|
fi
|
|
|
|
# If target language is set, update voice for new provider
|
|
if [[ -n "$target_lang_file" ]] && [[ -f "$target_lang_file" ]]; then
|
|
local target_lang
|
|
target_lang=$(cat "$target_lang_file")
|
|
|
|
if [[ -n "$target_lang" ]]; then
|
|
# Get the recommended voice for this language with new provider
|
|
local new_target_voice
|
|
new_target_voice=$(get_voice_for_language "$target_lang" "$new_provider")
|
|
|
|
if [[ -n "$new_target_voice" ]]; then
|
|
echo "$new_target_voice" > "$target_voice_file"
|
|
echo ""
|
|
echo "🔄 Updated target language voice:"
|
|
echo " Language: $target_lang"
|
|
echo " Voice: $new_target_voice (for $new_provider)"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Test new provider
|
|
echo ""
|
|
echo "🔊 Testing provider..."
|
|
"$SCRIPT_DIR/play-tts.sh" "Provider switched to $new_provider successfully" 2>/dev/null
|
|
|
|
echo ""
|
|
echo "✓ Provider switch complete!"
|
|
echo "Visit agentvibes.org for tips and tricks"
|
|
}
|
|
|
|
# @function provider_info
|
|
# @intent Show detailed information about a provider
|
|
provider_info() {
|
|
local provider_name="$1"
|
|
|
|
if [[ -z "$provider_name" ]]; then
|
|
echo "❌ Error: Provider name required"
|
|
echo "Usage: /agent-vibes:provider info <provider>"
|
|
return 1
|
|
fi
|
|
|
|
case "$provider_name" in
|
|
elevenlabs)
|
|
echo "┌────────────────────────────────────────────────────────────┐"
|
|
echo "│ ElevenLabs - Premium TTS Provider │"
|
|
echo "├────────────────────────────────────────────────────────────┤"
|
|
echo "│ Quality: ⭐⭐⭐⭐⭐ (Highest available) │"
|
|
echo "│ Cost: Free tier + \$5-22/mo │"
|
|
echo "│ Platform: All (Windows, macOS, Linux, WSL) │"
|
|
echo "│ Offline: No (requires internet) │"
|
|
echo "│ │"
|
|
echo "│ Trade-offs: │"
|
|
echo "│ + Highest voice quality and naturalness │"
|
|
echo "│ + 50+ premium voices available │"
|
|
echo "│ + Multilingual support (30+ languages) │"
|
|
echo "│ - Requires API key and internet │"
|
|
echo "│ - Costs money after free tier │"
|
|
echo "│ │"
|
|
echo "│ Best for: Premium quality, multilingual needs │"
|
|
echo "└────────────────────────────────────────────────────────────┘"
|
|
echo ""
|
|
echo "Full comparison: agentvibes.org/providers"
|
|
;;
|
|
|
|
piper)
|
|
echo "┌────────────────────────────────────────────────────────────┐"
|
|
echo "│ Piper TTS - Free Offline Provider │"
|
|
echo "├────────────────────────────────────────────────────────────┤"
|
|
echo "│ Quality: ⭐⭐⭐⭐ (Very good) │"
|
|
echo "│ Cost: Free forever │"
|
|
echo "│ Platform: WSL, Linux only │"
|
|
echo "│ Offline: Yes (fully local) │"
|
|
echo "│ │"
|
|
echo "│ Trade-offs: │"
|
|
echo "│ + Completely free, no API costs │"
|
|
echo "│ + Works offline, no internet needed │"
|
|
echo "│ + Fast synthesis (local processing) │"
|
|
echo "│ - WSL/Linux only (no macOS/Windows) │"
|
|
echo "│ - Slightly lower quality than ElevenLabs │"
|
|
echo "│ │"
|
|
echo "│ Best for: Budget-conscious, offline use, privacy │"
|
|
echo "└────────────────────────────────────────────────────────────┘"
|
|
echo ""
|
|
echo "Full comparison: agentvibes.org/providers"
|
|
;;
|
|
|
|
*)
|
|
echo "❌ Unknown provider: $provider_name"
|
|
echo "Available: elevenlabs, piper"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# @function provider_test
|
|
# @intent Test current provider with sample audio
|
|
provider_test() {
|
|
local current_provider
|
|
current_provider=$(get_active_provider)
|
|
|
|
echo "🔊 Testing provider: $current_provider"
|
|
echo ""
|
|
|
|
"$SCRIPT_DIR/play-tts.sh" "Provider test successful. Audio is working correctly with $current_provider."
|
|
|
|
echo ""
|
|
echo "✓ Test complete"
|
|
}
|
|
|
|
# @function provider_get
|
|
# @intent Show currently active provider
|
|
provider_get() {
|
|
local current_provider
|
|
current_provider=$(get_active_provider)
|
|
|
|
echo "🎤 Current Provider: $current_provider"
|
|
echo ""
|
|
|
|
# Show brief info
|
|
case "$current_provider" in
|
|
elevenlabs)
|
|
echo "Quality: ⭐⭐⭐⭐⭐"
|
|
echo "Cost: Free tier + \$5-22/mo"
|
|
echo "Offline: No"
|
|
;;
|
|
piper)
|
|
echo "Quality: ⭐⭐⭐⭐"
|
|
echo "Cost: Free forever"
|
|
echo "Offline: Yes"
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
echo "Use /agent-vibes:provider info $current_provider for details"
|
|
}
|
|
|
|
# @function provider_preview
|
|
# @intent Preview voices for the currently active provider
|
|
# @architecture Delegates to provider-specific voice managers
|
|
provider_preview() {
|
|
local current_provider
|
|
current_provider=$(get_active_provider)
|
|
|
|
echo "🎤 Voice Preview ($current_provider)"
|
|
echo ""
|
|
|
|
case "$current_provider" in
|
|
elevenlabs)
|
|
# Use the ElevenLabs voice manager
|
|
"$SCRIPT_DIR/voice-manager.sh" preview "$@"
|
|
;;
|
|
piper)
|
|
# Use the Piper voice manager's list functionality
|
|
source "$SCRIPT_DIR/piper-voice-manager.sh"
|
|
|
|
# Check if a specific voice was requested
|
|
local voice_arg="$1"
|
|
|
|
if [[ -n "$voice_arg" ]]; then
|
|
# User requested a specific voice - check if it's a valid Piper voice
|
|
# Piper voice names are like: en_US-lessac-medium
|
|
# Try to find a matching voice model
|
|
|
|
# Check if the voice arg looks like a Piper model name (contains underscores/hyphens)
|
|
if [[ "$voice_arg" =~ ^[a-z]{2}_[A-Z]{2}- ]]; then
|
|
# Looks like a Piper voice model name
|
|
if verify_voice "$voice_arg"; then
|
|
echo "🎤 Previewing Piper voice: $voice_arg"
|
|
echo ""
|
|
"$SCRIPT_DIR/play-tts.sh" "Hello, this is the $voice_arg voice. How do you like it?" "$voice_arg"
|
|
else
|
|
echo "❌ Voice model not found: $voice_arg"
|
|
echo ""
|
|
echo "💡 Piper voice names look like: en_US-lessac-medium"
|
|
echo " Run /agent-vibes:list to see available Piper voices"
|
|
fi
|
|
else
|
|
# Looks like an ElevenLabs voice name (like "Antoni", "Jessica")
|
|
echo "❌ '$voice_arg' appears to be an ElevenLabs voice"
|
|
echo ""
|
|
echo "You're currently using Piper TTS (free provider)."
|
|
echo "Piper has different voices than ElevenLabs."
|
|
echo ""
|
|
echo "Options:"
|
|
echo " 1. Run /agent-vibes:list to see available Piper voices"
|
|
echo " 2. Switch to ElevenLabs: /agent-vibes:provider switch elevenlabs"
|
|
echo ""
|
|
echo "Popular Piper voices to try:"
|
|
echo " • en_US-lessac-medium (clear, professional)"
|
|
echo " • en_US-amy-medium (warm, friendly)"
|
|
echo " • en_US-joe-medium (casual, natural)"
|
|
fi
|
|
return
|
|
fi
|
|
|
|
# No specific voice - preview first 3 voices
|
|
echo "🎤 Piper Preview of 3 people"
|
|
echo ""
|
|
|
|
# Play first 3 Piper voices as samples
|
|
local sample_voices=(
|
|
"en_US-lessac-medium:Lessac"
|
|
"en_US-amy-medium:Amy"
|
|
"en_US-joe-medium:Joe"
|
|
)
|
|
|
|
for voice_entry in "${sample_voices[@]}"; do
|
|
local voice_name="${voice_entry%%:*}"
|
|
local display_name="${voice_entry##*:}"
|
|
|
|
echo "🔊 ${display_name}..."
|
|
"$SCRIPT_DIR/play-tts.sh" "Hi, my name is ${display_name}" "$voice_name"
|
|
|
|
# Wait for the voice to finish playing before starting next one
|
|
sleep 3
|
|
done
|
|
|
|
echo ""
|
|
echo "✓ Preview complete"
|
|
echo "💡 Use /agent-vibes:list to see all available Piper voices"
|
|
;;
|
|
*)
|
|
echo "❌ Unknown provider: $current_provider"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# @function provider_help
|
|
# @intent Show help for provider commands
|
|
provider_help() {
|
|
echo "Provider Management Commands"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
echo "Usage:"
|
|
echo " /agent-vibes:provider list # Show all providers"
|
|
echo " /agent-vibes:provider switch <name> # Switch provider"
|
|
echo " /agent-vibes:provider info <name> # Provider details"
|
|
echo " /agent-vibes:provider test # Test current provider"
|
|
echo " /agent-vibes:provider get # Show active provider"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " /agent-vibes:provider switch piper"
|
|
echo " /agent-vibes:provider info elevenlabs"
|
|
echo ""
|
|
echo "Learn more: agentvibes.org/docs/providers"
|
|
}
|
|
|
|
# Route to appropriate function
|
|
case "$COMMAND" in
|
|
list)
|
|
provider_list
|
|
;;
|
|
switch)
|
|
provider_switch "$2" "$3"
|
|
;;
|
|
info)
|
|
provider_info "$2"
|
|
;;
|
|
test)
|
|
provider_test
|
|
;;
|
|
get)
|
|
provider_get
|
|
;;
|
|
preview)
|
|
shift # Remove 'preview' from args
|
|
provider_preview "$@"
|
|
;;
|
|
help|*)
|
|
provider_help
|
|
;;
|
|
esac
|