agentvibes 3.4.2 → 3.5.0-alpha.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/.claude/commands/agent-vibes/provider.md +0 -0
- package/.claude/config/audio-effects.cfg +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-speak-enhanced.sh +0 -0
- package/.claude/hooks/bmad-speak.sh +0 -0
- package/.claude/hooks/bmad-tts-injector.sh +0 -0
- 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/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-enhanced.sh +0 -0
- package/.claude/hooks/play-tts-macos.sh +0 -0
- package/.claude/hooks/play-tts-piper.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.sh +0 -6
- package/.claude/hooks/prepare-release.sh +0 -0
- package/.claude/hooks/provider-commands.sh +0 -0
- package/.claude/hooks/provider-manager.sh +0 -17
- 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/speed-manager.sh +0 -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 +0 -0
- package/.claude/hooks-windows/audio-cache-utils.ps1 +112 -0
- package/.claude/hooks-windows/play-tts-windows-piper.ps1 +142 -0
- package/.claude/hooks-windows/play-tts-windows-sapi.ps1 +101 -0
- package/.claude/hooks-windows/play-tts.ps1 +81 -0
- package/.claude/hooks-windows/provider-manager.ps1 +133 -0
- package/.claude/hooks-windows/voice-manager-windows.ps1 +172 -0
- package/README.md +17 -27
- package/RELEASE_NOTES.md +1 -199
- package/bin/agent-vibes +0 -0
- package/bin/mcp-server.js +0 -0
- package/bin/mcp-server.sh +0 -0
- package/bin/test-bmad-pr +0 -0
- package/mcp-server/WINDOWS_SETUP.md +0 -0
- package/mcp-server/install-deps.js +0 -0
- package/mcp-server/test_server.py +0 -0
- package/package.json +4 -2
- package/src/installer.js +47 -339
- package/templates/agentvibes-receiver.sh +0 -0
- package/templates/audio/welcome-music.mp3 +0 -0
- package/.claude/config/background-music-position.txt +0 -1
- package/.claude/github-star-reminder.txt +0 -1
- package/.claude/hooks/play-tts-soprano.sh +0 -320
- package/.claude/hooks/soprano-gradio-synth.py +0 -139
- package/.claude/piper-voices-dir.txt +0 -1
- package/.mcp.json +0 -12
|
File without changes
|
|
@@ -49,4 +49,4 @@ BMad Master|reverb 50 60 100 pitch -100|agentvibes_soft_flamenco_loop.mp3|0.30
|
|
|
49
49
|
_party_mode|compand 0.3,1 6:-70,-60,-20|agent_vibes_dark_chill_step_loop.mp3|0.40
|
|
50
50
|
|||
|
|
51
51
|
# Default (no agent specified) - clean with Bachata background|||
|
|
52
|
-
default|reverb
|
|
52
|
+
default|reverb 40 50 70|agentvibes_soft_flamenco_loop.mp3|0.30
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -146,9 +146,6 @@ speak_text() {
|
|
|
146
146
|
piper)
|
|
147
147
|
"$SCRIPT_DIR/play-tts-piper.sh" "$text" "$voice"
|
|
148
148
|
;;
|
|
149
|
-
soprano)
|
|
150
|
-
"$SCRIPT_DIR/play-tts-soprano.sh" "$text" "$voice"
|
|
151
|
-
;;
|
|
152
149
|
macos)
|
|
153
150
|
"$SCRIPT_DIR/play-tts-macos.sh" "$text" "$voice"
|
|
154
151
|
;;
|
|
@@ -268,9 +265,6 @@ case "$ACTIVE_PROVIDER" in
|
|
|
268
265
|
piper)
|
|
269
266
|
exec "$SCRIPT_DIR/play-tts-piper.sh" "$TEXT" "$VOICE_OVERRIDE"
|
|
270
267
|
;;
|
|
271
|
-
soprano)
|
|
272
|
-
exec "$SCRIPT_DIR/play-tts-soprano.sh" "$TEXT" "$VOICE_OVERRIDE"
|
|
273
|
-
;;
|
|
274
268
|
macos)
|
|
275
269
|
exec "$SCRIPT_DIR/play-tts-macos.sh" "$TEXT" "$VOICE_OVERRIDE"
|
|
276
270
|
;;
|
|
File without changes
|
|
File without changes
|
|
@@ -190,13 +190,6 @@ migrate_voice_to_provider() {
|
|
|
190
190
|
# Default voices by provider
|
|
191
191
|
local piper_default="en_US-lessac-medium"
|
|
192
192
|
local macos_default="Samantha"
|
|
193
|
-
local soprano_default="soprano-default" # Single voice — no selection needed
|
|
194
|
-
|
|
195
|
-
# Soprano has a single voice, so migration is straightforward
|
|
196
|
-
if [[ "$target_provider" == "soprano" ]]; then
|
|
197
|
-
echo "$soprano_default"
|
|
198
|
-
return 0
|
|
199
|
-
fi
|
|
200
193
|
|
|
201
194
|
# If no current voice, return default for target provider
|
|
202
195
|
if [[ -z "$current_voice" ]]; then
|
|
@@ -208,16 +201,6 @@ migrate_voice_to_provider() {
|
|
|
208
201
|
return 0
|
|
209
202
|
fi
|
|
210
203
|
|
|
211
|
-
# If migrating FROM Soprano, return default for target provider
|
|
212
|
-
if [[ "$current_voice" == "soprano-default" ]]; then
|
|
213
|
-
case "$target_provider" in
|
|
214
|
-
piper) echo "$piper_default" ;;
|
|
215
|
-
macos) echo "$macos_default" ;;
|
|
216
|
-
*) echo "$piper_default" ;;
|
|
217
|
-
esac
|
|
218
|
-
return 0
|
|
219
|
-
fi
|
|
220
|
-
|
|
221
204
|
# Convert to lowercase for case-insensitive comparison (portable)
|
|
222
205
|
local current_voice_lower
|
|
223
206
|
current_voice_lower=$(echo "$current_voice" | tr '[:upper:]' '[:lower:]')
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,112 @@
|
|
|
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
|
+
$AudioDir = "$env:USERPROFILE\.claude\audio"
|
|
14
|
+
|
|
15
|
+
function Ensure-AudioDir {
|
|
16
|
+
if (-not (Test-Path $AudioDir)) {
|
|
17
|
+
New-Item -ItemType Directory -Path $AudioDir -Force | Out-Null
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function Get-AudioCacheSize {
|
|
22
|
+
Ensure-AudioDir
|
|
23
|
+
|
|
24
|
+
if (-not (Test-Path $AudioDir)) {
|
|
25
|
+
return 0
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
$files = Get-ChildItem -Path $AudioDir -Filter "*.wav" -ErrorAction SilentlyContinue
|
|
29
|
+
$totalSize = 0
|
|
30
|
+
|
|
31
|
+
foreach ($file in $files) {
|
|
32
|
+
$totalSize += $file.Length
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return $totalSize
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function Format-FileSize {
|
|
39
|
+
param([long]$Size)
|
|
40
|
+
|
|
41
|
+
if ($Size -lt 1KB) { return "$Size B" }
|
|
42
|
+
if ($Size -lt 1MB) { return "{0:N2} KB" -f ($Size / 1KB) }
|
|
43
|
+
if ($Size -lt 1GB) { return "{0:N2} MB" -f ($Size / 1MB) }
|
|
44
|
+
return "{0:N2} GB" -f ($Size / 1GB)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function Get-CacheStats {
|
|
48
|
+
Ensure-AudioDir
|
|
49
|
+
|
|
50
|
+
$files = Get-ChildItem -Path $AudioDir -Filter "*.wav" -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum
|
|
51
|
+
|
|
52
|
+
$count = if ($files.Count -eq $null) { 0 } else { $files.Count }
|
|
53
|
+
$totalSize = if ($files.Sum -eq $null) { 0 } else { $files.Sum }
|
|
54
|
+
|
|
55
|
+
return @{
|
|
56
|
+
FileCount = $count
|
|
57
|
+
TotalSize = $totalSize
|
|
58
|
+
FormattedSize = Format-FileSize $totalSize
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function Clear-Cache {
|
|
63
|
+
Ensure-AudioDir
|
|
64
|
+
|
|
65
|
+
$files = Get-ChildItem -Path $AudioDir -Filter "*.wav" -ErrorAction SilentlyContinue
|
|
66
|
+
|
|
67
|
+
if ($files.Count -eq 0) {
|
|
68
|
+
Write-Host "[OK] Cache already empty" -ForegroundColor Green
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
$stats = Get-CacheStats
|
|
73
|
+
Write-Host "[CLEANUP] Clearing $($stats.FileCount) audio files ($($stats.FormattedSize))" -ForegroundColor Yellow
|
|
74
|
+
|
|
75
|
+
foreach ($file in $files) {
|
|
76
|
+
Remove-Item $file.FullName -Force -ErrorAction SilentlyContinue
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
Write-Host "[OK] Cache cleared" -ForegroundColor Green
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function Show-CacheStats {
|
|
83
|
+
Ensure-AudioDir
|
|
84
|
+
|
|
85
|
+
$stats = Get-CacheStats
|
|
86
|
+
|
|
87
|
+
Write-Host ""
|
|
88
|
+
Write-Host "[STATS] Audio Cache Statistics" -ForegroundColor Cyan
|
|
89
|
+
Write-Host " Location: $AudioDir"
|
|
90
|
+
Write-Host " Files: $($stats.FileCount)"
|
|
91
|
+
Write-Host " Total Size: $($stats.FormattedSize)"
|
|
92
|
+
Write-Host ""
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# Main command routing
|
|
96
|
+
switch ($Command) {
|
|
97
|
+
'stats' {
|
|
98
|
+
Show-CacheStats
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
'cleanup' {
|
|
102
|
+
Clear-Cache
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
'clear' {
|
|
106
|
+
Clear-Cache
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
default {
|
|
110
|
+
Show-CacheStats
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#
|
|
2
|
+
# File: .claude/hooks-windows/play-tts-windows-piper.ps1
|
|
3
|
+
#
|
|
4
|
+
# AgentVibes - Windows Piper TTS Provider
|
|
5
|
+
# High-quality neural TTS using Piper.exe
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
param(
|
|
9
|
+
[Parameter(Mandatory = $true)]
|
|
10
|
+
[string]$Text,
|
|
11
|
+
|
|
12
|
+
[Parameter(Mandatory = $false)]
|
|
13
|
+
[string]$VoiceOverride
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
# Configuration paths
|
|
17
|
+
$ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
18
|
+
$ProjectClaudeDir = Join-Path (Split-Path -Parent (Split-Path -Parent $ScriptPath)) ".claude"
|
|
19
|
+
|
|
20
|
+
if (Test-Path $ProjectClaudeDir) {
|
|
21
|
+
$ClaudeDir = $ProjectClaudeDir
|
|
22
|
+
} else {
|
|
23
|
+
$ClaudeDir = "$env:USERPROFILE\.claude"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
$AudioDir = "$ClaudeDir\audio"
|
|
27
|
+
$VoicesDir = "$ClaudeDir\piper-voices"
|
|
28
|
+
$VoiceFile = "$ClaudeDir\tts-voice-piper.txt"
|
|
29
|
+
$PiperExe = "$env:LOCALAPPDATA\Programs\Piper\piper.exe"
|
|
30
|
+
|
|
31
|
+
# Ensure directories exist
|
|
32
|
+
foreach ($dir in @($AudioDir, $VoicesDir)) {
|
|
33
|
+
if (-not (Test-Path $dir)) {
|
|
34
|
+
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# Check if Piper is installed
|
|
39
|
+
if (-not (Test-Path $PiperExe)) {
|
|
40
|
+
Write-Host "[ERROR] Piper not found at: $PiperExe" -ForegroundColor Red
|
|
41
|
+
Write-Host "Run: .\setup-windows.ps1 to install Piper" -ForegroundColor Yellow
|
|
42
|
+
exit 1
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Determine voice to use
|
|
46
|
+
$VoiceName = ""
|
|
47
|
+
|
|
48
|
+
if ($VoiceOverride) {
|
|
49
|
+
$VoiceName = $VoiceOverride
|
|
50
|
+
}
|
|
51
|
+
elseif (Test-Path $VoiceFile) {
|
|
52
|
+
$VoiceName = (Get-Content $VoiceFile -Raw).Trim()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Default voice if not specified
|
|
56
|
+
if (-not $VoiceName) {
|
|
57
|
+
$VoiceName = "en_US-ryan-high"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Resolve voice model path
|
|
61
|
+
$VoiceModelFile = "$VoicesDir\$VoiceName.onnx"
|
|
62
|
+
$VoiceJsonFile = "$VoicesDir\$VoiceName.onnx.json"
|
|
63
|
+
|
|
64
|
+
# Check if voice model exists, download if missing
|
|
65
|
+
if (-not (Test-Path $VoiceModelFile)) {
|
|
66
|
+
Write-Host "[DOWNLOAD] Voice model: $VoiceName" -ForegroundColor Yellow
|
|
67
|
+
|
|
68
|
+
# Try to download from Hugging Face
|
|
69
|
+
# Voice name format: {lang}_{region}-{speaker}-{quality}
|
|
70
|
+
# HF path format: {lang}/{lang}_{region}/{speaker}/{quality}/{voicename}.onnx
|
|
71
|
+
try {
|
|
72
|
+
# Parse voice name to build correct HF path
|
|
73
|
+
# e.g. en_US-ryan-high -> en/en_US/ryan/high/en_US-ryan-high.onnx
|
|
74
|
+
if ($VoiceName -match '^([a-z]{2})_([A-Z]{2})-(.+)-([a-z]+)$') {
|
|
75
|
+
$Lang = $Matches[1]
|
|
76
|
+
$LangRegion = "$($Matches[1])_$($Matches[2])"
|
|
77
|
+
$Speaker = $Matches[3]
|
|
78
|
+
$Quality = $Matches[4]
|
|
79
|
+
$HFBase = "https://huggingface.co/rhasspy/piper-voices/resolve/main/$Lang/$LangRegion/$Speaker/$Quality"
|
|
80
|
+
} else {
|
|
81
|
+
# Fallback for non-standard voice names
|
|
82
|
+
$HFBase = "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/ryan/high"
|
|
83
|
+
}
|
|
84
|
+
$ModelUrl = "$HFBase/$VoiceName.onnx"
|
|
85
|
+
$JsonUrl = "$HFBase/$VoiceName.onnx.json"
|
|
86
|
+
|
|
87
|
+
Write-Host " Downloading model..." -ForegroundColor Cyan
|
|
88
|
+
Invoke-WebRequest -Uri $ModelUrl -OutFile $VoiceModelFile -ErrorAction Stop
|
|
89
|
+
Write-Host " Downloading config..." -ForegroundColor Cyan
|
|
90
|
+
Invoke-WebRequest -Uri $JsonUrl -OutFile $VoiceJsonFile -ErrorAction Stop
|
|
91
|
+
Write-Host "[OK] Voice model downloaded" -ForegroundColor Green
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
Write-Host "[ERROR] Failed to download voice model: $_" -ForegroundColor Red
|
|
95
|
+
Write-Host "Make sure you have internet connection" -ForegroundColor Yellow
|
|
96
|
+
exit 1
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# Sanitize text for speech - strip characters that TTS engines read literally
|
|
101
|
+
$Text = $Text -replace '\\', ' '
|
|
102
|
+
$Text = $Text -replace '[{}<>|`~^]', ''
|
|
103
|
+
$Text = $Text -replace '\s+', ' '
|
|
104
|
+
$Text = $Text.Trim()
|
|
105
|
+
|
|
106
|
+
# Create audio file path
|
|
107
|
+
$Timestamp = Get-Date -Format 'yyyyMMdd-HHmmss-ffff'
|
|
108
|
+
$AudioFile = "$AudioDir\tts-$Timestamp.wav"
|
|
109
|
+
|
|
110
|
+
# Synthesize with Piper
|
|
111
|
+
try {
|
|
112
|
+
Write-Host "[SYNTH] Synthesizing with Piper..." -ForegroundColor Cyan
|
|
113
|
+
|
|
114
|
+
# Run Piper with text input
|
|
115
|
+
$Text | & $PiperExe `
|
|
116
|
+
--model $VoiceModelFile `
|
|
117
|
+
--output-file $AudioFile `
|
|
118
|
+
2>$null
|
|
119
|
+
|
|
120
|
+
if (-not (Test-Path $AudioFile)) {
|
|
121
|
+
Write-Host "[ERROR] Piper synthesis failed" -ForegroundColor Red
|
|
122
|
+
exit 1
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
# Display results
|
|
126
|
+
Write-Host "[OK] Saved to: $AudioFile" -ForegroundColor Green
|
|
127
|
+
Write-Host "[VOICE] Voice used: $VoiceName (Piper)" -ForegroundColor Green
|
|
128
|
+
|
|
129
|
+
# Play the audio using built-in Windows audio player
|
|
130
|
+
try {
|
|
131
|
+
$player = New-Object System.Media.SoundPlayer $AudioFile
|
|
132
|
+
$player.PlaySync()
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
Write-Host "[WARNING] Could not play audio (SoundPlayer unavailable)" -ForegroundColor Yellow
|
|
136
|
+
Write-Host "Audio saved to: $AudioFile" -ForegroundColor Gray
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
Write-Host "[ERROR] Error running Piper: $_" -ForegroundColor Red
|
|
141
|
+
exit 1
|
|
142
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#
|
|
2
|
+
# File: .claude/hooks-windows/play-tts-windows-sapi.ps1
|
|
3
|
+
#
|
|
4
|
+
# AgentVibes - Windows SAPI TTS Provider (Zero Dependencies)
|
|
5
|
+
# Uses built-in Windows System.Speech API
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
param(
|
|
9
|
+
[Parameter(Mandatory = $true)]
|
|
10
|
+
[string]$Text,
|
|
11
|
+
|
|
12
|
+
[Parameter(Mandatory = $false)]
|
|
13
|
+
[string]$VoiceOverride
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
# Configuration paths
|
|
17
|
+
$ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
18
|
+
$ProjectClaudeDir = Join-Path (Split-Path -Parent (Split-Path -Parent $ScriptPath)) ".claude"
|
|
19
|
+
|
|
20
|
+
if (Test-Path $ProjectClaudeDir) {
|
|
21
|
+
$ClaudeDir = $ProjectClaudeDir
|
|
22
|
+
} else {
|
|
23
|
+
$ClaudeDir = "$env:USERPROFILE\.claude"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
$AudioDir = "$ClaudeDir\audio"
|
|
27
|
+
$VoiceFile = "$ClaudeDir\tts-voice-sapi.txt"
|
|
28
|
+
|
|
29
|
+
# Ensure directories exist
|
|
30
|
+
if (-not (Test-Path $AudioDir)) {
|
|
31
|
+
New-Item -ItemType Directory -Path $AudioDir -Force | Out-Null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# Load System.Speech assembly
|
|
35
|
+
try {
|
|
36
|
+
Add-Type -AssemblyName System.Speech
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
Write-Host "[ERROR] System.Speech assembly not available" -ForegroundColor Red
|
|
40
|
+
exit 1
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Determine voice to use
|
|
44
|
+
$VoiceName = ""
|
|
45
|
+
|
|
46
|
+
if ($VoiceOverride) {
|
|
47
|
+
$VoiceName = $VoiceOverride
|
|
48
|
+
}
|
|
49
|
+
elseif (Test-Path $VoiceFile) {
|
|
50
|
+
$VoiceName = (Get-Content $VoiceFile -Raw).Trim()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# Initialize speech synthesizer
|
|
54
|
+
$synth = New-Object System.Speech.Synthesis.SpeechSynthesizer
|
|
55
|
+
|
|
56
|
+
# Set voice if specified
|
|
57
|
+
if ($VoiceName) {
|
|
58
|
+
try {
|
|
59
|
+
$synth.SelectVoice($VoiceName)
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
Write-Host "[WARNING] Voice '$VoiceName' not found, using default" -ForegroundColor Yellow
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# Sanitize text for speech - strip characters that TTS engines read literally
|
|
67
|
+
$Text = $Text -replace '\\', ' '
|
|
68
|
+
$Text = $Text -replace '[{}<>|`~^]', ''
|
|
69
|
+
$Text = $Text -replace '\s+', ' '
|
|
70
|
+
$Text = $Text.Trim()
|
|
71
|
+
|
|
72
|
+
# Get actual voice name (after selection or default)
|
|
73
|
+
$ActualVoice = $synth.Voice.Name
|
|
74
|
+
|
|
75
|
+
# Create audio file path
|
|
76
|
+
$Timestamp = Get-Date -Format 'yyyyMMdd-HHmmss-ffff'
|
|
77
|
+
$AudioFile = "$AudioDir\tts-$Timestamp.wav"
|
|
78
|
+
|
|
79
|
+
# Save to WAV file
|
|
80
|
+
try {
|
|
81
|
+
$synth.SetOutputToWaveFile($AudioFile)
|
|
82
|
+
$synth.Speak($Text)
|
|
83
|
+
$synth.Dispose()
|
|
84
|
+
|
|
85
|
+
# Display results
|
|
86
|
+
Write-Host "[OK] Saved to: $AudioFile" -ForegroundColor Green
|
|
87
|
+
Write-Host "[VOICE] Voice used: $ActualVoice (Windows SAPI)" -ForegroundColor Green
|
|
88
|
+
|
|
89
|
+
# Play the audio using built-in Windows audio player
|
|
90
|
+
try {
|
|
91
|
+
$player = New-Object System.Media.SoundPlayer $AudioFile
|
|
92
|
+
$player.PlaySync()
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
Write-Host "[WARNING] Could not play audio (SoundPlayer unavailable)" -ForegroundColor Yellow
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
Write-Host "[ERROR] Error synthesizing speech: $_" -ForegroundColor Red
|
|
100
|
+
exit 1
|
|
101
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#
|
|
2
|
+
# File: .claude/hooks-windows/play-tts.ps1
|
|
3
|
+
#
|
|
4
|
+
# AgentVibes - Windows TTS Router
|
|
5
|
+
# Delegates to active provider (windows-sapi or windows-piper)
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
param(
|
|
9
|
+
[Parameter(Mandatory = $true, Position = 0)]
|
|
10
|
+
[string]$Text,
|
|
11
|
+
|
|
12
|
+
[Parameter(Mandatory = $false, Position = 1)]
|
|
13
|
+
[string]$VoiceOverride
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
# Configuration paths
|
|
17
|
+
# First check if we're running from a project directory with .claude
|
|
18
|
+
$ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
19
|
+
$ProjectClaudeDir = Join-Path (Split-Path -Parent (Split-Path -Parent $ScriptPath)) ".claude"
|
|
20
|
+
|
|
21
|
+
# Use project .claude if running from there, otherwise use user profile
|
|
22
|
+
if (Test-Path $ProjectClaudeDir) {
|
|
23
|
+
$ClaudeDir = $ProjectClaudeDir
|
|
24
|
+
} else {
|
|
25
|
+
$ClaudeDir = "$env:USERPROFILE\.claude"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
$HooksDir = "$ClaudeDir\hooks-windows"
|
|
29
|
+
$ProviderFile = "$ClaudeDir\tts-provider.txt"
|
|
30
|
+
$MuteFile = "$ClaudeDir\tts-muted.txt"
|
|
31
|
+
|
|
32
|
+
# Check if TTS is muted
|
|
33
|
+
if (Test-Path $MuteFile) {
|
|
34
|
+
$muteStatus = Get-Content $MuteFile -Raw
|
|
35
|
+
if ($muteStatus.Trim() -eq "true") {
|
|
36
|
+
exit 0
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Determine active provider
|
|
41
|
+
$ActiveProvider = "windows-sapi"
|
|
42
|
+
if (Test-Path $ProviderFile) {
|
|
43
|
+
$ActiveProvider = (Get-Content $ProviderFile -Raw).Trim()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Validate and get provider script
|
|
47
|
+
$ProviderScript = ""
|
|
48
|
+
|
|
49
|
+
switch ($ActiveProvider) {
|
|
50
|
+
"windows-sapi" {
|
|
51
|
+
$ProviderScript = "$HooksDir\play-tts-windows-sapi.ps1"
|
|
52
|
+
}
|
|
53
|
+
"windows-piper" {
|
|
54
|
+
$ProviderScript = "$HooksDir\play-tts-windows-piper.ps1"
|
|
55
|
+
}
|
|
56
|
+
default {
|
|
57
|
+
Write-Host "[ERROR] Unknown provider: $ActiveProvider" -ForegroundColor Red
|
|
58
|
+
Write-Host "Use: .\provider-manager.ps1 list" -ForegroundColor Yellow
|
|
59
|
+
exit 1
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Check if provider script exists
|
|
64
|
+
if (-not (Test-Path $ProviderScript)) {
|
|
65
|
+
Write-Host "[ERROR] Provider script not found: $ProviderScript" -ForegroundColor Red
|
|
66
|
+
exit 1
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# Call the provider script
|
|
70
|
+
try {
|
|
71
|
+
if ($VoiceOverride) {
|
|
72
|
+
& $ProviderScript $Text $VoiceOverride
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
& $ProviderScript $Text
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
Write-Host "[ERROR] TTS Error: $_" -ForegroundColor Red
|
|
80
|
+
exit 1
|
|
81
|
+
}
|