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.
- package/.agentvibes/config.json +4 -4
- package/.claude/config/reverb-level.txt +1 -1
- package/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks-windows/bmad-speak.ps1 +112 -0
- package/.claude/hooks-windows/play-tts-piper.ps1 +3 -4
- package/.claude/hooks-windows/play-tts-sapi.ps1 +3 -4
- package/.claude/hooks-windows/play-tts-soprano.ps1 +2 -3
- package/.claude/hooks-windows/play-tts-termux-ssh.ps1 +138 -0
- package/.claude/hooks-windows/play-tts.ps1 +14 -6
- package/.claude/hooks-windows/provider-manager.ps1 +16 -1
- package/CLAUDE.md +4 -0
- package/README.md +39 -9
- package/RELEASE_NOTES.md +39 -0
- package/bin/agent-vibes +1 -1
- package/bin/agentvibes-voice-browser.js +1 -1
- package/bin/bmad-speak.js +52 -0
- package/bin/mcp-server.js +1 -1
- package/bin/test-bmad-pr +1 -1
- package/package.json +1 -1
- package/setup-windows.ps1 +4 -4
- package/src/console/app.js +58 -11
- package/src/console/tabs/agents-tab.js +61 -65
- package/src/console/tabs/help-tab.js +107 -54
- package/src/console/tabs/install-tab.js +107 -47
- package/src/console/tabs/music-tab.js +1030 -1011
- package/src/console/tabs/placeholder-tab.js +27 -0
- package/src/console/tabs/readme-tab.js +9 -7
- package/src/console/tabs/receiver-tab.js +23 -12
- package/src/console/tabs/settings-tab.js +4001 -3783
- package/src/console/tabs/voices-tab.js +1680 -1653
- package/src/console/widgets/personality-picker.js +35 -7
- package/src/console/widgets/reverb-picker.js +9 -6
- package/src/console/widgets/track-picker.js +6 -1
- package/src/i18n/de.js +201 -0
- package/src/i18n/en.js +201 -0
- package/src/i18n/es.js +201 -0
- package/src/i18n/fr.js +201 -0
- package/src/i18n/hi.js +201 -0
- package/src/i18n/ja.js +201 -0
- package/src/i18n/ko.js +201 -0
- package/src/i18n/pt.js +201 -0
- package/src/i18n/strings.js +54 -0
- package/src/i18n/zh-CN.js +201 -0
- package/src/installer/language-screen.js +31 -0
- package/src/installer.js +79 -25
- package/src/services/language-service.js +47 -0
- package/src/utils/file-ownership-verifier.js +2 -2
- package/src/utils/provider-validator.js +9 -13
- package/.claude/hooks-windows/play-tts-windows-piper.ps1 +0 -209
- 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
|
|
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: () =>
|
|
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
|
|
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(
|
|
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 ? '' :
|
|
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
|
|
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}
|
|
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:
|
|
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: () =>
|
|
1480
|
+
getFooterText: () => _tl('receiverFooter'),
|
|
1470
1481
|
getFooterColor: () => COLORS.footerBg,
|
|
1471
1482
|
};
|
|
1472
1483
|
}
|