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,12 +1,12 @@
1
1
  {
2
2
  "backgroundMusic": {
3
3
  "track": "agent_vibes_cumbia_v1_loop.mp3",
4
- "enabled": false,
5
- "volume": 20
4
+ "enabled": true,
5
+ "volume": 10
6
6
  },
7
+ "voice": "en_US-libritts-high::Ella",
7
8
  "provider": "piper",
8
- "voice": "en_US-libritts-high::Evan-8",
9
9
  "effects": {
10
- "reverbPreset": "off"
10
+ "reverbPreset": "light"
11
11
  }
12
12
  }
@@ -1,3 +1,4 @@
1
+ bmad-agent-tech-writer||agent_vibes_arabic_v2_loop.mp3|0.70
1
2
  # AgentVibes Audio Effects Configuration|||
2
3
  # Format: AGENT_NAME|SOX_EFFECTS|BACKGROUND_FILE|BACKGROUND_VOLUME
3
4
  #|||
@@ -1 +1 @@
1
- off
1
+ light
@@ -1 +1 @@
1
- 20260327
1
+ 20260329
@@ -343,7 +343,7 @@ main() {
343
343
  if [[ "$_prof_vol" =~ ^[0-9]+$ ]]; then
344
344
  bg_volume=$(awk "BEGIN{printf \"%.2f\", ${_prof_vol}/100}")
345
345
  else
346
- bg_volume="0.70"
346
+ bg_volume="0.20"
347
347
  fi
348
348
  fi
349
349
  fi
@@ -177,6 +177,20 @@ if [[ -n "$AGENT_ID" ]] && [[ -f "$VOICE_MAP_FILE" ]]; then
177
177
  IFS='|' read -r PROFILE_VOICE PROFILE_PRETEXT PROFILE_REVERB PROFILE_PERSONALITY PROFILE_MUSIC_TRACK PROFILE_MUSIC_VOLUME PROFILE_MUSIC_ENABLED <<< "$_ALL_FIELDS"
178
178
  fi
179
179
 
180
+ # Read global background music volume as fallback (stored as 0.0-1.0, convert to 0-100 integer)
181
+ _BG_VOL_FILE="${CLAUDE_PROJECT_DIR:-$PROJECT_ROOT}/.claude/config/background-music-volume.txt"
182
+ if [[ ! -f "$_BG_VOL_FILE" ]]; then
183
+ _BG_VOL_FILE="$HOME/.claude/config/background-music-volume.txt"
184
+ fi
185
+ if [[ -f "$_BG_VOL_FILE" ]]; then
186
+ GLOBAL_BG_VOLUME=$(_BG_VOL_RAW=$(cat "$_BG_VOL_FILE") node -e "
187
+ const v = parseFloat(process.env._BG_VOL_RAW);
188
+ process.stdout.write(isNaN(v) ? '20' : String(Math.round(v * 100)));
189
+ " 2>/dev/null || echo "20")
190
+ else
191
+ GLOBAL_BG_VOLUME=20
192
+ fi
193
+
180
194
  # Fallback to bmad-voice-manager.sh if no profile voice found
181
195
  AGENT_VOICE="$PROFILE_VOICE"
182
196
  AGENT_INTRO="$PROFILE_PRETEXT"
@@ -203,7 +217,7 @@ if [[ -n "$PROFILE_REVERB" ]] || [[ -n "$PROFILE_PERSONALITY" ]] || [[ -n "$PROF
203
217
  # Write profile as JSON for reliable parsing downstream
204
218
  # SECURITY: Pass values via env vars to prevent shell injection
205
219
  _P_REVERB="$PROFILE_REVERB" _P_PERSONALITY="$PROFILE_PERSONALITY" \
206
- _P_MUSIC_TRACK="$PROFILE_MUSIC_TRACK" _P_MUSIC_VOL="${PROFILE_MUSIC_VOLUME:-70}" \
220
+ _P_MUSIC_TRACK="$PROFILE_MUSIC_TRACK" _P_MUSIC_VOL="${PROFILE_MUSIC_VOLUME:-$GLOBAL_BG_VOLUME}" \
207
221
  _P_MUSIC_ENABLED="$PROFILE_MUSIC_ENABLED" \
208
222
  _P_OUTFILE="$TEMP_PROFILE" node -e "
209
223
  const p = {};
@@ -211,7 +225,7 @@ if [[ -n "$PROFILE_REVERB" ]] || [[ -n "$PROFILE_PERSONALITY" ]] || [[ -n "$PROF
211
225
  if (process.env._P_PERSONALITY) p.personality = process.env._P_PERSONALITY;
212
226
  if (process.env._P_MUSIC_TRACK) p.backgroundMusic = {
213
227
  track: process.env._P_MUSIC_TRACK,
214
- volume: parseInt(process.env._P_MUSIC_VOL) || 70,
228
+ volume: parseInt(process.env._P_MUSIC_VOL) || 20,
215
229
  enabled: process.env._P_MUSIC_ENABLED === 'true'
216
230
  };
217
231
  require('fs').writeFileSync(process.env._P_OUTFILE, JSON.stringify(p), { mode: 0o600 });
@@ -0,0 +1,200 @@
1
+ #
2
+ # File: .claude/hooks-windows/bmad-speak.ps1
3
+ #
4
+ # AgentVibes BMAD Voice Integration - Windows
5
+ # Maps BMAD agent display names or agent IDs to voices and triggers TTS.
6
+ # Windows port of .claude/hooks/bmad-speak.sh
7
+ #
8
+ # Usage: bmad-speak.ps1 "Agent Name" "dialogue text"
9
+ # bmad-speak.ps1 "agent-id" "dialogue text"
10
+ #
11
+
12
+ param(
13
+ [Parameter(Mandatory = $true, Position = 0)]
14
+ [string]$AgentNameOrId,
15
+
16
+ [Parameter(Mandatory = $true, Position = 1)]
17
+ [string]$Dialogue
18
+ )
19
+
20
+ $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
21
+ $ClaudeDir = Split-Path -Parent $ScriptDir
22
+ $ProjectRoot = Split-Path -Parent $ClaudeDir
23
+
24
+ # When running as global script, prefer CLAUDE_PROJECT_DIR for project root
25
+ if ($env:CLAUDE_PROJECT_DIR -and (Test-Path "$env:CLAUDE_PROJECT_DIR\_bmad")) {
26
+ $ProjectRoot = $env:CLAUDE_PROJECT_DIR
27
+ }
28
+
29
+ # Strip markdown formatting — prevent SAPI/Piper from speaking asterisks literally
30
+ $Dialogue = $Dialogue -replace '\*\*', '' -replace '\*', '' -replace '`', ''
31
+ $Dialogue = $Dialogue -replace '\\!', '!' -replace '\\\$', '$'
32
+
33
+ # Check if party mode is disabled
34
+ $PartyModeDisabledFlag = Join-Path $ProjectRoot ".agentvibes\bmad\bmad-party-mode-disabled.flag"
35
+ if (Test-Path $PartyModeDisabledFlag) {
36
+ exit 0
37
+ }
38
+
39
+ # Check if BMAD is installed
40
+ $ManifestFile = Join-Path $ProjectRoot "_bmad\_config\agent-manifest.csv"
41
+ if (-not (Test-Path $ManifestFile)) {
42
+ exit 0
43
+ }
44
+
45
+ # ---------------------------------------------------------------------------
46
+ # Read bmad-voice-map.json for per-agent profile
47
+ # Prefer project-local voice map, fall back to global
48
+ $VoiceMapLocal = Join-Path $ProjectRoot ".agentvibes\bmad-voice-map.json"
49
+ $VoiceMapGlobal = Join-Path $env:USERPROFILE ".agentvibes\bmad-voice-map.json"
50
+ $VoiceMapFile = if (Test-Path $VoiceMapLocal) { $VoiceMapLocal } else { $VoiceMapGlobal }
51
+
52
+ $AgentVoice = ""
53
+ $AgentPersonality = ""
54
+ $AgentBgEnabled = $false
55
+ $AgentBgTrack = ""
56
+ $AgentId = $null
57
+
58
+ # Read global background music volume (stored as 0.0-1.0 float)
59
+ $_BgVolFile = Join-Path $ProjectRoot ".claude\config\background-music-volume.txt"
60
+ if (-not (Test-Path $_BgVolFile)) {
61
+ $_BgVolFile = Join-Path $env:USERPROFILE ".claude\config\background-music-volume.txt"
62
+ }
63
+ if (Test-Path $_BgVolFile) {
64
+ $_BgVolRaw = (Get-Content $_BgVolFile -Raw -ErrorAction SilentlyContinue).Trim()
65
+ $_BgVolParsed = 0.0
66
+ if ([double]::TryParse($_BgVolRaw, [System.Globalization.NumberStyles]::Float, [System.Globalization.CultureInfo]::InvariantCulture, [ref]$_BgVolParsed)) {
67
+ $AgentBgVolume = "{0:F2}" -f $_BgVolParsed
68
+ } else {
69
+ $AgentBgVolume = "0.20"
70
+ }
71
+ } else {
72
+ $AgentBgVolume = "0.20"
73
+ }
74
+
75
+ if (Test-Path $VoiceMapFile) {
76
+ try {
77
+ $VoiceMap = Get-Content $VoiceMapFile -Raw | ConvertFrom-Json
78
+
79
+ # Resolve agent ID: match canonical ID or display name prefix
80
+ $ManifestRows = Import-Csv $ManifestFile -Encoding UTF8
81
+ foreach ($row in $ManifestRows) {
82
+ $id = ($row.PSObject.Properties | Select-Object -First 1).Value -replace '^"|"$', ''
83
+ $display = ($row.PSObject.Properties | Select-Object -Skip 1 -First 1).Value -replace '^"|"$', ''
84
+ if ($id -ieq $AgentNameOrId -or $display -like "$AgentNameOrId*") {
85
+ $AgentId = $id
86
+ break
87
+ }
88
+ }
89
+
90
+ if ($AgentId -and $VoiceMap.agents.$AgentId) {
91
+ $Profile = $VoiceMap.agents.$AgentId
92
+ if ($Profile.voice) { $AgentVoice = $Profile.voice }
93
+ if ($Profile.personality) { $AgentPersonality = $Profile.personality }
94
+ if ($Profile.backgroundMusic) {
95
+ $AgentBgEnabled = [bool]$Profile.backgroundMusic.enabled
96
+ if ($Profile.backgroundMusic.track) { $AgentBgTrack = $Profile.backgroundMusic.track }
97
+ if ($null -ne $Profile.backgroundMusic.volume) {
98
+ # Voice map stores 0-100; audio-effects.cfg uses 0.0-1.0
99
+ $AgentBgVolume = "{0:F2}" -f ([double]$Profile.backgroundMusic.volume / 100.0)
100
+ }
101
+ }
102
+ }
103
+ } catch {
104
+ # Silently degrade — TTS will still play with global settings
105
+ }
106
+ }
107
+
108
+ # ---------------------------------------------------------------------------
109
+ # Locate play-tts.ps1 — prefer project-local, fall back to global
110
+ $PlayTtsLocal = Join-Path $ProjectRoot ".claude\hooks-windows\play-tts.ps1"
111
+ $PlayTtsGlobal = Join-Path $env:USERPROFILE ".claude\hooks-windows\play-tts.ps1"
112
+ $PlayTtsScript = if (Test-Path $PlayTtsLocal) { $PlayTtsLocal } else { $PlayTtsGlobal }
113
+
114
+ if (-not (Test-Path $PlayTtsScript)) {
115
+ exit 0
116
+ }
117
+
118
+ # ---------------------------------------------------------------------------
119
+ # Determine which .claude config dir play-tts.ps1 will read.
120
+ # play-tts.ps1 checks CLAUDE_PROJECT_DIR first — match that logic exactly.
121
+ $TtsClaudeDir = if ($env:CLAUDE_PROJECT_DIR -and (Test-Path "$env:CLAUDE_PROJECT_DIR\.claude")) {
122
+ "$env:CLAUDE_PROJECT_DIR\.claude"
123
+ } else {
124
+ $ClaudeDir # ~/.claude (this script's own ClaudeDir)
125
+ }
126
+ $TtsConfigDir = Join-Path $TtsClaudeDir "config"
127
+
128
+ # ---------------------------------------------------------------------------
129
+ # Apply per-agent personality override if set
130
+ $OldPersonality = ""
131
+ $PersonalityFile = Join-Path $TtsClaudeDir "config\personality.txt"
132
+ if ($AgentPersonality -and (Test-Path (Split-Path $PersonalityFile -Parent))) {
133
+ if (Test-Path $PersonalityFile) {
134
+ $OldPersonality = (Get-Content $PersonalityFile -Raw).Trim()
135
+ }
136
+ Set-Content $PersonalityFile $AgentPersonality -NoNewline
137
+ }
138
+
139
+ # ---------------------------------------------------------------------------
140
+ # Temporarily patch background music config for this agent.
141
+ # The caller (bmad-party-speak.ps1) holds a named mutex so only one speak
142
+ # call runs at a time — these file patches are safe from concurrent clobber.
143
+ $BgEnabledFile = Join-Path $TtsConfigDir "background-music-enabled.txt"
144
+ $AudioEffectsCfg = Join-Path $TtsConfigDir "audio-effects.cfg"
145
+ $OldBgEnabled = $null
146
+ $TempCfgLine = ""
147
+
148
+ if ($AgentBgEnabled -and $AgentBgTrack) {
149
+ # Save + enable background music
150
+ if (Test-Path $BgEnabledFile) {
151
+ $OldBgEnabled = (Get-Content $BgEnabledFile -Raw -ErrorAction SilentlyContinue).Trim()
152
+ }
153
+ Set-Content $BgEnabledFile "true" -NoNewline
154
+
155
+ # Prepend agent line to audio-effects.cfg so play-tts.ps1 finds it first
156
+ # Format: AGENT_NAME|SOX_EFFECTS|BACKGROUND_FILE|BACKGROUND_VOLUME
157
+ $TempCfgLine = "${AgentId}||${AgentBgTrack}|${AgentBgVolume}"
158
+ $env:AGENTVIBES_AGENT_NAME = $AgentId
159
+ $existingCfg = if (Test-Path $AudioEffectsCfg) {
160
+ Get-Content $AudioEffectsCfg -Raw -ErrorAction SilentlyContinue
161
+ } else { "" }
162
+ Set-Content $AudioEffectsCfg "${TempCfgLine}`n${existingCfg}" -NoNewline
163
+ }
164
+
165
+ try {
166
+ # Speak with agent's voice (or global voice if none configured)
167
+ if ($AgentVoice) {
168
+ & powershell -NoProfile -ExecutionPolicy Bypass -File $PlayTtsScript $Dialogue $AgentVoice
169
+ } else {
170
+ & powershell -NoProfile -ExecutionPolicy Bypass -File $PlayTtsScript $Dialogue
171
+ }
172
+ } finally {
173
+ # Restore personality
174
+ if ($AgentPersonality -and $PersonalityFile) {
175
+ if ($OldPersonality) {
176
+ Set-Content $PersonalityFile $OldPersonality -NoNewline
177
+ } elseif (Test-Path $PersonalityFile) {
178
+ Remove-Item $PersonalityFile -Force -ErrorAction SilentlyContinue
179
+ }
180
+ }
181
+
182
+ # Restore background music config
183
+ if ($AgentBgEnabled -and $AgentBgTrack) {
184
+ if ($null -ne $OldBgEnabled) {
185
+ Set-Content $BgEnabledFile $OldBgEnabled -NoNewline
186
+ } elseif (Test-Path $BgEnabledFile) {
187
+ Remove-Item $BgEnabledFile -Force -ErrorAction SilentlyContinue
188
+ }
189
+
190
+ # Remove the prepended agent line from audio-effects.cfg
191
+ if (Test-Path $AudioEffectsCfg) {
192
+ $cfgRaw = Get-Content $AudioEffectsCfg -Raw -ErrorAction SilentlyContinue
193
+ $escaped = [regex]::Escape($TempCfgLine)
194
+ $cfgRaw = $cfgRaw -replace "^${escaped}\r?\n?", ""
195
+ Set-Content $AudioEffectsCfg $cfgRaw -NoNewline
196
+ }
197
+
198
+ $env:AGENTVIBES_AGENT_NAME = ""
199
+ }
200
+ }
@@ -1,5 +1,5 @@
1
1
  #
2
- # File: .claude/hooks-windows/play-tts-windows-piper.ps1
2
+ # File: .claude/hooks-windows/play-tts-piper.ps1
3
3
  #
4
4
  # AgentVibes - Windows Piper TTS Provider
5
5
  # High-quality neural TTS using Piper.exe
@@ -162,9 +162,8 @@ $Text = $Text -replace '[{}<>|`~^;]', ''
162
162
  $Text = $Text -replace '\s+', ' '
163
163
  $Text = $Text.Trim()
164
164
 
165
- # Create audio file path
166
- $Timestamp = Get-Date -Format 'yyyyMMdd-HHmmss-ffff'
167
- $AudioFile = "$AudioDir\tts-$Timestamp.wav"
165
+ # Create audio file path — SECURITY: use random name instead of predictable timestamp (#130)
166
+ $AudioFile = "$AudioDir\tts-$([System.IO.Path]::GetRandomFileName() -replace '\..*').wav"
168
167
 
169
168
  # Synthesize with Piper
170
169
  try {
@@ -1,5 +1,5 @@
1
1
  #
2
- # File: .claude/hooks-windows/play-tts-windows-sapi.ps1
2
+ # File: .claude/hooks-windows/play-tts-sapi.ps1
3
3
  #
4
4
  # AgentVibes - Windows SAPI TTS Provider (Zero Dependencies)
5
5
  # Uses built-in Windows System.Speech API
@@ -73,9 +73,8 @@ $Text = $Text.Trim()
73
73
  # Get actual voice name (after selection or default)
74
74
  $ActualVoice = $synth.Voice.Name
75
75
 
76
- # Create audio file path
77
- $Timestamp = Get-Date -Format 'yyyyMMdd-HHmmss-ffff'
78
- $AudioFile = "$AudioDir\tts-$Timestamp.wav"
76
+ # Create audio file path — SECURITY: use random name instead of predictable timestamp (#130)
77
+ $AudioFile = "$AudioDir\tts-$([System.IO.Path]::GetRandomFileName() -replace '\..*').wav"
79
78
 
80
79
  # Save to WAV file with proper resource cleanup
81
80
  $player = $null
@@ -53,9 +53,8 @@ if (-not (Test-Path $AudioDir)) {
53
53
  New-Item -ItemType Directory -Path $AudioDir -Force | Out-Null
54
54
  }
55
55
 
56
- $timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
57
- $random = Get-Random -Maximum 9999
58
- $TempFile = Join-Path $AudioDir "tts-$timestamp-$random.wav"
56
+ # SECURITY: use random name instead of predictable timestamp (#130)
57
+ $TempFile = Join-Path $AudioDir "tts-$([System.IO.Path]::GetRandomFileName() -replace '\..*').wav"
59
58
 
60
59
  # Check WebUI server
61
60
  function Test-WebUI {
@@ -0,0 +1,138 @@
1
+ #
2
+ # File: .claude/hooks-windows/play-tts-termux-ssh.ps1
3
+ #
4
+ # AgentVibes - Finally, your AI Agents can Talk Back! Text-to-Speech WITH personality for AI Assistants!
5
+ # Website: https://agentvibes.org
6
+ # Repository: https://github.com/paulpreibisch/AgentVibes
7
+ #
8
+ # Co-created by Paul Preibisch with Claude AI
9
+ # Copyright (c) 2025 Paul Preibisch
10
+ #
11
+ # Licensed under the Apache License, Version 2.0 (the "License");
12
+ # you may not use this file except in compliance with the License.
13
+ # You may obtain a copy of the License at
14
+ #
15
+ # http://www.apache.org/licenses/LICENSE-2.0
16
+ #
17
+ # ---
18
+ #
19
+ # @fileoverview Termux SSH TTS Provider (Windows) - Android TTS via SSH tunnel
20
+ # @context Enables TTS output on Android devices when connected via SSH from Windows
21
+ # @architecture SSH-based remote TTS invocation using termux-tts-speak on Android
22
+ # @dependencies ssh.exe (OpenSSH for Windows), termux-tts-speak (on Android), termux-api (on Android)
23
+ # @entrypoints Called by play-tts.ps1 router when provider=termux-ssh
24
+ # @patterns Remote TTS invocation, SSH host alias configuration, graceful fallback
25
+ # @related play-tts.ps1, provider-manager.ps1
26
+ #
27
+ # SETUP INSTRUCTIONS:
28
+ # ===================
29
+ # 1. On Android device (Termux):
30
+ # - Install: pkg install termux-api openssh
31
+ # - Install Termux:API app from F-Droid or Google Play
32
+ # - Start SSH server: sshd
33
+ #
34
+ # 2. On Windows:
35
+ # - Add to %USERPROFILE%\.ssh\config:
36
+ # Host android
37
+ # HostName <your-android-ip>
38
+ # User <your-termux-username>
39
+ # Port 8022
40
+ # IdentityFile ~/.ssh/id_rsa
41
+ #
42
+ # 3. Configure AgentVibes:
43
+ # - echo android > %USERPROFILE%\.claude\termux-ssh-host.txt
44
+ # OR
45
+ # - echo android > .claude\termux-ssh-host.txt (project-local)
46
+ #
47
+ # 4. Set provider:
48
+ # - echo termux-ssh > %USERPROFILE%\.claude\tts-provider.txt
49
+ #
50
+
51
+ param(
52
+ [Parameter(Mandatory = $true, Position = 0)]
53
+ [string]$Text,
54
+
55
+ [Parameter(Mandatory = $false, Position = 1)]
56
+ [string]$VoiceOverride # Not used for termux-ssh, kept for interface compatibility
57
+ )
58
+
59
+ # Resolve ClaudeDir (project-local preferred, fallback to global)
60
+ $ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
61
+ $ProjectClaudeDir = Split-Path -Parent $ScriptPath
62
+ if (Test-Path (Join-Path $ProjectClaudeDir "config")) {
63
+ $ClaudeDir = $ProjectClaudeDir
64
+ } else {
65
+ $ClaudeDir = "$env:USERPROFILE\.claude"
66
+ }
67
+
68
+ # @function Get-SshHost
69
+ # @intent Determine SSH host alias for Android device
70
+ # @why Allows users to configure their own SSH connection without hardcoded values
71
+ # Priority: env var > project config > script-dir config > global config
72
+ function Get-SshHost {
73
+ if ($env:TERMUX_SSH_HOST) { return $env:TERMUX_SSH_HOST }
74
+
75
+ $projectFile = Join-Path $ClaudeDir "termux-ssh-host.txt"
76
+ if (Test-Path $projectFile) {
77
+ $val = (Get-Content $projectFile -Raw).Trim()
78
+ if ($val) { return $val }
79
+ }
80
+
81
+ $globalFile = "$env:USERPROFILE\.claude\termux-ssh-host.txt"
82
+ if (Test-Path $globalFile) {
83
+ $val = (Get-Content $globalFile -Raw).Trim()
84
+ if ($val) { return $val }
85
+ }
86
+
87
+ return ""
88
+ }
89
+
90
+ # @function Test-SshConnection
91
+ # @intent Quick reachability check before sending TTS
92
+ # @why Prevents long hangs if Android is offline
93
+ function Test-SshConnection {
94
+ param([string]$SshTarget)
95
+ try {
96
+ $null = ssh -o ConnectTimeout=2 -o BatchMode=yes $SshTarget "echo ok" 2>&1
97
+ return $LASTEXITCODE -eq 0
98
+ } catch {
99
+ return $false
100
+ }
101
+ }
102
+
103
+ # --- Main ---
104
+
105
+ if (-not $Text) {
106
+ Write-Host "[ERROR] No text provided for TTS" -ForegroundColor Red
107
+ exit 1
108
+ }
109
+
110
+ $SshAlias = Get-SshHost
111
+
112
+ if (-not $SshAlias) {
113
+ Write-Host "[ERROR] Termux SSH provider not configured" -ForegroundColor Red
114
+ Write-Host " Set SSH host alias in one of:" -ForegroundColor Yellow
115
+ Write-Host " Environment: `$env:TERMUX_SSH_HOST = 'android'" -ForegroundColor Yellow
116
+ Write-Host " Global: echo android > `"$env:USERPROFILE\.claude\termux-ssh-host.txt`"" -ForegroundColor Yellow
117
+ Write-Host " Project: echo android > .claude\termux-ssh-host.txt" -ForegroundColor Yellow
118
+ exit 1
119
+ }
120
+
121
+ if (-not (Test-SshConnection -SshTarget $SshAlias)) {
122
+ Write-Host "[WARNING] Cannot connect to SSH host '$SshAlias' - Android offline?" -ForegroundColor Yellow
123
+ exit 1
124
+ }
125
+
126
+ # Escape single quotes for safe shell transmission
127
+ $SafeText = $Text -replace "'", "'\'''"
128
+
129
+ # Send TTS to Android asynchronously (audio plays on device)
130
+ $job = Start-Job -ScriptBlock {
131
+ param($alias, $text)
132
+ ssh -o ConnectTimeout=5 $alias "termux-tts-speak '$text'" 2>&1
133
+ } -ArgumentList $SshAlias, $SafeText
134
+
135
+ Write-Host "[OK] TTS sent to Android ($SshAlias) via SSH" -ForegroundColor Green
136
+
137
+ # Output empty string — audio plays on Android, not locally
138
+ Write-Output ""
@@ -14,15 +14,20 @@ param(
14
14
  )
15
15
 
16
16
  # Configuration paths
17
- # First check if we're running from a project directory with .claude
17
+ # Priority: CLAUDE_PROJECT_DIR env var script's parent project user profile
18
18
  $ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
19
- $ProjectClaudeDir = Join-Path (Split-Path -Parent (Split-Path -Parent $ScriptPath)) ".claude"
20
19
 
21
- # Use project .claude if running from there, otherwise use user profile
22
- if (Test-Path $ProjectClaudeDir) {
23
- $ClaudeDir = $ProjectClaudeDir
20
+ if ($env:CLAUDE_PROJECT_DIR -and (Test-Path "$env:CLAUDE_PROJECT_DIR\.claude")) {
21
+ $ClaudeDir = "$env:CLAUDE_PROJECT_DIR\.claude"
24
22
  } else {
25
- $ClaudeDir = "$env:USERPROFILE\.claude"
23
+ $PackageClaudeDir = Join-Path (Split-Path -Parent (Split-Path -Parent $ScriptPath)) ".claude"
24
+ if (Test-Path "$env:USERPROFILE\.claude\tts-provider.txt") {
25
+ $ClaudeDir = "$env:USERPROFILE\.claude"
26
+ } elseif (Test-Path $PackageClaudeDir) {
27
+ $ClaudeDir = $PackageClaudeDir
28
+ } else {
29
+ $ClaudeDir = "$env:USERPROFILE\.claude"
30
+ }
26
31
  }
27
32
 
28
33
  $HooksDir = "$ClaudeDir\hooks-windows"
@@ -56,6 +61,9 @@ switch ($ActiveProvider) {
56
61
  "soprano" {
57
62
  $ProviderScript = "$HooksDir\play-tts-soprano.ps1"
58
63
  }
64
+ "termux-ssh" {
65
+ $ProviderScript = "$HooksDir\play-tts-termux-ssh.ps1"
66
+ }
59
67
  default {
60
68
  Write-Host "[ERROR] Unknown provider: $ActiveProvider" -ForegroundColor Red
61
69
  Write-Host "Use: .\provider-manager.ps1 list" -ForegroundColor Yellow
@@ -22,7 +22,7 @@ if (Test-Path (Join-Path $ProjectClaudeDir "config")) {
22
22
  $ClaudeDir = "$env:USERPROFILE\.claude"
23
23
  }
24
24
  $ProviderFile = "$ClaudeDir\tts-provider.txt"
25
- $ValidProviders = @('piper', 'sapi', 'soprano')
25
+ $ValidProviders = @('piper', 'sapi', 'soprano', 'termux-ssh')
26
26
  # Backwards compat: normalize old names
27
27
  if ($Provider -eq 'windows-piper') { $Provider = 'piper' }
28
28
  if ($Provider -eq 'windows-sapi') { $Provider = 'sapi' }
@@ -91,6 +91,21 @@ function Get-AvailableProviders {
91
91
  installed = $sopranoInstalled
92
92
  }
93
93
 
94
+ # Check if termux-ssh is configured (ssh.exe available + host configured)
95
+ $termuxSshConfigured = $false
96
+ $sshExe = Get-Command ssh.exe -ErrorAction SilentlyContinue
97
+ if ($sshExe) {
98
+ $hostFile = "$ClaudeDir\termux-ssh-host.txt"
99
+ $globalHostFile = "$env:USERPROFILE\.claude\termux-ssh-host.txt"
100
+ $termuxSshConfigured = (Test-Path $hostFile) -or (Test-Path $globalHostFile) -or ($env:TERMUX_SSH_HOST -ne "")
101
+ }
102
+
103
+ $available += @{
104
+ name = "termux-ssh"
105
+ description = "Android Termux SSH TTS (plays on Android via termux-tts-speak)"
106
+ installed = $termuxSshConfigured
107
+ }
108
+
94
109
  return $available
95
110
  }
96
111
 
package/CLAUDE.md CHANGED
@@ -8,6 +8,10 @@
8
8
 
9
9
  AgentVibes is a Text-to-Speech system for AI assistants with personality support.
10
10
 
11
+ ### Time Estimates
12
+
13
+ Always estimate in **AI time** — Paul does not write code, that's Claude's job. "30 minutes" means 30 human minutes; the AI equivalent is seconds to a few minutes. When giving estimates, say things like "~10 minutes of AI work" or "quick — a few minutes tops."
14
+
11
15
  ### Project Uses BMAD Methodology
12
16
 
13
17
  This project follows **BMAD (BMM - Business Model Methodology)** for all story development:
package/README.md CHANGED
@@ -4,14 +4,14 @@
4
4
  >
5
5
  > 🌐 **[agentvibes.org](https://agentvibes.org)**
6
6
  >
7
- > Professional text-to-speech for **Claude Code**, **Claude Desktop**, **Warp Terminal**, and **OpenClaw** - **Soprano** (Neural), **Piper TTS** (Free!), **macOS Say** (Built-in!), or **Windows SAPI** (Zero Setup!)
7
+ > Professional text-to-speech for **Claude Code**, **Claude Desktop**, and **OpenClaw** - **Soprano** (Neural), **Piper TTS** (Free!), **macOS Say** (Built-in!), or **Windows SAPI** (Zero Setup!)
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/agentvibes)](https://www.npmjs.com/package/agentvibes)
10
10
  [![Test Suite](https://github.com/paulpreibisch/AgentVibes/actions/workflows/test.yml/badge.svg)](https://github.com/paulpreibisch/AgentVibes/actions/workflows/test.yml)
11
11
  [![Publish](https://github.com/paulpreibisch/AgentVibes/actions/workflows/publish.yml/badge.svg)](https://github.com/paulpreibisch/AgentVibes/actions/workflows/publish.yml)
12
12
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
13
13
 
14
- **Author**: Paul Preibisch ([@997Fire](https://x.com/997Fire)) | **Version**: v4.4
14
+ **Author**: Paul Preibisch ([@997Fire](https://x.com/997Fire)) | **Version**: v4.5
15
15
 
16
16
  ---
17
17
 
@@ -36,11 +36,41 @@
36
36
 
37
37
  **AgentVibes adds lively voice narration to your Claude AI sessions!**
38
38
 
39
- Whether you're coding in Claude Code, chatting in Claude Desktop, using Warp Terminal, or running OpenClaw - AgentVibes brings AI to life with professional voices and personalities.
39
+ Whether you're coding in Claude Code, chatting in Claude Desktop, or running OpenClaw AgentVibes brings AI to life with professional voices and personalities.
40
40
 
41
41
  ---
42
42
 
43
- ## 🌟 NEW IN v4.4Full Platform Parity Release
43
+ ## 🌟 NEW IN v4.5"Speak Every Language" Release
44
+
45
+ ### 🌍 Multilingual TUI — 9 Languages
46
+
47
+ Every screen, button, and label in `npx agentvibes` is now fully translated:
48
+
49
+ - **English, Spanish, French, German, Portuguese, Japanese, Korean, Chinese (Simplified), Italian**
50
+ - Language selection on first launch — pick your language before anything else
51
+ - Language sub-tab in Settings — switch live, no restart needed
52
+ - All tab labels, buttons, footer hints, status messages, and BMAD/Receiver tabs translated
53
+ - Per-language i18n files (`src/i18n/en.js`, `es.js`, `fr.js`, ...) with English fallback
54
+
55
+ ### 🪟 Windows Security Hardening
56
+
57
+ - **Unpredictable temp files** — `randomUUID()` replaces `Date.now()` in all temp filenames (JS + PowerShell)
58
+ - **No shell injection** — `spawnSync` replaces `execSync(..., { shell: true })` for `which` lookups
59
+ - **Smart music player detection** — `detectMp3Player()` replaces hardcoded `ffplay` on Windows
60
+ - **Boolean fix** — `isWindowsTerminal` now returns `true/false`, not the `WT_SESSION` UUID string
61
+
62
+ ### 🎙️ Cross-Platform BMAD Speak
63
+
64
+ BMAD (Build More Architect Dreams) is an AI multi-agent framework where specialized agents — Architect, PM, Developer, QA, and Analyst — collaborate to build software. With this release, every agent in a BMAD party mode session now speaks aloud with their own unique voice, personality, and music on Windows — making each role instantly recognizable.
65
+
66
+ - `bmad-speak.js` — cross-platform entry point; auto-routes to PowerShell on Windows or bash on Mac/Linux
67
+ - `bmad-speak.ps1` — native Windows BMAD speak with per-agent personality routing
68
+
69
+ ### 🧪 600 Tests, Zero Failures
70
+
71
+ ---
72
+
73
+ ## 🌟 v4.4 — Full Platform Parity Release
44
74
 
45
75
  ### 🪟 Windows MCP Parity — 27/27 Tools Working
46
76
 
@@ -294,7 +324,7 @@ Configure via: `npx agentvibes` → Music tab
294
324
  - 🎭 **Multi-Provider Support** - Soprano (neural), Piper TTS (50+ free voices), macOS Say (100+ built-in), or Windows SAPI
295
325
  - 🎙️ **27+ Professional AI Voices** - Character voices, accents, and unique personalities
296
326
  - 🎙️ **Verbosity Control** - Choose how much Claude speaks (LOW, MEDIUM, HIGH)
297
- - 🎙️ **AgentVibes MCP** - Natural language control ("Switch to Aria voice") for Claude Code, Desktop & Warp
327
+ - 🎙️ **AgentVibes MCP** - Natural language control ("Switch to Aria voice") for Claude Code & Desktop
298
328
  - 🔊 **SSH Audio Optimization** - Auto-detects remote sessions and eliminates static (VS Code Remote SSH, cloud dev)
299
329
 
300
330
  **🎭 Personalization:**
@@ -345,7 +375,7 @@ All 50+ Piper voices AgentVibes provides are sourced from Hugging Face's open-so
345
375
  ### AgentVibes MCP (Natural Language Control)
346
376
  - [🎙️ AgentVibes MCP Overview](#%EF%B8%8F-agentvibes-mcp) - **Easiest way** - Natural language commands
347
377
  - [For Claude Desktop](docs/mcp-setup.md#for-claude-desktop) - Windows/WSL setup, Python requirements
348
- - [For Warp Terminal](docs/mcp-setup.md#for-warp-terminal) - Warp configuration
378
+
349
379
  - [For Claude Code](docs/mcp-setup.md#for-claude-code) - Project-specific setup
350
380
 
351
381
  ### Core Features
@@ -473,9 +503,9 @@ We've now enhanced this capability by adding an MCP (Model Context Protocol) ser
473
503
 
474
504
  Setting it up is straightforward: just add the MCP server to your Claude Code configuration files.
475
505
 
476
- But the convenience doesn't stop there. With the MCP server in place, Claude Desktop can now use Agent Vibes too! We've even tested it successfully with Warp, an AI assistant that helps you navigate Windows and other operating systems.
506
+ But the convenience doesn't stop there. With the MCP server in place, Claude Desktop can now use Agent Vibes too!
477
507
 
478
- We're thrilled about this expansion because it means Claude Desktop and Warp can finally talk back as well!
508
+ We're thrilled about this expansion because it means Claude Desktop can finally talk back as well!
479
509
 
480
510
  If you decide to use the MCP server on Claude Desktop, after configuration, give Claude Desktop this command: "every time i give you a command, speak the acknowledgement using agentvibes and the confirmation about what you completed, when done"—and watch the magic happen!
481
511
 
@@ -483,7 +513,7 @@ If you decide to use the MCP server on Claude Desktop, after configuration, give
483
513
 
484
514
  Just say "Switch to Aria voice" or "Speak in Spanish" instead of typing commands.
485
515
 
486
- **Works in:** Claude Desktop, Claude Code, Warp Terminal
516
+ **Works in:** Claude Desktop, Claude Code
487
517
 
488
518
  **[→ View Complete MCP Setup Guide](docs/mcp-setup.md)** - Full setup for all platforms, configuration examples, available tools, and MCP vs slash commands comparison
489
519