agentvibes 5.1.4 → 5.2.1
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/.agentvibes/config.json +23 -13
- package/.claude/commands/agent-vibes/verbosity.md +98 -89
- package/.claude/config/audio-effects.cfg +4 -1
- package/.claude/hooks/audio-cache-utils.sh +246 -246
- package/.claude/hooks/background-music-manager.sh +404 -404
- package/.claude/hooks/bmad-speak-enhanced.sh +165 -165
- package/.claude/hooks/bmad-speak.sh +290 -290
- package/.claude/hooks/bmad-tts-injector.sh +568 -568
- package/.claude/hooks/bmad-voice-manager.sh +928 -928
- package/.claude/hooks/clawdbot-receiver-SECURE.sh +129 -129
- package/.claude/hooks/clawdbot-receiver.sh +107 -107
- package/.claude/hooks/clean-audio-cache.sh +22 -22
- package/.claude/hooks/cleanup-cache.sh +106 -106
- package/.claude/hooks/configure-rdp-mode.sh +137 -137
- package/.claude/hooks/download-extra-voices.sh +244 -244
- package/.claude/hooks/effects-manager.sh +268 -268
- package/.claude/hooks/github-star-reminder.sh +154 -154
- package/.claude/hooks/language-manager.sh +362 -362
- package/.claude/hooks/learn-manager.sh +492 -492
- package/.claude/hooks/macos-voice-manager.sh +205 -205
- package/.claude/hooks/migrate-background-music.sh +125 -125
- package/.claude/hooks/migrate-to-agentvibes.sh +161 -161
- package/.claude/hooks/optimize-background-music.sh +87 -87
- package/.claude/hooks/path-resolver.sh +60 -60
- package/.claude/hooks/personality-manager.sh +448 -448
- package/.claude/hooks/piper-download-voices.sh +233 -225
- package/.claude/hooks/piper-installer.sh +292 -292
- package/.claude/hooks/piper-multispeaker-registry.sh +171 -171
- package/.claude/hooks/piper-voice-manager.sh +125 -0
- package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +97 -90
- package/.claude/hooks/play-tts-enhanced.sh +105 -105
- package/.claude/hooks/play-tts-piper.sh +16 -5
- package/.claude/hooks/play-tts-ssh-remote.sh +168 -167
- package/.claude/hooks/play-tts-termux-ssh.sh +169 -169
- package/.claude/hooks/play-tts.sh +35 -14
- package/.claude/hooks/prepare-release.sh +54 -54
- package/.claude/hooks/provider-commands.sh +617 -617
- package/.claude/hooks/provider-manager.sh +399 -399
- package/.claude/hooks/replay-target-audio.sh +95 -95
- package/.claude/hooks/sentiment-manager.sh +201 -201
- package/.claude/hooks/session-start-tts.sh +4 -1
- package/.claude/hooks/speed-manager.sh +291 -291
- package/.claude/hooks/stop-tts.sh +84 -84
- package/.claude/hooks/termux-installer.sh +261 -261
- package/.claude/hooks/translate-manager.sh +341 -341
- package/.claude/hooks/tts-queue-worker.sh +145 -145
- package/.claude/hooks/tts-queue.sh +165 -165
- package/.claude/hooks/verbosity-manager.sh +185 -178
- package/.claude/hooks/voice-manager.sh +552 -548
- package/.claude/hooks-windows/download-extra-voices.ps1 +243 -185
- package/.claude/hooks-windows/play-tts-piper.ps1 +7 -2
- package/.claude/hooks-windows/play-tts.ps1 +9 -3
- package/.claude/hooks-windows/session-start-tts.ps1 +2 -1
- package/.claude/hooks-windows/verbosity-manager.ps1 +126 -119
- package/README.md +19 -2
- package/RELEASE_NOTES.md +74 -0
- package/bin/agentvibes-voice-browser.js +1939 -1840
- package/bin/mcp-server.sh +206 -206
- package/mcp-server/server.py +87 -15
- package/package.json +1 -1
- package/src/console/tabs/receiver-tab.js +1527 -1483
- package/src/console/tabs/settings-tab.js +2 -2
- package/src/console/tabs/setup-tab.js +112 -31
- package/src/console/tabs/voices-tab.js +130 -13
- package/src/i18n/en.js +202 -202
- package/src/installer.js +79 -213
- package/src/services/llm-provider-service.js +126 -75
- package/src/services/verbosity-service.js +159 -157
- package/templates/agentvibes-receiver.sh +3 -2
|
@@ -1,568 +1,568 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# File: .claude/hooks/bmad-tts-injector.sh
|
|
4
|
-
#
|
|
5
|
-
# AgentVibes - Finally, your AI Agents can Talk Back! Text-to-Speech WITH personality for AI Assistants!
|
|
6
|
-
# Website: https://agentvibes.org
|
|
7
|
-
# Repository: https://github.com/paulpreibisch/AgentVibes
|
|
8
|
-
#
|
|
9
|
-
# Co-created by Paul Preibisch with Claude AI
|
|
10
|
-
# Copyright (c) 2025 Paul Preibisch
|
|
11
|
-
#
|
|
12
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
13
|
-
# you may not use this file except in compliance with the License.
|
|
14
|
-
# You may obtain a copy of the License at
|
|
15
|
-
#
|
|
16
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
17
|
-
#
|
|
18
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
19
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
20
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21
|
-
# See the License for the specific language governing permissions and
|
|
22
|
-
# limitations under the License.
|
|
23
|
-
#
|
|
24
|
-
# DISCLAIMER: This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
25
|
-
# express or implied. Use at your own risk. See the Apache License for details.
|
|
26
|
-
#
|
|
27
|
-
# ---
|
|
28
|
-
#
|
|
29
|
-
# @fileoverview BMAD TTS Injection Manager - Patches BMAD agents for TTS integration
|
|
30
|
-
# @context Automatically modifies BMAD agent YAML files to include AgentVibes TTS capabilities
|
|
31
|
-
# @architecture Injects TTS hooks into activation-instructions and core_principles sections
|
|
32
|
-
# @dependencies bmad-core/agents/*.md files, play-tts.sh, bmad-voice-manager.sh
|
|
33
|
-
# @entrypoints Called via bmad-tts-injector.sh {enable|disable|status|restore}
|
|
34
|
-
# @patterns File patching with backup, provider-aware voice mapping, injection markers for idempotency
|
|
35
|
-
# @related play-tts.sh, bmad-voice-manager.sh, .bmad-core/agents/*.md
|
|
36
|
-
#
|
|
37
|
-
|
|
38
|
-
set -e # Exit on error
|
|
39
|
-
|
|
40
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
41
|
-
CLAUDE_DIR="$(dirname "$SCRIPT_DIR")"
|
|
42
|
-
|
|
43
|
-
# Colors for output
|
|
44
|
-
GREEN='\033[0;32m'
|
|
45
|
-
YELLOW='\033[1;33m'
|
|
46
|
-
RED='\033[0;31m'
|
|
47
|
-
CYAN='\033[0;36m'
|
|
48
|
-
GRAY='\033[0;90m'
|
|
49
|
-
NC='\033[0m' # No Color
|
|
50
|
-
|
|
51
|
-
# Detect BMAD installation and version
|
|
52
|
-
# Supports both v4 (.bmad-core/) and v6-alpha (.bmad/) installations
|
|
53
|
-
detect_bmad() {
|
|
54
|
-
local bmad_core_dir=""
|
|
55
|
-
|
|
56
|
-
# Check for v6-alpha first (newer version with dot prefix)
|
|
57
|
-
if [[ -d ".bmad" ]]; then
|
|
58
|
-
bmad_core_dir=".bmad"
|
|
59
|
-
elif [[ -d "../.bmad" ]]; then
|
|
60
|
-
bmad_core_dir="../.bmad"
|
|
61
|
-
# Check for v6-alpha without dot (legacy naming)
|
|
62
|
-
elif [[ -d "bmad" ]]; then
|
|
63
|
-
bmad_core_dir="bmad"
|
|
64
|
-
elif [[ -d "../bmad" ]]; then
|
|
65
|
-
bmad_core_dir="../bmad"
|
|
66
|
-
# Check for v4 (legacy)
|
|
67
|
-
elif [[ -d ".bmad-core" ]]; then
|
|
68
|
-
bmad_core_dir=".bmad-core"
|
|
69
|
-
elif [[ -d "../.bmad-core" ]]; then
|
|
70
|
-
bmad_core_dir="../.bmad-core"
|
|
71
|
-
# Check for bmad-core (without dot prefix, legacy variant)
|
|
72
|
-
elif [[ -d "bmad-core" ]]; then
|
|
73
|
-
bmad_core_dir="bmad-core"
|
|
74
|
-
elif [[ -d "../bmad-core" ]]; then
|
|
75
|
-
bmad_core_dir="../bmad-core"
|
|
76
|
-
else
|
|
77
|
-
echo -e "${RED}❌ BMAD installation not found${NC}" >&2
|
|
78
|
-
echo -e "${GRAY} Looked for bmad/, .bmad-core/, or bmad-core/ directory${NC}" >&2
|
|
79
|
-
return 1
|
|
80
|
-
fi
|
|
81
|
-
|
|
82
|
-
echo "$bmad_core_dir"
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
# Find all BMAD agents
|
|
86
|
-
find_agents() {
|
|
87
|
-
local bmad_core="$1"
|
|
88
|
-
local agents_dir=""
|
|
89
|
-
|
|
90
|
-
# Check for v6-alpha structure (bmad/bmm/agents/)
|
|
91
|
-
if [[ -d "$bmad_core/bmm/agents" ]]; then
|
|
92
|
-
agents_dir="$bmad_core/bmm/agents"
|
|
93
|
-
# Check for v4 structure (.bmad-core/agents/)
|
|
94
|
-
elif [[ -d "$bmad_core/agents" ]]; then
|
|
95
|
-
agents_dir="$bmad_core/agents"
|
|
96
|
-
else
|
|
97
|
-
echo -e "${RED}❌ Agents directory not found in $bmad_core${NC}" >&2
|
|
98
|
-
echo -e "${GRAY} Tried: $bmad_core/bmm/agents/ and $bmad_core/agents/${NC}" >&2
|
|
99
|
-
return 1
|
|
100
|
-
fi
|
|
101
|
-
|
|
102
|
-
find "$agents_dir" -name "*.md" -type f
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
# Check if agent has TTS injection
|
|
106
|
-
has_tts_injection() {
|
|
107
|
-
local agent_file="$1"
|
|
108
|
-
|
|
109
|
-
# Check for v4 marker (YAML comment)
|
|
110
|
-
if grep -q "# AGENTVIBES-TTS-INJECTION" "$agent_file" 2>/dev/null; then
|
|
111
|
-
return 0
|
|
112
|
-
fi
|
|
113
|
-
|
|
114
|
-
# Check for v6 marker (XML attribute or text)
|
|
115
|
-
if grep -q "AGENTVIBES TTS INJECTION" "$agent_file" 2>/dev/null; then
|
|
116
|
-
return 0
|
|
117
|
-
fi
|
|
118
|
-
|
|
119
|
-
if grep -q 'tts="agentvibes"' "$agent_file" 2>/dev/null; then
|
|
120
|
-
return 0
|
|
121
|
-
fi
|
|
122
|
-
|
|
123
|
-
return 1
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
# Extract agent ID from file
|
|
127
|
-
get_agent_id() {
|
|
128
|
-
local agent_file="$1"
|
|
129
|
-
|
|
130
|
-
# Look for "id: <agent-id>" in YAML block
|
|
131
|
-
local agent_id=$(grep -E "^ id:" "$agent_file" | head -1 | awk '{print $2}' | tr -d '"' | tr -d "'")
|
|
132
|
-
|
|
133
|
-
if [[ -z "$agent_id" ]]; then
|
|
134
|
-
# Fallback: use filename without extension
|
|
135
|
-
agent_id=$(basename "$agent_file" .md)
|
|
136
|
-
fi
|
|
137
|
-
|
|
138
|
-
echo "$agent_id"
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
# Get voice for agent from BMAD voice mapping
|
|
142
|
-
get_agent_voice() {
|
|
143
|
-
local agent_id="$1"
|
|
144
|
-
|
|
145
|
-
# Use bmad-voice-manager.sh to get voice
|
|
146
|
-
if [[ -f "$SCRIPT_DIR/bmad-voice-manager.sh" ]]; then
|
|
147
|
-
local voice=$("$SCRIPT_DIR/bmad-voice-manager.sh" get-voice "$agent_id" 2>/dev/null || echo "")
|
|
148
|
-
echo "$voice"
|
|
149
|
-
fi
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
# Map voice name to provider-specific equivalent
|
|
153
|
-
map_voice_to_provider() {
|
|
154
|
-
local voice="$1"
|
|
155
|
-
local provider="$2"
|
|
156
|
-
|
|
157
|
-
# Return as-is for macOS
|
|
158
|
-
if [[ "$provider" == "macos" ]]; then
|
|
159
|
-
echo "$voice"
|
|
160
|
-
return
|
|
161
|
-
fi
|
|
162
|
-
|
|
163
|
-
# For Piper, ensure we're using valid Piper voice format
|
|
164
|
-
# If already in Piper format (contains underscores), return as-is
|
|
165
|
-
if [[ "$voice" == *"_"* ]]; then
|
|
166
|
-
echo "$voice"
|
|
167
|
-
return
|
|
168
|
-
fi
|
|
169
|
-
|
|
170
|
-
# Map legacy voice names to Piper equivalents
|
|
171
|
-
case "$voice" in
|
|
172
|
-
"Jessica Anne Bogart"|"Aria")
|
|
173
|
-
echo "en_US-lessac-medium"
|
|
174
|
-
;;
|
|
175
|
-
"Matthew Schmitz"|"Archer"|"Michael")
|
|
176
|
-
echo "en_US-danny-low"
|
|
177
|
-
;;
|
|
178
|
-
"Burt Reynolds"|"Cowboy Bob")
|
|
179
|
-
echo "en_US-joe-medium"
|
|
180
|
-
;;
|
|
181
|
-
"Tiffany"|"Ms. Walker")
|
|
182
|
-
echo "en_US-amy-medium"
|
|
183
|
-
;;
|
|
184
|
-
"Ralf Eisend"|"Tom")
|
|
185
|
-
echo "en_US-libritts-high"
|
|
186
|
-
;;
|
|
187
|
-
*)
|
|
188
|
-
# Default to amy for unknown voices
|
|
189
|
-
echo "en_US-amy-medium"
|
|
190
|
-
;;
|
|
191
|
-
esac
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
# Get current TTS provider
|
|
195
|
-
get_current_provider() {
|
|
196
|
-
# Check project-local first, then global
|
|
197
|
-
if [[ -f ".claude/tts-provider.txt" ]]; then
|
|
198
|
-
cat ".claude/tts-provider.txt" 2>/dev/null || echo "piper"
|
|
199
|
-
elif [[ -f "$HOME/.claude/tts-provider.txt" ]]; then
|
|
200
|
-
cat "$HOME/.claude/tts-provider.txt" 2>/dev/null || echo "piper"
|
|
201
|
-
else
|
|
202
|
-
echo "piper"
|
|
203
|
-
fi
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
# Inject TTS hook into agent activation instructions
|
|
207
|
-
inject_tts() {
|
|
208
|
-
local agent_file="$1"
|
|
209
|
-
local agent_id=$(get_agent_id "$agent_file")
|
|
210
|
-
local configured_voice=$(get_agent_voice "$agent_id")
|
|
211
|
-
local current_provider=$(get_current_provider)
|
|
212
|
-
local agent_voice=$(map_voice_to_provider "$configured_voice" "$current_provider")
|
|
213
|
-
|
|
214
|
-
# Check if already injected
|
|
215
|
-
if has_tts_injection "$agent_file"; then
|
|
216
|
-
echo -e "${YELLOW}⚠️ TTS already injected in: $(basename "$agent_file")${NC}"
|
|
217
|
-
return 0
|
|
218
|
-
fi
|
|
219
|
-
|
|
220
|
-
# Create backup directory for centralized timestamped backups
|
|
221
|
-
local backup_dir=".agentvibes/backups/agents"
|
|
222
|
-
mkdir -p "$backup_dir"
|
|
223
|
-
|
|
224
|
-
# Create timestamped backup in central location (for permanent archive)
|
|
225
|
-
local timestamp=$(date +%Y%m%d_%H%M%S)
|
|
226
|
-
local backup_name="$(basename "$agent_file" .md)_${timestamp}.md"
|
|
227
|
-
cp "$agent_file" "$backup_dir/$backup_name"
|
|
228
|
-
|
|
229
|
-
# Also create quick-restore backup next to original file
|
|
230
|
-
cp "$agent_file" "$agent_file.backup-pre-tts"
|
|
231
|
-
|
|
232
|
-
echo -e "${GRAY} 📦 Backup saved: $backup_dir/$backup_name${NC}"
|
|
233
|
-
|
|
234
|
-
# Detect v4 vs v6 structure
|
|
235
|
-
local is_v6=false
|
|
236
|
-
if grep -q "<activation" "$agent_file"; then
|
|
237
|
-
is_v6=true
|
|
238
|
-
elif ! grep -q "activation-instructions:" "$agent_file"; then
|
|
239
|
-
echo -e "${RED}❌ No activation section found in: $(basename "$agent_file")${NC}"
|
|
240
|
-
return 1
|
|
241
|
-
fi
|
|
242
|
-
|
|
243
|
-
# Create TTS injection script based on version
|
|
244
|
-
if [[ "$is_v6" == "true" ]]; then
|
|
245
|
-
# v6 format: XML-style with <step n="4.5">
|
|
246
|
-
local tts_step=""
|
|
247
|
-
if [[ -n "$agent_voice" ]]; then
|
|
248
|
-
tts_step=" <step n=\"4.5\" tts=\"agentvibes\">🎤 AGENTVIBES TTS INJECTION:
|
|
249
|
-
- Create context: echo \"${agent_id}\" > .bmad-agent-context (Bash tool)
|
|
250
|
-
- Speak greeting: bash .claude/hooks/play-tts.sh \"Hello! I'm ready to help you.\" \"${agent_voice}\" (Bash tool)
|
|
251
|
-
- CRITICAL: Before EVERY response, scan for questions/prompts and speak them using Bash tool</step>"
|
|
252
|
-
else
|
|
253
|
-
tts_step=" <step n=\"4.5\" tts=\"agentvibes\">🎤 AGENTVIBES TTS INJECTION:
|
|
254
|
-
- Create context: echo \"${agent_id}\" > .bmad-agent-context (Bash tool)
|
|
255
|
-
- Speak greeting: bash .claude/hooks/play-tts.sh \"Hello! I'm ready to help you.\" (Bash tool)
|
|
256
|
-
- CRITICAL: Before EVERY response, scan for questions/prompts and speak them using Bash tool</step>"
|
|
257
|
-
fi
|
|
258
|
-
|
|
259
|
-
# Insert after step 4 - only first match
|
|
260
|
-
# Note: Greeting is in step 5, but we inject after step 4 for proper ordering
|
|
261
|
-
awk -v tts="$tts_step" '
|
|
262
|
-
!done && /<step n="4">/ {
|
|
263
|
-
print
|
|
264
|
-
print tts
|
|
265
|
-
done=1
|
|
266
|
-
next
|
|
267
|
-
}
|
|
268
|
-
{ print }
|
|
269
|
-
' "$agent_file" > "$agent_file.tmp"
|
|
270
|
-
|
|
271
|
-
# SAFETY CHECK: Verify the tmp file is not empty before comparing
|
|
272
|
-
local tmp_size=$(stat -c%s "$agent_file.tmp" 2>/dev/null || stat -f%z "$agent_file.tmp" 2>/dev/null || echo "0")
|
|
273
|
-
if [[ "$tmp_size" -eq 0 ]]; then
|
|
274
|
-
echo -e "${RED}❌ SAFETY: Refusing to overwrite - tmp file is empty: $(basename "$agent_file")${NC}" >&2
|
|
275
|
-
rm -f "$agent_file.tmp"
|
|
276
|
-
mv "$agent_file.backup-pre-tts" "$agent_file"
|
|
277
|
-
return 1
|
|
278
|
-
fi
|
|
279
|
-
|
|
280
|
-
# If no change (step 4 didn't match), restore backup and report
|
|
281
|
-
if ! diff -q "$agent_file.backup-pre-tts" "$agent_file.tmp" > /dev/null 2>&1; then
|
|
282
|
-
: # Changes were made, continue
|
|
283
|
-
else
|
|
284
|
-
# No changes - step 4 pattern didn't match
|
|
285
|
-
rm "$agent_file.tmp"
|
|
286
|
-
mv "$agent_file.backup-pre-tts" "$agent_file"
|
|
287
|
-
echo -e "${RED}❌ Could not find step 4 in: $(basename "$agent_file")${NC}"
|
|
288
|
-
return 1
|
|
289
|
-
fi
|
|
290
|
-
|
|
291
|
-
else
|
|
292
|
-
# v4 format: YAML-style with STEP 4:
|
|
293
|
-
local activation_injection=""
|
|
294
|
-
if [[ -n "$agent_voice" ]]; then
|
|
295
|
-
activation_injection=" - # AGENTVIBES-TTS-INJECTION: STEP 3.5 - Announce activation with TTS
|
|
296
|
-
- STEP 3.5a: Use Bash tool to create context file: echo \"${agent_id}\" > .bmad-agent-context
|
|
297
|
-
- STEP 3.5b: Use Bash tool to speak: bash .claude/hooks/play-tts.sh \"Agent ${agent_id} activated and ready\" \"${agent_voice}\"
|
|
298
|
-
- AGENTVIBES-TTS-INJECTION: Before every response, scan for questions and speak them using Bash tool"
|
|
299
|
-
else
|
|
300
|
-
activation_injection=" - # AGENTVIBES-TTS-INJECTION: STEP 3.5 - Announce activation with TTS
|
|
301
|
-
- STEP 3.5a: Use Bash tool to create context file: echo \"${agent_id}\" > .bmad-agent-context
|
|
302
|
-
- STEP 3.5b: Use Bash tool to speak: bash .claude/hooks/play-tts.sh \"Agent ${agent_id} activated and ready\"
|
|
303
|
-
- AGENTVIBES-TTS-INJECTION: Before every response, scan for questions and speak them using Bash tool"
|
|
304
|
-
fi
|
|
305
|
-
|
|
306
|
-
# Insert after STEP 4: Greet
|
|
307
|
-
awk -v activation="$activation_injection" '
|
|
308
|
-
/STEP 4:.*[Gg]reet/ {
|
|
309
|
-
print
|
|
310
|
-
print activation
|
|
311
|
-
next
|
|
312
|
-
}
|
|
313
|
-
{ print }
|
|
314
|
-
' "$agent_file" > "$agent_file.tmp"
|
|
315
|
-
|
|
316
|
-
# SAFETY CHECK: Verify the tmp file is not empty and has similar size to original
|
|
317
|
-
# This prevents data loss if awk fails or produces empty output
|
|
318
|
-
local original_size=$(stat -c%s "$agent_file" 2>/dev/null || stat -f%z "$agent_file" 2>/dev/null || echo "0")
|
|
319
|
-
local tmp_size=$(stat -c%s "$agent_file.tmp" 2>/dev/null || stat -f%z "$agent_file.tmp" 2>/dev/null || echo "0")
|
|
320
|
-
|
|
321
|
-
if [[ "$tmp_size" -eq 0 ]]; then
|
|
322
|
-
echo -e "${RED}❌ SAFETY: Refusing to overwrite - tmp file is empty: $(basename "$agent_file")${NC}" >&2
|
|
323
|
-
rm -f "$agent_file.tmp"
|
|
324
|
-
mv "$agent_file.backup-pre-tts" "$agent_file"
|
|
325
|
-
return 1
|
|
326
|
-
fi
|
|
327
|
-
|
|
328
|
-
# Tmp file should be at least 80% of original size (protects against truncation)
|
|
329
|
-
# No upper limit since injection adds substantial content (typically 300-500 bytes)
|
|
330
|
-
local min_size=$((original_size * 80 / 100))
|
|
331
|
-
|
|
332
|
-
if [[ "$tmp_size" -lt "$min_size" ]]; then
|
|
333
|
-
echo -e "${RED}❌ SAFETY: Refusing to overwrite - file would shrink too much (orig: ${original_size}B, tmp: ${tmp_size}B): $(basename "$agent_file")${NC}" >&2
|
|
334
|
-
rm -f "$agent_file.tmp"
|
|
335
|
-
mv "$agent_file.backup-pre-tts" "$agent_file"
|
|
336
|
-
return 1
|
|
337
|
-
fi
|
|
338
|
-
fi
|
|
339
|
-
|
|
340
|
-
mv "$agent_file.tmp" "$agent_file"
|
|
341
|
-
|
|
342
|
-
if [[ "$configured_voice" != "$agent_voice" ]] && [[ -n "$configured_voice" ]]; then
|
|
343
|
-
echo -e "${GREEN}✅ Injected TTS into: $(basename "$agent_file") → Voice: ${agent_voice:-default} (${current_provider}: mapped from ${configured_voice})${NC}"
|
|
344
|
-
else
|
|
345
|
-
echo -e "${GREEN}✅ Injected TTS into: $(basename "$agent_file") → Voice: ${agent_voice:-default} (${current_provider})${NC}"
|
|
346
|
-
fi
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
# Remove TTS injection from agent
|
|
350
|
-
remove_tts() {
|
|
351
|
-
local agent_file="$1"
|
|
352
|
-
|
|
353
|
-
# Check if has injection
|
|
354
|
-
if ! has_tts_injection "$agent_file"; then
|
|
355
|
-
echo -e "${GRAY} No TTS in: $(basename "$agent_file")${NC}"
|
|
356
|
-
return 0
|
|
357
|
-
fi
|
|
358
|
-
|
|
359
|
-
# Create backup
|
|
360
|
-
cp "$agent_file" "$agent_file.backup-tts-removal"
|
|
361
|
-
|
|
362
|
-
# Remove TTS injection lines
|
|
363
|
-
sed -i.bak '/# AGENTVIBES-TTS-INJECTION/,+1d' "$agent_file"
|
|
364
|
-
rm -f "$agent_file.bak"
|
|
365
|
-
|
|
366
|
-
echo -e "${GREEN}✅ Removed TTS from: $(basename "$agent_file")${NC}"
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
# Show status of TTS injections
|
|
370
|
-
show_status() {
|
|
371
|
-
local bmad_core=$(detect_bmad)
|
|
372
|
-
if [[ -z "$bmad_core" ]]; then
|
|
373
|
-
return 1
|
|
374
|
-
fi
|
|
375
|
-
|
|
376
|
-
echo -e "${CYAN}📊 BMAD TTS Injection Status:${NC}"
|
|
377
|
-
echo ""
|
|
378
|
-
|
|
379
|
-
local agents=$(find_agents "$bmad_core")
|
|
380
|
-
local enabled_count=0
|
|
381
|
-
local disabled_count=0
|
|
382
|
-
|
|
383
|
-
while IFS= read -r agent_file; do
|
|
384
|
-
local agent_id=$(get_agent_id "$agent_file")
|
|
385
|
-
local agent_name=$(basename "$agent_file" .md)
|
|
386
|
-
|
|
387
|
-
if has_tts_injection "$agent_file"; then
|
|
388
|
-
local voice=$(get_agent_voice "$agent_id")
|
|
389
|
-
echo -e " ${GREEN}✅${NC} $agent_name (${agent_id}) → Voice: ${voice:-default}"
|
|
390
|
-
enabled_count=$((enabled_count + 1))
|
|
391
|
-
else
|
|
392
|
-
echo -e " ${GRAY}❌ $agent_name (${agent_id})${NC}"
|
|
393
|
-
disabled_count=$((disabled_count + 1))
|
|
394
|
-
fi
|
|
395
|
-
done <<< "$agents"
|
|
396
|
-
|
|
397
|
-
echo ""
|
|
398
|
-
echo -e "${CYAN}Summary:${NC} $enabled_count enabled, $disabled_count disabled"
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
# Enable TTS for all agents
|
|
402
|
-
enable_all() {
|
|
403
|
-
local bmad_core=$(detect_bmad)
|
|
404
|
-
if [[ -z "$bmad_core" ]]; then
|
|
405
|
-
return 1
|
|
406
|
-
fi
|
|
407
|
-
|
|
408
|
-
echo -e "${CYAN}🎤 Enabling TTS for all BMAD agents...${NC}"
|
|
409
|
-
echo ""
|
|
410
|
-
|
|
411
|
-
local agents=$(find_agents "$bmad_core")
|
|
412
|
-
local success_count=0
|
|
413
|
-
local skip_count=0
|
|
414
|
-
local fail_count=0
|
|
415
|
-
|
|
416
|
-
# Track modified files and backups for summary
|
|
417
|
-
local modified_files=()
|
|
418
|
-
local backup_files=()
|
|
419
|
-
|
|
420
|
-
while IFS= read -r agent_file; do
|
|
421
|
-
if has_tts_injection "$agent_file"; then
|
|
422
|
-
skip_count=$((skip_count + 1))
|
|
423
|
-
continue
|
|
424
|
-
fi
|
|
425
|
-
|
|
426
|
-
if inject_tts "$agent_file"; then
|
|
427
|
-
success_count=$((success_count + 1))
|
|
428
|
-
modified_files+=("$agent_file")
|
|
429
|
-
# Track the backup file that was created
|
|
430
|
-
local timestamp=$(date +%Y%m%d_%H%M%S)
|
|
431
|
-
local backup_name="$(basename "$agent_file" .md)_${timestamp}.md"
|
|
432
|
-
backup_files+=(".agentvibes/backups/agents/$backup_name")
|
|
433
|
-
else
|
|
434
|
-
fail_count=$((fail_count + 1))
|
|
435
|
-
fi
|
|
436
|
-
done <<< "$agents"
|
|
437
|
-
|
|
438
|
-
echo ""
|
|
439
|
-
echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
|
|
440
|
-
echo -e "${CYAN} TTS INJECTION SUMMARY ${NC}"
|
|
441
|
-
echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
|
|
442
|
-
echo ""
|
|
443
|
-
|
|
444
|
-
# Show results
|
|
445
|
-
echo -e "${GREEN}✅ Successfully modified: $success_count agents${NC}"
|
|
446
|
-
[[ $skip_count -gt 0 ]] && echo -e "${YELLOW}⏭️ Skipped (already enabled): $skip_count agents${NC}"
|
|
447
|
-
[[ $fail_count -gt 0 ]] && echo -e "${RED}❌ Failed: $fail_count agents${NC}"
|
|
448
|
-
echo ""
|
|
449
|
-
|
|
450
|
-
# List modified files
|
|
451
|
-
if [[ ${#modified_files[@]} -gt 0 ]]; then
|
|
452
|
-
echo -e "${CYAN}📝 Modified Files:${NC}"
|
|
453
|
-
for file in "${modified_files[@]}"; do
|
|
454
|
-
echo -e " • $file"
|
|
455
|
-
done
|
|
456
|
-
echo ""
|
|
457
|
-
fi
|
|
458
|
-
|
|
459
|
-
# Show backup location
|
|
460
|
-
if [[ ${#modified_files[@]} -gt 0 ]]; then
|
|
461
|
-
echo -e "${CYAN}📦 Backups saved to:${NC}"
|
|
462
|
-
echo -e " .agentvibes/backups/agents/"
|
|
463
|
-
echo ""
|
|
464
|
-
echo -e "${CYAN}🔄 To restore original files, run:${NC}"
|
|
465
|
-
echo -e " ${GREEN}.claude/hooks/bmad-tts-injector.sh restore${NC}"
|
|
466
|
-
echo ""
|
|
467
|
-
fi
|
|
468
|
-
|
|
469
|
-
echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
|
|
470
|
-
echo ""
|
|
471
|
-
echo -e "${CYAN}💡 BMAD agents will now speak when activated!${NC}"
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
# Disable TTS for all agents
|
|
475
|
-
disable_all() {
|
|
476
|
-
local bmad_core=$(detect_bmad)
|
|
477
|
-
if [[ -z "$bmad_core" ]]; then
|
|
478
|
-
return 1
|
|
479
|
-
fi
|
|
480
|
-
|
|
481
|
-
echo -e "${CYAN}🔇 Disabling TTS for all BMAD agents...${NC}"
|
|
482
|
-
echo ""
|
|
483
|
-
|
|
484
|
-
local agents=$(find_agents "$bmad_core")
|
|
485
|
-
local success_count=0
|
|
486
|
-
|
|
487
|
-
while IFS= read -r agent_file; do
|
|
488
|
-
if remove_tts "$agent_file"; then
|
|
489
|
-
success_count=$((success_count + 1))
|
|
490
|
-
fi
|
|
491
|
-
done <<< "$agents"
|
|
492
|
-
|
|
493
|
-
echo ""
|
|
494
|
-
echo -e "${GREEN}✅ TTS disabled for $success_count agents${NC}"
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
# Restore from backup
|
|
498
|
-
restore_backup() {
|
|
499
|
-
local bmad_core=$(detect_bmad)
|
|
500
|
-
if [[ -z "$bmad_core" ]]; then
|
|
501
|
-
return 1
|
|
502
|
-
fi
|
|
503
|
-
|
|
504
|
-
echo -e "${CYAN}🔄 Restoring agents from backup...${NC}"
|
|
505
|
-
echo ""
|
|
506
|
-
|
|
507
|
-
# Determine agents directory (v6 vs v4)
|
|
508
|
-
local agents_dir=""
|
|
509
|
-
if [[ -d "$bmad_core/bmm/agents" ]]; then
|
|
510
|
-
agents_dir="$bmad_core/bmm/agents"
|
|
511
|
-
elif [[ -d "$bmad_core/agents" ]]; then
|
|
512
|
-
agents_dir="$bmad_core/agents"
|
|
513
|
-
else
|
|
514
|
-
echo -e "${RED}❌ Agents directory not found${NC}"
|
|
515
|
-
return 1
|
|
516
|
-
fi
|
|
517
|
-
|
|
518
|
-
local backup_count=0
|
|
519
|
-
|
|
520
|
-
for backup_file in "$agents_dir"/*.backup-pre-tts; do
|
|
521
|
-
if [[ -f "$backup_file" ]]; then
|
|
522
|
-
local original_file="${backup_file%.backup-pre-tts}"
|
|
523
|
-
cp "$backup_file" "$original_file"
|
|
524
|
-
echo -e "${GREEN}✅ Restored: $(basename "$original_file")${NC}"
|
|
525
|
-
backup_count=$((backup_count + 1))
|
|
526
|
-
fi
|
|
527
|
-
done
|
|
528
|
-
|
|
529
|
-
if [[ $backup_count -eq 0 ]]; then
|
|
530
|
-
echo -e "${YELLOW}⚠️ No backups found${NC}"
|
|
531
|
-
else
|
|
532
|
-
echo ""
|
|
533
|
-
echo -e "${GREEN}✅ Restored $backup_count agents from backup${NC}"
|
|
534
|
-
fi
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
# Main command dispatcher
|
|
538
|
-
case "${1:-help}" in
|
|
539
|
-
enable)
|
|
540
|
-
enable_all
|
|
541
|
-
;;
|
|
542
|
-
disable)
|
|
543
|
-
disable_all
|
|
544
|
-
;;
|
|
545
|
-
status)
|
|
546
|
-
show_status
|
|
547
|
-
;;
|
|
548
|
-
restore)
|
|
549
|
-
restore_backup
|
|
550
|
-
;;
|
|
551
|
-
help|*)
|
|
552
|
-
echo -e "${CYAN}AgentVibes BMAD TTS Injection Manager${NC}"
|
|
553
|
-
echo ""
|
|
554
|
-
echo "Usage: bmad-tts-injector.sh {enable|disable|status|restore}"
|
|
555
|
-
echo ""
|
|
556
|
-
echo "Commands:"
|
|
557
|
-
echo " enable Inject TTS hooks into all BMAD agents"
|
|
558
|
-
echo " disable Remove TTS hooks from all BMAD agents"
|
|
559
|
-
echo " status Show TTS injection status for all agents"
|
|
560
|
-
echo " restore Restore agents from backup (undo changes)"
|
|
561
|
-
echo ""
|
|
562
|
-
echo "What it does:"
|
|
563
|
-
echo " • Automatically patches BMAD agent activation instructions"
|
|
564
|
-
echo " • Adds TTS calls when agents greet users"
|
|
565
|
-
echo " • Uses voice mapping from AgentVibes BMAD plugin"
|
|
566
|
-
echo " • Creates backups before modifying files"
|
|
567
|
-
;;
|
|
568
|
-
esac
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# File: .claude/hooks/bmad-tts-injector.sh
|
|
4
|
+
#
|
|
5
|
+
# AgentVibes - Finally, your AI Agents can Talk Back! Text-to-Speech WITH personality for AI Assistants!
|
|
6
|
+
# Website: https://agentvibes.org
|
|
7
|
+
# Repository: https://github.com/paulpreibisch/AgentVibes
|
|
8
|
+
#
|
|
9
|
+
# Co-created by Paul Preibisch with Claude AI
|
|
10
|
+
# Copyright (c) 2025 Paul Preibisch
|
|
11
|
+
#
|
|
12
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
13
|
+
# you may not use this file except in compliance with the License.
|
|
14
|
+
# You may obtain a copy of the License at
|
|
15
|
+
#
|
|
16
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
17
|
+
#
|
|
18
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
19
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
20
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21
|
+
# See the License for the specific language governing permissions and
|
|
22
|
+
# limitations under the License.
|
|
23
|
+
#
|
|
24
|
+
# DISCLAIMER: This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
25
|
+
# express or implied. Use at your own risk. See the Apache License for details.
|
|
26
|
+
#
|
|
27
|
+
# ---
|
|
28
|
+
#
|
|
29
|
+
# @fileoverview BMAD TTS Injection Manager - Patches BMAD agents for TTS integration
|
|
30
|
+
# @context Automatically modifies BMAD agent YAML files to include AgentVibes TTS capabilities
|
|
31
|
+
# @architecture Injects TTS hooks into activation-instructions and core_principles sections
|
|
32
|
+
# @dependencies bmad-core/agents/*.md files, play-tts.sh, bmad-voice-manager.sh
|
|
33
|
+
# @entrypoints Called via bmad-tts-injector.sh {enable|disable|status|restore}
|
|
34
|
+
# @patterns File patching with backup, provider-aware voice mapping, injection markers for idempotency
|
|
35
|
+
# @related play-tts.sh, bmad-voice-manager.sh, .bmad-core/agents/*.md
|
|
36
|
+
#
|
|
37
|
+
|
|
38
|
+
set -e # Exit on error
|
|
39
|
+
|
|
40
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
41
|
+
CLAUDE_DIR="$(dirname "$SCRIPT_DIR")"
|
|
42
|
+
|
|
43
|
+
# Colors for output
|
|
44
|
+
GREEN='\033[0;32m'
|
|
45
|
+
YELLOW='\033[1;33m'
|
|
46
|
+
RED='\033[0;31m'
|
|
47
|
+
CYAN='\033[0;36m'
|
|
48
|
+
GRAY='\033[0;90m'
|
|
49
|
+
NC='\033[0m' # No Color
|
|
50
|
+
|
|
51
|
+
# Detect BMAD installation and version
|
|
52
|
+
# Supports both v4 (.bmad-core/) and v6-alpha (.bmad/) installations
|
|
53
|
+
detect_bmad() {
|
|
54
|
+
local bmad_core_dir=""
|
|
55
|
+
|
|
56
|
+
# Check for v6-alpha first (newer version with dot prefix)
|
|
57
|
+
if [[ -d ".bmad" ]]; then
|
|
58
|
+
bmad_core_dir=".bmad"
|
|
59
|
+
elif [[ -d "../.bmad" ]]; then
|
|
60
|
+
bmad_core_dir="../.bmad"
|
|
61
|
+
# Check for v6-alpha without dot (legacy naming)
|
|
62
|
+
elif [[ -d "bmad" ]]; then
|
|
63
|
+
bmad_core_dir="bmad"
|
|
64
|
+
elif [[ -d "../bmad" ]]; then
|
|
65
|
+
bmad_core_dir="../bmad"
|
|
66
|
+
# Check for v4 (legacy)
|
|
67
|
+
elif [[ -d ".bmad-core" ]]; then
|
|
68
|
+
bmad_core_dir=".bmad-core"
|
|
69
|
+
elif [[ -d "../.bmad-core" ]]; then
|
|
70
|
+
bmad_core_dir="../.bmad-core"
|
|
71
|
+
# Check for bmad-core (without dot prefix, legacy variant)
|
|
72
|
+
elif [[ -d "bmad-core" ]]; then
|
|
73
|
+
bmad_core_dir="bmad-core"
|
|
74
|
+
elif [[ -d "../bmad-core" ]]; then
|
|
75
|
+
bmad_core_dir="../bmad-core"
|
|
76
|
+
else
|
|
77
|
+
echo -e "${RED}❌ BMAD installation not found${NC}" >&2
|
|
78
|
+
echo -e "${GRAY} Looked for bmad/, .bmad-core/, or bmad-core/ directory${NC}" >&2
|
|
79
|
+
return 1
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
echo "$bmad_core_dir"
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Find all BMAD agents
|
|
86
|
+
find_agents() {
|
|
87
|
+
local bmad_core="$1"
|
|
88
|
+
local agents_dir=""
|
|
89
|
+
|
|
90
|
+
# Check for v6-alpha structure (bmad/bmm/agents/)
|
|
91
|
+
if [[ -d "$bmad_core/bmm/agents" ]]; then
|
|
92
|
+
agents_dir="$bmad_core/bmm/agents"
|
|
93
|
+
# Check for v4 structure (.bmad-core/agents/)
|
|
94
|
+
elif [[ -d "$bmad_core/agents" ]]; then
|
|
95
|
+
agents_dir="$bmad_core/agents"
|
|
96
|
+
else
|
|
97
|
+
echo -e "${RED}❌ Agents directory not found in $bmad_core${NC}" >&2
|
|
98
|
+
echo -e "${GRAY} Tried: $bmad_core/bmm/agents/ and $bmad_core/agents/${NC}" >&2
|
|
99
|
+
return 1
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
find "$agents_dir" -name "*.md" -type f
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# Check if agent has TTS injection
|
|
106
|
+
has_tts_injection() {
|
|
107
|
+
local agent_file="$1"
|
|
108
|
+
|
|
109
|
+
# Check for v4 marker (YAML comment)
|
|
110
|
+
if grep -q "# AGENTVIBES-TTS-INJECTION" "$agent_file" 2>/dev/null; then
|
|
111
|
+
return 0
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
# Check for v6 marker (XML attribute or text)
|
|
115
|
+
if grep -q "AGENTVIBES TTS INJECTION" "$agent_file" 2>/dev/null; then
|
|
116
|
+
return 0
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
if grep -q 'tts="agentvibes"' "$agent_file" 2>/dev/null; then
|
|
120
|
+
return 0
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
return 1
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# Extract agent ID from file
|
|
127
|
+
get_agent_id() {
|
|
128
|
+
local agent_file="$1"
|
|
129
|
+
|
|
130
|
+
# Look for "id: <agent-id>" in YAML block
|
|
131
|
+
local agent_id=$(grep -E "^ id:" "$agent_file" | head -1 | awk '{print $2}' | tr -d '"' | tr -d "'")
|
|
132
|
+
|
|
133
|
+
if [[ -z "$agent_id" ]]; then
|
|
134
|
+
# Fallback: use filename without extension
|
|
135
|
+
agent_id=$(basename "$agent_file" .md)
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
echo "$agent_id"
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
# Get voice for agent from BMAD voice mapping
|
|
142
|
+
get_agent_voice() {
|
|
143
|
+
local agent_id="$1"
|
|
144
|
+
|
|
145
|
+
# Use bmad-voice-manager.sh to get voice
|
|
146
|
+
if [[ -f "$SCRIPT_DIR/bmad-voice-manager.sh" ]]; then
|
|
147
|
+
local voice=$("$SCRIPT_DIR/bmad-voice-manager.sh" get-voice "$agent_id" 2>/dev/null || echo "")
|
|
148
|
+
echo "$voice"
|
|
149
|
+
fi
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# Map voice name to provider-specific equivalent
|
|
153
|
+
map_voice_to_provider() {
|
|
154
|
+
local voice="$1"
|
|
155
|
+
local provider="$2"
|
|
156
|
+
|
|
157
|
+
# Return as-is for macOS
|
|
158
|
+
if [[ "$provider" == "macos" ]]; then
|
|
159
|
+
echo "$voice"
|
|
160
|
+
return
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
# For Piper, ensure we're using valid Piper voice format
|
|
164
|
+
# If already in Piper format (contains underscores), return as-is
|
|
165
|
+
if [[ "$voice" == *"_"* ]]; then
|
|
166
|
+
echo "$voice"
|
|
167
|
+
return
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
# Map legacy voice names to Piper equivalents
|
|
171
|
+
case "$voice" in
|
|
172
|
+
"Jessica Anne Bogart"|"Aria")
|
|
173
|
+
echo "en_US-lessac-medium"
|
|
174
|
+
;;
|
|
175
|
+
"Matthew Schmitz"|"Archer"|"Michael")
|
|
176
|
+
echo "en_US-danny-low"
|
|
177
|
+
;;
|
|
178
|
+
"Burt Reynolds"|"Cowboy Bob")
|
|
179
|
+
echo "en_US-joe-medium"
|
|
180
|
+
;;
|
|
181
|
+
"Tiffany"|"Ms. Walker")
|
|
182
|
+
echo "en_US-amy-medium"
|
|
183
|
+
;;
|
|
184
|
+
"Ralf Eisend"|"Tom")
|
|
185
|
+
echo "en_US-libritts-high"
|
|
186
|
+
;;
|
|
187
|
+
*)
|
|
188
|
+
# Default to amy for unknown voices
|
|
189
|
+
echo "en_US-amy-medium"
|
|
190
|
+
;;
|
|
191
|
+
esac
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
# Get current TTS provider
|
|
195
|
+
get_current_provider() {
|
|
196
|
+
# Check project-local first, then global
|
|
197
|
+
if [[ -f ".claude/tts-provider.txt" ]]; then
|
|
198
|
+
cat ".claude/tts-provider.txt" 2>/dev/null || echo "piper"
|
|
199
|
+
elif [[ -f "$HOME/.claude/tts-provider.txt" ]]; then
|
|
200
|
+
cat "$HOME/.claude/tts-provider.txt" 2>/dev/null || echo "piper"
|
|
201
|
+
else
|
|
202
|
+
echo "piper"
|
|
203
|
+
fi
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
# Inject TTS hook into agent activation instructions
|
|
207
|
+
inject_tts() {
|
|
208
|
+
local agent_file="$1"
|
|
209
|
+
local agent_id=$(get_agent_id "$agent_file")
|
|
210
|
+
local configured_voice=$(get_agent_voice "$agent_id")
|
|
211
|
+
local current_provider=$(get_current_provider)
|
|
212
|
+
local agent_voice=$(map_voice_to_provider "$configured_voice" "$current_provider")
|
|
213
|
+
|
|
214
|
+
# Check if already injected
|
|
215
|
+
if has_tts_injection "$agent_file"; then
|
|
216
|
+
echo -e "${YELLOW}⚠️ TTS already injected in: $(basename "$agent_file")${NC}"
|
|
217
|
+
return 0
|
|
218
|
+
fi
|
|
219
|
+
|
|
220
|
+
# Create backup directory for centralized timestamped backups
|
|
221
|
+
local backup_dir=".agentvibes/backups/agents"
|
|
222
|
+
mkdir -p "$backup_dir"
|
|
223
|
+
|
|
224
|
+
# Create timestamped backup in central location (for permanent archive)
|
|
225
|
+
local timestamp=$(date +%Y%m%d_%H%M%S)
|
|
226
|
+
local backup_name="$(basename "$agent_file" .md)_${timestamp}.md"
|
|
227
|
+
cp "$agent_file" "$backup_dir/$backup_name"
|
|
228
|
+
|
|
229
|
+
# Also create quick-restore backup next to original file
|
|
230
|
+
cp "$agent_file" "$agent_file.backup-pre-tts"
|
|
231
|
+
|
|
232
|
+
echo -e "${GRAY} 📦 Backup saved: $backup_dir/$backup_name${NC}"
|
|
233
|
+
|
|
234
|
+
# Detect v4 vs v6 structure
|
|
235
|
+
local is_v6=false
|
|
236
|
+
if grep -q "<activation" "$agent_file"; then
|
|
237
|
+
is_v6=true
|
|
238
|
+
elif ! grep -q "activation-instructions:" "$agent_file"; then
|
|
239
|
+
echo -e "${RED}❌ No activation section found in: $(basename "$agent_file")${NC}"
|
|
240
|
+
return 1
|
|
241
|
+
fi
|
|
242
|
+
|
|
243
|
+
# Create TTS injection script based on version
|
|
244
|
+
if [[ "$is_v6" == "true" ]]; then
|
|
245
|
+
# v6 format: XML-style with <step n="4.5">
|
|
246
|
+
local tts_step=""
|
|
247
|
+
if [[ -n "$agent_voice" ]]; then
|
|
248
|
+
tts_step=" <step n=\"4.5\" tts=\"agentvibes\">🎤 AGENTVIBES TTS INJECTION:
|
|
249
|
+
- Create context: echo \"${agent_id}\" > .bmad-agent-context (Bash tool)
|
|
250
|
+
- Speak greeting: bash .claude/hooks/play-tts.sh \"Hello! I'm ready to help you.\" \"${agent_voice}\" (Bash tool)
|
|
251
|
+
- CRITICAL: Before EVERY response, scan for questions/prompts and speak them using Bash tool</step>"
|
|
252
|
+
else
|
|
253
|
+
tts_step=" <step n=\"4.5\" tts=\"agentvibes\">🎤 AGENTVIBES TTS INJECTION:
|
|
254
|
+
- Create context: echo \"${agent_id}\" > .bmad-agent-context (Bash tool)
|
|
255
|
+
- Speak greeting: bash .claude/hooks/play-tts.sh \"Hello! I'm ready to help you.\" (Bash tool)
|
|
256
|
+
- CRITICAL: Before EVERY response, scan for questions/prompts and speak them using Bash tool</step>"
|
|
257
|
+
fi
|
|
258
|
+
|
|
259
|
+
# Insert after step 4 - only first match
|
|
260
|
+
# Note: Greeting is in step 5, but we inject after step 4 for proper ordering
|
|
261
|
+
awk -v tts="$tts_step" '
|
|
262
|
+
!done && /<step n="4">/ {
|
|
263
|
+
print
|
|
264
|
+
print tts
|
|
265
|
+
done=1
|
|
266
|
+
next
|
|
267
|
+
}
|
|
268
|
+
{ print }
|
|
269
|
+
' "$agent_file" > "$agent_file.tmp"
|
|
270
|
+
|
|
271
|
+
# SAFETY CHECK: Verify the tmp file is not empty before comparing
|
|
272
|
+
local tmp_size=$(stat -c%s "$agent_file.tmp" 2>/dev/null || stat -f%z "$agent_file.tmp" 2>/dev/null || echo "0")
|
|
273
|
+
if [[ "$tmp_size" -eq 0 ]]; then
|
|
274
|
+
echo -e "${RED}❌ SAFETY: Refusing to overwrite - tmp file is empty: $(basename "$agent_file")${NC}" >&2
|
|
275
|
+
rm -f "$agent_file.tmp"
|
|
276
|
+
mv "$agent_file.backup-pre-tts" "$agent_file"
|
|
277
|
+
return 1
|
|
278
|
+
fi
|
|
279
|
+
|
|
280
|
+
# If no change (step 4 didn't match), restore backup and report
|
|
281
|
+
if ! diff -q "$agent_file.backup-pre-tts" "$agent_file.tmp" > /dev/null 2>&1; then
|
|
282
|
+
: # Changes were made, continue
|
|
283
|
+
else
|
|
284
|
+
# No changes - step 4 pattern didn't match
|
|
285
|
+
rm "$agent_file.tmp"
|
|
286
|
+
mv "$agent_file.backup-pre-tts" "$agent_file"
|
|
287
|
+
echo -e "${RED}❌ Could not find step 4 in: $(basename "$agent_file")${NC}"
|
|
288
|
+
return 1
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
else
|
|
292
|
+
# v4 format: YAML-style with STEP 4:
|
|
293
|
+
local activation_injection=""
|
|
294
|
+
if [[ -n "$agent_voice" ]]; then
|
|
295
|
+
activation_injection=" - # AGENTVIBES-TTS-INJECTION: STEP 3.5 - Announce activation with TTS
|
|
296
|
+
- STEP 3.5a: Use Bash tool to create context file: echo \"${agent_id}\" > .bmad-agent-context
|
|
297
|
+
- STEP 3.5b: Use Bash tool to speak: bash .claude/hooks/play-tts.sh \"Agent ${agent_id} activated and ready\" \"${agent_voice}\"
|
|
298
|
+
- AGENTVIBES-TTS-INJECTION: Before every response, scan for questions and speak them using Bash tool"
|
|
299
|
+
else
|
|
300
|
+
activation_injection=" - # AGENTVIBES-TTS-INJECTION: STEP 3.5 - Announce activation with TTS
|
|
301
|
+
- STEP 3.5a: Use Bash tool to create context file: echo \"${agent_id}\" > .bmad-agent-context
|
|
302
|
+
- STEP 3.5b: Use Bash tool to speak: bash .claude/hooks/play-tts.sh \"Agent ${agent_id} activated and ready\"
|
|
303
|
+
- AGENTVIBES-TTS-INJECTION: Before every response, scan for questions and speak them using Bash tool"
|
|
304
|
+
fi
|
|
305
|
+
|
|
306
|
+
# Insert after STEP 4: Greet
|
|
307
|
+
awk -v activation="$activation_injection" '
|
|
308
|
+
/STEP 4:.*[Gg]reet/ {
|
|
309
|
+
print
|
|
310
|
+
print activation
|
|
311
|
+
next
|
|
312
|
+
}
|
|
313
|
+
{ print }
|
|
314
|
+
' "$agent_file" > "$agent_file.tmp"
|
|
315
|
+
|
|
316
|
+
# SAFETY CHECK: Verify the tmp file is not empty and has similar size to original
|
|
317
|
+
# This prevents data loss if awk fails or produces empty output
|
|
318
|
+
local original_size=$(stat -c%s "$agent_file" 2>/dev/null || stat -f%z "$agent_file" 2>/dev/null || echo "0")
|
|
319
|
+
local tmp_size=$(stat -c%s "$agent_file.tmp" 2>/dev/null || stat -f%z "$agent_file.tmp" 2>/dev/null || echo "0")
|
|
320
|
+
|
|
321
|
+
if [[ "$tmp_size" -eq 0 ]]; then
|
|
322
|
+
echo -e "${RED}❌ SAFETY: Refusing to overwrite - tmp file is empty: $(basename "$agent_file")${NC}" >&2
|
|
323
|
+
rm -f "$agent_file.tmp"
|
|
324
|
+
mv "$agent_file.backup-pre-tts" "$agent_file"
|
|
325
|
+
return 1
|
|
326
|
+
fi
|
|
327
|
+
|
|
328
|
+
# Tmp file should be at least 80% of original size (protects against truncation)
|
|
329
|
+
# No upper limit since injection adds substantial content (typically 300-500 bytes)
|
|
330
|
+
local min_size=$((original_size * 80 / 100))
|
|
331
|
+
|
|
332
|
+
if [[ "$tmp_size" -lt "$min_size" ]]; then
|
|
333
|
+
echo -e "${RED}❌ SAFETY: Refusing to overwrite - file would shrink too much (orig: ${original_size}B, tmp: ${tmp_size}B): $(basename "$agent_file")${NC}" >&2
|
|
334
|
+
rm -f "$agent_file.tmp"
|
|
335
|
+
mv "$agent_file.backup-pre-tts" "$agent_file"
|
|
336
|
+
return 1
|
|
337
|
+
fi
|
|
338
|
+
fi
|
|
339
|
+
|
|
340
|
+
mv "$agent_file.tmp" "$agent_file"
|
|
341
|
+
|
|
342
|
+
if [[ "$configured_voice" != "$agent_voice" ]] && [[ -n "$configured_voice" ]]; then
|
|
343
|
+
echo -e "${GREEN}✅ Injected TTS into: $(basename "$agent_file") → Voice: ${agent_voice:-default} (${current_provider}: mapped from ${configured_voice})${NC}"
|
|
344
|
+
else
|
|
345
|
+
echo -e "${GREEN}✅ Injected TTS into: $(basename "$agent_file") → Voice: ${agent_voice:-default} (${current_provider})${NC}"
|
|
346
|
+
fi
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
# Remove TTS injection from agent
|
|
350
|
+
remove_tts() {
|
|
351
|
+
local agent_file="$1"
|
|
352
|
+
|
|
353
|
+
# Check if has injection
|
|
354
|
+
if ! has_tts_injection "$agent_file"; then
|
|
355
|
+
echo -e "${GRAY} No TTS in: $(basename "$agent_file")${NC}"
|
|
356
|
+
return 0
|
|
357
|
+
fi
|
|
358
|
+
|
|
359
|
+
# Create backup
|
|
360
|
+
cp "$agent_file" "$agent_file.backup-tts-removal"
|
|
361
|
+
|
|
362
|
+
# Remove TTS injection lines
|
|
363
|
+
sed -i.bak '/# AGENTVIBES-TTS-INJECTION/,+1d' "$agent_file"
|
|
364
|
+
rm -f "$agent_file.bak"
|
|
365
|
+
|
|
366
|
+
echo -e "${GREEN}✅ Removed TTS from: $(basename "$agent_file")${NC}"
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
# Show status of TTS injections
|
|
370
|
+
show_status() {
|
|
371
|
+
local bmad_core=$(detect_bmad)
|
|
372
|
+
if [[ -z "$bmad_core" ]]; then
|
|
373
|
+
return 1
|
|
374
|
+
fi
|
|
375
|
+
|
|
376
|
+
echo -e "${CYAN}📊 BMAD TTS Injection Status:${NC}"
|
|
377
|
+
echo ""
|
|
378
|
+
|
|
379
|
+
local agents=$(find_agents "$bmad_core")
|
|
380
|
+
local enabled_count=0
|
|
381
|
+
local disabled_count=0
|
|
382
|
+
|
|
383
|
+
while IFS= read -r agent_file; do
|
|
384
|
+
local agent_id=$(get_agent_id "$agent_file")
|
|
385
|
+
local agent_name=$(basename "$agent_file" .md)
|
|
386
|
+
|
|
387
|
+
if has_tts_injection "$agent_file"; then
|
|
388
|
+
local voice=$(get_agent_voice "$agent_id")
|
|
389
|
+
echo -e " ${GREEN}✅${NC} $agent_name (${agent_id}) → Voice: ${voice:-default}"
|
|
390
|
+
enabled_count=$((enabled_count + 1))
|
|
391
|
+
else
|
|
392
|
+
echo -e " ${GRAY}❌ $agent_name (${agent_id})${NC}"
|
|
393
|
+
disabled_count=$((disabled_count + 1))
|
|
394
|
+
fi
|
|
395
|
+
done <<< "$agents"
|
|
396
|
+
|
|
397
|
+
echo ""
|
|
398
|
+
echo -e "${CYAN}Summary:${NC} $enabled_count enabled, $disabled_count disabled"
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
# Enable TTS for all agents
|
|
402
|
+
enable_all() {
|
|
403
|
+
local bmad_core=$(detect_bmad)
|
|
404
|
+
if [[ -z "$bmad_core" ]]; then
|
|
405
|
+
return 1
|
|
406
|
+
fi
|
|
407
|
+
|
|
408
|
+
echo -e "${CYAN}🎤 Enabling TTS for all BMAD agents...${NC}"
|
|
409
|
+
echo ""
|
|
410
|
+
|
|
411
|
+
local agents=$(find_agents "$bmad_core")
|
|
412
|
+
local success_count=0
|
|
413
|
+
local skip_count=0
|
|
414
|
+
local fail_count=0
|
|
415
|
+
|
|
416
|
+
# Track modified files and backups for summary
|
|
417
|
+
local modified_files=()
|
|
418
|
+
local backup_files=()
|
|
419
|
+
|
|
420
|
+
while IFS= read -r agent_file; do
|
|
421
|
+
if has_tts_injection "$agent_file"; then
|
|
422
|
+
skip_count=$((skip_count + 1))
|
|
423
|
+
continue
|
|
424
|
+
fi
|
|
425
|
+
|
|
426
|
+
if inject_tts "$agent_file"; then
|
|
427
|
+
success_count=$((success_count + 1))
|
|
428
|
+
modified_files+=("$agent_file")
|
|
429
|
+
# Track the backup file that was created
|
|
430
|
+
local timestamp=$(date +%Y%m%d_%H%M%S)
|
|
431
|
+
local backup_name="$(basename "$agent_file" .md)_${timestamp}.md"
|
|
432
|
+
backup_files+=(".agentvibes/backups/agents/$backup_name")
|
|
433
|
+
else
|
|
434
|
+
fail_count=$((fail_count + 1))
|
|
435
|
+
fi
|
|
436
|
+
done <<< "$agents"
|
|
437
|
+
|
|
438
|
+
echo ""
|
|
439
|
+
echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
|
|
440
|
+
echo -e "${CYAN} TTS INJECTION SUMMARY ${NC}"
|
|
441
|
+
echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
|
|
442
|
+
echo ""
|
|
443
|
+
|
|
444
|
+
# Show results
|
|
445
|
+
echo -e "${GREEN}✅ Successfully modified: $success_count agents${NC}"
|
|
446
|
+
[[ $skip_count -gt 0 ]] && echo -e "${YELLOW}⏭️ Skipped (already enabled): $skip_count agents${NC}"
|
|
447
|
+
[[ $fail_count -gt 0 ]] && echo -e "${RED}❌ Failed: $fail_count agents${NC}"
|
|
448
|
+
echo ""
|
|
449
|
+
|
|
450
|
+
# List modified files
|
|
451
|
+
if [[ ${#modified_files[@]} -gt 0 ]]; then
|
|
452
|
+
echo -e "${CYAN}📝 Modified Files:${NC}"
|
|
453
|
+
for file in "${modified_files[@]}"; do
|
|
454
|
+
echo -e " • $file"
|
|
455
|
+
done
|
|
456
|
+
echo ""
|
|
457
|
+
fi
|
|
458
|
+
|
|
459
|
+
# Show backup location
|
|
460
|
+
if [[ ${#modified_files[@]} -gt 0 ]]; then
|
|
461
|
+
echo -e "${CYAN}📦 Backups saved to:${NC}"
|
|
462
|
+
echo -e " .agentvibes/backups/agents/"
|
|
463
|
+
echo ""
|
|
464
|
+
echo -e "${CYAN}🔄 To restore original files, run:${NC}"
|
|
465
|
+
echo -e " ${GREEN}.claude/hooks/bmad-tts-injector.sh restore${NC}"
|
|
466
|
+
echo ""
|
|
467
|
+
fi
|
|
468
|
+
|
|
469
|
+
echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
|
|
470
|
+
echo ""
|
|
471
|
+
echo -e "${CYAN}💡 BMAD agents will now speak when activated!${NC}"
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
# Disable TTS for all agents
|
|
475
|
+
disable_all() {
|
|
476
|
+
local bmad_core=$(detect_bmad)
|
|
477
|
+
if [[ -z "$bmad_core" ]]; then
|
|
478
|
+
return 1
|
|
479
|
+
fi
|
|
480
|
+
|
|
481
|
+
echo -e "${CYAN}🔇 Disabling TTS for all BMAD agents...${NC}"
|
|
482
|
+
echo ""
|
|
483
|
+
|
|
484
|
+
local agents=$(find_agents "$bmad_core")
|
|
485
|
+
local success_count=0
|
|
486
|
+
|
|
487
|
+
while IFS= read -r agent_file; do
|
|
488
|
+
if remove_tts "$agent_file"; then
|
|
489
|
+
success_count=$((success_count + 1))
|
|
490
|
+
fi
|
|
491
|
+
done <<< "$agents"
|
|
492
|
+
|
|
493
|
+
echo ""
|
|
494
|
+
echo -e "${GREEN}✅ TTS disabled for $success_count agents${NC}"
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
# Restore from backup
|
|
498
|
+
restore_backup() {
|
|
499
|
+
local bmad_core=$(detect_bmad)
|
|
500
|
+
if [[ -z "$bmad_core" ]]; then
|
|
501
|
+
return 1
|
|
502
|
+
fi
|
|
503
|
+
|
|
504
|
+
echo -e "${CYAN}🔄 Restoring agents from backup...${NC}"
|
|
505
|
+
echo ""
|
|
506
|
+
|
|
507
|
+
# Determine agents directory (v6 vs v4)
|
|
508
|
+
local agents_dir=""
|
|
509
|
+
if [[ -d "$bmad_core/bmm/agents" ]]; then
|
|
510
|
+
agents_dir="$bmad_core/bmm/agents"
|
|
511
|
+
elif [[ -d "$bmad_core/agents" ]]; then
|
|
512
|
+
agents_dir="$bmad_core/agents"
|
|
513
|
+
else
|
|
514
|
+
echo -e "${RED}❌ Agents directory not found${NC}"
|
|
515
|
+
return 1
|
|
516
|
+
fi
|
|
517
|
+
|
|
518
|
+
local backup_count=0
|
|
519
|
+
|
|
520
|
+
for backup_file in "$agents_dir"/*.backup-pre-tts; do
|
|
521
|
+
if [[ -f "$backup_file" ]]; then
|
|
522
|
+
local original_file="${backup_file%.backup-pre-tts}"
|
|
523
|
+
cp "$backup_file" "$original_file"
|
|
524
|
+
echo -e "${GREEN}✅ Restored: $(basename "$original_file")${NC}"
|
|
525
|
+
backup_count=$((backup_count + 1))
|
|
526
|
+
fi
|
|
527
|
+
done
|
|
528
|
+
|
|
529
|
+
if [[ $backup_count -eq 0 ]]; then
|
|
530
|
+
echo -e "${YELLOW}⚠️ No backups found${NC}"
|
|
531
|
+
else
|
|
532
|
+
echo ""
|
|
533
|
+
echo -e "${GREEN}✅ Restored $backup_count agents from backup${NC}"
|
|
534
|
+
fi
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
# Main command dispatcher
|
|
538
|
+
case "${1:-help}" in
|
|
539
|
+
enable)
|
|
540
|
+
enable_all
|
|
541
|
+
;;
|
|
542
|
+
disable)
|
|
543
|
+
disable_all
|
|
544
|
+
;;
|
|
545
|
+
status)
|
|
546
|
+
show_status
|
|
547
|
+
;;
|
|
548
|
+
restore)
|
|
549
|
+
restore_backup
|
|
550
|
+
;;
|
|
551
|
+
help|*)
|
|
552
|
+
echo -e "${CYAN}AgentVibes BMAD TTS Injection Manager${NC}"
|
|
553
|
+
echo ""
|
|
554
|
+
echo "Usage: bmad-tts-injector.sh {enable|disable|status|restore}"
|
|
555
|
+
echo ""
|
|
556
|
+
echo "Commands:"
|
|
557
|
+
echo " enable Inject TTS hooks into all BMAD agents"
|
|
558
|
+
echo " disable Remove TTS hooks from all BMAD agents"
|
|
559
|
+
echo " status Show TTS injection status for all agents"
|
|
560
|
+
echo " restore Restore agents from backup (undo changes)"
|
|
561
|
+
echo ""
|
|
562
|
+
echo "What it does:"
|
|
563
|
+
echo " • Automatically patches BMAD agent activation instructions"
|
|
564
|
+
echo " • Adds TTS calls when agents greet users"
|
|
565
|
+
echo " • Uses voice mapping from AgentVibes BMAD plugin"
|
|
566
|
+
echo " • Creates backups before modifying files"
|
|
567
|
+
;;
|
|
568
|
+
esac
|