agentvibes 3.3.0-alpha.6 → 3.3.0

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.
@@ -0,0 +1,18 @@
1
+ ---
2
+ description: Clean all TTS audio cache files (non-interactive)
3
+ scope: user
4
+ ---
5
+
6
+ Execute the AgentVibes non-interactive cleanup script using the Bash tool:
7
+
8
+ ```bash
9
+ .claude/hooks/clean-audio-cache.sh
10
+ ```
11
+
12
+ This will:
13
+ - Count all TTS cache files (tts-*.wav, tts-*.mp3, tts-*.aiff, tts-padded-*)
14
+ - Delete all cache files immediately (no confirmation required)
15
+ - Report the number of files deleted and space freed
16
+ - Preserve background music tracks in the tracks/ subdirectory
17
+
18
+ **Note:** For interactive cleanup with confirmation prompts, use `/agent-vibes:cleanup` instead.
@@ -135,7 +135,11 @@
135
135
  },
136
136
  {
137
137
  "name": "cleanup",
138
- "description": "Clean up TTS cache files to free up disk space"
138
+ "description": "Clean up TTS cache files to free up disk space (interactive)"
139
+ },
140
+ {
141
+ "name": "clean",
142
+ "description": "Clean all TTS audio cache files (non-interactive)"
139
143
  }
140
144
  ]
141
145
  }
@@ -49,4 +49,4 @@ BMad Master|reverb 50 60 100 pitch -100|agentvibes_soft_flamenco_loop.mp3|0.30
49
49
  _party_mode|compand 0.3,1 6:-70,-60,-20|agent_vibes_dark_chill_step_loop.mp3|0.40
50
50
  |||
51
51
  # Default (no agent specified) - clean with Bachata background|||
52
- default|reverb 20 50 50|agent_vibes_ganawa_ambient_v2_loop.mp3|0.30
52
+ default|reverb 40 50 70|agentvibes_soft_flamenco_loop.mp3|0.30
@@ -20,7 +20,7 @@ agent_vibes_japanese_city_pop_v1_loop.mp3:6.054512
20
20
  agent_vibes_bossa_nova_v2_loop.mp3:5.369524
21
21
  agent_vibes_salsa_v2_loop.mp3:9.972790
22
22
  agent_vibes_cumbia_v1_loop.mp3:5.717823
23
- agentvibes_soft_flamenco_loop.mp3:4.998005
24
23
  agent_vibes_arabic_v2_loop.mp3:.00000000000000000006132724
25
24
  agent_vibes_chillwave_v2_loop.mp3:14.628390
26
25
  agent_vibes_bachata_v1_loop.mp3:.00000000000000000005344000
26
+ agentvibes_soft_flamenco_loop.mp3:.00000000000000000006934441
@@ -1 +1 @@
1
- 20260201
1
+ 20260205
@@ -0,0 +1,246 @@
1
+ #!/usr/bin/env bash
2
+ # AgentVibes Audio Cache Utility Functions
3
+ # Provides common functions for file counting, sizing, and cleanup operations
4
+
5
+ # Get the audio directory path with priority order
6
+ # Returns: Absolute path to audio directory
7
+ get_audio_dir() {
8
+ local audio_dir=""
9
+
10
+ # Priority 1: Project-local directory (if CLAUDE_PROJECT_DIR is set)
11
+ if [[ -n "${CLAUDE_PROJECT_DIR:-}" ]]; then
12
+ audio_dir="$CLAUDE_PROJECT_DIR/.claude/audio"
13
+ else
14
+ # Priority 2: Walk up directory tree to find .claude
15
+ local current_dir="$PWD"
16
+ while [[ "$current_dir" != "/" ]]; do
17
+ if [[ -d "$current_dir/.claude" ]]; then
18
+ audio_dir="$current_dir/.claude/audio"
19
+ break
20
+ fi
21
+ current_dir=$(dirname "$current_dir")
22
+ done
23
+ fi
24
+
25
+ # Priority 3: Fallback to global ~/.claude/audio
26
+ if [[ -z "$audio_dir" ]]; then
27
+ audio_dir="$HOME/.claude/audio"
28
+ fi
29
+
30
+ echo "$audio_dir"
31
+ }
32
+
33
+ # Count TTS audio files (excludes background music tracks in tracks/ subdirectory)
34
+ # Args: $1 = audio_dir (optional, defaults to get_audio_dir)
35
+ # Returns: Integer count
36
+ count_tts_files() {
37
+ local audio_dir="${1:-$(get_audio_dir)}"
38
+
39
+ if [[ ! -d "$audio_dir" ]]; then
40
+ echo "0"
41
+ return
42
+ fi
43
+
44
+ # Count TTS output files only (excludes subdirectories like tracks/)
45
+ local count=0
46
+ count=$(find "$audio_dir" -maxdepth 1 -type f \( \
47
+ -name "tts-*.wav" -o \
48
+ -name "tts-*.mp3" -o \
49
+ -name "tts-*.aiff" -o \
50
+ -name "tts-padded-*.mp3" -o \
51
+ -name "tts-padded-*.wav" \
52
+ \) 2>/dev/null | wc -l)
53
+
54
+ echo "$count"
55
+ }
56
+
57
+ # Calculate total size of TTS audio files in bytes
58
+ # Args: $1 = audio_dir (optional, defaults to get_audio_dir)
59
+ # Returns: Size in bytes (integer)
60
+ calculate_tts_size_bytes() {
61
+ local audio_dir="${1:-$(get_audio_dir)}"
62
+
63
+ if [[ ! -d "$audio_dir" ]]; then
64
+ echo "0"
65
+ return
66
+ fi
67
+
68
+ local total_bytes=0
69
+ local stat_cmd=""
70
+
71
+ # Detect stat command format (BSD vs GNU)
72
+ if stat -c%s /dev/null >/dev/null 2>&1; then
73
+ stat_cmd="stat -c%s"
74
+ else
75
+ stat_cmd="stat -f%z"
76
+ fi
77
+
78
+ # Sum file sizes for all TTS files
79
+ while IFS= read -r file; do
80
+ if [[ -f "$file" ]]; then
81
+ local size=$($stat_cmd "$file" 2>/dev/null || echo "0")
82
+ total_bytes=$((total_bytes + size))
83
+ fi
84
+ done < <(find "$audio_dir" -maxdepth 1 -type f \( \
85
+ -name "tts-*.wav" -o \
86
+ -name "tts-*.mp3" -o \
87
+ -name "tts-*.aiff" -o \
88
+ -name "tts-padded-*.mp3" -o \
89
+ -name "tts-padded-*.wav" \
90
+ \) 2>/dev/null)
91
+
92
+ echo "$total_bytes"
93
+ }
94
+
95
+ # Convert bytes to human-readable format (e.g., "1.4MB", "230KB")
96
+ # Args: $1 = bytes
97
+ # Returns: Human-readable string (B, KB, MB, GB)
98
+ bytes_to_human() {
99
+ local bytes="${1:-0}"
100
+
101
+ if [[ ! "$bytes" =~ ^[0-9]+$ ]]; then
102
+ bytes="0"
103
+ fi
104
+
105
+ if [[ $bytes -lt 1024 ]]; then
106
+ echo "${bytes}B"
107
+ elif [[ $bytes -lt 1048576 ]]; then
108
+ awk "BEGIN {printf \"%.1fKB\", $bytes/1024}"
109
+ elif [[ $bytes -lt 1073741824 ]]; then
110
+ awk "BEGIN {printf \"%.1fMB\", $bytes/1048576}"
111
+ else
112
+ awk "BEGIN {printf \"%.1fGB\", $bytes/1073741824}"
113
+ fi
114
+ }
115
+
116
+ # Get auto-cleanup threshold (number of files before cleanup triggers)
117
+ # Returns: Integer threshold (default: 50)
118
+ get_auto_clean_threshold() {
119
+ local threshold_file=""
120
+ local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
121
+
122
+ # Priority order for config file
123
+ if [[ -n "${CLAUDE_PROJECT_DIR:-}" ]] && [[ -f "$CLAUDE_PROJECT_DIR/.claude/tts-auto-clean-threshold.txt" ]]; then
124
+ threshold_file="$CLAUDE_PROJECT_DIR/.claude/tts-auto-clean-threshold.txt"
125
+ elif [[ -f "$script_dir/../tts-auto-clean-threshold.txt" ]]; then
126
+ threshold_file="$script_dir/../tts-auto-clean-threshold.txt"
127
+ elif [[ -f "$HOME/.claude/tts-auto-clean-threshold.txt" ]]; then
128
+ threshold_file="$HOME/.claude/tts-auto-clean-threshold.txt"
129
+ fi
130
+
131
+ if [[ -n "$threshold_file" ]]; then
132
+ local threshold=$(grep -v '^\s*#' "$threshold_file" 2>/dev/null | grep -v '^\s*$' | head -1)
133
+ if [[ "$threshold" =~ ^[0-9]+$ ]]; then
134
+ echo "$threshold"
135
+ return
136
+ fi
137
+ fi
138
+
139
+ # Default threshold
140
+ echo "50"
141
+ }
142
+
143
+ # Delete oldest TTS files to stay under size threshold (in MB)
144
+ # Args: $1 = audio_dir, $2 = threshold (size in MB)
145
+ # Returns: Number of files deleted
146
+ auto_clean_old_files() {
147
+ local audio_dir="${1:-$(get_audio_dir)}"
148
+ local threshold_mb="${2:-15}"
149
+ local threshold_bytes=$((threshold_mb * 1048576))
150
+
151
+ if [[ ! -d "$audio_dir" ]]; then
152
+ echo "0"
153
+ return
154
+ fi
155
+
156
+ local current_size=$(calculate_tts_size_bytes "$audio_dir")
157
+
158
+ if [[ $current_size -le $threshold_bytes ]]; then
159
+ echo "0"
160
+ return
161
+ fi
162
+
163
+ # SAFETY CHECK: Skip cleanup if any TTS files have active write locks
164
+ # Check for .lock files that indicate in-progress TTS generation
165
+ local lock_count=$(find "$audio_dir" -maxdepth 1 -name "tts-*.lock" -type f 2>/dev/null | wc -l)
166
+ if [[ $lock_count -gt 0 ]]; then
167
+ # Active TTS generation in progress, skip cleanup to avoid race condition
168
+ return 0
169
+ fi
170
+
171
+ local files_deleted=0
172
+ local current_size_bytes=$current_size
173
+
174
+ # Delete oldest files until under threshold
175
+ # IMPORTANT: Only delete auto-generated TTS files, NOT project assets like welcome-multivoice-final.wav
176
+ while [[ $current_size_bytes -gt $threshold_bytes ]]; do
177
+ # Find the oldest file (only tts-processed-* and tts-padded-* files, not other project assets)
178
+ local oldest_file=$(find "$audio_dir" -maxdepth 1 -type f \( \
179
+ -name "tts-processed-*.wav" -o \
180
+ -name "tts-processed-*.mp3" -o \
181
+ -name "tts-padded-*.mp3" -o \
182
+ -name "tts-padded-*.wav" \
183
+ \) -printf '%T+ %p\n' 2>/dev/null | sort | head -1 | cut -d' ' -f2-)
184
+
185
+ if [[ -z "$oldest_file" ]]; then
186
+ break
187
+ fi
188
+
189
+ # Get file size and delete it
190
+ local file_size=$(stat -c%s "$oldest_file" 2>/dev/null || stat -f%z "$oldest_file" 2>/dev/null || echo "0")
191
+ rm -f "$oldest_file" 2>/dev/null || true
192
+ current_size_bytes=$((current_size_bytes - file_size))
193
+ files_deleted=$((files_deleted + 1))
194
+ done
195
+
196
+ echo "$files_deleted"
197
+ }
198
+
199
+ # Clean all TTS audio files and report stats
200
+ # Args: $1 = audio_dir (optional, defaults to get_audio_dir)
201
+ # Returns: Formatted output with cleanup stats
202
+ clean_all_tts_files() {
203
+ local audio_dir="${1:-$(get_audio_dir)}"
204
+
205
+ # Color codes
206
+ local RED='\033[0;31m'
207
+ local GREEN='\033[0;32m'
208
+ local YELLOW='\033[1;33m'
209
+ local BLUE='\033[0;34m'
210
+ local NC='\033[0m' # No Color
211
+
212
+ # Get stats before cleanup
213
+ local count_before=$(count_tts_files "$audio_dir")
214
+ local size_before=$(calculate_tts_size_bytes "$audio_dir")
215
+ local human_before=$(bytes_to_human "$size_before")
216
+
217
+ if [[ $count_before -eq 0 ]]; then
218
+ echo -e "${GREEN}✅ Cache is already clean! No TTS files found.${NC}"
219
+ return
220
+ fi
221
+
222
+ # Delete auto-generated TTS files only (preserve project assets like welcome-multivoice-final.wav)
223
+ find "$audio_dir" -maxdepth 1 -type f \( \
224
+ -name "tts-processed-*.wav" -o \
225
+ -name "tts-processed-*.mp3" -o \
226
+ -name "tts-padded-*.mp3" -o \
227
+ -name "tts-padded-*.wav" \
228
+ \) -delete 2>/dev/null || true
229
+
230
+ # Also delete BMAD party mode recordings if they exist
231
+ if [[ -d "$audio_dir/bmad-party-mode-recordings" ]]; then
232
+ rm -rf "$audio_dir/bmad-party-mode-recordings" 2>/dev/null || true
233
+ fi
234
+
235
+ # Get stats after cleanup
236
+ local count_after=$(count_tts_files "$audio_dir")
237
+ local size_after=$(calculate_tts_size_bytes "$audio_dir")
238
+ local human_after=$(bytes_to_human "$size_after")
239
+ local freed=$((size_before - size_after))
240
+ local human_freed=$(bytes_to_human "$freed")
241
+
242
+ echo -e "${GREEN}✅ Cleanup complete!${NC}"
243
+ echo " • Files deleted: ${YELLOW}$count_before${NC}"
244
+ echo " • Space freed: ${YELLOW}$human_freed${NC}"
245
+ echo " • Before: $human_before | After: $human_after"
246
+ }
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # File: .claude/hooks/clawdbot-receiver.sh (SECURITY-HARDENED VERSION)
4
+ #
5
+ # AgentVibes Clawdbot Receiver - SSH-Remote TTS with Agent Support and Intro Messages
6
+ # Receives base64-encoded text, voice, agent name, and optional intro from remote Clawdbot instances
7
+ # Calls AgentVibes play-tts-enhanced.sh to apply agent-specific audio effects
8
+ #
9
+ # SECURITY ENHANCEMENTS:
10
+ # - Voice name whitelist validation
11
+ # - Agent name format validation (alphanumeric only)
12
+ # - Length limits on decoded content
13
+ # - Safe HOME directory handling
14
+ #
15
+ # Usage (called via SSH from remote):
16
+ # clawdbot-receiver.sh <base64_text> <voice> <base64_agent_name> [base64_intro]
17
+ #
18
+ # Copyright (c) 2025 Paul Preibisch
19
+ # Licensed under the Apache License, Version 2.0
20
+ #
21
+
22
+ set -euo pipefail
23
+
24
+ ENCODED_TEXT="${1:-}"
25
+ VOICE="${2:-en_US-lessac-medium}"
26
+ ENCODED_AGENT="${3:-}"
27
+ ENCODED_INTRO="${4:-}"
28
+
29
+ # SECURITY: Whitelist of allowed voice names
30
+ ALLOWED_VOICES="en_US-amy-medium|en_US-lessac-medium|es_ES-mls_9972-low|es_ES-davefx-medium|en_US-joe-medium"
31
+
32
+ # Validate inputs
33
+ if [[ -z "$ENCODED_TEXT" ]]; then
34
+ echo "❌ No encoded text provided" >&2
35
+ echo "Usage: $0 <base64_text> <voice> <base64_agent_name> [base64_intro]" >&2
36
+ exit 1
37
+ fi
38
+
39
+ # SECURITY: Validate VOICE parameter against whitelist
40
+ if ! [[ "$VOICE" =~ ^($ALLOWED_VOICES)$ ]]; then
41
+ echo "❌ Invalid voice name: $VOICE" >&2
42
+ echo "Allowed voices: $(echo "$ALLOWED_VOICES" | tr '|' ', ')" >&2
43
+ exit 1
44
+ fi
45
+
46
+ # SECURITY: Decode base64 safely
47
+ # This prevents command injection from malicious text
48
+ DECODED_TEXT=$(echo -n "$ENCODED_TEXT" | base64 -d 2>/dev/null) || {
49
+ echo "❌ Failed to decode text (invalid base64)" >&2
50
+ exit 1
51
+ }
52
+
53
+ # SECURITY: Enforce length limit on decoded text (10KB max)
54
+ if [[ ${#DECODED_TEXT} -gt 10000 ]]; then
55
+ echo "❌ Decoded text too long (${#DECODED_TEXT} chars, max 10000)" >&2
56
+ exit 1
57
+ fi
58
+
59
+ DECODED_AGENT="default"
60
+ if [[ -n "$ENCODED_AGENT" ]]; then
61
+ DECODED_AGENT=$(echo -n "$ENCODED_AGENT" | base64 -d 2>/dev/null) || DECODED_AGENT="default"
62
+
63
+ # SECURITY: Validate agent name format (alphanumeric, dash, underscore only)
64
+ if ! [[ "$DECODED_AGENT" =~ ^[a-zA-Z0-9_-]+$ ]]; then
65
+ echo "⚠️ Invalid agent name format, using 'default'" >&2
66
+ DECODED_AGENT="default"
67
+ fi
68
+
69
+ # SECURITY: Enforce length limit on agent name
70
+ if [[ ${#DECODED_AGENT} -gt 50 ]]; then
71
+ echo "⚠️ Agent name too long, using 'default'" >&2
72
+ DECODED_AGENT="default"
73
+ fi
74
+ fi
75
+
76
+ # Decode and prepend intro if provided
77
+ DECODED_INTRO=""
78
+ if [[ -n "$ENCODED_INTRO" ]]; then
79
+ DECODED_INTRO=$(echo -n "$ENCODED_INTRO" | base64 -d 2>/dev/null) || DECODED_INTRO=""
80
+
81
+ # SECURITY: Enforce length limit on intro message
82
+ if [[ ${#DECODED_INTRO} -gt 200 ]]; then
83
+ echo "⚠️ Intro message too long, truncating" >&2
84
+ DECODED_INTRO="${DECODED_INTRO:0:200}"
85
+ fi
86
+ fi
87
+
88
+ # Prepend intro to text if configured
89
+ if [[ -n "$DECODED_INTRO" ]]; then
90
+ DECODED_TEXT="${DECODED_INTRO} ${DECODED_TEXT}"
91
+ fi
92
+
93
+ # Find AgentVibes installation
94
+ # SECURITY: Validate HOME is set and use absolute paths
95
+ if [[ -z "${HOME:-}" ]]; then
96
+ echo "❌ HOME environment variable not set" >&2
97
+ exit 1
98
+ fi
99
+
100
+ # Priority: 1. ~/agentvibes, 2. ~/AgentVibes-dev, 3. current directory
101
+ AGENTVIBES_ROOT=""
102
+ if [[ -f "$HOME/agentvibes/.claude/hooks/play-tts-enhanced.sh" ]]; then
103
+ AGENTVIBES_ROOT="$HOME/agentvibes"
104
+ elif [[ -f "$HOME/AgentVibes-dev/.claude/hooks/play-tts-enhanced.sh" ]]; then
105
+ AGENTVIBES_ROOT="$HOME/AgentVibes-dev"
106
+ elif [[ -f ".claude/hooks/play-tts-enhanced.sh" ]]; then
107
+ AGENTVIBES_ROOT="$(pwd)"
108
+ else
109
+ echo "❌ AgentVibes not found" >&2
110
+ echo "💡 Install AgentVibes at ~/agentvibes/ or ~/AgentVibes-dev/" >&2
111
+ exit 1
112
+ fi
113
+
114
+ # SECURITY: Validate AgentVibes root is a directory
115
+ if [[ ! -d "$AGENTVIBES_ROOT" ]]; then
116
+ echo "❌ AgentVibes root is not a directory: $AGENTVIBES_ROOT" >&2
117
+ exit 1
118
+ fi
119
+
120
+ echo "🎤 Voice: $VOICE | Agent: $DECODED_AGENT | Vol: 30% | Effects: default" >&2
121
+
122
+ # Call AgentVibes play-tts-enhanced.sh with agent name for audio-effects.cfg lookup
123
+ # SECURITY: Use absolute path and proper quoting
124
+ cd "$AGENTVIBES_ROOT" && \
125
+ bash .claude/hooks/play-tts-enhanced.sh "$DECODED_TEXT" "$DECODED_AGENT" "$VOICE" 2>&1 || {
126
+ # Fallback to standard play-tts if enhanced fails
127
+ echo "⚠️ Enhanced TTS failed, using standard TTS" >&2
128
+ bash .claude/hooks/play-tts.sh "$DECODED_TEXT" "$VOICE" 2>&1
129
+ }
130
+
131
+ exit 0
@@ -2,14 +2,20 @@
2
2
  #
3
3
  # File: .claude/hooks/clawdbot-receiver.sh
4
4
  #
5
- # AgentVibes Clawdbot Receiver - SSH-Remote TTS with Agent Support
6
- # Receives base64-encoded text, voice, and agent name from remote Clawdbot instances
7
- # Calls AgentVibes play-tts-enhanced.sh to apply agent-specific audio effects
5
+ # AgentVibes Clawdbot Receiver - SSH-Remote TTS with Agent Support and Intro Messages
6
+ # Receives base64-encoded text, voice, agent name, and optional intro from remote Clawdbot instances
8
7
  #
9
8
  # Usage (called via SSH from remote):
10
- # clawdbot-receiver.sh <base64_text> <voice> <base64_agent_name>
9
+ # clawdbot-receiver.sh <base64_text> <voice> <base64_agent_name> [base64_intro]
11
10
  #
12
- # Installed at: ~/.termux/agentvibes-play.sh or ~/.agentvibes/play-remote.sh
11
+ # Parameters:
12
+ # base64_text - The main TTS text (base64 encoded)
13
+ # voice - Piper voice name (e.g., en_US-amy-medium)
14
+ # base64_agent_name - Agent name for audio effects lookup (base64 encoded)
15
+ # base64_intro - Optional intro message to prepend (base64 encoded)
16
+ # e.g., "Orion ClawdBot here." or "Samuel, your assistant."
17
+ #
18
+ # The intro is prepended to the text: "${INTRO} ${TEXT}"
13
19
  #
14
20
  # Copyright (c) 2025 Paul Preibisch
15
21
  # Licensed under the Apache License, Version 2.0
@@ -20,16 +26,16 @@ set -euo pipefail
20
26
  ENCODED_TEXT="${1:-}"
21
27
  VOICE="${2:-en_US-lessac-medium}"
22
28
  ENCODED_AGENT="${3:-}"
29
+ ENCODED_INTRO="${4:-}"
23
30
 
24
31
  # Validate inputs
25
32
  if [[ -z "$ENCODED_TEXT" ]]; then
26
33
  echo "❌ No encoded text provided" >&2
27
- echo "Usage: $0 <base64_text> <voice> <base64_agent_name>" >&2
34
+ echo "Usage: $0 <base64_text> <voice> <base64_agent_name> [base64_intro]" >&2
28
35
  exit 1
29
36
  fi
30
37
 
31
38
  # SECURITY: Decode base64 safely
32
- # This prevents command injection from malicious text
33
39
  DECODED_TEXT=$(echo -n "$ENCODED_TEXT" | base64 -d 2>/dev/null) || {
34
40
  echo "❌ Failed to decode text (invalid base64)" >&2
35
41
  exit 1
@@ -40,8 +46,18 @@ if [[ -n "$ENCODED_AGENT" ]]; then
40
46
  DECODED_AGENT=$(echo -n "$ENCODED_AGENT" | base64 -d 2>/dev/null) || DECODED_AGENT="default"
41
47
  fi
42
48
 
49
+ # Decode and prepend intro if provided
50
+ DECODED_INTRO=""
51
+ if [[ -n "$ENCODED_INTRO" ]]; then
52
+ DECODED_INTRO=$(echo -n "$ENCODED_INTRO" | base64 -d 2>/dev/null) || DECODED_INTRO=""
53
+ fi
54
+
55
+ # Prepend intro to text if configured
56
+ if [[ -n "$DECODED_INTRO" ]]; then
57
+ DECODED_TEXT="${DECODED_INTRO} ${DECODED_TEXT}"
58
+ fi
59
+
43
60
  # Find AgentVibes installation
44
- # Priority: 1. ~/agentvibes, 2. ~/AgentVibes-dev, 3. current directory
45
61
  AGENTVIBES_ROOT=""
46
62
  if [[ -f "$HOME/agentvibes/.claude/hooks/play-tts-enhanced.sh" ]]; then
47
63
  AGENTVIBES_ROOT="$HOME/agentvibes"
@@ -55,12 +71,11 @@ else
55
71
  exit 1
56
72
  fi
57
73
 
58
- echo "🎤 Agent: $DECODED_AGENT | Voice: $VOICE" >&2
74
+ echo "🎤 Voice: $VOICE | Agent: $DECODED_AGENT" >&2
59
75
 
60
- # Call AgentVibes play-tts-enhanced.sh with agent name for audio-effects.cfg lookup
61
- cd "$AGENTVIBES_ROOT" && \
62
- bash .claude/hooks/play-tts-enhanced.sh "$DECODED_TEXT" "$DECODED_AGENT" "$VOICE" 2>&1 || {
63
- # Fallback to standard play-tts if enhanced fails
76
+ # Play TTS directly (lock removed temporarily for testing)
77
+ cd "$AGENTVIBES_ROOT"
78
+ bash .claude/hooks/play-tts-enhanced.sh "$DECODED_TEXT" "$DECODED_AGENT" "$VOICE" 2>&1 || {
64
79
  echo "⚠️ Enhanced TTS failed, using standard TTS" >&2
65
80
  bash .claude/hooks/play-tts.sh "$DECODED_TEXT" "$VOICE" 2>&1
66
81
  }
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+ # Non-Interactive TTS Audio Cache Cleanup
3
+ # Used by /agent-vibes:clean skill and MCP clean_audio_cache tool
4
+
5
+ set -euo pipefail
6
+
7
+ # Source audio cache utilities
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ source "$SCRIPT_DIR/audio-cache-utils.sh"
10
+
11
+ # Colors
12
+ BLUE='\033[0;34m'
13
+ NC='\033[0m' # No Color
14
+
15
+ echo -e "${BLUE}🧹 AgentVibes Cache Cleanup (Non-Interactive)${NC}"
16
+ echo ""
17
+
18
+ # Get audio directory and perform cleanup
19
+ AUDIO_DIR=$(get_audio_dir)
20
+ clean_all_tts_files "$AUDIO_DIR"
21
+
22
+ echo ""
@@ -40,6 +40,9 @@
40
40
  # @related piper-voice-manager.sh, piper-installer.sh
41
41
  #
42
42
 
43
+ # REQUIRED: Bash strict mode for security and reliability (CLAUDE.md)
44
+ set -euo pipefail
45
+
43
46
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
44
47
  source "$SCRIPT_DIR/piper-voice-manager.sh"
45
48
 
@@ -60,9 +63,8 @@ COMMON_VOICES=(
60
63
  "en_US-kathleen-low" # Clear female (13MB) - BMAD: Paige (tech-writer)
61
64
  "en_US-kusal-medium" # Male voice (13MB) - BMAD: Saif (frame-expert)
62
65
  "en_US-kristin-medium" # Female voice (13MB) - BMAD: Sally (ux-designer)
63
- "en_US-libritts_r-high" # Premium male (57MB) - BMAD: BMad Master
66
+ "en_US-libritts_r-medium" # Premium male (57MB) - BMAD: BMad Master (NOTE: Changed from -high to -medium, -high doesn't exist)
64
67
  "en_US-libritts-high" # Premium quality (57MB)
65
- "16Speakers" # Multi-speaker: 12 US + 4 UK voices (77MB) - REQUIRED for BMAD agents
66
68
  )
67
69
 
68
70
  echo "🎙️ Piper Voice Model Downloader"
@@ -74,15 +76,15 @@ echo ""
74
76
 
75
77
  # Check if piper is installed
76
78
  if ! command -v piper &> /dev/null; then
77
- echo "❌ Error: Piper TTS not installed"
78
- echo "Install with: pipx install piper-tts"
79
+ echo "❌ Error: Piper TTS not installed" >&2
80
+ echo "Install with: pipx install piper-tts" >&2
79
81
  exit 1
80
82
  fi
81
83
 
82
84
  # Get storage directory
83
85
  VOICE_DIR=$(get_voice_storage_dir)
84
86
 
85
- echo "📂 Storage location: $VOICE_DIR"
87
+ echo "📂 Storage location: \"$VOICE_DIR\""
86
88
  echo ""
87
89
 
88
90
  # Count already downloaded
@@ -100,8 +102,8 @@ for voice in "${COMMON_VOICES[@]}"; do
100
102
  done
101
103
 
102
104
  echo "📊 Status:"
103
- echo " Already downloaded: $ALREADY_DOWNLOADED voice(s)"
104
- echo " Need to download: ${#NEED_DOWNLOAD[@]} voice(s)"
105
+ echo " Already downloaded: \"$ALREADY_DOWNLOADED\" voice(s)"
106
+ echo " Need to download: \"${#NEED_DOWNLOAD[@]}\" voice(s)"
105
107
  echo ""
106
108
 
107
109
  # Show already downloaded voices
@@ -129,8 +131,14 @@ if [[ "$AUTO_YES" == "false" ]]; then
129
131
  read -p "Download ${#NEED_DOWNLOAD[@]} voice model(s)? [Y/n]: " -n 1 -r
130
132
  echo
131
133
 
132
- if [[ ! $REPLY =~ ^[Yy]$ ]] && [[ -n $REPLY ]]; then
133
- echo "❌ Download cancelled"
134
+ # Validate input is Y, y, N, n, or empty (default to Y)
135
+ if [[ ! $REPLY =~ ^[YyNn]?$ ]]; then
136
+ echo "❌ Invalid input. Please enter Y or N." >&2
137
+ exit 1
138
+ fi
139
+
140
+ if [[ $REPLY =~ ^[Nn]$ ]]; then
141
+ echo "❌ Download cancelled" >&2
134
142
  exit 0
135
143
  fi
136
144
  else
@@ -148,8 +156,8 @@ for voice in "${NEED_DOWNLOAD[@]}"; do
148
156
 
149
157
  if download_voice "$voice"; then
150
158
  ((DOWNLOADED++))
151
- local voice_path="$VOICE_DIR/${voice}.onnx"
152
- local file_size=$(du -h "$voice_path" 2>/dev/null | cut -f1)
159
+ voice_path="$VOICE_DIR/${voice}.onnx"
160
+ file_size=$(du -h "$voice_path" 2>/dev/null | cut -f1)
153
161
  echo " ✓ Downloaded: $voice"
154
162
  echo " 📁 Path: $voice_path"
155
163
  echo " 📦 Size: $file_size"
@@ -165,8 +173,8 @@ echo "📊 Download Summary:"
165
173
  echo ""
166
174
  echo "Installed voices:"
167
175
  for voice in "${ALREADY_DOWNLOADED_LIST[@]}"; do
168
- local voice_path="$VOICE_DIR/${voice}.onnx"
169
- local file_size=$(du -h "$voice_path" 2>/dev/null | cut -f1)
176
+ voice_path="$VOICE_DIR/${voice}.onnx"
177
+ file_size=$(du -h "$voice_path" 2>/dev/null | cut -f1)
170
178
  echo " ✓ $voice ($file_size)"
171
179
  echo " $voice_path"
172
180
  done
@@ -175,9 +183,9 @@ if [[ $DOWNLOADED -gt 0 ]]; then
175
183
  echo ""
176
184
  echo "Just downloaded:"
177
185
  for voice in "${NEED_DOWNLOAD[@]}"; do
178
- local voice_path="$VOICE_DIR/${voice}.onnx"
186
+ voice_path="$VOICE_DIR/${voice}.onnx"
179
187
  if [[ -f "$voice_path" ]]; then
180
- local file_size=$(du -h "$voice_path" 2>/dev/null | cut -f1)
188
+ file_size=$(du -h "$voice_path" 2>/dev/null | cut -f1)
181
189
  echo " ✓ $voice ($file_size)"
182
190
  echo " $voice_path"
183
191
  fi
@@ -188,7 +196,7 @@ if [[ $FAILED -gt 0 ]]; then
188
196
  echo ""
189
197
  echo "Failed downloads:"
190
198
  for voice in "${NEED_DOWNLOAD[@]}"; do
191
- local voice_path="$VOICE_DIR/${voice}.onnx"
199
+ voice_path="$VOICE_DIR/${voice}.onnx"
192
200
  if [[ ! -f "$voice_path" ]]; then
193
201
  echo " ✗ $voice"
194
202
  fi
@@ -207,6 +215,11 @@ if [[ $DOWNLOADED -gt 0 ]]; then
207
215
  echo " /agent-vibes:preview"
208
216
  fi
209
217
 
210
- # Always exit successfully even if some downloads failed
211
- # (individual failures are tracked in FAILED counter)
218
+ # Exit with error code if any downloads failed (Unix convention)
219
+ if [[ $FAILED -gt 0 ]]; then
220
+ echo "" >&2
221
+ echo "⚠️ Warning: $FAILED download(s) failed. Some voices may not be available." >&2
222
+ exit 1
223
+ fi
224
+
212
225
  exit 0