agentvibes 5.12.0-alpha.0 → 5.13.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 (129) 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/commands.json +0 -20
  5. package/.claude/commands/agent-vibes/unmute.md +6 -2
  6. package/.claude/commands/agent-vibes-bmad-voices.md +117 -117
  7. package/.claude/commands/agent-vibes-rdp.md +24 -24
  8. package/.claude/config/audio-effects.cfg +6 -6
  9. package/.claude/config/audio-effects.cfg.bak-kokoro +7 -0
  10. package/.claude/config/background-music-enabled.txt +1 -1
  11. package/.claude/config/background-music-position.txt +27 -0
  12. package/.claude/config/background-music-volume.txt +1 -0
  13. package/.claude/config/background-music.cfg +1 -0
  14. package/.claude/config/background-music.txt +1 -0
  15. package/.claude/config/language.txt +1 -0
  16. package/.claude/config/personality.txt +1 -0
  17. package/.claude/config/tts-speech-rate.txt +1 -0
  18. package/.claude/config/tts-verbosity.txt +1 -0
  19. package/.claude/docs/TERMUX_SETUP.md +408 -408
  20. package/.claude/github-star-reminder.txt +1 -1
  21. package/.claude/hooks/agentvibes-session-id.sh +69 -0
  22. package/.claude/hooks/bmad-party-speak.sh +20 -4
  23. package/.claude/hooks/bmad-speak.sh +60 -2
  24. package/.claude/hooks/bmad-tts-injector.sh +20 -1
  25. package/.claude/hooks/bmad-voice-manager.sh +25 -3
  26. package/.claude/hooks/clawdbot-receiver-SECURE.sh +21 -2
  27. package/.claude/hooks/clawdbot-receiver.sh +19 -1
  28. package/.claude/hooks/elevenlabs-voices.sh +62 -0
  29. package/.claude/hooks/kokoro-installer.sh +20 -10
  30. package/.claude/hooks/language-manager.sh +10 -3
  31. package/.claude/hooks/party-set-room.sh +71 -0
  32. package/.claude/hooks/party-stage-roster.py +328 -0
  33. package/.claude/hooks/personality-manager.sh +19 -2
  34. package/.claude/hooks/piper-voice-manager.sh +3 -2
  35. package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +24 -5
  36. package/.claude/hooks/play-tts-elevenlabs.sh +38 -118
  37. package/.claude/hooks/play-tts-kokoro.sh +33 -6
  38. package/.claude/hooks/play-tts-soprano.sh +3 -2
  39. package/.claude/hooks/play-tts-ssh-remote.sh +72 -27
  40. package/.claude/hooks/play-tts-termux-ssh.sh +5 -4
  41. package/.claude/hooks/play-tts.sh +86 -69
  42. package/.claude/hooks/provider-catalog.json +352 -0
  43. package/.claude/hooks/provider-catalog.sh +161 -0
  44. package/.claude/hooks/provider-commands.sh +2 -1
  45. package/.claude/hooks/provider-manager.sh +47 -9
  46. package/.claude/hooks/python-resolver.sh +117 -0
  47. package/.claude/hooks/session-id.sh +56 -0
  48. package/.claude/hooks/session-start-tts.sh +39 -0
  49. package/.claude/hooks/speed-manager.sh +1 -1
  50. package/.claude/hooks/translate-manager.sh +3 -2
  51. package/.claude/hooks/translator.py +1 -1
  52. package/.claude/hooks/voice-manager.sh +242 -10
  53. package/.claude/hooks-windows/audio-cache-utils.ps1.user.bak +119 -0
  54. package/.claude/hooks-windows/language-manager.ps1 +7 -1
  55. package/.claude/hooks-windows/personality-manager.ps1 +16 -1
  56. package/.claude/hooks-windows/play-tts-kokoro.ps1 +20 -4
  57. package/.claude/hooks-windows/play-tts.ps1 +32 -3
  58. package/.claude/hooks-windows/provider-catalog.ps1 +140 -0
  59. package/.claude/hooks-windows/provider-manager.ps1 +63 -8
  60. package/.claude/hooks-windows/soprano-gradio-synth.py.user.bak +153 -0
  61. package/.claude/hooks-windows/tts-watcher.ps1 +84 -8
  62. package/.claude/hooks-windows/voice-manager-windows.ps1 +49 -0
  63. package/.claude/piper-voices-dir.txt +1 -0
  64. package/.clawdbot/README.md +105 -105
  65. package/.mcp.json +15 -0
  66. package/LICENSE +190 -190
  67. package/README.md +20 -6
  68. package/RELEASE_NOTES.md +81 -0
  69. package/WINDOWS-SETUP.md +208 -208
  70. package/bin/agent-vibes +39 -39
  71. package/bin/mcp-server.js +121 -121
  72. package/bin/test-bmad-pr +78 -78
  73. package/mcp-server/QUICK_START.md +203 -203
  74. package/mcp-server/README.md +345 -345
  75. package/mcp-server/examples/claude_desktop_config.json +11 -11
  76. package/mcp-server/examples/claude_desktop_config_piper.json +9 -9
  77. package/mcp-server/examples/custom_instructions.md +169 -169
  78. package/mcp-server/pyproject.toml +52 -52
  79. package/mcp-server/requirements.txt +2 -2
  80. package/mcp-server/server.py +146 -49
  81. package/mcp-server/test_mcp_correctness.py +20 -2
  82. package/mcp-server/test_server.py +395 -395
  83. package/mcp-server/test_windows_script_parity.py +0 -2
  84. package/package.json +1 -1
  85. package/src/cli/list-personalities.js +110 -110
  86. package/src/cli/list-voices.js +115 -11
  87. package/src/commands/bmad-voices.js +394 -394
  88. package/src/console/app.js +1 -9
  89. package/src/console/bling.js +71 -0
  90. package/src/console/brand-colors.js +13 -13
  91. package/src/console/constants/personalities.js +44 -44
  92. package/src/console/footer-config.js +0 -4
  93. package/src/console/music-preview.js +79 -0
  94. package/src/console/navigation.js +0 -1
  95. package/src/console/tabs/agents-tab.js +14 -2
  96. package/src/console/tabs/help-tab.js +314 -314
  97. package/src/console/tabs/music-tab.js +81 -5
  98. package/src/console/tabs/placeholder-tab.js +0 -2
  99. package/src/console/tabs/readme-tab.js +272 -272
  100. package/src/console/tabs/settings-tab.js +207 -14
  101. package/src/console/tabs/setup-tab.js +39 -36
  102. package/src/console/tabs/voices-tab.js +102 -23
  103. package/src/console/widgets/destroy-list.js +25 -25
  104. package/src/console/widgets/notice.js +55 -55
  105. package/src/console/widgets/track-picker.js +82 -0
  106. package/src/installer/language-screen.js +31 -31
  107. package/src/installer/music-file-input.js +304 -304
  108. package/src/installer.js +124 -10
  109. package/src/services/language-service.js +47 -47
  110. package/src/services/navigation-service.js +1 -1
  111. package/src/services/provider-catalog.js +412 -0
  112. package/src/services/provider-voice-catalog.js +52 -73
  113. package/src/services/tts-engine-service.js +29 -0
  114. package/src/utils/audio-format-validator.js +277 -277
  115. package/src/utils/dependency-checker.js +469 -469
  116. package/src/utils/file-ownership-verifier.js +358 -358
  117. package/src/utils/music-file-validator.js +285 -285
  118. package/src/utils/preview-list-prompt.js +144 -144
  119. package/src/utils/provider-validator.js +62 -12
  120. package/src/utils/secure-music-storage.js +412 -412
  121. package/voice-assignments.json +8244 -8244
  122. package/.claude/commands/agent-vibes/language.md +0 -23
  123. package/.claude/commands/agent-vibes/learn.md +0 -67
  124. package/.claude/commands/agent-vibes/replay-target.md +0 -14
  125. package/.claude/commands/agent-vibes/target-voice.md +0 -26
  126. package/.claude/commands/agent-vibes/target.md +0 -30
  127. package/.claude/hooks/learn-manager.sh +0 -492
  128. package/.claude/hooks/replay-target-audio.sh +0 -95
  129. package/.claude/hooks-windows/learn-manager.ps1 +0 -241
@@ -1,144 +1,144 @@
1
- /**
2
- * Custom Inquirer List Prompt with Spacebar Preview
3
- * Uses wrapper approach with readline keypress events
4
- */
5
-
6
- import readline from 'node:readline';
7
- import { execSync } from 'node:child_process';
8
-
9
- /**
10
- * Wrapper for inquirer list prompt that adds spacebar preview
11
- * @param {Object} inquirer - Inquirer instance
12
- * @param {Object} config - Prompt configuration
13
- * @param {Function} config.onPreview - Callback for preview (receives selected value)
14
- * @returns {Promise} Inquirer prompt promise
15
- */
16
- export async function createPreviewListPrompt(inquirer, config) {
17
- const { onPreview, ...promptConfig } = config;
18
-
19
- // Set up keypress listener
20
- let keypressListener = null;
21
-
22
- // Track playing state
23
- let currentlyPlaying = null;
24
- let audioProcess = null;
25
-
26
- // Initialize currentSelection to match the default value
27
- let currentSelection = 0;
28
- if (promptConfig.default) {
29
- const defaultIndex = promptConfig.choices.findIndex(c => c.value === promptConfig.default);
30
- if (defaultIndex !== -1) {
31
- currentSelection = defaultIndex;
32
- }
33
- }
34
-
35
- // Function to stop currently playing audio
36
- const stopAudio = () => {
37
- // Kill the specific process if we have it
38
- // SECURITY: Only kill our own process, never use pkill which affects all users
39
- if (audioProcess) {
40
- try {
41
- audioProcess.kill('SIGKILL');
42
- audioProcess = null;
43
- } catch (e) {
44
- // Process might have already finished or already killed
45
- }
46
- }
47
-
48
- currentlyPlaying = null;
49
- };
50
-
51
- if (onPreview && process.stdin.isTTY) {
52
- readline.emitKeypressEvents(process.stdin);
53
- // SECURITY: Wrap in try-catch to prevent terminal corruption on error
54
- try {
55
- if (process.stdin.setRawMode) {
56
- process.stdin.setRawMode(true);
57
- }
58
- } catch (e) {
59
- // Failed to set raw mode, continue without it
60
- }
61
-
62
- keypressListener = async (str, key) => {
63
- // Track current selection based on arrow keys
64
- if (key && key.name === 'down') {
65
- currentSelection = Math.min(currentSelection + 1, promptConfig.choices.length - 1);
66
- } else if (key && key.name === 'up') {
67
- currentSelection = Math.max(currentSelection - 1, 0);
68
- }
69
-
70
- if (key && key.name === 'space') {
71
- // Get the current item (don't filter - use actual index)
72
- const currentChoice = promptConfig.choices[currentSelection];
73
-
74
- // Only preview if it's a valid choice (not separator, not special item)
75
- if (currentChoice && currentChoice.value && !currentChoice.value.startsWith('__')) {
76
-
77
- // Toggle: if same voice pressed twice, stop it
78
- if (currentlyPlaying === currentChoice.value) {
79
- stopAudio();
80
- return;
81
- }
82
-
83
- // CRITICAL: Stop previous voice BEFORE starting new one
84
- if (currentlyPlaying) {
85
- stopAudio();
86
- // Small delay to ensure kill takes effect
87
- await new Promise(resolve => setTimeout(resolve, 100));
88
- }
89
-
90
- currentlyPlaying = currentChoice.value;
91
-
92
- // Call onPreview and store process handle immediately
93
- try {
94
- const result = await onPreview(currentChoice.value);
95
- // Store the process handle - onPreview should return the spawn() result
96
- audioProcess = result;
97
- } catch (err) {
98
- console.error(`[Preview] Error playing sample:`, err.message);
99
- currentlyPlaying = null;
100
- audioProcess = null;
101
- }
102
- }
103
- }
104
- };
105
-
106
- process.stdin.on('keypress', keypressListener);
107
- }
108
-
109
- try {
110
- // Run the standard list prompt
111
- const result = await inquirer.prompt([{
112
- ...promptConfig,
113
- type: 'list'
114
- }]);
115
-
116
- return result;
117
- } finally {
118
- // Stop any playing audio
119
- stopAudio();
120
-
121
- // Clean up keypress listener
122
- if (keypressListener) {
123
- process.stdin.removeListener('keypress', keypressListener);
124
- // SECURITY: Wrap in try-catch to ensure cleanup always completes
125
- try {
126
- if (process.stdin.setRawMode) {
127
- process.stdin.setRawMode(false);
128
- }
129
- } catch (e) {
130
- // Failed to restore raw mode, but we're exiting anyway
131
- }
132
- // readline.emitKeypressEvents() put stdin into flowing mode; pause it so the
133
- // open stdin handle no longer keeps the event loop alive after the prompt
134
- // resolves (otherwise the process lingers until stdin is otherwise closed).
135
- try {
136
- process.stdin.pause();
137
- } catch (e) {
138
- // stdin may already be closed; nothing more to do
139
- }
140
- }
141
- }
142
- }
143
-
144
- export default createPreviewListPrompt;
1
+ /**
2
+ * Custom Inquirer List Prompt with Spacebar Preview
3
+ * Uses wrapper approach with readline keypress events
4
+ */
5
+
6
+ import readline from 'node:readline';
7
+ import { execSync } from 'node:child_process';
8
+
9
+ /**
10
+ * Wrapper for inquirer list prompt that adds spacebar preview
11
+ * @param {Object} inquirer - Inquirer instance
12
+ * @param {Object} config - Prompt configuration
13
+ * @param {Function} config.onPreview - Callback for preview (receives selected value)
14
+ * @returns {Promise} Inquirer prompt promise
15
+ */
16
+ export async function createPreviewListPrompt(inquirer, config) {
17
+ const { onPreview, ...promptConfig } = config;
18
+
19
+ // Set up keypress listener
20
+ let keypressListener = null;
21
+
22
+ // Track playing state
23
+ let currentlyPlaying = null;
24
+ let audioProcess = null;
25
+
26
+ // Initialize currentSelection to match the default value
27
+ let currentSelection = 0;
28
+ if (promptConfig.default) {
29
+ const defaultIndex = promptConfig.choices.findIndex(c => c.value === promptConfig.default);
30
+ if (defaultIndex !== -1) {
31
+ currentSelection = defaultIndex;
32
+ }
33
+ }
34
+
35
+ // Function to stop currently playing audio
36
+ const stopAudio = () => {
37
+ // Kill the specific process if we have it
38
+ // SECURITY: Only kill our own process, never use pkill which affects all users
39
+ if (audioProcess) {
40
+ try {
41
+ audioProcess.kill('SIGKILL');
42
+ audioProcess = null;
43
+ } catch (e) {
44
+ // Process might have already finished or already killed
45
+ }
46
+ }
47
+
48
+ currentlyPlaying = null;
49
+ };
50
+
51
+ if (onPreview && process.stdin.isTTY) {
52
+ readline.emitKeypressEvents(process.stdin);
53
+ // SECURITY: Wrap in try-catch to prevent terminal corruption on error
54
+ try {
55
+ if (process.stdin.setRawMode) {
56
+ process.stdin.setRawMode(true);
57
+ }
58
+ } catch (e) {
59
+ // Failed to set raw mode, continue without it
60
+ }
61
+
62
+ keypressListener = async (str, key) => {
63
+ // Track current selection based on arrow keys
64
+ if (key && key.name === 'down') {
65
+ currentSelection = Math.min(currentSelection + 1, promptConfig.choices.length - 1);
66
+ } else if (key && key.name === 'up') {
67
+ currentSelection = Math.max(currentSelection - 1, 0);
68
+ }
69
+
70
+ if (key && key.name === 'space') {
71
+ // Get the current item (don't filter - use actual index)
72
+ const currentChoice = promptConfig.choices[currentSelection];
73
+
74
+ // Only preview if it's a valid choice (not separator, not special item)
75
+ if (currentChoice && currentChoice.value && !currentChoice.value.startsWith('__')) {
76
+
77
+ // Toggle: if same voice pressed twice, stop it
78
+ if (currentlyPlaying === currentChoice.value) {
79
+ stopAudio();
80
+ return;
81
+ }
82
+
83
+ // CRITICAL: Stop previous voice BEFORE starting new one
84
+ if (currentlyPlaying) {
85
+ stopAudio();
86
+ // Small delay to ensure kill takes effect
87
+ await new Promise(resolve => setTimeout(resolve, 100));
88
+ }
89
+
90
+ currentlyPlaying = currentChoice.value;
91
+
92
+ // Call onPreview and store process handle immediately
93
+ try {
94
+ const result = await onPreview(currentChoice.value);
95
+ // Store the process handle - onPreview should return the spawn() result
96
+ audioProcess = result;
97
+ } catch (err) {
98
+ console.error(`[Preview] Error playing sample:`, err.message);
99
+ currentlyPlaying = null;
100
+ audioProcess = null;
101
+ }
102
+ }
103
+ }
104
+ };
105
+
106
+ process.stdin.on('keypress', keypressListener);
107
+ }
108
+
109
+ try {
110
+ // Run the standard list prompt
111
+ const result = await inquirer.prompt([{
112
+ ...promptConfig,
113
+ type: 'list'
114
+ }]);
115
+
116
+ return result;
117
+ } finally {
118
+ // Stop any playing audio
119
+ stopAudio();
120
+
121
+ // Clean up keypress listener
122
+ if (keypressListener) {
123
+ process.stdin.removeListener('keypress', keypressListener);
124
+ // SECURITY: Wrap in try-catch to ensure cleanup always completes
125
+ try {
126
+ if (process.stdin.setRawMode) {
127
+ process.stdin.setRawMode(false);
128
+ }
129
+ } catch (e) {
130
+ // Failed to restore raw mode, but we're exiting anyway
131
+ }
132
+ // readline.emitKeypressEvents() put stdin into flowing mode; pause it so the
133
+ // open stdin handle no longer keeps the event loop alive after the prompt
134
+ // resolves (otherwise the process lingers until stdin is otherwise closed).
135
+ try {
136
+ process.stdin.pause();
137
+ } catch (e) {
138
+ // stdin may already be closed; nothing more to do
139
+ }
140
+ }
141
+ }
142
+ }
143
+
144
+ export default createPreviewListPrompt;
@@ -7,6 +7,64 @@ import { spawnSync } from 'node:child_process';
7
7
  import path from 'node:path'; // For safe path operations and traversal prevention
8
8
  import fs from 'node:fs'; // For checking file/directory existence
9
9
  import os from 'node:os'; // For os.homedir() to prevent HOME injection attacks
10
+ import { listProviders, displayName as catalogDisplayName } from '../services/provider-catalog.js';
11
+
12
+ /**
13
+ * Canonical set of TTS synthesis providers AgentVibes knows how to use.
14
+ * SINGLE SOURCE OF TRUTH: every user-facing dispatcher (MCP set_provider
15
+ * allowlist, voice switch, voice list, Windows provider manager) must recognise
16
+ * these providers — platform-scoped where noted below. A provider that lives
17
+ * here but is missing from a dispatcher is the "silent drop to Piper / Unknown
18
+ * provider" class of bug (see AVI-S8.1). The dispatcher-parity conformance test
19
+ * asserts this invariant.
20
+ *
21
+ * Platform notes: `macos` is Darwin-only; `windows-sapi`/`windows-piper` are
22
+ * Windows-only. `kokoro` ships a play-tts runtime on BOTH Unix
23
+ * (play-tts-kokoro.sh) and PowerShell (play-tts-kokoro.ps1). `elevenlabs` ships
24
+ * a Unix runtime only (play-tts-elevenlabs.sh) — there is NO
25
+ * play-tts-elevenlabs.ps1 yet — so it is recognised by the portable (bash/JS)
26
+ * dispatchers but intentionally excluded from the Windows provider manager
27
+ * until a PowerShell runtime lands (AVI-S8.2).
28
+ * @type {readonly string[]}
29
+ */
30
+ export const SUPPORTED_PROVIDERS = Object.freeze(listProviders().map((p) => p.id));
31
+
32
+ /**
33
+ * Providers that use the SAME id on every OS and have at least a Unix (bash)
34
+ * runtime. Every PORTABLE dispatcher — the MCP set_provider allowlist,
35
+ * voice-manager.sh, and list-voices.js — must recognise these regardless of
36
+ * platform. DERIVED VIEW over the catalog: the providers with a Unix runtime
37
+ * whose voices are enumerable without disk (static/name-to-id) — i.e. kokoro and
38
+ * elevenlabs.
39
+ * @type {readonly string[]}
40
+ */
41
+ export const CROSS_PLATFORM_PROVIDERS = Object.freeze(
42
+ listProviders()
43
+ .filter((p) => p.runtime.unix !== null && (p.voiceModel === 'static' || p.voiceModel === 'name-to-id'))
44
+ .map((p) => p.id),
45
+ );
46
+
47
+ /**
48
+ * Subset of {@link CROSS_PLATFORM_PROVIDERS} that ALSO ships a PowerShell runtime
49
+ * (play-tts-*.ps1), so the Windows provider manager (provider-manager.ps1) must
50
+ * recognise them too. `elevenlabs` is excluded until play-tts-elevenlabs.ps1
51
+ * exists (AVI-S8.2). DERIVED VIEW over the catalog.
52
+ * @type {readonly string[]}
53
+ */
54
+ export const WINDOWS_RUNTIME_PROVIDERS = Object.freeze(
55
+ listProviders()
56
+ .filter((p) => p.runtime.unix !== null && (p.voiceModel === 'static' || p.voiceModel === 'name-to-id') && p.runtime.windows !== null)
57
+ .map((p) => p.id),
58
+ );
59
+
60
+ /**
61
+ * Is `name` a provider AgentVibes canonically supports?
62
+ * @param {string} name
63
+ * @returns {boolean}
64
+ */
65
+ export function isKnownProvider(name) {
66
+ return SUPPORTED_PROVIDERS.includes(String(name || '').toLowerCase());
67
+ }
10
68
 
11
69
  /**
12
70
  * Helper: Check if command exists in PATH
@@ -603,16 +661,8 @@ function getPackageInfo(pkgName) {
603
661
  * @returns {string} Display name
604
662
  */
605
663
  export function getProviderDisplayName(providerName) {
606
- const names = {
607
- soprano: 'Soprano TTS',
608
- piper: 'Piper TTS',
609
- kokoro: 'Kokoro TTS',
610
- elevenlabs: 'ElevenLabs',
611
- macos: 'macOS Say',
612
- sapi: 'Windows SAPI',
613
- 'windows-sapi': 'Windows SAPI',
614
- 'windows-piper': 'Piper TTS'
615
- };
616
-
617
- return names[providerName] || providerName;
664
+ // DERIVED VIEW: delegate to the Provider Catalog (single source of truth for
665
+ // display names). The catalog resolves aliases (e.g. 'sapi' → Windows SAPI) and
666
+ // passes unknown ids through unchanged, preserving this function's contract.
667
+ return catalogDisplayName(providerName);
618
668
  }