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,928 +1,928 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# File: .claude/hooks/bmad-voice-manager.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, including but not limited to the warranties of
|
|
26
|
-
# merchantability, fitness for a particular purpose and noninfringement.
|
|
27
|
-
# In no event shall the authors or copyright holders be liable for any claim,
|
|
28
|
-
# damages or other liability, whether in an action of contract, tort or
|
|
29
|
-
# otherwise, arising from, out of or in connection with the software or the
|
|
30
|
-
# use or other dealings in the software.
|
|
31
|
-
#
|
|
32
|
-
# ---
|
|
33
|
-
#
|
|
34
|
-
# @fileoverview BMAD Voice Plugin Manager - Maps BMAD agents to unique TTS voices
|
|
35
|
-
# @context Enables each BMAD agent to have its own distinct voice for multi-agent sessions
|
|
36
|
-
# @architecture Markdown table-based voice mapping with enable/disable flag, auto-detection of BMAD
|
|
37
|
-
# @dependencies .claude/config/bmad-voices.md (voice mappings), bmad-tts-injector.sh, .bmad-core/ (BMAD installation)
|
|
38
|
-
# @entrypoints Called by /agent-vibes:bmad commands, auto-enabled on BMAD detection
|
|
39
|
-
# @patterns Plugin architecture, auto-enable on dependency detection, state backup/restore on toggle
|
|
40
|
-
# @related bmad-tts-injector.sh, .claude/config/bmad-voices.md, .bmad-agent-context file
|
|
41
|
-
|
|
42
|
-
CONFIG_DIR=".agentvibes/bmad"
|
|
43
|
-
VOICE_CONFIG_FILE="$CONFIG_DIR/bmad-voices.md"
|
|
44
|
-
ENABLED_FLAG="$CONFIG_DIR/bmad-voices-enabled.flag"
|
|
45
|
-
|
|
46
|
-
# AI NOTE: Auto-enable pattern - When BMAD is detected via install-manifest.yaml,
|
|
47
|
-
# automatically enable the voice plugin to provide seamless multi-agent voice support.
|
|
48
|
-
# This avoids requiring manual plugin activation after BMAD installation.
|
|
49
|
-
# Supports both BMAD v4 (.bmad-core/) and v6-alpha (bmad/) directory structures.
|
|
50
|
-
|
|
51
|
-
# @function detect_bmad_version
|
|
52
|
-
# @intent Detect BMAD installation and return version number
|
|
53
|
-
# @why Support both v4 and v6-alpha installations with different directory structures
|
|
54
|
-
# @param None
|
|
55
|
-
# @returns Echoes version number (4, 6, or 0 for not installed) to stdout
|
|
56
|
-
# @exitcode 0=detected, 1=not installed
|
|
57
|
-
# @sideeffects None
|
|
58
|
-
# @edgecases Checks v6 first (newer version), falls back to v4
|
|
59
|
-
# @calledby auto_enable_if_bmad_detected, get_bmad_config_path
|
|
60
|
-
# @calls None
|
|
61
|
-
detect_bmad_version() {
|
|
62
|
-
if [[ -f ".bmad/_cfg/manifest.yaml" ]]; then
|
|
63
|
-
# v6 detected (standard path with dot prefix)
|
|
64
|
-
echo "6"
|
|
65
|
-
return 0
|
|
66
|
-
elif [[ -f "bmad/_cfg/manifest.yaml" ]]; then
|
|
67
|
-
# v6 detected (alternative path without dot prefix)
|
|
68
|
-
echo "6"
|
|
69
|
-
return 0
|
|
70
|
-
elif [[ -f ".bmad-core/install-manifest.yaml" ]]; then
|
|
71
|
-
# v4 detected
|
|
72
|
-
echo "4"
|
|
73
|
-
return 0
|
|
74
|
-
else
|
|
75
|
-
# Not installed
|
|
76
|
-
echo "0"
|
|
77
|
-
return 1
|
|
78
|
-
fi
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
# @function get_bmad_config_path
|
|
82
|
-
# @intent Get BMAD configuration file path based on detected version
|
|
83
|
-
# @why v4 and v6 use different directory structures for config files
|
|
84
|
-
# @param None
|
|
85
|
-
# @returns Echoes config path to stdout, empty string if not installed
|
|
86
|
-
# @exitcode 0=path returned, 1=not installed
|
|
87
|
-
# @sideeffects None
|
|
88
|
-
# @edgecases Returns empty string if BMAD not detected
|
|
89
|
-
# @calledby Commands that need to read BMAD config (future use)
|
|
90
|
-
# @calls detect_bmad_version
|
|
91
|
-
get_bmad_config_path() {
|
|
92
|
-
local version=$(detect_bmad_version)
|
|
93
|
-
|
|
94
|
-
if [[ "$version" == "6" ]]; then
|
|
95
|
-
# Check both possible v6 paths
|
|
96
|
-
if [[ -f ".bmad/core/config.yaml" ]]; then
|
|
97
|
-
echo ".bmad/core/config.yaml"
|
|
98
|
-
else
|
|
99
|
-
echo "bmad/core/config.yaml"
|
|
100
|
-
fi
|
|
101
|
-
return 0
|
|
102
|
-
elif [[ "$version" == "4" ]]; then
|
|
103
|
-
echo ".bmad-core/config.yaml"
|
|
104
|
-
return 0
|
|
105
|
-
else
|
|
106
|
-
echo ""
|
|
107
|
-
return 1
|
|
108
|
-
fi
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
# @function auto_enable_if_bmad_detected
|
|
112
|
-
# @intent Automatically enable BMAD voice plugin when BMAD framework is detected
|
|
113
|
-
# @why Provide seamless integration - users shouldn't need to manually enable voice mapping
|
|
114
|
-
# @param None
|
|
115
|
-
# @returns None
|
|
116
|
-
# @exitcode 0=auto-enabled, 1=not enabled (already enabled or BMAD not detected)
|
|
117
|
-
# @sideeffects Creates enabled flag file, creates plugin directory
|
|
118
|
-
# @edgecases Only auto-enables if plugin not already enabled, silent operation
|
|
119
|
-
# @calledby get_agent_voice
|
|
120
|
-
# @calls mkdir, touch, detect_bmad_version
|
|
121
|
-
auto_enable_if_bmad_detected() {
|
|
122
|
-
local version=$(detect_bmad_version)
|
|
123
|
-
|
|
124
|
-
# Check if BMAD is installed (any version) and plugin not already enabled
|
|
125
|
-
if [[ "$version" != "0" ]] && [[ ! -f "$ENABLED_FLAG" ]]; then
|
|
126
|
-
# BMAD detected but plugin not enabled - enable it silently
|
|
127
|
-
mkdir -p "$CONFIG_DIR"
|
|
128
|
-
touch "$ENABLED_FLAG"
|
|
129
|
-
return 0
|
|
130
|
-
fi
|
|
131
|
-
return 1
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
# @function get_agent_voice
|
|
135
|
-
# @intent Retrieve TTS voice assigned to specific BMAD agent (provider-aware)
|
|
136
|
-
# @why Each BMAD agent needs unique voice for multi-agent conversation differentiation
|
|
137
|
-
# @param $1 {string} agent_id - BMAD agent identifier (pm, dev, qa, architect, etc.)
|
|
138
|
-
# @returns Echoes voice name to stdout, empty string if plugin disabled or agent not found
|
|
139
|
-
# @exitcode Always 0
|
|
140
|
-
# @sideeffects May auto-enable plugin if BMAD detected
|
|
141
|
-
# @edgecases Returns empty string if plugin disabled/missing, parses markdown table syntax
|
|
142
|
-
# @calledby bmad-tts-injector.sh, play-tts.sh when BMAD agent is active
|
|
143
|
-
# @calls auto_enable_if_bmad_detected, grep, awk, sed
|
|
144
|
-
# @version 2.0.0 - Now provider-aware: returns Piper or macOS voice based on active provider
|
|
145
|
-
get_agent_voice() {
|
|
146
|
-
local agent_id="$1"
|
|
147
|
-
|
|
148
|
-
# Check for BMAD v6 CSV file first (preferred, loose coupling)
|
|
149
|
-
# If this exists, use it directly without requiring plugin enable flag
|
|
150
|
-
# Support both .bmad (standard) and bmad (alternative) paths
|
|
151
|
-
local bmad_voice_map=""
|
|
152
|
-
if [[ -f ".bmad/_cfg/agent-voice-map.csv" ]]; then
|
|
153
|
-
bmad_voice_map=".bmad/_cfg/agent-voice-map.csv"
|
|
154
|
-
elif [[ -f "bmad/_cfg/agent-voice-map.csv" ]]; then
|
|
155
|
-
bmad_voice_map="bmad/_cfg/agent-voice-map.csv"
|
|
156
|
-
fi
|
|
157
|
-
|
|
158
|
-
if [[ -n "$bmad_voice_map" ]]; then
|
|
159
|
-
# Read from BMAD's standard _cfg directory
|
|
160
|
-
# CSV format: agent_id,voice_name
|
|
161
|
-
local voice=$(grep "^$agent_id," "$bmad_voice_map" | cut -d',' -f2)
|
|
162
|
-
|
|
163
|
-
# If voice is empty or generic (same for all), use defaults
|
|
164
|
-
if [[ -n "$voice" ]] && [[ "$voice" != "en_US-lessac-medium" ]]; then
|
|
165
|
-
echo "$voice"
|
|
166
|
-
return
|
|
167
|
-
fi
|
|
168
|
-
# If empty or generic, fall through to defaults below
|
|
169
|
-
fi
|
|
170
|
-
|
|
171
|
-
# Default voice mappings (hardcoded fallback when CSV is missing or has generic values)
|
|
172
|
-
# These match the BMAD-METHOD defaults for consistency
|
|
173
|
-
case "$agent_id" in
|
|
174
|
-
bmad-master)
|
|
175
|
-
echo "en_US-lessac-medium"
|
|
176
|
-
return
|
|
177
|
-
;;
|
|
178
|
-
analyst)
|
|
179
|
-
echo "en_US-kristin-medium"
|
|
180
|
-
return
|
|
181
|
-
;;
|
|
182
|
-
architect)
|
|
183
|
-
echo "en_GB-alan-medium"
|
|
184
|
-
return
|
|
185
|
-
;;
|
|
186
|
-
dev)
|
|
187
|
-
echo "en_US-joe-medium"
|
|
188
|
-
return
|
|
189
|
-
;;
|
|
190
|
-
pm)
|
|
191
|
-
echo "en_US-ryan-high"
|
|
192
|
-
return
|
|
193
|
-
;;
|
|
194
|
-
quick-flow-solo-dev)
|
|
195
|
-
echo "en_US-joe-medium"
|
|
196
|
-
return
|
|
197
|
-
;;
|
|
198
|
-
sm)
|
|
199
|
-
echo "en_US-amy-medium"
|
|
200
|
-
return
|
|
201
|
-
;;
|
|
202
|
-
tea)
|
|
203
|
-
echo "en_US-kusal-medium"
|
|
204
|
-
return
|
|
205
|
-
;;
|
|
206
|
-
tech-writer)
|
|
207
|
-
echo "en_US-kristin-medium"
|
|
208
|
-
return
|
|
209
|
-
;;
|
|
210
|
-
ux-designer)
|
|
211
|
-
echo "en_US-kristin-medium"
|
|
212
|
-
return
|
|
213
|
-
;;
|
|
214
|
-
frame-expert)
|
|
215
|
-
echo "en_GB-alan-medium"
|
|
216
|
-
return
|
|
217
|
-
;;
|
|
218
|
-
esac
|
|
219
|
-
|
|
220
|
-
# Auto-enable if BMAD is detected (for legacy markdown config)
|
|
221
|
-
auto_enable_if_bmad_detected
|
|
222
|
-
|
|
223
|
-
if [[ ! -f "$ENABLED_FLAG" ]]; then
|
|
224
|
-
echo "" # Plugin disabled
|
|
225
|
-
return
|
|
226
|
-
fi
|
|
227
|
-
|
|
228
|
-
# Fallback to legacy markdown config file
|
|
229
|
-
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
230
|
-
echo "" # Plugin file missing
|
|
231
|
-
return
|
|
232
|
-
fi
|
|
233
|
-
|
|
234
|
-
# Detect active TTS provider
|
|
235
|
-
local provider_file=""
|
|
236
|
-
if [[ -f ".claude/tts-provider.txt" ]]; then
|
|
237
|
-
provider_file=".claude/tts-provider.txt"
|
|
238
|
-
elif [[ -f "$HOME/.claude/tts-provider.txt" ]]; then
|
|
239
|
-
provider_file="$HOME/.claude/tts-provider.txt"
|
|
240
|
-
fi
|
|
241
|
-
|
|
242
|
-
local active_provider="piper" # default
|
|
243
|
-
if [[ -n "$provider_file" ]] && [[ -f "$provider_file" ]]; then
|
|
244
|
-
active_provider=$(cat "$provider_file")
|
|
245
|
-
fi
|
|
246
|
-
|
|
247
|
-
# Extract voice from markdown table based on provider
|
|
248
|
-
# Table: Agent ID | Agent Name | Intro | Piper Voice | macOS Voice | Personality
|
|
249
|
-
# AWK columns: $1=empty | $2=ID | $3=Name | $4=Intro | $5=Piper | $6=macOS | $7=Personality
|
|
250
|
-
local column=5 # Default to Piper (AWK column 5)
|
|
251
|
-
if [[ "$active_provider" == "piper" ]]; then
|
|
252
|
-
column=6 # Use Piper (AWK column 6)
|
|
253
|
-
fi
|
|
254
|
-
|
|
255
|
-
local voice=$(grep "^| $agent_id " "$VOICE_CONFIG_FILE" | \
|
|
256
|
-
awk -F'|' "{print \$$column}" | \
|
|
257
|
-
sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
258
|
-
|
|
259
|
-
echo "$voice"
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
# @function sync_intros_from_manifest
|
|
263
|
-
# @intent Synchronize generic intros in agent-voice-map.csv with displayNames from agent-manifest.csv
|
|
264
|
-
# @why Until BMAD PR 987 merges, CSV has generic "Hello! Ready to help with the discussion." intros
|
|
265
|
-
# @param None (operates on .bmad/_cfg files)
|
|
266
|
-
# @returns 0 on success, 1 on error
|
|
267
|
-
# @exitcode 0 on success, 1 if files missing or sync fails
|
|
268
|
-
# @sideeffects Updates agent-voice-map.csv, creates .backup on first run, writes .bmad-csv-sync-timestamp
|
|
269
|
-
# @edgecases Only updates EXACT match of generic intro, preserves all custom intros, idempotent
|
|
270
|
-
# @calledby get_agent_intro (lazy trigger on manifest change)
|
|
271
|
-
# @calls grep, cut, sed, stat, date
|
|
272
|
-
# @version 2.17.4 - Safe CSV sync utility that preserves user customizations
|
|
273
|
-
sync_intros_from_manifest() {
|
|
274
|
-
# Locate the CSV and manifest files
|
|
275
|
-
local bmad_voice_map=""
|
|
276
|
-
local manifest_file=""
|
|
277
|
-
|
|
278
|
-
if [[ -f ".bmad/_cfg/agent-voice-map.csv" ]]; then
|
|
279
|
-
bmad_voice_map=".bmad/_cfg/agent-voice-map.csv"
|
|
280
|
-
elif [[ -f "bmad/_cfg/agent-voice-map.csv" ]]; then
|
|
281
|
-
bmad_voice_map="bmad/_cfg/agent-voice-map.csv"
|
|
282
|
-
fi
|
|
283
|
-
|
|
284
|
-
if [[ -f "_bmad/_config/agent-manifest.csv" ]]; then
|
|
285
|
-
manifest_file="_bmad/_config/agent-manifest.csv"
|
|
286
|
-
elif [[ -f "_bmad/_config/agent-manifest.csv" ]]; then
|
|
287
|
-
manifest_file="_bmad/_config/agent-manifest.csv"
|
|
288
|
-
fi
|
|
289
|
-
|
|
290
|
-
# Both files must exist for sync to work
|
|
291
|
-
if [[ -z "$bmad_voice_map" ]] || [[ -z "$manifest_file" ]]; then
|
|
292
|
-
return 1
|
|
293
|
-
fi
|
|
294
|
-
|
|
295
|
-
# Check if sync is needed based on manifest timestamp
|
|
296
|
-
local timestamp_file="${bmad_voice_map%/*}/.bmad-csv-sync-timestamp"
|
|
297
|
-
local manifest_mtime=$(stat -c '%Y' "$manifest_file" 2>/dev/null || stat -f '%m' "$manifest_file" 2>/dev/null)
|
|
298
|
-
|
|
299
|
-
if [[ -f "$timestamp_file" ]]; then
|
|
300
|
-
local last_sync=$(cat "$timestamp_file" 2>/dev/null || echo "0")
|
|
301
|
-
if [[ "$manifest_mtime" -le "$last_sync" ]]; then
|
|
302
|
-
# Manifest hasn't changed since last sync
|
|
303
|
-
return 0
|
|
304
|
-
fi
|
|
305
|
-
fi
|
|
306
|
-
|
|
307
|
-
# Create backup on first sync
|
|
308
|
-
if [[ ! -f "${bmad_voice_map}.backup" ]]; then
|
|
309
|
-
cp "$bmad_voice_map" "${bmad_voice_map}.backup"
|
|
310
|
-
fi
|
|
311
|
-
|
|
312
|
-
# Build a temp file with synced intros
|
|
313
|
-
local temp_file="${bmad_voice_map}.tmp"
|
|
314
|
-
local generic_intro="Hello! Ready to help with the discussion."
|
|
315
|
-
|
|
316
|
-
# Read header
|
|
317
|
-
head -n 1 "$bmad_voice_map" > "$temp_file"
|
|
318
|
-
|
|
319
|
-
# Process each agent entry
|
|
320
|
-
tail -n +2 "$bmad_voice_map" | while IFS=, read -r agent voice intro; do
|
|
321
|
-
# Remove quotes from intro
|
|
322
|
-
intro=$(echo "$intro" | sed 's/^"//;s/"$//')
|
|
323
|
-
|
|
324
|
-
# Only update if intro is the exact generic placeholder
|
|
325
|
-
if [[ "$intro" == "$generic_intro" ]]; then
|
|
326
|
-
# Look up displayName and title from manifest using awk for proper CSV parsing
|
|
327
|
-
# CSV format: name,displayName,title,icon,role,...
|
|
328
|
-
local manifest_data=$(grep "^\"*${agent}\"*," "$manifest_file" | awk -F'","' '{
|
|
329
|
-
gsub(/^"/, "", $2);
|
|
330
|
-
gsub(/"$/, "", $3);
|
|
331
|
-
print $2 "|" $3
|
|
332
|
-
}')
|
|
333
|
-
|
|
334
|
-
local display_name=$(echo "$manifest_data" | cut -d'|' -f1)
|
|
335
|
-
local title=$(echo "$manifest_data" | cut -d'|' -f2)
|
|
336
|
-
|
|
337
|
-
if [[ -n "$display_name" ]] && [[ -n "$title" ]]; then
|
|
338
|
-
# Generate intro like PR 987: "Hi! I'm [Name], your [Title]."
|
|
339
|
-
intro="Hi! I'm ${display_name}, your ${title}."
|
|
340
|
-
elif [[ -n "$display_name" ]]; then
|
|
341
|
-
# Fallback if title missing
|
|
342
|
-
intro="${display_name} here"
|
|
343
|
-
fi
|
|
344
|
-
fi
|
|
345
|
-
|
|
346
|
-
# Write the line (preserving custom intros, updating generic ones)
|
|
347
|
-
echo "${agent},${voice},\"${intro}\""
|
|
348
|
-
done >> "$temp_file"
|
|
349
|
-
|
|
350
|
-
# Replace original with synced version
|
|
351
|
-
mv "$temp_file" "$bmad_voice_map"
|
|
352
|
-
|
|
353
|
-
# Update timestamp
|
|
354
|
-
echo "$manifest_mtime" > "$timestamp_file"
|
|
355
|
-
|
|
356
|
-
return 0
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
# @function get_agent_intro
|
|
360
|
-
# @intent Retrieve intro text for BMAD agent (spoken before their message)
|
|
361
|
-
# @why Helps users identify which agent is speaking in party mode
|
|
362
|
-
# @param $1 {string} agent_id - BMAD agent identifier
|
|
363
|
-
# @returns Echoes intro text to stdout, empty string if not configured
|
|
364
|
-
# @exitcode Always 0
|
|
365
|
-
# @sideeffects Triggers CSV sync on first call or manifest change
|
|
366
|
-
# @edgecases Returns empty string if plugin file missing, parses column 3 of CSV or markdown table
|
|
367
|
-
# @calledby bmad-speak.sh for agent identification in party mode
|
|
368
|
-
# @calls sync_intros_from_manifest, grep, awk, sed, cut
|
|
369
|
-
# @version 2.2.1 - Added lazy CSV sync trigger
|
|
370
|
-
get_agent_intro() {
|
|
371
|
-
local agent_id="$1"
|
|
372
|
-
|
|
373
|
-
# Check for BMAD v6 CSV file first (preferred, loose coupling)
|
|
374
|
-
# If this exists, use it directly without requiring plugin enable flag
|
|
375
|
-
# Support both .bmad (standard) and bmad (alternative) paths
|
|
376
|
-
local bmad_voice_map=""
|
|
377
|
-
if [[ -f ".bmad/_cfg/agent-voice-map.csv" ]]; then
|
|
378
|
-
bmad_voice_map=".bmad/_cfg/agent-voice-map.csv"
|
|
379
|
-
elif [[ -f "bmad/_cfg/agent-voice-map.csv" ]]; then
|
|
380
|
-
bmad_voice_map="bmad/_cfg/agent-voice-map.csv"
|
|
381
|
-
fi
|
|
382
|
-
|
|
383
|
-
# Lazy trigger: sync intros from manifest if needed
|
|
384
|
-
if [[ -n "$bmad_voice_map" ]]; then
|
|
385
|
-
sync_intros_from_manifest
|
|
386
|
-
fi
|
|
387
|
-
|
|
388
|
-
if [[ -n "$bmad_voice_map" ]]; then
|
|
389
|
-
# Read from BMAD's standard _cfg directory
|
|
390
|
-
# CSV format: agent,voice,intro
|
|
391
|
-
# Use awk to properly handle quoted CSV fields (intro may contain commas)
|
|
392
|
-
local intro=$(grep "^$agent_id," "$bmad_voice_map" | awk -F',' '{
|
|
393
|
-
# Extract field 3 onwards (intro may span multiple comma-separated parts)
|
|
394
|
-
intro = $3;
|
|
395
|
-
for (i = 4; i <= NF; i++) {
|
|
396
|
-
intro = intro "," $i;
|
|
397
|
-
}
|
|
398
|
-
gsub(/^"/, "", intro);
|
|
399
|
-
gsub(/"$/, "", intro);
|
|
400
|
-
print intro;
|
|
401
|
-
}')
|
|
402
|
-
|
|
403
|
-
# If intro is empty or generic, fall back to agent display name from manifest
|
|
404
|
-
if [[ -z "$intro" ]] || [[ "$intro" == "Hello! Ready to help with the discussion." ]]; then
|
|
405
|
-
# Try to get display name from agent-manifest.csv
|
|
406
|
-
local manifest_file=""
|
|
407
|
-
if [[ -f "_bmad/_config/agent-manifest.csv" ]]; then
|
|
408
|
-
manifest_file="_bmad/_config/agent-manifest.csv"
|
|
409
|
-
elif [[ -f "_bmad/_config/agent-manifest.csv" ]]; then
|
|
410
|
-
manifest_file="_bmad/_config/agent-manifest.csv"
|
|
411
|
-
fi
|
|
412
|
-
|
|
413
|
-
if [[ -n "$manifest_file" ]]; then
|
|
414
|
-
# Extract displayName (column 2) where name (column 1) matches agent_id
|
|
415
|
-
# CSV format: name,displayName,title,icon,role,...
|
|
416
|
-
local display_name=$(grep "^\"*${agent_id}\"*," "$manifest_file" | cut -d',' -f2 | sed 's/^"//;s/"$//')
|
|
417
|
-
if [[ -n "$display_name" ]]; then
|
|
418
|
-
intro="$display_name here"
|
|
419
|
-
fi
|
|
420
|
-
fi
|
|
421
|
-
fi
|
|
422
|
-
|
|
423
|
-
# If we got an intro, return it
|
|
424
|
-
if [[ -n "$intro" ]]; then
|
|
425
|
-
echo "$intro"
|
|
426
|
-
return
|
|
427
|
-
fi
|
|
428
|
-
# Otherwise fall through to hardcoded defaults below
|
|
429
|
-
fi
|
|
430
|
-
|
|
431
|
-
# Hardcoded default intro mappings (final fallback)
|
|
432
|
-
# These match the BMAD-METHOD agent display names for consistency
|
|
433
|
-
case "$agent_id" in
|
|
434
|
-
bmad-master)
|
|
435
|
-
echo "BMad Master here"
|
|
436
|
-
return
|
|
437
|
-
;;
|
|
438
|
-
analyst)
|
|
439
|
-
echo "Mary here"
|
|
440
|
-
return
|
|
441
|
-
;;
|
|
442
|
-
architect)
|
|
443
|
-
echo "Winston here"
|
|
444
|
-
return
|
|
445
|
-
;;
|
|
446
|
-
dev)
|
|
447
|
-
echo "Amelia here"
|
|
448
|
-
return
|
|
449
|
-
;;
|
|
450
|
-
pm)
|
|
451
|
-
echo "John here"
|
|
452
|
-
return
|
|
453
|
-
;;
|
|
454
|
-
quick-flow-solo-dev)
|
|
455
|
-
echo "Barry here"
|
|
456
|
-
return
|
|
457
|
-
;;
|
|
458
|
-
sm)
|
|
459
|
-
echo "Bob here"
|
|
460
|
-
return
|
|
461
|
-
;;
|
|
462
|
-
tea)
|
|
463
|
-
echo "Murat here"
|
|
464
|
-
return
|
|
465
|
-
;;
|
|
466
|
-
tech-writer)
|
|
467
|
-
echo "Paige here"
|
|
468
|
-
return
|
|
469
|
-
;;
|
|
470
|
-
ux-designer)
|
|
471
|
-
echo "Sally here"
|
|
472
|
-
return
|
|
473
|
-
;;
|
|
474
|
-
frame-expert)
|
|
475
|
-
echo "Frame Expert here"
|
|
476
|
-
return
|
|
477
|
-
;;
|
|
478
|
-
esac
|
|
479
|
-
|
|
480
|
-
# Fallback to legacy markdown config file
|
|
481
|
-
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
482
|
-
echo ""
|
|
483
|
-
return
|
|
484
|
-
fi
|
|
485
|
-
|
|
486
|
-
# AWK column 4 = Intro text
|
|
487
|
-
local intro=$(grep "^| $agent_id " "$VOICE_CONFIG_FILE" | \
|
|
488
|
-
awk -F'|' '{print $4}' | \
|
|
489
|
-
sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
490
|
-
|
|
491
|
-
echo "$intro"
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
# @function get_agent_personality
|
|
495
|
-
# @intent Retrieve TTS personality assigned to specific BMAD agent
|
|
496
|
-
# @why Agents may have distinct speaking styles (friendly, professional, energetic, etc.)
|
|
497
|
-
# @param $1 {string} agent_id - BMAD agent identifier
|
|
498
|
-
# @returns Echoes personality name to stdout, empty string if not found
|
|
499
|
-
# @exitcode Always 0
|
|
500
|
-
# @sideeffects None
|
|
501
|
-
# @edgecases Returns empty string if plugin file missing, parses column 6 of markdown table
|
|
502
|
-
# @calledby bmad-tts-injector.sh for personality-aware voice synthesis
|
|
503
|
-
# @calls grep, awk, sed
|
|
504
|
-
# @version 2.0.0 - Updated to column 6 (was 5) due to new provider-aware format
|
|
505
|
-
get_agent_personality() {
|
|
506
|
-
local agent_id="$1"
|
|
507
|
-
|
|
508
|
-
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
509
|
-
echo ""
|
|
510
|
-
return
|
|
511
|
-
fi
|
|
512
|
-
|
|
513
|
-
# AWK column 7 = Personality
|
|
514
|
-
local personality=$(grep "^| $agent_id " "$VOICE_CONFIG_FILE" | \
|
|
515
|
-
awk -F'|' '{print $7}' | \
|
|
516
|
-
sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
517
|
-
|
|
518
|
-
echo "$personality"
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
# @function is_plugin_enabled
|
|
522
|
-
# @intent Check if BMAD voice plugin is currently enabled
|
|
523
|
-
# @why Allow conditional logic based on plugin state
|
|
524
|
-
# @param None
|
|
525
|
-
# @returns Echoes "true" or "false" to stdout
|
|
526
|
-
# @exitcode Always 0
|
|
527
|
-
# @sideeffects None
|
|
528
|
-
# @edgecases None
|
|
529
|
-
# @calledby show_status, enable_plugin, disable_plugin
|
|
530
|
-
# @calls None (file existence check)
|
|
531
|
-
is_plugin_enabled() {
|
|
532
|
-
[[ -f "$ENABLED_FLAG" ]] && echo "true" || echo "false"
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
# @function enable_plugin
|
|
536
|
-
# @intent Enable BMAD voice plugin and backup current voice settings
|
|
537
|
-
# @why Allow users to switch to per-agent voices while preserving original configuration
|
|
538
|
-
# @param None
|
|
539
|
-
# @returns None
|
|
540
|
-
# @exitcode Always 0
|
|
541
|
-
# @sideeffects Creates flag file, backs up current voice/personality/sentiment to .bmad-previous-settings
|
|
542
|
-
# @sideeffects Creates activation-instructions file for BMAD agents, calls bmad-tts-injector.sh
|
|
543
|
-
# @edgecases Handles missing settings files gracefully with defaults
|
|
544
|
-
# @calledby Main command dispatcher with "enable" argument
|
|
545
|
-
# @calls mkdir, cat, source, list_mappings, bmad-tts-injector.sh
|
|
546
|
-
enable_plugin() {
|
|
547
|
-
mkdir -p "$CONFIG_DIR"
|
|
548
|
-
|
|
549
|
-
# Save current settings before enabling
|
|
550
|
-
BACKUP_FILE="$CONFIG_DIR/.bmad-previous-settings"
|
|
551
|
-
|
|
552
|
-
# Save current voice
|
|
553
|
-
if [[ -f ".claude/tts-voice.txt" ]]; then
|
|
554
|
-
CURRENT_VOICE=$(cat .claude/tts-voice.txt 2>/dev/null)
|
|
555
|
-
elif [[ -f "$HOME/.claude/tts-voice.txt" ]]; then
|
|
556
|
-
CURRENT_VOICE=$(cat "$HOME/.claude/tts-voice.txt" 2>/dev/null)
|
|
557
|
-
else
|
|
558
|
-
CURRENT_VOICE="Aria"
|
|
559
|
-
fi
|
|
560
|
-
|
|
561
|
-
# Save current personality
|
|
562
|
-
if [[ -f ".claude/tts-personality.txt" ]]; then
|
|
563
|
-
CURRENT_PERSONALITY=$(cat .claude/tts-personality.txt 2>/dev/null)
|
|
564
|
-
elif [[ -f "$HOME/.claude/tts-personality.txt" ]]; then
|
|
565
|
-
CURRENT_PERSONALITY=$(cat "$HOME/.claude/tts-personality.txt" 2>/dev/null)
|
|
566
|
-
else
|
|
567
|
-
CURRENT_PERSONALITY="normal"
|
|
568
|
-
fi
|
|
569
|
-
|
|
570
|
-
# Save current sentiment
|
|
571
|
-
if [[ -f ".claude/tts-sentiment.txt" ]]; then
|
|
572
|
-
CURRENT_SENTIMENT=$(cat .claude/tts-sentiment.txt 2>/dev/null)
|
|
573
|
-
elif [[ -f "$HOME/.claude/tts-sentiment.txt" ]]; then
|
|
574
|
-
CURRENT_SENTIMENT=$(cat "$HOME/.claude/tts-sentiment.txt" 2>/dev/null)
|
|
575
|
-
else
|
|
576
|
-
CURRENT_SENTIMENT=""
|
|
577
|
-
fi
|
|
578
|
-
|
|
579
|
-
# Write backup
|
|
580
|
-
cat > "$BACKUP_FILE" <<EOF
|
|
581
|
-
VOICE=$CURRENT_VOICE
|
|
582
|
-
PERSONALITY=$CURRENT_PERSONALITY
|
|
583
|
-
SENTIMENT=$CURRENT_SENTIMENT
|
|
584
|
-
EOF
|
|
585
|
-
|
|
586
|
-
touch "$ENABLED_FLAG"
|
|
587
|
-
|
|
588
|
-
# Create activation-instructions file for BMAD agents
|
|
589
|
-
ACTIVATION_FILE=".claude/activation-instructions"
|
|
590
|
-
if [[ ! -f "$ACTIVATION_FILE" ]]; then
|
|
591
|
-
cat > "$ACTIVATION_FILE" <<'ACTIVATION_EOF'
|
|
592
|
-
# BMAD Agent Activation Instructions
|
|
593
|
-
|
|
594
|
-
When a BMAD agent activates, follow these steps EXACTLY:
|
|
595
|
-
|
|
596
|
-
## STEP 3.5a: Create BMAD Context File (CRITICAL)
|
|
597
|
-
|
|
598
|
-
**IMMEDIATELY after agent identification, create the context file:**
|
|
599
|
-
|
|
600
|
-
```bash
|
|
601
|
-
echo "$AGENT_ID" > .bmad-agent-context
|
|
602
|
-
```
|
|
603
|
-
|
|
604
|
-
Where `$AGENT_ID` is one of:
|
|
605
|
-
- `pm` (Product Manager)
|
|
606
|
-
- `dev` (Developer)
|
|
607
|
-
- `qa` (QA)
|
|
608
|
-
- `architect` (Architect)
|
|
609
|
-
- `po` (Product Owner)
|
|
610
|
-
- `analyst` (Analyst)
|
|
611
|
-
- `sm` (Scrum Master)
|
|
612
|
-
- `ux-expert` (UX Expert)
|
|
613
|
-
- `bmad-master` (BMAD Master)
|
|
614
|
-
- `bmad-orchestrator` (Orchestrator)
|
|
615
|
-
|
|
616
|
-
**Example:**
|
|
617
|
-
```bash
|
|
618
|
-
# For Product Manager agent
|
|
619
|
-
echo "pm" > .bmad-agent-context
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
This file allows AgentVibes to:
|
|
623
|
-
1. Detect which BMAD agent is active
|
|
624
|
-
2. Look up the correct voice mapping
|
|
625
|
-
3. Automatically speak questions using the agent's assigned voice
|
|
626
|
-
|
|
627
|
-
## STEP 10: Clean Up on Exit
|
|
628
|
-
|
|
629
|
-
**Before exiting the agent, remove the context file:**
|
|
630
|
-
|
|
631
|
-
```bash
|
|
632
|
-
rm -f .bmad-agent-context
|
|
633
|
-
```
|
|
634
|
-
|
|
635
|
-
This ensures voice switching only happens when an agent is active.
|
|
636
|
-
|
|
637
|
-
## Why This Matters
|
|
638
|
-
|
|
639
|
-
Without the `.bmad-agent-context` file:
|
|
640
|
-
- AgentVibes cannot detect which agent is active
|
|
641
|
-
- Questions won't be spoken automatically
|
|
642
|
-
- Voice switching won't work
|
|
643
|
-
- The BMAD voice plugin becomes non-functional
|
|
644
|
-
|
|
645
|
-
**This is MANDATORY for BMAD voice integration to work!**
|
|
646
|
-
ACTIVATION_EOF
|
|
647
|
-
echo "📝 Created activation instructions: $ACTIVATION_FILE"
|
|
648
|
-
fi
|
|
649
|
-
|
|
650
|
-
echo "✅ BMAD voice plugin enabled"
|
|
651
|
-
echo "💾 Previous settings backed up:"
|
|
652
|
-
echo " Voice: $CURRENT_VOICE"
|
|
653
|
-
echo " Personality: $CURRENT_PERSONALITY"
|
|
654
|
-
[[ -n "$CURRENT_SENTIMENT" ]] && echo " Sentiment: $CURRENT_SENTIMENT"
|
|
655
|
-
echo ""
|
|
656
|
-
list_mappings
|
|
657
|
-
|
|
658
|
-
# Automatically inject TTS into BMAD agents
|
|
659
|
-
echo ""
|
|
660
|
-
echo "🎤 Automatically enabling TTS for BMAD agents..."
|
|
661
|
-
echo ""
|
|
662
|
-
|
|
663
|
-
# Get the directory where this script is located
|
|
664
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
665
|
-
|
|
666
|
-
# Check if bmad-tts-injector.sh exists
|
|
667
|
-
if [[ -f "$SCRIPT_DIR/bmad-tts-injector.sh" ]]; then
|
|
668
|
-
# Run the TTS injector
|
|
669
|
-
"$SCRIPT_DIR/bmad-tts-injector.sh" enable
|
|
670
|
-
else
|
|
671
|
-
echo "⚠️ TTS injector not found at: $SCRIPT_DIR/bmad-tts-injector.sh"
|
|
672
|
-
echo " You can manually enable TTS with: /agent-vibes:bmad-tts enable"
|
|
673
|
-
fi
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
# @function disable_plugin
|
|
677
|
-
# @intent Disable BMAD voice plugin and restore previous voice settings
|
|
678
|
-
# @why Allow users to return to single-voice mode with their original configuration
|
|
679
|
-
# @param None
|
|
680
|
-
# @returns None
|
|
681
|
-
# @exitcode Always 0
|
|
682
|
-
# @sideeffects Removes flag file, restores settings from backup, calls bmad-tts-injector.sh disable
|
|
683
|
-
# @edgecases Handles missing backup file gracefully, warns user if no backup exists
|
|
684
|
-
# @calledby Main command dispatcher with "disable" argument
|
|
685
|
-
# @calls source, rm, echo, bmad-tts-injector.sh
|
|
686
|
-
disable_plugin() {
|
|
687
|
-
BACKUP_FILE="$CONFIG_DIR/.bmad-previous-settings"
|
|
688
|
-
|
|
689
|
-
# Check if we have a backup to restore
|
|
690
|
-
if [[ -f "$BACKUP_FILE" ]]; then
|
|
691
|
-
source "$BACKUP_FILE"
|
|
692
|
-
|
|
693
|
-
echo "❌ BMAD voice plugin disabled"
|
|
694
|
-
echo "🔄 Restoring previous settings:"
|
|
695
|
-
echo " Voice: $VOICE"
|
|
696
|
-
echo " Personality: $PERSONALITY"
|
|
697
|
-
[[ -n "$SENTIMENT" ]] && echo " Sentiment: $SENTIMENT"
|
|
698
|
-
|
|
699
|
-
# Restore voice
|
|
700
|
-
if [[ -n "$VOICE" ]]; then
|
|
701
|
-
echo "$VOICE" > .claude/tts-voice.txt 2>/dev/null || echo "$VOICE" > "$HOME/.claude/tts-voice.txt"
|
|
702
|
-
fi
|
|
703
|
-
|
|
704
|
-
# Restore personality
|
|
705
|
-
if [[ -n "$PERSONALITY" ]] && [[ "$PERSONALITY" != "normal" ]]; then
|
|
706
|
-
echo "$PERSONALITY" > .claude/tts-personality.txt 2>/dev/null || echo "$PERSONALITY" > "$HOME/.claude/tts-personality.txt"
|
|
707
|
-
fi
|
|
708
|
-
|
|
709
|
-
# Restore sentiment
|
|
710
|
-
if [[ -n "$SENTIMENT" ]]; then
|
|
711
|
-
echo "$SENTIMENT" > .claude/tts-sentiment.txt 2>/dev/null || echo "$SENTIMENT" > "$HOME/.claude/tts-sentiment.txt"
|
|
712
|
-
fi
|
|
713
|
-
|
|
714
|
-
# Clean up backup
|
|
715
|
-
rm -f "$BACKUP_FILE"
|
|
716
|
-
else
|
|
717
|
-
echo "❌ BMAD voice plugin disabled"
|
|
718
|
-
echo "⚠️ No previous settings found to restore"
|
|
719
|
-
echo "AgentVibes will use current voice/personality settings"
|
|
720
|
-
fi
|
|
721
|
-
|
|
722
|
-
rm -f "$ENABLED_FLAG"
|
|
723
|
-
|
|
724
|
-
# Automatically remove TTS from BMAD agents
|
|
725
|
-
echo ""
|
|
726
|
-
echo "🔇 Automatically disabling TTS for BMAD agents..."
|
|
727
|
-
echo ""
|
|
728
|
-
|
|
729
|
-
# Get the directory where this script is located
|
|
730
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
731
|
-
|
|
732
|
-
# Check if bmad-tts-injector.sh exists
|
|
733
|
-
if [[ -f "$SCRIPT_DIR/bmad-tts-injector.sh" ]]; then
|
|
734
|
-
# Run the TTS injector disable
|
|
735
|
-
"$SCRIPT_DIR/bmad-tts-injector.sh" disable
|
|
736
|
-
else
|
|
737
|
-
echo "⚠️ TTS injector not found"
|
|
738
|
-
echo " You can manually disable TTS with: /agent-vibes:bmad-tts disable"
|
|
739
|
-
fi
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
# @function list_mappings
|
|
743
|
-
# @intent Display all BMAD agent-to-voice mappings in readable format (provider-aware)
|
|
744
|
-
# @why Help users see which voice is assigned to each agent based on active TTS provider
|
|
745
|
-
# @param None
|
|
746
|
-
# @returns None
|
|
747
|
-
# @exitcode 0=success, 1=plugin file not found
|
|
748
|
-
# @sideeffects Writes formatted output to stdout
|
|
749
|
-
# @edgecases Parses markdown table format, skips header and separator rows
|
|
750
|
-
# @calledby enable_plugin, show_status, main command dispatcher with "list"
|
|
751
|
-
# @calls grep, sed, echo
|
|
752
|
-
# @version 2.1.0 - Now provider-aware: shows Piper or macOS voices based on active provider
|
|
753
|
-
list_mappings() {
|
|
754
|
-
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
755
|
-
echo "❌ Plugin file not found: $VOICE_CONFIG_FILE"
|
|
756
|
-
return 1
|
|
757
|
-
fi
|
|
758
|
-
|
|
759
|
-
# Detect active TTS provider
|
|
760
|
-
local provider_file=""
|
|
761
|
-
if [[ -f ".claude/tts-provider.txt" ]]; then
|
|
762
|
-
provider_file=".claude/tts-provider.txt"
|
|
763
|
-
elif [[ -f "$HOME/.claude/tts-provider.txt" ]]; then
|
|
764
|
-
provider_file="$HOME/.claude/tts-provider.txt"
|
|
765
|
-
fi
|
|
766
|
-
|
|
767
|
-
local active_provider="piper" # default
|
|
768
|
-
if [[ -n "$provider_file" ]] && [[ -f "$provider_file" ]]; then
|
|
769
|
-
active_provider=$(cat "$provider_file")
|
|
770
|
-
fi
|
|
771
|
-
|
|
772
|
-
# Display provider info
|
|
773
|
-
echo "📊 BMAD Agent Voice Mappings (Provider: $active_provider):"
|
|
774
|
-
echo ""
|
|
775
|
-
|
|
776
|
-
# Table: Agent ID | Agent Name | Intro | Piper Voice | macOS Voice | Personality
|
|
777
|
-
# AWK columns: $1=empty | $2=ID | $3=Name | $4=Intro | $5=Piper | $6=macOS | $7=Personality
|
|
778
|
-
local voice_column=5 # Default to Piper (AWK column 5)
|
|
779
|
-
if [[ "$active_provider" == "piper" ]]; then
|
|
780
|
-
voice_column=6 # Use Piper (AWK column 6)
|
|
781
|
-
fi
|
|
782
|
-
|
|
783
|
-
grep "^| " "$VOICE_CONFIG_FILE" | grep -v "Agent ID" | grep -v "^|---" | \
|
|
784
|
-
while IFS='|' read -r line; do
|
|
785
|
-
agent_id=$(echo "$line" | awk -F'|' '{print $2}' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
786
|
-
name=$(echo "$line" | awk -F'|' '{print $3}' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
787
|
-
voice=$(echo "$line" | awk -F'|' "{print \$$voice_column}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
788
|
-
personality=$(echo "$line" | awk -F'|' '{print $7}' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
789
|
-
|
|
790
|
-
[[ -n "$agent_id" ]] && echo " $agent_id → $voice [$personality]"
|
|
791
|
-
done
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
# @function set_agent_voice
|
|
795
|
-
# @intent Update voice and personality mapping for specific BMAD agent
|
|
796
|
-
# @why Allow customization of agent voices to user preferences
|
|
797
|
-
# @param $1 {string} agent_id - BMAD agent identifier
|
|
798
|
-
# @param $2 {string} voice - New voice name
|
|
799
|
-
# @param $3 {string} personality - New personality (optional, defaults to "normal")
|
|
800
|
-
# @returns None
|
|
801
|
-
# @exitcode 0=success, 1=plugin file not found or agent not found
|
|
802
|
-
# @sideeffects Modifies plugin file, creates .bak backup
|
|
803
|
-
# @edgecases Validates agent exists before updating
|
|
804
|
-
# @calledby Main command dispatcher with "set" argument
|
|
805
|
-
# @calls grep, sed
|
|
806
|
-
set_agent_voice() {
|
|
807
|
-
local agent_id="$1"
|
|
808
|
-
local voice="$2"
|
|
809
|
-
local personality="${3:-normal}"
|
|
810
|
-
|
|
811
|
-
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
812
|
-
echo "❌ Plugin file not found: $VOICE_CONFIG_FILE"
|
|
813
|
-
return 1
|
|
814
|
-
fi
|
|
815
|
-
|
|
816
|
-
# Check if agent exists
|
|
817
|
-
if ! grep -q "^| $agent_id " "$VOICE_CONFIG_FILE"; then
|
|
818
|
-
echo "❌ Agent '$agent_id' not found in plugin"
|
|
819
|
-
return 1
|
|
820
|
-
fi
|
|
821
|
-
|
|
822
|
-
# Update the voice and personality in the table
|
|
823
|
-
sed -i.bak "s/^| $agent_id |.*| .* | .* |$/| $agent_id | $(grep "^| $agent_id " "$VOICE_CONFIG_FILE" | awk -F'|' '{print $3}') | $voice | $personality |/" "$VOICE_CONFIG_FILE"
|
|
824
|
-
|
|
825
|
-
echo "✅ Updated $agent_id → $voice [$personality]"
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
# @function show_status
|
|
829
|
-
# @intent Display plugin status, BMAD detection, and current voice mappings
|
|
830
|
-
# @why Provide comprehensive overview of plugin state for troubleshooting
|
|
831
|
-
# @param None
|
|
832
|
-
# @returns None
|
|
833
|
-
# @exitcode Always 0
|
|
834
|
-
# @sideeffects Writes status information to stdout
|
|
835
|
-
# @edgecases Checks for BMAD installation via manifest file
|
|
836
|
-
# @calledby Main command dispatcher with "status" argument
|
|
837
|
-
# @calls is_plugin_enabled, list_mappings
|
|
838
|
-
show_status() {
|
|
839
|
-
# Check for BMAD installation
|
|
840
|
-
local bmad_installed="false"
|
|
841
|
-
if [[ -f ".bmad-core/install-manifest.yaml" ]]; then
|
|
842
|
-
bmad_installed="true"
|
|
843
|
-
fi
|
|
844
|
-
|
|
845
|
-
if [[ $(is_plugin_enabled) == "true" ]]; then
|
|
846
|
-
echo "✅ BMAD voice plugin: ENABLED"
|
|
847
|
-
if [[ "$bmad_installed" == "true" ]]; then
|
|
848
|
-
echo "🔍 BMAD detected: Auto-enabled"
|
|
849
|
-
fi
|
|
850
|
-
else
|
|
851
|
-
echo "❌ BMAD voice plugin: DISABLED"
|
|
852
|
-
if [[ "$bmad_installed" == "true" ]]; then
|
|
853
|
-
echo "⚠️ BMAD detected but plugin disabled (enable with: /agent-vibes-bmad enable)"
|
|
854
|
-
fi
|
|
855
|
-
fi
|
|
856
|
-
echo ""
|
|
857
|
-
list_mappings
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
# @function edit_plugin
|
|
861
|
-
# @intent Open plugin configuration file for manual editing
|
|
862
|
-
# @why Allow advanced users to modify voice mappings directly
|
|
863
|
-
# @param None
|
|
864
|
-
# @returns None
|
|
865
|
-
# @exitcode 0=success, 1=plugin file not found
|
|
866
|
-
# @sideeffects Displays file path and instructions
|
|
867
|
-
# @edgecases Does not actually open editor, just provides guidance
|
|
868
|
-
# @calledby Main command dispatcher with "edit" argument
|
|
869
|
-
# @calls echo
|
|
870
|
-
edit_plugin() {
|
|
871
|
-
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
872
|
-
echo "❌ Plugin file not found: $VOICE_CONFIG_FILE"
|
|
873
|
-
return 1
|
|
874
|
-
fi
|
|
875
|
-
|
|
876
|
-
echo "Opening $VOICE_CONFIG_FILE for editing..."
|
|
877
|
-
echo "Edit the markdown table to change voice mappings"
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
# Main command dispatcher
|
|
881
|
-
case "${1:-help}" in
|
|
882
|
-
enable)
|
|
883
|
-
enable_plugin
|
|
884
|
-
;;
|
|
885
|
-
disable)
|
|
886
|
-
disable_plugin
|
|
887
|
-
;;
|
|
888
|
-
status)
|
|
889
|
-
show_status
|
|
890
|
-
;;
|
|
891
|
-
list)
|
|
892
|
-
list_mappings
|
|
893
|
-
;;
|
|
894
|
-
set)
|
|
895
|
-
if [[ -z "$2" ]] || [[ -z "$3" ]]; then
|
|
896
|
-
echo "Usage: bmad-voice-manager.sh set <agent-id> <voice> [personality]"
|
|
897
|
-
exit 1
|
|
898
|
-
fi
|
|
899
|
-
set_agent_voice "$2" "$3" "$4"
|
|
900
|
-
;;
|
|
901
|
-
get-voice)
|
|
902
|
-
get_agent_voice "$2"
|
|
903
|
-
;;
|
|
904
|
-
get-intro)
|
|
905
|
-
get_agent_intro "$2"
|
|
906
|
-
;;
|
|
907
|
-
get-personality)
|
|
908
|
-
get_agent_personality "$2"
|
|
909
|
-
;;
|
|
910
|
-
edit)
|
|
911
|
-
edit_plugin
|
|
912
|
-
;;
|
|
913
|
-
*)
|
|
914
|
-
echo "Usage: bmad-voice-manager.sh {enable|disable|status|list|set|get-voice|get-intro|get-personality|edit}"
|
|
915
|
-
echo ""
|
|
916
|
-
echo "Commands:"
|
|
917
|
-
echo " enable Enable BMAD voice plugin"
|
|
918
|
-
echo " disable Disable BMAD voice plugin"
|
|
919
|
-
echo " status Show plugin status and mappings"
|
|
920
|
-
echo " list List all agent voice mappings"
|
|
921
|
-
echo " set <id> <voice> Set voice for agent"
|
|
922
|
-
echo " get-voice <id> Get voice for agent"
|
|
923
|
-
echo " get-intro <id> Get intro text for agent"
|
|
924
|
-
echo " get-personality <id> Get personality for agent"
|
|
925
|
-
echo " edit Edit plugin configuration"
|
|
926
|
-
exit 1
|
|
927
|
-
;;
|
|
928
|
-
esac
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# File: .claude/hooks/bmad-voice-manager.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, including but not limited to the warranties of
|
|
26
|
+
# merchantability, fitness for a particular purpose and noninfringement.
|
|
27
|
+
# In no event shall the authors or copyright holders be liable for any claim,
|
|
28
|
+
# damages or other liability, whether in an action of contract, tort or
|
|
29
|
+
# otherwise, arising from, out of or in connection with the software or the
|
|
30
|
+
# use or other dealings in the software.
|
|
31
|
+
#
|
|
32
|
+
# ---
|
|
33
|
+
#
|
|
34
|
+
# @fileoverview BMAD Voice Plugin Manager - Maps BMAD agents to unique TTS voices
|
|
35
|
+
# @context Enables each BMAD agent to have its own distinct voice for multi-agent sessions
|
|
36
|
+
# @architecture Markdown table-based voice mapping with enable/disable flag, auto-detection of BMAD
|
|
37
|
+
# @dependencies .claude/config/bmad-voices.md (voice mappings), bmad-tts-injector.sh, .bmad-core/ (BMAD installation)
|
|
38
|
+
# @entrypoints Called by /agent-vibes:bmad commands, auto-enabled on BMAD detection
|
|
39
|
+
# @patterns Plugin architecture, auto-enable on dependency detection, state backup/restore on toggle
|
|
40
|
+
# @related bmad-tts-injector.sh, .claude/config/bmad-voices.md, .bmad-agent-context file
|
|
41
|
+
|
|
42
|
+
CONFIG_DIR=".agentvibes/bmad"
|
|
43
|
+
VOICE_CONFIG_FILE="$CONFIG_DIR/bmad-voices.md"
|
|
44
|
+
ENABLED_FLAG="$CONFIG_DIR/bmad-voices-enabled.flag"
|
|
45
|
+
|
|
46
|
+
# AI NOTE: Auto-enable pattern - When BMAD is detected via install-manifest.yaml,
|
|
47
|
+
# automatically enable the voice plugin to provide seamless multi-agent voice support.
|
|
48
|
+
# This avoids requiring manual plugin activation after BMAD installation.
|
|
49
|
+
# Supports both BMAD v4 (.bmad-core/) and v6-alpha (bmad/) directory structures.
|
|
50
|
+
|
|
51
|
+
# @function detect_bmad_version
|
|
52
|
+
# @intent Detect BMAD installation and return version number
|
|
53
|
+
# @why Support both v4 and v6-alpha installations with different directory structures
|
|
54
|
+
# @param None
|
|
55
|
+
# @returns Echoes version number (4, 6, or 0 for not installed) to stdout
|
|
56
|
+
# @exitcode 0=detected, 1=not installed
|
|
57
|
+
# @sideeffects None
|
|
58
|
+
# @edgecases Checks v6 first (newer version), falls back to v4
|
|
59
|
+
# @calledby auto_enable_if_bmad_detected, get_bmad_config_path
|
|
60
|
+
# @calls None
|
|
61
|
+
detect_bmad_version() {
|
|
62
|
+
if [[ -f ".bmad/_cfg/manifest.yaml" ]]; then
|
|
63
|
+
# v6 detected (standard path with dot prefix)
|
|
64
|
+
echo "6"
|
|
65
|
+
return 0
|
|
66
|
+
elif [[ -f "bmad/_cfg/manifest.yaml" ]]; then
|
|
67
|
+
# v6 detected (alternative path without dot prefix)
|
|
68
|
+
echo "6"
|
|
69
|
+
return 0
|
|
70
|
+
elif [[ -f ".bmad-core/install-manifest.yaml" ]]; then
|
|
71
|
+
# v4 detected
|
|
72
|
+
echo "4"
|
|
73
|
+
return 0
|
|
74
|
+
else
|
|
75
|
+
# Not installed
|
|
76
|
+
echo "0"
|
|
77
|
+
return 1
|
|
78
|
+
fi
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# @function get_bmad_config_path
|
|
82
|
+
# @intent Get BMAD configuration file path based on detected version
|
|
83
|
+
# @why v4 and v6 use different directory structures for config files
|
|
84
|
+
# @param None
|
|
85
|
+
# @returns Echoes config path to stdout, empty string if not installed
|
|
86
|
+
# @exitcode 0=path returned, 1=not installed
|
|
87
|
+
# @sideeffects None
|
|
88
|
+
# @edgecases Returns empty string if BMAD not detected
|
|
89
|
+
# @calledby Commands that need to read BMAD config (future use)
|
|
90
|
+
# @calls detect_bmad_version
|
|
91
|
+
get_bmad_config_path() {
|
|
92
|
+
local version=$(detect_bmad_version)
|
|
93
|
+
|
|
94
|
+
if [[ "$version" == "6" ]]; then
|
|
95
|
+
# Check both possible v6 paths
|
|
96
|
+
if [[ -f ".bmad/core/config.yaml" ]]; then
|
|
97
|
+
echo ".bmad/core/config.yaml"
|
|
98
|
+
else
|
|
99
|
+
echo "bmad/core/config.yaml"
|
|
100
|
+
fi
|
|
101
|
+
return 0
|
|
102
|
+
elif [[ "$version" == "4" ]]; then
|
|
103
|
+
echo ".bmad-core/config.yaml"
|
|
104
|
+
return 0
|
|
105
|
+
else
|
|
106
|
+
echo ""
|
|
107
|
+
return 1
|
|
108
|
+
fi
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# @function auto_enable_if_bmad_detected
|
|
112
|
+
# @intent Automatically enable BMAD voice plugin when BMAD framework is detected
|
|
113
|
+
# @why Provide seamless integration - users shouldn't need to manually enable voice mapping
|
|
114
|
+
# @param None
|
|
115
|
+
# @returns None
|
|
116
|
+
# @exitcode 0=auto-enabled, 1=not enabled (already enabled or BMAD not detected)
|
|
117
|
+
# @sideeffects Creates enabled flag file, creates plugin directory
|
|
118
|
+
# @edgecases Only auto-enables if plugin not already enabled, silent operation
|
|
119
|
+
# @calledby get_agent_voice
|
|
120
|
+
# @calls mkdir, touch, detect_bmad_version
|
|
121
|
+
auto_enable_if_bmad_detected() {
|
|
122
|
+
local version=$(detect_bmad_version)
|
|
123
|
+
|
|
124
|
+
# Check if BMAD is installed (any version) and plugin not already enabled
|
|
125
|
+
if [[ "$version" != "0" ]] && [[ ! -f "$ENABLED_FLAG" ]]; then
|
|
126
|
+
# BMAD detected but plugin not enabled - enable it silently
|
|
127
|
+
mkdir -p "$CONFIG_DIR"
|
|
128
|
+
touch "$ENABLED_FLAG"
|
|
129
|
+
return 0
|
|
130
|
+
fi
|
|
131
|
+
return 1
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
# @function get_agent_voice
|
|
135
|
+
# @intent Retrieve TTS voice assigned to specific BMAD agent (provider-aware)
|
|
136
|
+
# @why Each BMAD agent needs unique voice for multi-agent conversation differentiation
|
|
137
|
+
# @param $1 {string} agent_id - BMAD agent identifier (pm, dev, qa, architect, etc.)
|
|
138
|
+
# @returns Echoes voice name to stdout, empty string if plugin disabled or agent not found
|
|
139
|
+
# @exitcode Always 0
|
|
140
|
+
# @sideeffects May auto-enable plugin if BMAD detected
|
|
141
|
+
# @edgecases Returns empty string if plugin disabled/missing, parses markdown table syntax
|
|
142
|
+
# @calledby bmad-tts-injector.sh, play-tts.sh when BMAD agent is active
|
|
143
|
+
# @calls auto_enable_if_bmad_detected, grep, awk, sed
|
|
144
|
+
# @version 2.0.0 - Now provider-aware: returns Piper or macOS voice based on active provider
|
|
145
|
+
get_agent_voice() {
|
|
146
|
+
local agent_id="$1"
|
|
147
|
+
|
|
148
|
+
# Check for BMAD v6 CSV file first (preferred, loose coupling)
|
|
149
|
+
# If this exists, use it directly without requiring plugin enable flag
|
|
150
|
+
# Support both .bmad (standard) and bmad (alternative) paths
|
|
151
|
+
local bmad_voice_map=""
|
|
152
|
+
if [[ -f ".bmad/_cfg/agent-voice-map.csv" ]]; then
|
|
153
|
+
bmad_voice_map=".bmad/_cfg/agent-voice-map.csv"
|
|
154
|
+
elif [[ -f "bmad/_cfg/agent-voice-map.csv" ]]; then
|
|
155
|
+
bmad_voice_map="bmad/_cfg/agent-voice-map.csv"
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
if [[ -n "$bmad_voice_map" ]]; then
|
|
159
|
+
# Read from BMAD's standard _cfg directory
|
|
160
|
+
# CSV format: agent_id,voice_name
|
|
161
|
+
local voice=$(grep "^$agent_id," "$bmad_voice_map" | cut -d',' -f2)
|
|
162
|
+
|
|
163
|
+
# If voice is empty or generic (same for all), use defaults
|
|
164
|
+
if [[ -n "$voice" ]] && [[ "$voice" != "en_US-lessac-medium" ]]; then
|
|
165
|
+
echo "$voice"
|
|
166
|
+
return
|
|
167
|
+
fi
|
|
168
|
+
# If empty or generic, fall through to defaults below
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
# Default voice mappings (hardcoded fallback when CSV is missing or has generic values)
|
|
172
|
+
# These match the BMAD-METHOD defaults for consistency
|
|
173
|
+
case "$agent_id" in
|
|
174
|
+
bmad-master)
|
|
175
|
+
echo "en_US-lessac-medium"
|
|
176
|
+
return
|
|
177
|
+
;;
|
|
178
|
+
analyst)
|
|
179
|
+
echo "en_US-kristin-medium"
|
|
180
|
+
return
|
|
181
|
+
;;
|
|
182
|
+
architect)
|
|
183
|
+
echo "en_GB-alan-medium"
|
|
184
|
+
return
|
|
185
|
+
;;
|
|
186
|
+
dev)
|
|
187
|
+
echo "en_US-joe-medium"
|
|
188
|
+
return
|
|
189
|
+
;;
|
|
190
|
+
pm)
|
|
191
|
+
echo "en_US-ryan-high"
|
|
192
|
+
return
|
|
193
|
+
;;
|
|
194
|
+
quick-flow-solo-dev)
|
|
195
|
+
echo "en_US-joe-medium"
|
|
196
|
+
return
|
|
197
|
+
;;
|
|
198
|
+
sm)
|
|
199
|
+
echo "en_US-amy-medium"
|
|
200
|
+
return
|
|
201
|
+
;;
|
|
202
|
+
tea)
|
|
203
|
+
echo "en_US-kusal-medium"
|
|
204
|
+
return
|
|
205
|
+
;;
|
|
206
|
+
tech-writer)
|
|
207
|
+
echo "en_US-kristin-medium"
|
|
208
|
+
return
|
|
209
|
+
;;
|
|
210
|
+
ux-designer)
|
|
211
|
+
echo "en_US-kristin-medium"
|
|
212
|
+
return
|
|
213
|
+
;;
|
|
214
|
+
frame-expert)
|
|
215
|
+
echo "en_GB-alan-medium"
|
|
216
|
+
return
|
|
217
|
+
;;
|
|
218
|
+
esac
|
|
219
|
+
|
|
220
|
+
# Auto-enable if BMAD is detected (for legacy markdown config)
|
|
221
|
+
auto_enable_if_bmad_detected
|
|
222
|
+
|
|
223
|
+
if [[ ! -f "$ENABLED_FLAG" ]]; then
|
|
224
|
+
echo "" # Plugin disabled
|
|
225
|
+
return
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
# Fallback to legacy markdown config file
|
|
229
|
+
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
230
|
+
echo "" # Plugin file missing
|
|
231
|
+
return
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
# Detect active TTS provider
|
|
235
|
+
local provider_file=""
|
|
236
|
+
if [[ -f ".claude/tts-provider.txt" ]]; then
|
|
237
|
+
provider_file=".claude/tts-provider.txt"
|
|
238
|
+
elif [[ -f "$HOME/.claude/tts-provider.txt" ]]; then
|
|
239
|
+
provider_file="$HOME/.claude/tts-provider.txt"
|
|
240
|
+
fi
|
|
241
|
+
|
|
242
|
+
local active_provider="piper" # default
|
|
243
|
+
if [[ -n "$provider_file" ]] && [[ -f "$provider_file" ]]; then
|
|
244
|
+
active_provider=$(cat "$provider_file")
|
|
245
|
+
fi
|
|
246
|
+
|
|
247
|
+
# Extract voice from markdown table based on provider
|
|
248
|
+
# Table: Agent ID | Agent Name | Intro | Piper Voice | macOS Voice | Personality
|
|
249
|
+
# AWK columns: $1=empty | $2=ID | $3=Name | $4=Intro | $5=Piper | $6=macOS | $7=Personality
|
|
250
|
+
local column=5 # Default to Piper (AWK column 5)
|
|
251
|
+
if [[ "$active_provider" == "piper" ]]; then
|
|
252
|
+
column=6 # Use Piper (AWK column 6)
|
|
253
|
+
fi
|
|
254
|
+
|
|
255
|
+
local voice=$(grep "^| $agent_id " "$VOICE_CONFIG_FILE" | \
|
|
256
|
+
awk -F'|' "{print \$$column}" | \
|
|
257
|
+
sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
258
|
+
|
|
259
|
+
echo "$voice"
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
# @function sync_intros_from_manifest
|
|
263
|
+
# @intent Synchronize generic intros in agent-voice-map.csv with displayNames from agent-manifest.csv
|
|
264
|
+
# @why Until BMAD PR 987 merges, CSV has generic "Hello! Ready to help with the discussion." intros
|
|
265
|
+
# @param None (operates on .bmad/_cfg files)
|
|
266
|
+
# @returns 0 on success, 1 on error
|
|
267
|
+
# @exitcode 0 on success, 1 if files missing or sync fails
|
|
268
|
+
# @sideeffects Updates agent-voice-map.csv, creates .backup on first run, writes .bmad-csv-sync-timestamp
|
|
269
|
+
# @edgecases Only updates EXACT match of generic intro, preserves all custom intros, idempotent
|
|
270
|
+
# @calledby get_agent_intro (lazy trigger on manifest change)
|
|
271
|
+
# @calls grep, cut, sed, stat, date
|
|
272
|
+
# @version 2.17.4 - Safe CSV sync utility that preserves user customizations
|
|
273
|
+
sync_intros_from_manifest() {
|
|
274
|
+
# Locate the CSV and manifest files
|
|
275
|
+
local bmad_voice_map=""
|
|
276
|
+
local manifest_file=""
|
|
277
|
+
|
|
278
|
+
if [[ -f ".bmad/_cfg/agent-voice-map.csv" ]]; then
|
|
279
|
+
bmad_voice_map=".bmad/_cfg/agent-voice-map.csv"
|
|
280
|
+
elif [[ -f "bmad/_cfg/agent-voice-map.csv" ]]; then
|
|
281
|
+
bmad_voice_map="bmad/_cfg/agent-voice-map.csv"
|
|
282
|
+
fi
|
|
283
|
+
|
|
284
|
+
if [[ -f "_bmad/_config/agent-manifest.csv" ]]; then
|
|
285
|
+
manifest_file="_bmad/_config/agent-manifest.csv"
|
|
286
|
+
elif [[ -f "_bmad/_config/agent-manifest.csv" ]]; then
|
|
287
|
+
manifest_file="_bmad/_config/agent-manifest.csv"
|
|
288
|
+
fi
|
|
289
|
+
|
|
290
|
+
# Both files must exist for sync to work
|
|
291
|
+
if [[ -z "$bmad_voice_map" ]] || [[ -z "$manifest_file" ]]; then
|
|
292
|
+
return 1
|
|
293
|
+
fi
|
|
294
|
+
|
|
295
|
+
# Check if sync is needed based on manifest timestamp
|
|
296
|
+
local timestamp_file="${bmad_voice_map%/*}/.bmad-csv-sync-timestamp"
|
|
297
|
+
local manifest_mtime=$(stat -c '%Y' "$manifest_file" 2>/dev/null || stat -f '%m' "$manifest_file" 2>/dev/null)
|
|
298
|
+
|
|
299
|
+
if [[ -f "$timestamp_file" ]]; then
|
|
300
|
+
local last_sync=$(cat "$timestamp_file" 2>/dev/null || echo "0")
|
|
301
|
+
if [[ "$manifest_mtime" -le "$last_sync" ]]; then
|
|
302
|
+
# Manifest hasn't changed since last sync
|
|
303
|
+
return 0
|
|
304
|
+
fi
|
|
305
|
+
fi
|
|
306
|
+
|
|
307
|
+
# Create backup on first sync
|
|
308
|
+
if [[ ! -f "${bmad_voice_map}.backup" ]]; then
|
|
309
|
+
cp "$bmad_voice_map" "${bmad_voice_map}.backup"
|
|
310
|
+
fi
|
|
311
|
+
|
|
312
|
+
# Build a temp file with synced intros
|
|
313
|
+
local temp_file="${bmad_voice_map}.tmp"
|
|
314
|
+
local generic_intro="Hello! Ready to help with the discussion."
|
|
315
|
+
|
|
316
|
+
# Read header
|
|
317
|
+
head -n 1 "$bmad_voice_map" > "$temp_file"
|
|
318
|
+
|
|
319
|
+
# Process each agent entry
|
|
320
|
+
tail -n +2 "$bmad_voice_map" | while IFS=, read -r agent voice intro; do
|
|
321
|
+
# Remove quotes from intro
|
|
322
|
+
intro=$(echo "$intro" | sed 's/^"//;s/"$//')
|
|
323
|
+
|
|
324
|
+
# Only update if intro is the exact generic placeholder
|
|
325
|
+
if [[ "$intro" == "$generic_intro" ]]; then
|
|
326
|
+
# Look up displayName and title from manifest using awk for proper CSV parsing
|
|
327
|
+
# CSV format: name,displayName,title,icon,role,...
|
|
328
|
+
local manifest_data=$(grep "^\"*${agent}\"*," "$manifest_file" | awk -F'","' '{
|
|
329
|
+
gsub(/^"/, "", $2);
|
|
330
|
+
gsub(/"$/, "", $3);
|
|
331
|
+
print $2 "|" $3
|
|
332
|
+
}')
|
|
333
|
+
|
|
334
|
+
local display_name=$(echo "$manifest_data" | cut -d'|' -f1)
|
|
335
|
+
local title=$(echo "$manifest_data" | cut -d'|' -f2)
|
|
336
|
+
|
|
337
|
+
if [[ -n "$display_name" ]] && [[ -n "$title" ]]; then
|
|
338
|
+
# Generate intro like PR 987: "Hi! I'm [Name], your [Title]."
|
|
339
|
+
intro="Hi! I'm ${display_name}, your ${title}."
|
|
340
|
+
elif [[ -n "$display_name" ]]; then
|
|
341
|
+
# Fallback if title missing
|
|
342
|
+
intro="${display_name} here"
|
|
343
|
+
fi
|
|
344
|
+
fi
|
|
345
|
+
|
|
346
|
+
# Write the line (preserving custom intros, updating generic ones)
|
|
347
|
+
echo "${agent},${voice},\"${intro}\""
|
|
348
|
+
done >> "$temp_file"
|
|
349
|
+
|
|
350
|
+
# Replace original with synced version
|
|
351
|
+
mv "$temp_file" "$bmad_voice_map"
|
|
352
|
+
|
|
353
|
+
# Update timestamp
|
|
354
|
+
echo "$manifest_mtime" > "$timestamp_file"
|
|
355
|
+
|
|
356
|
+
return 0
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
# @function get_agent_intro
|
|
360
|
+
# @intent Retrieve intro text for BMAD agent (spoken before their message)
|
|
361
|
+
# @why Helps users identify which agent is speaking in party mode
|
|
362
|
+
# @param $1 {string} agent_id - BMAD agent identifier
|
|
363
|
+
# @returns Echoes intro text to stdout, empty string if not configured
|
|
364
|
+
# @exitcode Always 0
|
|
365
|
+
# @sideeffects Triggers CSV sync on first call or manifest change
|
|
366
|
+
# @edgecases Returns empty string if plugin file missing, parses column 3 of CSV or markdown table
|
|
367
|
+
# @calledby bmad-speak.sh for agent identification in party mode
|
|
368
|
+
# @calls sync_intros_from_manifest, grep, awk, sed, cut
|
|
369
|
+
# @version 2.2.1 - Added lazy CSV sync trigger
|
|
370
|
+
get_agent_intro() {
|
|
371
|
+
local agent_id="$1"
|
|
372
|
+
|
|
373
|
+
# Check for BMAD v6 CSV file first (preferred, loose coupling)
|
|
374
|
+
# If this exists, use it directly without requiring plugin enable flag
|
|
375
|
+
# Support both .bmad (standard) and bmad (alternative) paths
|
|
376
|
+
local bmad_voice_map=""
|
|
377
|
+
if [[ -f ".bmad/_cfg/agent-voice-map.csv" ]]; then
|
|
378
|
+
bmad_voice_map=".bmad/_cfg/agent-voice-map.csv"
|
|
379
|
+
elif [[ -f "bmad/_cfg/agent-voice-map.csv" ]]; then
|
|
380
|
+
bmad_voice_map="bmad/_cfg/agent-voice-map.csv"
|
|
381
|
+
fi
|
|
382
|
+
|
|
383
|
+
# Lazy trigger: sync intros from manifest if needed
|
|
384
|
+
if [[ -n "$bmad_voice_map" ]]; then
|
|
385
|
+
sync_intros_from_manifest
|
|
386
|
+
fi
|
|
387
|
+
|
|
388
|
+
if [[ -n "$bmad_voice_map" ]]; then
|
|
389
|
+
# Read from BMAD's standard _cfg directory
|
|
390
|
+
# CSV format: agent,voice,intro
|
|
391
|
+
# Use awk to properly handle quoted CSV fields (intro may contain commas)
|
|
392
|
+
local intro=$(grep "^$agent_id," "$bmad_voice_map" | awk -F',' '{
|
|
393
|
+
# Extract field 3 onwards (intro may span multiple comma-separated parts)
|
|
394
|
+
intro = $3;
|
|
395
|
+
for (i = 4; i <= NF; i++) {
|
|
396
|
+
intro = intro "," $i;
|
|
397
|
+
}
|
|
398
|
+
gsub(/^"/, "", intro);
|
|
399
|
+
gsub(/"$/, "", intro);
|
|
400
|
+
print intro;
|
|
401
|
+
}')
|
|
402
|
+
|
|
403
|
+
# If intro is empty or generic, fall back to agent display name from manifest
|
|
404
|
+
if [[ -z "$intro" ]] || [[ "$intro" == "Hello! Ready to help with the discussion." ]]; then
|
|
405
|
+
# Try to get display name from agent-manifest.csv
|
|
406
|
+
local manifest_file=""
|
|
407
|
+
if [[ -f "_bmad/_config/agent-manifest.csv" ]]; then
|
|
408
|
+
manifest_file="_bmad/_config/agent-manifest.csv"
|
|
409
|
+
elif [[ -f "_bmad/_config/agent-manifest.csv" ]]; then
|
|
410
|
+
manifest_file="_bmad/_config/agent-manifest.csv"
|
|
411
|
+
fi
|
|
412
|
+
|
|
413
|
+
if [[ -n "$manifest_file" ]]; then
|
|
414
|
+
# Extract displayName (column 2) where name (column 1) matches agent_id
|
|
415
|
+
# CSV format: name,displayName,title,icon,role,...
|
|
416
|
+
local display_name=$(grep "^\"*${agent_id}\"*," "$manifest_file" | cut -d',' -f2 | sed 's/^"//;s/"$//')
|
|
417
|
+
if [[ -n "$display_name" ]]; then
|
|
418
|
+
intro="$display_name here"
|
|
419
|
+
fi
|
|
420
|
+
fi
|
|
421
|
+
fi
|
|
422
|
+
|
|
423
|
+
# If we got an intro, return it
|
|
424
|
+
if [[ -n "$intro" ]]; then
|
|
425
|
+
echo "$intro"
|
|
426
|
+
return
|
|
427
|
+
fi
|
|
428
|
+
# Otherwise fall through to hardcoded defaults below
|
|
429
|
+
fi
|
|
430
|
+
|
|
431
|
+
# Hardcoded default intro mappings (final fallback)
|
|
432
|
+
# These match the BMAD-METHOD agent display names for consistency
|
|
433
|
+
case "$agent_id" in
|
|
434
|
+
bmad-master)
|
|
435
|
+
echo "BMad Master here"
|
|
436
|
+
return
|
|
437
|
+
;;
|
|
438
|
+
analyst)
|
|
439
|
+
echo "Mary here"
|
|
440
|
+
return
|
|
441
|
+
;;
|
|
442
|
+
architect)
|
|
443
|
+
echo "Winston here"
|
|
444
|
+
return
|
|
445
|
+
;;
|
|
446
|
+
dev)
|
|
447
|
+
echo "Amelia here"
|
|
448
|
+
return
|
|
449
|
+
;;
|
|
450
|
+
pm)
|
|
451
|
+
echo "John here"
|
|
452
|
+
return
|
|
453
|
+
;;
|
|
454
|
+
quick-flow-solo-dev)
|
|
455
|
+
echo "Barry here"
|
|
456
|
+
return
|
|
457
|
+
;;
|
|
458
|
+
sm)
|
|
459
|
+
echo "Bob here"
|
|
460
|
+
return
|
|
461
|
+
;;
|
|
462
|
+
tea)
|
|
463
|
+
echo "Murat here"
|
|
464
|
+
return
|
|
465
|
+
;;
|
|
466
|
+
tech-writer)
|
|
467
|
+
echo "Paige here"
|
|
468
|
+
return
|
|
469
|
+
;;
|
|
470
|
+
ux-designer)
|
|
471
|
+
echo "Sally here"
|
|
472
|
+
return
|
|
473
|
+
;;
|
|
474
|
+
frame-expert)
|
|
475
|
+
echo "Frame Expert here"
|
|
476
|
+
return
|
|
477
|
+
;;
|
|
478
|
+
esac
|
|
479
|
+
|
|
480
|
+
# Fallback to legacy markdown config file
|
|
481
|
+
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
482
|
+
echo ""
|
|
483
|
+
return
|
|
484
|
+
fi
|
|
485
|
+
|
|
486
|
+
# AWK column 4 = Intro text
|
|
487
|
+
local intro=$(grep "^| $agent_id " "$VOICE_CONFIG_FILE" | \
|
|
488
|
+
awk -F'|' '{print $4}' | \
|
|
489
|
+
sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
490
|
+
|
|
491
|
+
echo "$intro"
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
# @function get_agent_personality
|
|
495
|
+
# @intent Retrieve TTS personality assigned to specific BMAD agent
|
|
496
|
+
# @why Agents may have distinct speaking styles (friendly, professional, energetic, etc.)
|
|
497
|
+
# @param $1 {string} agent_id - BMAD agent identifier
|
|
498
|
+
# @returns Echoes personality name to stdout, empty string if not found
|
|
499
|
+
# @exitcode Always 0
|
|
500
|
+
# @sideeffects None
|
|
501
|
+
# @edgecases Returns empty string if plugin file missing, parses column 6 of markdown table
|
|
502
|
+
# @calledby bmad-tts-injector.sh for personality-aware voice synthesis
|
|
503
|
+
# @calls grep, awk, sed
|
|
504
|
+
# @version 2.0.0 - Updated to column 6 (was 5) due to new provider-aware format
|
|
505
|
+
get_agent_personality() {
|
|
506
|
+
local agent_id="$1"
|
|
507
|
+
|
|
508
|
+
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
509
|
+
echo ""
|
|
510
|
+
return
|
|
511
|
+
fi
|
|
512
|
+
|
|
513
|
+
# AWK column 7 = Personality
|
|
514
|
+
local personality=$(grep "^| $agent_id " "$VOICE_CONFIG_FILE" | \
|
|
515
|
+
awk -F'|' '{print $7}' | \
|
|
516
|
+
sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
517
|
+
|
|
518
|
+
echo "$personality"
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
# @function is_plugin_enabled
|
|
522
|
+
# @intent Check if BMAD voice plugin is currently enabled
|
|
523
|
+
# @why Allow conditional logic based on plugin state
|
|
524
|
+
# @param None
|
|
525
|
+
# @returns Echoes "true" or "false" to stdout
|
|
526
|
+
# @exitcode Always 0
|
|
527
|
+
# @sideeffects None
|
|
528
|
+
# @edgecases None
|
|
529
|
+
# @calledby show_status, enable_plugin, disable_plugin
|
|
530
|
+
# @calls None (file existence check)
|
|
531
|
+
is_plugin_enabled() {
|
|
532
|
+
[[ -f "$ENABLED_FLAG" ]] && echo "true" || echo "false"
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
# @function enable_plugin
|
|
536
|
+
# @intent Enable BMAD voice plugin and backup current voice settings
|
|
537
|
+
# @why Allow users to switch to per-agent voices while preserving original configuration
|
|
538
|
+
# @param None
|
|
539
|
+
# @returns None
|
|
540
|
+
# @exitcode Always 0
|
|
541
|
+
# @sideeffects Creates flag file, backs up current voice/personality/sentiment to .bmad-previous-settings
|
|
542
|
+
# @sideeffects Creates activation-instructions file for BMAD agents, calls bmad-tts-injector.sh
|
|
543
|
+
# @edgecases Handles missing settings files gracefully with defaults
|
|
544
|
+
# @calledby Main command dispatcher with "enable" argument
|
|
545
|
+
# @calls mkdir, cat, source, list_mappings, bmad-tts-injector.sh
|
|
546
|
+
enable_plugin() {
|
|
547
|
+
mkdir -p "$CONFIG_DIR"
|
|
548
|
+
|
|
549
|
+
# Save current settings before enabling
|
|
550
|
+
BACKUP_FILE="$CONFIG_DIR/.bmad-previous-settings"
|
|
551
|
+
|
|
552
|
+
# Save current voice
|
|
553
|
+
if [[ -f ".claude/tts-voice.txt" ]]; then
|
|
554
|
+
CURRENT_VOICE=$(cat .claude/tts-voice.txt 2>/dev/null)
|
|
555
|
+
elif [[ -f "$HOME/.claude/tts-voice.txt" ]]; then
|
|
556
|
+
CURRENT_VOICE=$(cat "$HOME/.claude/tts-voice.txt" 2>/dev/null)
|
|
557
|
+
else
|
|
558
|
+
CURRENT_VOICE="Aria"
|
|
559
|
+
fi
|
|
560
|
+
|
|
561
|
+
# Save current personality
|
|
562
|
+
if [[ -f ".claude/tts-personality.txt" ]]; then
|
|
563
|
+
CURRENT_PERSONALITY=$(cat .claude/tts-personality.txt 2>/dev/null)
|
|
564
|
+
elif [[ -f "$HOME/.claude/tts-personality.txt" ]]; then
|
|
565
|
+
CURRENT_PERSONALITY=$(cat "$HOME/.claude/tts-personality.txt" 2>/dev/null)
|
|
566
|
+
else
|
|
567
|
+
CURRENT_PERSONALITY="normal"
|
|
568
|
+
fi
|
|
569
|
+
|
|
570
|
+
# Save current sentiment
|
|
571
|
+
if [[ -f ".claude/tts-sentiment.txt" ]]; then
|
|
572
|
+
CURRENT_SENTIMENT=$(cat .claude/tts-sentiment.txt 2>/dev/null)
|
|
573
|
+
elif [[ -f "$HOME/.claude/tts-sentiment.txt" ]]; then
|
|
574
|
+
CURRENT_SENTIMENT=$(cat "$HOME/.claude/tts-sentiment.txt" 2>/dev/null)
|
|
575
|
+
else
|
|
576
|
+
CURRENT_SENTIMENT=""
|
|
577
|
+
fi
|
|
578
|
+
|
|
579
|
+
# Write backup
|
|
580
|
+
cat > "$BACKUP_FILE" <<EOF
|
|
581
|
+
VOICE=$CURRENT_VOICE
|
|
582
|
+
PERSONALITY=$CURRENT_PERSONALITY
|
|
583
|
+
SENTIMENT=$CURRENT_SENTIMENT
|
|
584
|
+
EOF
|
|
585
|
+
|
|
586
|
+
touch "$ENABLED_FLAG"
|
|
587
|
+
|
|
588
|
+
# Create activation-instructions file for BMAD agents
|
|
589
|
+
ACTIVATION_FILE=".claude/activation-instructions"
|
|
590
|
+
if [[ ! -f "$ACTIVATION_FILE" ]]; then
|
|
591
|
+
cat > "$ACTIVATION_FILE" <<'ACTIVATION_EOF'
|
|
592
|
+
# BMAD Agent Activation Instructions
|
|
593
|
+
|
|
594
|
+
When a BMAD agent activates, follow these steps EXACTLY:
|
|
595
|
+
|
|
596
|
+
## STEP 3.5a: Create BMAD Context File (CRITICAL)
|
|
597
|
+
|
|
598
|
+
**IMMEDIATELY after agent identification, create the context file:**
|
|
599
|
+
|
|
600
|
+
```bash
|
|
601
|
+
echo "$AGENT_ID" > .bmad-agent-context
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
Where `$AGENT_ID` is one of:
|
|
605
|
+
- `pm` (Product Manager)
|
|
606
|
+
- `dev` (Developer)
|
|
607
|
+
- `qa` (QA)
|
|
608
|
+
- `architect` (Architect)
|
|
609
|
+
- `po` (Product Owner)
|
|
610
|
+
- `analyst` (Analyst)
|
|
611
|
+
- `sm` (Scrum Master)
|
|
612
|
+
- `ux-expert` (UX Expert)
|
|
613
|
+
- `bmad-master` (BMAD Master)
|
|
614
|
+
- `bmad-orchestrator` (Orchestrator)
|
|
615
|
+
|
|
616
|
+
**Example:**
|
|
617
|
+
```bash
|
|
618
|
+
# For Product Manager agent
|
|
619
|
+
echo "pm" > .bmad-agent-context
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
This file allows AgentVibes to:
|
|
623
|
+
1. Detect which BMAD agent is active
|
|
624
|
+
2. Look up the correct voice mapping
|
|
625
|
+
3. Automatically speak questions using the agent's assigned voice
|
|
626
|
+
|
|
627
|
+
## STEP 10: Clean Up on Exit
|
|
628
|
+
|
|
629
|
+
**Before exiting the agent, remove the context file:**
|
|
630
|
+
|
|
631
|
+
```bash
|
|
632
|
+
rm -f .bmad-agent-context
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
This ensures voice switching only happens when an agent is active.
|
|
636
|
+
|
|
637
|
+
## Why This Matters
|
|
638
|
+
|
|
639
|
+
Without the `.bmad-agent-context` file:
|
|
640
|
+
- AgentVibes cannot detect which agent is active
|
|
641
|
+
- Questions won't be spoken automatically
|
|
642
|
+
- Voice switching won't work
|
|
643
|
+
- The BMAD voice plugin becomes non-functional
|
|
644
|
+
|
|
645
|
+
**This is MANDATORY for BMAD voice integration to work!**
|
|
646
|
+
ACTIVATION_EOF
|
|
647
|
+
echo "📝 Created activation instructions: $ACTIVATION_FILE"
|
|
648
|
+
fi
|
|
649
|
+
|
|
650
|
+
echo "✅ BMAD voice plugin enabled"
|
|
651
|
+
echo "💾 Previous settings backed up:"
|
|
652
|
+
echo " Voice: $CURRENT_VOICE"
|
|
653
|
+
echo " Personality: $CURRENT_PERSONALITY"
|
|
654
|
+
[[ -n "$CURRENT_SENTIMENT" ]] && echo " Sentiment: $CURRENT_SENTIMENT"
|
|
655
|
+
echo ""
|
|
656
|
+
list_mappings
|
|
657
|
+
|
|
658
|
+
# Automatically inject TTS into BMAD agents
|
|
659
|
+
echo ""
|
|
660
|
+
echo "🎤 Automatically enabling TTS for BMAD agents..."
|
|
661
|
+
echo ""
|
|
662
|
+
|
|
663
|
+
# Get the directory where this script is located
|
|
664
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
665
|
+
|
|
666
|
+
# Check if bmad-tts-injector.sh exists
|
|
667
|
+
if [[ -f "$SCRIPT_DIR/bmad-tts-injector.sh" ]]; then
|
|
668
|
+
# Run the TTS injector
|
|
669
|
+
"$SCRIPT_DIR/bmad-tts-injector.sh" enable
|
|
670
|
+
else
|
|
671
|
+
echo "⚠️ TTS injector not found at: $SCRIPT_DIR/bmad-tts-injector.sh"
|
|
672
|
+
echo " You can manually enable TTS with: /agent-vibes:bmad-tts enable"
|
|
673
|
+
fi
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
# @function disable_plugin
|
|
677
|
+
# @intent Disable BMAD voice plugin and restore previous voice settings
|
|
678
|
+
# @why Allow users to return to single-voice mode with their original configuration
|
|
679
|
+
# @param None
|
|
680
|
+
# @returns None
|
|
681
|
+
# @exitcode Always 0
|
|
682
|
+
# @sideeffects Removes flag file, restores settings from backup, calls bmad-tts-injector.sh disable
|
|
683
|
+
# @edgecases Handles missing backup file gracefully, warns user if no backup exists
|
|
684
|
+
# @calledby Main command dispatcher with "disable" argument
|
|
685
|
+
# @calls source, rm, echo, bmad-tts-injector.sh
|
|
686
|
+
disable_plugin() {
|
|
687
|
+
BACKUP_FILE="$CONFIG_DIR/.bmad-previous-settings"
|
|
688
|
+
|
|
689
|
+
# Check if we have a backup to restore
|
|
690
|
+
if [[ -f "$BACKUP_FILE" ]]; then
|
|
691
|
+
source "$BACKUP_FILE"
|
|
692
|
+
|
|
693
|
+
echo "❌ BMAD voice plugin disabled"
|
|
694
|
+
echo "🔄 Restoring previous settings:"
|
|
695
|
+
echo " Voice: $VOICE"
|
|
696
|
+
echo " Personality: $PERSONALITY"
|
|
697
|
+
[[ -n "$SENTIMENT" ]] && echo " Sentiment: $SENTIMENT"
|
|
698
|
+
|
|
699
|
+
# Restore voice
|
|
700
|
+
if [[ -n "$VOICE" ]]; then
|
|
701
|
+
echo "$VOICE" > .claude/tts-voice.txt 2>/dev/null || echo "$VOICE" > "$HOME/.claude/tts-voice.txt"
|
|
702
|
+
fi
|
|
703
|
+
|
|
704
|
+
# Restore personality
|
|
705
|
+
if [[ -n "$PERSONALITY" ]] && [[ "$PERSONALITY" != "normal" ]]; then
|
|
706
|
+
echo "$PERSONALITY" > .claude/tts-personality.txt 2>/dev/null || echo "$PERSONALITY" > "$HOME/.claude/tts-personality.txt"
|
|
707
|
+
fi
|
|
708
|
+
|
|
709
|
+
# Restore sentiment
|
|
710
|
+
if [[ -n "$SENTIMENT" ]]; then
|
|
711
|
+
echo "$SENTIMENT" > .claude/tts-sentiment.txt 2>/dev/null || echo "$SENTIMENT" > "$HOME/.claude/tts-sentiment.txt"
|
|
712
|
+
fi
|
|
713
|
+
|
|
714
|
+
# Clean up backup
|
|
715
|
+
rm -f "$BACKUP_FILE"
|
|
716
|
+
else
|
|
717
|
+
echo "❌ BMAD voice plugin disabled"
|
|
718
|
+
echo "⚠️ No previous settings found to restore"
|
|
719
|
+
echo "AgentVibes will use current voice/personality settings"
|
|
720
|
+
fi
|
|
721
|
+
|
|
722
|
+
rm -f "$ENABLED_FLAG"
|
|
723
|
+
|
|
724
|
+
# Automatically remove TTS from BMAD agents
|
|
725
|
+
echo ""
|
|
726
|
+
echo "🔇 Automatically disabling TTS for BMAD agents..."
|
|
727
|
+
echo ""
|
|
728
|
+
|
|
729
|
+
# Get the directory where this script is located
|
|
730
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
731
|
+
|
|
732
|
+
# Check if bmad-tts-injector.sh exists
|
|
733
|
+
if [[ -f "$SCRIPT_DIR/bmad-tts-injector.sh" ]]; then
|
|
734
|
+
# Run the TTS injector disable
|
|
735
|
+
"$SCRIPT_DIR/bmad-tts-injector.sh" disable
|
|
736
|
+
else
|
|
737
|
+
echo "⚠️ TTS injector not found"
|
|
738
|
+
echo " You can manually disable TTS with: /agent-vibes:bmad-tts disable"
|
|
739
|
+
fi
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
# @function list_mappings
|
|
743
|
+
# @intent Display all BMAD agent-to-voice mappings in readable format (provider-aware)
|
|
744
|
+
# @why Help users see which voice is assigned to each agent based on active TTS provider
|
|
745
|
+
# @param None
|
|
746
|
+
# @returns None
|
|
747
|
+
# @exitcode 0=success, 1=plugin file not found
|
|
748
|
+
# @sideeffects Writes formatted output to stdout
|
|
749
|
+
# @edgecases Parses markdown table format, skips header and separator rows
|
|
750
|
+
# @calledby enable_plugin, show_status, main command dispatcher with "list"
|
|
751
|
+
# @calls grep, sed, echo
|
|
752
|
+
# @version 2.1.0 - Now provider-aware: shows Piper or macOS voices based on active provider
|
|
753
|
+
list_mappings() {
|
|
754
|
+
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
755
|
+
echo "❌ Plugin file not found: $VOICE_CONFIG_FILE"
|
|
756
|
+
return 1
|
|
757
|
+
fi
|
|
758
|
+
|
|
759
|
+
# Detect active TTS provider
|
|
760
|
+
local provider_file=""
|
|
761
|
+
if [[ -f ".claude/tts-provider.txt" ]]; then
|
|
762
|
+
provider_file=".claude/tts-provider.txt"
|
|
763
|
+
elif [[ -f "$HOME/.claude/tts-provider.txt" ]]; then
|
|
764
|
+
provider_file="$HOME/.claude/tts-provider.txt"
|
|
765
|
+
fi
|
|
766
|
+
|
|
767
|
+
local active_provider="piper" # default
|
|
768
|
+
if [[ -n "$provider_file" ]] && [[ -f "$provider_file" ]]; then
|
|
769
|
+
active_provider=$(cat "$provider_file")
|
|
770
|
+
fi
|
|
771
|
+
|
|
772
|
+
# Display provider info
|
|
773
|
+
echo "📊 BMAD Agent Voice Mappings (Provider: $active_provider):"
|
|
774
|
+
echo ""
|
|
775
|
+
|
|
776
|
+
# Table: Agent ID | Agent Name | Intro | Piper Voice | macOS Voice | Personality
|
|
777
|
+
# AWK columns: $1=empty | $2=ID | $3=Name | $4=Intro | $5=Piper | $6=macOS | $7=Personality
|
|
778
|
+
local voice_column=5 # Default to Piper (AWK column 5)
|
|
779
|
+
if [[ "$active_provider" == "piper" ]]; then
|
|
780
|
+
voice_column=6 # Use Piper (AWK column 6)
|
|
781
|
+
fi
|
|
782
|
+
|
|
783
|
+
grep "^| " "$VOICE_CONFIG_FILE" | grep -v "Agent ID" | grep -v "^|---" | \
|
|
784
|
+
while IFS='|' read -r line; do
|
|
785
|
+
agent_id=$(echo "$line" | awk -F'|' '{print $2}' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
786
|
+
name=$(echo "$line" | awk -F'|' '{print $3}' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
787
|
+
voice=$(echo "$line" | awk -F'|' "{print \$$voice_column}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
788
|
+
personality=$(echo "$line" | awk -F'|' '{print $7}' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
789
|
+
|
|
790
|
+
[[ -n "$agent_id" ]] && echo " $agent_id → $voice [$personality]"
|
|
791
|
+
done
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
# @function set_agent_voice
|
|
795
|
+
# @intent Update voice and personality mapping for specific BMAD agent
|
|
796
|
+
# @why Allow customization of agent voices to user preferences
|
|
797
|
+
# @param $1 {string} agent_id - BMAD agent identifier
|
|
798
|
+
# @param $2 {string} voice - New voice name
|
|
799
|
+
# @param $3 {string} personality - New personality (optional, defaults to "normal")
|
|
800
|
+
# @returns None
|
|
801
|
+
# @exitcode 0=success, 1=plugin file not found or agent not found
|
|
802
|
+
# @sideeffects Modifies plugin file, creates .bak backup
|
|
803
|
+
# @edgecases Validates agent exists before updating
|
|
804
|
+
# @calledby Main command dispatcher with "set" argument
|
|
805
|
+
# @calls grep, sed
|
|
806
|
+
set_agent_voice() {
|
|
807
|
+
local agent_id="$1"
|
|
808
|
+
local voice="$2"
|
|
809
|
+
local personality="${3:-normal}"
|
|
810
|
+
|
|
811
|
+
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
812
|
+
echo "❌ Plugin file not found: $VOICE_CONFIG_FILE"
|
|
813
|
+
return 1
|
|
814
|
+
fi
|
|
815
|
+
|
|
816
|
+
# Check if agent exists
|
|
817
|
+
if ! grep -q "^| $agent_id " "$VOICE_CONFIG_FILE"; then
|
|
818
|
+
echo "❌ Agent '$agent_id' not found in plugin"
|
|
819
|
+
return 1
|
|
820
|
+
fi
|
|
821
|
+
|
|
822
|
+
# Update the voice and personality in the table
|
|
823
|
+
sed -i.bak "s/^| $agent_id |.*| .* | .* |$/| $agent_id | $(grep "^| $agent_id " "$VOICE_CONFIG_FILE" | awk -F'|' '{print $3}') | $voice | $personality |/" "$VOICE_CONFIG_FILE"
|
|
824
|
+
|
|
825
|
+
echo "✅ Updated $agent_id → $voice [$personality]"
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
# @function show_status
|
|
829
|
+
# @intent Display plugin status, BMAD detection, and current voice mappings
|
|
830
|
+
# @why Provide comprehensive overview of plugin state for troubleshooting
|
|
831
|
+
# @param None
|
|
832
|
+
# @returns None
|
|
833
|
+
# @exitcode Always 0
|
|
834
|
+
# @sideeffects Writes status information to stdout
|
|
835
|
+
# @edgecases Checks for BMAD installation via manifest file
|
|
836
|
+
# @calledby Main command dispatcher with "status" argument
|
|
837
|
+
# @calls is_plugin_enabled, list_mappings
|
|
838
|
+
show_status() {
|
|
839
|
+
# Check for BMAD installation
|
|
840
|
+
local bmad_installed="false"
|
|
841
|
+
if [[ -f ".bmad-core/install-manifest.yaml" ]]; then
|
|
842
|
+
bmad_installed="true"
|
|
843
|
+
fi
|
|
844
|
+
|
|
845
|
+
if [[ $(is_plugin_enabled) == "true" ]]; then
|
|
846
|
+
echo "✅ BMAD voice plugin: ENABLED"
|
|
847
|
+
if [[ "$bmad_installed" == "true" ]]; then
|
|
848
|
+
echo "🔍 BMAD detected: Auto-enabled"
|
|
849
|
+
fi
|
|
850
|
+
else
|
|
851
|
+
echo "❌ BMAD voice plugin: DISABLED"
|
|
852
|
+
if [[ "$bmad_installed" == "true" ]]; then
|
|
853
|
+
echo "⚠️ BMAD detected but plugin disabled (enable with: /agent-vibes-bmad enable)"
|
|
854
|
+
fi
|
|
855
|
+
fi
|
|
856
|
+
echo ""
|
|
857
|
+
list_mappings
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
# @function edit_plugin
|
|
861
|
+
# @intent Open plugin configuration file for manual editing
|
|
862
|
+
# @why Allow advanced users to modify voice mappings directly
|
|
863
|
+
# @param None
|
|
864
|
+
# @returns None
|
|
865
|
+
# @exitcode 0=success, 1=plugin file not found
|
|
866
|
+
# @sideeffects Displays file path and instructions
|
|
867
|
+
# @edgecases Does not actually open editor, just provides guidance
|
|
868
|
+
# @calledby Main command dispatcher with "edit" argument
|
|
869
|
+
# @calls echo
|
|
870
|
+
edit_plugin() {
|
|
871
|
+
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
872
|
+
echo "❌ Plugin file not found: $VOICE_CONFIG_FILE"
|
|
873
|
+
return 1
|
|
874
|
+
fi
|
|
875
|
+
|
|
876
|
+
echo "Opening $VOICE_CONFIG_FILE for editing..."
|
|
877
|
+
echo "Edit the markdown table to change voice mappings"
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
# Main command dispatcher
|
|
881
|
+
case "${1:-help}" in
|
|
882
|
+
enable)
|
|
883
|
+
enable_plugin
|
|
884
|
+
;;
|
|
885
|
+
disable)
|
|
886
|
+
disable_plugin
|
|
887
|
+
;;
|
|
888
|
+
status)
|
|
889
|
+
show_status
|
|
890
|
+
;;
|
|
891
|
+
list)
|
|
892
|
+
list_mappings
|
|
893
|
+
;;
|
|
894
|
+
set)
|
|
895
|
+
if [[ -z "$2" ]] || [[ -z "$3" ]]; then
|
|
896
|
+
echo "Usage: bmad-voice-manager.sh set <agent-id> <voice> [personality]"
|
|
897
|
+
exit 1
|
|
898
|
+
fi
|
|
899
|
+
set_agent_voice "$2" "$3" "$4"
|
|
900
|
+
;;
|
|
901
|
+
get-voice)
|
|
902
|
+
get_agent_voice "$2"
|
|
903
|
+
;;
|
|
904
|
+
get-intro)
|
|
905
|
+
get_agent_intro "$2"
|
|
906
|
+
;;
|
|
907
|
+
get-personality)
|
|
908
|
+
get_agent_personality "$2"
|
|
909
|
+
;;
|
|
910
|
+
edit)
|
|
911
|
+
edit_plugin
|
|
912
|
+
;;
|
|
913
|
+
*)
|
|
914
|
+
echo "Usage: bmad-voice-manager.sh {enable|disable|status|list|set|get-voice|get-intro|get-personality|edit}"
|
|
915
|
+
echo ""
|
|
916
|
+
echo "Commands:"
|
|
917
|
+
echo " enable Enable BMAD voice plugin"
|
|
918
|
+
echo " disable Disable BMAD voice plugin"
|
|
919
|
+
echo " status Show plugin status and mappings"
|
|
920
|
+
echo " list List all agent voice mappings"
|
|
921
|
+
echo " set <id> <voice> Set voice for agent"
|
|
922
|
+
echo " get-voice <id> Get voice for agent"
|
|
923
|
+
echo " get-intro <id> Get intro text for agent"
|
|
924
|
+
echo " get-personality <id> Get personality for agent"
|
|
925
|
+
echo " edit Edit plugin configuration"
|
|
926
|
+
exit 1
|
|
927
|
+
;;
|
|
928
|
+
esac
|