agentvibes 5.6.8 → 5.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/.agentvibes/config.json +2 -0
- package/.claude/commands/agent-vibes-bmad-voices.md +117 -117
- package/.claude/commands/agent-vibes-rdp.md +24 -24
- package/.claude/config/audio-effects.cfg +2 -2
- package/.claude/config/background-music-position.txt +0 -1
- package/.claude/docs/TERMUX_SETUP.md +408 -408
- package/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks/audio-cache-utils.sh +0 -0
- package/.claude/hooks/audio-processor.sh +0 -0
- package/.claude/hooks/background-music-manager.sh +0 -0
- package/.claude/hooks/bmad-party-manager.sh +225 -0
- package/.claude/hooks/bmad-party-speak.sh +0 -0
- package/.claude/hooks/bmad-speak-enhanced.sh +0 -0
- package/.claude/hooks/bmad-speak.sh +0 -0
- package/.claude/hooks/bmad-tts-injector.sh +49 -21
- package/.claude/hooks/bmad-voice-manager.sh +0 -0
- package/.claude/hooks/clawdbot-receiver-SECURE.sh +0 -0
- package/.claude/hooks/clawdbot-receiver.sh +0 -0
- package/.claude/hooks/clean-audio-cache.sh +0 -0
- package/.claude/hooks/cleanup-cache.sh +0 -0
- package/.claude/hooks/configure-rdp-mode.sh +0 -0
- package/.claude/hooks/download-extra-voices.sh +0 -0
- package/.claude/hooks/effects-manager.sh +0 -0
- package/.claude/hooks/github-star-reminder.sh +0 -0
- package/.claude/hooks/language-manager.sh +0 -0
- package/.claude/hooks/learn-manager.sh +0 -0
- package/.claude/hooks/macos-voice-manager.sh +0 -0
- package/.claude/hooks/migrate-background-music.sh +0 -0
- package/.claude/hooks/migrate-to-agentvibes.sh +0 -0
- package/.claude/hooks/optimize-background-music.sh +0 -0
- package/.claude/hooks/path-resolver.sh +0 -0
- package/.claude/hooks/personality-manager.sh +0 -0
- package/.claude/hooks/piper-download-voices.sh +0 -0
- package/.claude/hooks/piper-installer.sh +0 -0
- package/.claude/hooks/piper-multispeaker-registry.sh +0 -0
- package/.claude/hooks/piper-voice-manager.sh +0 -0
- package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +0 -0
- package/.claude/hooks/play-tts-enhanced.sh +0 -0
- package/.claude/hooks/play-tts-macos.sh +0 -0
- package/.claude/hooks/play-tts-piper.sh +1 -1
- package/.claude/hooks/play-tts-soprano.sh +0 -0
- package/.claude/hooks/play-tts-ssh-remote.sh +0 -0
- package/.claude/hooks/play-tts-termux-ssh.sh +0 -0
- package/.claude/hooks/play-tts-windows-receiver.sh +0 -0
- package/.claude/hooks/play-tts.sh +4 -0
- package/.claude/hooks/prepare-release.sh +0 -0
- package/.claude/hooks/provider-commands.sh +16 -4
- package/.claude/hooks/provider-manager.sh +38 -0
- package/.claude/hooks/replay-target-audio.sh +0 -0
- package/.claude/hooks/sentiment-manager.sh +0 -0
- package/.claude/hooks/session-start-tts.sh +0 -0
- package/.claude/hooks/soprano-gradio-synth.py +0 -0
- package/.claude/hooks/speed-manager.sh +0 -0
- package/.claude/hooks/stop-tts.sh +0 -0
- package/.claude/hooks/stop.sh +38 -0
- package/.claude/hooks/termux-installer.sh +0 -0
- package/.claude/hooks/translate-manager.sh +0 -0
- package/.claude/hooks/translator.py +0 -0
- package/.claude/hooks/tts-queue-worker.sh +0 -0
- package/.claude/hooks/tts-queue.sh +0 -0
- package/.claude/hooks/verbosity-manager.sh +0 -0
- package/.claude/hooks/voice-manager.sh +50 -2
- package/.claude/hooks-windows/audio-cache-utils.ps1 +119 -119
- 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 -0
- package/.clawdbot/README.md +105 -105
- package/.mcp.json +14 -5
- package/README.md +10 -2
- package/RELEASE_NOTES.md +61 -0
- package/WINDOWS-SETUP.md +208 -208
- package/bin/agent-vibes +39 -39
- package/bin/agentvibes-voice-browser.js +59 -4
- package/bin/agentvibes.js +0 -0
- package/bin/mcp-server.js +121 -121
- package/bin/mcp-server.sh +0 -0
- package/bin/test-bmad-pr +78 -78
- package/mcp-server/QUICK_START.md +203 -203
- package/mcp-server/README.md +345 -345
- package/mcp-server/WINDOWS_SETUP.md +260 -260
- package/mcp-server/docs/troubleshooting-audio.md +313 -313
- package/mcp-server/examples/claude_desktop_config.json +11 -11
- package/mcp-server/examples/claude_desktop_config_piper.json +9 -9
- package/mcp-server/examples/custom_instructions.md +169 -169
- package/mcp-server/install-deps.js +177 -130
- package/mcp-server/server.py +1797 -1787
- package/mcp-server/test_server.py +0 -0
- 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/music-tab.js +18 -2
- 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 +70 -7
- 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/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/templates/agentvibes-receiver.sh +0 -0
- package/templates/audio/welcome-music.mp3 +0 -0
- package/.claude/hooks/play-tts-agentvibes-receiver.sh +0 -1
|
@@ -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
|
|
@@ -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
|
+
}
|
|
@@ -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
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#
|
|
2
|
+
# File: .claude/hooks-windows/tts-watcher.ps1
|
|
3
|
+
#
|
|
4
|
+
# AgentVibes TTS Queue Watcher — runs in the user's interactive session for audio access.
|
|
5
|
+
# The SSH receiver (session 0, no audio) writes JSON request files to the queue; this
|
|
6
|
+
# watcher picks them up and calls play-tts.ps1 in the user's desktop session.
|
|
7
|
+
#
|
|
8
|
+
# Single-instance guard: exit immediately if another watcher instance is already running.
|
|
9
|
+
$mutex = New-Object System.Threading.Mutex($false, 'Global\AgentVibesTtsWatcher')
|
|
10
|
+
if (-not $mutex.WaitOne(0)) { $mutex.Dispose(); exit 0 }
|
|
11
|
+
|
|
12
|
+
$QueueDir = "$env:USERPROFILE\.agentvibes\tts-queue"
|
|
13
|
+
$LogFile = "$env:USERPROFILE\.agentvibes\watcher.log"
|
|
14
|
+
$PlayTts = "$env:USERPROFILE\.claude\hooks-windows\play-tts.ps1"
|
|
15
|
+
if (-not (Test-Path $QueueDir)) { New-Item -ItemType Directory -Path $QueueDir -Force | Out-Null }
|
|
16
|
+
|
|
17
|
+
function Write-WatcherLog {
|
|
18
|
+
param([string]$Level, [string]$Msg)
|
|
19
|
+
$ts = Get-Date -Format 'yyyy-MM-ddTHH:mm:ss'
|
|
20
|
+
Add-Content -Path $LogFile -Value "$ts [$Level] $Msg" -ErrorAction SilentlyContinue
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
Write-WatcherLog "INFO" "Watcher started. PlayTts=$PlayTts exists=$(Test-Path $PlayTts)"
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
while ($true) {
|
|
27
|
+
$files = Get-ChildItem "$QueueDir\req-*.json" -ErrorAction SilentlyContinue | Sort-Object CreationTime
|
|
28
|
+
foreach ($f in $files) {
|
|
29
|
+
# Rename to proc-* before processing — crash recovery on restart
|
|
30
|
+
$procFile = $f.FullName -replace '\\req-', '\proc-'
|
|
31
|
+
try { Rename-Item $f.FullName $procFile -ErrorAction Stop } catch { continue }
|
|
32
|
+
try {
|
|
33
|
+
$req = Get-Content $procFile -Raw | ConvertFrom-Json
|
|
34
|
+
# Validate voice before passing to command line
|
|
35
|
+
$safeVoice = if ($req.voice -and $req.voice -match '^[a-zA-Z0-9_\-\. :]+$') { $req.voice } else { "" }
|
|
36
|
+
$env:CLAUDE_PROJECT_DIR = $env:USERPROFILE
|
|
37
|
+
$env:AGENTVIBES_NO_PRETEXT = "1"
|
|
38
|
+
# Use SetEnvironmentVariable to truly unset (assignment to $null leaves empty string)
|
|
39
|
+
if ($req.music) { $env:AGENTVIBES_OVERRIDE_MUSIC = $req.music }
|
|
40
|
+
else { [System.Environment]::SetEnvironmentVariable("AGENTVIBES_OVERRIDE_MUSIC", $null, "Process") }
|
|
41
|
+
if ($req.volume) { $env:AGENTVIBES_OVERRIDE_VOLUME = $req.volume }
|
|
42
|
+
else { [System.Environment]::SetEnvironmentVariable("AGENTVIBES_OVERRIDE_VOLUME", $null, "Process") }
|
|
43
|
+
if ($req.effects) { $env:AGENTVIBES_OVERRIDE_EFFECTS = $req.effects }
|
|
44
|
+
else { [System.Environment]::SetEnvironmentVariable("AGENTVIBES_OVERRIDE_EFFECTS", $null, "Process") }
|
|
45
|
+
|
|
46
|
+
if (Test-Path $PlayTts) {
|
|
47
|
+
# Play remote arrival prefix sound if configured
|
|
48
|
+
$prefixSoundFile = "$env:USERPROFILE\.agentvibes\remote-prefix-sound.txt"
|
|
49
|
+
if (Test-Path $prefixSoundFile) {
|
|
50
|
+
$prefixSound = (Get-Content $prefixSoundFile -Raw).Trim()
|
|
51
|
+
if ($prefixSound -and (Test-Path $prefixSound)) {
|
|
52
|
+
$ffplay = Get-Command ffplay -ErrorAction SilentlyContinue
|
|
53
|
+
if ($ffplay) {
|
|
54
|
+
& $ffplay.Source -autoexit -nodisp -loglevel quiet $prefixSound 2>$null
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
$tempText = Join-Path $env:TEMP "agentvibes-tts-$($req.id).txt"
|
|
60
|
+
try {
|
|
61
|
+
[System.IO.File]::WriteAllText($tempText, $req.text, [System.Text.UTF8Encoding]::new($false))
|
|
62
|
+
$env:AGENTVIBES_TEXT_FILE = $tempText
|
|
63
|
+
|
|
64
|
+
$llmArg = @()
|
|
65
|
+
if ($req.llm) {
|
|
66
|
+
if ($req.llm -match '^[a-zA-Z0-9][a-zA-Z0-9_-]*$') {
|
|
67
|
+
$llmArg = @('-llm', $req.llm)
|
|
68
|
+
} else {
|
|
69
|
+
Write-WatcherLog "WARN" "Invalid LLM name '$($req.llm)' - using default"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Provider override: the Linux sender embeds its configured provider
|
|
74
|
+
# (from receiver-provider.txt / audio-effects.cfg ENGINE column) in the
|
|
75
|
+
# JSON payload so the Windows receiver uses the Linux-side engine choice.
|
|
76
|
+
$providerArg = @()
|
|
77
|
+
if ($req.provider) {
|
|
78
|
+
$safeProvider = $req.provider.Trim()
|
|
79
|
+
if ($safeProvider -match '^[a-zA-Z0-9][a-zA-Z0-9_-]*$') {
|
|
80
|
+
$providerArg = @('-ProviderOverride', $safeProvider)
|
|
81
|
+
} else {
|
|
82
|
+
Write-WatcherLog "WARN" "Invalid provider '$safeProvider' in payload - ignored"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
Write-WatcherLog "INFO" "play-tts id=$($req.id) voice=$safeVoice llm=$($req.llm) provider=$safeProvider"
|
|
87
|
+
$playOutput = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $PlayTts "__from_file__" $safeVoice @llmArg @providerArg 2>&1
|
|
88
|
+
if ($LASTEXITCODE -ne 0) {
|
|
89
|
+
Write-WatcherLog "ERROR" "play-tts exit=$LASTEXITCODE id=$($req.id) output=$($playOutput -join ' | ')"
|
|
90
|
+
} else {
|
|
91
|
+
Write-WatcherLog "INFO" "play-tts ok exit=0 id=$($req.id)"
|
|
92
|
+
}
|
|
93
|
+
} finally {
|
|
94
|
+
[System.Environment]::SetEnvironmentVariable("AGENTVIBES_TEXT_FILE", $null, "Process")
|
|
95
|
+
Remove-Item $tempText -Force -ErrorAction SilentlyContinue
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
# Fallback: Windows SAPI (built-in, no installation required)
|
|
99
|
+
Write-WatcherLog "WARN" "play-tts.ps1 not found - using SAPI fallback for id=$($req.id)"
|
|
100
|
+
Add-Type -AssemblyName System.Speech
|
|
101
|
+
$synth = New-Object System.Speech.Synthesis.SpeechSynthesizer
|
|
102
|
+
$synth.Speak($req.text)
|
|
103
|
+
$synth.Dispose()
|
|
104
|
+
}
|
|
105
|
+
Remove-Item $procFile -Force -ErrorAction SilentlyContinue
|
|
106
|
+
} catch {
|
|
107
|
+
Write-WatcherLog "ERROR" "id=$($req.id) err=$_"
|
|
108
|
+
Remove-Item $procFile -Force -ErrorAction SilentlyContinue
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
# Crash recovery: re-queue any proc-* files left from a previous watcher crash
|
|
112
|
+
$stale = Get-ChildItem "$QueueDir\proc-*.json" -ErrorAction SilentlyContinue
|
|
113
|
+
foreach ($s in $stale) {
|
|
114
|
+
$recovered = $s.FullName -replace '\\proc-', '\req-'
|
|
115
|
+
try { Rename-Item $s.FullName $recovered -ErrorAction SilentlyContinue } catch {}
|
|
116
|
+
}
|
|
117
|
+
Start-Sleep -Milliseconds 200
|
|
118
|
+
}
|
|
119
|
+
} finally {
|
|
120
|
+
$mutex.ReleaseMutex()
|
|
121
|
+
$mutex.Dispose()
|
|
122
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/home/administrator/.claude/piper-voices
|