agentvibes 4.4.1 → 4.5.7

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.
Files changed (55) hide show
  1. package/.agentvibes/config.json +4 -4
  2. package/.claude/config/audio-effects.cfg +1 -0
  3. package/.claude/config/background-music-enabled.txt +1 -0
  4. package/.claude/config/reverb-level.txt +1 -1
  5. package/.claude/github-star-reminder.txt +1 -1
  6. package/.claude/hooks/audio-processor.sh +1 -1
  7. package/.claude/hooks/bmad-speak.sh +16 -2
  8. package/.claude/hooks-windows/bmad-speak.ps1 +200 -0
  9. package/.claude/hooks-windows/play-tts-piper.ps1 +3 -4
  10. package/.claude/hooks-windows/play-tts-sapi.ps1 +3 -4
  11. package/.claude/hooks-windows/play-tts-soprano.ps1 +2 -3
  12. package/.claude/hooks-windows/play-tts-termux-ssh.ps1 +138 -0
  13. package/.claude/hooks-windows/play-tts.ps1 +14 -6
  14. package/.claude/hooks-windows/provider-manager.ps1 +16 -1
  15. package/CLAUDE.md +4 -0
  16. package/README.md +39 -9
  17. package/RELEASE_NOTES.md +78 -0
  18. package/bin/agent-vibes +1 -1
  19. package/bin/agentvibes-voice-browser.js +1 -1
  20. package/bin/bmad-speak.js +52 -0
  21. package/bin/mcp-server.js +1 -1
  22. package/bin/test-bmad-pr +1 -1
  23. package/package.json +1 -1
  24. package/setup-windows.ps1 +4 -4
  25. package/src/console/app.js +63 -12
  26. package/src/console/navigation.js +5 -2
  27. package/src/console/tabs/agents-tab.js +72 -76
  28. package/src/console/tabs/help-tab.js +107 -54
  29. package/src/console/tabs/install-tab.js +132 -56
  30. package/src/console/tabs/music-tab.js +1039 -1011
  31. package/src/console/tabs/placeholder-tab.js +27 -0
  32. package/src/console/tabs/readme-tab.js +9 -7
  33. package/src/console/tabs/receiver-tab.js +23 -12
  34. package/src/console/tabs/settings-tab.js +4001 -3783
  35. package/src/console/tabs/voices-tab.js +1680 -1653
  36. package/src/console/widgets/personality-picker.js +35 -7
  37. package/src/console/widgets/reverb-picker.js +9 -6
  38. package/src/console/widgets/track-picker.js +7 -2
  39. package/src/i18n/de.js +203 -0
  40. package/src/i18n/en.js +203 -0
  41. package/src/i18n/es.js +203 -0
  42. package/src/i18n/fr.js +203 -0
  43. package/src/i18n/hi.js +203 -0
  44. package/src/i18n/ja.js +203 -0
  45. package/src/i18n/ko.js +203 -0
  46. package/src/i18n/pt.js +203 -0
  47. package/src/i18n/strings.js +54 -0
  48. package/src/i18n/zh-CN.js +203 -0
  49. package/src/installer/language-screen.js +31 -0
  50. package/src/installer.js +79 -25
  51. package/src/services/language-service.js +47 -0
  52. package/src/utils/file-ownership-verifier.js +2 -2
  53. package/src/utils/provider-validator.js +9 -13
  54. package/.claude/hooks-windows/play-tts-windows-piper.ps1 +0 -209
  55. package/.claude/hooks-windows/play-tts-windows-sapi.ps1 +0 -108
@@ -1,209 +0,0 @@
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
- # Audio cache and voice config use project-local .claude
27
- $AudioDir = "$ClaudeDir\audio"
28
- # Try provider-specific file first, then generic tts-voice.txt (set by TUI)
29
- $VoiceFile = "$ClaudeDir\tts-voice-piper.txt"
30
- if (-not (Test-Path $VoiceFile)) {
31
- $VoiceFile = "$ClaudeDir\tts-voice.txt"
32
- }
33
-
34
- # Voices and Piper binary are global (shared across projects, ~100MB+)
35
- $UserClaudeDir = "$env:USERPROFILE\.claude"
36
- $VoicesDir = "$UserClaudeDir\piper-voices"
37
- # Try standard install location first, then fall back to PATH
38
- $PiperExe = "$env:LOCALAPPDATA\Programs\Piper\piper.exe"
39
- if (-not (Test-Path $PiperExe)) {
40
- $found = Get-Command piper.exe -ErrorAction SilentlyContinue
41
- if (-not $found) {
42
- # PATH may be stale (SSH sessions inherit minimal PATH); refresh from registry
43
- $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
44
- $found = Get-Command piper.exe -ErrorAction SilentlyContinue
45
- }
46
- if ($found) { $PiperExe = $found.Source }
47
- }
48
-
49
- # Ensure directories exist
50
- foreach ($dir in @($AudioDir, $VoicesDir)) {
51
- if (-not (Test-Path $dir)) {
52
- New-Item -ItemType Directory -Path $dir -Force | Out-Null
53
- }
54
- }
55
-
56
- # Check if Piper is installed
57
- if (-not (Test-Path $PiperExe)) {
58
- Write-Host "[ERROR] Piper not found at: $PiperExe" -ForegroundColor Red
59
- Write-Host "Run: .\setup-windows.ps1 to install Piper" -ForegroundColor Yellow
60
- exit 1
61
- }
62
-
63
- # Determine voice to use
64
- $VoiceName = ""
65
-
66
- if ($VoiceOverride) {
67
- $VoiceName = $VoiceOverride
68
- }
69
- elseif (Test-Path $VoiceFile) {
70
- $VoiceName = (Get-Content $VoiceFile -Raw).Trim()
71
- }
72
-
73
- # Strip display name suffix (e.g. "en_US-libritts-high::Bella-9" -> "en_US-libritts-high")
74
- # and extract speaker ID if present (works for both override and file)
75
- if ($VoiceName -match '::') {
76
- $parts = $VoiceName -split '::'
77
- $VoiceName = $parts[0]
78
- if ($parts.Length -ge 2 -and $parts[1] -match '-(\d+)$') {
79
- $env:PIPER_SPEAKER = $Matches[1]
80
- } else {
81
- Remove-Item env:PIPER_SPEAKER -ErrorAction SilentlyContinue
82
- }
83
- } else {
84
- # No multi-speaker syntax — clear any stale speaker env var
85
- Remove-Item env:PIPER_SPEAKER -ErrorAction SilentlyContinue
86
- }
87
-
88
- # Default voice if not specified
89
- # Prefer en_US-lessac-medium (bundled/commonly installed) over en_US-ryan-high
90
- if (-not $VoiceName) {
91
- $UserClaudePiperDir = "$env:USERPROFILE\.claude\piper-voices"
92
- if (Test-Path "$UserClaudePiperDir\en_US-lessac-medium.onnx") {
93
- $VoiceName = "en_US-lessac-medium"
94
- } elseif (Test-Path "$UserClaudePiperDir\en_US-ryan-high.onnx") {
95
- $VoiceName = "en_US-ryan-high"
96
- } else {
97
- # Fallback: use first available .onnx file, or default name for auto-download
98
- $firstVoice = Get-ChildItem -Path $UserClaudePiperDir -Filter "*.onnx" -ErrorAction SilentlyContinue | Select-Object -First 1
99
- if ($firstVoice) {
100
- $VoiceName = $firstVoice.BaseName
101
- } else {
102
- $VoiceName = "en_US-lessac-medium"
103
- }
104
- }
105
- }
106
-
107
- # Security: Validate voice name to prevent path traversal
108
- # Only allow alphanumeric, underscore, hyphen, and period
109
- if ($VoiceName -notmatch '^[a-zA-Z0-9_\-\.]+$') {
110
- Write-Host "[ERROR] Invalid voice name: $VoiceName" -ForegroundColor Red
111
- exit 1
112
- }
113
-
114
- # Resolve voice model path and validate it stays within VoicesDir
115
- $VoiceModelFile = [System.IO.Path]::GetFullPath("$VoicesDir\$VoiceName.onnx")
116
- $VoiceJsonFile = [System.IO.Path]::GetFullPath("$VoicesDir\$VoiceName.onnx.json")
117
- $ResolvedVoicesDir = [System.IO.Path]::GetFullPath($VoicesDir)
118
- if (-not $VoiceModelFile.StartsWith($ResolvedVoicesDir)) {
119
- Write-Host "[ERROR] Voice path outside voices directory" -ForegroundColor Red
120
- exit 1
121
- }
122
-
123
- # Check if voice model exists, download if missing
124
- if (-not (Test-Path $VoiceModelFile)) {
125
- Write-Host "[DOWNLOAD] Voice model: $VoiceName" -ForegroundColor Yellow
126
-
127
- # Try to download from Hugging Face
128
- # Voice name format: {lang}_{region}-{speaker}-{quality}
129
- # HF path format: {lang}/{lang}_{region}/{speaker}/{quality}/{voicename}.onnx
130
- try {
131
- # Parse voice name to build correct HF path
132
- # e.g. en_US-ryan-high -> en/en_US/ryan/high/en_US-ryan-high.onnx
133
- if ($VoiceName -match '^([a-z]{2})_([A-Z]{2})-([a-zA-Z0-9_]+)-([a-z]+)$') {
134
- $Lang = $Matches[1]
135
- $LangRegion = "$($Matches[1])_$($Matches[2])"
136
- $Speaker = $Matches[3]
137
- $Quality = $Matches[4]
138
- $HFBase = "https://huggingface.co/rhasspy/piper-voices/resolve/main/$Lang/$LangRegion/$Speaker/$Quality"
139
- } else {
140
- # Fallback for non-standard voice names
141
- $HFBase = "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/ryan/high"
142
- }
143
- $ModelUrl = "$HFBase/$VoiceName.onnx"
144
- $JsonUrl = "$HFBase/$VoiceName.onnx.json"
145
-
146
- Write-Host " Downloading model..." -ForegroundColor Cyan
147
- Invoke-WebRequest -Uri $ModelUrl -OutFile $VoiceModelFile -ErrorAction Stop
148
- Write-Host " Downloading config..." -ForegroundColor Cyan
149
- Invoke-WebRequest -Uri $JsonUrl -OutFile $VoiceJsonFile -ErrorAction Stop
150
- Write-Host "[OK] Voice model downloaded" -ForegroundColor Green
151
- }
152
- catch {
153
- Write-Host "[ERROR] Failed to download voice model: $_" -ForegroundColor Red
154
- Write-Host "Make sure you have internet connection" -ForegroundColor Yellow
155
- exit 1
156
- }
157
- }
158
-
159
- # Sanitize text for speech - strip shell metacharacters and PS special chars
160
- $Text = $Text -replace '\\', ' '
161
- $Text = $Text -replace '[{}<>|`~^$;"''()]', ''
162
- $Text = $Text -replace '\s+', ' '
163
- $Text = $Text.Trim()
164
-
165
- # Create audio file path
166
- $Timestamp = Get-Date -Format 'yyyyMMdd-HHmmss-ffff'
167
- $AudioFile = "$AudioDir\tts-$Timestamp.wav"
168
-
169
- # Synthesize with Piper
170
- try {
171
- Write-Host "[SYNTH] Synthesizing with Piper..." -ForegroundColor Cyan
172
-
173
- # Run Piper with text input
174
- # Add --speaker for multi-speaker models (e.g. libritts-high with speaker 9)
175
- $piperArgs = @("--model", $VoiceModelFile, "--output-file", $AudioFile)
176
- if ($env:PIPER_SPEAKER) {
177
- $piperArgs += @("--speaker", $env:PIPER_SPEAKER)
178
- }
179
- $Text | & $PiperExe @piperArgs 2>$null
180
-
181
- if (-not (Test-Path $AudioFile)) {
182
- Write-Host "[ERROR] Piper synthesis failed" -ForegroundColor Red
183
- exit 1
184
- }
185
-
186
- # Display results
187
- Write-Host "[OK] Saved to: $AudioFile" -ForegroundColor Green
188
- Write-Host "[VOICE] Voice used: $VoiceName (Piper)" -ForegroundColor Green
189
-
190
- # Play the audio using built-in Windows audio player (skip if AGENTVIBES_NO_PLAY is set)
191
- if (-not $env:AGENTVIBES_NO_PLAY) {
192
- $player = $null
193
- try {
194
- $player = New-Object System.Media.SoundPlayer $AudioFile
195
- $player.PlaySync()
196
- }
197
- catch {
198
- Write-Host "[WARNING] Could not play audio (SoundPlayer unavailable)" -ForegroundColor Yellow
199
- Write-Host "Audio saved to: $AudioFile" -ForegroundColor Gray
200
- }
201
- finally {
202
- if ($player) { $player.Dispose() }
203
- }
204
- }
205
- }
206
- catch {
207
- Write-Host "[ERROR] Error running Piper: $_" -ForegroundColor Red
208
- exit 1
209
- }
@@ -1,108 +0,0 @@
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 only dangerous shell metacharacters and SSML tags
67
- $Text = $Text -replace '\\', ' '
68
- $Text = $Text -replace '[{}<>|`~^;]', ''
69
- $Text = $Text -replace '&[a-zA-Z]+;', ''
70
- $Text = $Text -replace '\s+', ' '
71
- $Text = $Text.Trim()
72
-
73
- # Get actual voice name (after selection or default)
74
- $ActualVoice = $synth.Voice.Name
75
-
76
- # Create audio file path
77
- $Timestamp = Get-Date -Format 'yyyyMMdd-HHmmss-ffff'
78
- $AudioFile = "$AudioDir\tts-$Timestamp.wav"
79
-
80
- # Save to WAV file with proper resource cleanup
81
- $player = $null
82
- try {
83
- $synth.SetOutputToWaveFile($AudioFile)
84
- $synth.Speak($Text)
85
-
86
- # Display results
87
- Write-Host "[OK] Saved to: $AudioFile" -ForegroundColor Green
88
- Write-Host "[VOICE] Voice used: $ActualVoice (Windows SAPI)" -ForegroundColor Green
89
-
90
- # Play the audio using built-in Windows audio player (skip if AGENTVIBES_NO_PLAY is set)
91
- if (-not $env:AGENTVIBES_NO_PLAY) {
92
- try {
93
- $player = New-Object System.Media.SoundPlayer $AudioFile
94
- $player.PlaySync()
95
- }
96
- catch {
97
- Write-Host "[WARNING] Could not play audio (SoundPlayer unavailable)" -ForegroundColor Yellow
98
- }
99
- }
100
- }
101
- catch {
102
- Write-Host "[ERROR] Error synthesizing speech: $_" -ForegroundColor Red
103
- exit 1
104
- }
105
- finally {
106
- if ($synth) { $synth.Dispose() }
107
- if ($player) { $player.Dispose() }
108
- }