agentvibes 5.2.0 → 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.
Files changed (49) hide show
  1. package/.claude/config/audio-effects.cfg +1 -1
  2. package/.claude/hooks/audio-cache-utils.sh +246 -246
  3. package/.claude/hooks/background-music-manager.sh +404 -404
  4. package/.claude/hooks/bmad-speak-enhanced.sh +165 -165
  5. package/.claude/hooks/bmad-speak.sh +290 -290
  6. package/.claude/hooks/bmad-tts-injector.sh +568 -568
  7. package/.claude/hooks/bmad-voice-manager.sh +928 -928
  8. package/.claude/hooks/clawdbot-receiver-SECURE.sh +129 -129
  9. package/.claude/hooks/clawdbot-receiver.sh +107 -107
  10. package/.claude/hooks/clean-audio-cache.sh +22 -22
  11. package/.claude/hooks/cleanup-cache.sh +106 -106
  12. package/.claude/hooks/configure-rdp-mode.sh +137 -137
  13. package/.claude/hooks/download-extra-voices.sh +244 -244
  14. package/.claude/hooks/effects-manager.sh +268 -268
  15. package/.claude/hooks/github-star-reminder.sh +154 -154
  16. package/.claude/hooks/language-manager.sh +362 -362
  17. package/.claude/hooks/learn-manager.sh +492 -492
  18. package/.claude/hooks/macos-voice-manager.sh +205 -205
  19. package/.claude/hooks/migrate-background-music.sh +125 -125
  20. package/.claude/hooks/migrate-to-agentvibes.sh +161 -161
  21. package/.claude/hooks/optimize-background-music.sh +87 -87
  22. package/.claude/hooks/path-resolver.sh +60 -60
  23. package/.claude/hooks/personality-manager.sh +448 -448
  24. package/.claude/hooks/piper-installer.sh +292 -292
  25. package/.claude/hooks/piper-multispeaker-registry.sh +171 -171
  26. package/.claude/hooks/play-tts-enhanced.sh +105 -105
  27. package/.claude/hooks/play-tts-termux-ssh.sh +169 -169
  28. package/.claude/hooks/play-tts.sh +14 -5
  29. package/.claude/hooks/prepare-release.sh +54 -54
  30. package/.claude/hooks/provider-commands.sh +617 -617
  31. package/.claude/hooks/provider-manager.sh +399 -399
  32. package/.claude/hooks/replay-target-audio.sh +95 -95
  33. package/.claude/hooks/sentiment-manager.sh +201 -201
  34. package/.claude/hooks/speed-manager.sh +291 -291
  35. package/.claude/hooks/stop-tts.sh +84 -84
  36. package/.claude/hooks/termux-installer.sh +261 -261
  37. package/.claude/hooks/translate-manager.sh +341 -341
  38. package/.claude/hooks/tts-queue-worker.sh +145 -145
  39. package/.claude/hooks/tts-queue.sh +165 -165
  40. package/.claude/hooks/voice-manager.sh +552 -548
  41. package/.claude/hooks-windows/play-tts.ps1 +2 -2
  42. package/README.md +11 -2
  43. package/RELEASE_NOTES.md +38 -0
  44. package/bin/mcp-server.sh +206 -206
  45. package/mcp-server/server.py +35 -6
  46. package/package.json +1 -1
  47. package/src/console/tabs/setup-tab.js +59 -23
  48. package/src/installer.js +79 -213
  49. package/src/services/llm-provider-service.js +126 -75
@@ -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