agentvibes 2.6.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/hooks/README-TTS-QUEUE.md +135 -0
- package/.claude/hooks/bmad-speak.sh +33 -9
- package/.claude/hooks/bmad-voice-manager.sh +61 -29
- package/.claude/hooks/play-tts.sh +5 -0
- package/.claude/hooks/tts-queue-worker.sh +68 -0
- package/.claude/hooks/tts-queue.sh +105 -0
- package/.claude/verbosity.txt +1 -0
- package/README.md +8 -8
- package/RELEASE_NOTES.md +160 -614
- package/package.json +1 -1
- package/.claude/plugins/bmad-voices-enabled.flag +0 -0
- package/.claude/plugins/bmad-voices.md +0 -67
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# TTS Queue System for Party Mode
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The TTS Queue System enables BMAD party mode agents to speak sequentially without blocking Claude Code's response generation. This provides the best of both worlds:
|
|
6
|
+
|
|
7
|
+
- ✅ **Non-blocking**: Claude Code continues generating agent responses immediately
|
|
8
|
+
- ✅ **Sequential playback**: Agents speak one at a time in order, with correct voices
|
|
9
|
+
- ✅ **No audio overlap**: The queue ensures voices don't talk over each other
|
|
10
|
+
|
|
11
|
+
## How It Works
|
|
12
|
+
|
|
13
|
+
### Architecture
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
bmad-speak.sh → tts-queue.sh → Queue Directory → tts-queue-worker.sh → play-tts.sh → Audio
|
|
17
|
+
↓ ↓ ↓
|
|
18
|
+
Returns Returns Processes queue
|
|
19
|
+
immediately immediately sequentially
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Components
|
|
23
|
+
|
|
24
|
+
1. **`tts-queue.sh`** - Queue manager (adds requests, shows status, clears queue)
|
|
25
|
+
2. **`tts-queue-worker.sh`** - Background worker process (plays audio sequentially)
|
|
26
|
+
3. **`bmad-speak.sh`** - Updated to use queue system with `&` for non-blocking
|
|
27
|
+
4. **Queue directory**: `/tmp/agentvibes-tts-queue/` - Stores pending TTS requests
|
|
28
|
+
|
|
29
|
+
### Flow
|
|
30
|
+
|
|
31
|
+
1. Party mode agent generates response
|
|
32
|
+
2. `bmad-speak.sh` adds TTS request to queue and returns immediately (`&`)
|
|
33
|
+
3. Queue worker automatically starts if needed
|
|
34
|
+
4. Worker processes queue items sequentially (oldest first)
|
|
35
|
+
5. Each audio file plays to completion before next one starts
|
|
36
|
+
6. Worker auto-exits after 5 seconds of idle time
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
### Normal Usage (Automatic)
|
|
41
|
+
|
|
42
|
+
Party mode automatically uses the queue system. No manual intervention needed.
|
|
43
|
+
|
|
44
|
+
### Manual Queue Management
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Check queue status
|
|
48
|
+
bash .claude/hooks/tts-queue.sh status
|
|
49
|
+
|
|
50
|
+
# Clear all pending TTS (emergency stop)
|
|
51
|
+
bash .claude/hooks/tts-queue.sh clear
|
|
52
|
+
|
|
53
|
+
# Manually add to queue
|
|
54
|
+
bash .claude/hooks/tts-queue.sh add "Hello world" "en_US-ryan-high"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Performance Benefits
|
|
58
|
+
|
|
59
|
+
### Before (Sequential Blocking):
|
|
60
|
+
```
|
|
61
|
+
Agent 1 text output → [WAIT 3s for audio] → Agent 2 text → [WAIT 3s] → Agent 3 text
|
|
62
|
+
Total time: ~9+ seconds
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### After (Queue System):
|
|
66
|
+
```
|
|
67
|
+
Agent 1 text → Agent 2 text → Agent 3 text (all immediate)
|
|
68
|
+
↓
|
|
69
|
+
[Audio plays sequentially in background]
|
|
70
|
+
Total text output time: <1 second
|
|
71
|
+
Total audio time: ~9 seconds (plays while Claude continues working)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Technical Details
|
|
75
|
+
|
|
76
|
+
### Queue File Format
|
|
77
|
+
|
|
78
|
+
Each queue item is a timestamped file containing escaped bash variables:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
TEXT='First\ message\ from\ John'
|
|
82
|
+
VOICE='en_US-ryan-high'
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Worker Lifecycle
|
|
86
|
+
|
|
87
|
+
- **Start**: Automatically launched when first item added to empty queue
|
|
88
|
+
- **Processing**: Continues while queue has items
|
|
89
|
+
- **Idle timeout**: Exits after 5 seconds with no new items
|
|
90
|
+
- **Auto-restart**: Next queue addition spawns new worker
|
|
91
|
+
|
|
92
|
+
### Thread Safety
|
|
93
|
+
|
|
94
|
+
- Queue items use nanosecond timestamps for uniqueness
|
|
95
|
+
- Worker processes oldest items first (sorted by filename)
|
|
96
|
+
- Audio lock mechanism in `play-tts.sh` prevents actual audio overlap
|
|
97
|
+
- Worker PID tracked in `/tmp/agentvibes-tts-queue/worker.pid`
|
|
98
|
+
|
|
99
|
+
## Troubleshooting
|
|
100
|
+
|
|
101
|
+
### Queue stuck or voices not playing
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Check status
|
|
105
|
+
bash .claude/hooks/tts-queue.sh status
|
|
106
|
+
|
|
107
|
+
# Clear queue and restart
|
|
108
|
+
bash .claude/hooks/tts-queue.sh clear
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Worker not starting
|
|
112
|
+
|
|
113
|
+
Check for errors in the worker script:
|
|
114
|
+
```bash
|
|
115
|
+
bash .claude/hooks/tts-queue-worker.sh
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Audio still overlapping
|
|
119
|
+
|
|
120
|
+
Verify the lock file mechanism is working:
|
|
121
|
+
```bash
|
|
122
|
+
ls -la /tmp/agentvibes-audio.lock
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Files Modified
|
|
126
|
+
|
|
127
|
+
- `.claude/hooks/bmad-speak.sh` - Now uses queue system with `&` for non-blocking
|
|
128
|
+
- `.claude/hooks/tts-queue.sh` - New queue manager (added)
|
|
129
|
+
- `.claude/hooks/tts-queue-worker.sh` - New background worker (added)
|
|
130
|
+
|
|
131
|
+
## Compatibility
|
|
132
|
+
|
|
133
|
+
- Works with both Piper and ElevenLabs TTS providers
|
|
134
|
+
- Compatible with all existing BMAD voice mappings
|
|
135
|
+
- No changes needed to party mode workflow instructions
|
|
@@ -59,11 +59,26 @@ map_to_agent_id() {
|
|
|
59
59
|
fi
|
|
60
60
|
|
|
61
61
|
# Otherwise map display name to agent ID (for party mode)
|
|
62
|
-
# Extract 'name' (column 1) where displayName (column 2)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
# Extract 'name' (column 1) where displayName (column 2) contains the name
|
|
63
|
+
# displayName format in CSV: "John", "Mary", "Winston", etc. (first word before any parentheses)
|
|
64
|
+
local agent_id=$(awk -F',' -v name="$name_or_id" '
|
|
65
|
+
BEGIN { IGNORECASE=1 }
|
|
66
|
+
NR > 1 {
|
|
67
|
+
# Extract displayName (column 2)
|
|
68
|
+
display = $2
|
|
69
|
+
gsub(/^"|"$/, "", display) # Remove surrounding quotes
|
|
70
|
+
|
|
71
|
+
# Check if display name starts with the search name (case-insensitive)
|
|
72
|
+
# This handles both "John" and "John (Product Manager)"
|
|
73
|
+
if (tolower(display) ~ "^" tolower(name) "($| |\\()") {
|
|
74
|
+
# Extract agent ID (column 1)
|
|
75
|
+
agent = $1
|
|
76
|
+
gsub(/^"|"$/, "", agent)
|
|
77
|
+
print agent
|
|
78
|
+
exit
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
' "$PROJECT_ROOT/.bmad/_cfg/agent-manifest.csv")
|
|
67
82
|
|
|
68
83
|
echo "$agent_id"
|
|
69
84
|
}
|
|
@@ -71,16 +86,25 @@ map_to_agent_id() {
|
|
|
71
86
|
# Get agent ID
|
|
72
87
|
AGENT_ID=$(map_to_agent_id "$AGENT_NAME_OR_ID")
|
|
73
88
|
|
|
74
|
-
# Get agent's voice
|
|
89
|
+
# Get agent's voice and intro text
|
|
75
90
|
AGENT_VOICE=""
|
|
91
|
+
AGENT_INTRO=""
|
|
76
92
|
if [[ -n "$AGENT_ID" ]] && [[ -f "$SCRIPT_DIR/bmad-voice-manager.sh" ]]; then
|
|
77
93
|
AGENT_VOICE=$("$SCRIPT_DIR/bmad-voice-manager.sh" get-voice "$AGENT_ID" 2>/dev/null)
|
|
94
|
+
AGENT_INTRO=$("$SCRIPT_DIR/bmad-voice-manager.sh" get-intro "$AGENT_ID" 2>/dev/null)
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# Prepend intro text if configured (e.g., "John, Product Manager here. [dialogue]")
|
|
98
|
+
FULL_TEXT="$DIALOGUE"
|
|
99
|
+
if [[ -n "$AGENT_INTRO" ]]; then
|
|
100
|
+
FULL_TEXT="${AGENT_INTRO}. ${DIALOGUE}"
|
|
78
101
|
fi
|
|
79
102
|
|
|
80
|
-
# Speak with agent's voice
|
|
103
|
+
# Speak with agent's voice using queue system (non-blocking for Claude)
|
|
104
|
+
# Queue system ensures sequential playback while allowing Claude to continue
|
|
81
105
|
if [[ -n "$AGENT_VOICE" ]]; then
|
|
82
|
-
bash "$SCRIPT_DIR/
|
|
106
|
+
bash "$SCRIPT_DIR/tts-queue.sh" add "$FULL_TEXT" "$AGENT_VOICE" &
|
|
83
107
|
else
|
|
84
108
|
# Fallback to default voice
|
|
85
|
-
bash "$SCRIPT_DIR/
|
|
109
|
+
bash "$SCRIPT_DIR/tts-queue.sh" add "$FULL_TEXT" &
|
|
86
110
|
fi
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
# @fileoverview BMAD Voice Plugin Manager - Maps BMAD agents to unique TTS voices
|
|
35
35
|
# @context Enables each BMAD agent to have its own distinct voice for multi-agent sessions
|
|
36
36
|
# @architecture Markdown table-based voice mapping with enable/disable flag, auto-detection of BMAD
|
|
37
|
-
# @dependencies .claude/
|
|
37
|
+
# @dependencies .claude/config/bmad-voices.md (voice mappings), bmad-tts-injector.sh, .bmad-core/ (BMAD installation)
|
|
38
38
|
# @entrypoints Called by /agent-vibes:bmad commands, auto-enabled on BMAD detection
|
|
39
39
|
# @patterns Plugin architecture, auto-enable on dependency detection, state backup/restore on toggle
|
|
40
|
-
# @related bmad-tts-injector.sh, .claude/
|
|
40
|
+
# @related bmad-tts-injector.sh, .claude/config/bmad-voices.md, .bmad-agent-context file
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
ENABLED_FLAG="$
|
|
42
|
+
CONFIG_DIR=".claude/config"
|
|
43
|
+
VOICE_CONFIG_FILE="$CONFIG_DIR/bmad-voices.md"
|
|
44
|
+
ENABLED_FLAG="$CONFIG_DIR/bmad-voices-enabled.flag"
|
|
45
45
|
|
|
46
46
|
# AI NOTE: Auto-enable pattern - When BMAD is detected via install-manifest.yaml,
|
|
47
47
|
# automatically enable the voice plugin to provide seamless multi-agent voice support.
|
|
@@ -115,7 +115,7 @@ auto_enable_if_bmad_detected() {
|
|
|
115
115
|
# Check if BMAD is installed (any version) and plugin not already enabled
|
|
116
116
|
if [[ "$version" != "0" ]] && [[ ! -f "$ENABLED_FLAG" ]]; then
|
|
117
117
|
# BMAD detected but plugin not enabled - enable it silently
|
|
118
|
-
mkdir -p "$
|
|
118
|
+
mkdir -p "$CONFIG_DIR"
|
|
119
119
|
touch "$ENABLED_FLAG"
|
|
120
120
|
return 0
|
|
121
121
|
fi
|
|
@@ -144,7 +144,7 @@ get_agent_voice() {
|
|
|
144
144
|
return
|
|
145
145
|
fi
|
|
146
146
|
|
|
147
|
-
if [[ ! -f "$
|
|
147
|
+
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
148
148
|
echo "" # Plugin file missing
|
|
149
149
|
return
|
|
150
150
|
fi
|
|
@@ -163,19 +163,47 @@ get_agent_voice() {
|
|
|
163
163
|
fi
|
|
164
164
|
|
|
165
165
|
# Extract voice from markdown table based on provider
|
|
166
|
-
#
|
|
167
|
-
|
|
166
|
+
# Table: Agent ID | Agent Name | Intro | ElevenLabs Voice | Piper Voice | Personality
|
|
167
|
+
# AWK columns: $1=empty | $2=ID | $3=Name | $4=Intro | $5=ElevenLabs | $6=Piper | $7=Personality
|
|
168
|
+
local column=5 # Default to ElevenLabs (AWK column 5)
|
|
168
169
|
if [[ "$active_provider" == "piper" ]]; then
|
|
169
|
-
column=
|
|
170
|
+
column=6 # Use Piper (AWK column 6)
|
|
170
171
|
fi
|
|
171
172
|
|
|
172
|
-
local voice=$(grep "^| $agent_id " "$
|
|
173
|
+
local voice=$(grep "^| $agent_id " "$VOICE_CONFIG_FILE" | \
|
|
173
174
|
awk -F'|' "{print \$$column}" | \
|
|
174
175
|
sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
175
176
|
|
|
176
177
|
echo "$voice"
|
|
177
178
|
}
|
|
178
179
|
|
|
180
|
+
# @function get_agent_intro
|
|
181
|
+
# @intent Retrieve intro text for BMAD agent (spoken before their message)
|
|
182
|
+
# @why Helps users identify which agent is speaking in party mode
|
|
183
|
+
# @param $1 {string} agent_id - BMAD agent identifier
|
|
184
|
+
# @returns Echoes intro text to stdout, empty string if not configured
|
|
185
|
+
# @exitcode Always 0
|
|
186
|
+
# @sideeffects None
|
|
187
|
+
# @edgecases Returns empty string if plugin file missing, parses column 3 of markdown table
|
|
188
|
+
# @calledby bmad-speak.sh for agent identification in party mode
|
|
189
|
+
# @calls grep, awk, sed
|
|
190
|
+
# @version 2.1.0 - New function for customizable agent introductions
|
|
191
|
+
get_agent_intro() {
|
|
192
|
+
local agent_id="$1"
|
|
193
|
+
|
|
194
|
+
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
195
|
+
echo ""
|
|
196
|
+
return
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
# AWK column 4 = Intro text
|
|
200
|
+
local intro=$(grep "^| $agent_id " "$VOICE_CONFIG_FILE" | \
|
|
201
|
+
awk -F'|' '{print $4}' | \
|
|
202
|
+
sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
203
|
+
|
|
204
|
+
echo "$intro"
|
|
205
|
+
}
|
|
206
|
+
|
|
179
207
|
# @function get_agent_personality
|
|
180
208
|
# @intent Retrieve TTS personality assigned to specific BMAD agent
|
|
181
209
|
# @why Agents may have distinct speaking styles (friendly, professional, energetic, etc.)
|
|
@@ -190,14 +218,14 @@ get_agent_voice() {
|
|
|
190
218
|
get_agent_personality() {
|
|
191
219
|
local agent_id="$1"
|
|
192
220
|
|
|
193
|
-
if [[ ! -f "$
|
|
221
|
+
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
194
222
|
echo ""
|
|
195
223
|
return
|
|
196
224
|
fi
|
|
197
225
|
|
|
198
|
-
#
|
|
199
|
-
local personality=$(grep "^| $agent_id " "$
|
|
200
|
-
awk -F'|' '{print $
|
|
226
|
+
# AWK column 7 = Personality
|
|
227
|
+
local personality=$(grep "^| $agent_id " "$VOICE_CONFIG_FILE" | \
|
|
228
|
+
awk -F'|' '{print $7}' | \
|
|
201
229
|
sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
202
230
|
|
|
203
231
|
echo "$personality"
|
|
@@ -229,10 +257,10 @@ is_plugin_enabled() {
|
|
|
229
257
|
# @calledby Main command dispatcher with "enable" argument
|
|
230
258
|
# @calls mkdir, cat, source, list_mappings, bmad-tts-injector.sh
|
|
231
259
|
enable_plugin() {
|
|
232
|
-
mkdir -p "$
|
|
260
|
+
mkdir -p "$CONFIG_DIR"
|
|
233
261
|
|
|
234
262
|
# Save current settings before enabling
|
|
235
|
-
BACKUP_FILE="$
|
|
263
|
+
BACKUP_FILE="$CONFIG_DIR/.bmad-previous-settings"
|
|
236
264
|
|
|
237
265
|
# Save current voice
|
|
238
266
|
if [[ -f ".claude/tts-voice.txt" ]]; then
|
|
@@ -369,7 +397,7 @@ ACTIVATION_EOF
|
|
|
369
397
|
# @calledby Main command dispatcher with "disable" argument
|
|
370
398
|
# @calls source, rm, echo, bmad-tts-injector.sh
|
|
371
399
|
disable_plugin() {
|
|
372
|
-
BACKUP_FILE="$
|
|
400
|
+
BACKUP_FILE="$CONFIG_DIR/.bmad-previous-settings"
|
|
373
401
|
|
|
374
402
|
# Check if we have a backup to restore
|
|
375
403
|
if [[ -f "$BACKUP_FILE" ]]; then
|
|
@@ -435,15 +463,15 @@ disable_plugin() {
|
|
|
435
463
|
# @calledby enable_plugin, show_status, main command dispatcher with "list"
|
|
436
464
|
# @calls grep, sed, echo
|
|
437
465
|
list_mappings() {
|
|
438
|
-
if [[ ! -f "$
|
|
439
|
-
echo "❌ Plugin file not found: $
|
|
466
|
+
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
467
|
+
echo "❌ Plugin file not found: $VOICE_CONFIG_FILE"
|
|
440
468
|
return 1
|
|
441
469
|
fi
|
|
442
470
|
|
|
443
471
|
echo "📊 BMAD Agent Voice Mappings:"
|
|
444
472
|
echo ""
|
|
445
473
|
|
|
446
|
-
grep "^| " "$
|
|
474
|
+
grep "^| " "$VOICE_CONFIG_FILE" | grep -v "Agent ID" | grep -v "^|---" | \
|
|
447
475
|
while IFS='|' read -r _ agent_id name voice personality _; do
|
|
448
476
|
agent_id=$(echo "$agent_id" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
449
477
|
name=$(echo "$name" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
@@ -471,19 +499,19 @@ set_agent_voice() {
|
|
|
471
499
|
local voice="$2"
|
|
472
500
|
local personality="${3:-normal}"
|
|
473
501
|
|
|
474
|
-
if [[ ! -f "$
|
|
475
|
-
echo "❌ Plugin file not found: $
|
|
502
|
+
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
503
|
+
echo "❌ Plugin file not found: $VOICE_CONFIG_FILE"
|
|
476
504
|
return 1
|
|
477
505
|
fi
|
|
478
506
|
|
|
479
507
|
# Check if agent exists
|
|
480
|
-
if ! grep -q "^| $agent_id " "$
|
|
508
|
+
if ! grep -q "^| $agent_id " "$VOICE_CONFIG_FILE"; then
|
|
481
509
|
echo "❌ Agent '$agent_id' not found in plugin"
|
|
482
510
|
return 1
|
|
483
511
|
fi
|
|
484
512
|
|
|
485
513
|
# Update the voice and personality in the table
|
|
486
|
-
sed -i.bak "s/^| $agent_id |.*| .* | .* |$/| $agent_id | $(grep "^| $agent_id " "$
|
|
514
|
+
sed -i.bak "s/^| $agent_id |.*| .* | .* |$/| $agent_id | $(grep "^| $agent_id " "$VOICE_CONFIG_FILE" | awk -F'|' '{print $3}') | $voice | $personality |/" "$VOICE_CONFIG_FILE"
|
|
487
515
|
|
|
488
516
|
echo "✅ Updated $agent_id → $voice [$personality]"
|
|
489
517
|
}
|
|
@@ -531,12 +559,12 @@ show_status() {
|
|
|
531
559
|
# @calledby Main command dispatcher with "edit" argument
|
|
532
560
|
# @calls echo
|
|
533
561
|
edit_plugin() {
|
|
534
|
-
if [[ ! -f "$
|
|
535
|
-
echo "❌ Plugin file not found: $
|
|
562
|
+
if [[ ! -f "$VOICE_CONFIG_FILE" ]]; then
|
|
563
|
+
echo "❌ Plugin file not found: $VOICE_CONFIG_FILE"
|
|
536
564
|
return 1
|
|
537
565
|
fi
|
|
538
566
|
|
|
539
|
-
echo "Opening $
|
|
567
|
+
echo "Opening $VOICE_CONFIG_FILE for editing..."
|
|
540
568
|
echo "Edit the markdown table to change voice mappings"
|
|
541
569
|
}
|
|
542
570
|
|
|
@@ -564,6 +592,9 @@ case "${1:-help}" in
|
|
|
564
592
|
get-voice)
|
|
565
593
|
get_agent_voice "$2"
|
|
566
594
|
;;
|
|
595
|
+
get-intro)
|
|
596
|
+
get_agent_intro "$2"
|
|
597
|
+
;;
|
|
567
598
|
get-personality)
|
|
568
599
|
get_agent_personality "$2"
|
|
569
600
|
;;
|
|
@@ -571,7 +602,7 @@ case "${1:-help}" in
|
|
|
571
602
|
edit_plugin
|
|
572
603
|
;;
|
|
573
604
|
*)
|
|
574
|
-
echo "Usage: bmad-voice-manager.sh {enable|disable|status|list|set|get-voice|get-personality|edit}"
|
|
605
|
+
echo "Usage: bmad-voice-manager.sh {enable|disable|status|list|set|get-voice|get-intro|get-personality|edit}"
|
|
575
606
|
echo ""
|
|
576
607
|
echo "Commands:"
|
|
577
608
|
echo " enable Enable BMAD voice plugin"
|
|
@@ -580,6 +611,7 @@ case "${1:-help}" in
|
|
|
580
611
|
echo " list List all agent voice mappings"
|
|
581
612
|
echo " set <id> <voice> Set voice for agent"
|
|
582
613
|
echo " get-voice <id> Get voice for agent"
|
|
614
|
+
echo " get-intro <id> Get intro text for agent"
|
|
583
615
|
echo " get-personality <id> Get personality for agent"
|
|
584
616
|
echo " edit Edit plugin configuration"
|
|
585
617
|
exit 1
|
|
@@ -46,6 +46,11 @@ export LC_ALL=C
|
|
|
46
46
|
TEXT="$1"
|
|
47
47
|
VOICE_OVERRIDE="$2" # Optional: voice name or ID
|
|
48
48
|
|
|
49
|
+
# Remove backslash escaping that Claude might add for special chars like ! and $
|
|
50
|
+
# In single quotes these don't need escaping, but Claude sometimes adds \! anyway
|
|
51
|
+
TEXT="${TEXT//\\!/!}"
|
|
52
|
+
TEXT="${TEXT//\\\$/\$}"
|
|
53
|
+
|
|
49
54
|
# Get script directory
|
|
50
55
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
51
56
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# File: .claude/hooks/tts-queue-worker.sh
|
|
4
|
+
#
|
|
5
|
+
# TTS Queue Worker - Background process that plays queued TTS sequentially
|
|
6
|
+
# Automatically exits when queue is empty for 5 seconds
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
QUEUE_DIR="/tmp/agentvibes-tts-queue"
|
|
11
|
+
WORKER_PID_FILE="$QUEUE_DIR/worker.pid"
|
|
12
|
+
IDLE_TIMEOUT=5 # Exit after 5 seconds of no new requests
|
|
13
|
+
|
|
14
|
+
# Get script directory
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
|
|
17
|
+
# Trap to clean up on exit
|
|
18
|
+
trap "rm -f $WORKER_PID_FILE" EXIT
|
|
19
|
+
|
|
20
|
+
# Process queue items
|
|
21
|
+
process_queue() {
|
|
22
|
+
local idle_count=0
|
|
23
|
+
|
|
24
|
+
while true; do
|
|
25
|
+
# Find oldest queue item
|
|
26
|
+
local queue_item=$(ls -1 "$QUEUE_DIR"/*.queue 2>/dev/null | sort | head -1)
|
|
27
|
+
|
|
28
|
+
if [[ -z "$queue_item" ]]; then
|
|
29
|
+
# Queue is empty, increment idle counter
|
|
30
|
+
idle_count=$((idle_count + 1))
|
|
31
|
+
|
|
32
|
+
if [[ $idle_count -ge $IDLE_TIMEOUT ]]; then
|
|
33
|
+
# No new items for timeout period, exit worker
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# Wait 1 second and check again
|
|
38
|
+
sleep 1
|
|
39
|
+
continue
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# Reset idle counter - we have work
|
|
43
|
+
idle_count=0
|
|
44
|
+
|
|
45
|
+
# Load TTS request
|
|
46
|
+
source "$queue_item"
|
|
47
|
+
|
|
48
|
+
# Decode base64 values
|
|
49
|
+
TEXT=$(echo -n "$TEXT_B64" | base64 -d)
|
|
50
|
+
VOICE=$(echo -n "$VOICE_B64" | base64 -d)
|
|
51
|
+
|
|
52
|
+
# Play TTS (this blocks until audio finishes due to lock mechanism)
|
|
53
|
+
if [[ -n "${VOICE:-}" ]]; then
|
|
54
|
+
bash "$SCRIPT_DIR/play-tts.sh" "$TEXT" "$VOICE" 2>/dev/null || true
|
|
55
|
+
else
|
|
56
|
+
bash "$SCRIPT_DIR/play-tts.sh" "$TEXT" 2>/dev/null || true
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# Add 2-second pause between speakers for natural conversation flow
|
|
60
|
+
sleep 2
|
|
61
|
+
|
|
62
|
+
# Remove processed item
|
|
63
|
+
rm -f "$queue_item"
|
|
64
|
+
done
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# Start processing
|
|
68
|
+
process_queue
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# File: .claude/hooks/tts-queue.sh
|
|
4
|
+
#
|
|
5
|
+
# TTS Queue Manager for Party Mode
|
|
6
|
+
# Queues TTS requests and plays them sequentially in the background
|
|
7
|
+
# This allows Claude to continue generating responses while audio plays in order
|
|
8
|
+
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
QUEUE_DIR="/tmp/agentvibes-tts-queue"
|
|
12
|
+
QUEUE_LOCK="$QUEUE_DIR/queue.lock"
|
|
13
|
+
WORKER_PID_FILE="$QUEUE_DIR/worker.pid"
|
|
14
|
+
|
|
15
|
+
# Get script directory
|
|
16
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
17
|
+
|
|
18
|
+
# Initialize queue directory
|
|
19
|
+
mkdir -p "$QUEUE_DIR"
|
|
20
|
+
|
|
21
|
+
# @function add_to_queue
|
|
22
|
+
# @intent Add a TTS request to the queue for sequential playback
|
|
23
|
+
# @param $1 dialogue text
|
|
24
|
+
# @param $2 voice name (optional)
|
|
25
|
+
add_to_queue() {
|
|
26
|
+
local text="$1"
|
|
27
|
+
local voice="${2:-}"
|
|
28
|
+
|
|
29
|
+
# Create unique queue item with timestamp
|
|
30
|
+
local timestamp=$(date +%s%N)
|
|
31
|
+
local queue_file="$QUEUE_DIR/$timestamp.queue"
|
|
32
|
+
|
|
33
|
+
# Write request to queue file (base64 encoded to handle all special chars)
|
|
34
|
+
cat > "$queue_file" <<EOF
|
|
35
|
+
TEXT_B64=$(echo -n "$text" | base64 -w0)
|
|
36
|
+
VOICE_B64=$(echo -n "$voice" | base64 -w0)
|
|
37
|
+
EOF
|
|
38
|
+
|
|
39
|
+
# Start queue worker if not already running
|
|
40
|
+
start_worker_if_needed
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# @function start_worker_if_needed
|
|
44
|
+
# @intent Start the queue worker process if it's not already running
|
|
45
|
+
start_worker_if_needed() {
|
|
46
|
+
# Check if worker is already running
|
|
47
|
+
if [[ -f "$WORKER_PID_FILE" ]]; then
|
|
48
|
+
local pid=$(cat "$WORKER_PID_FILE")
|
|
49
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
50
|
+
# Worker is running
|
|
51
|
+
return 0
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# Start worker in background
|
|
56
|
+
"$SCRIPT_DIR/tts-queue-worker.sh" &
|
|
57
|
+
echo $! > "$WORKER_PID_FILE"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# @function clear_queue
|
|
61
|
+
# @intent Clear all pending TTS requests (emergency stop)
|
|
62
|
+
clear_queue() {
|
|
63
|
+
rm -f "$QUEUE_DIR"/*.queue
|
|
64
|
+
echo "✅ Queue cleared"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# @function show_queue
|
|
68
|
+
# @intent Display current queue status
|
|
69
|
+
show_queue() {
|
|
70
|
+
local count=$(ls -1 "$QUEUE_DIR"/*.queue 2>/dev/null | wc -l)
|
|
71
|
+
echo "📊 Queue status: $count items pending"
|
|
72
|
+
|
|
73
|
+
if [[ -f "$WORKER_PID_FILE" ]]; then
|
|
74
|
+
local pid=$(cat "$WORKER_PID_FILE")
|
|
75
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
76
|
+
echo "✅ Worker process running (PID: $pid)"
|
|
77
|
+
else
|
|
78
|
+
echo "❌ Worker process not running"
|
|
79
|
+
fi
|
|
80
|
+
else
|
|
81
|
+
echo "❌ Worker process not running"
|
|
82
|
+
fi
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Main command dispatcher
|
|
86
|
+
case "${1:-help}" in
|
|
87
|
+
add)
|
|
88
|
+
add_to_queue "${2:-}" "${3:-}"
|
|
89
|
+
;;
|
|
90
|
+
clear)
|
|
91
|
+
clear_queue
|
|
92
|
+
;;
|
|
93
|
+
status)
|
|
94
|
+
show_queue
|
|
95
|
+
;;
|
|
96
|
+
*)
|
|
97
|
+
echo "Usage: tts-queue.sh {add|clear|status}"
|
|
98
|
+
echo ""
|
|
99
|
+
echo "Commands:"
|
|
100
|
+
echo " add <text> [voice] Add TTS request to queue"
|
|
101
|
+
echo " clear Clear all pending requests"
|
|
102
|
+
echo " status Show queue status"
|
|
103
|
+
exit 1
|
|
104
|
+
;;
|
|
105
|
+
esac
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
high
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
[](https://github.com/paulpreibisch/AgentVibes/actions/workflows/publish.yml)
|
|
12
12
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
13
13
|
|
|
14
|
-
**Author**: Paul Preibisch ([@997Fire](https://x.com/997Fire)) | **Version**: v2.
|
|
14
|
+
**Author**: Paul Preibisch ([@997Fire](https://x.com/997Fire)) | **Version**: v2.7.0
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
@@ -94,16 +94,16 @@ Whether you're coding in Claude Code, chatting in Claude Desktop, or using Warp
|
|
|
94
94
|
|
|
95
95
|
## 📰 Latest Release
|
|
96
96
|
|
|
97
|
-
**[v2.
|
|
97
|
+
**[v2.7.0 - Party Mode Voice Improvements](https://github.com/paulpreibisch/AgentVibes/releases/tag/v2.7.0)** 🎉
|
|
98
98
|
|
|
99
|
-
AgentVibes v2.
|
|
99
|
+
AgentVibes v2.7.0 transforms BMAD party mode into a professional multi-agent voice conversation system! This release introduces a sophisticated TTS queue architecture enabling sequential voice playback without blocking Claude Code, natural 2-second pauses between speakers, and customizable agent introductions.
|
|
100
100
|
|
|
101
101
|
**Key highlights:**
|
|
102
|
-
-
|
|
103
|
-
-
|
|
104
|
-
-
|
|
105
|
-
-
|
|
106
|
-
-
|
|
102
|
+
- 🎭 **TTS Queue System** - Sequential non-blocking voice playback for party mode
|
|
103
|
+
- ⏸️ **Natural Speaker Pauses** - 2-second delay between agents for conversation flow
|
|
104
|
+
- 🎤 **Speaker Introductions** - Configurable intro text (e.g., "John, Product Manager here")
|
|
105
|
+
- 📁 **Config Reorganization** - Moved from `.claude/plugins/` to official `.claude/config/`
|
|
106
|
+
- 🐛 **Text Escaping Fixes** - No more "backslash exclamation" in speech
|
|
107
107
|
|
|
108
108
|
[→ View All Releases](https://github.com/paulpreibisch/AgentVibes/releases)
|
|
109
109
|
|