agentvibes 4.2.0 → 4.4.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/bmad/bmad-voices.md +69 -69
- package/.agentvibes/config.json +12 -0
- package/.claude/activation-instructions +54 -54
- package/.claude/audio/tracks/README.md +52 -52
- package/.claude/commands/agent-vibes/add.md +21 -21
- package/.claude/commands/agent-vibes/agent-vibes.md +101 -101
- package/.claude/commands/agent-vibes/agent.md +79 -79
- package/.claude/commands/agent-vibes/background-music.md +111 -111
- package/.claude/commands/agent-vibes/bmad.md +198 -198
- package/.claude/commands/agent-vibes/clean.md +18 -18
- package/.claude/commands/agent-vibes/cleanup.md +18 -18
- package/.claude/commands/agent-vibes/commands.json +145 -145
- package/.claude/commands/agent-vibes/effects.md +97 -97
- package/.claude/commands/agent-vibes/get.md +9 -9
- package/.claude/commands/agent-vibes/hide.md +91 -91
- package/.claude/commands/agent-vibes/language.md +23 -23
- package/.claude/commands/agent-vibes/learn.md +67 -67
- package/.claude/commands/agent-vibes/list.md +13 -13
- package/.claude/commands/agent-vibes/mute.md +37 -37
- package/.claude/commands/agent-vibes/preview.md +17 -17
- package/.claude/commands/agent-vibes/provider.md +68 -68
- package/.claude/commands/agent-vibes/replay-target.md +14 -14
- package/.claude/commands/agent-vibes/sample.md +12 -12
- package/.claude/commands/agent-vibes/set-favorite-voice.md +84 -84
- package/.claude/commands/agent-vibes/set-pretext.md +65 -65
- package/.claude/commands/agent-vibes/set-speed.md +41 -41
- package/.claude/commands/agent-vibes/show.md +84 -84
- package/.claude/commands/agent-vibes/switch.md +87 -87
- package/.claude/commands/agent-vibes/target-voice.md +26 -26
- package/.claude/commands/agent-vibes/target.md +30 -30
- package/.claude/commands/agent-vibes/translate.md +68 -68
- package/.claude/commands/agent-vibes/unmute.md +45 -45
- package/.claude/commands/agent-vibes/verbosity.md +89 -89
- package/.claude/commands/agent-vibes/whoami.md +7 -7
- package/.claude/commands/agent-vibes-bmad-voices.md +117 -117
- package/.claude/commands/agent-vibes-rdp.md +24 -24
- package/.claude/config/agentvibes.json +1 -0
- package/.claude/config/audio-effects.cfg +2 -2
- package/.claude/config/audio-effects.cfg.sample +52 -52
- package/.claude/config/background-music-volume.txt +1 -0
- package/.claude/config/intro-text.txt +1 -0
- package/.claude/config/piper-speech-rate.txt +4 -0
- package/.claude/config/piper-target-speech-rate.txt +1 -0
- package/.claude/config/reverb-level.txt +1 -0
- package/.claude/config/tts-speech-rate.txt +4 -0
- package/.claude/config/tts-target-speech-rate.txt +1 -0
- package/.claude/docs/TERMUX_SETUP.md +408 -408
- package/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks/README-TTS-QUEUE.md +135 -135
- package/.claude/hooks/audio-cache-utils.sh +246 -246
- package/.claude/hooks/audio-processor.sh +433 -433
- package/.claude/hooks/background-music-manager.sh +404 -404
- package/.claude/hooks/bmad-speak-enhanced.sh +165 -165
- package/.claude/hooks/bmad-speak.sh +269 -269
- package/.claude/hooks/bmad-tts-injector.sh +568 -568
- package/.claude/hooks/bmad-voice-manager.sh +928 -928
- package/.claude/hooks/clawdbot-receiver-SECURE.sh +129 -129
- package/.claude/hooks/clawdbot-receiver.sh +107 -107
- package/.claude/hooks/clean-audio-cache.sh +22 -22
- package/.claude/hooks/cleanup-cache.sh +106 -106
- package/.claude/hooks/configure-rdp-mode.sh +137 -137
- package/.claude/hooks/download-extra-voices.sh +244 -244
- package/.claude/hooks/effects-manager.sh +268 -268
- package/.claude/hooks/github-star-reminder.sh +154 -154
- package/.claude/hooks/language-manager.sh +362 -362
- package/.claude/hooks/learn-manager.sh +492 -492
- package/.claude/hooks/macos-voice-manager.sh +205 -205
- package/.claude/hooks/migrate-background-music.sh +125 -125
- package/.claude/hooks/migrate-to-agentvibes.sh +161 -161
- package/.claude/hooks/optimize-background-music.sh +87 -87
- package/.claude/hooks/path-resolver.sh +60 -60
- package/.claude/hooks/personality-manager.sh +448 -448
- package/.claude/hooks/piper-download-voices.sh +225 -225
- package/.claude/hooks/piper-installer.sh +292 -292
- package/.claude/hooks/piper-multispeaker-registry.sh +171 -171
- package/.claude/hooks/piper-voice-manager.sh +24 -3
- package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +90 -90
- package/.claude/hooks/play-tts-enhanced.sh +105 -105
- package/.claude/hooks/play-tts-macos.sh +368 -368
- package/.claude/hooks/play-tts-piper.sh +679 -679
- package/.claude/hooks/play-tts-soprano.sh +356 -356
- package/.claude/hooks/play-tts-ssh-remote.sh +167 -167
- package/.claude/hooks/play-tts-termux-ssh.sh +169 -169
- package/.claude/hooks/play-tts.sh +301 -301
- package/.claude/hooks/prepare-release.sh +54 -54
- package/.claude/hooks/provider-commands.sh +617 -617
- package/.claude/hooks/provider-manager.sh +399 -399
- package/.claude/hooks/replay-target-audio.sh +95 -95
- package/.claude/hooks/requirements.txt +6 -6
- package/.claude/hooks/sentiment-manager.sh +201 -201
- package/.claude/hooks/session-start-tts.sh +81 -81
- package/.claude/hooks/soprano-gradio-synth.py +139 -139
- package/.claude/hooks/speed-manager.sh +291 -291
- package/.claude/hooks/stop-tts.sh +84 -84
- package/.claude/hooks/termux-installer.sh +261 -261
- package/.claude/hooks/translate-manager.sh +341 -341
- package/.claude/hooks/translator.py +237 -237
- package/.claude/hooks/tts-queue-worker.sh +145 -145
- package/.claude/hooks/tts-queue.sh +165 -165
- package/.claude/hooks/verbosity-manager.sh +178 -178
- package/.claude/hooks/voice-manager.sh +548 -548
- package/.claude/hooks-windows/audio-cache-utils.ps1 +119 -119
- package/.claude/hooks-windows/background-music-manager.ps1 +348 -0
- package/.claude/hooks-windows/clean-audio-cache.ps1 +53 -0
- package/.claude/hooks-windows/download-extra-voices.ps1 +185 -0
- package/.claude/hooks-windows/effects-manager.ps1 +294 -0
- package/.claude/hooks-windows/language-manager.ps1 +193 -0
- package/.claude/hooks-windows/learn-manager.ps1 +241 -0
- package/.claude/hooks-windows/personality-manager.ps1 +266 -0
- package/.claude/hooks-windows/play-tts-piper.ps1 +209 -0
- package/.claude/hooks-windows/play-tts-sapi.ps1 +108 -0
- package/.claude/hooks-windows/play-tts-soprano.ps1 +159 -158
- package/.claude/hooks-windows/play-tts-windows-piper.ps1 +50 -5
- package/.claude/hooks-windows/play-tts-windows-sapi.ps1 +108 -108
- package/.claude/hooks-windows/play-tts.ps1 +344 -266
- package/.claude/hooks-windows/provider-manager.ps1 +29 -10
- package/.claude/hooks-windows/session-start-tts.ps1 +124 -124
- package/.claude/hooks-windows/soprano-gradio-synth.py +153 -153
- package/.claude/hooks-windows/speed-manager.ps1 +166 -0
- package/.claude/hooks-windows/verbosity-manager.ps1 +119 -0
- package/.claude/hooks-windows/voice-manager-windows.ps1 +92 -8
- package/.claude/output-styles/agent-vibes.md +202 -202
- package/.claude/personalities/angry.md +14 -14
- package/.claude/personalities/annoying.md +14 -14
- package/.claude/personalities/crass.md +14 -14
- package/.claude/personalities/dramatic.md +14 -14
- package/.claude/personalities/dry-humor.md +50 -50
- package/.claude/personalities/flirty.md +20 -20
- package/.claude/personalities/funny.md +14 -14
- package/.claude/personalities/grandpa.md +32 -32
- package/.claude/personalities/millennial.md +14 -14
- package/.claude/personalities/moody.md +14 -14
- package/.claude/personalities/normal.md +16 -16
- package/.claude/personalities/pirate.md +14 -14
- package/.claude/personalities/poetic.md +14 -14
- package/.claude/personalities/professional.md +14 -14
- package/.claude/personalities/rapper.md +55 -55
- package/.claude/personalities/robot.md +14 -14
- package/.claude/personalities/sarcastic.md +38 -38
- package/.claude/personalities/sassy.md +14 -14
- package/.claude/personalities/surfer-dude.md +14 -14
- package/.claude/personalities/zen.md +14 -14
- package/.claude/settings.json +15 -15
- package/.claude/verbosity.txt +1 -1
- package/.clawdbot/README.md +105 -105
- package/.clawdbot/skill/SKILL.md +241 -241
- package/.mcp.json +12 -0
- package/CLAUDE.md +170 -170
- package/README.md +2029 -2007
- package/RELEASE_NOTES.md +1310 -1203
- package/WINDOWS-SETUP.md +208 -208
- package/bin/agent-vibes +39 -39
- package/bin/agentvibes-voice-browser.js +1840 -1840
- package/bin/agentvibes.js +48 -2
- package/bin/mcp-server.js +121 -121
- package/bin/mcp-server.sh +206 -206
- 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 +260 -260
- package/mcp-server/docs/troubleshooting-audio.md +313 -313
- 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 +130 -130
- package/mcp-server/pyproject.toml +52 -52
- package/mcp-server/requirements.txt +2 -2
- package/mcp-server/server.py +1465 -1453
- package/mcp-server/test_server.py +395 -395
- package/mcp-server/test_windows_script_parity.py +336 -0
- package/package.json +110 -110
- package/setup-windows.ps1 +815 -815
- package/src/bmad-detector.js +71 -71
- 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/app.js +824 -824
- package/src/console/audio-env.js +20 -1
- package/src/console/brand-colors.js +13 -13
- package/src/console/constants/personalities.js +44 -44
- package/src/console/footer-config.js +50 -50
- package/src/console/modals/modal-overlay.js +247 -247
- package/src/console/navigation.js +62 -62
- package/src/console/tabs/agents-tab.js +1684 -1516
- package/src/console/tabs/help-tab.js +261 -261
- package/src/console/tabs/install-tab.js +1007 -991
- package/src/console/tabs/music-tab.js +22 -8
- package/src/console/tabs/placeholder-tab.js +53 -53
- package/src/console/tabs/readme-tab.js +267 -267
- package/src/console/tabs/receiver-tab.js +1472 -1212
- package/src/console/tabs/settings-tab.js +152 -79
- package/src/console/tabs/voices-tab.js +100 -21
- package/src/console/widgets/destroy-list.js +25 -25
- package/src/console/widgets/format-utils.js +89 -89
- package/src/console/widgets/notice.js +55 -55
- package/src/console/widgets/personality-picker.js +185 -185
- package/src/console/widgets/reverb-picker.js +94 -94
- package/src/console/widgets/track-picker.js +285 -285
- package/src/installer/music-file-input.js +304 -304
- package/src/installer.js +5882 -5829
- package/src/services/agent-voice-store.js +423 -423
- package/src/services/config-service.js +264 -264
- package/src/services/navigation-service.js +123 -123
- package/src/services/provider-service.js +132 -132
- package/src/services/verbosity-service.js +157 -157
- 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 -466
- 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/provider-validator.js +96 -12
- package/src/utils/secure-music-storage.js +412 -412
- package/templates/agentvibes-receiver.sh +482 -482
- package/templates/audio/welcome-music.mp3 +0 -0
- package/voice-assignments.json +8244 -8244
- package/.claude/config/background-music-position.txt +0 -1
|
@@ -32,11 +32,11 @@ const COLORS = {
|
|
|
32
32
|
sectionHdr: '#00897b', // Teal — section headers for Voices tab
|
|
33
33
|
labelFg: '#e3f2fd',
|
|
34
34
|
valueFg: '#f06292', // Light magenta — brand color
|
|
35
|
-
activeFg: '
|
|
35
|
+
activeFg: 'bright-cyan', // Cyan — active voice
|
|
36
36
|
favoriteFg: '#ffff00', // Yellow — favorite star
|
|
37
37
|
btnDefault: '#00695c', // Teal — Voices tab buttons
|
|
38
|
-
btnFocus: '#
|
|
39
|
-
btnFocusFg: '#
|
|
38
|
+
btnFocus: '#2e7d32', // Green — focused/selected
|
|
39
|
+
btnFocusFg: '#ffffff',
|
|
40
40
|
btnPress: '#ff00ff',
|
|
41
41
|
borderFg: '#00897b',
|
|
42
42
|
footerBg: '#00695c', // Teal — Voices tab footer
|
|
@@ -513,7 +513,7 @@ export function createVoicesTab(screen, services) {
|
|
|
513
513
|
keys: true,
|
|
514
514
|
style: {
|
|
515
515
|
fg: COLORS.valueFg,
|
|
516
|
-
bg: '#
|
|
516
|
+
bg: '#1a3a5c',
|
|
517
517
|
focus: { bg: '#283593' },
|
|
518
518
|
},
|
|
519
519
|
});
|
|
@@ -549,7 +549,7 @@ export function createVoicesTab(screen, services) {
|
|
|
549
549
|
fg: COLORS.labelFg,
|
|
550
550
|
bg: COLORS.contentBg,
|
|
551
551
|
border: { fg: COLORS.borderFg },
|
|
552
|
-
selected: { bg: '#
|
|
552
|
+
selected: { bg: '#2e7d32', fg: '#ffffff', bold: true },
|
|
553
553
|
item: { fg: COLORS.labelFg },
|
|
554
554
|
},
|
|
555
555
|
});
|
|
@@ -670,12 +670,24 @@ export function createVoicesTab(screen, services) {
|
|
|
670
670
|
const tempWav = path.join(os.tmpdir(), `agentvibes-preview-${Date.now()}.wav`);
|
|
671
671
|
const phrase = SAMPLE_PHRASES[Math.floor(Math.random() * SAMPLE_PHRASES.length)];
|
|
672
672
|
|
|
673
|
-
// Synthesize: spawn piper
|
|
673
|
+
// Synthesize: spawn piper; on Windows use the exe path directly
|
|
674
|
+
const isWindows = process.platform === 'win32' && !process.env.WSL_DISTRO_NAME;
|
|
675
|
+
let piperBin = 'piper';
|
|
676
|
+
if (isWindows) {
|
|
677
|
+
const localAppData = process.env.LOCALAPPDATA ||
|
|
678
|
+
(process.env.USERPROFILE ? path.join(process.env.USERPROFILE, 'AppData', 'Local') : null);
|
|
679
|
+
if (localAppData) {
|
|
680
|
+
const exePath = path.join(localAppData, 'Programs', 'Piper', 'piper.exe');
|
|
681
|
+
if (fs.existsSync(exePath)) piperBin = exePath;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
674
684
|
const piperArgs = ['--model', voicePath, '--output_file', tempWav];
|
|
675
685
|
if (ms.speakerId != null) piperArgs.push('--speaker', String(ms.speakerId));
|
|
676
|
-
|
|
686
|
+
// On Windows, avoid detached:true which opens a visible console window
|
|
687
|
+
const piper = spawn(piperBin, piperArgs, {
|
|
677
688
|
stdio: ['pipe', 'ignore', 'ignore'],
|
|
678
|
-
detached:
|
|
689
|
+
detached: !isWindows,
|
|
690
|
+
windowsHide: true,
|
|
679
691
|
env: _spawnEnv,
|
|
680
692
|
});
|
|
681
693
|
piper.stdin.write(phrase + '\n');
|
|
@@ -716,7 +728,8 @@ export function createVoicesTab(screen, services) {
|
|
|
716
728
|
}
|
|
717
729
|
const playProc = spawn(_wavP.bin, _wavP.args(tempWav), {
|
|
718
730
|
stdio: 'ignore',
|
|
719
|
-
detached:
|
|
731
|
+
detached: !isWindows,
|
|
732
|
+
windowsHide: true,
|
|
720
733
|
env: _spawnEnv,
|
|
721
734
|
});
|
|
722
735
|
// Race note: _playingVoiceId could change between piper exit and here
|
|
@@ -993,7 +1006,7 @@ export function createVoicesTab(screen, services) {
|
|
|
993
1006
|
refreshDisplay();
|
|
994
1007
|
_showVoiceChangedNotice(displayName);
|
|
995
1008
|
});
|
|
996
|
-
const okGlobalBtn = _makeBtn('Save Globally & Locally', '#
|
|
1009
|
+
const okGlobalBtn = _makeBtn('Save Globally & Locally', '#1565c0', 18, 5, () => {
|
|
997
1010
|
_activateVoice(voiceId);
|
|
998
1011
|
_activateVoiceGlobal(voiceId);
|
|
999
1012
|
refreshDisplay();
|
|
@@ -1100,26 +1113,91 @@ export function createVoicesTab(screen, services) {
|
|
|
1100
1113
|
function _startDownload() {
|
|
1101
1114
|
if (_downloading) return;
|
|
1102
1115
|
_downloading = true;
|
|
1103
|
-
statusLine.setContent(`{${COLORS.activeFg}-fg}⬇ Downloading ${modelToDownload}…{/${COLORS.activeFg}-fg}`);
|
|
1104
|
-
screen.render();
|
|
1105
1116
|
|
|
1106
|
-
//
|
|
1107
|
-
const
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1117
|
+
// Animated spinner
|
|
1118
|
+
const spinFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
1119
|
+
let spinIdx = 0;
|
|
1120
|
+
let dlPhase = 'Downloading model';
|
|
1121
|
+
const progressBar = (pct) => {
|
|
1122
|
+
const filled = Math.round(pct / 5);
|
|
1123
|
+
const empty = 20 - filled;
|
|
1124
|
+
return '█'.repeat(filled) + '░'.repeat(empty);
|
|
1125
|
+
};
|
|
1126
|
+
|
|
1127
|
+
const spinTimer = setInterval(() => {
|
|
1128
|
+
spinIdx = (spinIdx + 1) % spinFrames.length;
|
|
1129
|
+
const frame = spinFrames[spinIdx];
|
|
1130
|
+
statusLine.setContent(
|
|
1131
|
+
`{${COLORS.activeFg}-fg}${frame} ${dlPhase}… ${modelToDownload}{/${COLORS.activeFg}-fg}`
|
|
1132
|
+
);
|
|
1133
|
+
screen.render();
|
|
1134
|
+
}, 100);
|
|
1135
|
+
|
|
1136
|
+
// Download voice model — use PowerShell on Windows, bash on Unix
|
|
1137
|
+
const packageRoot = path.resolve(__dirname, '..', '..', '..');
|
|
1138
|
+
const isWindows = process.platform === 'win32' && !process.env.WSL_DISTRO_NAME;
|
|
1139
|
+
let dlProc;
|
|
1140
|
+
|
|
1141
|
+
if (isWindows) {
|
|
1142
|
+
const piperVoicesDir = resolvePiperVoicesDir();
|
|
1143
|
+
const hfBase = 'https://huggingface.co/rhasspy/piper-voices/resolve/main';
|
|
1144
|
+
const match = modelToDownload.match(/^([a-z]{2})_([A-Z]{2})-([a-zA-Z0-9_]+)-([a-z]+)$/);
|
|
1145
|
+
let modelUrl, configUrl;
|
|
1146
|
+
if (match) {
|
|
1147
|
+
const [, lang, region, speaker, quality] = match;
|
|
1148
|
+
const hfPath = `${lang}/${lang}_${region}/${speaker}/${quality}`;
|
|
1149
|
+
modelUrl = `${hfBase}/${hfPath}/${modelToDownload}.onnx`;
|
|
1150
|
+
configUrl = `${hfBase}/${hfPath}/${modelToDownload}.onnx.json`;
|
|
1151
|
+
} else {
|
|
1152
|
+
const customBase = 'https://huggingface.co/agentvibes/piper-custom-voices/resolve/main';
|
|
1153
|
+
modelUrl = `${customBase}/${modelToDownload}.onnx`;
|
|
1154
|
+
configUrl = `${customBase}/${modelToDownload}.onnx.json`;
|
|
1155
|
+
}
|
|
1156
|
+
const modelFile = path.join(piperVoicesDir, `${modelToDownload}.onnx`);
|
|
1157
|
+
const configFile = path.join(piperVoicesDir, `${modelToDownload}.onnx.json`);
|
|
1158
|
+
// PowerShell script with progress reporting
|
|
1159
|
+
const psScript = `
|
|
1160
|
+
$ErrorActionPreference = 'Stop'
|
|
1161
|
+
$ProgressPreference = 'SilentlyContinue'
|
|
1162
|
+
$voicesDir = '${piperVoicesDir.replace(/'/g, "''")}'
|
|
1163
|
+
if (-not (Test-Path $voicesDir)) { New-Item -ItemType Directory -Path $voicesDir -Force | Out-Null }
|
|
1164
|
+
Write-Output 'PHASE:model'
|
|
1165
|
+
Invoke-WebRequest -Uri '${modelUrl}' -OutFile '${modelFile.replace(/'/g, "''")}' -ErrorAction Stop
|
|
1166
|
+
Write-Output 'PHASE:config'
|
|
1167
|
+
Invoke-WebRequest -Uri '${configUrl}' -OutFile '${configFile.replace(/'/g, "''")}' -ErrorAction Stop
|
|
1168
|
+
Write-Output 'PHASE:done'
|
|
1169
|
+
`;
|
|
1170
|
+
dlProc = spawn('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', psScript], {
|
|
1171
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1172
|
+
env: _spawnEnv,
|
|
1173
|
+
});
|
|
1174
|
+
} else {
|
|
1175
|
+
const managerScript = path.resolve(packageRoot, '.claude', 'hooks', 'piper-voice-manager.sh');
|
|
1176
|
+
dlProc = spawn('bash', ['-c', 'source "$1" && download_voice "$2"', '_', managerScript, modelToDownload], {
|
|
1177
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1178
|
+
env: _spawnEnv,
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1112
1181
|
_downloadProcess = dlProc;
|
|
1113
1182
|
|
|
1114
1183
|
let output = '';
|
|
1115
|
-
dlProc.stdout.on('data', (d) => {
|
|
1184
|
+
dlProc.stdout.on('data', (d) => {
|
|
1185
|
+
const chunk = d.toString();
|
|
1186
|
+
output += chunk;
|
|
1187
|
+
// Update phase based on progress markers
|
|
1188
|
+
if (chunk.includes('PHASE:config') || chunk.includes('config file')) {
|
|
1189
|
+
dlPhase = 'Downloading config';
|
|
1190
|
+
} else if (chunk.includes('PHASE:done') || chunk.includes('successfully')) {
|
|
1191
|
+
dlPhase = 'Finishing up';
|
|
1192
|
+
}
|
|
1193
|
+
});
|
|
1116
1194
|
dlProc.stderr.on('data', (d) => { output += d.toString(); });
|
|
1117
1195
|
|
|
1118
1196
|
dlProc.on('exit', (code) => {
|
|
1197
|
+
clearInterval(spinTimer);
|
|
1119
1198
|
_downloading = false;
|
|
1120
1199
|
_downloadProcess = null;
|
|
1121
1200
|
if (code === 0) {
|
|
1122
|
-
// Patch speaker names for freshly downloaded LibriTTS model
|
|
1123
1201
|
if (isLibriTTS) {
|
|
1124
1202
|
patchLibriTTSSpeakerNames();
|
|
1125
1203
|
_metaCache.clear();
|
|
@@ -1137,6 +1215,7 @@ export function createVoicesTab(screen, services) {
|
|
|
1137
1215
|
});
|
|
1138
1216
|
|
|
1139
1217
|
dlProc.on('error', () => {
|
|
1218
|
+
clearInterval(spinTimer);
|
|
1140
1219
|
_downloading = false;
|
|
1141
1220
|
_downloadProcess = null;
|
|
1142
1221
|
statusLine.setContent(`{red-fg}✗ Could not run download script{/red-fg}`);
|
|
@@ -1235,7 +1314,7 @@ export function createVoicesTab(screen, services) {
|
|
|
1235
1314
|
// Greyed-out row for uninstalled catalog voices
|
|
1236
1315
|
return `{bright-black-fg} ${star} ${name}${gender.padEnd(COL_GENDER_W)}${provider}{/bright-black-fg}`;
|
|
1237
1316
|
}
|
|
1238
|
-
return ` ${star}${dot} ${name}${gender.padEnd(COL_GENDER_W)}${provider}${isPrev ? ' (playing)' : ''}`;
|
|
1317
|
+
return `{${COLORS.labelFg}-fg} ${star}${dot} ${name}${gender.padEnd(COL_GENDER_W)}${provider}${isPrev ? ' (playing)' : ''}{/${COLORS.labelFg}-fg}`;
|
|
1239
1318
|
});
|
|
1240
1319
|
}
|
|
1241
1320
|
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentVibes TUI — Shared Widget: Modal Destroy Helper
|
|
3
|
-
*
|
|
4
|
-
* Force-invalidates blessed's olines buffer after destroying a modal widget.
|
|
5
|
-
* Without this, blessed skips repainting cells where lines==olines and the
|
|
6
|
-
* terminal retains stale modal content as ghost artifacts.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Destroy a blessed list/box widget and force full screen repaint.
|
|
11
|
-
*
|
|
12
|
-
* @param {object} widget - blessed widget to destroy
|
|
13
|
-
* @param {object} screen - blessed screen instance
|
|
14
|
-
* @param {Function} [onClose] - optional callback after destruction
|
|
15
|
-
*/
|
|
16
|
-
export function destroyList(widget, screen, onClose) {
|
|
17
|
-
widget.destroy();
|
|
18
|
-
try {
|
|
19
|
-
for (let r = 0; r < screen.height; r++)
|
|
20
|
-
for (let c = 0; c < screen.width; c++)
|
|
21
|
-
if (screen.olines[r]?.[c]) screen.olines[r][c][0] = -1;
|
|
22
|
-
} catch {}
|
|
23
|
-
onClose?.();
|
|
24
|
-
screen.render();
|
|
25
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* AgentVibes TUI — Shared Widget: Modal Destroy Helper
|
|
3
|
+
*
|
|
4
|
+
* Force-invalidates blessed's olines buffer after destroying a modal widget.
|
|
5
|
+
* Without this, blessed skips repainting cells where lines==olines and the
|
|
6
|
+
* terminal retains stale modal content as ghost artifacts.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Destroy a blessed list/box widget and force full screen repaint.
|
|
11
|
+
*
|
|
12
|
+
* @param {object} widget - blessed widget to destroy
|
|
13
|
+
* @param {object} screen - blessed screen instance
|
|
14
|
+
* @param {Function} [onClose] - optional callback after destruction
|
|
15
|
+
*/
|
|
16
|
+
export function destroyList(widget, screen, onClose) {
|
|
17
|
+
widget.destroy();
|
|
18
|
+
try {
|
|
19
|
+
for (let r = 0; r < screen.height; r++)
|
|
20
|
+
for (let c = 0; c < screen.width; c++)
|
|
21
|
+
if (screen.olines[r]?.[c]) screen.olines[r][c][0] = -1;
|
|
22
|
+
} catch {}
|
|
23
|
+
onClose?.();
|
|
24
|
+
screen.render();
|
|
25
|
+
}
|
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentVibes TUI — Shared Format Utilities
|
|
3
|
-
*
|
|
4
|
-
* Pure formatting functions extracted from settings-tab.js to avoid
|
|
5
|
-
* circular imports between widgets and tabs.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const TRACK_NAMES = Object.freeze({
|
|
9
|
-
'agentvibes_soft_flamenco_loop.mp3': '🎻 Soft Flamenco',
|
|
10
|
-
'agent_vibes_bachata_v1_loop.mp3': '🎺 Bachata',
|
|
11
|
-
'agent_vibes_salsa_v2_loop.mp3': '💃 Salsa',
|
|
12
|
-
'agent_vibes_cumbia_v1_loop.mp3': '🎸 Cumbia',
|
|
13
|
-
'agent_vibes_bossa_nova_v2_loop.mp3': '🌸 Bossa Nova',
|
|
14
|
-
'agent_vibes_japanese_city_pop_v1_loop.mp3': '🌆 Japanese City Pop',
|
|
15
|
-
'agent_vibes_chillwave_v2_loop.mp3': '🌊 Chillwave',
|
|
16
|
-
'agent_vibes_dark_chill_step_loop.mp3': '🌙 Dark Chill Step',
|
|
17
|
-
'agent_vibes_goa_trance_v2_loop.mp3': '🌀 Goa Trance',
|
|
18
|
-
'agent_vibes_harpsichord_v2_loop.mp3': '🎼 Harpsichord',
|
|
19
|
-
'agent_vibes_celtic_harp_v1_loop.mp3': '🎻 Celtic Harp',
|
|
20
|
-
'agent_vibes_hawaiian_slack_key_guitar_v2_loop.mp3': '🌺 Hawaiian Slack Key Guitar',
|
|
21
|
-
'agent_vibes_arabic_v2_loop.mp3': '🎵 Arabic Oud',
|
|
22
|
-
'agent_vibes_ganawa_ambient_v2_loop.mp3': '🪘 Gnawa Ambient',
|
|
23
|
-
'agent_vibes_tabla_dream_pop_v1_loop.mp3': '🥁 Tabla Dream Pop',
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @param {string} track - filename (e.g. 'agentvibes_soft_flamenco_loop.mp3')
|
|
28
|
-
* @returns {string}
|
|
29
|
-
*/
|
|
30
|
-
export function formatTrackName(track) {
|
|
31
|
-
if (!track) return 'None';
|
|
32
|
-
if (TRACK_NAMES[track]) return TRACK_NAMES[track];
|
|
33
|
-
return track
|
|
34
|
-
.replace(/\.[^.]+$/, '')
|
|
35
|
-
.replace(/^agentvibes_|^agent_vibes_/, '')
|
|
36
|
-
.replace(/_v\d+_loop$|_loop$|_v\d+$/, '')
|
|
37
|
-
.replace(/_/g, ' ')
|
|
38
|
-
.replace(/\b\w/g, c => c.toUpperCase());
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Beautify a raw voice identifier for display in narrow table columns.
|
|
43
|
-
*
|
|
44
|
-
* Examples:
|
|
45
|
-
* 16Speakers::Rose_Ibex → Rose Ibex
|
|
46
|
-
* 16Speakers::Emily_Cripps → Emily Cripps
|
|
47
|
-
* en_US-kusal-medium → Kusal
|
|
48
|
-
* en_US-lessac-high → Lessac
|
|
49
|
-
* en_US-libritts_r-medium → Libritts R
|
|
50
|
-
* kristin → Kristin
|
|
51
|
-
*
|
|
52
|
-
* @param {string} voice - raw voice identifier
|
|
53
|
-
* @returns {string}
|
|
54
|
-
*/
|
|
55
|
-
export function formatVoiceName(voice) {
|
|
56
|
-
if (!voice) return '(global)';
|
|
57
|
-
|
|
58
|
-
let name;
|
|
59
|
-
if (voice.includes('::')) {
|
|
60
|
-
// 16Speakers::Rose_Ibex → extract after '::'
|
|
61
|
-
name = voice.split('::')[1];
|
|
62
|
-
} else {
|
|
63
|
-
const parts = voice.split('-');
|
|
64
|
-
const QUALITIES = new Set(['high', 'medium', 'low']);
|
|
65
|
-
if (parts.length >= 2 && /^[a-z]{2}_[A-Z]{2}$/.test(parts[0])) {
|
|
66
|
-
// Strip locale prefix and quality suffix
|
|
67
|
-
name = parts.slice(1).filter(p => !QUALITIES.has(p)).join(' ');
|
|
68
|
-
} else {
|
|
69
|
-
name = voice;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Replace underscores with spaces, title-case each word
|
|
74
|
-
return name
|
|
75
|
-
.replace(/_/g, ' ')
|
|
76
|
-
.split(' ')
|
|
77
|
-
.filter(Boolean)
|
|
78
|
-
.map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
|
|
79
|
-
.join(' ') || '(global)';
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* @param {string} preset - 'off' | 'light' | 'medium' | 'heavy' | 'cathedral'
|
|
84
|
-
* @returns {string}
|
|
85
|
-
*/
|
|
86
|
-
export function formatReverbState(preset) {
|
|
87
|
-
const LABELS = { off: 'Off', light: 'Light (Small room)', medium: 'Medium (Conference room)', heavy: 'Heavy (Large hall)', cathedral: 'Cathedral (Epic space)' };
|
|
88
|
-
return LABELS[preset] ?? LABELS.light;
|
|
89
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* AgentVibes TUI — Shared Format Utilities
|
|
3
|
+
*
|
|
4
|
+
* Pure formatting functions extracted from settings-tab.js to avoid
|
|
5
|
+
* circular imports between widgets and tabs.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const TRACK_NAMES = Object.freeze({
|
|
9
|
+
'agentvibes_soft_flamenco_loop.mp3': '🎻 Soft Flamenco',
|
|
10
|
+
'agent_vibes_bachata_v1_loop.mp3': '🎺 Bachata',
|
|
11
|
+
'agent_vibes_salsa_v2_loop.mp3': '💃 Salsa',
|
|
12
|
+
'agent_vibes_cumbia_v1_loop.mp3': '🎸 Cumbia',
|
|
13
|
+
'agent_vibes_bossa_nova_v2_loop.mp3': '🌸 Bossa Nova',
|
|
14
|
+
'agent_vibes_japanese_city_pop_v1_loop.mp3': '🌆 Japanese City Pop',
|
|
15
|
+
'agent_vibes_chillwave_v2_loop.mp3': '🌊 Chillwave',
|
|
16
|
+
'agent_vibes_dark_chill_step_loop.mp3': '🌙 Dark Chill Step',
|
|
17
|
+
'agent_vibes_goa_trance_v2_loop.mp3': '🌀 Goa Trance',
|
|
18
|
+
'agent_vibes_harpsichord_v2_loop.mp3': '🎼 Harpsichord',
|
|
19
|
+
'agent_vibes_celtic_harp_v1_loop.mp3': '🎻 Celtic Harp',
|
|
20
|
+
'agent_vibes_hawaiian_slack_key_guitar_v2_loop.mp3': '🌺 Hawaiian Slack Key Guitar',
|
|
21
|
+
'agent_vibes_arabic_v2_loop.mp3': '🎵 Arabic Oud',
|
|
22
|
+
'agent_vibes_ganawa_ambient_v2_loop.mp3': '🪘 Gnawa Ambient',
|
|
23
|
+
'agent_vibes_tabla_dream_pop_v1_loop.mp3': '🥁 Tabla Dream Pop',
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} track - filename (e.g. 'agentvibes_soft_flamenco_loop.mp3')
|
|
28
|
+
* @returns {string}
|
|
29
|
+
*/
|
|
30
|
+
export function formatTrackName(track) {
|
|
31
|
+
if (!track) return 'None';
|
|
32
|
+
if (TRACK_NAMES[track]) return TRACK_NAMES[track];
|
|
33
|
+
return track
|
|
34
|
+
.replace(/\.[^.]+$/, '')
|
|
35
|
+
.replace(/^agentvibes_|^agent_vibes_/, '')
|
|
36
|
+
.replace(/_v\d+_loop$|_loop$|_v\d+$/, '')
|
|
37
|
+
.replace(/_/g, ' ')
|
|
38
|
+
.replace(/\b\w/g, c => c.toUpperCase());
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Beautify a raw voice identifier for display in narrow table columns.
|
|
43
|
+
*
|
|
44
|
+
* Examples:
|
|
45
|
+
* 16Speakers::Rose_Ibex → Rose Ibex
|
|
46
|
+
* 16Speakers::Emily_Cripps → Emily Cripps
|
|
47
|
+
* en_US-kusal-medium → Kusal
|
|
48
|
+
* en_US-lessac-high → Lessac
|
|
49
|
+
* en_US-libritts_r-medium → Libritts R
|
|
50
|
+
* kristin → Kristin
|
|
51
|
+
*
|
|
52
|
+
* @param {string} voice - raw voice identifier
|
|
53
|
+
* @returns {string}
|
|
54
|
+
*/
|
|
55
|
+
export function formatVoiceName(voice) {
|
|
56
|
+
if (!voice) return '(global)';
|
|
57
|
+
|
|
58
|
+
let name;
|
|
59
|
+
if (voice.includes('::')) {
|
|
60
|
+
// 16Speakers::Rose_Ibex → extract after '::'
|
|
61
|
+
name = voice.split('::')[1];
|
|
62
|
+
} else {
|
|
63
|
+
const parts = voice.split('-');
|
|
64
|
+
const QUALITIES = new Set(['high', 'medium', 'low']);
|
|
65
|
+
if (parts.length >= 2 && /^[a-z]{2}_[A-Z]{2}$/.test(parts[0])) {
|
|
66
|
+
// Strip locale prefix and quality suffix
|
|
67
|
+
name = parts.slice(1).filter(p => !QUALITIES.has(p)).join(' ');
|
|
68
|
+
} else {
|
|
69
|
+
name = voice;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Replace underscores with spaces, title-case each word
|
|
74
|
+
return name
|
|
75
|
+
.replace(/_/g, ' ')
|
|
76
|
+
.split(' ')
|
|
77
|
+
.filter(Boolean)
|
|
78
|
+
.map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
|
|
79
|
+
.join(' ') || '(global)';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {string} preset - 'off' | 'light' | 'medium' | 'heavy' | 'cathedral'
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/
|
|
86
|
+
export function formatReverbState(preset) {
|
|
87
|
+
const LABELS = { off: 'Off', light: 'Light (Small room)', medium: 'Medium (Conference room)', heavy: 'Heavy (Large hall)', cathedral: 'Cathedral (Epic space)' };
|
|
88
|
+
return LABELS[preset] ?? LABELS.light;
|
|
89
|
+
}
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentVibes TUI — Shared Widget: Notice Toast
|
|
3
|
-
*
|
|
4
|
-
* Displays a short auto-dismissing notice modal centred on screen.
|
|
5
|
-
* Usable from any tab; no settings-specific state required.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { destroyList } from './destroy-list.js';
|
|
9
|
-
|
|
10
|
-
const IS_TEST = process.env.AGENTVIBES_TEST_MODE === 'true';
|
|
11
|
-
let blessed;
|
|
12
|
-
if (!IS_TEST) {
|
|
13
|
-
const { default: b } = await import('blessed');
|
|
14
|
-
blessed = b;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Show a temporary notice that auto-dismisses after 2.5 seconds.
|
|
19
|
-
*
|
|
20
|
-
* @param {object} screen - blessed screen instance
|
|
21
|
-
* @param {string} message - text to display
|
|
22
|
-
* @param {object} [opts]
|
|
23
|
-
* @param {string} [opts.bg='#0a0e1a'] - background colour
|
|
24
|
-
* @param {string} [opts.fg='#e3f2fd'] - foreground colour
|
|
25
|
-
* @param {string} [opts.borderFg='
|
|
26
|
-
* @param {number} [opts.durationMs=2500] - auto-dismiss delay in ms
|
|
27
|
-
*/
|
|
28
|
-
export function showNotice(screen, message, opts = {}) {
|
|
29
|
-
const bg = opts.bg ?? '#0a0e1a';
|
|
30
|
-
const fg = opts.fg ?? '#e3f2fd';
|
|
31
|
-
const borderFg = opts.borderFg ?? '
|
|
32
|
-
const durationMs = opts.durationMs ?? 2500;
|
|
33
|
-
|
|
34
|
-
const width = Math.max(28, message.length + 6);
|
|
35
|
-
const modal = blessed.box({
|
|
36
|
-
parent: screen,
|
|
37
|
-
top: 'center',
|
|
38
|
-
left: 'center',
|
|
39
|
-
width,
|
|
40
|
-
height: 3,
|
|
41
|
-
border: { type: 'line' },
|
|
42
|
-
tags: true,
|
|
43
|
-
content: `{center}${message}{/center}`,
|
|
44
|
-
style: {
|
|
45
|
-
fg,
|
|
46
|
-
bg,
|
|
47
|
-
border: { fg: borderFg },
|
|
48
|
-
},
|
|
49
|
-
});
|
|
50
|
-
screen.render();
|
|
51
|
-
|
|
52
|
-
setTimeout(() => {
|
|
53
|
-
destroyList(modal, screen);
|
|
54
|
-
}, durationMs);
|
|
55
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* AgentVibes TUI — Shared Widget: Notice Toast
|
|
3
|
+
*
|
|
4
|
+
* Displays a short auto-dismissing notice modal centred on screen.
|
|
5
|
+
* Usable from any tab; no settings-specific state required.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { destroyList } from './destroy-list.js';
|
|
9
|
+
|
|
10
|
+
const IS_TEST = process.env.AGENTVIBES_TEST_MODE === 'true';
|
|
11
|
+
let blessed;
|
|
12
|
+
if (!IS_TEST) {
|
|
13
|
+
const { default: b } = await import('blessed');
|
|
14
|
+
blessed = b;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Show a temporary notice that auto-dismisses after 2.5 seconds.
|
|
19
|
+
*
|
|
20
|
+
* @param {object} screen - blessed screen instance
|
|
21
|
+
* @param {string} message - text to display
|
|
22
|
+
* @param {object} [opts]
|
|
23
|
+
* @param {string} [opts.bg='#0a0e1a'] - background colour
|
|
24
|
+
* @param {string} [opts.fg='#e3f2fd'] - foreground colour
|
|
25
|
+
* @param {string} [opts.borderFg='bright-cyan'] - border colour
|
|
26
|
+
* @param {number} [opts.durationMs=2500] - auto-dismiss delay in ms
|
|
27
|
+
*/
|
|
28
|
+
export function showNotice(screen, message, opts = {}) {
|
|
29
|
+
const bg = opts.bg ?? '#0a0e1a';
|
|
30
|
+
const fg = opts.fg ?? '#e3f2fd';
|
|
31
|
+
const borderFg = opts.borderFg ?? 'bright-cyan';
|
|
32
|
+
const durationMs = opts.durationMs ?? 2500;
|
|
33
|
+
|
|
34
|
+
const width = Math.max(28, message.length + 6);
|
|
35
|
+
const modal = blessed.box({
|
|
36
|
+
parent: screen,
|
|
37
|
+
top: 'center',
|
|
38
|
+
left: 'center',
|
|
39
|
+
width,
|
|
40
|
+
height: 3,
|
|
41
|
+
border: { type: 'line' },
|
|
42
|
+
tags: true,
|
|
43
|
+
content: `{center}${message}{/center}`,
|
|
44
|
+
style: {
|
|
45
|
+
fg,
|
|
46
|
+
bg,
|
|
47
|
+
border: { fg: borderFg },
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
screen.render();
|
|
51
|
+
|
|
52
|
+
setTimeout(() => {
|
|
53
|
+
destroyList(modal, screen);
|
|
54
|
+
}, durationMs);
|
|
55
|
+
}
|