agentvibes 5.2.0 → 5.2.1
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-termux-ssh.sh +169 -169
- package/.claude/hooks/play-tts.sh +14 -5
- 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/play-tts.ps1 +2 -2
- package/README.md +11 -2
- package/RELEASE_NOTES.md +38 -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 +59 -23
- package/src/installer.js +79 -213
- package/src/services/llm-provider-service.js +126 -75
|
@@ -125,53 +125,78 @@ export async function checkCodexInstalled(targetDir) {
|
|
|
125
125
|
// ── Claude Code install ────────────────────────────────────────────────────
|
|
126
126
|
|
|
127
127
|
/**
|
|
128
|
-
*
|
|
128
|
+
* Install AgentVibes for Claude Code.
|
|
129
|
+
*
|
|
130
|
+
* Writes .mcp.json to register the AgentVibes MCP server (enables natural
|
|
131
|
+
* language control: text_to_speech, get_config, set_voice, etc.).
|
|
132
|
+
*
|
|
133
|
+
* .mcp.json does NOT set AGENTVIBES_LLM because Copilot also reads it.
|
|
134
|
+
* Claude Code is auto-detected via CLAUDECODE=1 env var at runtime.
|
|
135
|
+
*
|
|
129
136
|
* Also copies hooks, commands, config, personality, plugin, and bmad config files.
|
|
130
137
|
*/
|
|
131
138
|
export async function installClaudeMcp(targetDir) {
|
|
132
139
|
const mcpConfigPath = path.join(targetDir, '.mcp.json');
|
|
133
140
|
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
// `~/.copilot/mcp-config.json` — so if we set `AGENTVIBES_LLM=claude-code`
|
|
139
|
-
// in `.mcp.json`, Copilot CLI picks up that value too and mis-routes.
|
|
140
|
-
// Instead, the MCP server (mcp-server/server.py) auto-detects Claude
|
|
141
|
-
// Code via the `CLAUDECODE=1` env var that Claude Code sets on every
|
|
142
|
-
// subprocess it spawns. Copilot CLI does NOT set that var, so its
|
|
143
|
-
// spawned MCP server correctly falls back to its own config.
|
|
141
|
+
// AGENTVIBES_MCP_FALLBACK=copilot is the fallback identity for non-Claude-Code
|
|
142
|
+
// tools reading .mcp.json (primarily VS Code Copilot, which reads .mcp.json
|
|
143
|
+
// with precedence over .vscode/mcp.json). Claude Code auto-detects via
|
|
144
|
+
// CLAUDECODE=1 which takes priority over the fallback in server.py.
|
|
144
145
|
const agentvibesServer = {
|
|
145
146
|
command: 'npx',
|
|
146
147
|
args: ['-y', '--package=agentvibes', 'agentvibes-mcp-server'],
|
|
148
|
+
env: { AGENTVIBES_MCP_FALLBACK: 'copilot' },
|
|
147
149
|
};
|
|
148
150
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
};
|
|
154
|
-
|
|
151
|
+
// MCP config and file copies are independent — report partial success
|
|
152
|
+
// when one succeeds but the other fails.
|
|
153
|
+
let mcpCreated = false;
|
|
154
|
+
let mcpError = null;
|
|
155
155
|
try {
|
|
156
|
-
let
|
|
156
|
+
let existing = null;
|
|
157
|
+
let parseError = null;
|
|
157
158
|
try {
|
|
158
|
-
await fs.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
159
|
+
const raw = await fs.readFile(mcpConfigPath, 'utf8');
|
|
160
|
+
existing = JSON.parse(raw);
|
|
161
|
+
} catch (err) {
|
|
162
|
+
// ENOENT = new file (fine); anything else = malformed (report to caller)
|
|
163
|
+
if (err.code !== 'ENOENT') parseError = err;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (parseError) {
|
|
167
|
+
throw new Error(`Existing ${mcpConfigPath} is malformed: ${parseError.message}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Guard: non-object root
|
|
171
|
+
if (existing && (typeof existing !== 'object' || Array.isArray(existing))) {
|
|
172
|
+
throw new Error(`${mcpConfigPath} has a non-object root — please fix manually.`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (existing) {
|
|
176
|
+
// Guard: mcpServers must be a plain object
|
|
177
|
+
if (!existing.mcpServers || typeof existing.mcpServers !== 'object' || Array.isArray(existing.mcpServers)) {
|
|
178
|
+
existing.mcpServers = {};
|
|
179
|
+
}
|
|
180
|
+
const current = existing.mcpServers.agentvibes;
|
|
181
|
+
// Strip any stale AGENTVIBES_LLM (from older versions — causes collisions)
|
|
182
|
+
if (current?.env?.AGENTVIBES_LLM) delete current.env.AGENTVIBES_LLM;
|
|
183
|
+
// Preserve user's other env keys, ensure AGENTVIBES_MCP_FALLBACK is set
|
|
184
|
+
const mergedEnv = { ...(current?.env ?? {}), AGENTVIBES_MCP_FALLBACK: 'copilot' };
|
|
185
|
+
existing.mcpServers.agentvibes = {
|
|
186
|
+
command: 'npx',
|
|
187
|
+
args: ['-y', '--package=agentvibes', 'agentvibes-mcp-server'],
|
|
188
|
+
env: mergedEnv,
|
|
189
|
+
};
|
|
190
|
+
await fs.writeFile(mcpConfigPath, JSON.stringify(existing, null, 2) + '\n');
|
|
191
|
+
} else {
|
|
192
|
+
await fs.writeFile(mcpConfigPath, JSON.stringify({ mcpServers: { agentvibes: agentvibesServer } }, null, 2) + '\n');
|
|
173
193
|
}
|
|
194
|
+
mcpCreated = true;
|
|
195
|
+
} catch (err) {
|
|
196
|
+
mcpError = err.message;
|
|
197
|
+
}
|
|
174
198
|
|
|
199
|
+
try {
|
|
175
200
|
// Copy hooks, commands, config, personality, plugin, bmad config files
|
|
176
201
|
const silentSpinner = { start: () => {}, succeed: () => {}, fail: () => {} };
|
|
177
202
|
const installer = await import('../installer.js');
|
|
@@ -184,20 +209,20 @@ export async function installClaudeMcp(targetDir) {
|
|
|
184
209
|
await installer.copyBackgroundMusicFiles(targetDir, silentSpinner);
|
|
185
210
|
ensureDefaultLlmConfigSync('claude-code', targetDir);
|
|
186
211
|
|
|
187
|
-
return { success: true, mcpCreated };
|
|
212
|
+
return { success: true, mcpCreated, mcpError };
|
|
188
213
|
} catch (err) {
|
|
189
|
-
return { success: false, error: err.message };
|
|
214
|
+
return { success: false, error: err.message, mcpError };
|
|
190
215
|
}
|
|
191
216
|
}
|
|
192
217
|
|
|
193
218
|
export async function removeClaudeMcp(targetDir) {
|
|
194
|
-
|
|
219
|
+
// Clean up .mcp.json agentvibes entry (legacy from older versions)
|
|
195
220
|
try {
|
|
221
|
+
const mcpConfigPath = path.join(targetDir, '.mcp.json');
|
|
196
222
|
const content = await fs.readFile(mcpConfigPath, 'utf8');
|
|
197
223
|
const parsed = JSON.parse(content);
|
|
198
224
|
if (parsed.mcpServers?.agentvibes) {
|
|
199
225
|
delete parsed.mcpServers.agentvibes;
|
|
200
|
-
// Only delete file if mcpServers is empty AND no other top-level keys
|
|
201
226
|
const noServers = Object.keys(parsed.mcpServers).length === 0;
|
|
202
227
|
const noOtherKeys = Object.keys(parsed).length === 1;
|
|
203
228
|
if (noServers && noOtherKeys) {
|
|
@@ -217,9 +242,9 @@ export async function removeClaudeMcp(targetDir) {
|
|
|
217
242
|
export async function uninstallClaude(targetDir) {
|
|
218
243
|
const removed = [];
|
|
219
244
|
|
|
220
|
-
// 1. Remove
|
|
245
|
+
// 1. Remove legacy .mcp.json agentvibes entry if present
|
|
221
246
|
await removeClaudeMcp(targetDir);
|
|
222
|
-
removed.push('.mcp.json
|
|
247
|
+
removed.push('.mcp.json agentvibes entry (if present)');
|
|
223
248
|
|
|
224
249
|
// 2. Remove AgentVibes directories
|
|
225
250
|
const dirs = [
|
|
@@ -304,6 +329,7 @@ export async function installCopilotMcp(targetDir) {
|
|
|
304
329
|
env: { AGENTVIBES_LLM: 'copilot' },
|
|
305
330
|
};
|
|
306
331
|
|
|
332
|
+
let mcpError = null;
|
|
307
333
|
try {
|
|
308
334
|
await fs.mkdir(vscodeDir, { recursive: true });
|
|
309
335
|
let mcpConfig = { servers: {} };
|
|
@@ -316,6 +342,8 @@ export async function installCopilotMcp(targetDir) {
|
|
|
316
342
|
}
|
|
317
343
|
} catch { /* new file */ }
|
|
318
344
|
|
|
345
|
+
// Clean up any old "agentvibes-copilot" entry from a prior attempt.
|
|
346
|
+
delete mcpConfig.servers['agentvibes-copilot'];
|
|
319
347
|
mcpConfig.servers.agentvibes = agentvibesServer;
|
|
320
348
|
await fs.writeFile(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + '\n');
|
|
321
349
|
|
|
@@ -325,34 +353,45 @@ export async function installCopilotMcp(targetDir) {
|
|
|
325
353
|
// CLI reads ONLY from ~/.copilot/mcp-config.json per docs:
|
|
326
354
|
// https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers
|
|
327
355
|
try {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
356
|
+
// If neither USERPROFILE nor HOME is set, skip — writing to a
|
|
357
|
+
// relative `.copilot/` path would pollute the project dir.
|
|
358
|
+
const home = process.env.COPILOT_HOME ||
|
|
359
|
+
process.env.USERPROFILE || process.env.HOME;
|
|
360
|
+
if (home) {
|
|
361
|
+
const copilotHome = process.env.COPILOT_HOME || path.join(home, '.copilot');
|
|
362
|
+
const copilotMcpPath = path.join(copilotHome, 'mcp-config.json');
|
|
363
|
+
await fs.mkdir(copilotHome, { recursive: true });
|
|
364
|
+
let cliConfig = { mcpServers: {} };
|
|
365
|
+
try {
|
|
366
|
+
const existingCli = await fs.readFile(copilotMcpPath, 'utf8');
|
|
367
|
+
const parsedCli = JSON.parse(existingCli);
|
|
368
|
+
if (parsedCli && typeof parsedCli === 'object' && !Array.isArray(parsedCli)) {
|
|
369
|
+
cliConfig = parsedCli;
|
|
370
|
+
if (!cliConfig.mcpServers || typeof cliConfig.mcpServers !== 'object' || Array.isArray(cliConfig.mcpServers)) {
|
|
371
|
+
cliConfig.mcpServers = {};
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
} catch { /* new file or malformed — start fresh */ }
|
|
375
|
+
cliConfig.mcpServers.agentvibes = {
|
|
376
|
+
type: 'local',
|
|
377
|
+
command: 'npx',
|
|
378
|
+
args: ['-y', '--package=agentvibes', 'agentvibes-mcp-server'],
|
|
379
|
+
env: { AGENTVIBES_LLM: 'copilot' },
|
|
380
|
+
tools: ['*'],
|
|
381
|
+
};
|
|
382
|
+
await fs.writeFile(copilotMcpPath, JSON.stringify(cliConfig, null, 2) + '\n');
|
|
383
|
+
}
|
|
384
|
+
} catch (err) {
|
|
385
|
+
// Best effort — CLI might not be installed. Log to stderr so users
|
|
386
|
+
// with COPILOT_HOME set but write failures (EACCES) can diagnose.
|
|
387
|
+
console.error(`[agentvibes] Warning: could not write ~/.copilot/mcp-config.json: ${err.message}`);
|
|
388
|
+
}
|
|
353
389
|
} catch (err) {
|
|
354
|
-
|
|
390
|
+
mcpError = err.message;
|
|
355
391
|
}
|
|
392
|
+
|
|
393
|
+
ensureDefaultLlmConfigSync('copilot', targetDir);
|
|
394
|
+
return { success: true, mcpError };
|
|
356
395
|
}
|
|
357
396
|
|
|
358
397
|
export async function removeCopilotMcp(targetDir) {
|
|
@@ -360,13 +399,23 @@ export async function removeCopilotMcp(targetDir) {
|
|
|
360
399
|
try {
|
|
361
400
|
const content = await fs.readFile(mcpJsonPath, 'utf8');
|
|
362
401
|
const parsed = JSON.parse(content);
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
402
|
+
// Guard against non-object root or non-object servers (malformed config)
|
|
403
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
404
|
+
return { success: true };
|
|
405
|
+
}
|
|
406
|
+
const servers = parsed.servers;
|
|
407
|
+
if (!servers || typeof servers !== 'object' || Array.isArray(servers)) {
|
|
408
|
+
return { success: true };
|
|
409
|
+
}
|
|
410
|
+
// Remove both old ("agentvibes") and new ("agentvibes-copilot") names
|
|
411
|
+
delete servers.agentvibes;
|
|
412
|
+
delete servers['agentvibes-copilot'];
|
|
413
|
+
const noServers = Object.keys(servers).length === 0;
|
|
414
|
+
const noOtherKeys = Object.keys(parsed).length === 1; // only "servers"
|
|
415
|
+
if (noServers && noOtherKeys) {
|
|
416
|
+
await fs.unlink(mcpJsonPath);
|
|
417
|
+
} else {
|
|
418
|
+
await fs.writeFile(mcpJsonPath, JSON.stringify(parsed, null, 2) + '\n');
|
|
370
419
|
}
|
|
371
420
|
return { success: true };
|
|
372
421
|
} catch {
|
|
@@ -396,17 +445,19 @@ export async function installCodexMcp(targetDir) {
|
|
|
396
445
|
const codexDir = path.join(targetDir, '.codex');
|
|
397
446
|
const tomlPath = path.join(codexDir, 'config.toml');
|
|
398
447
|
|
|
448
|
+
let mcpError = null;
|
|
399
449
|
try {
|
|
400
450
|
await fs.mkdir(codexDir, { recursive: true });
|
|
401
451
|
let existing = '';
|
|
402
452
|
try { existing = await fs.readFile(tomlPath, 'utf8'); } catch { /* new file */ }
|
|
403
453
|
const content = buildCodexToml(existing);
|
|
404
454
|
await fs.writeFile(tomlPath, content);
|
|
405
|
-
ensureDefaultLlmConfigSync('codex', targetDir);
|
|
406
|
-
return { success: true };
|
|
407
455
|
} catch (err) {
|
|
408
|
-
|
|
456
|
+
mcpError = err.message;
|
|
409
457
|
}
|
|
458
|
+
|
|
459
|
+
ensureDefaultLlmConfigSync('codex', targetDir);
|
|
460
|
+
return { success: true, mcpError };
|
|
410
461
|
}
|
|
411
462
|
|
|
412
463
|
export async function removeCodexMcp(targetDir) {
|