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.
- package/.claude/commands/agent-vibes/clean.md +18 -0
- package/.claude/commands/agent-vibes/commands.json +5 -1
- package/.claude/config/audio-effects.cfg +1 -1
- package/.claude/config/background-music-position.txt +1 -1
- package/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks/audio-cache-utils.sh +246 -0
- package/.claude/hooks/clawdbot-receiver-SECURE.sh +131 -0
- package/.claude/hooks/clawdbot-receiver.sh +28 -13
- package/.claude/hooks/clean-audio-cache.sh +22 -0
- package/.claude/hooks/piper-download-voices.sh +31 -18
- package/.claude/hooks/play-tts-macos.sh +63 -2
- package/.claude/hooks/play-tts-piper.sh +75 -7
- package/.claude/hooks/play-tts.sh +1 -0
- package/.clawdbot/skill/SKILL.md +144 -335
- package/README.md +272 -58
- package/RELEASE_NOTES.md +308 -0
- package/mcp-server/server.py +21 -0
- package/package.json +1 -1
- package/src/installer.js +176 -16
|
@@ -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
|
|
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
|
-
|
|
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,
|
|
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
|
-
#
|
|
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 "🎤
|
|
74
|
+
echo "🎤 Voice: $VOICE | Agent: $DECODED_AGENT" >&2
|
|
59
75
|
|
|
60
|
-
#
|
|
61
|
-
cd "$AGENTVIBES_ROOT"
|
|
62
|
-
|
|
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-
|
|
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
|
-
|
|
133
|
-
|
|
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
|
-
|
|
152
|
-
|
|
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
|
-
|
|
169
|
-
|
|
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
|
-
|
|
186
|
+
voice_path="$VOICE_DIR/${voice}.onnx"
|
|
179
187
|
if [[ -f "$voice_path" ]]; then
|
|
180
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
211
|
-
|
|
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
|