agentvibes 5.7.7 → 5.10.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.
- package/.agentvibes/config.json +0 -2
- package/.claude/config/audio-effects.cfg +4 -4
- package/.claude/config/background-music-enabled.txt +1 -0
- package/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks/play-tts-piper.sh +20 -13
- package/.claude/hooks/play-tts-ssh-remote.sh +2 -2
- package/.claude/hooks/voice-manager.sh +6 -0
- package/.claude/hooks-windows/audio-cache-utils.ps1 +119 -119
- package/.claude/hooks-windows/bmad-speak.ps1 +9 -38
- package/.claude/hooks-windows/play-tts-soprano.ps1 +13 -2
- package/.claude/hooks-windows/play-tts-windows-piper.ps1 +22 -16
- package/.mcp.json +13 -9
- package/README.md +33 -2
- package/RELEASE_NOTES.md +80 -0
- package/mcp-server/server.py +17 -7
- package/package.json +2 -2
- package/src/commands/install-mcp.js +270 -16
- package/src/console/app.js +3 -3
- package/src/console/audio-env.js +4 -1
- package/src/console/tabs/agents-tab.js +89 -66
- package/src/console/tabs/music-tab.js +4 -3
- package/src/console/tabs/receiver-tab.js +13 -13
- package/src/console/tabs/settings-tab.js +2 -2
- package/src/console/tabs/setup-tab.js +291 -47
- package/src/console/tabs/voices-tab.js +17 -5
- package/src/console/widgets/personality-picker.js +2 -2
- package/src/console/widgets/reverb-picker.js +1 -1
- package/src/installer.js +32 -27
- package/src/services/provider-service.js +1 -1
- package/src/services/tts-engine-service.js +2 -2
- package/src/utils/audio-duration-validator.js +2 -2
- package/src/utils/list-formatter.js +9 -3
- package/src/utils/platform-resolver.js +369 -0
- package/src/utils/provider-validator.js +9 -9
- package/.agentvibes/install-manifest.json +0 -442
- package/.claude/config/background-music-position.txt +0 -27
- package/.claude/config/background-music-volume.txt +0 -1
- package/.claude/config/background-music.cfg +0 -1
- package/.claude/config/background-music.txt +0 -1
- package/.claude/config/reverb-level.txt +0 -1
- package/.claude/config/tts-speech-rate.txt +0 -1
- package/.claude/config/tts-verbosity.txt +0 -1
- package/.claude/hooks/bmad-party-manager.sh +0 -225
- package/.claude/hooks/stop.sh +0 -38
- package/.claude/piper-voices-dir.txt +0 -1
- /package/.claude/audio/tracks/{CelestialVelvet.mp3 → celestial_velvet.mp3} +0 -0
|
@@ -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';
|
|
@@ -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', [ // NOSONAR
|
|
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
|
|
|
@@ -1042,35 +1079,24 @@ ${_tl('bmadDesc')}
|
|
|
1042
1079
|
const args = [playTtsScript, phrase, voiceId];
|
|
1043
1080
|
if (remoteLlm) args.push('--llm', remoteLlm);
|
|
1044
1081
|
|
|
1045
|
-
_previewProc = spawn('bash', args, {
|
|
1046
|
-
stdio: 'ignore',
|
|
1047
|
-
detached: true,
|
|
1082
|
+
_previewProc = spawn('bash', args, { // NOSONAR
|
|
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);
|
|
@@ -1243,7 +1277,7 @@ ${_tl('bmadDesc')}
|
|
|
1243
1277
|
|
|
1244
1278
|
const voiceId = profile.voice || '';
|
|
1245
1279
|
const pretext = profile.pretext || AgentVoiceStore.getDefaultPretext(agent.displayName, agent.title);
|
|
1246
|
-
const phrase = `${pretext} ${SAMPLE_PHRASES[Math.floor(Math.random() * SAMPLE_PHRASES.length)]}`;
|
|
1280
|
+
const phrase = `${pretext} ${SAMPLE_PHRASES[Math.floor(Math.random() * SAMPLE_PHRASES.length)]}`; // NOSONAR
|
|
1247
1281
|
|
|
1248
1282
|
const isWindows = process.platform === 'win32' && !process.env.WSL_DISTRO_NAME;
|
|
1249
1283
|
|
|
@@ -1261,7 +1295,7 @@ ${_tl('bmadDesc')}
|
|
|
1261
1295
|
if (voiceId) args.push(voiceId);
|
|
1262
1296
|
if (remoteLlm) args.push('--llm', remoteLlm);
|
|
1263
1297
|
|
|
1264
|
-
const proc = spawn('bash', args, {
|
|
1298
|
+
const proc = spawn('bash', args, { // NOSONAR
|
|
1265
1299
|
stdio: ['ignore', 'ignore', 'ignore'],
|
|
1266
1300
|
detached: true,
|
|
1267
1301
|
env: { ..._spawnEnv },
|
|
@@ -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,49 +1399,43 @@ ${_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 || '';
|
|
1403
1434
|
const psArgs = ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', playTts, phrase];
|
|
1404
1435
|
if (voiceId) psArgs.push(voiceId);
|
|
1405
1436
|
|
|
1406
|
-
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 },
|
|
1437
|
+
const proc = spawn('powershell', psArgs, { // NOSONAR
|
|
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
|
|
|
@@ -1434,7 +1457,7 @@ ${_tl('bmadDesc')}
|
|
|
1434
1457
|
function _shuffleArray(arr) {
|
|
1435
1458
|
const a = [...arr];
|
|
1436
1459
|
for (let i = a.length - 1; i > 0; i--) {
|
|
1437
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
1460
|
+
const j = Math.floor(Math.random() * (i + 1)); // NOSONAR
|
|
1438
1461
|
[a[i], a[j]] = [a[j], a[i]];
|
|
1439
1462
|
}
|
|
1440
1463
|
return a;
|
|
@@ -64,7 +64,8 @@ const TRACK_DISPLAY = Object.freeze({
|
|
|
64
64
|
'agent_vibes_arabic_v2_loop.mp3': '🎵 Arabic Oud',
|
|
65
65
|
'agent_vibes_bachata_v1_loop.mp3': '🎺 Bachata',
|
|
66
66
|
'agent_vibes_bossa_nova_v2_loop.mp3': '🌸 Bossa Nova',
|
|
67
|
-
'
|
|
67
|
+
'celestial_velvet.mp3': '🌌 Celestial Velvet',
|
|
68
|
+
'CelestialVelvet.mp3': '🌌 Celestial Velvet', // legacy alias — display only, file renamed in v5.7.7
|
|
68
69
|
'agent_vibes_celtic_harp_v1_loop.mp3': '🎶 Celtic Harp',
|
|
69
70
|
'agent_vibes_chillwave_v2_loop.mp3': '🌊 Chillwave',
|
|
70
71
|
'agent_vibes_cumbia_v1_loop.mp3': '🎸 Cumbia',
|
|
@@ -86,7 +87,7 @@ const BUILT_IN_TRACK_CATALOG = Object.freeze([
|
|
|
86
87
|
{ id: 'agent_vibes_arabic_v2_loop.mp3', label: '🎵 Arabic Oud' },
|
|
87
88
|
{ id: 'agent_vibes_bachata_v1_loop.mp3', label: '🎺 Bachata' },
|
|
88
89
|
{ id: 'agent_vibes_bossa_nova_v2_loop.mp3', label: '🌸 Bossa Nova' },
|
|
89
|
-
{ id: '
|
|
90
|
+
{ id: 'celestial_velvet.mp3', label: '🌌 Celestial Velvet' },
|
|
90
91
|
{ id: 'agent_vibes_celtic_harp_v1_loop.mp3', label: '🎶 Celtic Harp' },
|
|
91
92
|
{ id: 'agent_vibes_chillwave_v2_loop.mp3', label: '🌊 Chillwave' },
|
|
92
93
|
{ id: 'agent_vibes_cumbia_v1_loop.mp3', label: '🎸 Cumbia' },
|
|
@@ -548,7 +549,7 @@ export function createMusicTab(screen, services) {
|
|
|
548
549
|
try {
|
|
549
550
|
if (_isWin) {
|
|
550
551
|
// Windows: kill the process tree via taskkill (process group kill doesn't work)
|
|
551
|
-
spawn('taskkill', ['/F', '/T', '/PID', String(_playingProcess.pid)], {
|
|
552
|
+
spawn('taskkill', ['/F', '/T', '/PID', String(_playingProcess.pid)], { // NOSONAR
|
|
552
553
|
stdio: 'ignore', windowsHide: true,
|
|
553
554
|
});
|
|
554
555
|
} else {
|
|
@@ -73,10 +73,10 @@ function _getNetworkInfo() {
|
|
|
73
73
|
try {
|
|
74
74
|
if (isWin) {
|
|
75
75
|
// Use PowerShell to get local IP on Windows
|
|
76
|
-
localIp = execSync('powershell -NoProfile -Command "(Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.InterfaceAlias -notmatch \'Loopback\' } | Select-Object -First 1).IPAddress"',
|
|
76
|
+
localIp = execSync('powershell -NoProfile -Command "(Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.InterfaceAlias -notmatch \'Loopback\' } | Select-Object -First 1).IPAddress"', // NOSONAR
|
|
77
77
|
{ timeout: 5000, stdio: 'pipe' }).toString().trim();
|
|
78
78
|
} else {
|
|
79
|
-
localIp = execSync("hostname -I 2>/dev/null | awk '{print $1}'", { timeout: 3000, stdio: 'pipe' }).toString().trim();
|
|
79
|
+
localIp = execSync("hostname -I 2>/dev/null | awk '{print $1}'", { timeout: 3000, stdio: 'pipe' }).toString().trim(); // NOSONAR
|
|
80
80
|
}
|
|
81
81
|
} catch { /* ignore */ }
|
|
82
82
|
try {
|
|
@@ -86,7 +86,7 @@ function _getNetworkInfo() {
|
|
|
86
86
|
const m = sshdConf.match(/^Port\s+(\d+)/m);
|
|
87
87
|
if (m) sshPort = m[1];
|
|
88
88
|
} else {
|
|
89
|
-
const portLine = execSync("grep -E '^Port ' /etc/ssh/sshd_config 2>/dev/null || echo 'Port 22'", { timeout: 3000, stdio: 'pipe' }).toString().trim();
|
|
89
|
+
const portLine = execSync("grep -E '^Port ' /etc/ssh/sshd_config 2>/dev/null || echo 'Port 22'", { timeout: 3000, stdio: 'pipe' }).toString().trim(); // NOSONAR
|
|
90
90
|
const m = portLine.match(/^Port\s+(\d+)/);
|
|
91
91
|
if (m) sshPort = m[1];
|
|
92
92
|
}
|
|
@@ -130,7 +130,7 @@ function _detectSetupState() {
|
|
|
130
130
|
|
|
131
131
|
// Check sshd running
|
|
132
132
|
try {
|
|
133
|
-
const svc = execSync('powershell -NoProfile -Command "(Get-Service sshd -EA SilentlyContinue).Status"',
|
|
133
|
+
const svc = execSync('powershell -NoProfile -Command "(Get-Service sshd -EA SilentlyContinue).Status"', // NOSONAR
|
|
134
134
|
{ timeout: 5000, stdio: 'pipe' }).toString().trim();
|
|
135
135
|
state.sshdRunning = svc === 'Running';
|
|
136
136
|
} catch { /* sshd not installed */ }
|
|
@@ -143,13 +143,13 @@ function _detectSetupState() {
|
|
|
143
143
|
|
|
144
144
|
// Check ffmpeg
|
|
145
145
|
try {
|
|
146
|
-
execSync('where ffmpeg', { timeout: 3000, stdio: 'pipe' });
|
|
146
|
+
execSync('where ffmpeg', { timeout: 3000, stdio: 'pipe' }); // NOSONAR
|
|
147
147
|
state.ffmpegInstalled = true;
|
|
148
148
|
} catch { /* not found */ }
|
|
149
149
|
|
|
150
150
|
// Check piper
|
|
151
151
|
try {
|
|
152
|
-
execSync('where piper', { timeout: 3000, stdio: 'pipe' });
|
|
152
|
+
execSync('where piper', { timeout: 3000, stdio: 'pipe' }); // NOSONAR
|
|
153
153
|
state.piperInstalled = true;
|
|
154
154
|
} catch {
|
|
155
155
|
const piperPath = path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Piper', 'piper.exe');
|
|
@@ -165,10 +165,10 @@ function _detectSetupState() {
|
|
|
165
165
|
// Linux/macOS detection (original)
|
|
166
166
|
let receiverHome = '';
|
|
167
167
|
try {
|
|
168
|
-
execSync('id agentvibes-receiver', { timeout: 3000, stdio: 'pipe' });
|
|
168
|
+
execSync('id agentvibes-receiver', { timeout: 3000, stdio: 'pipe' }); // NOSONAR
|
|
169
169
|
state.receiverUserExists = true;
|
|
170
170
|
try {
|
|
171
|
-
receiverHome = execSync("getent passwd agentvibes-receiver 2>/dev/null | cut -d: -f6 || echo '/home/agentvibes-receiver'",
|
|
171
|
+
receiverHome = execSync("getent passwd agentvibes-receiver 2>/dev/null | cut -d: -f6 || echo '/home/agentvibes-receiver'", // NOSONAR
|
|
172
172
|
{ timeout: 3000, stdio: 'pipe' }).toString().trim();
|
|
173
173
|
} catch { receiverHome = '/home/agentvibes-receiver'; }
|
|
174
174
|
} catch { /* user does not exist */ }
|
|
@@ -201,7 +201,7 @@ function _detectSetupState() {
|
|
|
201
201
|
} catch { /* no read access */ }
|
|
202
202
|
|
|
203
203
|
try {
|
|
204
|
-
const modules = execSync('pactl list modules short 2>/dev/null', { timeout: 3000, stdio: 'pipe' }).toString();
|
|
204
|
+
const modules = execSync('pactl list modules short 2>/dev/null', { timeout: 3000, stdio: 'pipe' }).toString(); // NOSONAR
|
|
205
205
|
state.tcpModuleLoaded = modules.includes('module-native-protocol-tcp');
|
|
206
206
|
} catch { /* pactl not available */ }
|
|
207
207
|
}
|
|
@@ -1031,7 +1031,7 @@ export function createReceiverTab(screen, services) {
|
|
|
1031
1031
|
screen.render();
|
|
1032
1032
|
|
|
1033
1033
|
// Fire SSH in background — don't block the TUI
|
|
1034
|
-
const child = spawn('ssh', ['-o', 'ConnectTimeout=5', sshHost, encoded], {
|
|
1034
|
+
const child = spawn('ssh', ['-o', 'ConnectTimeout=5', sshHost, encoded], { // NOSONAR
|
|
1035
1035
|
stdio: 'ignore',
|
|
1036
1036
|
detached: true,
|
|
1037
1037
|
});
|
|
@@ -1361,7 +1361,7 @@ export function createReceiverTab(screen, services) {
|
|
|
1361
1361
|
}
|
|
1362
1362
|
_refreshCachedInfo();
|
|
1363
1363
|
const text = _buildDetailedInstructions(RECEIVER_ALIAS, RECEIVER_SCRIPT, _networkInfo)
|
|
1364
|
-
.replace(/\{[^}]*\}/g, '')
|
|
1364
|
+
.replace(/\{[^}]*\}/g, '') // NOSONAR
|
|
1365
1365
|
// eslint-disable-next-line no-control-regex
|
|
1366
1366
|
.replace(/\x1b\[[0-9;]*m/g, '');
|
|
1367
1367
|
// Try platform-appropriate clipboard command
|
|
@@ -1374,7 +1374,7 @@ export function createReceiverTab(screen, services) {
|
|
|
1374
1374
|
mkdirSync(AGENTVIBES_DIR, { recursive: true });
|
|
1375
1375
|
writeFileSync(tmpFile, '\ufeff' + text, 'utf-8');
|
|
1376
1376
|
const psCmd = `Get-Content -Path "${tmpFile.replace(/\\/g, '/')}" -Encoding UTF8 -Raw | Set-Clipboard; Remove-Item "${tmpFile.replace(/\\/g, '/')}"`;
|
|
1377
|
-
const r = spawnSync('powershell', ['-NoProfile', '-Command', psCmd], { timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
1377
|
+
const r = spawnSync('powershell', ['-NoProfile', '-Command', psCmd], { timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }); // NOSONAR
|
|
1378
1378
|
if (r.status === 0) {
|
|
1379
1379
|
_showFeedback('{green-fg}Copied to clipboard!{/green-fg}');
|
|
1380
1380
|
copied = true;
|
|
@@ -1413,7 +1413,7 @@ export function createReceiverTab(screen, services) {
|
|
|
1413
1413
|
// List available audio sinks and let user pick one
|
|
1414
1414
|
let sinks;
|
|
1415
1415
|
try {
|
|
1416
|
-
const out = execSync('pactl --server=tcp:127.0.0.1:34567 list sinks short 2>/dev/null || pactl list sinks short 2>/dev/null', { timeout: 5000 }).toString().trim();
|
|
1416
|
+
const out = execSync('pactl --server=tcp:127.0.0.1:34567 list sinks short 2>/dev/null || pactl list sinks short 2>/dev/null', { timeout: 5000 }).toString().trim(); // NOSONAR
|
|
1417
1417
|
sinks = out.split('\n').filter(l => l.length > 0).map(line => {
|
|
1418
1418
|
const parts = line.split('\t');
|
|
1419
1419
|
return { id: parts[0], name: parts[1] || '', driver: parts[2] || '', state: parts[4] || '' };
|
|
@@ -645,7 +645,7 @@ export function createSettingsTab(screen, services) {
|
|
|
645
645
|
if (_previewVoiceId === voiceId) { _killVP(); vpPreviewLine.setContent(''); _refreshVP(); return; }
|
|
646
646
|
_killVP();
|
|
647
647
|
|
|
648
|
-
const phrase = SAMPLE_PHRASES[Math.floor(Math.random() * SAMPLE_PHRASES.length)];
|
|
648
|
+
const phrase = SAMPLE_PHRASES[Math.floor(Math.random() * SAMPLE_PHRASES.length)]; // NOSONAR
|
|
649
649
|
const playTtsScript = path.join(_projectRoot, '.claude', 'hooks', 'play-tts.sh');
|
|
650
650
|
if (!fs.existsSync(playTtsScript)) return;
|
|
651
651
|
|
|
@@ -653,7 +653,7 @@ export function createSettingsTab(screen, services) {
|
|
|
653
653
|
const args = [playTtsScript, phrase, voiceId];
|
|
654
654
|
if (remoteLlm) args.push('--llm', remoteLlm);
|
|
655
655
|
|
|
656
|
-
_previewProc = spawn('bash', args, {
|
|
656
|
+
_previewProc = spawn('bash', args, { // NOSONAR
|
|
657
657
|
stdio: 'ignore',
|
|
658
658
|
detached: true,
|
|
659
659
|
env: _spawnEnv,
|