agentvibes 5.2.0 → 5.3.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/.claude/config/audio-effects.cfg +1 -1
- package/.claude/hooks/audio-cache-utils.sh +246 -246
- package/.claude/hooks/background-music-manager.sh +404 -404
- package/.claude/hooks/bmad-speak-enhanced.sh +165 -165
- package/.claude/hooks/bmad-speak.sh +290 -290
- 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-installer.sh +292 -292
- package/.claude/hooks/piper-multispeaker-registry.sh +171 -171
- package/.claude/hooks/play-tts-enhanced.sh +105 -105
- package/.claude/hooks/play-tts-ssh-remote.sh +104 -10
- package/.claude/hooks/play-tts-termux-ssh.sh +169 -169
- package/.claude/hooks/play-tts.sh +31 -11
- 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/sentiment-manager.sh +201 -201
- 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/tts-queue-worker.sh +145 -145
- package/.claude/hooks/tts-queue.sh +165 -165
- package/.claude/hooks/voice-manager.sh +552 -548
- package/.claude/hooks-windows/bmad-party-speak.ps1 +5 -1
- package/.claude/hooks-windows/play-tts.ps1 +91 -59
- package/README.md +21 -2
- package/RELEASE_NOTES.md +130 -0
- package/bin/mcp-server.sh +206 -206
- package/mcp-server/server.py +35 -6
- package/package.json +1 -1
- package/src/console/tabs/setup-tab.js +68 -29
- package/src/console/tabs/voices-tab.js +9 -3
- package/src/installer.js +79 -213
- package/src/services/llm-provider-service.js +139 -75
|
@@ -605,12 +605,19 @@ export function createSetupTab(screen, services) {
|
|
|
605
605
|
}
|
|
606
606
|
});
|
|
607
607
|
btn.key(['down'], () => {
|
|
608
|
+
// Column-preserving down nav. If pressing down from Install/Remove
|
|
609
|
+
// would land on the Default row (which has no Install/Remove — all
|
|
610
|
+
// three slots are configBtn duplicates), don't move. Configure
|
|
611
|
+
// column navigates normally into Default row's Configure.
|
|
612
|
+
const col = providerFocusIndex % 3;
|
|
608
613
|
const nextIdx = providerFocusIndex + 3;
|
|
609
|
-
if (nextIdx
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
+
if (nextIdx >= providerFocusableItems.length) return;
|
|
615
|
+
const nextRowIdx = Math.floor(nextIdx / 3);
|
|
616
|
+
const nextRow = PROVIDERS[nextRowIdx];
|
|
617
|
+
if (col < 2 && nextRow && nextRow.isDefault) return; // skip Default from Install/Remove
|
|
618
|
+
providerFocusIndex = nextIdx;
|
|
619
|
+
providerFocusableItems[providerFocusIndex].focus();
|
|
620
|
+
screen.render();
|
|
614
621
|
});
|
|
615
622
|
}
|
|
616
623
|
|
|
@@ -1271,29 +1278,32 @@ export function createSetupTab(screen, services) {
|
|
|
1271
1278
|
const phrase = SAMPLE_PHRASES[Math.floor(Math.random() * SAMPLE_PHRASES.length)];
|
|
1272
1279
|
|
|
1273
1280
|
// Route through remote provider if active
|
|
1281
|
+
// Search order: CLAUDE_PROJECT_DIR → cwd → package root → home
|
|
1274
1282
|
const _remoteProviders = ['ssh-remote', 'agentvibes-receiver'];
|
|
1275
1283
|
let _activeProvider = '';
|
|
1276
1284
|
try {
|
|
1277
|
-
const
|
|
1285
|
+
const _pkgRoot = path.resolve(__dirname, '..', '..');
|
|
1278
1286
|
const _provPaths = [
|
|
1279
|
-
path.join(
|
|
1287
|
+
process.env.CLAUDE_PROJECT_DIR && path.join(process.env.CLAUDE_PROJECT_DIR, '.claude', 'tts-provider.txt'),
|
|
1288
|
+
path.join(process.cwd(), '.claude', 'tts-provider.txt'),
|
|
1289
|
+
path.join(_pkgRoot, '.claude', 'tts-provider.txt'),
|
|
1280
1290
|
path.join(os.homedir(), '.claude', 'tts-provider.txt'),
|
|
1281
|
-
];
|
|
1291
|
+
].filter(Boolean);
|
|
1282
1292
|
for (const p of _provPaths) {
|
|
1283
1293
|
if (fs.existsSync(p)) { _activeProvider = fs.readFileSync(p, 'utf8').trim(); break; }
|
|
1284
1294
|
}
|
|
1285
1295
|
} catch {}
|
|
1286
1296
|
|
|
1287
1297
|
if (_remoteProviders.includes(_activeProvider)) {
|
|
1288
|
-
const
|
|
1298
|
+
const _hooksBase = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
1289
1299
|
let rProc;
|
|
1290
1300
|
if (_isWin) {
|
|
1291
|
-
const _playTts = path.join(
|
|
1301
|
+
const _playTts = path.join(_hooksBase, '.claude', 'hooks-windows', 'play-tts.ps1');
|
|
1292
1302
|
rProc = spawn('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', _playTts, phrase, voiceId], {
|
|
1293
1303
|
stdio: 'ignore', detached: false, windowsHide: true, env: _spawnEnv,
|
|
1294
1304
|
});
|
|
1295
1305
|
} else {
|
|
1296
|
-
const _playTts = path.join(
|
|
1306
|
+
const _playTts = path.join(_hooksBase, '.claude', 'hooks', 'play-tts.sh');
|
|
1297
1307
|
rProc = spawn('bash', [_playTts, phrase, voiceId], {
|
|
1298
1308
|
stdio: 'ignore', detached: true, env: _spawnEnv,
|
|
1299
1309
|
});
|
|
@@ -1522,7 +1532,6 @@ export function createSetupTab(screen, services) {
|
|
|
1522
1532
|
hideAllProviderRows();
|
|
1523
1533
|
contentBox.hide();
|
|
1524
1534
|
|
|
1525
|
-
const mcpPath = path.join(targetDir, '.mcp.json');
|
|
1526
1535
|
const hooksDir = path.join(targetDir, '.claude', process.platform === 'win32' ? 'hooks-windows' : 'hooks');
|
|
1527
1536
|
const installed = installedState['claude-code'];
|
|
1528
1537
|
const verb = wasInstalled ? 'reinstalled' : 'installed';
|
|
@@ -1532,9 +1541,14 @@ export function createSetupTab(screen, services) {
|
|
|
1532
1541
|
lines.push('');
|
|
1533
1542
|
|
|
1534
1543
|
if (result) {
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1544
|
+
if (result.success) {
|
|
1545
|
+
lines.push(`{green-fg}AgentVibes for Claude Code ${verb}!{/green-fg}`);
|
|
1546
|
+
if (result.mcpError) {
|
|
1547
|
+
lines.push(`{yellow-fg}Warning:{/yellow-fg} ${result.mcpError}`);
|
|
1548
|
+
}
|
|
1549
|
+
} else {
|
|
1550
|
+
lines.push(`{red-fg}Installation failed:{/red-fg} ${result.error || 'Unknown error'}`);
|
|
1551
|
+
}
|
|
1538
1552
|
} else {
|
|
1539
1553
|
lines.push(installed
|
|
1540
1554
|
? '{green-fg}Installed{/green-fg}'
|
|
@@ -1543,10 +1557,7 @@ export function createSetupTab(screen, services) {
|
|
|
1543
1557
|
|
|
1544
1558
|
lines.push('');
|
|
1545
1559
|
lines.push(`{bold}{cyan-fg}What ${result ? `got ${verb}` : 'gets installed'}:{/cyan-fg}{/bold}`);
|
|
1546
|
-
lines.push('');
|
|
1547
|
-
lines.push(' {yellow-fg}1.{/yellow-fg} {bold}.mcp.json{/bold} (project root)');
|
|
1548
|
-
lines.push(` Location: ${mcpPath}`);
|
|
1549
|
-
lines.push(' Registers the AgentVibes MCP server for Claude Code.');
|
|
1560
|
+
lines.push(' {yellow-fg}1.{/yellow-fg} {bold}.mcp.json{/bold} (MCP server — natural language voice control)');
|
|
1550
1561
|
lines.push('');
|
|
1551
1562
|
lines.push(' {yellow-fg}2.{/yellow-fg} {bold}.claude/hooks/{/bold} (session-start + pre-tool hooks)');
|
|
1552
1563
|
lines.push(` Location: ${hooksDir}`);
|
|
@@ -1575,9 +1586,14 @@ export function createSetupTab(screen, services) {
|
|
|
1575
1586
|
const lines = [];
|
|
1576
1587
|
lines.push('{bold}{cyan-fg}GitHub Copilot -- AgentVibes Integration{/cyan-fg}{/bold}');
|
|
1577
1588
|
lines.push('');
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1589
|
+
if (result.success) {
|
|
1590
|
+
lines.push(`{green-fg}AgentVibes for Copilot ${verb}!{/green-fg}`);
|
|
1591
|
+
if (result.mcpError) {
|
|
1592
|
+
lines.push(`{yellow-fg}MCP config failed:{/yellow-fg} ${result.mcpError}`);
|
|
1593
|
+
}
|
|
1594
|
+
} else {
|
|
1595
|
+
lines.push(`{red-fg}Installation failed:{/red-fg} ${result.error || 'Unknown error'}`);
|
|
1596
|
+
}
|
|
1581
1597
|
lines.push('');
|
|
1582
1598
|
lines.push(`{bold}{cyan-fg}What got ${verb}:{/cyan-fg}{/bold}`);
|
|
1583
1599
|
lines.push('');
|
|
@@ -1604,9 +1620,14 @@ export function createSetupTab(screen, services) {
|
|
|
1604
1620
|
const lines = [];
|
|
1605
1621
|
lines.push('{bold}{cyan-fg}OpenAI Codex -- AgentVibes Integration{/cyan-fg}{/bold}');
|
|
1606
1622
|
lines.push('');
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1623
|
+
if (result.success) {
|
|
1624
|
+
lines.push(`{green-fg}AgentVibes for Codex ${verb}!{/green-fg}`);
|
|
1625
|
+
if (result.mcpError) {
|
|
1626
|
+
lines.push(`{yellow-fg}MCP config failed:{/yellow-fg} ${result.mcpError}`);
|
|
1627
|
+
}
|
|
1628
|
+
} else {
|
|
1629
|
+
lines.push(`{red-fg}Installation failed:{/red-fg} ${result.error || 'Unknown error'}`);
|
|
1630
|
+
}
|
|
1610
1631
|
lines.push('');
|
|
1611
1632
|
lines.push(`{bold}{cyan-fg}What got ${verb}:{/cyan-fg}{/bold}`);
|
|
1612
1633
|
lines.push('');
|
|
@@ -1679,11 +1700,29 @@ export function createSetupTab(screen, services) {
|
|
|
1679
1700
|
|
|
1680
1701
|
infoBox.key(['escape', 'enter'], () => {
|
|
1681
1702
|
// After dismissing the install/remove info page, advance focus to the
|
|
1682
|
-
// NEXT provider row but keep the same column (Install
|
|
1683
|
-
//
|
|
1684
|
-
//
|
|
1703
|
+
// NEXT provider row but keep the same column (Install/Remove/Configure).
|
|
1704
|
+
// Each row has 3 focusable slots, so +3 moves one full row down.
|
|
1705
|
+
//
|
|
1706
|
+
// Special case: when leaving the LAST installable provider (Codex) from
|
|
1707
|
+
// Install or Remove column, skip the Default row (it has no Install or
|
|
1708
|
+
// Remove) and wrap to the FIRST Configure button (Claude Code Configure).
|
|
1709
|
+
// This lets the user cleanly walk all three installs, then all three
|
|
1710
|
+
// Configures, ending on Default Configure.
|
|
1685
1711
|
const max = providerFocusableItems.length;
|
|
1686
|
-
|
|
1712
|
+
if (max === 0) { showProviderListView(0); return; }
|
|
1713
|
+
const col = _preInfoFocusIndex % 3; // 0=Install, 1=Remove, 2=Configure
|
|
1714
|
+
const row = Math.floor(_preInfoFocusIndex / 3);
|
|
1715
|
+
const nextRow = PROVIDERS[row + 1];
|
|
1716
|
+
const nextRowIsDefault = nextRow && nextRow.isDefault;
|
|
1717
|
+
let nextIdx;
|
|
1718
|
+
if (col < 2 && nextRowIsDefault) {
|
|
1719
|
+
// Last Install/Remove → jump to the FIRST non-default provider's
|
|
1720
|
+
// Configure column (dynamic: don't hardcode PROVIDERS[0]).
|
|
1721
|
+
const firstInstallableIdx = PROVIDERS.findIndex(p => !p.isDefault);
|
|
1722
|
+
nextIdx = firstInstallableIdx >= 0 ? firstInstallableIdx * 3 + 2 : (_preInfoFocusIndex + 3) % max;
|
|
1723
|
+
} else {
|
|
1724
|
+
nextIdx = (_preInfoFocusIndex + 3) % max;
|
|
1725
|
+
}
|
|
1687
1726
|
showProviderListView(nextIdx);
|
|
1688
1727
|
});
|
|
1689
1728
|
|
|
@@ -868,14 +868,17 @@ export function createVoicesTab(screen, services) {
|
|
|
868
868
|
_playingVoiceId = null;
|
|
869
869
|
|
|
870
870
|
// Check if we should route through remote provider (ssh-remote / agentvibes-receiver)
|
|
871
|
+
// Search order: CLAUDE_PROJECT_DIR (actual project) → cwd → package root → home
|
|
871
872
|
const projectRoot = path.resolve(__dirname, '..', '..');
|
|
872
873
|
const remoteProviders = ['ssh-remote', 'agentvibes-receiver'];
|
|
873
874
|
let activeProvider = '';
|
|
874
875
|
try {
|
|
875
876
|
const providerPaths = [
|
|
877
|
+
process.env.CLAUDE_PROJECT_DIR && path.join(process.env.CLAUDE_PROJECT_DIR, '.claude', 'tts-provider.txt'),
|
|
878
|
+
path.join(process.cwd(), '.claude', 'tts-provider.txt'),
|
|
876
879
|
path.join(projectRoot, '.claude', 'tts-provider.txt'),
|
|
877
880
|
path.join(os.homedir(), '.claude', 'tts-provider.txt'),
|
|
878
|
-
];
|
|
881
|
+
].filter(Boolean);
|
|
879
882
|
for (const p of providerPaths) {
|
|
880
883
|
if (fs.existsSync(p)) { activeProvider = fs.readFileSync(p, 'utf8').trim(); break; }
|
|
881
884
|
}
|
|
@@ -884,14 +887,17 @@ export function createVoicesTab(screen, services) {
|
|
|
884
887
|
if (remoteProviders.includes(activeProvider)) {
|
|
885
888
|
const isWindows = process.platform === 'win32' && !process.env.WSL_DISTRO_NAME;
|
|
886
889
|
const phrase = SAMPLE_PHRASES[Math.floor(Math.random() * SAMPLE_PHRASES.length)];
|
|
890
|
+
// Resolve play-tts from the actual project (CLAUDE_PROJECT_DIR / cwd),
|
|
891
|
+
// not the npm package root — hooks live in the user's project dir.
|
|
892
|
+
const hooksBase = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
887
893
|
let proc;
|
|
888
894
|
if (isWindows) {
|
|
889
|
-
const playTts = path.join(
|
|
895
|
+
const playTts = path.join(hooksBase, '.claude', 'hooks-windows', 'play-tts.ps1');
|
|
890
896
|
proc = spawn('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', playTts, phrase, voiceId], {
|
|
891
897
|
stdio: 'ignore', detached: false, windowsHide: true, env: _spawnEnv,
|
|
892
898
|
});
|
|
893
899
|
} else {
|
|
894
|
-
const playTts = path.join(
|
|
900
|
+
const playTts = path.join(hooksBase, '.claude', 'hooks', 'play-tts.sh');
|
|
895
901
|
proc = spawn('bash', [playTts, phrase, voiceId], {
|
|
896
902
|
stdio: 'ignore', detached: true, env: _spawnEnv,
|
|
897
903
|
});
|
package/src/installer.js
CHANGED
|
@@ -3771,18 +3771,31 @@ async function copyConfigFiles(targetDir, spinner) {
|
|
|
3771
3771
|
const stat = await fs.stat(srcPath);
|
|
3772
3772
|
|
|
3773
3773
|
if (stat.isFile()) {
|
|
3774
|
-
//
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3774
|
+
// For .sample files: copy as the real config name if it doesn't exist yet
|
|
3775
|
+
// e.g. audio-effects.cfg.sample → audio-effects.cfg (only if absent)
|
|
3776
|
+
let finalDest = destPath;
|
|
3777
|
+
let finalName = file;
|
|
3778
|
+
if (file.endsWith('.sample')) {
|
|
3779
|
+
finalName = file.replace(/\.sample$/, '');
|
|
3780
|
+
finalDest = path.join(destConfigDir, finalName);
|
|
3781
|
+
try {
|
|
3782
|
+
await fs.access(finalDest);
|
|
3783
|
+
continue; // Real config already exists, don't overwrite
|
|
3784
|
+
} catch {
|
|
3785
|
+
// Real config doesn't exist, install from sample
|
|
3786
|
+
}
|
|
3787
|
+
} else {
|
|
3788
|
+
// Non-sample files: skip if already exists
|
|
3789
|
+
try {
|
|
3790
|
+
await fs.access(destPath);
|
|
3791
|
+
continue;
|
|
3792
|
+
} catch {
|
|
3793
|
+
// File doesn't exist, proceed with copy
|
|
3779
3794
|
}
|
|
3780
|
-
} catch {
|
|
3781
|
-
// File doesn't exist, proceed with copy
|
|
3782
3795
|
}
|
|
3783
3796
|
|
|
3784
|
-
await fs.copyFile(srcPath,
|
|
3785
|
-
copiedFiles.push(
|
|
3797
|
+
await fs.copyFile(srcPath, finalDest);
|
|
3798
|
+
copiedFiles.push(finalName);
|
|
3786
3799
|
}
|
|
3787
3800
|
}
|
|
3788
3801
|
|
|
@@ -4297,239 +4310,92 @@ function isPathSafe(targetPath, basePath) {
|
|
|
4297
4310
|
async function handleMcpConfiguration(targetDir, options) {
|
|
4298
4311
|
const mcpConfigPath = path.join(targetDir, '.mcp.json');
|
|
4299
4312
|
|
|
4300
|
-
// MCP server
|
|
4313
|
+
// .mcp.json registers the AgentVibes MCP server for Claude Code, enabling
|
|
4314
|
+
// natural language control (text_to_speech, get_config, set_voice, etc.).
|
|
4301
4315
|
//
|
|
4302
|
-
//
|
|
4303
|
-
//
|
|
4304
|
-
//
|
|
4305
|
-
//
|
|
4306
|
-
// env var that Claude Code sets on every subprocess it spawns.
|
|
4316
|
+
// AGENTVIBES_MCP_FALLBACK=copilot is the identity for non-Claude-Code tools
|
|
4317
|
+
// that read .mcp.json (primarily VS Code Copilot, which reads .mcp.json
|
|
4318
|
+
// with precedence over its own .vscode/mcp.json). Claude Code is
|
|
4319
|
+
// auto-detected via CLAUDECODE=1 which takes priority over the fallback.
|
|
4307
4320
|
const mcpConfig = {
|
|
4308
4321
|
mcpServers: {
|
|
4309
4322
|
agentvibes: {
|
|
4310
4323
|
command: 'npx',
|
|
4311
|
-
args: ['-y', '--package=agentvibes', 'agentvibes-mcp-server']
|
|
4324
|
+
args: ['-y', '--package=agentvibes', 'agentvibes-mcp-server'],
|
|
4325
|
+
env: { AGENTVIBES_MCP_FALLBACK: 'copilot' }
|
|
4312
4326
|
}
|
|
4313
4327
|
}
|
|
4314
4328
|
};
|
|
4315
4329
|
|
|
4316
|
-
// Check if .mcp.json already exists
|
|
4317
4330
|
let mcpExists = false;
|
|
4318
4331
|
try {
|
|
4319
4332
|
await fs.access(mcpConfigPath);
|
|
4320
4333
|
mcpExists = true;
|
|
4321
|
-
} catch {
|
|
4322
|
-
// File doesn't exist
|
|
4323
|
-
}
|
|
4334
|
+
} catch { /* doesn't exist */ }
|
|
4324
4335
|
|
|
4325
4336
|
if (mcpExists) {
|
|
4326
|
-
//
|
|
4327
|
-
|
|
4328
|
-
// 2. STRIP any stale `env.AGENTVIBES_LLM` from earlier versions
|
|
4329
|
-
// (v5.1.2..v5.1.4) — setting it in `.mcp.json` broke Copilot CLI
|
|
4330
|
-
// routing because Copilot CLI also reads `.mcp.json` and would
|
|
4331
|
-
// adopt claude-code's env. Claude Code is now auto-detected
|
|
4332
|
-
// downstream via the CLAUDECODE=1 env var.
|
|
4333
|
-
let migrated = false;
|
|
4334
|
-
let migrationError = null;
|
|
4337
|
+
// Upgrade: ensure agentvibes entry exists with fallback env
|
|
4338
|
+
let parseFailed = false;
|
|
4335
4339
|
try {
|
|
4336
|
-
const
|
|
4337
|
-
|
|
4338
|
-
if (
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
// drop AGENTVIBES_LLM so Copilot CLI doesn't mis-route.
|
|
4348
|
-
const cleanEnv = { ...(current?.env ?? {}) };
|
|
4349
|
-
delete cleanEnv.AGENTVIBES_LLM;
|
|
4350
|
-
const newEntry = {
|
|
4351
|
-
command: 'npx',
|
|
4352
|
-
args: ['-y', '--package=agentvibes', 'agentvibes-mcp-server'],
|
|
4353
|
-
};
|
|
4354
|
-
if (Object.keys(cleanEnv).length > 0) {
|
|
4355
|
-
newEntry.env = cleanEnv;
|
|
4356
|
-
}
|
|
4357
|
-
existingCfg.mcpServers.agentvibes = newEntry;
|
|
4358
|
-
await fs.writeFile(mcpConfigPath, JSON.stringify(existingCfg, null, 2) + '\n');
|
|
4359
|
-
migrated = true;
|
|
4360
|
-
}
|
|
4340
|
+
const existing = JSON.parse(await fs.readFile(mcpConfigPath, 'utf8'));
|
|
4341
|
+
// Guard: non-object root (arrays/primitives are valid JSON but wrong shape)
|
|
4342
|
+
if (!existing || typeof existing !== 'object' || Array.isArray(existing)) {
|
|
4343
|
+
console.log(chalk.yellow(
|
|
4344
|
+
`⚠️ ${mcpConfigPath} has a non-object root — skipping MCP registration. Fix the file manually and re-run.`
|
|
4345
|
+
));
|
|
4346
|
+
return;
|
|
4347
|
+
}
|
|
4348
|
+
// Guard: mcpServers must be a plain object
|
|
4349
|
+
if (!existing.mcpServers || typeof existing.mcpServers !== 'object' || Array.isArray(existing.mcpServers)) {
|
|
4350
|
+
existing.mcpServers = {};
|
|
4361
4351
|
}
|
|
4352
|
+
const current = existing.mcpServers.agentvibes;
|
|
4353
|
+
// Strip AGENTVIBES_LLM if present (causes identity collisions)
|
|
4354
|
+
if (current?.env?.AGENTVIBES_LLM) {
|
|
4355
|
+
delete current.env.AGENTVIBES_LLM;
|
|
4356
|
+
}
|
|
4357
|
+
// Ensure fallback is set
|
|
4358
|
+
const mergedEnv = { ...(current?.env ?? {}), AGENTVIBES_MCP_FALLBACK: 'copilot' };
|
|
4359
|
+
existing.mcpServers.agentvibes = {
|
|
4360
|
+
command: 'npx',
|
|
4361
|
+
args: ['-y', '--package=agentvibes', 'agentvibes-mcp-server'],
|
|
4362
|
+
env: mergedEnv,
|
|
4363
|
+
};
|
|
4364
|
+
await fs.writeFile(mcpConfigPath, JSON.stringify(existing, null, 2) + '\n');
|
|
4362
4365
|
} catch (err) {
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
boxen(
|
|
4369
|
-
chalk.green.bold('✅ MCP Configuration Updated\n\n') +
|
|
4370
|
-
chalk.white('Your existing ') + chalk.cyan('.mcp.json') + chalk.white(' has been updated.\n') +
|
|
4371
|
-
chalk.white('Claude Code is auto-detected via ') + chalk.cyan('CLAUDECODE=1') + chalk.white(' at runtime.'),
|
|
4372
|
-
{
|
|
4373
|
-
padding: 1,
|
|
4374
|
-
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
4375
|
-
borderStyle: 'double',
|
|
4376
|
-
borderColor: 'green',
|
|
4377
|
-
}
|
|
4378
|
-
)
|
|
4379
|
-
);
|
|
4380
|
-
return;
|
|
4366
|
+
parseFailed = true;
|
|
4367
|
+
console.log(chalk.yellow(
|
|
4368
|
+
`⚠️ Could not update ${mcpConfigPath}: ${err.message}\n` +
|
|
4369
|
+
` AgentVibes MCP server was NOT registered. Fix the file manually and re-run.`
|
|
4370
|
+
));
|
|
4381
4371
|
}
|
|
4382
|
-
|
|
4383
|
-
// Migration was not needed (already correct) or failed — fall through
|
|
4384
|
-
// to the manual-instructions box.
|
|
4385
|
-
console.log(
|
|
4386
|
-
boxen(
|
|
4387
|
-
chalk.yellow.bold('ℹ️ MCP Configuration Already Exists\n\n') +
|
|
4388
|
-
chalk.white('An ') + chalk.cyan('.mcp.json') + chalk.white(' file already exists in this project.\n\n') +
|
|
4389
|
-
(migrationError
|
|
4390
|
-
? chalk.red('Could not auto-update it: ' + migrationError.message + '\n\n')
|
|
4391
|
-
: chalk.gray('It already has the correct AgentVibes entry.\n\n')) +
|
|
4392
|
-
chalk.white('To add or fix the AgentVibes MCP server manually, use:'),
|
|
4393
|
-
{
|
|
4394
|
-
padding: 1,
|
|
4395
|
-
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
4396
|
-
borderStyle: 'round',
|
|
4397
|
-
borderColor: migrationError ? 'red' : 'yellow',
|
|
4398
|
-
}
|
|
4399
|
-
)
|
|
4400
|
-
);
|
|
4401
|
-
|
|
4402
|
-
// Display the snippet to add
|
|
4403
|
-
console.log(
|
|
4404
|
-
'\n"agentvibes": {\n' +
|
|
4405
|
-
' "command": "npx",\n' +
|
|
4406
|
-
' "args": ["-y", "--package=agentvibes", "agentvibes-mcp-server"]\n' +
|
|
4407
|
-
'}\n'
|
|
4408
|
-
);
|
|
4409
|
-
|
|
4410
|
-
console.log(
|
|
4411
|
-
boxen(
|
|
4412
|
-
chalk.cyan('To use with Claude Code:\n') +
|
|
4413
|
-
chalk.white(' claude --mcp-config .mcp.json\n\n') +
|
|
4414
|
-
chalk.cyan('📖 Full Guide:\n') +
|
|
4415
|
-
chalk.cyan.bold('https://github.com/paulpreibisch/AgentVibes#mcp-server'),
|
|
4416
|
-
{
|
|
4417
|
-
padding: 1,
|
|
4418
|
-
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
4419
|
-
borderStyle: 'round',
|
|
4420
|
-
borderColor: 'cyan',
|
|
4421
|
-
}
|
|
4422
|
-
)
|
|
4423
|
-
);
|
|
4372
|
+
if (!parseFailed) return;
|
|
4424
4373
|
return;
|
|
4425
4374
|
}
|
|
4426
4375
|
|
|
4427
|
-
//
|
|
4428
|
-
console.log(
|
|
4429
|
-
boxen(
|
|
4430
|
-
chalk.cyan.bold('🎙️ MCP Server Configuration\n\n') +
|
|
4431
|
-
chalk.white.bold('AgentVibes MCP Server - Control TTS with Natural Language!\n\n') +
|
|
4432
|
-
chalk.gray('Use natural language instead of slash commands:\n') +
|
|
4433
|
-
chalk.gray(' "Switch to Aria voice" instead of /agent-vibes:switch "Aria"\n') +
|
|
4434
|
-
chalk.gray(' "Set personality to sarcastic" instead of /agent-vibes:personality sarcastic\n\n') +
|
|
4435
|
-
chalk.white('No ') + chalk.cyan('.mcp.json') + chalk.white(' found in this project.'),
|
|
4436
|
-
{
|
|
4437
|
-
padding: 1,
|
|
4438
|
-
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
4439
|
-
borderStyle: 'round',
|
|
4440
|
-
borderColor: 'cyan',
|
|
4441
|
-
}
|
|
4442
|
-
)
|
|
4443
|
-
);
|
|
4444
|
-
|
|
4445
|
-
let createConfig = options.yes; // Auto-create if --yes flag
|
|
4446
|
-
|
|
4376
|
+
// New install — create .mcp.json
|
|
4447
4377
|
if (!options.yes) {
|
|
4448
|
-
const { confirmCreate } = await inquirer.prompt([
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
]);
|
|
4456
|
-
createConfig = confirmCreate;
|
|
4457
|
-
}
|
|
4458
|
-
|
|
4459
|
-
if (createConfig) {
|
|
4460
|
-
// Scenario 1: User says YES - create the config
|
|
4461
|
-
try {
|
|
4462
|
-
await fs.writeFile(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + '\n');
|
|
4463
|
-
|
|
4464
|
-
console.log(
|
|
4465
|
-
boxen(
|
|
4466
|
-
chalk.green.bold('✅ MCP Configuration Created!\n\n') +
|
|
4467
|
-
chalk.white('Your ') + chalk.cyan('.mcp.json') + chalk.white(' has been created in this project.\n\n') +
|
|
4468
|
-
chalk.white('To use AgentVibes MCP server with Claude, run:\n') +
|
|
4469
|
-
chalk.cyan.bold(' claude --mcp-config .mcp.json\n\n') +
|
|
4470
|
-
chalk.green('The MCP server is now installed and ready to use!'),
|
|
4471
|
-
{
|
|
4472
|
-
padding: 1,
|
|
4473
|
-
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
4474
|
-
borderStyle: 'double',
|
|
4475
|
-
borderColor: 'green',
|
|
4476
|
-
}
|
|
4477
|
-
)
|
|
4478
|
-
);
|
|
4479
|
-
|
|
4480
|
-
// Show the installed JSON so users can see exactly what was written
|
|
4481
|
-
console.log(chalk.gray(JSON.stringify(mcpConfig, null, 2)) + '\n');
|
|
4482
|
-
} catch (error) {
|
|
4483
|
-
console.log(chalk.red(`\n✗ Failed to create .mcp.json: ${error.message}`));
|
|
4484
|
-
console.log(chalk.gray(' You can create it manually with the config shown below.\n'));
|
|
4485
|
-
// Fall through to show manual instructions
|
|
4486
|
-
createConfig = false;
|
|
4487
|
-
}
|
|
4378
|
+
const { confirmCreate } = await inquirer.prompt([{
|
|
4379
|
+
type: 'confirm',
|
|
4380
|
+
name: 'confirmCreate',
|
|
4381
|
+
message: chalk.cyan('Create .mcp.json for AgentVibes MCP server? (enables natural language voice control)'),
|
|
4382
|
+
default: true,
|
|
4383
|
+
}]);
|
|
4384
|
+
if (!confirmCreate) return;
|
|
4488
4385
|
}
|
|
4489
4386
|
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
console.log(
|
|
4493
|
-
boxen(
|
|
4494
|
-
chalk.cyan.bold('📋 Manual MCP Configuration\n\n') +
|
|
4495
|
-
chalk.white('Create a ') + chalk.cyan('.mcp.json') + chalk.white(' file in your project with:'),
|
|
4496
|
-
{
|
|
4497
|
-
padding: 1,
|
|
4498
|
-
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
4499
|
-
borderStyle: 'round',
|
|
4500
|
-
borderColor: 'cyan',
|
|
4501
|
-
}
|
|
4502
|
-
)
|
|
4503
|
-
);
|
|
4504
|
-
|
|
4505
|
-
// Display JSON config
|
|
4506
|
-
console.log(
|
|
4507
|
-
'\n{\n' +
|
|
4508
|
-
' "mcpServers": {\n' +
|
|
4509
|
-
' "agentvibes": {\n' +
|
|
4510
|
-
' "command": "npx",\n' +
|
|
4511
|
-
' "args": ["-y", "--package=agentvibes", "agentvibes-mcp-server"]\n' +
|
|
4512
|
-
' }\n' +
|
|
4513
|
-
' }\n' +
|
|
4514
|
-
'}\n'
|
|
4515
|
-
);
|
|
4516
|
-
|
|
4387
|
+
try {
|
|
4388
|
+
await fs.writeFile(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + '\n');
|
|
4517
4389
|
console.log(
|
|
4518
4390
|
boxen(
|
|
4519
|
-
chalk.
|
|
4520
|
-
chalk.white('
|
|
4521
|
-
chalk.
|
|
4522
|
-
|
|
4523
|
-
chalk.cyan('📖 Full Guide:\n') +
|
|
4524
|
-
chalk.cyan.bold('https://github.com/paulpreibisch/AgentVibes#mcp-server'),
|
|
4525
|
-
{
|
|
4526
|
-
padding: 1,
|
|
4527
|
-
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
4528
|
-
borderStyle: 'round',
|
|
4529
|
-
borderColor: 'cyan',
|
|
4530
|
-
}
|
|
4391
|
+
chalk.green.bold('✅ MCP Configuration Created!\n\n') +
|
|
4392
|
+
chalk.white('AgentVibes MCP server registered in ') + chalk.cyan('.mcp.json') + chalk.white('.\n') +
|
|
4393
|
+
chalk.green('Natural language voice control is ready!'),
|
|
4394
|
+
{ padding: 1, margin: { top: 1, bottom: 1 }, borderStyle: 'double', borderColor: 'green' }
|
|
4531
4395
|
)
|
|
4532
4396
|
);
|
|
4397
|
+
} catch (err) {
|
|
4398
|
+
console.log(chalk.red(`\n✗ Failed to create .mcp.json: ${err.message}`));
|
|
4533
4399
|
}
|
|
4534
4400
|
}
|
|
4535
4401
|
|