agentvibes 5.7.6 → 5.9.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 +12 -5
- package/.agentvibes/install-manifest.json +188 -300
- package/.claude/audio/tracks/celestial_velvet.mp3 +0 -0
- package/.claude/commands/agent-vibes-bmad-voices.md +117 -117
- package/.claude/commands/agent-vibes-rdp.md +24 -24
- package/.claude/config/audio-effects.cfg +3 -2
- package/.claude/config/audio-effects.cfg.sample +52 -52
- package/.claude/config/background-music-enabled.txt +1 -0
- package/.claude/config/background-music-position.txt +1 -1
- package/.claude/config/language.txt +1 -0
- package/.claude/docs/TERMUX_SETUP.md +408 -408
- package/.claude/hooks/audio-cache-utils.sh +0 -0
- package/.claude/hooks/audio-processor.sh +0 -0
- package/.claude/hooks/background-music-manager.sh +0 -0
- package/.claude/hooks/bmad-party-speak.sh +27 -6
- package/.claude/hooks/bmad-speak-enhanced.sh +0 -0
- package/.claude/hooks/bmad-speak.sh +0 -0
- package/.claude/hooks/bmad-tts-injector.sh +0 -0
- package/.claude/hooks/bmad-voice-manager.sh +0 -0
- package/.claude/hooks/clawdbot-receiver-SECURE.sh +0 -0
- package/.claude/hooks/clawdbot-receiver.sh +0 -0
- package/.claude/hooks/clean-audio-cache.sh +0 -0
- package/.claude/hooks/cleanup-cache.sh +0 -0
- package/.claude/hooks/configure-rdp-mode.sh +0 -0
- package/.claude/hooks/download-extra-voices.sh +0 -0
- package/.claude/hooks/effects-manager.sh +0 -0
- package/.claude/hooks/github-star-reminder.sh +0 -0
- package/.claude/hooks/language-manager.sh +0 -0
- package/.claude/hooks/learn-manager.sh +0 -0
- package/.claude/hooks/macos-voice-manager.sh +0 -0
- package/.claude/hooks/migrate-background-music.sh +0 -0
- package/.claude/hooks/migrate-to-agentvibes.sh +0 -0
- package/.claude/hooks/optimize-background-music.sh +0 -0
- package/.claude/hooks/path-resolver.sh +0 -0
- package/.claude/hooks/personality-manager.sh +0 -0
- package/.claude/hooks/piper-download-voices.sh +0 -0
- package/.claude/hooks/piper-installer.sh +0 -0
- package/.claude/hooks/piper-multispeaker-registry.sh +0 -0
- package/.claude/hooks/piper-voice-manager.sh +0 -0
- package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +0 -0
- package/.claude/hooks/play-tts-agentvibes-receiver.sh +1 -0
- package/.claude/hooks/play-tts-enhanced.sh +0 -0
- package/.claude/hooks/play-tts-macos.sh +0 -0
- package/.claude/hooks/play-tts-piper.sh +0 -0
- package/.claude/hooks/play-tts-soprano.sh +0 -0
- package/.claude/hooks/play-tts-ssh-remote.sh +11 -8
- package/.claude/hooks/play-tts-termux-ssh.sh +0 -0
- package/.claude/hooks/play-tts-windows-receiver.sh +0 -0
- package/.claude/hooks/play-tts.sh +0 -0
- package/.claude/hooks/prepare-release.sh +0 -0
- package/.claude/hooks/provider-commands.sh +0 -0
- package/.claude/hooks/provider-manager.sh +0 -0
- package/.claude/hooks/replay-target-audio.sh +0 -0
- package/.claude/hooks/requirements.txt +6 -6
- package/.claude/hooks/sentiment-manager.sh +0 -0
- package/.claude/hooks/session-start-tts.sh +0 -0
- package/.claude/hooks/soprano-gradio-synth.py +139 -139
- package/.claude/hooks/speed-manager.sh +0 -0
- package/.claude/hooks/stop-tts.sh +0 -0
- package/.claude/hooks/termux-installer.sh +0 -0
- package/.claude/hooks/translate-manager.sh +0 -0
- package/.claude/hooks/translator.py +237 -237
- package/.claude/hooks/tts-queue-worker.sh +0 -0
- package/.claude/hooks/tts-queue.sh +0 -0
- package/.claude/hooks/verbosity-manager.sh +0 -0
- package/.claude/hooks/voice-manager.sh +0 -0
- package/.claude/hooks-windows/audio-cache-utils.ps1 +119 -119
- package/.claude/hooks-windows/audio-cache-utils.ps1.user.bak +119 -0
- package/.claude/hooks-windows/bmad-speak.ps1 +9 -38
- package/.claude/hooks-windows/play-tts-soprano.ps1 +13 -2
- package/.claude/hooks-windows/soprano-gradio-synth.py +153 -153
- package/.claude/hooks-windows/soprano-gradio-synth.py.user.bak +153 -0
- package/.claude/piper-voices-dir.txt +1 -1
- package/.claude/verbosity.txt +1 -1
- package/.clawdbot/README.md +105 -105
- package/.mcp.json +5 -14
- package/README.md +43 -2
- package/RELEASE_NOTES.md +110 -0
- package/WINDOWS-SETUP.md +208 -208
- package/bin/agent-vibes +39 -39
- package/bin/agentvibes-voice-browser.js +0 -0
- package/bin/agentvibes.js +0 -0
- package/bin/mcp-server.js +121 -121
- package/bin/mcp-server.sh +0 -0
- package/bin/test-bmad-pr +78 -78
- package/mcp-server/QUICK_START.md +203 -203
- package/mcp-server/README.md +345 -345
- package/mcp-server/WINDOWS_SETUP.md +0 -0
- package/mcp-server/examples/claude_desktop_config.json +11 -11
- package/mcp-server/examples/claude_desktop_config_piper.json +9 -9
- package/mcp-server/examples/custom_instructions.md +169 -169
- package/mcp-server/install-deps.js +0 -0
- package/mcp-server/server.py +1797 -1797
- package/mcp-server/test_server.py +0 -0
- package/package.json +1 -1
- package/src/cli/list-personalities.js +110 -110
- package/src/cli/list-voices.js +114 -114
- package/src/commands/bmad-voices.js +394 -394
- package/src/commands/install-mcp.js +476 -476
- package/src/console/audio-env.js +4 -1
- package/src/console/brand-colors.js +13 -13
- package/src/console/constants/personalities.js +44 -44
- package/src/console/tabs/agents-tab.js +85 -62
- package/src/console/tabs/help-tab.js +314 -314
- package/src/console/tabs/music-tab.js +3 -0
- package/src/console/tabs/readme-tab.js +272 -272
- package/src/console/tabs/setup-tab.js +285 -41
- package/src/console/tabs/voices-tab.js +14 -2
- package/src/console/widgets/destroy-list.js +25 -25
- package/src/console/widgets/notice.js +55 -55
- package/src/i18n/de.js +202 -202
- package/src/i18n/es.js +202 -202
- package/src/i18n/fr.js +202 -202
- package/src/i18n/hi.js +202 -202
- package/src/i18n/ja.js +202 -202
- package/src/i18n/ko.js +202 -202
- package/src/i18n/pt.js +202 -202
- package/src/i18n/strings.js +54 -54
- package/src/i18n/zh-CN.js +202 -202
- package/src/installer/language-screen.js +31 -31
- package/src/installer/music-file-input.js +304 -304
- package/src/installer.js +0 -0
- package/src/services/config-service.js +264 -264
- package/src/services/language-service.js +47 -47
- package/src/services/provider-service.js +143 -143
- package/src/utils/audio-duration-validator.js +298 -298
- package/src/utils/audio-format-validator.js +277 -277
- package/src/utils/dependency-checker.js +469 -469
- package/src/utils/file-ownership-verifier.js +358 -358
- package/src/utils/list-formatter.js +194 -194
- package/src/utils/music-file-validator.js +285 -285
- package/src/utils/preview-list-prompt.js +136 -136
- package/src/utils/secure-music-storage.js +412 -412
- package/templates/agentvibes-receiver.sh +231 -231
- package/templates/audio/welcome-music.mp3 +0 -0
- package/.claude/hooks/bmad-party-manager.sh +0 -225
- package/.claude/hooks/stop.sh +0 -38
package/src/console/audio-env.js
CHANGED
|
@@ -121,7 +121,10 @@ function _detect(players, env) {
|
|
|
121
121
|
* @returns {string|null}
|
|
122
122
|
*/
|
|
123
123
|
export function detectRemoteLlm() {
|
|
124
|
-
|
|
124
|
+
// process.env.HOME takes priority over os.homedir() so tests can inject a fake home on all platforms.
|
|
125
|
+
// On Windows production use, HOME is typically unset, so os.homedir() (reads USERPROFILE) is the fallback.
|
|
126
|
+
const homeDir = process.env.HOME ?? os.homedir();
|
|
127
|
+
const cfgPath = path.join(homeDir, '.agentvibes', 'transport-config.json');
|
|
125
128
|
if (!fs.existsSync(cfgPath)) return null;
|
|
126
129
|
try {
|
|
127
130
|
const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf-8'));
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentVibes TUI — Brand Color Constants
|
|
3
|
-
*
|
|
4
|
-
* Single source of truth for the two primary brand colors.
|
|
5
|
-
* Change BRAND_PINK or BRAND_BLUE here to update every modal title,
|
|
6
|
-
* button, and the "Vibes" logotype across the entire TUI.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/** Magenta-pink used for modal titles and the "Vibes" logotype. */
|
|
10
|
-
export const BRAND_PINK = '#f06292'; // Light magenta — Pink 300
|
|
11
|
-
|
|
12
|
-
/** Indigo blue used for default button backgrounds and primary accents. */
|
|
13
|
-
export const BRAND_BLUE = '#3949ab';
|
|
1
|
+
/**
|
|
2
|
+
* AgentVibes TUI — Brand Color Constants
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for the two primary brand colors.
|
|
5
|
+
* Change BRAND_PINK or BRAND_BLUE here to update every modal title,
|
|
6
|
+
* button, and the "Vibes" logotype across the entire TUI.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Magenta-pink used for modal titles and the "Vibes" logotype. */
|
|
10
|
+
export const BRAND_PINK = '#f06292'; // Light magenta — Pink 300
|
|
11
|
+
|
|
12
|
+
/** Indigo blue used for default button backgrounds and primary accents. */
|
|
13
|
+
export const BRAND_BLUE = '#3949ab';
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentVibes — Canonical personality constants.
|
|
3
|
-
*
|
|
4
|
-
* Single source of truth for personality names and their associated emoji
|
|
5
|
-
* glyphs. All TUI modules (settings-tab, agents-tab, personality-picker …)
|
|
6
|
-
* import from here; src/installer.js maintains its own copy because it
|
|
7
|
-
* predates the TUI and uses a different module-load path.
|
|
8
|
-
*
|
|
9
|
-
* Exported:
|
|
10
|
-
* PERSONALITY_EMOJIS — Map of personality name → emoji string
|
|
11
|
-
* PERSONALITIES — Ordered array of personality names (canonical order
|
|
12
|
-
* used for picker lists)
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
export const PERSONALITY_EMOJIS = Object.freeze({
|
|
16
|
-
angry: '😠',
|
|
17
|
-
annoying: '😤',
|
|
18
|
-
crass: '🤬',
|
|
19
|
-
dramatic: '🎭',
|
|
20
|
-
'dry-humor': '😐',
|
|
21
|
-
flirty: '😘',
|
|
22
|
-
funny: '😂',
|
|
23
|
-
grandpa: '👴',
|
|
24
|
-
millennial: '🙄',
|
|
25
|
-
moody: '😒',
|
|
26
|
-
none: '😊',
|
|
27
|
-
normal: '😊',
|
|
28
|
-
pirate: '⚓',
|
|
29
|
-
poetic: '📜',
|
|
30
|
-
professional: '👔',
|
|
31
|
-
rapper: '🎤',
|
|
32
|
-
robot: '🤖',
|
|
33
|
-
sarcastic: '😏',
|
|
34
|
-
sassy: '💁',
|
|
35
|
-
'surfer-dude':'🏄',
|
|
36
|
-
zen: '🧘',
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
export const PERSONALITIES = Object.freeze([
|
|
40
|
-
'none', 'angry', 'annoying', 'crass', 'dramatic', 'dry-humor',
|
|
41
|
-
'flirty', 'funny', 'grandpa', 'millennial', 'moody', 'normal',
|
|
42
|
-
'pirate', 'poetic', 'professional', 'rapper', 'robot', 'sarcastic',
|
|
43
|
-
'sassy', 'surfer-dude', 'zen',
|
|
44
|
-
]);
|
|
1
|
+
/**
|
|
2
|
+
* AgentVibes — Canonical personality constants.
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for personality names and their associated emoji
|
|
5
|
+
* glyphs. All TUI modules (settings-tab, agents-tab, personality-picker …)
|
|
6
|
+
* import from here; src/installer.js maintains its own copy because it
|
|
7
|
+
* predates the TUI and uses a different module-load path.
|
|
8
|
+
*
|
|
9
|
+
* Exported:
|
|
10
|
+
* PERSONALITY_EMOJIS — Map of personality name → emoji string
|
|
11
|
+
* PERSONALITIES — Ordered array of personality names (canonical order
|
|
12
|
+
* used for picker lists)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const PERSONALITY_EMOJIS = Object.freeze({
|
|
16
|
+
angry: '😠',
|
|
17
|
+
annoying: '😤',
|
|
18
|
+
crass: '🤬',
|
|
19
|
+
dramatic: '🎭',
|
|
20
|
+
'dry-humor': '😐',
|
|
21
|
+
flirty: '😘',
|
|
22
|
+
funny: '😂',
|
|
23
|
+
grandpa: '👴',
|
|
24
|
+
millennial: '🙄',
|
|
25
|
+
moody: '😒',
|
|
26
|
+
none: '😊',
|
|
27
|
+
normal: '😊',
|
|
28
|
+
pirate: '⚓',
|
|
29
|
+
poetic: '📜',
|
|
30
|
+
professional: '👔',
|
|
31
|
+
rapper: '🎤',
|
|
32
|
+
robot: '🤖',
|
|
33
|
+
sarcastic: '😏',
|
|
34
|
+
sassy: '💁',
|
|
35
|
+
'surfer-dude':'🏄',
|
|
36
|
+
zen: '🧘',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export const PERSONALITIES = Object.freeze([
|
|
40
|
+
'none', 'angry', 'annoying', 'crass', 'dramatic', 'dry-humor',
|
|
41
|
+
'flirty', 'funny', 'grandpa', 'millennial', 'moody', 'normal',
|
|
42
|
+
'pirate', 'poetic', 'professional', 'rapper', 'robot', 'sarcastic',
|
|
43
|
+
'sassy', 'surfer-dude', 'zen',
|
|
44
|
+
]);
|
|
@@ -17,6 +17,7 @@ import { formatReverbState, formatTrackName, formatVoiceName } from '../widgets/
|
|
|
17
17
|
import {
|
|
18
18
|
PIPER_VOICES_DIR, SAMPLE_PHRASES,
|
|
19
19
|
parseMultiSpeaker, scanInstalledVoices, getVoiceMeta, genderIconTag,
|
|
20
|
+
getFavorites, getThumbsDown, toggleThumbsUp, toggleThumbsDown,
|
|
20
21
|
} from './voices-tab.js';
|
|
21
22
|
import { buildAudioEnv, detectWavPlayer, detectRemoteLlm } from '../audio-env.js';
|
|
22
23
|
import { destroyList } from '../widgets/destroy-list.js';
|
|
@@ -153,7 +154,7 @@ const COL_MUSIC = 11;
|
|
|
153
154
|
const COL_VOL = 5; // e.g. "70%" or "100%"
|
|
154
155
|
|
|
155
156
|
// Inline hint appended to the selected row when list is focused
|
|
156
|
-
const _ROW_HINT_BMAD = ` {
|
|
157
|
+
const _ROW_HINT_BMAD = ` {white-fg}[Space] Preview [Enter] Configure{/white-fg}`;
|
|
157
158
|
|
|
158
159
|
// ---------------------------------------------------------------------------
|
|
159
160
|
|
|
@@ -694,7 +695,7 @@ ${_tl('bmadDesc')}
|
|
|
694
695
|
left: 2,
|
|
695
696
|
right: 2,
|
|
696
697
|
tags: true,
|
|
697
|
-
content: '{white-fg}[↑↓] Navigate [Enter] Edit [Tab] →
|
|
698
|
+
content: '{white-fg}[↑↓] Navigate [Enter] Edit [Tab] → Buttons [Esc] Close{/white-fg}',
|
|
698
699
|
style: { bg: COLORS.contentBg },
|
|
699
700
|
});
|
|
700
701
|
|
|
@@ -754,10 +755,12 @@ ${_tl('bmadDesc')}
|
|
|
754
755
|
refreshDisplay();
|
|
755
756
|
});
|
|
756
757
|
|
|
757
|
-
const
|
|
758
|
+
const saveBtn = _modalBtn('Save', 41, () => { try { _autoSaveAgent(); } catch {} _closeModal(); });
|
|
759
|
+
|
|
760
|
+
const closeBtn = _modalBtn('Close', 50, _closeModal);
|
|
758
761
|
|
|
759
762
|
// Blinking █ cursor + preview spinner — reusable across all modal buttons
|
|
760
|
-
const btnBlink = attachBtnBlink([previewBtn, resetBtn, closeBtn], screen);
|
|
763
|
+
const btnBlink = attachBtnBlink([previewBtn, resetBtn, saveBtn, closeBtn], screen);
|
|
761
764
|
|
|
762
765
|
function _closeModal() {
|
|
763
766
|
if (_closed) return;
|
|
@@ -868,6 +871,7 @@ ${_tl('bmadDesc')}
|
|
|
868
871
|
fieldList.key(['escape', 'q', 'Q'], _closeModal);
|
|
869
872
|
previewBtn.key(['escape', 'q', 'Q'], _closeModal);
|
|
870
873
|
resetBtn.key(['escape', 'q', 'Q'], _closeModal);
|
|
874
|
+
saveBtn.key(['escape', 'q', 'Q'], _closeModal);
|
|
871
875
|
closeBtn.key(['escape', 'q', 'Q'], _closeModal);
|
|
872
876
|
|
|
873
877
|
// Tab + arrow navigation within modal
|
|
@@ -895,18 +899,27 @@ ${_tl('bmadDesc')}
|
|
|
895
899
|
// Wrap: up on buttons → back to field list
|
|
896
900
|
previewBtn.key(['up'], () => { fieldList.focus(); fieldList.select(FIELDS.length - 1); screen.render(); });
|
|
897
901
|
resetBtn.key(['up'], () => { fieldList.focus(); fieldList.select(FIELDS.length - 1); screen.render(); });
|
|
902
|
+
saveBtn.key(['up'], () => { fieldList.focus(); fieldList.select(FIELDS.length - 1); screen.render(); });
|
|
898
903
|
closeBtn.key(['up'], () => { fieldList.focus(); fieldList.select(FIELDS.length - 1); screen.render(); });
|
|
899
904
|
|
|
900
905
|
previewBtn.key(['tab', 'right'], () => { resetBtn.focus(); screen.render(); });
|
|
901
906
|
previewBtn.key(['left'], () => { closeBtn.focus(); screen.render(); });
|
|
902
907
|
|
|
903
|
-
resetBtn.key(['tab', 'right'], () => {
|
|
908
|
+
resetBtn.key(['tab', 'right'], () => { saveBtn.focus(); screen.render(); });
|
|
904
909
|
resetBtn.key(['left'], () => { previewBtn.focus(); screen.render(); });
|
|
905
910
|
|
|
911
|
+
saveBtn.key(['tab', 'right'], () => { closeBtn.focus(); screen.render(); });
|
|
912
|
+
saveBtn.key(['left'], () => { resetBtn.focus(); screen.render(); });
|
|
913
|
+
|
|
906
914
|
closeBtn.key(['tab', 'right'], () => { fieldList.focus(); screen.render(); });
|
|
907
|
-
closeBtn.key(['left'], () => {
|
|
915
|
+
closeBtn.key(['left'], () => { saveBtn.focus(); screen.render(); });
|
|
908
916
|
|
|
909
917
|
fieldList.focus();
|
|
918
|
+
try {
|
|
919
|
+
for (let r = 0; r < screen.height; r++)
|
|
920
|
+
for (let c = 0; c < screen.width; c++)
|
|
921
|
+
if (screen.olines[r]?.[c]) screen.olines[r][c][0] = -1;
|
|
922
|
+
} catch {}
|
|
910
923
|
screen.render();
|
|
911
924
|
}
|
|
912
925
|
|
|
@@ -984,33 +997,38 @@ ${_tl('bmadDesc')}
|
|
|
984
997
|
},
|
|
985
998
|
});
|
|
986
999
|
|
|
987
|
-
const vpInfoLine = blessed.text({
|
|
988
|
-
parent: vpModal, bottom: 4, left: 2, right: 2, tags: true,
|
|
989
|
-
content: '', style: { fg: COLORS.labelFg, bg: COLORS.contentBg },
|
|
990
|
-
});
|
|
991
|
-
|
|
992
1000
|
const vpPreviewLine = blessed.text({
|
|
993
|
-
parent: vpModal, bottom:
|
|
1001
|
+
parent: vpModal, bottom: 4, left: 2, right: 2, height: 1, tags: true,
|
|
994
1002
|
content: '', style: { fg: 'bright-cyan', bg: COLORS.contentBg },
|
|
995
1003
|
});
|
|
996
1004
|
|
|
997
1005
|
blessed.text({
|
|
998
|
-
parent: vpModal, bottom:
|
|
999
|
-
content: '{
|
|
1006
|
+
parent: vpModal, bottom: 3, left: 2, right: 2, height: 1, tags: true,
|
|
1007
|
+
content: '{white-fg}[↑↓] Nav [PgUp/PgDn] Page [a-z] Jump{/white-fg}',
|
|
1008
|
+
style: { bg: COLORS.contentBg },
|
|
1009
|
+
});
|
|
1010
|
+
blessed.text({
|
|
1011
|
+
parent: vpModal, bottom: 2, left: 2, right: 2, height: 1, tags: true,
|
|
1012
|
+
content: '{white-fg}[Enter] Select [Space] Preview [+] 👍 [-] 👎 [Esc] Cancel{/white-fg}',
|
|
1000
1013
|
style: { bg: COLORS.contentBg },
|
|
1001
1014
|
});
|
|
1002
1015
|
|
|
1003
1016
|
function _buildVoiceItems(voices) {
|
|
1017
|
+
const favs = getFavorites(configService);
|
|
1018
|
+
const td = getThumbsDown(configService);
|
|
1004
1019
|
return voices.map(v => {
|
|
1005
1020
|
const isActive = v === draft.voice;
|
|
1006
1021
|
const isPrev = v === _previewVoiceId;
|
|
1022
|
+
const isUp = favs.includes(v);
|
|
1023
|
+
const isDown = td.includes(v);
|
|
1007
1024
|
const dot = isPrev ? '♪' : (isActive ? '●' : ' ');
|
|
1025
|
+
const star = isUp ? '{green-fg}👍{/green-fg}' : (isDown ? '{red-fg}👎{/red-fg}' : ' ');
|
|
1008
1026
|
const meta = getVoiceMeta(v);
|
|
1009
1027
|
const name = meta.displayName.length > COL_N
|
|
1010
1028
|
? meta.displayName.slice(0, COL_N - 1) + '…'
|
|
1011
1029
|
: meta.displayName.padEnd(COL_N);
|
|
1012
1030
|
// genderIconTag has invisible color tags — pad with literal spaces (1 visible char + 3 spaces = 4)
|
|
1013
|
-
return ` ${dot} ${name}${genderIconTag(meta.gender)} ${meta.provider}`;
|
|
1031
|
+
return ` ${dot}${star} ${name}${genderIconTag(meta.gender)} ${meta.provider}`;
|
|
1014
1032
|
});
|
|
1015
1033
|
}
|
|
1016
1034
|
|
|
@@ -1034,7 +1052,26 @@ ${_tl('bmadDesc')}
|
|
|
1034
1052
|
_killVP();
|
|
1035
1053
|
if (_previewMinTimer) { clearTimeout(_previewMinTimer); _previewMinTimer = null; }
|
|
1036
1054
|
|
|
1037
|
-
const phrase =
|
|
1055
|
+
const phrase = `Hi, my name is ${getVoiceMeta(voiceId).displayName}.`;
|
|
1056
|
+
const _isWin = process.platform === 'win32' && !process.env.WSL_DISTRO_NAME;
|
|
1057
|
+
|
|
1058
|
+
if (_isWin) {
|
|
1059
|
+
// Windows: route through play-tts.ps1 (same pattern as non-Windows bash route)
|
|
1060
|
+
const playTtsScript = path.join(_projectRoot, '.claude', 'hooks-windows', 'play-tts.ps1');
|
|
1061
|
+
if (!fs.existsSync(playTtsScript)) return;
|
|
1062
|
+
_previewVoiceId = voiceId;
|
|
1063
|
+
if (!_vpClosed) { vpPreviewLine.setContent(`{bright-cyan-fg}♪ Playing: ${voiceId}...{/bright-cyan-fg}`); _refreshVP(); }
|
|
1064
|
+
_previewProc = spawn('powershell', [
|
|
1065
|
+
'-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', playTtsScript, phrase, voiceId,
|
|
1066
|
+
], { stdio: 'ignore', detached: false, windowsHide: true, env: _spawnEnv });
|
|
1067
|
+
_previewProc.on('exit', () => {
|
|
1068
|
+
if (_previewVoiceId === voiceId) { _previewVoiceId = null; _previewProc = null; if (!_vpClosed) { vpPreviewLine.setContent(''); _refreshVP(); } }
|
|
1069
|
+
});
|
|
1070
|
+
_previewProc.on('error', () => { _previewProc = null; _previewVoiceId = null; });
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// Non-Windows: use bash play-tts.sh
|
|
1038
1075
|
const playTtsScript = path.join(_projectRoot, '.claude', 'hooks', 'play-tts.sh');
|
|
1039
1076
|
if (!fs.existsSync(playTtsScript)) return;
|
|
1040
1077
|
|
|
@@ -1043,34 +1080,23 @@ ${_tl('bmadDesc')}
|
|
|
1043
1080
|
if (remoteLlm) args.push('--llm', remoteLlm);
|
|
1044
1081
|
|
|
1045
1082
|
_previewProc = spawn('bash', args, {
|
|
1046
|
-
stdio: 'ignore',
|
|
1047
|
-
detached: true,
|
|
1083
|
+
stdio: 'ignore', detached: true,
|
|
1048
1084
|
env: { ..._spawnEnv, CLAUDE_PROJECT_DIR: _projectRoot },
|
|
1049
1085
|
cwd: _projectRoot,
|
|
1050
1086
|
});
|
|
1051
1087
|
_previewVoiceId = voiceId;
|
|
1052
|
-
|
|
1053
|
-
if (!_vpClosed) {
|
|
1054
|
-
vpPreviewLine.setContent(`{bright-cyan-fg}♪ Playing: ${voiceId}...{/bright-cyan-fg}`);
|
|
1055
|
-
_refreshVP();
|
|
1056
|
-
}
|
|
1088
|
+
if (!_vpClosed) { vpPreviewLine.setContent(`{bright-cyan-fg}♪ Playing: ${voiceId}...{/bright-cyan-fg}`); _refreshVP(); }
|
|
1057
1089
|
|
|
1058
1090
|
const _clearAfterMinDisplay = () => {
|
|
1059
1091
|
if (_previewVoiceId === voiceId) {
|
|
1060
|
-
_previewVoiceId = null;
|
|
1061
|
-
_previewProc = null;
|
|
1092
|
+
_previewVoiceId = null; _previewProc = null;
|
|
1062
1093
|
if (!_vpClosed) { vpPreviewLine.setContent(''); _refreshVP(); }
|
|
1063
1094
|
}
|
|
1064
1095
|
_previewMinTimer = null;
|
|
1065
1096
|
};
|
|
1066
|
-
|
|
1067
1097
|
// Keep indicator visible for at least 2s (ssh-remote exits immediately)
|
|
1068
|
-
_previewProc.on('exit', () => {
|
|
1069
|
-
|
|
1070
|
-
});
|
|
1071
|
-
_previewProc.on('error', () => {
|
|
1072
|
-
_previewMinTimer = setTimeout(_clearAfterMinDisplay, 2000);
|
|
1073
|
-
});
|
|
1098
|
+
_previewProc.on('exit', () => { _previewMinTimer = setTimeout(_clearAfterMinDisplay, 2000); });
|
|
1099
|
+
_previewProc.on('error', () => { _previewMinTimer = setTimeout(_clearAfterMinDisplay, 2000); });
|
|
1074
1100
|
}
|
|
1075
1101
|
|
|
1076
1102
|
vpList.key(['enter'], () => {
|
|
@@ -1081,7 +1107,15 @@ ${_tl('bmadDesc')}
|
|
|
1081
1107
|
const sel = _allVoices[vpList.selected];
|
|
1082
1108
|
if (sel) _previewVoice(sel);
|
|
1083
1109
|
});
|
|
1084
|
-
vpList.key(['
|
|
1110
|
+
vpList.key(['*', '+'], () => {
|
|
1111
|
+
const sel = _allVoices[vpList.selected];
|
|
1112
|
+
if (sel) { toggleThumbsUp(configService, sel); _refreshVP(); }
|
|
1113
|
+
});
|
|
1114
|
+
vpList.key(['-'], () => {
|
|
1115
|
+
const sel = _allVoices[vpList.selected];
|
|
1116
|
+
if (sel) { toggleThumbsDown(configService, sel); _refreshVP(); }
|
|
1117
|
+
});
|
|
1118
|
+
vpList.key(['escape', 'q', 'Q'], _closeVP);
|
|
1085
1119
|
|
|
1086
1120
|
// PageUp / PageDown / Home / End navigation
|
|
1087
1121
|
const _pageSize = () => Math.max(1, (vpList.height ?? 10) - 2);
|
|
@@ -1357,7 +1391,7 @@ ${_tl('bmadDesc')}
|
|
|
1357
1391
|
});
|
|
1358
1392
|
}
|
|
1359
1393
|
|
|
1360
|
-
/** Windows full-effects preview: temporarily patches
|
|
1394
|
+
/** Windows full-effects preview: temporarily patches personality/reverb, passes music via env vars */
|
|
1361
1395
|
function _sampleWithFullEffectsWindows(gen, agent, profile, phrase, onComplete) {
|
|
1362
1396
|
const _spawnEnv = buildAudioEnv();
|
|
1363
1397
|
const homeDir = process.env.USERPROFILE || os.homedir();
|
|
@@ -1365,38 +1399,35 @@ ${_tl('bmadDesc')}
|
|
|
1365
1399
|
const claudeDir = fs.existsSync(path.join(_projectRoot, '.claude'))
|
|
1366
1400
|
? path.join(_projectRoot, '.claude')
|
|
1367
1401
|
: path.join(homeDir, '.claude');
|
|
1368
|
-
const configDir
|
|
1369
|
-
const hooksDir
|
|
1370
|
-
const playTts
|
|
1402
|
+
const configDir = path.join(claudeDir, 'config');
|
|
1403
|
+
const hooksDir = path.join(claudeDir, 'hooks-windows');
|
|
1404
|
+
const playTts = path.join(hooksDir, 'play-tts.ps1');
|
|
1371
1405
|
if (!fs.existsSync(playTts)) { _sampleWithPiperDirect(gen, profile.voice || '', phrase); return; }
|
|
1372
1406
|
|
|
1373
|
-
|
|
1374
|
-
const
|
|
1375
|
-
const reverbFile = path.join(configDir, 'reverb-level.txt');
|
|
1376
|
-
const bgEnabledFile = path.join(configDir, 'background-music-enabled.txt');
|
|
1377
|
-
const audioEffectsCfg = path.join(configDir, 'audio-effects.cfg');
|
|
1407
|
+
const personalityFile = path.join(configDir, 'personality.txt');
|
|
1408
|
+
const reverbFile = path.join(configDir, 'reverb-level.txt');
|
|
1378
1409
|
|
|
1379
|
-
// Save originals
|
|
1380
1410
|
const _read = f => { try { return fs.readFileSync(f, 'utf8'); } catch { return null; } };
|
|
1381
1411
|
const origPersonality = _read(personalityFile);
|
|
1382
1412
|
const origReverb = _read(reverbFile);
|
|
1383
|
-
const origBgEnabled = _read(bgEnabledFile);
|
|
1384
|
-
const origAudioEffects = _read(audioEffectsCfg);
|
|
1385
1413
|
|
|
1386
|
-
const bgMusic
|
|
1387
|
-
|
|
1414
|
+
const bgMusic = profile.backgroundMusic;
|
|
1415
|
+
|
|
1416
|
+
// Build spawn env — pass per-agent music via AGENTVIBES_OVERRIDE_* env vars.
|
|
1417
|
+
// play-tts.ps1 reads these directly and forces BgEnabled=true when set,
|
|
1418
|
+
// so no config file patching is needed for background music.
|
|
1419
|
+
const spawnEnv = { ..._spawnEnv, AGENTVIBES_AGENT_NAME: agent.id, CLAUDE_PROJECT_DIR: _projectRoot };
|
|
1420
|
+
if (bgMusic?.enabled && bgMusic?.track) {
|
|
1421
|
+
const vol = ((bgMusic.volume ?? 20) / 100).toFixed(2);
|
|
1422
|
+
spawnEnv.AGENTVIBES_OVERRIDE_MUSIC = bgMusic.track;
|
|
1423
|
+
spawnEnv.AGENTVIBES_OVERRIDE_VOLUME = vol;
|
|
1424
|
+
}
|
|
1388
1425
|
|
|
1389
1426
|
try {
|
|
1390
1427
|
if (profile.personality && profile.personality !== 'none')
|
|
1391
1428
|
fs.writeFileSync(personalityFile, profile.personality);
|
|
1392
1429
|
if (profile.reverbPreset)
|
|
1393
1430
|
fs.writeFileSync(reverbFile, profile.reverbPreset);
|
|
1394
|
-
if (bgMusic?.enabled && bgMusic?.track) {
|
|
1395
|
-
fs.writeFileSync(bgEnabledFile, 'true');
|
|
1396
|
-
const vol = ((bgMusic.volume ?? 20) / 100).toFixed(2);
|
|
1397
|
-
tempCfgLine = `${agent.id}||${bgMusic.track}|${vol}`;
|
|
1398
|
-
fs.writeFileSync(audioEffectsCfg, `${tempCfgLine}\n${origAudioEffects || ''}`);
|
|
1399
|
-
}
|
|
1400
1431
|
} catch { /* degrade gracefully */ }
|
|
1401
1432
|
|
|
1402
1433
|
const voiceId = profile.voice || '';
|
|
@@ -1404,10 +1435,7 @@ ${_tl('bmadDesc')}
|
|
|
1404
1435
|
if (voiceId) psArgs.push(voiceId);
|
|
1405
1436
|
|
|
1406
1437
|
const proc = spawn('powershell', psArgs, {
|
|
1407
|
-
stdio: 'ignore', detached: false, windowsHide: true,
|
|
1408
|
-
// CLAUDE_PROJECT_DIR tells play-tts.ps1 to use the project's .claude/config
|
|
1409
|
-
// rather than falling back to ~/.claude where our patches don't exist.
|
|
1410
|
-
env: { ..._spawnEnv, AGENTVIBES_AGENT_NAME: agent.id, CLAUDE_PROJECT_DIR: _projectRoot },
|
|
1438
|
+
stdio: 'ignore', detached: false, windowsHide: true, env: spawnEnv,
|
|
1411
1439
|
});
|
|
1412
1440
|
_playingProcess = proc;
|
|
1413
1441
|
|
|
@@ -1416,11 +1444,6 @@ ${_tl('bmadDesc')}
|
|
|
1416
1444
|
if (origPersonality !== null) fs.writeFileSync(personalityFile, origPersonality);
|
|
1417
1445
|
else try { fs.unlinkSync(personalityFile); } catch {}
|
|
1418
1446
|
if (origReverb !== null) fs.writeFileSync(reverbFile, origReverb);
|
|
1419
|
-
if (bgMusic?.enabled && bgMusic?.track) {
|
|
1420
|
-
if (origBgEnabled !== null) fs.writeFileSync(bgEnabledFile, origBgEnabled);
|
|
1421
|
-
else try { fs.unlinkSync(bgEnabledFile); } catch {}
|
|
1422
|
-
if (origAudioEffects !== null) fs.writeFileSync(audioEffectsCfg, origAudioEffects);
|
|
1423
|
-
}
|
|
1424
1447
|
} catch {}
|
|
1425
1448
|
}
|
|
1426
1449
|
|