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
|
@@ -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
|
-
mcpCreated = true;
|
|
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
173
|
}
|
|
174
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');
|
|
193
|
+
}
|
|
194
|
+
mcpCreated = true;
|
|
195
|
+
} catch (err) {
|
|
196
|
+
mcpError = err.message;
|
|
197
|
+
}
|
|
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,33 @@ export async function installClaudeMcp(targetDir) {
|
|
|
184
209
|
await installer.copyBackgroundMusicFiles(targetDir, silentSpinner);
|
|
185
210
|
ensureDefaultLlmConfigSync('claude-code', targetDir);
|
|
186
211
|
|
|
187
|
-
|
|
212
|
+
// Explicitly write tts-provider.txt so `get_active_provider()` in
|
|
213
|
+
// provider-manager.sh doesn't silently fall back to "piper". Without
|
|
214
|
+
// this, headless servers with no audio device hit a confusing failure
|
|
215
|
+
// mode where TTS tries to synth locally and fails silently. Users
|
|
216
|
+
// can still change the provider via the Setup TUI or slash command.
|
|
217
|
+
const ttsProviderPath = path.join(targetDir, '.claude', 'tts-provider.txt');
|
|
218
|
+
try {
|
|
219
|
+
await fs.access(ttsProviderPath);
|
|
220
|
+
// Already exists — user has explicitly set a provider, don't clobber
|
|
221
|
+
} catch {
|
|
222
|
+
await fs.writeFile(ttsProviderPath, 'piper\n');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return { success: true, mcpCreated, mcpError };
|
|
188
226
|
} catch (err) {
|
|
189
|
-
return { success: false, error: err.message };
|
|
227
|
+
return { success: false, error: err.message, mcpError };
|
|
190
228
|
}
|
|
191
229
|
}
|
|
192
230
|
|
|
193
231
|
export async function removeClaudeMcp(targetDir) {
|
|
194
|
-
|
|
232
|
+
// Clean up .mcp.json agentvibes entry (legacy from older versions)
|
|
195
233
|
try {
|
|
234
|
+
const mcpConfigPath = path.join(targetDir, '.mcp.json');
|
|
196
235
|
const content = await fs.readFile(mcpConfigPath, 'utf8');
|
|
197
236
|
const parsed = JSON.parse(content);
|
|
198
237
|
if (parsed.mcpServers?.agentvibes) {
|
|
199
238
|
delete parsed.mcpServers.agentvibes;
|
|
200
|
-
// Only delete file if mcpServers is empty AND no other top-level keys
|
|
201
239
|
const noServers = Object.keys(parsed.mcpServers).length === 0;
|
|
202
240
|
const noOtherKeys = Object.keys(parsed).length === 1;
|
|
203
241
|
if (noServers && noOtherKeys) {
|
|
@@ -217,9 +255,9 @@ export async function removeClaudeMcp(targetDir) {
|
|
|
217
255
|
export async function uninstallClaude(targetDir) {
|
|
218
256
|
const removed = [];
|
|
219
257
|
|
|
220
|
-
// 1. Remove
|
|
258
|
+
// 1. Remove legacy .mcp.json agentvibes entry if present
|
|
221
259
|
await removeClaudeMcp(targetDir);
|
|
222
|
-
removed.push('.mcp.json
|
|
260
|
+
removed.push('.mcp.json agentvibes entry (if present)');
|
|
223
261
|
|
|
224
262
|
// 2. Remove AgentVibes directories
|
|
225
263
|
const dirs = [
|
|
@@ -304,6 +342,7 @@ export async function installCopilotMcp(targetDir) {
|
|
|
304
342
|
env: { AGENTVIBES_LLM: 'copilot' },
|
|
305
343
|
};
|
|
306
344
|
|
|
345
|
+
let mcpError = null;
|
|
307
346
|
try {
|
|
308
347
|
await fs.mkdir(vscodeDir, { recursive: true });
|
|
309
348
|
let mcpConfig = { servers: {} };
|
|
@@ -316,6 +355,8 @@ export async function installCopilotMcp(targetDir) {
|
|
|
316
355
|
}
|
|
317
356
|
} catch { /* new file */ }
|
|
318
357
|
|
|
358
|
+
// Clean up any old "agentvibes-copilot" entry from a prior attempt.
|
|
359
|
+
delete mcpConfig.servers['agentvibes-copilot'];
|
|
319
360
|
mcpConfig.servers.agentvibes = agentvibesServer;
|
|
320
361
|
await fs.writeFile(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + '\n');
|
|
321
362
|
|
|
@@ -325,34 +366,45 @@ export async function installCopilotMcp(targetDir) {
|
|
|
325
366
|
// CLI reads ONLY from ~/.copilot/mcp-config.json per docs:
|
|
326
367
|
// https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers
|
|
327
368
|
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
|
-
|
|
369
|
+
// If neither USERPROFILE nor HOME is set, skip — writing to a
|
|
370
|
+
// relative `.copilot/` path would pollute the project dir.
|
|
371
|
+
const home = process.env.COPILOT_HOME ||
|
|
372
|
+
process.env.USERPROFILE || process.env.HOME;
|
|
373
|
+
if (home) {
|
|
374
|
+
const copilotHome = process.env.COPILOT_HOME || path.join(home, '.copilot');
|
|
375
|
+
const copilotMcpPath = path.join(copilotHome, 'mcp-config.json');
|
|
376
|
+
await fs.mkdir(copilotHome, { recursive: true });
|
|
377
|
+
let cliConfig = { mcpServers: {} };
|
|
378
|
+
try {
|
|
379
|
+
const existingCli = await fs.readFile(copilotMcpPath, 'utf8');
|
|
380
|
+
const parsedCli = JSON.parse(existingCli);
|
|
381
|
+
if (parsedCli && typeof parsedCli === 'object' && !Array.isArray(parsedCli)) {
|
|
382
|
+
cliConfig = parsedCli;
|
|
383
|
+
if (!cliConfig.mcpServers || typeof cliConfig.mcpServers !== 'object' || Array.isArray(cliConfig.mcpServers)) {
|
|
384
|
+
cliConfig.mcpServers = {};
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
} catch { /* new file or malformed — start fresh */ }
|
|
388
|
+
cliConfig.mcpServers.agentvibes = {
|
|
389
|
+
type: 'local',
|
|
390
|
+
command: 'npx',
|
|
391
|
+
args: ['-y', '--package=agentvibes', 'agentvibes-mcp-server'],
|
|
392
|
+
env: { AGENTVIBES_LLM: 'copilot' },
|
|
393
|
+
tools: ['*'],
|
|
394
|
+
};
|
|
395
|
+
await fs.writeFile(copilotMcpPath, JSON.stringify(cliConfig, null, 2) + '\n');
|
|
396
|
+
}
|
|
397
|
+
} catch (err) {
|
|
398
|
+
// Best effort — CLI might not be installed. Log to stderr so users
|
|
399
|
+
// with COPILOT_HOME set but write failures (EACCES) can diagnose.
|
|
400
|
+
console.error(`[agentvibes] Warning: could not write ~/.copilot/mcp-config.json: ${err.message}`);
|
|
401
|
+
}
|
|
353
402
|
} catch (err) {
|
|
354
|
-
|
|
403
|
+
mcpError = err.message;
|
|
355
404
|
}
|
|
405
|
+
|
|
406
|
+
ensureDefaultLlmConfigSync('copilot', targetDir);
|
|
407
|
+
return { success: true, mcpError };
|
|
356
408
|
}
|
|
357
409
|
|
|
358
410
|
export async function removeCopilotMcp(targetDir) {
|
|
@@ -360,13 +412,23 @@ export async function removeCopilotMcp(targetDir) {
|
|
|
360
412
|
try {
|
|
361
413
|
const content = await fs.readFile(mcpJsonPath, 'utf8');
|
|
362
414
|
const parsed = JSON.parse(content);
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
415
|
+
// Guard against non-object root or non-object servers (malformed config)
|
|
416
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
417
|
+
return { success: true };
|
|
418
|
+
}
|
|
419
|
+
const servers = parsed.servers;
|
|
420
|
+
if (!servers || typeof servers !== 'object' || Array.isArray(servers)) {
|
|
421
|
+
return { success: true };
|
|
422
|
+
}
|
|
423
|
+
// Remove both old ("agentvibes") and new ("agentvibes-copilot") names
|
|
424
|
+
delete servers.agentvibes;
|
|
425
|
+
delete servers['agentvibes-copilot'];
|
|
426
|
+
const noServers = Object.keys(servers).length === 0;
|
|
427
|
+
const noOtherKeys = Object.keys(parsed).length === 1; // only "servers"
|
|
428
|
+
if (noServers && noOtherKeys) {
|
|
429
|
+
await fs.unlink(mcpJsonPath);
|
|
430
|
+
} else {
|
|
431
|
+
await fs.writeFile(mcpJsonPath, JSON.stringify(parsed, null, 2) + '\n');
|
|
370
432
|
}
|
|
371
433
|
return { success: true };
|
|
372
434
|
} catch {
|
|
@@ -396,17 +458,19 @@ export async function installCodexMcp(targetDir) {
|
|
|
396
458
|
const codexDir = path.join(targetDir, '.codex');
|
|
397
459
|
const tomlPath = path.join(codexDir, 'config.toml');
|
|
398
460
|
|
|
461
|
+
let mcpError = null;
|
|
399
462
|
try {
|
|
400
463
|
await fs.mkdir(codexDir, { recursive: true });
|
|
401
464
|
let existing = '';
|
|
402
465
|
try { existing = await fs.readFile(tomlPath, 'utf8'); } catch { /* new file */ }
|
|
403
466
|
const content = buildCodexToml(existing);
|
|
404
467
|
await fs.writeFile(tomlPath, content);
|
|
405
|
-
ensureDefaultLlmConfigSync('codex', targetDir);
|
|
406
|
-
return { success: true };
|
|
407
468
|
} catch (err) {
|
|
408
|
-
|
|
469
|
+
mcpError = err.message;
|
|
409
470
|
}
|
|
471
|
+
|
|
472
|
+
ensureDefaultLlmConfigSync('codex', targetDir);
|
|
473
|
+
return { success: true, mcpError };
|
|
410
474
|
}
|
|
411
475
|
|
|
412
476
|
export async function removeCodexMcp(targetDir) {
|