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
@@ -0,0 +1,163 @@
1
+ #!/bin/bash
2
+ # Sentiment manager for AgentVibes - applies personality to current voice
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ PERSONALITIES_DIR="$SCRIPT_DIR/../personalities"
6
+
7
+ # Project-local file first, global fallback
8
+ # Use logical path (not physical) to handle symlinked .claude directories
9
+ # Script is at .claude/hooks/sentiment-manager.sh, so .claude is ..
10
+ CLAUDE_DIR="$(cd "$SCRIPT_DIR/.." 2>/dev/null && pwd)"
11
+
12
+ # Check if we have a project-local .claude directory
13
+ if [[ -d "$CLAUDE_DIR" ]] && [[ "$CLAUDE_DIR" != "$HOME/.claude" ]]; then
14
+ SENTIMENT_FILE="$CLAUDE_DIR/tts-sentiment.txt"
15
+ else
16
+ SENTIMENT_FILE="$HOME/.claude/tts-sentiment.txt"
17
+ fi
18
+
19
+ # Function to get personality data from markdown file
20
+ get_personality_data() {
21
+ local personality="$1"
22
+ local field="$2"
23
+ local file="$PERSONALITIES_DIR/${personality}.md"
24
+
25
+ if [[ ! -f "$file" ]]; then
26
+ return 1
27
+ fi
28
+
29
+ case "$field" in
30
+ description)
31
+ grep "^description:" "$file" | cut -d: -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'
32
+ ;;
33
+ esac
34
+ }
35
+
36
+ # Function to list all available personalities
37
+ list_personalities() {
38
+ if [[ -d "$PERSONALITIES_DIR" ]]; then
39
+ for file in "$PERSONALITIES_DIR"/*.md; do
40
+ if [[ -f "$file" ]]; then
41
+ basename "$file" .md
42
+ fi
43
+ done
44
+ fi
45
+ }
46
+
47
+ case "$1" in
48
+ list)
49
+ echo "🎭 Available Sentiments:"
50
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
51
+
52
+ # Get current sentiment
53
+ CURRENT="none"
54
+ if [ -f "$SENTIMENT_FILE" ]; then
55
+ CURRENT=$(cat "$SENTIMENT_FILE")
56
+ fi
57
+
58
+ # List personalities from markdown files
59
+ echo "Available sentiment styles:"
60
+ for personality in $(list_personalities | sort); do
61
+ desc=$(get_personality_data "$personality" "description")
62
+ if [[ "$personality" == "$CURRENT" ]]; then
63
+ echo " ✓ $personality - $desc (current)"
64
+ else
65
+ echo " - $personality - $desc"
66
+ fi
67
+ done
68
+
69
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
70
+ echo ""
71
+ echo "Usage: /agent-vibes:sentiment <name>"
72
+ echo " /agent-vibes:sentiment clear"
73
+ ;;
74
+
75
+ set)
76
+ SENTIMENT="$2"
77
+
78
+ if [[ -z "$SENTIMENT" ]]; then
79
+ echo "❌ Please specify a sentiment name"
80
+ echo "Usage: $0 set <sentiment>"
81
+ exit 1
82
+ fi
83
+
84
+ # Check if sentiment file exists
85
+ if [[ ! -f "$PERSONALITIES_DIR/${SENTIMENT}.md" ]]; then
86
+ echo "❌ Sentiment not found: $SENTIMENT"
87
+ echo ""
88
+ echo "Available sentiments:"
89
+ for p in $(list_personalities | sort); do
90
+ echo " • $p"
91
+ done
92
+ exit 1
93
+ fi
94
+
95
+ # Save the sentiment (but don't change personality or voice)
96
+ echo "$SENTIMENT" > "$SENTIMENT_FILE"
97
+ echo "🎭 Sentiment set to: $SENTIMENT"
98
+ echo "🎤 Voice remains unchanged"
99
+ echo ""
100
+
101
+ # Make a sentiment-appropriate remark with TTS
102
+ TTS_SCRIPT="$SCRIPT_DIR/play-tts.sh"
103
+
104
+ # Try to get acknowledgment from personality file (sentiments use same personality files)
105
+ PERSONALITY_FILE_PATH="$PERSONALITIES_DIR/${SENTIMENT}.md"
106
+ REMARK=""
107
+
108
+ if [[ -f "$PERSONALITY_FILE_PATH" ]]; then
109
+ # Extract example responses from personality file (lines starting with "- ")
110
+ mapfile -t EXAMPLES < <(grep '^- "' "$PERSONALITY_FILE_PATH" | sed 's/^- "//; s/"$//')
111
+
112
+ if [[ ${#EXAMPLES[@]} -gt 0 ]]; then
113
+ # Pick a random example
114
+ REMARK="${EXAMPLES[$RANDOM % ${#EXAMPLES[@]}]}"
115
+ fi
116
+ fi
117
+
118
+ # Fallback if no examples found
119
+ if [[ -z "$REMARK" ]]; then
120
+ REMARK="Sentiment set to ${SENTIMENT} while maintaining current voice"
121
+ fi
122
+
123
+ echo "💬 $REMARK"
124
+ "$TTS_SCRIPT" "$REMARK"
125
+ ;;
126
+
127
+ get)
128
+ if [ -f "$SENTIMENT_FILE" ]; then
129
+ CURRENT=$(cat "$SENTIMENT_FILE")
130
+ echo "Current sentiment: $CURRENT"
131
+
132
+ desc=$(get_personality_data "$CURRENT" "description")
133
+ [[ -n "$desc" ]] && echo "Description: $desc"
134
+ else
135
+ echo "Current sentiment: none (voice personality only)"
136
+ fi
137
+ ;;
138
+
139
+ clear)
140
+ rm -f "$SENTIMENT_FILE"
141
+ echo "🎭 Sentiment cleared - using voice personality only"
142
+ ;;
143
+
144
+ *)
145
+ # If a single argument is provided and it's not a command, treat it as "set <sentiment>"
146
+ if [[ -n "$1" ]] && [[ -f "$PERSONALITIES_DIR/${1}.md" ]]; then
147
+ exec "$0" set "$1"
148
+ else
149
+ echo "AgentVibes Sentiment Manager"
150
+ echo ""
151
+ echo "Commands:"
152
+ echo " list - List all sentiments"
153
+ echo " set <name> - Set sentiment for current voice"
154
+ echo " get - Show current sentiment"
155
+ echo " clear - Clear sentiment"
156
+ echo ""
157
+ echo "Examples:"
158
+ echo " /agent-vibes:sentiment flirty # Add flirty style to current voice"
159
+ echo " /agent-vibes:sentiment sarcastic # Add sarcasm to current voice"
160
+ echo " /agent-vibes:sentiment clear # Remove sentiment"
161
+ fi
162
+ ;;
163
+ esac
@@ -0,0 +1,349 @@
1
+ #!/bin/bash
2
+ # Voice Manager - Handle voice switching and listing
3
+ # Usage: voice-manager.sh [list|switch|get] [voice_name]
4
+
5
+ # Get script directory (physical path for sourcing files)
6
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
7
+ source "$SCRIPT_DIR/voices-config.sh"
8
+
9
+ # Project-local file first, global fallback
10
+ # Use the logical path from BASH_SOURCE to find .claude directory
11
+ # This handles both normal installations and symlinked hooks directories correctly
12
+ SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
+ CLAUDE_DIR="$(dirname "$SCRIPT_PATH")"
14
+
15
+ # Check if we have a project-local .claude directory
16
+ if [[ -d "$CLAUDE_DIR" ]] && [[ "$CLAUDE_DIR" != "$HOME/.claude" ]]; then
17
+ VOICE_FILE="$CLAUDE_DIR/tts-voice.txt"
18
+ else
19
+ # Fallback to global
20
+ VOICE_FILE="$HOME/.claude/tts-voice.txt"
21
+ fi
22
+
23
+ case "$1" in
24
+ list)
25
+ echo "🎤 Available TTS Voices:"
26
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
27
+ CURRENT_VOICE=$(cat "$VOICE_FILE" 2>/dev/null || echo "Cowboy")
28
+ for voice in "${!VOICES[@]}"; do
29
+ if [ "$voice" = "$CURRENT_VOICE" ]; then
30
+ echo " ▶ $voice (current)"
31
+ else
32
+ echo " $voice"
33
+ fi
34
+ done | sort
35
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
36
+ echo ""
37
+ echo "Usage: voice-manager.sh switch <name>"
38
+ echo " voice-manager.sh preview"
39
+ ;;
40
+
41
+ preview)
42
+ # Get play-tts.sh path
43
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
44
+ TTS_SCRIPT="$SCRIPT_DIR/play-tts.sh"
45
+
46
+ # Check if a specific voice name was provided
47
+ if [[ -n "$2" ]] && [[ "$2" != "first" ]] && [[ "$2" != "last" ]] && ! [[ "$2" =~ ^[0-9]+$ ]]; then
48
+ # User specified a voice name
49
+ VOICE_NAME="$2"
50
+
51
+ # Check if voice exists
52
+ if [[ -n "${VOICES[$VOICE_NAME]}" ]]; then
53
+ echo "🎤 Previewing voice: ${VOICE_NAME}"
54
+ echo ""
55
+ "$TTS_SCRIPT" "Hello, this is ${VOICE_NAME}. How do you like my voice?" "${VOICE_NAME}"
56
+ else
57
+ echo "❌ Voice not found: ${VOICE_NAME}"
58
+ echo ""
59
+ echo "Available voices:"
60
+ for voice in "${!VOICES[@]}"; do
61
+ echo " • $voice"
62
+ done | sort
63
+ fi
64
+ exit 0
65
+ fi
66
+
67
+ # Original preview logic for first/last/number
68
+ echo "🎤 Voice Preview - Playing first 3 voices..."
69
+ echo ""
70
+
71
+ # Sort voices and preview first 3
72
+ VOICE_ARRAY=()
73
+ for voice in "${!VOICES[@]}"; do
74
+ VOICE_ARRAY+=("$voice")
75
+ done
76
+
77
+ # Sort the array
78
+ IFS=$'\n' SORTED_VOICES=($(sort <<<"${VOICE_ARRAY[*]}"))
79
+ unset IFS
80
+
81
+ # Play first 3 voices
82
+ COUNT=0
83
+ for voice in "${SORTED_VOICES[@]}"; do
84
+ if [ $COUNT -eq 3 ]; then
85
+ break
86
+ fi
87
+ echo "🔊 ${voice}..."
88
+ "$TTS_SCRIPT" "Hi, I'm ${voice}" "${VOICES[$voice]}"
89
+ sleep 0.5
90
+ COUNT=$((COUNT + 1))
91
+ done
92
+
93
+ echo ""
94
+ echo "Would you like to hear more? Reply 'yes' to continue."
95
+ ;;
96
+
97
+ switch)
98
+ VOICE_NAME="$2"
99
+ SILENT_MODE=false
100
+
101
+ # Check for --silent flag
102
+ if [[ "$2" == "--silent" ]] || [[ "$3" == "--silent" ]]; then
103
+ SILENT_MODE=true
104
+ # If --silent is first arg, voice name is in $3
105
+ [[ "$2" == "--silent" ]] && VOICE_NAME="$3"
106
+ fi
107
+
108
+ if [[ -z "$VOICE_NAME" ]]; then
109
+ # Show numbered list for selection
110
+ echo "🎤 Select a voice by number:"
111
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
112
+
113
+ # Get current voice
114
+ CURRENT="Cowboy Bob"
115
+ if [ -f "$VOICE_FILE" ]; then
116
+ CURRENT=$(cat "$VOICE_FILE")
117
+ fi
118
+
119
+ # Create array of voice names
120
+ VOICE_ARRAY=()
121
+ for voice in "${!VOICES[@]}"; do
122
+ VOICE_ARRAY+=("$voice")
123
+ done
124
+
125
+ # Sort the array
126
+ IFS=$'\n' SORTED_VOICES=($(sort <<<"${VOICE_ARRAY[*]}"))
127
+ unset IFS
128
+
129
+ # Display numbered list in two columns for compactness
130
+ HALF=$(( (${#SORTED_VOICES[@]} + 1) / 2 ))
131
+
132
+ for i in $(seq 0 $((HALF - 1))); do
133
+ NUM1=$((i + 1))
134
+ VOICE1="${SORTED_VOICES[$i]}"
135
+
136
+ # Format first column
137
+ if [[ "$VOICE1" == "$CURRENT" ]]; then
138
+ COL1=$(printf "%2d. %-20s ✓" "$NUM1" "$VOICE1")
139
+ else
140
+ COL1=$(printf "%2d. %-20s " "$NUM1" "$VOICE1")
141
+ fi
142
+
143
+ # Format second column if it exists
144
+ NUM2=$((i + HALF + 1))
145
+ if [[ $((i + HALF)) -lt ${#SORTED_VOICES[@]} ]]; then
146
+ VOICE2="${SORTED_VOICES[$((i + HALF))]}"
147
+ if [[ "$VOICE2" == "$CURRENT" ]]; then
148
+ COL2=$(printf "%2d. %-20s ✓" "$NUM2" "$VOICE2")
149
+ else
150
+ COL2=$(printf "%2d. %-20s " "$NUM2" "$VOICE2")
151
+ fi
152
+ echo " $COL1 $COL2"
153
+ else
154
+ echo " $COL1"
155
+ fi
156
+ done
157
+
158
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
159
+ echo ""
160
+ echo "Enter number (1-${#SORTED_VOICES[@]}) or voice name:"
161
+ echo "Usage: /agent-vibes:switch 5"
162
+ echo " /agent-vibes:switch \"Northern Terry\""
163
+ exit 0
164
+ fi
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
173
+
174
+ # Sort the array
175
+ IFS=$'\n' SORTED_VOICES=($(sort <<<"${VOICE_ARRAY[*]}"))
176
+ unset IFS
177
+
178
+ # Get voice by number (adjust for 0-based index)
179
+ INDEX=$((VOICE_NAME - 1))
180
+
181
+ if [[ $INDEX -ge 0 && $INDEX -lt ${#SORTED_VOICES[@]} ]]; then
182
+ VOICE_NAME="${SORTED_VOICES[$INDEX]}"
183
+ FOUND="${SORTED_VOICES[$INDEX]}"
184
+ else
185
+ echo "❌ Invalid number. Please choose between 1 and ${#SORTED_VOICES[@]}"
186
+ exit 1
187
+ 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
+
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
207
+ fi
208
+
209
+ echo "$FOUND" > "$VOICE_FILE"
210
+ echo "✅ Voice switched to: $FOUND"
211
+ echo "🎤 Voice ID: ${VOICES[$FOUND]}"
212
+
213
+ # Have the new voice introduce itself (unless silent mode)
214
+ if [[ "$SILENT_MODE" != "true" ]]; then
215
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
216
+ PLAY_TTS="$SCRIPT_DIR/play-tts.sh"
217
+ if [ -x "$PLAY_TTS" ]; then
218
+ "$PLAY_TTS" "Hi, I'm $FOUND. I'll be your voice assistant moving forward." "$FOUND" > /dev/null 2>&1 &
219
+ fi
220
+ fi
221
+ ;;
222
+
223
+ get)
224
+ if [ -f "$VOICE_FILE" ]; then
225
+ cat "$VOICE_FILE"
226
+ else
227
+ echo "Cowboy Bob"
228
+ fi
229
+ ;;
230
+
231
+ whoami)
232
+ echo "🎤 Current Voice Configuration"
233
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
234
+
235
+ # Get current voice
236
+ if [ -f "$VOICE_FILE" ]; then
237
+ CURRENT_VOICE=$(cat "$VOICE_FILE")
238
+ else
239
+ CURRENT_VOICE="Cowboy Bob"
240
+ fi
241
+ echo "Voice: $CURRENT_VOICE"
242
+
243
+ # Get current sentiment (priority)
244
+ if [ -f "$HOME/.claude/tts-sentiment.txt" ]; then
245
+ SENTIMENT=$(cat "$HOME/.claude/tts-sentiment.txt")
246
+ echo "Sentiment: $SENTIMENT (active)"
247
+
248
+ # Also show personality if set
249
+ if [ -f "$HOME/.claude/tts-personality.txt" ]; then
250
+ PERSONALITY=$(cat "$HOME/.claude/tts-personality.txt")
251
+ echo "Personality: $PERSONALITY (overridden by sentiment)"
252
+ fi
253
+ else
254
+ # No sentiment, check personality
255
+ if [ -f "$HOME/.claude/tts-personality.txt" ]; then
256
+ PERSONALITY=$(cat "$HOME/.claude/tts-personality.txt")
257
+ echo "Personality: $PERSONALITY (active)"
258
+ else
259
+ echo "Personality: normal"
260
+ fi
261
+ fi
262
+
263
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
264
+ ;;
265
+
266
+ list-simple)
267
+ # Simple list for AI to parse and display
268
+ for voice in "${!VOICES[@]}"; do
269
+ echo "$voice"
270
+ done | sort
271
+ ;;
272
+
273
+ replay)
274
+ # Replay recent TTS audio from history
275
+ # Use project-local directory with same logic as play-tts.sh
276
+ if [[ -n "$CLAUDE_PROJECT_DIR" ]]; then
277
+ AUDIO_DIR="$CLAUDE_PROJECT_DIR/.claude/audio"
278
+ else
279
+ # Fallback: try to find .claude directory in current path
280
+ CURRENT_DIR="$PWD"
281
+ while [[ "$CURRENT_DIR" != "/" ]]; do
282
+ if [[ -d "$CURRENT_DIR/.claude" ]]; then
283
+ AUDIO_DIR="$CURRENT_DIR/.claude/audio"
284
+ break
285
+ fi
286
+ CURRENT_DIR=$(dirname "$CURRENT_DIR")
287
+ done
288
+ # Final fallback to global if no project .claude found
289
+ if [[ -z "$AUDIO_DIR" ]]; then
290
+ AUDIO_DIR="$HOME/.claude/audio"
291
+ fi
292
+ fi
293
+
294
+ # Default to replay last audio (N=1)
295
+ N="${2:-1}"
296
+
297
+ # Validate N is a number
298
+ if ! [[ "$N" =~ ^[0-9]+$ ]]; then
299
+ echo "❌ Invalid argument. Please use a number (1-10)"
300
+ echo "Usage: /agent-vibes:replay [N]"
301
+ echo " N=1 - Last audio (default)"
302
+ echo " N=2 - Second-to-last"
303
+ echo " N=3 - Third-to-last"
304
+ exit 1
305
+ fi
306
+
307
+ # Check bounds
308
+ if [[ $N -lt 1 || $N -gt 10 ]]; then
309
+ echo "❌ Number out of range. Please choose 1-10"
310
+ exit 1
311
+ fi
312
+
313
+ # Get list of audio files sorted by time (newest first)
314
+ if [[ ! -d "$AUDIO_DIR" ]]; then
315
+ echo "❌ No audio history found"
316
+ echo "Audio files are stored in: $AUDIO_DIR"
317
+ exit 1
318
+ fi
319
+
320
+ # Get the Nth most recent file
321
+ AUDIO_FILE=$(ls -t "$AUDIO_DIR"/tts-*.mp3 2>/dev/null | sed -n "${N}p")
322
+
323
+ if [[ -z "$AUDIO_FILE" ]]; then
324
+ TOTAL=$(ls -t "$AUDIO_DIR"/tts-*.mp3 2>/dev/null | wc -l)
325
+ echo "❌ Audio #$N not found in history"
326
+ echo "Total audio files available: $TOTAL"
327
+ exit 1
328
+ fi
329
+
330
+ echo "🔊 Replaying audio #$N:"
331
+ echo " File: $(basename "$AUDIO_FILE")"
332
+ echo " Path: $AUDIO_FILE"
333
+
334
+ # Play the audio file in background
335
+ (paplay "$AUDIO_FILE" 2>/dev/null || aplay "$AUDIO_FILE" 2>/dev/null || mpg123 "$AUDIO_FILE" 2>/dev/null) &
336
+ ;;
337
+
338
+ *)
339
+ echo "Usage: voice-manager.sh [list|switch|get|replay|whoami] [voice_name]"
340
+ echo ""
341
+ echo "Commands:"
342
+ echo " list - List all available voices"
343
+ echo " switch <voice_name> - Switch to a different voice"
344
+ echo " get - Get current voice name"
345
+ echo " replay [N] - Replay Nth most recent audio (default: 1)"
346
+ echo " whoami - Show current voice and personality"
347
+ exit 1
348
+ ;;
349
+ esac
@@ -0,0 +1,33 @@
1
+ #!/bin/bash
2
+ # Single source of truth for ElevenLabs voice IDs
3
+ # This file is sourced by both voice-manager.sh and play-tts.sh
4
+
5
+ declare -A VOICES=(
6
+ ["Amy"]="bhJUNIXWQQ94l8eI2VUf"
7
+ ["Antoni"]="ErXwobaYiN019PkySvjV"
8
+ ["Archer"]="L0Dsvb3SLTyegXwtm47J"
9
+ ["Aria"]="TC0Zp7WVFzhA8zpTlRqV"
10
+ ["Bella"]="EXAVITQu4vr4xnSDxMaL"
11
+ ["Burt Reynolds"]="4YYIPFl9wE5c4L2eu2Gb"
12
+ ["Charlotte"]="XB0fDUnXU5powFXDhCwa"
13
+ ["Cowboy Bob"]="KTPVrSVAEUSJRClDzBw7"
14
+ ["Demon Monster"]="vfaqCOvlrKi4Zp7C2IAm"
15
+ ["Domi"]="AZnzlk1XvdvUeBnXmlld"
16
+ ["Dr. Von Fusion"]="yjJ45q8TVCrtMhEKurxY"
17
+ ["Drill Sergeant"]="vfaqCOvlrKi4Zp7C2IAm"
18
+ ["Grandpa Spuds Oxley"]="NOpBlnGInO9m6vDvFkFC"
19
+ ["Grandpa Werthers"]="MKlLqCItoCkvdhrxgtLv"
20
+ ["Jessica Anne Bogart"]="flHkNRp1BlvT73UL6gyz"
21
+ ["Juniper"]="aMSt68OGf4xUZAnLpTU8"
22
+ ["Lutz Laugh"]="9yzdeviXkFddZ4Oz8Mok"
23
+ ["Matilda"]="XrExE9yKIg1WjnnlVkGX"
24
+ ["Matthew Schmitz"]="0SpgpJ4D3MpHCiWdyTg3"
25
+ ["Michael"]="U1Vk2oyatMdYs096Ety7"
26
+ ["Ms. Walker"]="DLsHlh26Ugcm6ELvS0qi"
27
+ ["Northern Terry"]="wo6udizrrtpIxWGp2qJk"
28
+ ["Pirate Marshal"]="PPzYpIqttlTYA83688JI"
29
+ ["Rachel"]="21m00Tcm4TlvDq8ikWAM"
30
+ ["Ralf Eisend"]="A9evEp8yGjv4c3WsIKuY"
31
+ ["Tiffany"]="6aDn1KB0hjpdcocrUkmq"
32
+ ["Tom"]="DYkrAHD8iwork3YSUBbs"
33
+ )