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
|
@@ -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
|
+
}
|