agentvibes 5.12.0-alpha.0 → 5.12.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 +15 -1
- package/.agentvibes/install-manifest.json +342 -0
- package/.claude/activation-instructions +54 -54
- 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 +5 -5
- package/.claude/config/audio-effects.cfg.bak-kokoro +7 -0
- package/.claude/config/background-music-enabled.txt +1 -1
- package/.claude/config/background-music-position.txt +27 -0
- package/.claude/config/background-music-volume.txt +1 -0
- package/.claude/config/background-music.cfg +1 -0
- package/.claude/config/background-music.txt +1 -0
- package/.claude/config/language.txt +1 -0
- package/.claude/config/personality.txt +1 -0
- package/.claude/config/tts-speech-rate.txt +1 -0
- package/.claude/config/tts-verbosity.txt +1 -0
- package/.claude/docs/TERMUX_SETUP.md +408 -408
- package/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks/play-tts-ssh-remote.sh +42 -5
- package/.claude/hooks/play-tts.sh +21 -9
- package/.claude/hooks-windows/audio-cache-utils.ps1.user.bak +119 -0
- package/.claude/hooks-windows/soprano-gradio-synth.py.user.bak +153 -0
- package/.claude/hooks-windows/tts-watcher.ps1 +55 -0
- package/.claude/piper-voices-dir.txt +1 -0
- package/.clawdbot/README.md +105 -105
- package/.mcp.json +22 -0
- package/LICENSE +190 -190
- package/README.md +11 -6
- package/RELEASE_NOTES.md +38 -0
- package/WINDOWS-SETUP.md +208 -208
- package/bin/agent-vibes +39 -39
- package/bin/mcp-server.js +121 -121
- 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/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/pyproject.toml +52 -52
- package/mcp-server/requirements.txt +2 -2
- package/mcp-server/test_server.py +395 -395
- 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/console/app.js +1 -9
- package/src/console/brand-colors.js +13 -13
- package/src/console/constants/personalities.js +44 -44
- package/src/console/footer-config.js +0 -4
- package/src/console/navigation.js +0 -1
- package/src/console/tabs/agents-tab.js +14 -2
- package/src/console/tabs/help-tab.js +314 -314
- package/src/console/tabs/music-tab.js +101 -2
- package/src/console/tabs/placeholder-tab.js +0 -2
- package/src/console/tabs/readme-tab.js +272 -272
- package/src/console/tabs/settings-tab.js +13 -2
- package/src/console/tabs/setup-tab.js +30 -2
- package/src/console/tabs/voices-tab.js +19 -9
- package/src/console/widgets/destroy-list.js +25 -25
- package/src/console/widgets/notice.js +55 -55
- package/src/installer/language-screen.js +31 -31
- package/src/installer/music-file-input.js +304 -304
- package/src/services/language-service.js +47 -47
- package/src/services/navigation-service.js +1 -1
- 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/music-file-validator.js +285 -285
- package/src/utils/preview-list-prompt.js +144 -144
- package/src/utils/secure-music-storage.js +412 -412
- package/voice-assignments.json +8244 -8244
|
@@ -12,16 +12,43 @@
|
|
|
12
12
|
import fs from 'node:fs';
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import os from 'node:os';
|
|
15
|
+
import { spawn } from 'node:child_process';
|
|
15
16
|
import { fileURLToPath } from 'node:url';
|
|
16
17
|
import { buildAudioEnv, spawnMp3Player } from '../audio-env.js';
|
|
17
18
|
import { t } from '../../i18n/strings.js';
|
|
18
19
|
|
|
20
|
+
const _MUSIC_TAB_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
|
|
19
22
|
// Package-relative tracks dir — used as fallback when cwd has no .claude/audio/tracks/
|
|
20
23
|
const _PKG_TRACKS_DIR = path.resolve(
|
|
21
|
-
|
|
22
|
-
'..', '..', '..', '.claude', 'audio', 'tracks'
|
|
24
|
+
_MUSIC_TAB_DIR, '..', '..', '..', '.claude', 'audio', 'tracks'
|
|
23
25
|
);
|
|
24
26
|
|
|
27
|
+
// Transport providers route audio to a remote receiver; local MP3 playback is
|
|
28
|
+
// silent on a headless/remote box, so track previews must be forwarded instead.
|
|
29
|
+
const _REMOTE_PROVIDERS = ['ssh-remote', 'agentvibes-receiver'];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the active provider and the project dir it was read from, using the
|
|
33
|
+
* same search order as the voice pickers (CLAUDE_PROJECT_DIR → cwd → package →
|
|
34
|
+
* home). Returns { remote, projectDir } so a remote preview can forward the
|
|
35
|
+
* track to the receiver and tell the sender which .claude dir to resolve.
|
|
36
|
+
*/
|
|
37
|
+
function _resolveMusicProvider() {
|
|
38
|
+
const projectRoot = path.resolve(_MUSIC_TAB_DIR, '..', '..', '..');
|
|
39
|
+
const dirs = [process.env.CLAUDE_PROJECT_DIR, process.cwd(), projectRoot, os.homedir()].filter(Boolean);
|
|
40
|
+
for (const d of dirs) {
|
|
41
|
+
const p = path.join(d, '.claude', 'tts-provider.txt');
|
|
42
|
+
try {
|
|
43
|
+
if (fs.existsSync(p)) {
|
|
44
|
+
const provider = fs.readFileSync(p, 'utf8').trim();
|
|
45
|
+
return { remote: _REMOTE_PROVIDERS.includes(provider), projectDir: d };
|
|
46
|
+
}
|
|
47
|
+
} catch { /* next */ }
|
|
48
|
+
}
|
|
49
|
+
return { remote: false, projectDir: '' };
|
|
50
|
+
}
|
|
51
|
+
|
|
25
52
|
const IS_TEST = process.env.AGENTVIBES_TEST_MODE === 'true';
|
|
26
53
|
|
|
27
54
|
let blessed;
|
|
@@ -540,6 +567,10 @@ export function createMusicTab(screen, services) {
|
|
|
540
567
|
|
|
541
568
|
let _playingProcess = null;
|
|
542
569
|
let _playingTrackId = null;
|
|
570
|
+
// Remote playback is fire-and-forget (the local sender exits in ms while the
|
|
571
|
+
// receiver keeps playing), so the on/off toggle tracks the intended remote
|
|
572
|
+
// track here rather than relying on a live local process.
|
|
573
|
+
let _remotePlayingTrackId = null;
|
|
543
574
|
|
|
544
575
|
function _killPlayingProcess() {
|
|
545
576
|
if (_playingProcess) {
|
|
@@ -550,6 +581,51 @@ export function createMusicTab(screen, services) {
|
|
|
550
581
|
|
|
551
582
|
const _spawnEnv = buildAudioEnv();
|
|
552
583
|
|
|
584
|
+
/**
|
|
585
|
+
* Spawn the SSH sender to play a music track on the receiver, or stop the
|
|
586
|
+
* current one. Fire-and-forget (the sender exits after handing the payload to
|
|
587
|
+
* SSH; the receiver plays/stops asynchronously). Returns true if the send was
|
|
588
|
+
* launched. On a play error the remote-playing state is cleared so the toggle
|
|
589
|
+
* doesn't get stuck "on".
|
|
590
|
+
* @param {{remote:boolean, projectDir:string}} mp
|
|
591
|
+
* @param {{track?:string, stop?:boolean}} opts
|
|
592
|
+
*/
|
|
593
|
+
function _sendMusicRemote(mp, { track = null, stop = false } = {}) {
|
|
594
|
+
const _pkgSender = path.resolve(_MUSIC_TAB_DIR, '..', '..', '..', '.claude', 'hooks', 'play-tts-ssh-remote.sh');
|
|
595
|
+
const senderPath = fs.existsSync(_pkgSender)
|
|
596
|
+
? _pkgSender
|
|
597
|
+
: path.join(mp.projectDir, '.claude', 'hooks', 'play-tts-ssh-remote.sh');
|
|
598
|
+
const env = { ..._spawnEnv, CLAUDE_PROJECT_DIR: mp.projectDir };
|
|
599
|
+
if (stop) env.AGENTVIBES_MUSIC_STOP = '1';
|
|
600
|
+
else env.AGENTVIBES_MUSIC_ONLY = track;
|
|
601
|
+
let rproc;
|
|
602
|
+
try {
|
|
603
|
+
rproc = spawn('bash', [senderPath, '', ''], { stdio: ['ignore', 'ignore', 'pipe'], detached: true, env }); // NOSONAR
|
|
604
|
+
} catch {
|
|
605
|
+
previewLine.setContent('{red-fg}Remote music preview failed{/red-fg}');
|
|
606
|
+
screen.render();
|
|
607
|
+
setTimeout(() => { previewLine.setContent(_listFocused ? _hintText() : ''); screen.render(); }, 4000);
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
let _rerr = '';
|
|
611
|
+
if (rproc.stderr) rproc.stderr.on('data', d => { _rerr += d.toString(); });
|
|
612
|
+
rproc.on('exit', (code) => {
|
|
613
|
+
if (stop || code === 0) return;
|
|
614
|
+
if (_remotePlayingTrackId === track) _remotePlayingTrackId = null;
|
|
615
|
+
const msg = _rerr.trim().split('\n').pop() || 'Remote preview failed';
|
|
616
|
+
previewLine.setContent(`{red-fg}♪ ${msg}{/red-fg}`);
|
|
617
|
+
screen.render();
|
|
618
|
+
setTimeout(() => { previewLine.setContent(_listFocused ? _hintText() : ''); screen.render(); }, 4000);
|
|
619
|
+
});
|
|
620
|
+
rproc.on('error', () => {
|
|
621
|
+
if (stop) return;
|
|
622
|
+
if (_remotePlayingTrackId === track) _remotePlayingTrackId = null;
|
|
623
|
+
previewLine.setContent('{red-fg}Remote music preview failed{/red-fg}');
|
|
624
|
+
screen.render();
|
|
625
|
+
});
|
|
626
|
+
return true;
|
|
627
|
+
}
|
|
628
|
+
|
|
553
629
|
process.on('exit', () => { _killPlayingProcess(); });
|
|
554
630
|
|
|
555
631
|
/**
|
|
@@ -566,6 +642,29 @@ export function createMusicTab(screen, services) {
|
|
|
566
642
|
return;
|
|
567
643
|
}
|
|
568
644
|
|
|
645
|
+
// Remote provider: forward to the receiver instead of local playback (which
|
|
646
|
+
// is silent on a headless box). The receiver auto-stops any prior track when
|
|
647
|
+
// a new one arrives; pressing Space on the currently-playing track sends an
|
|
648
|
+
// explicit stop (toggle off).
|
|
649
|
+
const _mp = _resolveMusicProvider();
|
|
650
|
+
if (_mp.remote) {
|
|
651
|
+
if (_remotePlayingTrackId === trackId) {
|
|
652
|
+
_sendMusicRemote(_mp, { stop: true });
|
|
653
|
+
_remotePlayingTrackId = null;
|
|
654
|
+
previewLine.setContent(_listFocused ? _hintText() : '');
|
|
655
|
+
screen.render();
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
const rlabel = _allTracks.find(t => t.id === trackId)?.label ?? formatTrackLabel(trackId);
|
|
659
|
+
if (_sendMusicRemote(_mp, { track: trackId })) {
|
|
660
|
+
_remotePlayingTrackId = trackId;
|
|
661
|
+
previewLine.setContent(`{${COLORS.playingFg}-fg}♪ Playing on receiver: ${rlabel} (Space to stop){/${COLORS.playingFg}-fg}`);
|
|
662
|
+
}
|
|
663
|
+
screen.render();
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// ── Local playback ──────────────────────────────────────────────────────
|
|
569
668
|
// Toggle: second press on the same track → stop
|
|
570
669
|
if (_playingTrackId === trackId) {
|
|
571
670
|
_killPlayingProcess();
|
|
@@ -38,7 +38,6 @@ export function createPlaceholderTab(contentArea, label) {
|
|
|
38
38
|
/** Map of tabId → display label for all tabs */
|
|
39
39
|
export const TAB_DISPLAY_LABELS = {
|
|
40
40
|
settings: 'Settings',
|
|
41
|
-
voices: 'Voices',
|
|
42
41
|
music: 'Music',
|
|
43
42
|
agents: 'BMad',
|
|
44
43
|
receiver: 'Receiver',
|
|
@@ -66,7 +65,6 @@ export function getTabLabel(id, lang = 'en') {
|
|
|
66
65
|
const keyMap = {
|
|
67
66
|
setup: 'tabSetup',
|
|
68
67
|
settings: 'tabSettings',
|
|
69
|
-
voices: 'tabVoices',
|
|
70
68
|
music: 'tabMusic',
|
|
71
69
|
agents: 'tabBmad',
|
|
72
70
|
receiver: 'tabReceiver',
|