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
package/RELEASE_NOTES.md CHANGED
@@ -1,5 +1,83 @@
1
1
  # AgentVibes Release Notes
2
2
 
3
+ ## 🐛 v4.5.7 — Patch Release
4
+
5
+ **Release Date:** April 2026
6
+
7
+ ### Bug Fixes
8
+
9
+ - **Background music volume default** — All volume defaults lowered from 70% to 20% across the UI (settings tab, agents tab, music tab, track picker). New installs and newly configured agents will default to a much more reasonable background music level.
10
+ - **bmad-speak volume inheritance** — `bmad-speak.sh` and `bmad-speak.ps1` now read the global `background-music-volume.txt` config file as the fallback volume instead of a hardcoded value. Per-agent background music volume now correctly inherits the global setting when no explicit per-agent override is saved.
11
+
12
+ ---
13
+
14
+ ## 🐛 v4.5.1 — Patch Release
15
+
16
+ **Release Date:** April 2026
17
+
18
+ ### Bug Fix
19
+
20
+ - **Music tab preview** — Pressing Space on a track in the Music tab now plays correctly
21
+ when running `npx agentvibes` from a fresh directory. Previously, if `.claude/audio/tracks/`
22
+ didn't exist in the current working directory, the track list showed built-in tracks but
23
+ Space did nothing (the player was spawned against a non-existent path). Now falls back to
24
+ the package-bundled tracks directory automatically.
25
+
26
+ ---
27
+
28
+ ## 🌍 v4.5.0 — "Speak Every Language" Release
29
+
30
+ **Release Date:** April 2026
31
+
32
+ Full multilingual TUI support across all 9 languages, complete Windows security hardening, and zero failing tests.
33
+
34
+ ### 🌍 Multilingual TUI — 9 Languages
35
+
36
+ Every screen, tab, button, and label in the `npx agentvibes` TUI is now fully translated:
37
+
38
+ - **English, Spanish, French, German, Portuguese, Japanese, Korean, Chinese (Simplified), Italian**
39
+ - Language selection on first launch (Screen 0 of the installer wizard)
40
+ - Language sub-tab in Settings — switch language live without restarting
41
+ - All tab bar labels, button text, footer hints, and status messages translated
42
+ - BMAD tab and SSH Receiver tab fully localized
43
+ - Per-language i18n files (`src/i18n/en.js`, `es.js`, `fr.js`, ...) with English fallback
44
+
45
+ ### 🪟 Windows Security & Bug Fixes
46
+
47
+ - **Temp filenames** — All `Date.now()` temp filenames replaced with `randomUUID()` across JS and PowerShell (unpredictable, prevents temp file hijacking)
48
+ - **Shell injection** — `execSync('which ...', { shell: true })` replaced with `spawnSync` (no shell expansion)
49
+ - **Music player** — Hardcoded `ffplay` on Windows replaced with `detectMp3Player()` (respects user's installed player)
50
+ - **Boolean coercion** — `isWindowsTerminal` now correctly returns `true/false` instead of leaking `WT_SESSION` UUID string
51
+ - **Network mount detection** — `.match()` result properly coerced to boolean
52
+
53
+ ### 🎙️ Cross-Platform BMAD Speak
54
+
55
+ 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.
56
+
57
+ ## 🐛 v4.5.1 — Patch Release
58
+
59
+ **Release Date:** April 2026
60
+
61
+ ### Bug Fix
62
+
63
+ - **Music tab preview** — Pressing Space on a track in the Music tab now plays correctly
64
+ when running `npx agentvibes` from a fresh directory. Previously, if `.claude/audio/tracks/`
65
+ didn't exist in the current working directory, the track list showed built-in tracks but
66
+ Space did nothing (the player was spawned against a non-existent path). Now falls back to
67
+ the package-bundled tracks directory automatically.
68
+
69
+ ---
70
+
71
+ - `bin/bmad-speak.js` — cross-platform entry point for BMAD agent speech
72
+ - `.claude/hooks-windows/bmad-speak.ps1` — native Windows BMAD speak with per-agent personality routing
73
+
74
+ ### 🧪 Test Suite
75
+
76
+ - 600 tests, 0 failures
77
+ - Full cross-platform coverage (Windows path separators, chmod skip, provider file restore)
78
+
79
+ ---
80
+
3
81
  ## 🎉 v4.4.0 — "Full Platform Parity" Release
4
82
 
5
83
  **Release Date:** March 2026
package/bin/agent-vibes CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * AgentVibes - Beautiful ElevenLabs TTS voice commands for Claude Code
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * AgentVibes Voice Browser
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * AgentVibes - Cross-platform BMAD agent TTS entry point
5
+ *
6
+ * Delegates to the correct platform script:
7
+ * Windows (non-WSL) → .claude/hooks-windows/bmad-speak.ps1
8
+ * Linux/Mac/WSL → .claude/hooks/bmad-speak.sh
9
+ *
10
+ * Usage: node bin/bmad-speak.js "Agent Name" "dialogue text"
11
+ */
12
+
13
+ import { spawnSync } from 'node:child_process';
14
+ import path from 'node:path';
15
+ import fs from 'node:fs';
16
+ import os from 'node:os';
17
+
18
+ const [, , agentName, dialogue] = process.argv;
19
+
20
+ if (!agentName || !dialogue) {
21
+ process.stderr.write('Usage: bmad-speak.js "Agent Name" "dialogue text"\n');
22
+ process.exit(1);
23
+ }
24
+
25
+ const IS_WINDOWS = process.platform === 'win32' && !process.env.WSL_DISTRO_NAME;
26
+
27
+ // Resolve script path — prefer project-local, fall back to global ~/.claude install
28
+ function resolveScript(relPath) {
29
+ const cwdPath = path.join(process.cwd(), relPath);
30
+ const homePath = path.join(os.homedir(), relPath.replace(/^\.claude[\\/]/, '.claude/'));
31
+ if (fs.existsSync(cwdPath)) return cwdPath;
32
+ if (fs.existsSync(homePath)) return homePath;
33
+ return null;
34
+ }
35
+
36
+ let result;
37
+
38
+ if (IS_WINDOWS) {
39
+ const script = resolveScript('.claude/hooks-windows/bmad-speak.ps1');
40
+ if (!script) process.exit(0);
41
+ result = spawnSync(
42
+ 'powershell',
43
+ ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', script, agentName, dialogue],
44
+ { stdio: 'inherit' }
45
+ );
46
+ } else {
47
+ const script = resolveScript('.claude/hooks/bmad-speak.sh');
48
+ if (!script) process.exit(0);
49
+ result = spawnSync('bash', [script, agentName, dialogue], { stdio: 'inherit' });
50
+ }
51
+
52
+ process.exit(result.status ?? 0);
package/bin/mcp-server.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * AgentVibes MCP Server Launcher (Cross-Platform)
package/bin/test-bmad-pr CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env bash
1
+ #!/usr/bin/env bash
2
2
  #
3
3
  # AgentVibes BMAD PR Testing Command
4
4
  # Quick command to test BMAD PRs with AgentVibes integration
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "agentvibes",
4
- "version": "4.4.1",
4
+ "version": "4.5.7",
5
5
  "description": "Now your AI Agents can finally talk back! Professional TTS voice for Claude Code, Claude Desktop (via MCP), and Clawdbot with multi-provider support.",
6
6
  "homepage": "https://agentvibes.org",
7
7
  "keywords": [
package/setup-windows.ps1 CHANGED
@@ -283,8 +283,8 @@ if (-not (Test-Path $HooksDir)) {
283
283
  $HookScriptInfo = @(
284
284
  @{ Name = "play-tts.ps1"; Desc = "Main TTS router - dispatches to active provider" },
285
285
  @{ Name = "play-tts-soprano.ps1"; Desc = "Soprano neural voice provider (fastest)" },
286
- @{ Name = "play-tts-windows-piper.ps1"; Desc = "Piper offline neural voice provider" },
287
- @{ Name = "play-tts-windows-sapi.ps1"; Desc = "Windows built-in SAPI voice provider" },
286
+ @{ Name = "play-tts-piper.ps1"; Desc = "Piper offline neural voice provider" },
287
+ @{ Name = "play-tts-sapi.ps1"; Desc = "Windows built-in SAPI voice provider" },
288
288
  @{ Name = "provider-manager.ps1"; Desc = "Switch between TTS providers" },
289
289
  @{ Name = "voice-manager-windows.ps1"; Desc = "Browse and select voice models" },
290
290
  @{ Name = "audio-cache-utils.ps1"; Desc = "Manage TTS audio file cache" },
@@ -765,11 +765,11 @@ try {
765
765
  }
766
766
  }
767
767
  "piper" {
768
- & "$HooksDir\play-tts-windows-piper.ps1" $TestMessage | Out-Null
768
+ & "$HooksDir\play-tts-piper.ps1" $TestMessage | Out-Null
769
769
  Write-Ok "Piper TTS is working"
770
770
  }
771
771
  "sapi" {
772
- & "$HooksDir\play-tts-windows-sapi.ps1" $TestMessage | Out-Null
772
+ & "$HooksDir\play-tts-sapi.ps1" $TestMessage | Out-Null
773
773
  Write-Ok "Windows SAPI is working"
774
774
  }
775
775
  }
@@ -14,7 +14,9 @@ import { fileURLToPath } from 'node:url';
14
14
  import { spawnSync, execFileSync } from 'node:child_process';
15
15
  import { NavigationService, TAB_ORDER } from '../services/navigation-service.js';
16
16
  import { setupNavigation } from './navigation.js';
17
- import { createPlaceholderTab, TAB_DISPLAY_LABELS, TAB_SHORTCUT_KEYS } from './tabs/placeholder-tab.js';
17
+ import { createPlaceholderTab, TAB_DISPLAY_LABELS, TAB_SHORTCUT_KEYS, getTabLabel } from './tabs/placeholder-tab.js';
18
+ import { LanguageService } from '../services/language-service.js';
19
+ import { t } from '../i18n/strings.js';
18
20
  import { FOOTER_CONFIG, DEFAULT_FOOTER_COLOR } from './footer-config.js';
19
21
  import { createModalOverlay } from './modals/modal-overlay.js';
20
22
  import { BRAND_PINK } from './brand-colors.js';
@@ -169,7 +171,7 @@ export class AgentVibesConsole {
169
171
  });
170
172
 
171
173
  // Row 1: subtitle
172
- blessed.text({
174
+ this._headerSubtitleText = blessed.text({
173
175
  parent: this.headerBox,
174
176
  top: 1,
175
177
  left: 2,
@@ -180,7 +182,7 @@ export class AgentVibesConsole {
180
182
  });
181
183
 
182
184
  // Row 1: Quit shortcut — left-anchored after "Customization Tool" (18 chars at left:2)
183
- blessed.text({
185
+ this._headerQuitText = blessed.text({
184
186
  parent: this.headerBox,
185
187
  top: 1,
186
188
  left: 22,
@@ -291,9 +293,11 @@ export class AgentVibesConsole {
291
293
 
292
294
  // One box per tab — direct screen children at absolute top:3. No tag parsing, no wrapping.
293
295
  this._tabItems = {};
296
+ this._tabItemXOffsets = {}; // track x positions for label refresh
294
297
  let xOffset = 1;
295
298
  for (const id of TAB_ORDER) {
296
- const label = TAB_DISPLAY_LABELS[id];
299
+ const lang = this._languageService?.getLang() ?? 'en';
300
+ const label = getTabLabel(id, lang);
297
301
  const shortcutKey = TAB_SHORTCUT_KEYS[id] || label[0];
298
302
  const text = ` [${shortcutKey}] ${label} `;
299
303
  const el = blessed.box({
@@ -304,11 +308,13 @@ export class AgentVibesConsole {
304
308
  height: 1,
305
309
  content: text,
306
310
  tags: false,
311
+ wrap: false,
307
312
  keys: true,
308
313
  focusable: true,
309
314
  style: { fg: COLORS.focusCyan, bg: COLORS.tabBarBg },
310
315
  });
311
316
  this._tabItems[id] = el;
317
+ this._tabItemXOffsets[id] = xOffset;
312
318
  xOffset += text.length + 1; // 1-space gap between tabs
313
319
  }
314
320
 
@@ -359,9 +365,8 @@ export class AgentVibesConsole {
359
365
  const el = this._tabItems[tabIds[i]];
360
366
 
361
367
  // Blinking block cursor: replace trailing space with █, toggle at 500ms
362
- const _tabLabel = TAB_DISPLAY_LABELS[tabIds[i]];
363
- const _baseContent = ` [${_tabLabel[0]}] ${_tabLabel} `;
364
- const _blockContent = _baseContent.slice(0, -1) + '█';
368
+ // Always derive from current el.content so language changes are preserved.
369
+ const _getBaseContent = () => el.content.replace(/█$/, ' ');
365
370
  let _cursorInterval = null;
366
371
  let _cursorOn = false;
367
372
 
@@ -369,18 +374,21 @@ export class AgentVibesConsole {
369
374
  el.style.fg = 'white';
370
375
  el.style.bg = '#9c27b0'; // purple — cursor on this tab item
371
376
  _cursorOn = true;
372
- el.setContent(_blockContent);
377
+ const _base = _getBaseContent();
378
+ const _block = _base.slice(0, -1) + '█';
379
+ el.setContent(_block);
373
380
  this.screen.render();
374
381
  if (_cursorInterval) { clearInterval(_cursorInterval); _cursorInterval = null; }
375
382
  _cursorInterval = setInterval(() => {
376
383
  _cursorOn = !_cursorOn;
377
- el.setContent(_cursorOn ? _blockContent : _baseContent);
384
+ const b = _getBaseContent();
385
+ el.setContent(_cursorOn ? b.slice(0, -1) + '█' : b);
378
386
  this.screen.render();
379
387
  }, 500);
380
388
  });
381
389
  el.on('blur', () => {
382
390
  if (_cursorInterval) { clearInterval(_cursorInterval); _cursorInterval = null; }
383
- el.setContent(_baseContent);
391
+ el.setContent(_getBaseContent());
384
392
  // navigationService set up after _createTabBar, but blur fires lazily — safe
385
393
  this._updateTabBar(this.navigationService?.getActiveTab() ?? tabIds[0]);
386
394
  this.screen.render();
@@ -474,13 +482,47 @@ export class AgentVibesConsole {
474
482
  }
475
483
  }
476
484
 
485
+ // ---------------------------------------------------------------------------
486
+ // Private: Refresh all chrome strings (header subtitle, tab bar labels) when lang changes
487
+
488
+ _refreshChrome(lang) {
489
+ // Update header subtitle "Customization Tool"
490
+ if (this._headerSubtitleText) {
491
+ this._headerSubtitleText.setContent(`{green-fg}${t(lang, 'customizationTool')}{/green-fg}`);
492
+ }
493
+ if (this._headerQuitText) {
494
+ this._headerQuitText.setContent(`{#ef9a9a-fg}${t(lang, 'quitLabel')}{/#ef9a9a-fg}`);
495
+ }
496
+
497
+ // Update tab bar item labels — resize and reposition to fit translated labels
498
+ let xOffset = 1;
499
+ for (const id of TAB_ORDER) {
500
+ const el = this._tabItems?.[id];
501
+ if (!el) continue;
502
+ const label = getTabLabel(id, lang);
503
+ const shortcutKey = TAB_SHORTCUT_KEYS[id] || label[0];
504
+ const text = ` [${shortcutKey}] ${label} `;
505
+ el.left = xOffset;
506
+ el.width = text.length;
507
+ el.setContent(text);
508
+ xOffset += text.length + 1;
509
+ }
510
+
511
+ // Update active tab's footer text if it supports language-aware footer
512
+ const activeId = this.navigationService?.getActiveTab();
513
+ if (activeId) this._updateContextFooter(activeId);
514
+
515
+ this.screen.render();
516
+ }
517
+
477
518
  // ---------------------------------------------------------------------------
478
519
  // Private: Render tab bar content string for given active tab
479
520
  // (kept as a pure helper for unit tests; real rendering uses _updateTabBar)
480
521
 
481
522
  _renderTabBarContent(activeTabId) {
523
+ const lang = this._languageService?.getLang() ?? 'en';
482
524
  return TAB_ORDER.map(id => {
483
- const label = TAB_DISPLAY_LABELS[id];
525
+ const label = getTabLabel(id, lang);
484
526
  const shortcutKey = TAB_SHORTCUT_KEYS[id] || label[0];
485
527
  if (id === activeTabId) {
486
528
  return `{bold}{white-fg}[${shortcutKey}] ${label}{/white-fg}{/bold}`;
@@ -614,9 +656,14 @@ export class AgentVibesConsole {
614
656
  const providerService = new ProviderService(configService);
615
657
  this._configService = configService;
616
658
  this._providerService = providerService;
659
+ const languageService = new LanguageService();
660
+ this._languageService = languageService;
661
+ // Refresh UI chrome when language changes
662
+ languageService.onChange(lang => this._refreshChrome(lang));
617
663
  const services = {
618
664
  configService,
619
665
  providerService,
666
+ languageService,
620
667
  navigationService: this.navigationService,
621
668
  updateHeaderStatus: () => this._updateHeaderStatus(),
622
669
  focusMainTabBar: () => {
@@ -778,7 +825,11 @@ export class AgentVibesConsole {
778
825
  });
779
826
 
780
827
  // Register global key bindings (S/V/M/A/R/H/I/T/Esc)
781
- setupNavigation(this.screen, this.navigationService);
828
+ setupNavigation(this.screen, this.navigationService, () => {
829
+ const id = this.navigationService.getActiveTab();
830
+ const item = this._tabItems?.[id];
831
+ if (item) item.focus();
832
+ });
782
833
  }
783
834
 
784
835
  // ---------------------------------------------------------------------------
@@ -35,8 +35,9 @@ const KEY_TO_TAB = {
35
35
  *
36
36
  * @param {object} screen - Blessed screen instance (or stub in tests)
37
37
  * @param {import('../services/navigation-service.js').NavigationService} navigationService
38
+ * @param {function} [focusMainTabBar] - Optional callback to return focus to the tab bar
38
39
  */
39
- export function setupNavigation(screen, navigationService) {
40
+ export function setupNavigation(screen, navigationService, focusMainTabBar) {
40
41
  // Tab switching shortcuts — one handler per key (both cases)
41
42
  for (const [key, tabId] of Object.entries(KEY_TO_TAB)) {
42
43
  screen.key([key], () => {
@@ -53,10 +54,12 @@ export function setupNavigation(screen, navigationService) {
53
54
  }
54
55
  });
55
56
 
56
- // Escape — close modal (story 6.4 will expand modal handling)
57
+ // Escape — close modal if open, otherwise return focus to tab bar
57
58
  screen.key(['escape'], () => {
58
59
  if (navigationService.isModalOpen()) {
59
60
  navigationService.closeModal();
61
+ } else if (typeof focusMainTabBar === 'function') {
62
+ focusMainTabBar();
60
63
  }
61
64
  });
62
65
  }