agentvibes 4.0.1 → 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.
Files changed (219) hide show
  1. package/.agentvibes/bmad/bmad-voices.md +69 -69
  2. package/.agentvibes/config.json +12 -0
  3. package/.claude/activation-instructions +54 -54
  4. package/.claude/audio/tracks/README.md +52 -52
  5. package/.claude/commands/agent-vibes/add.md +21 -21
  6. package/.claude/commands/agent-vibes/agent-vibes.md +101 -101
  7. package/.claude/commands/agent-vibes/agent.md +79 -79
  8. package/.claude/commands/agent-vibes/background-music.md +111 -111
  9. package/.claude/commands/agent-vibes/bmad.md +198 -198
  10. package/.claude/commands/agent-vibes/clean.md +18 -18
  11. package/.claude/commands/agent-vibes/cleanup.md +18 -18
  12. package/.claude/commands/agent-vibes/commands.json +145 -145
  13. package/.claude/commands/agent-vibes/effects.md +97 -97
  14. package/.claude/commands/agent-vibes/get.md +9 -9
  15. package/.claude/commands/agent-vibes/hide.md +91 -91
  16. package/.claude/commands/agent-vibes/language.md +23 -23
  17. package/.claude/commands/agent-vibes/learn.md +67 -67
  18. package/.claude/commands/agent-vibes/list.md +13 -13
  19. package/.claude/commands/agent-vibes/mute.md +37 -37
  20. package/.claude/commands/agent-vibes/preview.md +17 -17
  21. package/.claude/commands/agent-vibes/provider.md +68 -68
  22. package/.claude/commands/agent-vibes/replay-target.md +14 -14
  23. package/.claude/commands/agent-vibes/sample.md +12 -12
  24. package/.claude/commands/agent-vibes/set-favorite-voice.md +84 -84
  25. package/.claude/commands/agent-vibes/set-pretext.md +65 -65
  26. package/.claude/commands/agent-vibes/set-speed.md +41 -41
  27. package/.claude/commands/agent-vibes/show.md +84 -84
  28. package/.claude/commands/agent-vibes/switch.md +87 -87
  29. package/.claude/commands/agent-vibes/target-voice.md +26 -26
  30. package/.claude/commands/agent-vibes/target.md +30 -30
  31. package/.claude/commands/agent-vibes/translate.md +68 -68
  32. package/.claude/commands/agent-vibes/unmute.md +45 -45
  33. package/.claude/commands/agent-vibes/verbosity.md +89 -89
  34. package/.claude/commands/agent-vibes/whoami.md +7 -7
  35. package/.claude/commands/agent-vibes-bmad-voices.md +117 -117
  36. package/.claude/commands/agent-vibes-rdp.md +24 -24
  37. package/.claude/config/agentvibes.json +1 -0
  38. package/.claude/config/audio-effects.cfg +3 -2
  39. package/.claude/config/audio-effects.cfg.sample +52 -52
  40. package/.claude/config/background-music-volume.txt +1 -0
  41. package/.claude/config/intro-text.txt +1 -0
  42. package/.claude/config/piper-speech-rate.txt +4 -0
  43. package/.claude/config/piper-target-speech-rate.txt +1 -0
  44. package/.claude/config/reverb-level.txt +1 -0
  45. package/.claude/config/tts-speech-rate.txt +4 -0
  46. package/.claude/config/tts-target-speech-rate.txt +1 -0
  47. package/.claude/docs/TERMUX_SETUP.md +408 -408
  48. package/.claude/github-star-reminder.txt +1 -1
  49. package/.claude/hooks/README-TTS-QUEUE.md +135 -135
  50. package/.claude/hooks/audio-cache-utils.sh +246 -246
  51. package/.claude/hooks/audio-processor.sh +433 -389
  52. package/.claude/hooks/background-music-manager.sh +404 -404
  53. package/.claude/hooks/bmad-speak-enhanced.sh +165 -165
  54. package/.claude/hooks/bmad-speak.sh +269 -112
  55. package/.claude/hooks/bmad-tts-injector.sh +568 -568
  56. package/.claude/hooks/bmad-voice-manager.sh +928 -928
  57. package/.claude/hooks/clawdbot-receiver-SECURE.sh +129 -129
  58. package/.claude/hooks/clawdbot-receiver.sh +107 -107
  59. package/.claude/hooks/clean-audio-cache.sh +22 -22
  60. package/.claude/hooks/cleanup-cache.sh +106 -106
  61. package/.claude/hooks/configure-rdp-mode.sh +137 -137
  62. package/.claude/hooks/download-extra-voices.sh +244 -244
  63. package/.claude/hooks/effects-manager.sh +268 -268
  64. package/.claude/hooks/github-star-reminder.sh +154 -154
  65. package/.claude/hooks/language-manager.sh +362 -362
  66. package/.claude/hooks/learn-manager.sh +492 -492
  67. package/.claude/hooks/macos-voice-manager.sh +205 -205
  68. package/.claude/hooks/migrate-background-music.sh +125 -125
  69. package/.claude/hooks/migrate-to-agentvibes.sh +161 -161
  70. package/.claude/hooks/optimize-background-music.sh +87 -87
  71. package/.claude/hooks/path-resolver.sh +60 -60
  72. package/.claude/hooks/personality-manager.sh +448 -448
  73. package/.claude/hooks/piper-download-voices.sh +225 -225
  74. package/.claude/hooks/piper-installer.sh +292 -292
  75. package/.claude/hooks/piper-multispeaker-registry.sh +171 -171
  76. package/.claude/hooks/piper-voice-manager.sh +24 -3
  77. package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +90 -90
  78. package/.claude/hooks/play-tts-enhanced.sh +105 -70
  79. package/.claude/hooks/play-tts-macos.sh +368 -345
  80. package/.claude/hooks/play-tts-piper.sh +679 -578
  81. package/.claude/hooks/play-tts-soprano.sh +356 -320
  82. package/.claude/hooks/play-tts-ssh-remote.sh +167 -88
  83. package/.claude/hooks/play-tts-termux-ssh.sh +169 -169
  84. package/.claude/hooks/play-tts.sh +301 -298
  85. package/.claude/hooks/prepare-release.sh +54 -54
  86. package/.claude/hooks/provider-commands.sh +617 -617
  87. package/.claude/hooks/provider-manager.sh +399 -399
  88. package/.claude/hooks/replay-target-audio.sh +95 -95
  89. package/.claude/hooks/requirements.txt +6 -6
  90. package/.claude/hooks/sentiment-manager.sh +201 -201
  91. package/.claude/hooks/session-start-tts.sh +81 -71
  92. package/.claude/hooks/soprano-gradio-synth.py +139 -139
  93. package/.claude/hooks/speed-manager.sh +291 -291
  94. package/.claude/hooks/stop-tts.sh +84 -0
  95. package/.claude/hooks/termux-installer.sh +261 -261
  96. package/.claude/hooks/translate-manager.sh +341 -341
  97. package/.claude/hooks/translator.py +237 -237
  98. package/.claude/hooks/tts-queue-worker.sh +145 -114
  99. package/.claude/hooks/tts-queue.sh +165 -136
  100. package/.claude/hooks/verbosity-manager.sh +178 -178
  101. package/.claude/hooks/voice-manager.sh +548 -544
  102. package/.claude/hooks-windows/audio-cache-utils.ps1 +119 -119
  103. package/.claude/hooks-windows/background-music-manager.ps1 +348 -0
  104. package/.claude/hooks-windows/clean-audio-cache.ps1 +53 -0
  105. package/.claude/hooks-windows/download-extra-voices.ps1 +185 -0
  106. package/.claude/hooks-windows/effects-manager.ps1 +294 -0
  107. package/.claude/hooks-windows/language-manager.ps1 +193 -0
  108. package/.claude/hooks-windows/learn-manager.ps1 +241 -0
  109. package/.claude/hooks-windows/personality-manager.ps1 +266 -0
  110. package/.claude/hooks-windows/play-tts-piper.ps1 +209 -0
  111. package/.claude/hooks-windows/play-tts-sapi.ps1 +108 -0
  112. package/.claude/hooks-windows/play-tts-soprano.ps1 +159 -158
  113. package/.claude/hooks-windows/play-tts-windows-piper.ps1 +50 -5
  114. package/.claude/hooks-windows/play-tts-windows-sapi.ps1 +108 -108
  115. package/.claude/hooks-windows/play-tts.ps1 +344 -266
  116. package/.claude/hooks-windows/provider-manager.ps1 +29 -10
  117. package/.claude/hooks-windows/session-start-tts.ps1 +124 -124
  118. package/.claude/hooks-windows/soprano-gradio-synth.py +153 -153
  119. package/.claude/hooks-windows/speed-manager.ps1 +166 -0
  120. package/.claude/hooks-windows/verbosity-manager.ps1 +119 -0
  121. package/.claude/hooks-windows/voice-manager-windows.ps1 +92 -8
  122. package/.claude/output-styles/agent-vibes.md +202 -202
  123. package/.claude/personalities/angry.md +14 -14
  124. package/.claude/personalities/annoying.md +14 -14
  125. package/.claude/personalities/crass.md +14 -14
  126. package/.claude/personalities/dramatic.md +14 -14
  127. package/.claude/personalities/dry-humor.md +50 -50
  128. package/.claude/personalities/flirty.md +20 -20
  129. package/.claude/personalities/funny.md +14 -14
  130. package/.claude/personalities/grandpa.md +32 -32
  131. package/.claude/personalities/millennial.md +14 -14
  132. package/.claude/personalities/moody.md +14 -14
  133. package/.claude/personalities/normal.md +16 -16
  134. package/.claude/personalities/pirate.md +14 -14
  135. package/.claude/personalities/poetic.md +14 -14
  136. package/.claude/personalities/professional.md +14 -14
  137. package/.claude/personalities/rapper.md +55 -55
  138. package/.claude/personalities/robot.md +14 -14
  139. package/.claude/personalities/sarcastic.md +38 -38
  140. package/.claude/personalities/sassy.md +14 -14
  141. package/.claude/personalities/surfer-dude.md +14 -14
  142. package/.claude/personalities/zen.md +14 -14
  143. package/.claude/settings.json +15 -15
  144. package/.claude/verbosity.txt +1 -1
  145. package/.clawdbot/README.md +105 -105
  146. package/.clawdbot/skill/SKILL.md +241 -241
  147. package/.mcp.json +12 -0
  148. package/CLAUDE.md +170 -181
  149. package/README.md +2029 -1909
  150. package/RELEASE_NOTES.md +1310 -66
  151. package/WINDOWS-SETUP.md +208 -208
  152. package/bin/agent-vibes +39 -39
  153. package/bin/agentvibes-voice-browser.js +1840 -1826
  154. package/bin/agentvibes.js +48 -2
  155. package/bin/mcp-server.js +121 -121
  156. package/bin/mcp-server.sh +206 -206
  157. package/bin/test-bmad-pr +78 -78
  158. package/mcp-server/QUICK_START.md +203 -203
  159. package/mcp-server/README.md +345 -345
  160. package/mcp-server/WINDOWS_SETUP.md +260 -260
  161. package/mcp-server/docs/troubleshooting-audio.md +313 -313
  162. package/mcp-server/examples/claude_desktop_config.json +11 -11
  163. package/mcp-server/examples/claude_desktop_config_piper.json +9 -9
  164. package/mcp-server/examples/custom_instructions.md +169 -169
  165. package/mcp-server/install-deps.js +130 -130
  166. package/mcp-server/pyproject.toml +52 -52
  167. package/mcp-server/requirements.txt +2 -2
  168. package/mcp-server/server.py +1465 -1417
  169. package/mcp-server/test_server.py +395 -395
  170. package/mcp-server/test_windows_script_parity.py +336 -0
  171. package/package.json +110 -112
  172. package/setup-windows.ps1 +815 -815
  173. package/src/bmad-detector.js +71 -71
  174. package/src/cli/list-personalities.js +110 -110
  175. package/src/cli/list-voices.js +114 -114
  176. package/src/commands/bmad-voices.js +394 -394
  177. package/src/commands/install-mcp.js +476 -476
  178. package/src/console/app.js +824 -806
  179. package/src/console/audio-env.js +20 -1
  180. package/src/console/brand-colors.js +13 -13
  181. package/src/console/constants/personalities.js +44 -0
  182. package/src/console/footer-config.js +50 -46
  183. package/src/console/modals/modal-overlay.js +247 -247
  184. package/src/console/navigation.js +62 -61
  185. package/src/console/tabs/agents-tab.js +1684 -369
  186. package/src/console/tabs/help-tab.js +261 -261
  187. package/src/console/tabs/install-tab.js +1007 -991
  188. package/src/console/tabs/music-tab.js +22 -8
  189. package/src/console/tabs/placeholder-tab.js +53 -46
  190. package/src/console/tabs/readme-tab.js +267 -267
  191. package/src/console/tabs/receiver-tab.js +1472 -0
  192. package/src/console/tabs/settings-tab.js +185 -402
  193. package/src/console/tabs/voices-tab.js +100 -21
  194. package/src/console/widgets/destroy-list.js +25 -0
  195. package/src/console/widgets/format-utils.js +89 -0
  196. package/src/console/widgets/notice.js +55 -0
  197. package/src/console/widgets/personality-picker.js +185 -0
  198. package/src/console/widgets/reverb-picker.js +94 -0
  199. package/src/console/widgets/track-picker.js +285 -0
  200. package/src/installer/music-file-input.js +304 -304
  201. package/src/installer.js +5882 -5777
  202. package/src/services/agent-voice-store.js +423 -163
  203. package/src/services/config-service.js +264 -264
  204. package/src/services/navigation-service.js +123 -123
  205. package/src/services/provider-service.js +132 -132
  206. package/src/services/verbosity-service.js +157 -157
  207. package/src/utils/audio-duration-validator.js +298 -298
  208. package/src/utils/audio-format-validator.js +277 -277
  209. package/src/utils/dependency-checker.js +469 -466
  210. package/src/utils/file-ownership-verifier.js +358 -358
  211. package/src/utils/list-formatter.js +194 -194
  212. package/src/utils/music-file-validator.js +285 -275
  213. package/src/utils/preview-list-prompt.js +136 -136
  214. package/src/utils/provider-validator.js +96 -12
  215. package/src/utils/secure-music-storage.js +412 -412
  216. package/templates/agentvibes-receiver.sh +482 -162
  217. package/templates/audio/welcome-music.mp3 +0 -0
  218. package/voice-assignments.json +8244 -8244
  219. package/.claude/config/background-music-position.txt +0 -1
@@ -1,476 +1,476 @@
1
- #!/usr/bin/env node
2
- /**
3
- * AgentVibes MCP Server Installer
4
- *
5
- * Interactive installer for setting up AgentVibes MCP server with Claude Desktop
6
- * Handles platform-specific installation (Windows/Mac/Linux)
7
- */
8
-
9
- import inquirer from 'inquirer';
10
- import { execSync, execFileSync } from 'child_process';
11
- import fs from 'fs';
12
- import path from 'path';
13
- import os from 'os';
14
- import chalk from 'chalk';
15
- import ora from 'ora';
16
- import boxen from 'boxen';
17
- import { checkDependencies, displayMissingDependencies } from '../utils/dependency-checker.js';
18
-
19
- /**
20
- * Check if WSL is installed on Windows
21
- */
22
- function checkWSL() {
23
- try {
24
- // Security: Use execFileSync with array args to prevent command injection
25
- execFileSync('wsl', ['--version'], { stdio: 'pipe' });
26
- return true;
27
- } catch {
28
- return false;
29
- }
30
- }
31
-
32
- /**
33
- * Check if Python is available
34
- */
35
- function checkPython() {
36
- const commands = ['python3', 'python', 'py'];
37
-
38
- for (const cmd of commands) {
39
- try {
40
- // Security: Use execFileSync with array args to prevent command injection
41
- const version = execFileSync(cmd, ['--version'], { encoding: 'utf8', stdio: 'pipe' });
42
- return { available: true, command: cmd, version: version.trim() };
43
- } catch {
44
- continue;
45
- }
46
- }
47
-
48
- return { available: false };
49
- }
50
-
51
- /**
52
- * Check if Python MCP package is installed
53
- */
54
- function checkMCPPackage(pythonCmd) {
55
- try {
56
- // Security: Use execFileSync with array args to prevent command injection
57
- execFileSync(pythonCmd, ['-c', 'import mcp'], { stdio: 'pipe' });
58
- return true;
59
- } catch {
60
- return false;
61
- }
62
- }
63
-
64
- /**
65
- * Get Claude Desktop config path for current platform
66
- */
67
- function getClaudeConfigPath() {
68
- const platform = os.platform();
69
-
70
- switch (platform) {
71
- case 'darwin': // macOS
72
- return path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
73
- case 'win32': // Windows
74
- return path.join(os.homedir(), 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json');
75
- default: // Linux
76
- return path.join(os.homedir(), '.config', 'Claude', 'claude_desktop_config.json');
77
- }
78
- }
79
-
80
- /**
81
- * Get AgentVibes installation directory
82
- */
83
- function getAgentVibesDir() {
84
- // Try to find AgentVibes directory
85
- // 1. Current directory
86
- if (fs.existsSync('./.claude/hooks/play-tts.sh')) {
87
- return process.cwd();
88
- }
89
-
90
- // 2. Parent directory
91
- const parentDir = path.resolve(process.cwd(), '..');
92
- if (fs.existsSync(path.join(parentDir, '.claude/hooks/play-tts.sh'))) {
93
- return parentDir;
94
- }
95
-
96
- // 3. Ask user
97
- return null;
98
- }
99
-
100
- /**
101
- * Update Claude Desktop configuration
102
- */
103
- function updateClaudeConfig(agentVibesPath, provider, apiKey = null) {
104
- const configPath = getClaudeConfigPath();
105
- const platform = os.platform();
106
-
107
- // Create config directory if it doesn't exist
108
- const configDir = path.dirname(configPath);
109
- if (!fs.existsSync(configDir)) {
110
- fs.mkdirSync(configDir, { recursive: true });
111
- }
112
-
113
- // Read existing config or create new one
114
- let config = { mcpServers: {} };
115
- if (fs.existsSync(configPath)) {
116
- const content = fs.readFileSync(configPath, 'utf8');
117
- config = JSON.parse(content);
118
- if (!config.mcpServers) {
119
- config.mcpServers = {};
120
- }
121
- }
122
-
123
- // Prepare MCP server config
124
- let serverPath = path.join(agentVibesPath, 'mcp-server', 'server.py');
125
-
126
- if (platform === 'win32') {
127
- // Windows: Use WSL
128
- serverPath = serverPath.replace(/\\/g, '/').replace(/^([A-Z]):/, (match, drive) => {
129
- return `/mnt/${drive.toLowerCase()}`;
130
- });
131
-
132
- config.mcpServers.agentvibes = {
133
- command: 'wsl',
134
- args: ['python3', serverPath],
135
- env: {}
136
- };
137
- } else {
138
- // macOS/Linux: Use native Python
139
- config.mcpServers.agentvibes = {
140
- command: 'python3',
141
- args: [serverPath],
142
- env: {}
143
- };
144
- }
145
-
146
-
147
- // Write config atomically to prevent race conditions (TOCTOU)
148
- // Write to temp file first, then rename atomically
149
- const tempPath = `${configPath}.tmp.${process.pid}`;
150
- try {
151
- fs.writeFileSync(tempPath, JSON.stringify(config, null, 2), { mode: 0o600 });
152
- fs.renameSync(tempPath, configPath);
153
- } catch (error) {
154
- // Clean up temp file if rename fails
155
- try { fs.unlinkSync(tempPath); } catch { /* ignore cleanup errors */ }
156
- throw error;
157
- }
158
-
159
- return configPath;
160
- }
161
-
162
- /**
163
- * Install Piper TTS
164
- */
165
- async function installPiper(useWSL = false) {
166
- const spinner = ora('Installing Piper TTS...').start();
167
-
168
- try {
169
- // Security: Use execFileSync with array args to prevent command injection
170
- if (useWSL) {
171
- execFileSync('wsl', ['pipx', 'install', 'piper-tts'], { stdio: 'inherit' });
172
- } else {
173
- execFileSync('pipx', ['install', 'piper-tts'], { stdio: 'inherit' });
174
- }
175
- spinner.succeed('Piper TTS installed successfully!');
176
- return true;
177
- } catch (error) {
178
- spinner.fail('Failed to install Piper TTS');
179
- console.error(chalk.yellow('\n⚠️ You may need to install pipx first:'));
180
- if (useWSL) {
181
- console.log(chalk.cyan(' wsl sudo apt install pipx'));
182
- } else {
183
- console.log(chalk.cyan(' brew install pipx (macOS)'));
184
- console.log(chalk.cyan(' sudo apt install pipx (Linux)'));
185
- }
186
- return false;
187
- }
188
- }
189
-
190
- /**
191
- * Install Python MCP package
192
- */
193
- async function installMCPPackage(pythonCmd, useWSL = false) {
194
- const spinner = ora('Installing Python MCP package...').start();
195
-
196
- try {
197
- // Security: Use execFileSync with array args to prevent command injection
198
- if (useWSL) {
199
- execFileSync('wsl', [pythonCmd, '-m', 'pip', 'install', '--break-system-packages', 'mcp'], { stdio: 'pipe' });
200
- } else {
201
- execFileSync(pythonCmd, ['-m', 'pip', 'install', '--user', 'mcp'], { stdio: 'pipe' });
202
- }
203
- spinner.succeed('Python MCP package installed successfully!');
204
- return true;
205
- } catch (error) {
206
- spinner.fail('Failed to install Python MCP package');
207
- console.error(chalk.red(`\n❌ Error: ${error.message}`));
208
- return false;
209
- }
210
- }
211
-
212
- /**
213
- * Main installer
214
- */
215
- /**
216
- * Check system dependencies and handle missing ones
217
- * @returns {Promise<void>}
218
- */
219
- async function checkSystemDependencies() {
220
- console.log(chalk.bold('🔍 Step 1: Checking system dependencies...\n'));
221
-
222
- const depResults = checkDependencies();
223
- const hasMissingDeps = displayMissingDependencies(depResults);
224
-
225
- if (!hasMissingDeps) {
226
- console.log(chalk.green('✓ All dependencies installed!\n'));
227
- return;
228
- }
229
-
230
- const hasCoreMissing = depResults.missing.node || depResults.missing.python || depResults.missing.bash;
231
-
232
- if (hasCoreMissing) {
233
- console.log(chalk.red('\n❌ Critical dependencies are missing. Please install them before continuing.\n'));
234
- process.exit(1);
235
- }
236
-
237
- // Only optional dependencies missing
238
- const { continueAnyway } = await inquirer.prompt([{
239
- type: 'confirm',
240
- name: 'continueAnyway',
241
- message: 'Some optional dependencies are missing. Continue anyway?',
242
- default: true
243
- }]);
244
-
245
- if (!continueAnyway) {
246
- console.log(chalk.yellow('\nInstallation cancelled. Please install the dependencies and try again.\n'));
247
- process.exit(0);
248
- }
249
- }
250
-
251
- /**
252
- * Locate AgentVibes installation directory
253
- * @param {boolean} isWindows - Whether running on Windows
254
- * @returns {Promise<string>} AgentVibes directory path
255
- */
256
- async function locateAgentVibesDir(isWindows) {
257
- console.log(chalk.bold('📁 Step 2: Locating AgentVibes installation...\n'));
258
-
259
- let agentVibesDir = getAgentVibesDir();
260
-
261
- if (!agentVibesDir) {
262
- const { customPath } = await inquirer.prompt([{
263
- type: 'input',
264
- name: 'customPath',
265
- message: 'Enter the path to your AgentVibes installation:',
266
- default: isWindows ? 'C:\\Users\\USERNAME\\AgentVibes' : '~/AgentVibes',
267
- validate: (input) => {
268
- const expanded = input.replace(/^~/, os.homedir());
269
- if (fs.existsSync(path.join(expanded, '.claude/hooks/play-tts.sh'))) {
270
- return true;
271
- }
272
- return 'AgentVibes not found at this path. Please check and try again.';
273
- }
274
- }]);
275
-
276
- agentVibesDir = customPath.replace(/^~/, os.homedir());
277
- }
278
-
279
- console.log(chalk.green(`✓ Found AgentVibes at: ${agentVibesDir}\n`));
280
- return agentVibesDir;
281
- }
282
-
283
- /**
284
- * Check and setup WSL on Windows
285
- * @returns {Promise<void>}
286
- */
287
- async function setupWindowsWSL() {
288
- console.log(chalk.bold('🪟 Step 3: Windows environment setup...\n'));
289
-
290
- const hasWSL = checkWSL();
291
-
292
- if (hasWSL) {
293
- console.log(chalk.green('✓ WSL is installed\n'));
294
- return;
295
- }
296
-
297
- console.log(chalk.yellow('⚠️ WSL (Windows Subsystem for Linux) is required but not installed.'));
298
- const { installWSL } = await inquirer.prompt([{
299
- type: 'confirm',
300
- name: 'installWSL',
301
- message: 'Install WSL now? (Requires restart)',
302
- default: true
303
- }]);
304
-
305
- if (!installWSL) {
306
- console.log(chalk.red('\n❌ WSL is required for AgentVibes MCP server on Windows'));
307
- process.exit(1);
308
- }
309
-
310
- console.log(chalk.cyan('\n📦 Installing WSL...'));
311
- try {
312
- // Security: Use execFileSync with array args to prevent command injection
313
- execFileSync('wsl', ['--install'], { stdio: 'inherit' });
314
- console.log(chalk.green('\n✅ WSL installed successfully!'));
315
- console.log(chalk.yellow('⚠️ Please restart your computer and run this installer again.'));
316
- process.exit(0);
317
- } catch (error) {
318
- console.error(chalk.red('\n❌ Failed to install WSL'));
319
- console.error(chalk.yellow('Please install WSL manually: https://aka.ms/wsl'));
320
- process.exit(1);
321
- }
322
- }
323
-
324
- /**
325
- * Select and install TTS provider
326
- * @param {boolean} isWindows - Whether running on Windows
327
- * @returns {Promise<string>} Selected provider name
328
- */
329
- async function setupTTSProvider(isWindows) {
330
- console.log(chalk.bold('🎤 Step 4: Choose TTS provider...\n'));
331
-
332
- const { provider } = await inquirer.prompt([{
333
- type: 'list',
334
- name: 'provider',
335
- message: 'Select your preferred TTS provider:',
336
- choices: [
337
- {
338
- name: 'Piper TTS (Free, Offline, Open Source) - Recommended',
339
- value: 'piper',
340
- short: 'Piper'
341
- },
342
- {
343
- name: 'macOS TTS (Native macOS text-to-speech)',
344
- value: 'macos',
345
- short: 'macOS'
346
- }
347
- ]
348
- }]);
349
-
350
- if (provider === 'piper') {
351
- console.log(chalk.cyan('\n📦 Installing Piper TTS...'));
352
- await installPiper(isWindows);
353
- } else if (provider === 'macos') {
354
- console.log(chalk.cyan('\n✅ macOS TTS uses native system voices - no installation needed'));
355
- }
356
-
357
- return provider;
358
- }
359
-
360
- /**
361
- * Setup Python dependencies
362
- * @param {boolean} isWindows - Whether running on Windows
363
- * @returns {Promise<void>}
364
- */
365
- async function setupPythonDependencies(isWindows) {
366
- console.log(chalk.bold('\n🐍 Step 5: Installing Python dependencies...\n'));
367
-
368
- const pythonCheck = isWindows
369
- ? { available: true, command: 'python3' } // WSL Python
370
- : checkPython();
371
-
372
- if (!pythonCheck.available) {
373
- console.error(chalk.red('❌ Python not found!'));
374
- console.log(chalk.yellow('Please install Python 3.10+ from https://python.org'));
375
- process.exit(1);
376
- }
377
-
378
- console.log(chalk.green(`✓ Python found: ${pythonCheck.version || 'python3'}\n`));
379
-
380
- // Check and install MCP package
381
- const hasMCP = isWindows
382
- ? false // Always install in WSL
383
- : checkMCPPackage(pythonCheck.command);
384
-
385
- if (!hasMCP) {
386
- await installMCPPackage(pythonCheck.command, isWindows);
387
- } else {
388
- console.log(chalk.green('✓ Python MCP package already installed\n'));
389
- }
390
- }
391
-
392
- /**
393
- * Display welcome banner
394
- * @param {string} platform - Platform name
395
- */
396
- function showWelcomeBanner(platform) {
397
- console.log(boxen(
398
- chalk.bold.cyan('AgentVibes MCP Server Installer') + '\n\n' +
399
- 'Give Claude Desktop a voice! 🎤',
400
- {
401
- padding: 1,
402
- margin: 1,
403
- borderStyle: 'round',
404
- borderColor: 'cyan'
405
- }
406
- ));
407
-
408
- const platformLabel = platform === 'win32' ? 'Windows' : platform === 'darwin' ? 'macOS' : 'Linux';
409
- console.log(chalk.gray(`Platform: ${platformLabel}\n`));
410
- }
411
-
412
- /**
413
- * Display success message
414
- * @param {string} configPath - Config file path
415
- * @param {string} provider - Provider name
416
- */
417
- function showSuccessMessage(configPath, provider) {
418
- console.log(boxen(
419
- chalk.bold.green('✅ Installation Complete!') + '\n\n' +
420
- chalk.white('Next steps:\n') +
421
- chalk.cyan('1. Restart Claude Desktop\n') +
422
- chalk.cyan('2. Try: "Say hello using text to speech"\n') +
423
- chalk.cyan('3. Enjoy your talking Claude! 🎤'),
424
- {
425
- padding: 1,
426
- margin: 1,
427
- borderStyle: 'round',
428
- borderColor: 'green'
429
- }
430
- ));
431
-
432
- console.log(chalk.gray('\nConfiguration saved to:'));
433
- console.log(chalk.gray(` ${configPath}\n`));
434
-
435
- if (provider === 'piper') {
436
- console.log(chalk.gray('Voice models will download automatically on first use.\n'));
437
- }
438
- }
439
-
440
- export async function installMCP() {
441
- const platform = os.platform();
442
- const isWindows = platform === 'win32';
443
-
444
- showWelcomeBanner(platform);
445
-
446
- // Step 1: Check system dependencies
447
- await checkSystemDependencies();
448
-
449
- // Step 2: Find AgentVibes directory
450
- const agentVibesDir = await locateAgentVibesDir(isWindows);
451
-
452
- // Step 3: Windows-specific checks
453
- if (isWindows) {
454
- await setupWindowsWSL();
455
- }
456
-
457
- // Step 4: Choose TTS provider
458
- const provider = await setupTTSProvider(isWindows);
459
-
460
- // Step 5: Install Python dependencies
461
- await setupPythonDependencies(isWindows);
462
-
463
- // Step 6: Configure provider in AgentVibes
464
- console.log(chalk.bold('⚙️ Step 6: Configuring AgentVibes...\n'));
465
- const providerFile = path.join(agentVibesDir, '.claude', 'tts-provider.txt');
466
- fs.writeFileSync(providerFile, provider);
467
- console.log(chalk.green(`✓ Set provider to: ${provider}\n`));
468
-
469
- // Step 7: Update Claude Desktop config
470
- console.log(chalk.bold('📝 Step 7: Updating Claude Desktop configuration...\n'));
471
- const configPath = updateClaudeConfig(agentVibesDir, provider, apiKey);
472
- console.log(chalk.green(`✓ Updated: ${configPath}\n`));
473
-
474
- // Success!
475
- showSuccessMessage(configPath, provider);
476
- }
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AgentVibes MCP Server Installer
4
+ *
5
+ * Interactive installer for setting up AgentVibes MCP server with Claude Desktop
6
+ * Handles platform-specific installation (Windows/Mac/Linux)
7
+ */
8
+
9
+ import inquirer from 'inquirer';
10
+ import { execSync, execFileSync } from 'child_process';
11
+ import fs from 'fs';
12
+ import path from 'path';
13
+ import os from 'os';
14
+ import chalk from 'chalk';
15
+ import ora from 'ora';
16
+ import boxen from 'boxen';
17
+ import { checkDependencies, displayMissingDependencies } from '../utils/dependency-checker.js';
18
+
19
+ /**
20
+ * Check if WSL is installed on Windows
21
+ */
22
+ function checkWSL() {
23
+ try {
24
+ // Security: Use execFileSync with array args to prevent command injection
25
+ execFileSync('wsl', ['--version'], { stdio: 'pipe' });
26
+ return true;
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Check if Python is available
34
+ */
35
+ function checkPython() {
36
+ const commands = ['python3', 'python', 'py'];
37
+
38
+ for (const cmd of commands) {
39
+ try {
40
+ // Security: Use execFileSync with array args to prevent command injection
41
+ const version = execFileSync(cmd, ['--version'], { encoding: 'utf8', stdio: 'pipe' });
42
+ return { available: true, command: cmd, version: version.trim() };
43
+ } catch {
44
+ continue;
45
+ }
46
+ }
47
+
48
+ return { available: false };
49
+ }
50
+
51
+ /**
52
+ * Check if Python MCP package is installed
53
+ */
54
+ function checkMCPPackage(pythonCmd) {
55
+ try {
56
+ // Security: Use execFileSync with array args to prevent command injection
57
+ execFileSync(pythonCmd, ['-c', 'import mcp'], { stdio: 'pipe' });
58
+ return true;
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Get Claude Desktop config path for current platform
66
+ */
67
+ function getClaudeConfigPath() {
68
+ const platform = os.platform();
69
+
70
+ switch (platform) {
71
+ case 'darwin': // macOS
72
+ return path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
73
+ case 'win32': // Windows
74
+ return path.join(os.homedir(), 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json');
75
+ default: // Linux
76
+ return path.join(os.homedir(), '.config', 'Claude', 'claude_desktop_config.json');
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Get AgentVibes installation directory
82
+ */
83
+ function getAgentVibesDir() {
84
+ // Try to find AgentVibes directory
85
+ // 1. Current directory
86
+ if (fs.existsSync('./.claude/hooks/play-tts.sh')) {
87
+ return process.cwd();
88
+ }
89
+
90
+ // 2. Parent directory
91
+ const parentDir = path.resolve(process.cwd(), '..');
92
+ if (fs.existsSync(path.join(parentDir, '.claude/hooks/play-tts.sh'))) {
93
+ return parentDir;
94
+ }
95
+
96
+ // 3. Ask user
97
+ return null;
98
+ }
99
+
100
+ /**
101
+ * Update Claude Desktop configuration
102
+ */
103
+ function updateClaudeConfig(agentVibesPath, provider, apiKey = null) {
104
+ const configPath = getClaudeConfigPath();
105
+ const platform = os.platform();
106
+
107
+ // Create config directory if it doesn't exist
108
+ const configDir = path.dirname(configPath);
109
+ if (!fs.existsSync(configDir)) {
110
+ fs.mkdirSync(configDir, { recursive: true });
111
+ }
112
+
113
+ // Read existing config or create new one
114
+ let config = { mcpServers: {} };
115
+ if (fs.existsSync(configPath)) {
116
+ const content = fs.readFileSync(configPath, 'utf8');
117
+ config = JSON.parse(content);
118
+ if (!config.mcpServers) {
119
+ config.mcpServers = {};
120
+ }
121
+ }
122
+
123
+ // Prepare MCP server config
124
+ let serverPath = path.join(agentVibesPath, 'mcp-server', 'server.py');
125
+
126
+ if (platform === 'win32') {
127
+ // Windows: Use WSL
128
+ serverPath = serverPath.replace(/\\/g, '/').replace(/^([A-Z]):/, (match, drive) => {
129
+ return `/mnt/${drive.toLowerCase()}`;
130
+ });
131
+
132
+ config.mcpServers.agentvibes = {
133
+ command: 'wsl',
134
+ args: ['python3', serverPath],
135
+ env: {}
136
+ };
137
+ } else {
138
+ // macOS/Linux: Use native Python
139
+ config.mcpServers.agentvibes = {
140
+ command: 'python3',
141
+ args: [serverPath],
142
+ env: {}
143
+ };
144
+ }
145
+
146
+
147
+ // Write config atomically to prevent race conditions (TOCTOU)
148
+ // Write to temp file first, then rename atomically
149
+ const tempPath = `${configPath}.tmp.${process.pid}`;
150
+ try {
151
+ fs.writeFileSync(tempPath, JSON.stringify(config, null, 2), { mode: 0o600 });
152
+ fs.renameSync(tempPath, configPath);
153
+ } catch (error) {
154
+ // Clean up temp file if rename fails
155
+ try { fs.unlinkSync(tempPath); } catch { /* ignore cleanup errors */ }
156
+ throw error;
157
+ }
158
+
159
+ return configPath;
160
+ }
161
+
162
+ /**
163
+ * Install Piper TTS
164
+ */
165
+ async function installPiper(useWSL = false) {
166
+ const spinner = ora('Installing Piper TTS...').start();
167
+
168
+ try {
169
+ // Security: Use execFileSync with array args to prevent command injection
170
+ if (useWSL) {
171
+ execFileSync('wsl', ['pipx', 'install', 'piper-tts'], { stdio: 'inherit' });
172
+ } else {
173
+ execFileSync('pipx', ['install', 'piper-tts'], { stdio: 'inherit' });
174
+ }
175
+ spinner.succeed('Piper TTS installed successfully!');
176
+ return true;
177
+ } catch (error) {
178
+ spinner.fail('Failed to install Piper TTS');
179
+ console.error(chalk.yellow('\n⚠️ You may need to install pipx first:'));
180
+ if (useWSL) {
181
+ console.log(chalk.cyan(' wsl sudo apt install pipx'));
182
+ } else {
183
+ console.log(chalk.cyan(' brew install pipx (macOS)'));
184
+ console.log(chalk.cyan(' sudo apt install pipx (Linux)'));
185
+ }
186
+ return false;
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Install Python MCP package
192
+ */
193
+ async function installMCPPackage(pythonCmd, useWSL = false) {
194
+ const spinner = ora('Installing Python MCP package...').start();
195
+
196
+ try {
197
+ // Security: Use execFileSync with array args to prevent command injection
198
+ if (useWSL) {
199
+ execFileSync('wsl', [pythonCmd, '-m', 'pip', 'install', '--break-system-packages', 'mcp'], { stdio: 'pipe' });
200
+ } else {
201
+ execFileSync(pythonCmd, ['-m', 'pip', 'install', '--user', 'mcp'], { stdio: 'pipe' });
202
+ }
203
+ spinner.succeed('Python MCP package installed successfully!');
204
+ return true;
205
+ } catch (error) {
206
+ spinner.fail('Failed to install Python MCP package');
207
+ console.error(chalk.red(`\n❌ Error: ${error.message}`));
208
+ return false;
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Main installer
214
+ */
215
+ /**
216
+ * Check system dependencies and handle missing ones
217
+ * @returns {Promise<void>}
218
+ */
219
+ async function checkSystemDependencies() {
220
+ console.log(chalk.bold('🔍 Step 1: Checking system dependencies...\n'));
221
+
222
+ const depResults = checkDependencies();
223
+ const hasMissingDeps = displayMissingDependencies(depResults);
224
+
225
+ if (!hasMissingDeps) {
226
+ console.log(chalk.green('✓ All dependencies installed!\n'));
227
+ return;
228
+ }
229
+
230
+ const hasCoreMissing = depResults.missing.node || depResults.missing.python || depResults.missing.bash;
231
+
232
+ if (hasCoreMissing) {
233
+ console.log(chalk.red('\n❌ Critical dependencies are missing. Please install them before continuing.\n'));
234
+ process.exit(1);
235
+ }
236
+
237
+ // Only optional dependencies missing
238
+ const { continueAnyway } = await inquirer.prompt([{
239
+ type: 'confirm',
240
+ name: 'continueAnyway',
241
+ message: 'Some optional dependencies are missing. Continue anyway?',
242
+ default: true
243
+ }]);
244
+
245
+ if (!continueAnyway) {
246
+ console.log(chalk.yellow('\nInstallation cancelled. Please install the dependencies and try again.\n'));
247
+ process.exit(0);
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Locate AgentVibes installation directory
253
+ * @param {boolean} isWindows - Whether running on Windows
254
+ * @returns {Promise<string>} AgentVibes directory path
255
+ */
256
+ async function locateAgentVibesDir(isWindows) {
257
+ console.log(chalk.bold('📁 Step 2: Locating AgentVibes installation...\n'));
258
+
259
+ let agentVibesDir = getAgentVibesDir();
260
+
261
+ if (!agentVibesDir) {
262
+ const { customPath } = await inquirer.prompt([{
263
+ type: 'input',
264
+ name: 'customPath',
265
+ message: 'Enter the path to your AgentVibes installation:',
266
+ default: isWindows ? 'C:\\Users\\USERNAME\\AgentVibes' : '~/AgentVibes',
267
+ validate: (input) => {
268
+ const expanded = input.replace(/^~/, os.homedir());
269
+ if (fs.existsSync(path.join(expanded, '.claude/hooks/play-tts.sh'))) {
270
+ return true;
271
+ }
272
+ return 'AgentVibes not found at this path. Please check and try again.';
273
+ }
274
+ }]);
275
+
276
+ agentVibesDir = customPath.replace(/^~/, os.homedir());
277
+ }
278
+
279
+ console.log(chalk.green(`✓ Found AgentVibes at: ${agentVibesDir}\n`));
280
+ return agentVibesDir;
281
+ }
282
+
283
+ /**
284
+ * Check and setup WSL on Windows
285
+ * @returns {Promise<void>}
286
+ */
287
+ async function setupWindowsWSL() {
288
+ console.log(chalk.bold('🪟 Step 3: Windows environment setup...\n'));
289
+
290
+ const hasWSL = checkWSL();
291
+
292
+ if (hasWSL) {
293
+ console.log(chalk.green('✓ WSL is installed\n'));
294
+ return;
295
+ }
296
+
297
+ console.log(chalk.yellow('⚠️ WSL (Windows Subsystem for Linux) is required but not installed.'));
298
+ const { installWSL } = await inquirer.prompt([{
299
+ type: 'confirm',
300
+ name: 'installWSL',
301
+ message: 'Install WSL now? (Requires restart)',
302
+ default: true
303
+ }]);
304
+
305
+ if (!installWSL) {
306
+ console.log(chalk.red('\n❌ WSL is required for AgentVibes MCP server on Windows'));
307
+ process.exit(1);
308
+ }
309
+
310
+ console.log(chalk.cyan('\n📦 Installing WSL...'));
311
+ try {
312
+ // Security: Use execFileSync with array args to prevent command injection
313
+ execFileSync('wsl', ['--install'], { stdio: 'inherit' });
314
+ console.log(chalk.green('\n✅ WSL installed successfully!'));
315
+ console.log(chalk.yellow('⚠️ Please restart your computer and run this installer again.'));
316
+ process.exit(0);
317
+ } catch (error) {
318
+ console.error(chalk.red('\n❌ Failed to install WSL'));
319
+ console.error(chalk.yellow('Please install WSL manually: https://aka.ms/wsl'));
320
+ process.exit(1);
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Select and install TTS provider
326
+ * @param {boolean} isWindows - Whether running on Windows
327
+ * @returns {Promise<string>} Selected provider name
328
+ */
329
+ async function setupTTSProvider(isWindows) {
330
+ console.log(chalk.bold('🎤 Step 4: Choose TTS provider...\n'));
331
+
332
+ const { provider } = await inquirer.prompt([{
333
+ type: 'list',
334
+ name: 'provider',
335
+ message: 'Select your preferred TTS provider:',
336
+ choices: [
337
+ {
338
+ name: 'Piper TTS (Free, Offline, Open Source) - Recommended',
339
+ value: 'piper',
340
+ short: 'Piper'
341
+ },
342
+ {
343
+ name: 'macOS TTS (Native macOS text-to-speech)',
344
+ value: 'macos',
345
+ short: 'macOS'
346
+ }
347
+ ]
348
+ }]);
349
+
350
+ if (provider === 'piper') {
351
+ console.log(chalk.cyan('\n📦 Installing Piper TTS...'));
352
+ await installPiper(isWindows);
353
+ } else if (provider === 'macos') {
354
+ console.log(chalk.cyan('\n✅ macOS TTS uses native system voices - no installation needed'));
355
+ }
356
+
357
+ return provider;
358
+ }
359
+
360
+ /**
361
+ * Setup Python dependencies
362
+ * @param {boolean} isWindows - Whether running on Windows
363
+ * @returns {Promise<void>}
364
+ */
365
+ async function setupPythonDependencies(isWindows) {
366
+ console.log(chalk.bold('\n🐍 Step 5: Installing Python dependencies...\n'));
367
+
368
+ const pythonCheck = isWindows
369
+ ? { available: true, command: 'python3' } // WSL Python
370
+ : checkPython();
371
+
372
+ if (!pythonCheck.available) {
373
+ console.error(chalk.red('❌ Python not found!'));
374
+ console.log(chalk.yellow('Please install Python 3.10+ from https://python.org'));
375
+ process.exit(1);
376
+ }
377
+
378
+ console.log(chalk.green(`✓ Python found: ${pythonCheck.version || 'python3'}\n`));
379
+
380
+ // Check and install MCP package
381
+ const hasMCP = isWindows
382
+ ? false // Always install in WSL
383
+ : checkMCPPackage(pythonCheck.command);
384
+
385
+ if (!hasMCP) {
386
+ await installMCPPackage(pythonCheck.command, isWindows);
387
+ } else {
388
+ console.log(chalk.green('✓ Python MCP package already installed\n'));
389
+ }
390
+ }
391
+
392
+ /**
393
+ * Display welcome banner
394
+ * @param {string} platform - Platform name
395
+ */
396
+ function showWelcomeBanner(platform) {
397
+ console.log(boxen(
398
+ chalk.bold.cyan('AgentVibes MCP Server Installer') + '\n\n' +
399
+ 'Give Claude Desktop a voice! 🎤',
400
+ {
401
+ padding: 1,
402
+ margin: 1,
403
+ borderStyle: 'round',
404
+ borderColor: 'cyan'
405
+ }
406
+ ));
407
+
408
+ const platformLabel = platform === 'win32' ? 'Windows' : platform === 'darwin' ? 'macOS' : 'Linux';
409
+ console.log(chalk.gray(`Platform: ${platformLabel}\n`));
410
+ }
411
+
412
+ /**
413
+ * Display success message
414
+ * @param {string} configPath - Config file path
415
+ * @param {string} provider - Provider name
416
+ */
417
+ function showSuccessMessage(configPath, provider) {
418
+ console.log(boxen(
419
+ chalk.bold.green('✅ Installation Complete!') + '\n\n' +
420
+ chalk.white('Next steps:\n') +
421
+ chalk.cyan('1. Restart Claude Desktop\n') +
422
+ chalk.cyan('2. Try: "Say hello using text to speech"\n') +
423
+ chalk.cyan('3. Enjoy your talking Claude! 🎤'),
424
+ {
425
+ padding: 1,
426
+ margin: 1,
427
+ borderStyle: 'round',
428
+ borderColor: 'green'
429
+ }
430
+ ));
431
+
432
+ console.log(chalk.gray('\nConfiguration saved to:'));
433
+ console.log(chalk.gray(` ${configPath}\n`));
434
+
435
+ if (provider === 'piper') {
436
+ console.log(chalk.gray('Voice models will download automatically on first use.\n'));
437
+ }
438
+ }
439
+
440
+ export async function installMCP() {
441
+ const platform = os.platform();
442
+ const isWindows = platform === 'win32';
443
+
444
+ showWelcomeBanner(platform);
445
+
446
+ // Step 1: Check system dependencies
447
+ await checkSystemDependencies();
448
+
449
+ // Step 2: Find AgentVibes directory
450
+ const agentVibesDir = await locateAgentVibesDir(isWindows);
451
+
452
+ // Step 3: Windows-specific checks
453
+ if (isWindows) {
454
+ await setupWindowsWSL();
455
+ }
456
+
457
+ // Step 4: Choose TTS provider
458
+ const provider = await setupTTSProvider(isWindows);
459
+
460
+ // Step 5: Install Python dependencies
461
+ await setupPythonDependencies(isWindows);
462
+
463
+ // Step 6: Configure provider in AgentVibes
464
+ console.log(chalk.bold('⚙️ Step 6: Configuring AgentVibes...\n'));
465
+ const providerFile = path.join(agentVibesDir, '.claude', 'tts-provider.txt');
466
+ fs.writeFileSync(providerFile, provider);
467
+ console.log(chalk.green(`✓ Set provider to: ${provider}\n`));
468
+
469
+ // Step 7: Update Claude Desktop config
470
+ console.log(chalk.bold('📝 Step 7: Updating Claude Desktop configuration...\n'));
471
+ const configPath = updateClaudeConfig(agentVibesDir, provider, apiKey);
472
+ console.log(chalk.green(`✓ Updated: ${configPath}\n`));
473
+
474
+ // Success!
475
+ showSuccessMessage(configPath, provider);
476
+ }