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.
Files changed (71) hide show
  1. package/.agentvibes/config.json +15 -1
  2. package/.agentvibes/install-manifest.json +342 -0
  3. package/.claude/activation-instructions +54 -54
  4. package/.claude/commands/agent-vibes-bmad-voices.md +117 -117
  5. package/.claude/commands/agent-vibes-rdp.md +24 -24
  6. package/.claude/config/audio-effects.cfg +5 -5
  7. package/.claude/config/audio-effects.cfg.bak-kokoro +7 -0
  8. package/.claude/config/background-music-enabled.txt +1 -1
  9. package/.claude/config/background-music-position.txt +27 -0
  10. package/.claude/config/background-music-volume.txt +1 -0
  11. package/.claude/config/background-music.cfg +1 -0
  12. package/.claude/config/background-music.txt +1 -0
  13. package/.claude/config/language.txt +1 -0
  14. package/.claude/config/personality.txt +1 -0
  15. package/.claude/config/tts-speech-rate.txt +1 -0
  16. package/.claude/config/tts-verbosity.txt +1 -0
  17. package/.claude/docs/TERMUX_SETUP.md +408 -408
  18. package/.claude/github-star-reminder.txt +1 -1
  19. package/.claude/hooks/play-tts-ssh-remote.sh +42 -5
  20. package/.claude/hooks/play-tts.sh +21 -9
  21. package/.claude/hooks-windows/audio-cache-utils.ps1.user.bak +119 -0
  22. package/.claude/hooks-windows/soprano-gradio-synth.py.user.bak +153 -0
  23. package/.claude/hooks-windows/tts-watcher.ps1 +55 -0
  24. package/.claude/piper-voices-dir.txt +1 -0
  25. package/.clawdbot/README.md +105 -105
  26. package/.mcp.json +22 -0
  27. package/LICENSE +190 -190
  28. package/README.md +11 -6
  29. package/RELEASE_NOTES.md +38 -0
  30. package/WINDOWS-SETUP.md +208 -208
  31. package/bin/agent-vibes +39 -39
  32. package/bin/mcp-server.js +121 -121
  33. package/bin/test-bmad-pr +78 -78
  34. package/mcp-server/QUICK_START.md +203 -203
  35. package/mcp-server/README.md +345 -345
  36. package/mcp-server/examples/claude_desktop_config.json +11 -11
  37. package/mcp-server/examples/claude_desktop_config_piper.json +9 -9
  38. package/mcp-server/examples/custom_instructions.md +169 -169
  39. package/mcp-server/pyproject.toml +52 -52
  40. package/mcp-server/requirements.txt +2 -2
  41. package/mcp-server/test_server.py +395 -395
  42. package/package.json +1 -1
  43. package/src/cli/list-personalities.js +110 -110
  44. package/src/cli/list-voices.js +114 -114
  45. package/src/commands/bmad-voices.js +394 -394
  46. package/src/console/app.js +1 -9
  47. package/src/console/brand-colors.js +13 -13
  48. package/src/console/constants/personalities.js +44 -44
  49. package/src/console/footer-config.js +0 -4
  50. package/src/console/navigation.js +0 -1
  51. package/src/console/tabs/agents-tab.js +14 -2
  52. package/src/console/tabs/help-tab.js +314 -314
  53. package/src/console/tabs/music-tab.js +101 -2
  54. package/src/console/tabs/placeholder-tab.js +0 -2
  55. package/src/console/tabs/readme-tab.js +272 -272
  56. package/src/console/tabs/settings-tab.js +13 -2
  57. package/src/console/tabs/setup-tab.js +30 -2
  58. package/src/console/tabs/voices-tab.js +19 -9
  59. package/src/console/widgets/destroy-list.js +25 -25
  60. package/src/console/widgets/notice.js +55 -55
  61. package/src/installer/language-screen.js +31 -31
  62. package/src/installer/music-file-input.js +304 -304
  63. package/src/services/language-service.js +47 -47
  64. package/src/services/navigation-service.js +1 -1
  65. package/src/utils/audio-format-validator.js +277 -277
  66. package/src/utils/dependency-checker.js +469 -469
  67. package/src/utils/file-ownership-verifier.js +358 -358
  68. package/src/utils/music-file-validator.js +285 -285
  69. package/src/utils/preview-list-prompt.js +144 -144
  70. package/src/utils/secure-music-storage.js +412 -412
  71. 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
- path.dirname(fileURLToPath(import.meta.url)),
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',