agentvibes 5.7.7 → 5.10.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 +0 -2
- package/.claude/config/audio-effects.cfg +4 -4
- package/.claude/config/background-music-enabled.txt +1 -0
- package/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks/play-tts-piper.sh +20 -13
- package/.claude/hooks/play-tts-ssh-remote.sh +2 -2
- package/.claude/hooks/voice-manager.sh +6 -0
- package/.claude/hooks-windows/audio-cache-utils.ps1 +119 -119
- package/.claude/hooks-windows/bmad-speak.ps1 +9 -38
- package/.claude/hooks-windows/play-tts-soprano.ps1 +13 -2
- package/.claude/hooks-windows/play-tts-windows-piper.ps1 +22 -16
- package/.mcp.json +13 -9
- package/README.md +33 -2
- package/RELEASE_NOTES.md +80 -0
- package/mcp-server/server.py +17 -7
- package/package.json +2 -2
- package/src/commands/install-mcp.js +270 -16
- package/src/console/app.js +3 -3
- package/src/console/audio-env.js +4 -1
- package/src/console/tabs/agents-tab.js +89 -66
- package/src/console/tabs/music-tab.js +4 -3
- package/src/console/tabs/receiver-tab.js +13 -13
- package/src/console/tabs/settings-tab.js +2 -2
- package/src/console/tabs/setup-tab.js +291 -47
- package/src/console/tabs/voices-tab.js +17 -5
- package/src/console/widgets/personality-picker.js +2 -2
- package/src/console/widgets/reverb-picker.js +1 -1
- package/src/installer.js +32 -27
- package/src/services/provider-service.js +1 -1
- package/src/services/tts-engine-service.js +2 -2
- package/src/utils/audio-duration-validator.js +2 -2
- package/src/utils/list-formatter.js +9 -3
- package/src/utils/platform-resolver.js +369 -0
- package/src/utils/provider-validator.js +9 -9
- package/.agentvibes/install-manifest.json +0 -442
- package/.claude/config/background-music-position.txt +0 -27
- package/.claude/config/background-music-volume.txt +0 -1
- package/.claude/config/background-music.cfg +0 -1
- package/.claude/config/background-music.txt +0 -1
- package/.claude/config/reverb-level.txt +0 -1
- package/.claude/config/tts-speech-rate.txt +0 -1
- package/.claude/config/tts-verbosity.txt +0 -1
- package/.claude/hooks/bmad-party-manager.sh +0 -225
- package/.claude/hooks/stop.sh +0 -38
- package/.claude/piper-voices-dir.txt +0 -1
- /package/.claude/audio/tracks/{CelestialVelvet.mp3 → celestial_velvet.mp3} +0 -0
package/.agentvibes/config.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
llm:default|light||0.15|en_US-lessac-high||piper
|
|
3
|
-
llm:claude-code|light|
|
|
4
|
-
llm:copilot|light|agent_vibes_bossa_nova_v2_loop.mp3|0.15|en_US-libritts-high::Anna-11
|
|
5
|
-
llm:codex|light|agent_vibes_chillwave_v2_loop.mp3|0.15|en_US-lessac-high
|
|
6
|
-
llm:hermes|light|agent_vibes_bachata_v1_loop.mp3|0.15|en_US-libritts-high::Leo-8
|
|
3
|
+
llm:claude-code|light|agent_vibes_chillwave_v2_loop.mp3|0.15|en_US-lessac-high||piper
|
|
4
|
+
llm:copilot|light|agent_vibes_bossa_nova_v2_loop.mp3|0.15|en_US-libritts-high::Anna-11||piper
|
|
5
|
+
llm:codex|light|agent_vibes_chillwave_v2_loop.mp3|0.15|en_US-lessac-high||piper
|
|
6
|
+
llm:hermes|light|agent_vibes_bachata_v1_loop.mp3|0.15|en_US-libritts-high::Leo-8||piper
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
true
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
20260525
|
|
@@ -73,10 +73,8 @@ TEXT=$(printf '%s' "$TEXT" | perl -CSD -pe '
|
|
|
73
73
|
s/^\s*[-]\s*//g; # list dashes
|
|
74
74
|
')
|
|
75
75
|
|
|
76
|
-
#
|
|
77
|
-
|
|
78
|
-
SCRIPT_PATH="$(readlink -f "${BASH_SOURCE[0]}")"
|
|
79
|
-
SCRIPT_DIR="$(dirname "$SCRIPT_PATH")"
|
|
76
|
+
# cd-based resolution works on macOS (BSD readlink lacks -f) and Linux alike
|
|
77
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
80
78
|
source "$SCRIPT_DIR/piper-voice-manager.sh"
|
|
81
79
|
source "$SCRIPT_DIR/language-manager.sh"
|
|
82
80
|
source "$SCRIPT_DIR/audio-cache-utils.sh"
|
|
@@ -226,11 +224,19 @@ if [[ -z "$TEXT" ]]; then
|
|
|
226
224
|
fi
|
|
227
225
|
|
|
228
226
|
# Augment PATH for non-interactive shells (pipx installs to ~/.local/bin which
|
|
229
|
-
# interactive shells get via .bashrc/.zshrc, but Bash tool calls skip profile)
|
|
227
|
+
# interactive shells get via .bashrc/.zshrc, but Bash tool calls skip profile).
|
|
228
|
+
# Mac: add both Apple Silicon (/opt/homebrew) and Intel (/usr/local) Homebrew locations.
|
|
230
229
|
export PATH="$HOME/.local/bin:$HOME/.local/share/pipx/venvs/piper-tts/bin:$PATH"
|
|
230
|
+
if [[ "$(uname -s 2>/dev/null)" == "Darwin" ]]; then
|
|
231
|
+
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
# Resolve explicit piper binary path — avoids bare `piper` invocation failing when
|
|
235
|
+
# PATH augmentation above hasn't propagated into nested subshells.
|
|
236
|
+
PIPER_BIN=$(command -v piper 2>/dev/null || echo "")
|
|
231
237
|
|
|
232
238
|
# Check if Piper is installed
|
|
233
|
-
if
|
|
239
|
+
if [[ -z "$PIPER_BIN" ]]; then
|
|
234
240
|
echo "❌ Error: Piper TTS not installed"
|
|
235
241
|
echo "Install with: pipx install piper-tts"
|
|
236
242
|
echo "Or run: .claude/hooks/piper-installer.sh"
|
|
@@ -309,6 +315,8 @@ else
|
|
|
309
315
|
fi
|
|
310
316
|
|
|
311
317
|
mkdir -p "$AUDIO_DIR"
|
|
318
|
+
# Normalize to canonical path (handles Git Bash /tmp→/c/Users/..., macOS /var→/private/var)
|
|
319
|
+
AUDIO_DIR=$(cd "$AUDIO_DIR" && pwd -P)
|
|
312
320
|
_tmp=$(mktemp "$AUDIO_DIR/tts-XXXXXX"); TEMP_FILE="${_tmp}.wav"; mv "$_tmp" "$TEMP_FILE"
|
|
313
321
|
|
|
314
322
|
# @function get_speech_rate
|
|
@@ -380,6 +388,10 @@ get_speech_rate() {
|
|
|
380
388
|
|
|
381
389
|
SPEECH_RATE=$(get_speech_rate)
|
|
382
390
|
|
|
391
|
+
# Ensure piper log directory exists so stderr redirect never silently fails
|
|
392
|
+
_PIPER_LOG_DIR="${AGENTVIBES_LOG_DIR:-$HOME/.local/state/agentvibes/logs}"
|
|
393
|
+
mkdir -p "$_PIPER_LOG_DIR" 2>/dev/null || true
|
|
394
|
+
|
|
383
395
|
# @function synthesize_with_piper
|
|
384
396
|
# @intent Generate speech using Piper TTS
|
|
385
397
|
# @why Provides free, offline TTS alternative
|
|
@@ -391,10 +403,10 @@ SPEECH_RATE=$(get_speech_rate)
|
|
|
391
403
|
if [[ -n "${SPEAKER_ID:-}" ]]; then
|
|
392
404
|
# Multi-speaker voice: Pass speaker ID
|
|
393
405
|
# SECURITY: Use printf instead of echo for pipe safety (#134)
|
|
394
|
-
printf '%s\n' "$TEXT" |
|
|
406
|
+
printf '%s\n' "$TEXT" | "$PIPER_BIN" --model "$VOICE_PATH" --speaker "$SPEAKER_ID" --length-scale "$SPEECH_RATE" --sentence-silence 2.0 --output_file "$TEMP_FILE" 2>>"$_PIPER_LOG_DIR/piper.log"
|
|
395
407
|
else
|
|
396
408
|
# Single-speaker voice
|
|
397
|
-
printf '%s\n' "$TEXT" |
|
|
409
|
+
printf '%s\n' "$TEXT" | "$PIPER_BIN" --model "$VOICE_PATH" --length-scale "$SPEECH_RATE" --sentence-silence 2.0 --output_file "$TEMP_FILE" 2>>"$_PIPER_LOG_DIR/piper.log"
|
|
398
410
|
fi
|
|
399
411
|
|
|
400
412
|
if [[ ! -f "$TEMP_FILE" ]] || [[ ! -s "$TEMP_FILE" ]]; then
|
|
@@ -538,11 +550,6 @@ AUDIO_DIR="${TEMP_FILE%/*}"
|
|
|
538
550
|
WRITE_LOCK_FILE="$AUDIO_DIR/$(basename "$TEMP_FILE" .wav).lock"
|
|
539
551
|
touch "$WRITE_LOCK_FILE"
|
|
540
552
|
|
|
541
|
-
# Get audio duration for proper lock timing
|
|
542
|
-
DURATION=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$TEMP_FILE" 2>/dev/null)
|
|
543
|
-
DURATION=${DURATION%.*} # Round to integer
|
|
544
|
-
DURATION=${DURATION:-1} # Default to 1 second if detection fails
|
|
545
|
-
|
|
546
553
|
# Get audio duration for proper lock timing
|
|
547
554
|
DURATION=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$TEMP_FILE" 2>/dev/null || true)
|
|
548
555
|
DURATION=${DURATION%.*} # Round to integer
|
|
@@ -60,7 +60,7 @@ SSH_PORT=""
|
|
|
60
60
|
if [[ -n "${AGENTVIBES_SSH_HOST:-}" ]]; then
|
|
61
61
|
SSH_HOST="$AGENTVIBES_SSH_HOST"
|
|
62
62
|
SSH_KEY="${AGENTVIBES_SSH_KEY:-}"
|
|
63
|
-
SSH_PORT="${AGENTVIBES_SSH_PORT:-
|
|
63
|
+
SSH_PORT="${AGENTVIBES_SSH_PORT:-}"
|
|
64
64
|
fi
|
|
65
65
|
|
|
66
66
|
# Priority 2: ~/.agentvibes/transport-config.json (ssh-remote section)
|
|
@@ -340,7 +340,7 @@ SSH_ARGS=()
|
|
|
340
340
|
# Run ssh inside the backgrounded subshell so its exit code is reachable via $?
|
|
341
341
|
# (a `wait` from outside the spawning shell would error: "pid X is not a child").
|
|
342
342
|
(
|
|
343
|
-
ssh "${SSH_ARGS[@]}" "$SSH_HOST" "$ENCODED_PAYLOAD"
|
|
343
|
+
ssh -o ConnectTimeout=10 "${SSH_ARGS[@]}" "$SSH_HOST" "$ENCODED_PAYLOAD"
|
|
344
344
|
_exit=$?
|
|
345
345
|
if [[ $_exit -ne 0 ]]; then
|
|
346
346
|
echo "$(date -Iseconds) [ERROR] SSH to $SSH_HOST failed (exit $_exit)" \
|
|
@@ -513,6 +513,12 @@ case "$1" in
|
|
|
513
513
|
fi
|
|
514
514
|
fi
|
|
515
515
|
|
|
516
|
+
# Normalize to canonical path so ls output is consistent across platforms
|
|
517
|
+
# (Git Bash resolves /tmp→/c/Users/..., macOS has /var→/private/var)
|
|
518
|
+
if [[ -d "$AUDIO_DIR" ]]; then
|
|
519
|
+
AUDIO_DIR=$(cd "$AUDIO_DIR" && pwd -P)
|
|
520
|
+
fi
|
|
521
|
+
|
|
516
522
|
# Default to replay last audio (N=1)
|
|
517
523
|
N="${2:-1}"
|
|
518
524
|
|
|
@@ -1,119 +1,119 @@
|
|
|
1
|
-
#
|
|
2
|
-
# File: .claude/hooks-windows/audio-cache-utils.ps1
|
|
3
|
-
#
|
|
4
|
-
# AgentVibes Audio Cache Utilities for Windows
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
param(
|
|
8
|
-
[Parameter(Position = 0)]
|
|
9
|
-
[ValidateSet('cleanup', 'stats', 'clear')]
|
|
10
|
-
[string]$Command
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
# Detect project-local audio dir (same logic as TTS scripts)
|
|
14
|
-
$ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
15
|
-
$ProjectClaudeDir = Join-Path (Split-Path -Parent (Split-Path -Parent $ScriptPath)) ".claude"
|
|
16
|
-
if (Test-Path $ProjectClaudeDir) {
|
|
17
|
-
$AudioDir = "$ProjectClaudeDir\audio"
|
|
18
|
-
} else {
|
|
19
|
-
$AudioDir = "$env:USERPROFILE\.claude\audio"
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function Ensure-AudioDir {
|
|
23
|
-
if (-not (Test-Path $AudioDir)) {
|
|
24
|
-
New-Item -ItemType Directory -Path $AudioDir -Force | Out-Null
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function Get-AudioCacheSize {
|
|
29
|
-
Ensure-AudioDir
|
|
30
|
-
|
|
31
|
-
if (-not (Test-Path $AudioDir)) {
|
|
32
|
-
return 0
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
$files = Get-ChildItem -Path $AudioDir -Filter "*.wav" -ErrorAction SilentlyContinue
|
|
36
|
-
$totalSize = 0
|
|
37
|
-
|
|
38
|
-
foreach ($file in $files) {
|
|
39
|
-
$totalSize += $file.Length
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return $totalSize
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function Format-FileSize {
|
|
46
|
-
param([long]$Size)
|
|
47
|
-
|
|
48
|
-
if ($Size -lt 1KB) { return "$Size B" }
|
|
49
|
-
if ($Size -lt 1MB) { return "{0:N2} KB" -f ($Size / 1KB) }
|
|
50
|
-
if ($Size -lt 1GB) { return "{0:N2} MB" -f ($Size / 1MB) }
|
|
51
|
-
return "{0:N2} GB" -f ($Size / 1GB)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function Get-CacheStats {
|
|
55
|
-
Ensure-AudioDir
|
|
56
|
-
|
|
57
|
-
$files = Get-ChildItem -Path $AudioDir -Filter "*.wav" -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum
|
|
58
|
-
|
|
59
|
-
$count = if ($files.Count -eq $null) { 0 } else { $files.Count }
|
|
60
|
-
$totalSize = if ($files.Sum -eq $null) { 0 } else { $files.Sum }
|
|
61
|
-
|
|
62
|
-
return @{
|
|
63
|
-
FileCount = $count
|
|
64
|
-
TotalSize = $totalSize
|
|
65
|
-
FormattedSize = Format-FileSize $totalSize
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function Clear-Cache {
|
|
70
|
-
Ensure-AudioDir
|
|
71
|
-
|
|
72
|
-
$files = Get-ChildItem -Path $AudioDir -Filter "*.wav" -ErrorAction SilentlyContinue
|
|
73
|
-
|
|
74
|
-
if ($files.Count -eq 0) {
|
|
75
|
-
Write-Host "[OK] Cache already empty" -ForegroundColor Green
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
$stats = Get-CacheStats
|
|
80
|
-
Write-Host "[CLEANUP] Clearing $($stats.FileCount) audio files ($($stats.FormattedSize))" -ForegroundColor Yellow
|
|
81
|
-
|
|
82
|
-
foreach ($file in $files) {
|
|
83
|
-
Remove-Item $file.FullName -Force -ErrorAction SilentlyContinue
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
Write-Host "[OK] Cache cleared" -ForegroundColor Green
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function Show-CacheStats {
|
|
90
|
-
Ensure-AudioDir
|
|
91
|
-
|
|
92
|
-
$stats = Get-CacheStats
|
|
93
|
-
|
|
94
|
-
Write-Host ""
|
|
95
|
-
Write-Host "[STATS] Audio Cache Statistics" -ForegroundColor Cyan
|
|
96
|
-
Write-Host " Location: $AudioDir"
|
|
97
|
-
Write-Host " Files: $($stats.FileCount)"
|
|
98
|
-
Write-Host " Total Size: $($stats.FormattedSize)"
|
|
99
|
-
Write-Host ""
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
# Main command routing
|
|
103
|
-
switch ($Command) {
|
|
104
|
-
'stats' {
|
|
105
|
-
Show-CacheStats
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
'cleanup' {
|
|
109
|
-
Clear-Cache
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
'clear' {
|
|
113
|
-
Clear-Cache
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
default {
|
|
117
|
-
Show-CacheStats
|
|
118
|
-
}
|
|
119
|
-
}
|
|
1
|
+
#
|
|
2
|
+
# File: .claude/hooks-windows/audio-cache-utils.ps1
|
|
3
|
+
#
|
|
4
|
+
# AgentVibes Audio Cache Utilities for Windows
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
param(
|
|
8
|
+
[Parameter(Position = 0)]
|
|
9
|
+
[ValidateSet('cleanup', 'stats', 'clear')]
|
|
10
|
+
[string]$Command
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
# Detect project-local audio dir (same logic as TTS scripts)
|
|
14
|
+
$ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
15
|
+
$ProjectClaudeDir = Join-Path (Split-Path -Parent (Split-Path -Parent $ScriptPath)) ".claude"
|
|
16
|
+
if (Test-Path $ProjectClaudeDir) {
|
|
17
|
+
$AudioDir = "$ProjectClaudeDir\audio"
|
|
18
|
+
} else {
|
|
19
|
+
$AudioDir = "$env:USERPROFILE\.claude\audio"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function Ensure-AudioDir {
|
|
23
|
+
if (-not (Test-Path $AudioDir)) {
|
|
24
|
+
New-Item -ItemType Directory -Path $AudioDir -Force | Out-Null
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function Get-AudioCacheSize {
|
|
29
|
+
Ensure-AudioDir
|
|
30
|
+
|
|
31
|
+
if (-not (Test-Path $AudioDir)) {
|
|
32
|
+
return 0
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
$files = Get-ChildItem -Path $AudioDir -Filter "*.wav" -ErrorAction SilentlyContinue
|
|
36
|
+
$totalSize = 0
|
|
37
|
+
|
|
38
|
+
foreach ($file in $files) {
|
|
39
|
+
$totalSize += $file.Length
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return $totalSize
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function Format-FileSize {
|
|
46
|
+
param([long]$Size)
|
|
47
|
+
|
|
48
|
+
if ($Size -lt 1KB) { return "$Size B" }
|
|
49
|
+
if ($Size -lt 1MB) { return "{0:N2} KB" -f ($Size / 1KB) }
|
|
50
|
+
if ($Size -lt 1GB) { return "{0:N2} MB" -f ($Size / 1MB) }
|
|
51
|
+
return "{0:N2} GB" -f ($Size / 1GB)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function Get-CacheStats {
|
|
55
|
+
Ensure-AudioDir
|
|
56
|
+
|
|
57
|
+
$files = Get-ChildItem -Path $AudioDir -Filter "*.wav" -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum
|
|
58
|
+
|
|
59
|
+
$count = if ($files.Count -eq $null) { 0 } else { $files.Count }
|
|
60
|
+
$totalSize = if ($files.Sum -eq $null) { 0 } else { $files.Sum }
|
|
61
|
+
|
|
62
|
+
return @{
|
|
63
|
+
FileCount = $count
|
|
64
|
+
TotalSize = $totalSize
|
|
65
|
+
FormattedSize = Format-FileSize $totalSize
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function Clear-Cache {
|
|
70
|
+
Ensure-AudioDir
|
|
71
|
+
|
|
72
|
+
$files = Get-ChildItem -Path $AudioDir -Filter "*.wav" -ErrorAction SilentlyContinue
|
|
73
|
+
|
|
74
|
+
if ($files.Count -eq 0) {
|
|
75
|
+
Write-Host "[OK] Cache already empty" -ForegroundColor Green
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
$stats = Get-CacheStats
|
|
80
|
+
Write-Host "[CLEANUP] Clearing $($stats.FileCount) audio files ($($stats.FormattedSize))" -ForegroundColor Yellow
|
|
81
|
+
|
|
82
|
+
foreach ($file in $files) {
|
|
83
|
+
Remove-Item $file.FullName -Force -ErrorAction SilentlyContinue
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
Write-Host "[OK] Cache cleared" -ForegroundColor Green
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function Show-CacheStats {
|
|
90
|
+
Ensure-AudioDir
|
|
91
|
+
|
|
92
|
+
$stats = Get-CacheStats
|
|
93
|
+
|
|
94
|
+
Write-Host ""
|
|
95
|
+
Write-Host "[STATS] Audio Cache Statistics" -ForegroundColor Cyan
|
|
96
|
+
Write-Host " Location: $AudioDir"
|
|
97
|
+
Write-Host " Files: $($stats.FileCount)"
|
|
98
|
+
Write-Host " Total Size: $($stats.FormattedSize)"
|
|
99
|
+
Write-Host ""
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# Main command routing
|
|
103
|
+
switch ($Command) {
|
|
104
|
+
'stats' {
|
|
105
|
+
Show-CacheStats
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
'cleanup' {
|
|
109
|
+
Clear-Cache
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
'clear' {
|
|
113
|
+
Clear-Cache
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
default {
|
|
117
|
+
Show-CacheStats
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -198,29 +198,13 @@ if ($AgentPersonality -and (Test-Path (Split-Path $PersonalityFile -Parent))) {
|
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
# ---------------------------------------------------------------------------
|
|
201
|
-
#
|
|
202
|
-
#
|
|
203
|
-
#
|
|
204
|
-
|
|
205
|
-
$AudioEffectsCfg = Join-Path $TtsConfigDir "audio-effects.cfg"
|
|
206
|
-
$OldBgEnabled = $null
|
|
207
|
-
$TempCfgLine = ""
|
|
208
|
-
|
|
201
|
+
# Apply per-agent background music via AGENTVIBES_OVERRIDE_* env vars.
|
|
202
|
+
# play-tts.ps1 reads these directly and forces BgEnabled=true when OVERRIDE_MUSIC
|
|
203
|
+
# is set — no config file patching needed, and cleanup is automatic (env vars
|
|
204
|
+
# are scoped to the child process spawned by & powershell below).
|
|
209
205
|
if ($AgentBgEnabled -and $AgentBgTrack) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
$OldBgEnabled = (Get-Content $BgEnabledFile -Raw -ErrorAction SilentlyContinue).Trim()
|
|
213
|
-
}
|
|
214
|
-
Set-Content $BgEnabledFile "true" -NoNewline
|
|
215
|
-
|
|
216
|
-
# Prepend agent line to audio-effects.cfg so play-tts.ps1 finds it first
|
|
217
|
-
# Format: AGENT_NAME|SOX_EFFECTS|BACKGROUND_FILE|BACKGROUND_VOLUME
|
|
218
|
-
$TempCfgLine = "${AgentId}||${AgentBgTrack}|${AgentBgVolume}"
|
|
219
|
-
$env:AGENTVIBES_AGENT_NAME = $AgentId
|
|
220
|
-
$existingCfg = if (Test-Path $AudioEffectsCfg) {
|
|
221
|
-
Get-Content $AudioEffectsCfg -Raw -ErrorAction SilentlyContinue
|
|
222
|
-
} else { "" }
|
|
223
|
-
Set-Content $AudioEffectsCfg "${TempCfgLine}`n${existingCfg}" -NoNewline
|
|
206
|
+
$env:AGENTVIBES_OVERRIDE_MUSIC = $AgentBgTrack
|
|
207
|
+
$env:AGENTVIBES_OVERRIDE_VOLUME = $AgentBgVolume
|
|
224
208
|
}
|
|
225
209
|
|
|
226
210
|
try {
|
|
@@ -243,22 +227,9 @@ try {
|
|
|
243
227
|
}
|
|
244
228
|
}
|
|
245
229
|
|
|
246
|
-
#
|
|
230
|
+
# Clear music override env vars (use SetEnvironmentVariable to fully remove, not just empty)
|
|
247
231
|
if ($AgentBgEnabled -and $AgentBgTrack) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
} elseif (Test-Path $BgEnabledFile) {
|
|
251
|
-
Remove-Item $BgEnabledFile -Force -ErrorAction SilentlyContinue
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
# Remove the prepended agent line from audio-effects.cfg
|
|
255
|
-
if (Test-Path $AudioEffectsCfg) {
|
|
256
|
-
$cfgRaw = Get-Content $AudioEffectsCfg -Raw -ErrorAction SilentlyContinue
|
|
257
|
-
$escaped = [regex]::Escape($TempCfgLine)
|
|
258
|
-
$cfgRaw = $cfgRaw -replace "^${escaped}\r?\n?", ""
|
|
259
|
-
Set-Content $AudioEffectsCfg $cfgRaw -NoNewline
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
$env:AGENTVIBES_AGENT_NAME = ""
|
|
232
|
+
[System.Environment]::SetEnvironmentVariable("AGENTVIBES_OVERRIDE_MUSIC", $null, "Process")
|
|
233
|
+
[System.Environment]::SetEnvironmentVariable("AGENTVIBES_OVERRIDE_VOLUME", $null, "Process")
|
|
263
234
|
}
|
|
264
235
|
}
|
|
@@ -20,6 +20,13 @@ param(
|
|
|
20
20
|
|
|
21
21
|
$ErrorActionPreference = "Stop"
|
|
22
22
|
|
|
23
|
+
# Append registry PATH entries so Python-installed scripts (soprano, soprano-webui) are
|
|
24
|
+
# found when spawned from Node.js. Prepend existing PATH to preserve runtime shims
|
|
25
|
+
# (nvm, conda, pyenv) that were added after the process started.
|
|
26
|
+
$env:Path = $env:Path + ";" +
|
|
27
|
+
[System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" +
|
|
28
|
+
[System.Environment]::GetEnvironmentVariable("Path", "User")
|
|
29
|
+
|
|
23
30
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
24
31
|
# Validate port is numeric to prevent injection
|
|
25
32
|
$SopranoPort = "7860"
|
|
@@ -101,7 +108,8 @@ if (Test-WebUI) {
|
|
|
101
108
|
$SynthMode = "webui"
|
|
102
109
|
$pythonHelper = Join-Path $ScriptDir "soprano-gradio-synth.py"
|
|
103
110
|
if (Test-Path $pythonHelper) {
|
|
104
|
-
|
|
111
|
+
# Wrap in try/catch: PS7.3+ throws on native non-zero exit with ErrorActionPreference=Stop
|
|
112
|
+
try { & python $pythonHelper $Text $TempFile $SopranoPort 2>$null } catch { }
|
|
105
113
|
} else {
|
|
106
114
|
Write-Host "[ERROR] soprano-gradio-synth.py not found" -ForegroundColor Red
|
|
107
115
|
exit 1
|
|
@@ -123,7 +131,8 @@ if (Test-WebUI) {
|
|
|
123
131
|
} elseif (Test-SopranoCLI) {
|
|
124
132
|
# CLI fallback - reloads model each call (slowest)
|
|
125
133
|
$SynthMode = "cli"
|
|
126
|
-
|
|
134
|
+
# Wrap in try/catch: PS7.3+ throws on native non-zero exit with ErrorActionPreference=Stop
|
|
135
|
+
try { & soprano $Text -o $TempFile -d $SopranoDevice 2>$null } catch { }
|
|
127
136
|
} else {
|
|
128
137
|
Write-Host "[ERROR] Soprano TTS not installed and no server running on port $SopranoPort" -ForegroundColor Red
|
|
129
138
|
Write-Host ""
|
|
@@ -156,3 +165,5 @@ if (-not $env:AGENTVIBES_NO_PLAY) {
|
|
|
156
165
|
|
|
157
166
|
Write-Host "Saved to: $TempFile"
|
|
158
167
|
Write-Host "Voice: Soprano-1.1-80M (Soprano TTS, $SynthMode mode)"
|
|
168
|
+
# Output bare path so play-tts.ps1 can capture it for reverb/bg-music processing
|
|
169
|
+
Write-Output $TempFile
|
|
@@ -62,15 +62,20 @@ if (-not $VoiceName) {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
# Security: Validate voice name to prevent path traversal
|
|
65
|
-
#
|
|
66
|
-
|
|
65
|
+
# Format: <model> or <model>::<speaker> for multi-speaker Piper models
|
|
66
|
+
# Only allow alphanumeric, underscore, hyphen, period, and the :: separator
|
|
67
|
+
if ($VoiceName -notmatch '^[a-zA-Z0-9_\-\.]+(::[a-zA-Z0-9_\-\.]+)?$') {
|
|
67
68
|
Write-Host "[ERROR] Invalid voice name: $VoiceName" -ForegroundColor Red
|
|
68
69
|
exit 1
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
# Extract model name and optional speaker (multi-speaker format: model::Speaker)
|
|
73
|
+
$VoiceModel = ($VoiceName -split '::')[0]
|
|
74
|
+
$SpeakerName = if ($VoiceName -match '::(.+)$') { $Matches[1] } else { "" }
|
|
75
|
+
|
|
71
76
|
# Resolve voice model path and validate it stays within VoicesDir
|
|
72
|
-
$VoiceModelFile = [System.IO.Path]::GetFullPath("$VoicesDir\$
|
|
73
|
-
$VoiceJsonFile
|
|
77
|
+
$VoiceModelFile = [System.IO.Path]::GetFullPath("$VoicesDir\$VoiceModel.onnx")
|
|
78
|
+
$VoiceJsonFile = [System.IO.Path]::GetFullPath("$VoicesDir\$VoiceModel.onnx.json")
|
|
74
79
|
$ResolvedVoicesDir = [System.IO.Path]::GetFullPath($VoicesDir)
|
|
75
80
|
if (-not $VoiceModelFile.StartsWith($ResolvedVoicesDir)) {
|
|
76
81
|
Write-Host "[ERROR] Voice path outside voices directory" -ForegroundColor Red
|
|
@@ -79,15 +84,15 @@ if (-not $VoiceModelFile.StartsWith($ResolvedVoicesDir)) {
|
|
|
79
84
|
|
|
80
85
|
# Check if voice model exists, download if missing
|
|
81
86
|
if (-not (Test-Path $VoiceModelFile)) {
|
|
82
|
-
Write-Host "[DOWNLOAD] Voice model: $
|
|
87
|
+
Write-Host "[DOWNLOAD] Voice model: $VoiceModel" -ForegroundColor Yellow
|
|
83
88
|
|
|
84
89
|
# Try to download from Hugging Face
|
|
85
90
|
# Voice name format: {lang}_{region}-{speaker}-{quality}
|
|
86
91
|
# HF path format: {lang}/{lang}_{region}/{speaker}/{quality}/{voicename}.onnx
|
|
87
92
|
try {
|
|
88
|
-
# Parse
|
|
93
|
+
# Parse model name to build correct HF path (strip ::Speaker suffix first)
|
|
89
94
|
# e.g. en_US-ryan-high -> en/en_US/ryan/high/en_US-ryan-high.onnx
|
|
90
|
-
if ($
|
|
95
|
+
if ($VoiceModel -match '^([a-z]{2})_([A-Z]{2})-([a-zA-Z0-9_]+)-([a-z]+)$') {
|
|
91
96
|
$Lang = $Matches[1]
|
|
92
97
|
$LangRegion = "$($Matches[1])_$($Matches[2])"
|
|
93
98
|
$Speaker = $Matches[3]
|
|
@@ -97,8 +102,8 @@ if (-not (Test-Path $VoiceModelFile)) {
|
|
|
97
102
|
# Fallback for non-standard voice names
|
|
98
103
|
$HFBase = "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/ryan/high"
|
|
99
104
|
}
|
|
100
|
-
$ModelUrl = "$HFBase/$
|
|
101
|
-
$JsonUrl = "$HFBase/$
|
|
105
|
+
$ModelUrl = "$HFBase/$VoiceModel.onnx"
|
|
106
|
+
$JsonUrl = "$HFBase/$VoiceModel.onnx.json"
|
|
102
107
|
|
|
103
108
|
Write-Host " Downloading model..." -ForegroundColor Cyan
|
|
104
109
|
Invoke-WebRequest -Uri $ModelUrl -OutFile $VoiceModelFile -ErrorAction Stop
|
|
@@ -127,11 +132,10 @@ $AudioFile = "$AudioDir\tts-$Timestamp.wav"
|
|
|
127
132
|
try {
|
|
128
133
|
Write-Host "[SYNTH] Synthesizing with Piper..." -ForegroundColor Cyan
|
|
129
134
|
|
|
130
|
-
# Run Piper with text input
|
|
131
|
-
$
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
2>$null
|
|
135
|
+
# Run Piper with text input; pass --speaker for multi-speaker models (model::Speaker format)
|
|
136
|
+
$piperArgs = @('--model', $VoiceModelFile, '--output-file', $AudioFile)
|
|
137
|
+
if ($SpeakerName) { $piperArgs += @('--speaker', $SpeakerName) }
|
|
138
|
+
$Text | & $PiperExe @piperArgs 2>$null
|
|
135
139
|
|
|
136
140
|
if (-not (Test-Path $AudioFile)) {
|
|
137
141
|
Write-Host "[ERROR] Piper synthesis failed" -ForegroundColor Red
|
|
@@ -151,12 +155,14 @@ try {
|
|
|
151
155
|
if (-not $env:AGENTVIBES_NO_PLAY) {
|
|
152
156
|
# Prefer ffplay: handles 22050 Hz → 48000 Hz resampling cleanly (SoundPlayer uses
|
|
153
157
|
# WinMM's low-quality resampler which produces choppy audio at non-native rates).
|
|
154
|
-
$
|
|
158
|
+
$ffplayCmd = Get-Command ffplay -ErrorAction SilentlyContinue
|
|
159
|
+
$ffplayPath = if ($ffplayCmd) { $ffplayCmd.Source }
|
|
155
160
|
if (-not $ffplayPath) {
|
|
156
161
|
# SSH/watcher sessions may have a minimal PATH — refresh from registry
|
|
157
162
|
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" +
|
|
158
163
|
[System.Environment]::GetEnvironmentVariable("Path","User")
|
|
159
|
-
$
|
|
164
|
+
$ffplayCmd = Get-Command ffplay -ErrorAction SilentlyContinue
|
|
165
|
+
$ffplayPath = if ($ffplayCmd) { $ffplayCmd.Source }
|
|
160
166
|
}
|
|
161
167
|
if ($ffplayPath) {
|
|
162
168
|
& $ffplayPath -autoexit -nodisp -loglevel quiet $AudioFile 2>$null
|
package/.mcp.json
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"mcpServers": {
|
|
3
|
-
"
|
|
3
|
+
"firecrawl": {
|
|
4
|
+
"command": "npx",
|
|
5
|
+
"args": [
|
|
6
|
+
"firecrawl-mcp"
|
|
7
|
+
]
|
|
8
|
+
},
|
|
9
|
+
"context7": {
|
|
4
10
|
"command": "npx",
|
|
5
11
|
"args": [
|
|
6
12
|
"-y",
|
|
7
|
-
"
|
|
8
|
-
"agentvibes-mcp-server"
|
|
13
|
+
"@upstash/context7-mcp"
|
|
9
14
|
],
|
|
10
15
|
"env": {
|
|
11
|
-
"
|
|
16
|
+
"CONTEXT7_API_KEY": "${CONTEXT7_API_KEY}"
|
|
12
17
|
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"firecrawl-mcp"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
awl-mcp"
|
|
18
22
|
]
|
|
19
23
|
},
|
|
20
24
|
"context7": {
|
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**: v5.
|
|
14
|
+
**Author**: Paul Preibisch ([@997Fire](https://x.com/997Fire)) | **Version**: v5.10.0
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
@@ -40,7 +40,33 @@ Whether you're coding in Claude Code, chatting in Claude Desktop, using Warp Ter
|
|
|
40
40
|
|
|
41
41
|
---
|
|
42
42
|
|
|
43
|
-
## 🌟 NEW IN v5.
|
|
43
|
+
## 🌟 NEW IN v5.9.0 — SSH Remote + Windows Home Directory Fixes
|
|
44
|
+
|
|
45
|
+
**SSH remote no longer hangs:** The SSH transport now applies a 10-second connection
|
|
46
|
+
timeout, so a stuck or unreachable remote host surfaces an error quickly instead of
|
|
47
|
+
blocking forever.
|
|
48
|
+
|
|
49
|
+
**Windows home directory detection fixed:** `detectRemoteLlm()` now uses null-coalescing
|
|
50
|
+
(`??`) to fall back to `os.homedir()` only when `HOME` is genuinely unset — safer than
|
|
51
|
+
the previous `||` which could misfire on an empty string.
|
|
52
|
+
|
|
53
|
+
## v5.8.0 — Soprano Now Works + Voice Picker Fixed for All Engines
|
|
54
|
+
|
|
55
|
+
**Soprano TTS actually works now:** Soprano (our neural TTS engine) was silently broken
|
|
56
|
+
on Windows since launch — wrong binary name, stripped PATH, wav path sent to the wrong
|
|
57
|
+
output stream, and no auto-start for the WebUI server. All fixed. Install with
|
|
58
|
+
`pip install soprano-tts`, select Soprano in the setup tab, and AgentVibes handles
|
|
59
|
+
the rest.
|
|
60
|
+
|
|
61
|
+
**Voice picker now works for Windows SAPI and macOS Say:** Previously the picker showed
|
|
62
|
+
the entire Piper voice catalog even when SAPI or macOS Say was selected, and Space-bar
|
|
63
|
+
preview played through the wrong engine. The picker now shows exactly one item for each
|
|
64
|
+
native engine and previews through the correct binary.
|
|
65
|
+
|
|
66
|
+
**Auto-save no longer breaks your engine setting:** Saving an LLM config no longer silently
|
|
67
|
+
overwrites your chosen engine back to Piper.
|
|
68
|
+
|
|
69
|
+
## v5.7.7 — Party Mode Voice Restore + Polish
|
|
44
70
|
|
|
45
71
|
**Party mode agents now speak again:** BMAD `/party-mode` now reliably invokes the correct AgentVibes skill, and each agent's response is spoken aloud in their unique voice with per-agent music, pretext, and reverb — loaded automatically from `~/.agentvibes/bmad-voice-map.json`.
|
|
46
72
|
|
|
@@ -220,6 +246,11 @@ Replace the default background tracks with your own audio files for complete son
|
|
|
220
246
|
- Automatic format detection
|
|
221
247
|
- Duration warnings for non-optimal lengths
|
|
222
248
|
|
|
249
|
+
**Custom Track Naming Rules:**
|
|
250
|
+
- Use `snake_case` filenames only — e.g. `my_focus_music.mp3` ✅
|
|
251
|
+
- No spaces or uppercase letters in filenames — e.g. `My Focus Music.mp3` ❌
|
|
252
|
+
- Misnamed files will not appear in the music picker and will be skipped by the audio engine
|
|
253
|
+
|
|
223
254
|
**Perfect for:**
|
|
224
255
|
- 🎸 **Team Audio Branding** - Company theme music
|
|
225
256
|
- 🎮 **Gaming Sessions** - Epic background tracks
|