agentvibes 5.12.0 โ†’ 5.13.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.
Files changed (73) hide show
  1. package/.agentvibes/install-manifest.json +111 -91
  2. package/.claude/commands/agent-vibes/commands.json +0 -20
  3. package/.claude/commands/agent-vibes/unmute.md +6 -2
  4. package/.claude/config/audio-effects.cfg +6 -6
  5. package/.claude/github-star-reminder.txt +1 -1
  6. package/.claude/hooks/agentvibes-session-id.sh +69 -0
  7. package/.claude/hooks/bmad-party-speak.sh +20 -4
  8. package/.claude/hooks/bmad-speak.sh +60 -2
  9. package/.claude/hooks/bmad-tts-injector.sh +20 -1
  10. package/.claude/hooks/bmad-voice-manager.sh +25 -3
  11. package/.claude/hooks/clawdbot-receiver-SECURE.sh +21 -2
  12. package/.claude/hooks/clawdbot-receiver.sh +19 -1
  13. package/.claude/hooks/elevenlabs-voices.sh +62 -0
  14. package/.claude/hooks/kokoro-installer.sh +20 -10
  15. package/.claude/hooks/language-manager.sh +10 -3
  16. package/.claude/hooks/party-set-room.sh +71 -0
  17. package/.claude/hooks/party-stage-roster.py +328 -0
  18. package/.claude/hooks/personality-manager.sh +19 -2
  19. package/.claude/hooks/piper-voice-manager.sh +3 -2
  20. package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +24 -5
  21. package/.claude/hooks/play-tts-elevenlabs.sh +38 -118
  22. package/.claude/hooks/play-tts-kokoro.sh +33 -6
  23. package/.claude/hooks/play-tts-soprano.sh +3 -2
  24. package/.claude/hooks/play-tts-ssh-remote.sh +37 -29
  25. package/.claude/hooks/play-tts-termux-ssh.sh +5 -4
  26. package/.claude/hooks/play-tts.sh +66 -61
  27. package/.claude/hooks/provider-catalog.json +352 -0
  28. package/.claude/hooks/provider-catalog.sh +161 -0
  29. package/.claude/hooks/provider-commands.sh +2 -1
  30. package/.claude/hooks/provider-manager.sh +47 -9
  31. package/.claude/hooks/python-resolver.sh +117 -0
  32. package/.claude/hooks/session-id.sh +56 -0
  33. package/.claude/hooks/session-start-tts.sh +39 -0
  34. package/.claude/hooks/speed-manager.sh +1 -1
  35. package/.claude/hooks/translate-manager.sh +3 -2
  36. package/.claude/hooks/translator.py +1 -1
  37. package/.claude/hooks/voice-manager.sh +242 -10
  38. package/.claude/hooks-windows/language-manager.ps1 +7 -1
  39. package/.claude/hooks-windows/personality-manager.ps1 +16 -1
  40. package/.claude/hooks-windows/play-tts-kokoro.ps1 +20 -4
  41. package/.claude/hooks-windows/play-tts.ps1 +32 -3
  42. package/.claude/hooks-windows/provider-catalog.ps1 +140 -0
  43. package/.claude/hooks-windows/provider-manager.ps1 +63 -8
  44. package/.claude/hooks-windows/tts-watcher.ps1 +33 -12
  45. package/.claude/hooks-windows/voice-manager-windows.ps1 +49 -0
  46. package/.mcp.json +0 -7
  47. package/README.md +12 -3
  48. package/RELEASE_NOTES.md +61 -0
  49. package/mcp-server/server.py +146 -49
  50. package/mcp-server/test_mcp_correctness.py +20 -2
  51. package/mcp-server/test_windows_script_parity.py +0 -2
  52. package/package.json +2 -2
  53. package/src/cli/list-voices.js +218 -114
  54. package/src/console/bling.js +71 -0
  55. package/src/console/music-preview.js +79 -0
  56. package/src/console/tabs/music-tab.js +16 -39
  57. package/src/console/tabs/settings-tab.js +195 -13
  58. package/src/console/tabs/setup-tab.js +9 -34
  59. package/src/console/tabs/voices-tab.js +83 -14
  60. package/src/console/widgets/track-picker.js +82 -0
  61. package/src/installer.js +136 -18
  62. package/src/services/provider-catalog.js +412 -0
  63. package/src/services/provider-voice-catalog.js +52 -73
  64. package/src/services/tts-engine-service.js +29 -0
  65. package/src/utils/provider-validator.js +62 -12
  66. package/.claude/commands/agent-vibes/language.md +0 -23
  67. package/.claude/commands/agent-vibes/learn.md +0 -67
  68. package/.claude/commands/agent-vibes/replay-target.md +0 -14
  69. package/.claude/commands/agent-vibes/target-voice.md +0 -26
  70. package/.claude/commands/agent-vibes/target.md +0 -30
  71. package/.claude/hooks/learn-manager.sh +0 -492
  72. package/.claude/hooks/replay-target-audio.sh +0 -95
  73. package/.claude/hooks-windows/learn-manager.ps1 +0 -241
@@ -27,6 +27,20 @@ $ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
27
27
  $ProjectClaudeDir = Join-Path (Split-Path -Parent (Split-Path -Parent $ScriptPath)) ".claude"
28
28
  if (Test-Path $ProjectClaudeDir) { $ClaudeDir = $ProjectClaudeDir } else { $ClaudeDir = "$env:USERPROFILE\.claude" }
29
29
 
30
+ # --- Default voice from the generated Provider Catalog (SSOT) -----------------
31
+ # FAIL-SAFE: keep the literal fallback for installed-tree skew (mirrors play-tts.sh
32
+ # PLAN_OK). The synth-time shape check below stays LENIENT (design ยง3.3) so a voice
33
+ # from a newer kokoro model still degrades audibly instead of bricking.
34
+ $KokoroDefault = "af_heart"
35
+ $CatalogPs1 = Join-Path $ScriptPath 'provider-catalog.ps1'
36
+ if (Test-Path $CatalogPs1) {
37
+ . $CatalogPs1
38
+ if (Get-Command Get-CatalogDefaultVoice -ErrorAction SilentlyContinue) {
39
+ $d = Get-CatalogDefaultVoice -Provider 'kokoro'
40
+ if ($d) { $KokoroDefault = $d }
41
+ }
42
+ }
43
+
30
44
  $AudioDir = "$ClaudeDir\audio"
31
45
  $ConfigDir = "$ClaudeDir\config"
32
46
  if (-not (Test-Path $AudioDir)) { New-Item -ItemType Directory -Path $AudioDir -Force | Out-Null }
@@ -70,13 +84,15 @@ if ($VoiceOverride) {
70
84
  }
71
85
  # Kokoro voice ids look like af_heart, am_michael, bf_emma. Strip any ::display suffix.
72
86
  if ($VoiceName -match '::') { $VoiceName = ($VoiceName -split '::')[0] }
73
- if (-not $VoiceName) { $VoiceName = "af_heart" }
87
+ if (-not $VoiceName) { $VoiceName = $KokoroDefault }
74
88
 
75
89
  # Security: validate voice id (lowercase prefix + underscore + name).
76
- # Rejects path-traversal / injection before it reaches the command line.
90
+ # LENIENT shape check (design ยง3.3) โ€” rejects path-traversal / injection but lets
91
+ # an unknown-but-shape-valid voice from a newer kokoro model through; falls back
92
+ # to the catalog default only on a shape violation.
77
93
  if ($VoiceName -notmatch '^[a-z]{2}_[a-z0-9_]+$') {
78
- Write-Host "[WARNING] Invalid Kokoro voice '$VoiceName' - falling back to af_heart" -ForegroundColor Yellow
79
- $VoiceName = "af_heart"
94
+ Write-Host "[WARNING] Invalid Kokoro voice '$VoiceName' - falling back to $KokoroDefault" -ForegroundColor Yellow
95
+ $VoiceName = $KokoroDefault
80
96
  }
81
97
 
82
98
  # --- Optional speed ----------------------------------------------------------
@@ -116,8 +116,9 @@ switch ($ActiveProvider) {
116
116
  # by the SSH-receiver watcher via -ProviderOverride). This lets the Linux-side
117
117
  # audio-effects.cfg row for llm:claude-code specify "piper" and have it honoured
118
118
  # on Windows without requiring the Windows tts-provider.txt to be reconfigured.
119
- # Priority: lower than per-LLM $_LlmEngine (audio-effects.cfg row, set later), higher
120
- # than the global tts-provider.txt default set above.
119
+ # Priority: an explicit -ProviderOverride is AUTHORITATIVE โ€” it wins over both the
120
+ # per-LLM $_LlmEngine column and the global tts-provider.txt default (see the
121
+ # $ProviderOverride guard in the engine-resolution block below).
121
122
  if ($ProviderOverride) {
122
123
  switch ($ProviderOverride) {
123
124
  { $_ -in "windows-piper", "piper" } {
@@ -308,8 +309,30 @@ $PlanVoiceIsOverride = $false
308
309
  if ($ProviderOverride) {
309
310
  # A fresh -ProviderOverride for THIS request must win over any stale/inherited
310
311
  # AGENTVIBES_FORCE_PROVIDER in the environment (a validated allowlist only).
312
+ #
313
+ # The allowlist DERIVES from the Provider Catalog (SSOT): the Windows platform
314
+ # set PLUS the cross-platform forwarding aliases this script normalizes
315
+ # (piperโ†’windows-piper, sapiโ†’windows-sapi, and macos forwarded verbatim for
316
+ # SSH-relayed senders). FAIL-SAFE: the literal below is the legacy fallback
317
+ # used when the generated provider-catalog.ps1 is missing (installed-tree skew).
318
+ $ProviderOverrideAllowlist = @('piper','soprano','macos','windows-sapi','sapi','kokoro','windows-piper')
319
+ $__CatalogPs1 = Join-Path $ScriptPath 'provider-catalog.ps1'
320
+ if (Test-Path $__CatalogPs1) {
321
+ try {
322
+ . $__CatalogPs1
323
+ if (Get-Command Get-CatalogProvidersForPlatform -ErrorAction SilentlyContinue) {
324
+ $__WinSet = @(Get-CatalogProvidersForPlatform 'windows')
325
+ if ($__WinSet.Count -gt 0) {
326
+ # Windows synth providers + the forwarding aliases play-tts.ps1 normalizes.
327
+ $ProviderOverrideAllowlist = @($__WinSet + @('piper','sapi','macos') | Select-Object -Unique)
328
+ }
329
+ }
330
+ } catch {
331
+ # Keep the legacy fallback allowlist on any catalog load error.
332
+ }
333
+ }
311
334
  switch ($ProviderOverride) {
312
- { $_ -in @('piper','soprano','macos','windows-sapi','sapi','kokoro','elevenlabs','windows-piper') } {
335
+ { $_ -in $ProviderOverrideAllowlist } {
313
336
  $env:AGENTVIBES_FORCE_PROVIDER = $ProviderOverride
314
337
  }
315
338
  }
@@ -522,6 +545,12 @@ else {
522
545
  # ENGINE column or the global tts-provider.txt default.
523
546
  $ProviderScript = "$HooksDir\play-tts-kokoro.ps1"
524
547
  }
548
+ elseif ($ProviderOverride) {
549
+ # An explicit -ProviderOverride (forwarded from the SSH payload's "provider"
550
+ # field, or a preview) is AUTHORITATIVE and was already applied above. Do NOT
551
+ # let the per-LLM ENGINE column override it, or a receiver whose llm row
552
+ # defaults to piper silently swallows a forwarded windows-sapi request.
553
+ }
525
554
  elseif ($_LlmEngine) {
526
555
  # Accept both canonical Windows names and the cross-platform aliases the TUI
527
556
  # writes (e.g. "piper" saved on a Linux/WSL install that is later read on
@@ -0,0 +1,140 @@
1
+ # provider-catalog.ps1 โ€” GENERATED FILE. DO NOT EDIT.
2
+ # DO NOT EDIT โ€” generated from src/services/provider-catalog.js
3
+ # Regenerate with: node scripts/generate-provider-catalog.mjs
4
+ # content-hash: sha256:5e717024f1250a8f54d07291256e532606dba7b4a15ab9589305d1af6bba7f35
5
+ # Dot-source this file to load the catalog accessors; do not execute it.
6
+
7
+ $script:AgentVibesCatalogVersion = '5e717024f1250a8f'
8
+
9
+ $script:CatalogDefaults = @{
10
+ 'soprano' = 'soprano-default'
11
+ 'piper' = 'en_US-lessac-medium'
12
+ 'kokoro' = 'af_heart'
13
+ 'elevenlabs' = 'Sarah'
14
+ 'macos' = 'Samantha'
15
+ 'windows-sapi' = ''
16
+ 'windows-piper' = 'en_US-lessac-medium'
17
+ }
18
+
19
+ $script:CatalogDisplayNames = @{
20
+ 'soprano' = 'Soprano TTS'
21
+ 'piper' = 'Piper TTS'
22
+ 'kokoro' = 'Kokoro TTS'
23
+ 'elevenlabs' = 'ElevenLabs'
24
+ 'macos' = 'macOS Say'
25
+ 'windows-sapi' = 'Windows SAPI'
26
+ 'windows-piper' = 'Piper TTS'
27
+ }
28
+
29
+ $script:CatalogAlias = @{
30
+ 'macos-say' = 'macos'
31
+ 'say' = 'macos'
32
+ 'sapi' = 'windows-sapi'
33
+ }
34
+
35
+ $script:CatalogPlatform = @{
36
+ 'unix' = @('soprano','piper','kokoro','elevenlabs')
37
+ 'darwin' = @('soprano','piper','kokoro','elevenlabs','macos')
38
+ 'windows' = @('soprano','kokoro','windows-sapi','windows-piper')
39
+ }
40
+
41
+ $script:CatalogVoices = @{
42
+ 'soprano' = @('soprano-default')
43
+ 'piper' = @()
44
+ 'kokoro' = @('af_heart','af_alloy','af_aoede','af_bella','af_jessica','af_kore','af_nicole','af_nova','af_river','af_sarah','af_sky','am_adam','am_echo','am_eric','am_fenrir','am_liam','am_michael','am_onyx','am_puck','bf_alice','bf_emma','bf_isabella','bf_lily','bm_daniel','bm_fable','bm_george','bm_lewis','jf_alpha','jf_gongitsune','jf_nezumi','jf_tebukuro','jm_kumo','zf_xiaobei','zf_xiaoni','zf_xiaoxiao','zf_xiaoyi','zm_yunxi','zm_yunxia','zm_yunyang','ef_dora','em_alex','em_santa','ff_siwis','hf_alpha','hm_omega','if_sara','im_nicola','pf_dora','pm_alex','pm_santa','kf_alpha','km_hyunsu')
45
+ 'elevenlabs' = @('Sarah','Roger','Laura','Charlie','George','Callum','River','Harry','Liam','Alice','Matilda','Will','Jessica','Eric','Bella','Chris','Brian','Daniel','Lily','Adam','Bill')
46
+ 'macos' = @()
47
+ 'windows-sapi' = @()
48
+ 'windows-piper' = @()
49
+ }
50
+
51
+ $script:CatalogElevenLabs = @{
52
+ 'sarah' = 'EXAVITQu4vr4xnSDxMaL'
53
+ 'roger' = 'CwhRBWXzGAHq8TQ4Fs17'
54
+ 'laura' = 'FGY2WhTYpPnrIDTdsKH5'
55
+ 'charlie' = 'IKne3meq5aSn9XLyUdCD'
56
+ 'george' = 'JBFqnCBsd6RMkjVDRZzb'
57
+ 'callum' = 'N2lVS1w4EtoT3dr4eOWO'
58
+ 'river' = 'SAz9YHcvj6GT2YYXdXww'
59
+ 'harry' = 'SOYHLrjzK2X1ezoPC6cr'
60
+ 'liam' = 'TX3LPaxmHKxFdv7VOQHJ'
61
+ 'alice' = 'Xb7hH8MSUJpSbSDYk0k2'
62
+ 'matilda' = 'XrExE9yKIg1WjnnlVkGX'
63
+ 'will' = 'bIHbv24MWmeRgasZH58o'
64
+ 'jessica' = 'cgSgspJ2msm6clMCkdW9'
65
+ 'eric' = 'cjVigY5qzO86Huf0OWal'
66
+ 'bella' = 'hpp4J3VqNfWAUOO0d1Us'
67
+ 'chris' = 'iP95p4xoKVk53GoZ742B'
68
+ 'brian' = 'nPczCjzI2devNBz1zQrb'
69
+ 'daniel' = 'onwK4e9ZLuTAKqWW03F9'
70
+ 'lily' = 'pFZP5JQG7iQjIQuC4Bku'
71
+ 'adam' = 'pNInz6obpgDQGcFmaJgB'
72
+ 'bill' = 'pqHfZKP75CvOlQylNhV4'
73
+ }
74
+
75
+ function Get-CatalogCanonicalProvider {
76
+ param([Parameter(Mandatory)][string]$Provider)
77
+ $key = $Provider.Trim().ToLowerInvariant()
78
+ if ($script:CatalogDefaults.ContainsKey($key)) { return $key }
79
+ if ($script:CatalogAlias.ContainsKey($key)) { return $script:CatalogAlias[$key] }
80
+ return $null
81
+ }
82
+
83
+ function Get-CatalogDefaultVoice {
84
+ param([Parameter(Mandatory)][string]$Provider)
85
+ $p = Get-CatalogCanonicalProvider $Provider
86
+ if ($null -eq $p) { return $null }
87
+ return $script:CatalogDefaults[$p]
88
+ }
89
+
90
+ function Get-CatalogDisplayName {
91
+ param([Parameter(Mandatory)][string]$Provider)
92
+ $p = Get-CatalogCanonicalProvider $Provider
93
+ if ($null -eq $p) { return $Provider }
94
+ return $script:CatalogDisplayNames[$p]
95
+ }
96
+
97
+ function Get-CatalogProvidersForPlatform {
98
+ param([Parameter(Mandatory)][string]$Platform)
99
+ $key = $Platform.Trim().ToLowerInvariant()
100
+ if ($script:CatalogPlatform.ContainsKey($key)) { return $script:CatalogPlatform[$key] }
101
+ return @()
102
+ }
103
+
104
+ function Get-CatalogVoices {
105
+ param([Parameter(Mandatory)][string]$Provider)
106
+ $p = Get-CatalogCanonicalProvider $Provider
107
+ if ($null -eq $p) { return @() }
108
+ return $script:CatalogVoices[$p]
109
+ }
110
+
111
+ function Test-CatalogVoice {
112
+ param([Parameter(Mandatory)][string]$Provider, [Parameter(Mandatory)][AllowEmptyString()][string]$Voice)
113
+ $p = Get-CatalogCanonicalProvider $Provider
114
+ if ($null -eq $p) { return $null }
115
+ $lc = $Voice.Trim().ToLowerInvariant()
116
+ switch ($p) {
117
+ 'kokoro' {
118
+ if ($script:CatalogVoices['kokoro'] -contains $lc) { return $lc }
119
+ return $null
120
+ }
121
+ 'elevenlabs' {
122
+ if ($script:CatalogElevenLabs.ContainsKey($lc)) { return $script:CatalogElevenLabs[$lc] }
123
+ if ($Voice -match '^[A-Za-z0-9]{20}$') { return $Voice }
124
+ return $null
125
+ }
126
+ 'soprano' {
127
+ if ($lc -eq '' -or $lc -eq 'soprano' -or $lc -eq 'soprano-default') { return 'soprano-default' }
128
+ return $null
129
+ }
130
+ { $_ -eq 'piper' -or $_ -eq 'windows-piper' } {
131
+ if ($Voice -match '^[a-z]{2,3}_[A-Z]{2}-' -or $Voice -like '*::*') { return $Voice }
132
+ return $null
133
+ }
134
+ default {
135
+ if ($Voice -ne '') { return $Voice }
136
+ if ($p -eq 'windows-sapi') { return '' }
137
+ return $null
138
+ }
139
+ }
140
+ }
@@ -15,7 +15,25 @@ param(
15
15
 
16
16
  $ClaudeDir = "$env:USERPROFILE\.claude"
17
17
  $ProviderFile = "$ClaudeDir\tts-provider.txt"
18
- $ValidProviders = @('windows-piper', 'windows-sapi', 'soprano')
18
+ # $ValidProviders DERIVES from the generated Provider Catalog (SSOT): the Windows
19
+ # platform set (kokoro is cross-platform โ€” play-tts-kokoro.ps1 exists; elevenlabs
20
+ # is NOT playable on Windows and is therefore absent). Dot-source the generated
21
+ # provider-catalog.ps1 and pull `windows`. FAIL-SAFE: the literal below is the
22
+ # legacy fallback used when the catalog artifact is missing (installed-tree skew)
23
+ # โ€” mirrors AVI-S9.3's probe/fallback pattern. See AVI-S8.1 / AVI-S9.5.
24
+ $ValidProviders = @('windows-piper', 'windows-sapi', 'soprano', 'kokoro')
25
+ $__CatalogPs1 = Join-Path $PSScriptRoot 'provider-catalog.ps1'
26
+ if (Test-Path $__CatalogPs1) {
27
+ try {
28
+ . $__CatalogPs1
29
+ if (Get-Command Get-CatalogProvidersForPlatform -ErrorAction SilentlyContinue) {
30
+ $__WinSet = @(Get-CatalogProvidersForPlatform 'windows')
31
+ if ($__WinSet.Count -gt 0) { $ValidProviders = $__WinSet }
32
+ }
33
+ } catch {
34
+ # Keep the legacy fallback literal on any catalog load error.
35
+ }
36
+ }
19
37
 
20
38
  # Ensure claude directory exists
21
39
  if (-not (Test-Path $ClaudeDir)) {
@@ -35,6 +53,14 @@ function Get-ActiveProvider {
35
53
  function Get-AvailableProviders {
36
54
  $available = @()
37
55
 
56
+ # Fast path: skip the live install probes (soprano HTTP + kokoro python
57
+ # spawns). Those probes are what make `provider list` occasionally stall for
58
+ # tens of seconds under load; when the caller only needs the provider NAMES
59
+ # (e.g. the parallel test harness, or an offline listing) set
60
+ # AGENTVIBES_PROVIDER_LIST_NO_PROBE=1 to report installed=$false without
61
+ # touching the network or spawning interpreters. Default behavior unchanged.
62
+ $noProbe = ($env:AGENTVIBES_PROVIDER_LIST_NO_PROBE -eq '1')
63
+
38
64
  # Always available
39
65
  $available += @{
40
66
  name = "windows-sapi"
@@ -59,15 +85,18 @@ function Get-AvailableProviders {
59
85
  $null = Get-Command soprano -ErrorAction Stop
60
86
  $sopranoInstalled = $true
61
87
  } catch {
62
- # Also check if a Soprano server is running (try both API paths)
63
- try {
64
- $null = Invoke-WebRequest -Uri "http://127.0.0.1:${sopranoPort}/gradio_api/info" -TimeoutSec 1 -UseBasicParsing -ErrorAction Stop
65
- $sopranoInstalled = $true
66
- } catch {
88
+ # Also check if a Soprano server is running (try both API paths) โ€” unless
89
+ # the fast path is requested, in which case skip the network probes.
90
+ if (-not $noProbe) {
67
91
  try {
68
- $null = Invoke-WebRequest -Uri "http://127.0.0.1:${sopranoPort}/info" -TimeoutSec 1 -UseBasicParsing -ErrorAction Stop
92
+ $null = Invoke-WebRequest -Uri "http://127.0.0.1:${sopranoPort}/gradio_api/info" -TimeoutSec 1 -UseBasicParsing -ErrorAction Stop
69
93
  $sopranoInstalled = $true
70
- } catch {}
94
+ } catch {
95
+ try {
96
+ $null = Invoke-WebRequest -Uri "http://127.0.0.1:${sopranoPort}/info" -TimeoutSec 1 -UseBasicParsing -ErrorAction Stop
97
+ $sopranoInstalled = $true
98
+ } catch {}
99
+ }
71
100
  }
72
101
  }
73
102
 
@@ -77,6 +106,32 @@ function Get-AvailableProviders {
77
106
  installed = $sopranoInstalled
78
107
  }
79
108
 
109
+ # Check if Kokoro is installed. Mirror validateKokoroInstallation() in
110
+ # src/utils/provider-validator.js, but use importlib.util.find_spec so we do
111
+ # NOT actually import kokoro โ€” importing the 82M-param neural lib takes
112
+ # several seconds and would make `provider list` hang (AVI-S8.1). find_spec
113
+ # only confirms the packages are importable, which is all we need here.
114
+ $kokoroInstalled = $false
115
+ $kokoroProbe = "import importlib.util as u,sys; sys.exit(0 if all(u.find_spec(m) for m in ('kokoro','soundfile','numpy')) else 1)"
116
+ foreach ($py in ($(if ($noProbe) { @() } else { @('py', 'python', 'python3') }))) {
117
+ if (-not (Get-Command $py -ErrorAction SilentlyContinue)) { continue }
118
+ try {
119
+ $null = & $py -c $kokoroProbe 2>$null
120
+ # Only stop once an interpreter actually HAS the packages โ€” mirrors
121
+ # validateKokoroInstallation(), which tries every interpreter (the
122
+ # packages may live under python/python3, not the `py` launcher).
123
+ if ($LASTEXITCODE -eq 0) { $kokoroInstalled = $true; break }
124
+ } catch {
125
+ # interpreter present but failed to run the probe โ€” try next
126
+ }
127
+ }
128
+
129
+ $available += @{
130
+ name = "kokoro"
131
+ description = "Kokoro TTS (Neural, pip install kokoro-onnx soundfile numpy)"
132
+ installed = $kokoroInstalled
133
+ }
134
+
80
135
  return $available
81
136
  }
82
137
 
@@ -52,7 +52,7 @@ try {
52
52
  }
53
53
  if ($req.kind -eq 'music') {
54
54
  $trackName = [string]$req.music
55
- if ($trackName -match '^[A-Za-z0-9._][A-Za-z0-9._\-]*\.mp3$') {
55
+ if ($trackName -match '^[A-Za-z0-9._][A-Za-z0-9._ \-]*\.mp3$') {
56
56
  $tracksDir = Join-Path $env:USERPROFILE '.claude\audio\tracks'
57
57
  $full = [System.IO.Path]::GetFullPath((Join-Path $tracksDir $trackName))
58
58
  $baseFull = [System.IO.Path]::GetFullPath($tracksDir)
@@ -61,6 +61,17 @@ try {
61
61
  $ffplay = Get-Command ffplay -ErrorAction SilentlyContinue
62
62
  if ($ffplay) {
63
63
  Write-WatcherLog "INFO" "music-preview id=$($req.id) track=$trackName"
64
+ # Readiness bling โ€” a short cue played in THIS (audio-capable) watcher
65
+ # session BEFORE the track so a listener knows music is incoming. Launched
66
+ # async (NOT -Wait โ€” the wav's shimmer tail is long); a fixed 0.3s gap then
67
+ # lets the music follow tightly. Best-effort: a missing wav / player error
68
+ # never blocks the track.
69
+ $blingWav = Join-Path $env:USERPROFILE '.claude\audio\ui\bling-success.wav'
70
+ if (-not (Test-Path $blingWav)) { $blingWav = Join-Path $env:USERPROFILE '.agentvibes\bling-success.wav' }
71
+ if (Test-Path $blingWav) {
72
+ try { Start-Process -FilePath $ffplay.Source -ArgumentList @('-autoexit','-nodisp','-loglevel','quiet',$blingWav) -WindowStyle Hidden | Out-Null } catch { }
73
+ Start-Sleep -Milliseconds 300
74
+ }
64
75
  $mp = Start-Process -FilePath $ffplay.Source -ArgumentList @('-autoexit','-nodisp','-loglevel','quiet','-volume','80',$full) -WindowStyle Hidden -PassThru
65
76
  if ($mp) { Set-Content -Path $pidFile -Value $mp.Id -NoNewline -ErrorAction SilentlyContinue }
66
77
  } else {
@@ -83,9 +94,9 @@ try {
83
94
  $safeVoice = if ($req.voice -and $req.voice -match '^[a-zA-Z0-9_\-\. :]+$') { $req.voice } else { "" }
84
95
  $env:CLAUDE_PROJECT_DIR = $env:USERPROFILE
85
96
  $env:AGENTVIBES_NO_PRETEXT = "1"
86
- # Carry the sender's project (folder name/path) through to the
87
- # TalkingHead forward so the avatar badge/tab shows the real remote
88
- # origin instead of this machine's own profile dir. (Grafted from master WIP.)
97
+ # Carry the sender's project (folder name/path) through so a
98
+ # downstream receiver shows the real remote origin instead of
99
+ # this machine's own profile dir.
89
100
  if ($req.project) { $env:AGENTVIBES_PROJECT = $req.project }
90
101
  else { [System.Environment]::SetEnvironmentVariable("AGENTVIBES_PROJECT", $null, "Process") }
91
102
  if ($req.projectPath) { $env:AGENTVIBES_PROJECT_PATH = $req.projectPath }
@@ -99,16 +110,26 @@ try {
99
110
  else { [System.Environment]::SetEnvironmentVariable("AGENTVIBES_OVERRIDE_EFFECTS", $null, "Process") }
100
111
 
101
112
  if (Test-Path $PlayTts) {
102
- # Play remote arrival prefix sound if configured
103
- $prefixSoundFile = "$env:USERPROFILE\.agentvibes\remote-prefix-sound.txt"
104
- if (Test-Path $prefixSoundFile) {
105
- $prefixSound = (Get-Content $prefixSoundFile -Raw).Trim()
106
- if ($prefixSound -and (Test-Path $prefixSound)) {
107
- $ffplay = Get-Command ffplay -ErrorAction SilentlyContinue
108
- if ($ffplay) {
109
- & $ffplay.Source -autoexit -nodisp -loglevel quiet $prefixSound 2>$null
113
+ # Always play a crisp local "incoming" bling so the user KNOWS audio is
114
+ # on the way โ€” even when audio is routed to a downstream receiver
115
+ # instead of the local speakers. Standard bling-success.wav, async
116
+ # with a 0.3s gap (same as the music-preview path); fall back to the
117
+ # user-configured remote-prefix-sound.txt if the bling wav is missing.
118
+ $ffplay = Get-Command ffplay -ErrorAction SilentlyContinue
119
+ if ($ffplay) {
120
+ $blingWav = Join-Path $env:USERPROFILE '.claude\audio\ui\bling-success.wav'
121
+ if (-not (Test-Path $blingWav)) { $blingWav = Join-Path $env:USERPROFILE '.agentvibes\bling-success.wav' }
122
+ if (-not (Test-Path $blingWav)) {
123
+ $prefixSoundFile = "$env:USERPROFILE\.agentvibes\remote-prefix-sound.txt"
124
+ if (Test-Path $prefixSoundFile) {
125
+ $ps = (Get-Content $prefixSoundFile -Raw).Trim()
126
+ if ($ps -and (Test-Path $ps)) { $blingWav = $ps }
110
127
  }
111
128
  }
129
+ if (Test-Path $blingWav) {
130
+ try { Start-Process -FilePath $ffplay.Source -ArgumentList @('-autoexit','-nodisp','-loglevel','quiet',$blingWav) -WindowStyle Hidden | Out-Null } catch { }
131
+ Start-Sleep -Milliseconds 300
132
+ }
112
133
  }
113
134
 
114
135
  $tempText = Join-Path $env:TEMP "agentvibes-tts-$($req.id).txt"
@@ -17,6 +17,14 @@ $ClaudeDir = "$env:USERPROFILE\.claude"
17
17
  $ProviderFile = "$ClaudeDir\tts-provider.txt"
18
18
  $VoiceSapiFile = "$ClaudeDir\tts-voice-sapi.txt"
19
19
  $VoicePiperFile = "$ClaudeDir\tts-voice-piper.txt"
20
+ $VoiceKokoroFile = "$ClaudeDir\tts-voice-kokoro.txt"
21
+
22
+ # Load the generated Provider Catalog (SSOT) for kokoro switch-time validation.
23
+ # FAIL-SAFE: consumers probe `Get-Command Test-CatalogVoice` and fall back to the
24
+ # legacy shape regex when the artifact is missing (installed-tree skew) โ€” mirrors
25
+ # play-tts.sh's PLAN_OK legacy fallback. Switch never breaks on a missing catalog.
26
+ $CatalogPs1 = Join-Path $PSScriptRoot 'provider-catalog.ps1'
27
+ if (Test-Path $CatalogPs1) { . $CatalogPs1 }
20
28
 
21
29
  # Get active provider
22
30
  $ActiveProvider = "windows-sapi"
@@ -129,6 +137,47 @@ function Switch-Voice {
129
137
  Write-Host "[INFO] Soprano uses a single fixed voice (Soprano-1.1-80M)" -ForegroundColor Cyan
130
138
  return $true
131
139
  }
140
+ elseif ($ActiveProvider -eq "kokoro") {
141
+ # Kokoro switch-time validation via the generated Provider Catalog (SSOT):
142
+ # MEMBERSHIP + canonical case-fold, so a typo like af_hart is rejected HERE
143
+ # and never reaches tts-voice-kokoro.txt to die silently at the model.
144
+ # Escape hatch AGENTVIBES_ALLOW_UNLISTED_VOICE=1 bypasses MEMBERSHIP (not
145
+ # shape) so a voice from a newer kokoro model still switches. FAIL-SAFE:
146
+ # fall back to the legacy shape regex when the catalog artifact is missing.
147
+ $kokoroVoice = $NewVoice.ToLower()
148
+ $allowUnlisted = ($env:AGENTVIBES_ALLOW_UNLISTED_VOICE -eq '1')
149
+ if (Get-Command Test-CatalogVoice -ErrorAction SilentlyContinue) {
150
+ $canon = Test-CatalogVoice -Provider 'kokoro' -Voice $NewVoice
151
+ if ($canon) {
152
+ Set-Content -Path $VoiceKokoroFile -Value $canon
153
+ Write-Host "[OK] Voice set to: $canon" -ForegroundColor Green
154
+ return $true
155
+ }
156
+ elseif ($allowUnlisted -and $kokoroVoice -match '^[a-z]{2}_[a-z0-9_]+$') {
157
+ Write-Host "[WARN] '$NewVoice' is not in the shipped Kokoro catalog; allowing it (AGENTVIBES_ALLOW_UNLISTED_VOICE=1)." -ForegroundColor Yellow
158
+ Set-Content -Path $VoiceKokoroFile -Value $kokoroVoice
159
+ Write-Host "[OK] Voice set to: $kokoroVoice" -ForegroundColor Green
160
+ return $true
161
+ }
162
+ else {
163
+ Write-Host "[ERROR] Kokoro voice not found: $NewVoice" -ForegroundColor Red
164
+ Write-Host "That is not a known Kokoro voice. Examples: af_heart, am_michael, bf_emma, bm_george, jf_alpha, zf_xiaoxiao, ef_dora." -ForegroundColor Yellow
165
+ Write-Host "Set AGENTVIBES_ALLOW_UNLISTED_VOICE=1 to allow a newer-model voice." -ForegroundColor Yellow
166
+ return $false
167
+ }
168
+ }
169
+ else {
170
+ # FAIL-SAFE legacy path (catalog artifact missing): shape-only regex.
171
+ if ($kokoroVoice -notmatch '^[a-z]{2}_[a-z0-9_]+$') {
172
+ Write-Host "[ERROR] Kokoro voice not found: $NewVoice" -ForegroundColor Red
173
+ Write-Host "Kokoro ids look like <lang><sex>_name, e.g. af_heart, am_michael, bf_emma." -ForegroundColor Yellow
174
+ return $false
175
+ }
176
+ Set-Content -Path $VoiceKokoroFile -Value $kokoroVoice
177
+ Write-Host "[OK] Voice set to: $kokoroVoice" -ForegroundColor Green
178
+ return $true
179
+ }
180
+ }
132
181
 
133
182
  if ($ValidVoices -notcontains $NewVoice) {
134
183
  Write-Host "[ERROR] Voice not found: $NewVoice" -ForegroundColor Red
package/.mcp.json CHANGED
@@ -1,12 +1,5 @@
1
1
  {
2
2
  "mcpServers": {
3
- "firecrawl-mcp": {
4
- "command": "npx",
5
- "args": [
6
- "-y",
7
- "firecrawl-mcp"
8
- ]
9
- },
10
3
  "agentvibes": {
11
4
  "command": "npx",
12
5
  "args": [
package/README.md CHANGED
@@ -58,9 +58,18 @@ New here? The [**Quick Start guide**](docs/quick-start.md) walks you through you
58
58
 
59
59
  ---
60
60
 
61
- ## ๐Ÿ†• A stronger core (v5.12.0)
61
+ ## ๐Ÿ†• Your voices everywhere (v5.13.0)
62
62
 
63
- During a week of early access to Anthropic's new **Fable** model, we rebuilt the heart of AgentVibes into **one shared core**. The voice / engine / routing / volume / mute logic that used to be copied across four scripts (and drift apart) now lives in a single place โ€” simpler, more consistent, and steadier.
63
+ Pick the voices built into **Windows** or **Mac** and hear them wherever you're listening โ€” even when your agents run on another computer. Plus a friendly heads-up chime so you always know sound is coming.
64
+
65
+ - **๐Ÿ–ฅ๏ธ Your computer's own voices, from anywhere** โ€” choose Windows or Mac voices and hear them on your machine; every voice is shown, with unavailable ones clearly marked.
66
+ - **๐Ÿ—‚๏ธ All voices in one list** โ€” Piper, Kokoro, ElevenLabs, Windows, Mac, and Soprano in one place, so what you see is what you can use.
67
+ - **๐Ÿ”” Heads-up chime** โ€” a short sound plays just before a voice or music preview, so you know audio is on the way.
68
+ - **๐Ÿ†” Agents that introduce themselves** โ€” optional self-introductions so you know who's talking in a team.
69
+
70
+ ### v5.12.0 โ€” A stronger core
71
+
72
+ During a week of early access to Anthropic's new **Fable** model, we rebuilt the heart of AgentVibes into **one shared core**. The voice / engine / routing / volume / mute logic now lives in a single place โ€” simpler, more consistent, and steadier.
64
73
 
65
74
  - **๐Ÿ”Š Previews play in the right place** โ€” with SSH remote configured, voice and **music previews** play on your receiver; otherwise they play locally.
66
75
  - **๐Ÿง  One shared core** โ€” Kokoro-on-Linux silence and per-voice drift fixed at the source, with a safe fallback if needed.
@@ -224,7 +233,7 @@ Running a full BMAD agent team? **Every agent gets its own distinct voice**, aut
224
233
 
225
234
  ## About
226
235
 
227
- **AgentVibes** ยท v5.12.0 ยท Licensed under [Apache-2.0](LICENSE)
236
+ **AgentVibes** ยท v5.13.1 ยท Licensed under [Apache-2.0](LICENSE)
228
237
 
229
238
  Built by **Paul Preibisch** โ€” [@997Fire on X](https://x.com/997Fire) ยท [agentvibes.org](https://agentvibes.org) ยท [github.com/paulpreibisch/AgentVibes](https://github.com/paulpreibisch/AgentVibes)
230
239
 
package/RELEASE_NOTES.md CHANGED
@@ -1,5 +1,66 @@
1
1
  # AgentVibes Release Notes
2
2
 
3
+ ## ๐Ÿ”ง v5.13.1 โ€” Windows updates that actually update
4
+
5
+ **Released:** 2026-07-16 ยท on `latest` โ€” `npm install agentvibes@latest`
6
+
7
+ ### ๐ŸชŸ Your Windows scripts now really do update
8
+
9
+ On Windows, the little scripts that make your agents talk live in your `.claude/hooks` folder. Updating said it refreshed them โ€” but on Windows it quietly didn't, so they could sit on the version you first installed for months.
10
+
11
+ Now they update for real. Run `npx agentvibes update` and you'll pick up every fix you've been missing. Anything you'd customized yourself is still kept safe beside it as a `.user.bak` file, exactly as before.
12
+
13
+ If you're on macOS or Linux, nothing changes โ€” updates were already working for you.
14
+
15
+ ### ๐Ÿ”’ A security tune-up under the hood
16
+
17
+ We updated one of the building blocks AgentVibes uses to read settings files. A specially-crafted settings file could have made it grind to a halt. Nothing could ever have been stolen or snooped on โ€” but now it can't get stuck either. Nothing for you to do; it's just in.
18
+
19
+ ---
20
+
21
+ ## ๐ŸŽ‰ v5.13.0 โ€” Your Voices Everywhere, With a Heads-Up
22
+
23
+ **Released:** 2026-07-16 ยท on `latest` โ€” `npm install agentvibes`
24
+
25
+ What's new:
26
+
27
+ ### ๐Ÿ–ฅ๏ธ Use your computer's own voices, from anywhere
28
+
29
+ Run your agents on one machine and listen on another? You can now pick the voices built into **Windows** (David, Zira, Mark) or **Mac**, and hear them right where you're sitting. AgentVibes shows you every voice and clearly marks the ones your listening device can play.
30
+
31
+ ### ๐Ÿ—‚๏ธ All your voices in one tidy list
32
+
33
+ Piper, Kokoro, ElevenLabs, Windows, Mac, Soprano โ€” every voice now comes from a single list, so what you see is always what you can use.
34
+
35
+ ### ๐Ÿ”” A "heads-up" chime before sound plays
36
+
37
+ Just before a voice line or music preview starts, you'll hear a short chime โ€” so you always know audio is on the way, even if it takes a moment.
38
+
39
+ ### ๐ŸŽต Music previews follow your sound
40
+
41
+ Preview a track and it plays wherever your audio is set to go โ€” including on another computer.
42
+
43
+ ### ๐Ÿ†” Agents that introduce themselves
44
+
45
+ Turn on self-introductions and each agent says who it is when it starts โ€” handy when a whole team is talking.
46
+
47
+ ### ๐Ÿ›Ÿ Your own edits are kept safe when you update
48
+
49
+ Tinkered with one of the AgentVibes files in your `.claude/hooks` folder? From this version on, updating never throws your work away. If we need to update a file you'd changed, we tuck your copy in beside it with `.user.bak` on the end โ€” like `play-tts.sh.user.bak`.
50
+
51
+ **That file is made by AgentVibes โ€” nothing is broken and nothing else put it there.** It's simply your old version, saved so you can peek at it or copy your changes back into the new one. Delete it whenever you're done with it.
52
+
53
+ If you customized files in an older version, it's worth a quick look in `.claude/hooks` for anything you'd want to put back.
54
+
55
+ ### โœจ More voices, smoother ride
56
+
57
+ - **ElevenLabs** voices fully supported
58
+ - More **Kokoro** voices, working great on Windows
59
+ - Faster, more reliable setup on Windows
60
+ - **3,263 automated tests passing** โ€” steady and dependable
61
+
62
+ ---
63
+
3
64
  ## ๐ŸŽ‰ v5.12.0 โ€” The Fable Week Overhaul (Stable)
4
65
 
5
66
  **Released:** 2026-07-05 ยท now on `latest` โ€” `npm install agentvibes`