agentvibes 5.6.9 → 5.7.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 +3 -38
- package/.claude/commands/agent-vibes/provider.md +0 -0
- package/.claude/config/audio-effects.cfg +1 -1
- package/.claude/config/background-music-position.txt +6 -8
- package/.claude/config/reverb-level.txt +0 -0
- package/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks/bmad-tts-injector.sh +49 -21
- package/.claude/hooks/migrate-to-agentvibes.sh +24 -16
- package/.claude/hooks/personality-manager.sh +15 -2
- package/.claude/hooks/play-tts.sh +6 -0
- package/.claude/hooks/provider-commands.sh +16 -4
- package/.claude/hooks/provider-manager.sh +38 -0
- package/.claude/hooks/stop.sh +2 -27
- package/.claude/hooks/voice-manager.sh +50 -2
- package/.claude/hooks-windows/play-tts.ps1 +34 -1
- package/.claude/hooks-windows/tts-watcher.ps1 +122 -0
- package/.claude/piper-voices-dir.txt +1 -1
- package/.mcp.json +13 -33
- package/README.md +6 -8
- package/RELEASE_NOTES.md +32 -0
- package/bin/agent-vibes +39 -39
- package/package.json +1 -1
- package/src/bmad-detector.js +85 -71
- package/src/cli/list-personalities.js +110 -110
- package/src/cli/list-voices.js +114 -114
- package/src/commands/bmad-voices.js +394 -394
- package/src/commands/install-mcp.js +476 -476
- package/src/console/brand-colors.js +13 -13
- package/src/console/constants/personalities.js +44 -44
- package/src/console/tabs/help-tab.js +314 -314
- package/src/console/tabs/readme-tab.js +272 -272
- package/src/console/widgets/destroy-list.js +25 -25
- package/src/console/widgets/notice.js +55 -55
- package/src/console/widgets/personality-picker.js +213 -213
- package/src/i18n/de.js +202 -202
- package/src/i18n/es.js +202 -202
- package/src/i18n/fr.js +202 -202
- package/src/i18n/hi.js +202 -202
- package/src/i18n/ja.js +202 -202
- package/src/i18n/ko.js +202 -202
- package/src/i18n/pt.js +202 -202
- package/src/i18n/strings.js +54 -54
- package/src/i18n/zh-CN.js +202 -202
- package/src/installer/language-screen.js +31 -31
- package/src/installer/music-file-input.js +304 -304
- package/src/installer.js +330 -64
- package/src/services/agent-voice-store.js +59 -12
- package/src/services/config-service.js +264 -264
- package/src/services/language-service.js +47 -47
- package/src/services/llm-provider-service.js +57 -12
- package/src/services/provider-service.js +143 -143
- package/src/utils/audio-duration-validator.js +298 -298
- package/src/utils/audio-format-validator.js +277 -277
- package/src/utils/dependency-checker.js +469 -469
- package/src/utils/file-ownership-verifier.js +358 -358
- package/src/utils/list-formatter.js +194 -194
- package/src/utils/music-file-validator.js +285 -285
- package/src/utils/preview-list-prompt.js +136 -136
- package/src/utils/secure-music-storage.js +412 -412
- package/.agentvibes/LITE-MODE.md +0 -236
- package/.agentvibes/README.md +0 -136
- package/.agentvibes/backup/session-start-tts.sh.20251210_212814 +0 -141
- package/.agentvibes/backups/agents/analyst_20260204_144958.md +0 -78
- package/.agentvibes/backups/agents/architect_20260204_144958.md +0 -72
- package/.agentvibes/backups/agents/dev_20260204_144958.md +0 -74
- package/.agentvibes/backups/agents/pm_20260204_144958.md +0 -72
- package/.agentvibes/backups/agents/quick-flow-solo-dev_20260204_144958.md +0 -64
- package/.agentvibes/backups/agents/sm_20260204_144958.md +0 -87
- package/.agentvibes/backups/agents/tea_20260204_144958.md +0 -79
- package/.agentvibes/backups/agents/tech-writer_20260204_144958.md +0 -82
- package/.agentvibes/backups/agents/ux-designer_20260204_144958.md +0 -80
- package/.agentvibes/config/README-personality-defaults.md +0 -162
- package/.agentvibes/config/agentvibes.json +0 -1
- package/.agentvibes/config/mode.txt +0 -1
- package/.agentvibes/config/personality-voice-defaults.default.json +0 -21
- package/.agentvibes/config/save-audio.txt +0 -1
- package/.agentvibes/config/voice-metadata.json +0 -160
- package/.agentvibes/hooks/help.sh +0 -191
- package/.agentvibes/hooks/post-tool-use-lite.sh +0 -111
- package/.agentvibes/hooks/save-audio-manager.sh +0 -162
- package/.agentvibes/hooks/session-start-full-optimized.sh +0 -102
- package/.agentvibes/hooks/session-start-full.sh +0 -142
- package/.agentvibes/hooks/session-start-lite-v2.sh +0 -34
- package/.agentvibes/hooks/session-start-lite.sh +0 -29
- package/.agentvibes/hooks/stop-lite.sh +0 -115
- package/.agentvibes/hooks/switch-mode.sh +0 -215
- package/.agentvibes/output-styles/audio-summary.md +0 -30
- package/.claude/audio/voice-samples/piper/alan.wav +0 -0
- package/.claude/audio/voice-samples/piper/amy.wav +0 -0
- package/.claude/audio/voice-samples/piper/charlotte.wav +0 -0
- package/.claude/audio/voice-samples/piper/joe.wav +0 -0
- package/.claude/audio/voice-samples/piper/john.wav +0 -0
- package/.claude/audio/voice-samples/piper/katherine.wav +0 -0
- package/.claude/audio/voice-samples/piper/kristin.wav +0 -0
- package/.claude/audio/voice-samples/piper/linda.wav +0 -0
- package/.claude/audio/voice-samples/piper/marcus.wav +0 -0
- package/.claude/audio/voice-samples/piper/ryan.wav +0 -0
- package/.claude/hooks/post-response.sh +0 -41
- package/bin/ensure-soprano-running.sh +0 -43
package/.agentvibes/config.json
CHANGED
|
@@ -1,40 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"backgroundMusic": {
|
|
6
|
-
"enabled": false,
|
|
7
|
-
"track": "agent_vibes_japanese_city_pop_v1_loop.mp3",
|
|
8
|
-
"volume": 20
|
|
9
|
-
},
|
|
10
|
-
"music": {
|
|
11
|
-
"enabled": true,
|
|
12
|
-
"track": "agentvibes_nature_sounds_rain.mp3"
|
|
13
|
-
},
|
|
14
|
-
"musicFavorites": [
|
|
15
|
-
"agentvibes_blues_smooth_guitar.mp3",
|
|
16
|
-
"agentvibes_lofi_beats_coding.mp3",
|
|
17
|
-
"agentvibes_nature_sounds_rain.mp3",
|
|
18
|
-
"agent_vibes_ganawa_ambient_v2_loop.mp3"
|
|
19
|
-
],
|
|
20
|
-
"effects": {
|
|
21
|
-
"reverb": true,
|
|
22
|
-
"reverbAmount": 0.6,
|
|
23
|
-
"pitch": 2,
|
|
24
|
-
"reverbPreset": "medium"
|
|
25
|
-
},
|
|
26
|
-
"verbosity": "high",
|
|
27
|
-
"personality": "grandpa",
|
|
28
|
-
"voice": "16Speakers::Cori_Samuel",
|
|
29
|
-
"pretext": "Agent Vibes Here",
|
|
30
|
-
"setupCompleted": true,
|
|
31
|
-
"ttsEngine": "piper",
|
|
32
|
-
"thumbsUp": [
|
|
33
|
-
"en_US-libritts-high::Adam",
|
|
34
|
-
"en_US-libritts_r-medium::Adam-2"
|
|
35
|
-
],
|
|
36
|
-
"favorites": [
|
|
37
|
-
"en_US-libritts-high::Adam",
|
|
38
|
-
"en_US-libritts_r-medium::Adam-2"
|
|
39
|
-
]
|
|
2
|
+
"audio_destination": "remote",
|
|
3
|
+
"audio_ssh_alias": "laptop-win",
|
|
4
|
+
"setupCompleted": true
|
|
40
5
|
}
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
llm:default|light||0.15|en_US-lessac-high||piper
|
|
3
|
-
llm:claude-code|light|
|
|
3
|
+
llm:claude-code|light|agent_vibes_cumbia_v1_loop.mp3|0.05|en_US-libritts-high::Mike-3|Ubuntu RDP Agentvibes|piper
|
|
4
4
|
llm:copilot|light|agent_vibes_bossa_nova_v2_loop.mp3|0.15|en_US-libritts-high::Anna-11|Copilot here|piper
|
|
5
5
|
llm:codex|light|agent_vibes_chillwave_v2_loop.mp3|0.15|en_US-lessac-high|Codex here|piper
|
|
6
6
|
llm:hermes|light|agent_vibes_bachata_v1_loop.mp3|0.15|en_US-libritts-high::Leo-8|Hermes here|piper
|
|
@@ -16,14 +16,12 @@ Agent Vibes Ganawa Ambient v2-loop.mp3:.00000000000000000002815996
|
|
|
16
16
|
Agent Vibes Tabla Dream Pop v1-loop.mp3:.00000000000000000009067943
|
|
17
17
|
Agent Vibes ChillWave v2-loop.mp3:.00000000000000000007080511
|
|
18
18
|
Agent Vibes Harpsichord v2-loop.mp3:.00000000000000000013140818
|
|
19
|
-
agent_vibes_japanese_city_pop_v1_loop.mp3:6.054512
|
|
20
19
|
agent_vibes_bossa_nova_v2_loop.mp3:5.369524
|
|
21
20
|
agent_vibes_salsa_v2_loop.mp3:9.972790
|
|
22
21
|
agent_vibes_arabic_v2_loop.mp3:.00000000000000000006132724
|
|
23
|
-
|
|
24
|
-
agent_vibes_bachata_v1_loop.mp3
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
agent_vibes_cumbia_v1_loop.mp3:10.472018
|
|
22
|
+
agent_vibes_cumbia_v1_loop.mp3:.00000000000000000010256000
|
|
23
|
+
agent_vibes_bachata_v1_loop.mp3:5.125714
|
|
24
|
+
agent_vibes_chillwave_v2_loop.mp3:.00000000000000000003308191
|
|
25
|
+
Midnight Charleston Stomp.mp3:.00000000000000000010960000
|
|
26
|
+
agent_vibes_japanese_city_pop_v1_loop.mp3:11.702675
|
|
27
|
+
agentvibes_soft_flamenco_loop.mp3:9.589660
|
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
20260511
|
|
@@ -49,7 +49,7 @@ GRAY='\033[0;90m'
|
|
|
49
49
|
NC='\033[0m' # No Color
|
|
50
50
|
|
|
51
51
|
# Detect BMAD installation and version
|
|
52
|
-
# Supports both v4 (.bmad-core/) and v6-alpha (.bmad/) installations
|
|
52
|
+
# Supports both v4 (.bmad-core/) and v6-alpha (.bmad/, _bmad/) installations
|
|
53
53
|
detect_bmad() {
|
|
54
54
|
local bmad_core_dir=""
|
|
55
55
|
|
|
@@ -58,6 +58,11 @@ detect_bmad() {
|
|
|
58
58
|
bmad_core_dir=".bmad"
|
|
59
59
|
elif [[ -d "../.bmad" ]]; then
|
|
60
60
|
bmad_core_dir="../.bmad"
|
|
61
|
+
# Check for _bmad (current BMAD installer default — underscore prefix, project-local)
|
|
62
|
+
elif [[ -d "_bmad" ]]; then
|
|
63
|
+
bmad_core_dir="_bmad"
|
|
64
|
+
elif [[ -d "../_bmad" ]]; then
|
|
65
|
+
bmad_core_dir="../_bmad"
|
|
61
66
|
# Check for v6-alpha without dot (legacy naming)
|
|
62
67
|
elif [[ -d "bmad" ]]; then
|
|
63
68
|
bmad_core_dir="bmad"
|
|
@@ -73,6 +78,11 @@ detect_bmad() {
|
|
|
73
78
|
bmad_core_dir="bmad-core"
|
|
74
79
|
elif [[ -d "../bmad-core" ]]; then
|
|
75
80
|
bmad_core_dir="../bmad-core"
|
|
81
|
+
# Check home-dir global BMAD install (~/_bmad is the default for standalone BMAD installs)
|
|
82
|
+
elif [[ -d "$HOME/_bmad" ]]; then
|
|
83
|
+
bmad_core_dir="$HOME/_bmad"
|
|
84
|
+
elif [[ -d "$HOME/.bmad" ]]; then
|
|
85
|
+
bmad_core_dir="$HOME/.bmad"
|
|
76
86
|
else
|
|
77
87
|
echo -e "${RED}❌ BMAD installation not found${NC}" >&2
|
|
78
88
|
echo -e "${GRAY} Looked for bmad/, .bmad-core/, or bmad-core/ directory${NC}" >&2
|
|
@@ -85,21 +95,34 @@ detect_bmad() {
|
|
|
85
95
|
# Find all BMAD agents
|
|
86
96
|
find_agents() {
|
|
87
97
|
local bmad_core="$1"
|
|
88
|
-
local
|
|
98
|
+
local found=0
|
|
99
|
+
|
|
100
|
+
# v6.6+: agents under .claude/skills/*/agents/ (new BMAD structure)
|
|
101
|
+
local skills_dir=".claude/skills"
|
|
102
|
+
if [[ -d "$skills_dir" ]]; then
|
|
103
|
+
while IFS= read -r -d '' agent_file; do
|
|
104
|
+
echo "$agent_file"
|
|
105
|
+
found=1
|
|
106
|
+
done < <(find "$skills_dir" -path "*/agents/*.md" -type f -print0 2>/dev/null)
|
|
107
|
+
fi
|
|
89
108
|
|
|
90
|
-
#
|
|
109
|
+
# v6.x: bmad/bmm/agents/
|
|
91
110
|
if [[ -d "$bmad_core/bmm/agents" ]]; then
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
111
|
+
find "$bmad_core/bmm/agents" -name "*.md" -type f
|
|
112
|
+
found=1
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
# v4: .bmad-core/agents/
|
|
116
|
+
if [[ -d "$bmad_core/agents" ]]; then
|
|
117
|
+
find "$bmad_core/agents" -name "*.md" -type f
|
|
118
|
+
found=1
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
if [[ $found -eq 0 ]]; then
|
|
97
122
|
echo -e "${RED}❌ Agents directory not found in $bmad_core${NC}" >&2
|
|
98
|
-
echo -e "${GRAY} Tried: $bmad_core/bmm/agents
|
|
123
|
+
echo -e "${GRAY} Tried: $bmad_core/bmm/agents/, $bmad_core/agents/, .claude/skills/*/agents/${NC}" >&2
|
|
99
124
|
return 1
|
|
100
125
|
fi
|
|
101
|
-
|
|
102
|
-
find "$agents_dir" -name "*.md" -type f
|
|
103
126
|
}
|
|
104
127
|
|
|
105
128
|
# Check if agent has TTS injection
|
|
@@ -217,6 +240,15 @@ inject_tts() {
|
|
|
217
240
|
return 0
|
|
218
241
|
fi
|
|
219
242
|
|
|
243
|
+
# Detect format BEFORE creating backups — skip v6.6+ plain Markdown agents (no activation section)
|
|
244
|
+
local is_v6=false
|
|
245
|
+
if grep -q "<activation" "$agent_file"; then
|
|
246
|
+
is_v6=true
|
|
247
|
+
elif ! grep -q "activation-instructions:" "$agent_file"; then
|
|
248
|
+
echo -e "${GRAY} ℹ️ Skipped (v6.6+ format): $(basename "$agent_file")${NC}"
|
|
249
|
+
return 2
|
|
250
|
+
fi
|
|
251
|
+
|
|
220
252
|
# Create backup directory for centralized timestamped backups
|
|
221
253
|
local backup_dir=".agentvibes/backups/agents"
|
|
222
254
|
mkdir -p "$backup_dir"
|
|
@@ -231,15 +263,6 @@ inject_tts() {
|
|
|
231
263
|
|
|
232
264
|
echo -e "${GRAY} 📦 Backup saved: $backup_dir/$backup_name${NC}"
|
|
233
265
|
|
|
234
|
-
# Detect v4 vs v6 structure
|
|
235
|
-
local is_v6=false
|
|
236
|
-
if grep -q "<activation" "$agent_file"; then
|
|
237
|
-
is_v6=true
|
|
238
|
-
elif ! grep -q "activation-instructions:" "$agent_file"; then
|
|
239
|
-
echo -e "${RED}❌ No activation section found in: $(basename "$agent_file")${NC}"
|
|
240
|
-
return 1
|
|
241
|
-
fi
|
|
242
|
-
|
|
243
266
|
# Create TTS injection script based on version
|
|
244
267
|
if [[ "$is_v6" == "true" ]]; then
|
|
245
268
|
# v6 format: XML-style with <step n="4.5">
|
|
@@ -411,6 +434,7 @@ enable_all() {
|
|
|
411
434
|
local agents=$(find_agents "$bmad_core")
|
|
412
435
|
local success_count=0
|
|
413
436
|
local skip_count=0
|
|
437
|
+
local format_skip_count=0
|
|
414
438
|
local fail_count=0
|
|
415
439
|
|
|
416
440
|
# Track modified files and backups for summary
|
|
@@ -423,7 +447,10 @@ enable_all() {
|
|
|
423
447
|
continue
|
|
424
448
|
fi
|
|
425
449
|
|
|
426
|
-
|
|
450
|
+
inject_tts "$agent_file" && local inject_result=0 || local inject_result=$?
|
|
451
|
+
if [[ $inject_result -eq 2 ]]; then
|
|
452
|
+
format_skip_count=$((format_skip_count + 1))
|
|
453
|
+
elif [[ $inject_result -eq 0 ]]; then
|
|
427
454
|
success_count=$((success_count + 1))
|
|
428
455
|
modified_files+=("$agent_file")
|
|
429
456
|
# Track the backup file that was created
|
|
@@ -444,6 +471,7 @@ enable_all() {
|
|
|
444
471
|
# Show results
|
|
445
472
|
echo -e "${GREEN}✅ Successfully modified: $success_count agents${NC}"
|
|
446
473
|
[[ $skip_count -gt 0 ]] && echo -e "${YELLOW}⏭️ Skipped (already enabled): $skip_count agents${NC}"
|
|
474
|
+
[[ $format_skip_count -gt 0 ]] && echo -e "${GRAY} ℹ️ Skipped (v6.6+ format, not supported): $format_skip_count agents${NC}"
|
|
447
475
|
[[ $fail_count -gt 0 ]] && echo -e "${RED}❌ Failed: $fail_count agents${NC}"
|
|
448
476
|
echo ""
|
|
449
477
|
|
|
@@ -46,22 +46,25 @@ echo -e "${BLUE}🔍 Checking for BMAD files in .claude/plugins/...${NC}"
|
|
|
46
46
|
|
|
47
47
|
if [[ -f ".claude/plugins/bmad-voices-enabled.flag" ]]; then
|
|
48
48
|
echo -e "${YELLOW} Found: bmad-voices-enabled.flag${NC}"
|
|
49
|
-
mv .claude/plugins/bmad-voices-enabled.flag .agentvibes/bmad/
|
|
50
|
-
|
|
49
|
+
mv -n .claude/plugins/bmad-voices-enabled.flag .agentvibes/bmad/ && \
|
|
50
|
+
echo -e "${GREEN} ✓ Moved to .agentvibes/bmad/${NC}" || \
|
|
51
|
+
echo -e "${YELLOW} ⚠ Already exists in .agentvibes/bmad/ — skipped${NC}"
|
|
51
52
|
MIGRATED=true
|
|
52
53
|
fi
|
|
53
54
|
|
|
54
55
|
if [[ -f ".claude/plugins/bmad-party-mode-disabled.flag" ]]; then
|
|
55
56
|
echo -e "${YELLOW} Found: bmad-party-mode-disabled.flag${NC}"
|
|
56
|
-
mv .claude/plugins/bmad-party-mode-disabled.flag .agentvibes/bmad/
|
|
57
|
-
|
|
57
|
+
mv -n .claude/plugins/bmad-party-mode-disabled.flag .agentvibes/bmad/ && \
|
|
58
|
+
echo -e "${GREEN} ✓ Moved to .agentvibes/bmad/${NC}" || \
|
|
59
|
+
echo -e "${YELLOW} ⚠ Already exists in .agentvibes/bmad/ — skipped${NC}"
|
|
58
60
|
MIGRATED=true
|
|
59
61
|
fi
|
|
60
62
|
|
|
61
63
|
if [[ -f ".claude/plugins/.bmad-previous-settings" ]]; then
|
|
62
64
|
echo -e "${YELLOW} Found: .bmad-previous-settings${NC}"
|
|
63
|
-
mv .claude/plugins/.bmad-previous-settings .agentvibes/bmad/
|
|
64
|
-
|
|
65
|
+
mv -n .claude/plugins/.bmad-previous-settings .agentvibes/bmad/ && \
|
|
66
|
+
echo -e "${GREEN} ✓ Moved to .agentvibes/bmad/${NC}" || \
|
|
67
|
+
echo -e "${YELLOW} ⚠ Already exists in .agentvibes/bmad/ — skipped${NC}"
|
|
65
68
|
MIGRATED=true
|
|
66
69
|
fi
|
|
67
70
|
|
|
@@ -72,8 +75,9 @@ echo -e "${BLUE}🔍 Checking for BMAD files in .claude/config/...${NC}"
|
|
|
72
75
|
|
|
73
76
|
if [[ -f ".claude/config/bmad-voices.md" ]]; then
|
|
74
77
|
echo -e "${YELLOW} Found: bmad-voices.md${NC}"
|
|
75
|
-
mv .claude/config/bmad-voices.md .agentvibes/bmad/
|
|
76
|
-
|
|
78
|
+
mv -n .claude/config/bmad-voices.md .agentvibes/bmad/ && \
|
|
79
|
+
echo -e "${GREEN} ✓ Moved to .agentvibes/bmad/${NC}" || \
|
|
80
|
+
echo -e "${YELLOW} ⚠ Already exists in .agentvibes/bmad/ — skipped${NC}"
|
|
77
81
|
MIGRATED=true
|
|
78
82
|
fi
|
|
79
83
|
|
|
@@ -97,29 +101,33 @@ echo -e "${BLUE}🔍 Checking for AgentVibes config in .claude/config/...${NC}"
|
|
|
97
101
|
|
|
98
102
|
if [[ -f ".claude/config/agentvibes.json" ]]; then
|
|
99
103
|
echo -e "${YELLOW} Found: agentvibes.json${NC}"
|
|
100
|
-
mv .claude/config/agentvibes.json .agentvibes/config/
|
|
101
|
-
|
|
104
|
+
mv -n .claude/config/agentvibes.json .agentvibes/config/ && \
|
|
105
|
+
echo -e "${GREEN} ✓ Moved to .agentvibes/config/${NC}" || \
|
|
106
|
+
echo -e "${YELLOW} ⚠ Already exists in .agentvibes/config/ — skipped${NC}"
|
|
102
107
|
MIGRATED=true
|
|
103
108
|
fi
|
|
104
109
|
|
|
105
110
|
if [[ -f ".claude/config/personality-voice-defaults.default.json" ]]; then
|
|
106
111
|
echo -e "${YELLOW} Found: personality-voice-defaults.default.json${NC}"
|
|
107
|
-
mv .claude/config/personality-voice-defaults.default.json .agentvibes/config/
|
|
108
|
-
|
|
112
|
+
mv -n .claude/config/personality-voice-defaults.default.json .agentvibes/config/ && \
|
|
113
|
+
echo -e "${GREEN} ✓ Moved to .agentvibes/config/${NC}" || \
|
|
114
|
+
echo -e "${YELLOW} ⚠ Already exists in .agentvibes/config/ — skipped${NC}"
|
|
109
115
|
MIGRATED=true
|
|
110
116
|
fi
|
|
111
117
|
|
|
112
118
|
if [[ -f ".claude/config/personality-voice-defaults.json" ]]; then
|
|
113
119
|
echo -e "${YELLOW} Found: personality-voice-defaults.json${NC}"
|
|
114
|
-
mv .claude/config/personality-voice-defaults.json .agentvibes/config/
|
|
115
|
-
|
|
120
|
+
mv -n .claude/config/personality-voice-defaults.json .agentvibes/config/ && \
|
|
121
|
+
echo -e "${GREEN} ✓ Moved to .agentvibes/config/${NC}" || \
|
|
122
|
+
echo -e "${YELLOW} ⚠ Already exists in .agentvibes/config/ — skipped${NC}"
|
|
116
123
|
MIGRATED=true
|
|
117
124
|
fi
|
|
118
125
|
|
|
119
126
|
if [[ -f ".claude/config/README-personality-defaults.md" ]]; then
|
|
120
127
|
echo -e "${YELLOW} Found: README-personality-defaults.md${NC}"
|
|
121
|
-
mv .claude/config/README-personality-defaults.md .agentvibes/config/
|
|
122
|
-
|
|
128
|
+
mv -n .claude/config/README-personality-defaults.md .agentvibes/config/ && \
|
|
129
|
+
echo -e "${GREEN} ✓ Moved to .agentvibes/config/${NC}" || \
|
|
130
|
+
echo -e "${YELLOW} ⚠ Already exists in .agentvibes/config/ — skipped${NC}"
|
|
123
131
|
MIGRATED=true
|
|
124
132
|
fi
|
|
125
133
|
|
|
@@ -276,6 +276,13 @@ case "$1" in
|
|
|
276
276
|
exit 1
|
|
277
277
|
fi
|
|
278
278
|
|
|
279
|
+
# Validate name: alphanumeric, hyphens, underscores only — no path traversal
|
|
280
|
+
if [[ ! "$NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
|
281
|
+
echo "❌ Invalid personality name '$NAME'"
|
|
282
|
+
echo " Name must contain only letters, numbers, hyphens, and underscores"
|
|
283
|
+
exit 1
|
|
284
|
+
fi
|
|
285
|
+
|
|
279
286
|
FILE="$PERSONALITIES_DIR/${NAME}.md"
|
|
280
287
|
if [[ -f "$FILE" ]]; then
|
|
281
288
|
echo "❌ Personality '$NAME' already exists"
|
|
@@ -306,8 +313,8 @@ Describe how the AI should generate messages for this personality.
|
|
|
306
313
|
- "Example response 2"
|
|
307
314
|
EOF
|
|
308
315
|
|
|
309
|
-
# Replace NAME with
|
|
310
|
-
sed -i "s
|
|
316
|
+
# Replace NAME placeholder — use | as delimiter to avoid issues with / in paths
|
|
317
|
+
sed -i "s|NAME|${NAME}|g" "$FILE"
|
|
311
318
|
|
|
312
319
|
echo "✅ Created new personality: $NAME"
|
|
313
320
|
echo "📝 Edit the file: $FILE"
|
|
@@ -327,6 +334,12 @@ EOF
|
|
|
327
334
|
exit 1
|
|
328
335
|
fi
|
|
329
336
|
|
|
337
|
+
if [[ ! "$NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
|
338
|
+
echo "❌ Invalid personality name '$NAME'"
|
|
339
|
+
echo " Name must contain only letters, numbers, hyphens, and underscores"
|
|
340
|
+
exit 1
|
|
341
|
+
fi
|
|
342
|
+
|
|
330
343
|
FILE="$PERSONALITIES_DIR/${NAME}.md"
|
|
331
344
|
if [[ ! -f "$FILE" ]]; then
|
|
332
345
|
echo "❌ Personality '$NAME' not found"
|
|
@@ -335,6 +335,12 @@ if [[ -n "$VOICE_OVERRIDE" ]]; then
|
|
|
335
335
|
esac
|
|
336
336
|
fi
|
|
337
337
|
|
|
338
|
+
# Emit resolved voice and provider in verbose mode (used by tests and diagnostics)
|
|
339
|
+
if [[ "${AGENTVIBES_VERBOSE:-0}" == "1" ]]; then
|
|
340
|
+
[[ -n "${VOICE_OVERRIDE:-}" ]] && echo "voice=${VOICE_OVERRIDE}" >&2
|
|
341
|
+
echo "provider=${ACTIVE_PROVIDER}" >&2
|
|
342
|
+
fi
|
|
343
|
+
|
|
338
344
|
# @function speak_text
|
|
339
345
|
# @intent Route text to appropriate TTS provider
|
|
340
346
|
# @why Reusable function for speaking, used by both single and learning modes
|
|
@@ -449,6 +449,14 @@ provider_preview() {
|
|
|
449
449
|
echo "🎤 Voice Preview ($current_provider)"
|
|
450
450
|
echo ""
|
|
451
451
|
|
|
452
|
+
# Detect routing LLM so preview audio reaches SSH/remote receivers
|
|
453
|
+
# (without --llm, play-tts.sh plays locally on headless servers instead of
|
|
454
|
+
# forwarding to the configured Windows/remote receiver)
|
|
455
|
+
local _preview_llm _preview_llm_args
|
|
456
|
+
_preview_llm=$(detect_routing_llm 2>/dev/null || echo "")
|
|
457
|
+
_preview_llm_args=()
|
|
458
|
+
[[ -n "$_preview_llm" ]] && _preview_llm_args=(--llm "$_preview_llm")
|
|
459
|
+
|
|
452
460
|
case "$current_provider" in
|
|
453
461
|
piper)
|
|
454
462
|
# Use the Piper voice manager's list functionality
|
|
@@ -468,7 +476,8 @@ provider_preview() {
|
|
|
468
476
|
if verify_voice "$voice_arg"; then
|
|
469
477
|
echo "🎤 Previewing Piper voice: $voice_arg"
|
|
470
478
|
echo ""
|
|
471
|
-
"$SCRIPT_DIR/play-tts.sh" "Hello, this is the $voice_arg voice. How do you like it?"
|
|
479
|
+
"$SCRIPT_DIR/play-tts.sh" "Hello, this is the $voice_arg voice. How do you like it?" \
|
|
480
|
+
"$voice_arg" "${_preview_llm_args[@]+"${_preview_llm_args[@]}"}"
|
|
472
481
|
else
|
|
473
482
|
echo "❌ Voice model not found: $voice_arg"
|
|
474
483
|
echo ""
|
|
@@ -506,7 +515,8 @@ provider_preview() {
|
|
|
506
515
|
local display_name="${voice_entry##*:}"
|
|
507
516
|
|
|
508
517
|
echo "🔊 ${display_name}..."
|
|
509
|
-
"$SCRIPT_DIR/play-tts.sh" "Hi, my name is ${display_name}"
|
|
518
|
+
"$SCRIPT_DIR/play-tts.sh" "Hi, my name is ${display_name}" \
|
|
519
|
+
"$voice_name" "${_preview_llm_args[@]+"${_preview_llm_args[@]}"}"
|
|
510
520
|
|
|
511
521
|
# Wait for the voice to finish playing before starting next one
|
|
512
522
|
sleep 3
|
|
@@ -532,7 +542,8 @@ provider_preview() {
|
|
|
532
542
|
if say -v ? 2>/dev/null | grep -qi "^${voice_arg} "; then
|
|
533
543
|
echo "🎤 Previewing macOS voice: $voice_arg"
|
|
534
544
|
echo ""
|
|
535
|
-
"$SCRIPT_DIR/play-tts.sh" "Hello, this is ${voice_arg}. How do you like my voice?"
|
|
545
|
+
"$SCRIPT_DIR/play-tts.sh" "Hello, this is ${voice_arg}. How do you like my voice?" \
|
|
546
|
+
"$voice_arg" "${_preview_llm_args[@]+"${_preview_llm_args[@]}"}"
|
|
536
547
|
else
|
|
537
548
|
echo "❌ Voice not found: $voice_arg"
|
|
538
549
|
echo ""
|
|
@@ -554,7 +565,8 @@ provider_preview() {
|
|
|
554
565
|
for voice in "${sample_voices[@]}"; do
|
|
555
566
|
if say -v ? 2>/dev/null | grep -qi "^${voice} "; then
|
|
556
567
|
echo "🔊 ${voice}..."
|
|
557
|
-
"$SCRIPT_DIR/play-tts.sh" "Hi, my name is ${voice}"
|
|
568
|
+
"$SCRIPT_DIR/play-tts.sh" "Hi, my name is ${voice}" \
|
|
569
|
+
"$voice" "${_preview_llm_args[@]+"${_preview_llm_args[@]}"}"
|
|
558
570
|
sleep 3
|
|
559
571
|
fi
|
|
560
572
|
done
|
|
@@ -356,6 +356,44 @@ get_provider_script_path() {
|
|
|
356
356
|
echo "$provider_script"
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
+
# @function detect_routing_llm
|
|
360
|
+
# @intent Detect the LLM name to use for SSH/remote routing in preview and sample commands
|
|
361
|
+
# @why preview/sample call play-tts.sh without --llm, so SSH routing is skipped and audio
|
|
362
|
+
# plays locally (no sound on headless servers). Detecting the remote LLM and passing
|
|
363
|
+
# --llm mirrors normal hook behaviour so audio reaches the configured receiver.
|
|
364
|
+
# @returns Echoes LLM name (e.g. "claude-code") or empty string if no remote routing configured
|
|
365
|
+
# @exitcode 0=always
|
|
366
|
+
detect_routing_llm() {
|
|
367
|
+
# Priority 1: AGENTVIBES_LLM_KEY env var (set during active TTS sessions)
|
|
368
|
+
if [[ -n "${AGENTVIBES_LLM_KEY:-}" ]] && [[ "$AGENTVIBES_LLM_KEY" =~ ^llm:([a-zA-Z0-9][a-zA-Z0-9_-]*)$ ]]; then
|
|
369
|
+
echo "${BASH_REMATCH[1]}"
|
|
370
|
+
return 0
|
|
371
|
+
fi
|
|
372
|
+
|
|
373
|
+
# Priority 2: First mode=remote entry in transport-config.json
|
|
374
|
+
local _transport_cfg="$HOME/.agentvibes/transport-config.json"
|
|
375
|
+
if [[ -f "$_transport_cfg" ]] && command -v python3 &>/dev/null; then
|
|
376
|
+
local _remote_llm
|
|
377
|
+
_remote_llm=$(AGENTVIBES_CFG="$_transport_cfg" python3 - <<'PYEOF'
|
|
378
|
+
import json, os, sys
|
|
379
|
+
try:
|
|
380
|
+
d = json.load(open(os.environ['AGENTVIBES_CFG'], encoding='utf-8'))
|
|
381
|
+
hits = [k for k, v in d.items() if isinstance(v, dict) and v.get('mode') == 'remote']
|
|
382
|
+
print(hits[0] if hits else '')
|
|
383
|
+
except Exception:
|
|
384
|
+
print('')
|
|
385
|
+
PYEOF
|
|
386
|
+
)
|
|
387
|
+
# Validate the key is a safe LLM name before returning
|
|
388
|
+
if [[ -n "$_remote_llm" ]] && [[ "$_remote_llm" =~ ^[a-zA-Z0-9][a-zA-Z0-9_-]*$ ]]; then
|
|
389
|
+
echo "$_remote_llm"
|
|
390
|
+
return 0
|
|
391
|
+
fi
|
|
392
|
+
fi
|
|
393
|
+
|
|
394
|
+
echo ""
|
|
395
|
+
}
|
|
396
|
+
|
|
359
397
|
# AI NOTE: This file provides the core abstraction layer for multi-provider TTS.
|
|
360
398
|
# All provider state is managed through simple text files for simplicity and reliability.
|
|
361
399
|
# Project-local configuration takes precedence over global to support per-project providers.
|
package/.claude/hooks/stop.sh
CHANGED
|
@@ -32,32 +32,7 @@ fi
|
|
|
32
32
|
# Only run stop hook in LITE mode
|
|
33
33
|
# (Full mode uses tool calls for TTS, not stop hooks)
|
|
34
34
|
if [[ "$CURRENT_MODE" == "lite" ]]; then
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
TRANSCRIPT_PATH=$(echo "$HOOK_INPUT" | jq -r '.transcript_path // empty' 2>/dev/null || true)
|
|
38
|
-
|
|
39
|
-
[[ -z "$TRANSCRIPT_PATH" ]] && exit 0
|
|
40
|
-
[[ ! -f "$TRANSCRIPT_PATH" ]] && exit 0
|
|
41
|
-
|
|
42
|
-
# Extract Audio Summary ONLY from the most recent assistant response.
|
|
43
|
-
# Key insight: we must NOT search earlier responses — if the latest response
|
|
44
|
-
# has no Audio Summary: marker, we stay silent (no stale replay).
|
|
45
|
-
SUMMARY=$(jq --slurp -r '
|
|
46
|
-
[.[] | try select(.message.role == "assistant")] | last // null
|
|
47
|
-
| if . == null then "" else
|
|
48
|
-
[.message.content[]? | select(.type == "text") | .text] | join("\n")
|
|
49
|
-
end
|
|
50
|
-
| if test("[Aa]udio [Ss]ummary:") then . else "" end
|
|
51
|
-
' "$TRANSCRIPT_PATH" 2>/dev/null \
|
|
52
|
-
| sed -n 's/.*[Aa]udio [Ss]ummary:[[:space:]]*\(.*\)/\1/p' \
|
|
53
|
-
| head -1 \
|
|
54
|
-
| sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
55
|
-
|
|
56
|
-
[[ -z "$SUMMARY" ]] && exit 0
|
|
57
|
-
|
|
58
|
-
# Speak via AgentVibes play-tts.sh
|
|
59
|
-
if [[ -f "$PROJECT_ROOT/.claude/hooks/play-tts.sh" ]]; then
|
|
60
|
-
AGENTVIBES_MIN_TOKENS=3 AGENTVIBES_SHORT_TOKENS=50 \
|
|
61
|
-
bash "$PROJECT_ROOT/.claude/hooks/play-tts.sh" "$SUMMARY" >/dev/null 2>&1 &
|
|
35
|
+
if [[ -f "$AGENTVIBES_HOOKS/stop-lite.sh" ]]; then
|
|
36
|
+
bash "$AGENTVIBES_HOOKS/stop-lite.sh" "$@"
|
|
62
37
|
fi
|
|
63
38
|
fi
|
|
@@ -561,8 +561,55 @@ case "$1" in
|
|
|
561
561
|
fi
|
|
562
562
|
;;
|
|
563
563
|
|
|
564
|
+
sample)
|
|
565
|
+
# Play a sample phrase with the specified voice (used by /agent-vibes:sample)
|
|
566
|
+
SAMPLE_VOICE="${2:-}"
|
|
567
|
+
if [[ -z "$SAMPLE_VOICE" ]]; then
|
|
568
|
+
echo "❌ Usage: /agent-vibes:sample <voice-name>"
|
|
569
|
+
echo ""
|
|
570
|
+
echo "Examples:"
|
|
571
|
+
echo " /agent-vibes:sample en_US-amy-medium"
|
|
572
|
+
echo " /agent-vibes:sample Ryan"
|
|
573
|
+
exit 1
|
|
574
|
+
fi
|
|
575
|
+
|
|
576
|
+
# Source provider-manager.sh first — get_active_provider and detect_routing_llm
|
|
577
|
+
# are both defined there; sourcing after calling them silently produces empty values.
|
|
578
|
+
source "$SCRIPT_DIR/provider-manager.sh" 2>/dev/null || true
|
|
579
|
+
|
|
580
|
+
ACTIVE_PROVIDER=$(get_active_provider)
|
|
581
|
+
|
|
582
|
+
# Friendly name resolution for Piper and transport providers
|
|
583
|
+
case "$ACTIVE_PROVIDER" in
|
|
584
|
+
piper|ssh-remote|agentvibes-receiver|termux-ssh)
|
|
585
|
+
source "$SCRIPT_DIR/piper-voice-manager.sh" 2>/dev/null || true
|
|
586
|
+
SAMPLE_META="$(realpath "$SCRIPT_DIR/../../.agentvibes/config/voice-metadata.json" 2>/dev/null || echo "")"
|
|
587
|
+
if [[ -f "$SAMPLE_META" ]] && command -v jq >/dev/null 2>&1; then
|
|
588
|
+
SAMPLE_RESOLVED=$(jq -r --arg n "$(to_lower "$SAMPLE_VOICE")" '
|
|
589
|
+
.voices | to_entries[] |
|
|
590
|
+
select(.key == $n or (.value.displayName | ascii_downcase) == $n) |
|
|
591
|
+
.value.id
|
|
592
|
+
' "$SAMPLE_META" 2>/dev/null | head -1)
|
|
593
|
+
if [[ -n "$SAMPLE_RESOLVED" ]] && [[ "$SAMPLE_RESOLVED" =~ ^[a-zA-Z0-9_.:+-]+$ ]]; then
|
|
594
|
+
echo "🔍 Resolved '${SAMPLE_VOICE}' → '${SAMPLE_RESOLVED}'"
|
|
595
|
+
SAMPLE_VOICE="$SAMPLE_RESOLVED"
|
|
596
|
+
fi
|
|
597
|
+
fi
|
|
598
|
+
;;
|
|
599
|
+
esac
|
|
600
|
+
|
|
601
|
+
# Detect routing LLM so SSH-remote setups forward audio to the receiver
|
|
602
|
+
SAMPLE_LLM=$(detect_routing_llm 2>/dev/null || echo "")
|
|
603
|
+
SAMPLE_LLM_ARG=()
|
|
604
|
+
[[ -n "$SAMPLE_LLM" ]] && SAMPLE_LLM_ARG=(--llm "$SAMPLE_LLM")
|
|
605
|
+
|
|
606
|
+
echo "🎤 Sampling voice: $SAMPLE_VOICE"
|
|
607
|
+
"$SCRIPT_DIR/play-tts.sh" "Hi, I'm ${SAMPLE_VOICE}. How does my voice sound?" \
|
|
608
|
+
"$SAMPLE_VOICE" "${SAMPLE_LLM_ARG[@]+"${SAMPLE_LLM_ARG[@]}"}"
|
|
609
|
+
;;
|
|
610
|
+
|
|
564
611
|
*)
|
|
565
|
-
echo "Usage: voice-manager.sh [list|switch|get|replay|whoami] [voice_name]"
|
|
612
|
+
echo "Usage: voice-manager.sh [list|switch|get|replay|whoami|sample] [voice_name]"
|
|
566
613
|
echo ""
|
|
567
614
|
echo "Commands:"
|
|
568
615
|
echo " list - List all available voices"
|
|
@@ -570,6 +617,7 @@ case "$1" in
|
|
|
570
617
|
echo " get - Get current voice name"
|
|
571
618
|
echo " replay [N] - Replay Nth most recent audio (default: 1)"
|
|
572
619
|
echo " whoami - Show current voice and personality"
|
|
620
|
+
echo " sample <voice_name> - Play sample audio with the given voice"
|
|
573
621
|
exit 1
|
|
574
622
|
;;
|
|
575
|
-
esac
|
|
623
|
+
esac
|
|
@@ -22,7 +22,17 @@ param(
|
|
|
22
22
|
# CLAUDE_PROJECT_DIR value here so per-project config is found even when
|
|
23
23
|
# Bash tool calls do not propagate CLAUDE_PROJECT_DIR to child processes.
|
|
24
24
|
[Parameter(Mandatory = $false)]
|
|
25
|
-
[string]$ProjectDir = ""
|
|
25
|
+
[string]$ProjectDir = "",
|
|
26
|
+
|
|
27
|
+
# Provider override from the remote sender (set by the watcher from the
|
|
28
|
+
# JSON payload's "provider" field). Overrides the local tts-provider.txt
|
|
29
|
+
# default so the Linux-side config fully controls which engine the Windows
|
|
30
|
+
# receiver uses — no Windows-side provider config needed.
|
|
31
|
+
# Per-LLM engine rows in audio-effects.cfg still take final priority for
|
|
32
|
+
# explicit Windows overrides (e.g. llm:copilot → windows-sapi).
|
|
33
|
+
# Accepts cross-platform aliases: "piper" = windows-piper, "sapi" = windows-sapi.
|
|
34
|
+
[Parameter(Mandatory = $false)]
|
|
35
|
+
[string]$ProviderOverride = ""
|
|
26
36
|
)
|
|
27
37
|
|
|
28
38
|
# Text-file handoff: the SSH receiver watcher writes long/special-char text to
|
|
@@ -99,6 +109,29 @@ switch ($ActiveProvider) {
|
|
|
99
109
|
}
|
|
100
110
|
}
|
|
101
111
|
|
|
112
|
+
# Apply remote provider override (from the JSON payload's "provider" field, passed
|
|
113
|
+
# by the SSH-receiver watcher via -ProviderOverride). This lets the Linux-side
|
|
114
|
+
# audio-effects.cfg row for llm:claude-code specify "piper" and have it honoured
|
|
115
|
+
# on Windows without requiring the Windows tts-provider.txt to be reconfigured.
|
|
116
|
+
# Priority: lower than per-LLM $_LlmEngine (audio-effects.cfg row, set later), higher
|
|
117
|
+
# than the global tts-provider.txt default set above.
|
|
118
|
+
if ($ProviderOverride) {
|
|
119
|
+
switch ($ProviderOverride) {
|
|
120
|
+
{ $_ -in "windows-piper", "piper" } {
|
|
121
|
+
$ProviderScript = "$HooksDir\play-tts-piper.ps1"
|
|
122
|
+
if (-not (Test-Path $ProviderScript)) { $ProviderScript = "$HooksDir\play-tts-windows-piper.ps1" }
|
|
123
|
+
}
|
|
124
|
+
{ $_ -in "windows-sapi", "sapi" } {
|
|
125
|
+
$ProviderScript = "$HooksDir\play-tts-sapi.ps1"
|
|
126
|
+
if (-not (Test-Path $ProviderScript)) { $ProviderScript = "$HooksDir\play-tts-windows-sapi.ps1" }
|
|
127
|
+
}
|
|
128
|
+
"soprano" { $ProviderScript = "$HooksDir\play-tts-soprano.ps1" }
|
|
129
|
+
default {
|
|
130
|
+
Write-Host "[WARNING] play-tts.ps1: Unknown ProviderOverride '$ProviderOverride' ignored" -ForegroundColor Yellow
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
102
135
|
# Check if provider script exists
|
|
103
136
|
if (-not (Test-Path $ProviderScript)) {
|
|
104
137
|
Write-Host "[ERROR] Provider script not found: $ProviderScript" -ForegroundColor Red
|