agentvibes 4.4.1 → 4.5.0

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 (50) hide show
  1. package/.agentvibes/config.json +4 -4
  2. package/.claude/config/reverb-level.txt +1 -1
  3. package/.claude/github-star-reminder.txt +1 -1
  4. package/.claude/hooks-windows/bmad-speak.ps1 +112 -0
  5. package/.claude/hooks-windows/play-tts-piper.ps1 +3 -4
  6. package/.claude/hooks-windows/play-tts-sapi.ps1 +3 -4
  7. package/.claude/hooks-windows/play-tts-soprano.ps1 +2 -3
  8. package/.claude/hooks-windows/play-tts-termux-ssh.ps1 +138 -0
  9. package/.claude/hooks-windows/play-tts.ps1 +14 -6
  10. package/.claude/hooks-windows/provider-manager.ps1 +16 -1
  11. package/CLAUDE.md +4 -0
  12. package/README.md +39 -9
  13. package/RELEASE_NOTES.md +39 -0
  14. package/bin/agent-vibes +1 -1
  15. package/bin/agentvibes-voice-browser.js +1 -1
  16. package/bin/bmad-speak.js +52 -0
  17. package/bin/mcp-server.js +1 -1
  18. package/bin/test-bmad-pr +1 -1
  19. package/package.json +1 -1
  20. package/setup-windows.ps1 +4 -4
  21. package/src/console/app.js +58 -11
  22. package/src/console/tabs/agents-tab.js +61 -65
  23. package/src/console/tabs/help-tab.js +107 -54
  24. package/src/console/tabs/install-tab.js +107 -47
  25. package/src/console/tabs/music-tab.js +1030 -1011
  26. package/src/console/tabs/placeholder-tab.js +27 -0
  27. package/src/console/tabs/readme-tab.js +9 -7
  28. package/src/console/tabs/receiver-tab.js +23 -12
  29. package/src/console/tabs/settings-tab.js +4001 -3783
  30. package/src/console/tabs/voices-tab.js +1680 -1653
  31. package/src/console/widgets/personality-picker.js +35 -7
  32. package/src/console/widgets/reverb-picker.js +9 -6
  33. package/src/console/widgets/track-picker.js +6 -1
  34. package/src/i18n/de.js +201 -0
  35. package/src/i18n/en.js +201 -0
  36. package/src/i18n/es.js +201 -0
  37. package/src/i18n/fr.js +201 -0
  38. package/src/i18n/hi.js +201 -0
  39. package/src/i18n/ja.js +201 -0
  40. package/src/i18n/ko.js +201 -0
  41. package/src/i18n/pt.js +201 -0
  42. package/src/i18n/strings.js +54 -0
  43. package/src/i18n/zh-CN.js +201 -0
  44. package/src/installer/language-screen.js +31 -0
  45. package/src/installer.js +79 -25
  46. package/src/services/language-service.js +47 -0
  47. package/src/utils/file-ownership-verifier.js +2 -2
  48. package/src/utils/provider-validator.js +9 -13
  49. package/.claude/hooks-windows/play-tts-windows-piper.ps1 +0 -209
  50. package/.claude/hooks-windows/play-tts-windows-sapi.ps1 +0 -108
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import blessed from 'blessed';
10
+ import { t } from '../../i18n/strings.js';
10
11
 
11
12
  /**
12
13
  * Create a hidden placeholder box for a tab, appended into the content area.
@@ -51,3 +52,29 @@ export const TAB_SHORTCUT_KEYS = {
51
52
  agents: 'B',
52
53
  receiver: 'X',
53
54
  };
55
+
56
+ /**
57
+ * Return the translated display label for a tab.
58
+ * Falls back to TAB_DISPLAY_LABELS[id] if no i18n key is found.
59
+ *
60
+ * @param {string} id - Tab identifier (e.g. 'settings', 'voices')
61
+ * @param {string} lang - BCP-47 language code (e.g. 'es', 'zh-CN')
62
+ * @returns {string}
63
+ */
64
+ export function getTabLabel(id, lang = 'en') {
65
+ const keyMap = {
66
+ install: 'tabInstall',
67
+ settings: 'tabSettings',
68
+ voices: 'tabVoices',
69
+ music: 'tabMusic',
70
+ agents: 'tabBmad',
71
+ receiver: 'tabReceiver',
72
+ readme: 'tabReadme',
73
+ help: 'tabHelp',
74
+ };
75
+ const key = keyMap[id];
76
+ if (!key) return TAB_DISPLAY_LABELS[id] ?? id;
77
+ const translated = t(lang, key);
78
+ // t() returns the key itself when missing — fall back to English display label
79
+ return (translated && translated !== key) ? translated : (TAB_DISPLAY_LABELS[id] ?? id);
80
+ }
@@ -10,6 +10,7 @@
10
10
 
11
11
  import fs from 'node:fs';
12
12
  import path from 'node:path';
13
+ import { t } from '../../i18n/strings.js';
13
14
 
14
15
  const IS_TEST = process.env.AGENTVIBES_TEST_MODE === 'true';
15
16
 
@@ -34,7 +35,7 @@ const COLORS = {
34
35
  footerBg: '#455a64', // Dark gray — Readme tab footer
35
36
  };
36
37
 
37
- const FOOTER_TEXT = '[↑↓/jk] Scroll [PgUp/PgDn] Page [/] Search [S/V/M/A/R] Tab [Q] Quit';
38
+ const _FOOTER_TEXT_EN = '[↑↓/jk] Scroll [PgUp/PgDn] Page [/] Search [S/V/M/A/R] Tab [Q] Quit';
38
39
 
39
40
  // ---------------------------------------------------------------------------
40
41
  // Markdown renderer (story 13.2)
@@ -106,7 +107,7 @@ function createTestStub() {
106
107
  hide: () => {},
107
108
  onFocus: () => {},
108
109
  onBlur: () => {},
109
- getFooterText: () => FOOTER_TEXT,
110
+ getFooterText: () => _FOOTER_TEXT_EN,
110
111
  getFooterColor: () => COLORS.footerBg,
111
112
  };
112
113
  }
@@ -123,7 +124,8 @@ function createTestStub() {
123
124
  export function createReadmeTab(screen, services) {
124
125
  if (IS_TEST) return createTestStub();
125
126
 
126
- const { focusMainTabBar } = services;
127
+ const { focusMainTabBar, languageService } = services;
128
+ const _tl = (key) => languageService ? languageService.t(key) : t('en', key);
127
129
 
128
130
  // -------------------------------------------------------------------------
129
131
  // Container
@@ -157,7 +159,7 @@ export function createReadmeTab(screen, services) {
157
159
  }
158
160
  }
159
161
  }
160
- return '# README\n\n*(No README.md found in current directory)*';
162
+ return `# README\n\n${_tl('readmeNotFound')}`;
161
163
  }
162
164
 
163
165
  // -------------------------------------------------------------------------
@@ -197,7 +199,7 @@ export function createReadmeTab(screen, services) {
197
199
  const markdown = _loadReadme();
198
200
  const rendered = renderMarkdown(markdown);
199
201
  scrollBox.setContent(rendered);
200
- scrollIndicator.setContent('{#607d8b-fg}↓ Scroll for more content ↓{/#607d8b-fg}');
202
+ scrollIndicator.setContent(`{#607d8b-fg}${_tl('readmeScrollMore')}{/#607d8b-fg}`);
201
203
  screen.render();
202
204
  }
203
205
 
@@ -205,7 +207,7 @@ export function createReadmeTab(screen, services) {
205
207
  scrollBox.on('scroll', () => {
206
208
  const atBottom = scrollBox.getScrollPerc() >= 99;
207
209
  scrollIndicator.setContent(
208
- atBottom ? '' : '{#607d8b-fg}↓ Scroll for more content ↓{/#607d8b-fg}'
210
+ atBottom ? '' : `{#607d8b-fg}${_tl('readmeScrollMore')}{/#607d8b-fg}`
209
211
  );
210
212
  screen.render();
211
213
  });
@@ -257,7 +259,7 @@ export function createReadmeTab(screen, services) {
257
259
  onBlur() {},
258
260
 
259
261
  getFooterText() {
260
- return FOOTER_TEXT;
262
+ return _tl('readmeFooter');
261
263
  },
262
264
 
263
265
  getFooterColor() {
@@ -14,6 +14,7 @@ import { execSync, spawnSync, spawn } from 'node:child_process';
14
14
  import path from 'node:path';
15
15
  import { homedir } from 'node:os';
16
16
  import { fileURLToPath } from 'node:url';
17
+ import { t } from '../../i18n/strings.js';
17
18
 
18
19
  const IS_TEST = process.env.AGENTVIBES_TEST_MODE === 'true';
19
20
 
@@ -767,6 +768,9 @@ function _buildDetailedInstructions(receiverAlias, receiverScript, networkInfo)
767
768
  export function createReceiverTab(screen, services) {
768
769
  if (IS_TEST) return createTestStub();
769
770
 
771
+ const { languageService, focusMainTabBar } = services || {};
772
+ const _tl = (key) => languageService ? languageService.t(key) : t('en', key);
773
+
770
774
  const AGENTVIBES_DIR = path.join(homedir(), '.agentvibes');
771
775
  const RECEIVER_SCRIPT = path.join(AGENTVIBES_DIR, 'play-remote.sh');
772
776
  const RECEIVER_ALIAS = 'my-receiver';
@@ -791,21 +795,18 @@ export function createReceiverTab(screen, services) {
791
795
  width: '100%',
792
796
  bottom: 2,
793
797
  hidden: true,
798
+ keys: true,
794
799
  style: { fg: COLORS.labelFg, bg: COLORS.contentBg },
795
800
  border: { type: 'line' },
796
801
  borderStyle: { fg: COLORS.borderFg },
797
802
  });
798
803
 
804
+ box.key(['escape'], () => {
805
+ if (typeof focusMainTabBar === 'function') { focusMainTabBar(); screen.render(); }
806
+ });
807
+
799
808
  // -------------------------------------------------------------------------
800
809
  // Description text (collapsible)
801
- const DESC_TEXT = [
802
- 'SSH Receiver lets your remote servers speak through this machine.',
803
- 'When an AI assistant on a remote server (VPS, cloud, dev box) needs',
804
- 'to play TTS audio, it sends the text over SSH to this machine, which',
805
- 'generates and plays the audio through your speakers locally.',
806
- '',
807
- 'Remote AI ──[SSH]──► This Machine ──[piper+sox+ffmpeg]──► Your Speakers',
808
- ].join('\n');
809
810
 
810
811
  const descBox = blessed.box({
811
812
  parent: box,
@@ -816,7 +817,7 @@ export function createReceiverTab(screen, services) {
816
817
  tags: true,
817
818
  hidden: true,
818
819
  border: { type: 'line' },
819
- label: ` {bold}What is SSH Receiver?{/bold} `,
820
+ label: ` {bold}${_tl('receiverWhatIsTitle')}{/bold} `,
820
821
  style: {
821
822
  fg: COLORS.labelFg,
822
823
  bg: '#111827',
@@ -824,12 +825,12 @@ export function createReceiverTab(screen, services) {
824
825
  },
825
826
  });
826
827
 
827
- blessed.text({
828
+ const descText = blessed.text({
828
829
  parent: descBox,
829
830
  top: 0,
830
831
  left: 1,
831
832
  tags: true,
832
- content: DESC_TEXT,
833
+ content: _tl('receiverDesc'),
833
834
  style: { fg: '#b0bec5', bg: '#111827' },
834
835
  });
835
836
 
@@ -1451,6 +1452,16 @@ export function createReceiverTab(screen, services) {
1451
1452
  });
1452
1453
 
1453
1454
  // -------------------------------------------------------------------------
1455
+ // Language change handler
1456
+
1457
+ if (languageService) {
1458
+ languageService.onChange(() => {
1459
+ descBox.setLabel(` {bold}${_tl('receiverWhatIsTitle')}{/bold} `);
1460
+ descText.setContent(_tl('receiverDesc'));
1461
+ screen.render();
1462
+ });
1463
+ }
1464
+
1454
1465
  // Tab Component Contract
1455
1466
 
1456
1467
  return {
@@ -1466,7 +1477,7 @@ export function createReceiverTab(screen, services) {
1466
1477
  },
1467
1478
  onFocus() { box.focus(); },
1468
1479
  onBlur() {},
1469
- getFooterText: () => FOOTER_TEXT,
1480
+ getFooterText: () => _tl('receiverFooter'),
1470
1481
  getFooterColor: () => COLORS.footerBg,
1471
1482
  };
1472
1483
  }