@holdyourvoice/hyv 2.9.14 → 2.9.15
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/CHANGELOG.md +11 -0
- package/agents/chatgpt.md +12 -1
- package/agents/cursor.md +3 -1
- package/agents/generic.md +10 -1
- package/dist/index.js +890 -411
- package/package.json +1 -1
- package/scripts/postinstall-lib.js +333 -25
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holdyourvoice/hyv",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.15",
|
|
4
4
|
"description": "Free local AI writing scan for cursor & claude. MCP server, 220+ pattern detection, voice profiles. npx @holdyourvoice/hyv welcome",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -89,6 +89,18 @@ function resolveHyvMcpCommandArray(pkgDir) {
|
|
|
89
89
|
return [command, ...args];
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
function mcpJsonEntriesMatch(existing, desired) {
|
|
93
|
+
if (!existing || typeof existing !== 'object') return false;
|
|
94
|
+
const a = existing.args || [];
|
|
95
|
+
const b = desired.args || [];
|
|
96
|
+
return existing.command === desired.command && JSON.stringify(a) === JSON.stringify(b);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function mcpCommandArrayMatches(existing, desired) {
|
|
100
|
+
if (!Array.isArray(existing) || !Array.isArray(desired)) return false;
|
|
101
|
+
return JSON.stringify(existing) === JSON.stringify(desired);
|
|
102
|
+
}
|
|
103
|
+
|
|
92
104
|
function parseJsonc(text) {
|
|
93
105
|
const noBlock = text.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
94
106
|
const noLine = noBlock.replace(/^\s*\/\/.*$/gm, '');
|
|
@@ -151,6 +163,226 @@ function chatgptHelperDir(home) {
|
|
|
151
163
|
return path.join(home, '.chatgpt');
|
|
152
164
|
}
|
|
153
165
|
|
|
166
|
+
function macAppInstalled(appName, home) {
|
|
167
|
+
if (process.platform !== 'darwin') return false;
|
|
168
|
+
const roots = ['/Applications', path.join(home, 'Applications')];
|
|
169
|
+
return roots.some((root) => fs.existsSync(path.join(root, `${appName}.app`)));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function dirExists(dir) {
|
|
173
|
+
try {
|
|
174
|
+
return fs.existsSync(dir) && fs.statSync(dir).isDirectory();
|
|
175
|
+
} catch {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function fileExists(file) {
|
|
181
|
+
try {
|
|
182
|
+
return fs.existsSync(file);
|
|
183
|
+
} catch {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function commandOnPath(cmd) {
|
|
189
|
+
const pathVar = process.env.PATH || '';
|
|
190
|
+
const exts = process.platform === 'win32' ? (process.env.PATHEXT || '.EXE;.CMD;.BAT').split(';') : [''];
|
|
191
|
+
for (const entry of pathVar.split(path.delimiter)) {
|
|
192
|
+
if (!entry) continue;
|
|
193
|
+
for (const ext of exts) {
|
|
194
|
+
const candidate = path.join(entry, process.platform === 'win32' ? `${cmd}${ext}` : cmd);
|
|
195
|
+
try {
|
|
196
|
+
if (fs.existsSync(candidate)) return true;
|
|
197
|
+
} catch {
|
|
198
|
+
// ignore
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Detect locally installed AI apps that hyv can integrate with.
|
|
207
|
+
* @returns {{ id: string, label: string, reason: string, integration: 'mcp'|'rules'|'manual' }[]}
|
|
208
|
+
*/
|
|
209
|
+
function detectInstalledAgents(home = require('os').homedir(), isWin = process.platform === 'win32') {
|
|
210
|
+
const detected = [];
|
|
211
|
+
|
|
212
|
+
const claudeDir = claudeDesktopDir(home, isWin);
|
|
213
|
+
if (
|
|
214
|
+
dirExists(claudeDir) ||
|
|
215
|
+
fileExists(path.join(claudeDir, 'claude_desktop_config.json')) ||
|
|
216
|
+
macAppInstalled('Claude', home)
|
|
217
|
+
) {
|
|
218
|
+
detected.push({
|
|
219
|
+
id: 'claude-desktop',
|
|
220
|
+
label: 'claude desktop',
|
|
221
|
+
reason: dirExists(claudeDir) ? 'config directory found' : 'claude desktop app found',
|
|
222
|
+
integration: 'mcp',
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const cursorDir = path.join(home, '.cursor');
|
|
227
|
+
if (dirExists(cursorDir) || macAppInstalled('Cursor', home)) {
|
|
228
|
+
detected.push({
|
|
229
|
+
id: 'cursor',
|
|
230
|
+
label: 'cursor',
|
|
231
|
+
reason: dirExists(cursorDir) ? '~/.cursor found' : 'cursor app found',
|
|
232
|
+
integration: 'mcp',
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const claudeCodeDir = path.join(home, '.claude');
|
|
237
|
+
if (dirExists(claudeCodeDir) || commandOnPath('claude')) {
|
|
238
|
+
detected.push({
|
|
239
|
+
id: 'claude-code',
|
|
240
|
+
label: 'claude code',
|
|
241
|
+
reason: dirExists(claudeCodeDir) ? '~/.claude found' : 'claude cli on path',
|
|
242
|
+
integration: 'rules',
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const windsurfDirs = [
|
|
247
|
+
path.join(home, '.windsurf'),
|
|
248
|
+
isWin ? path.join(home, 'AppData', 'Roaming', 'Windsurf') : path.join(home, 'Library', 'Application Support', 'Windsurf'),
|
|
249
|
+
].filter(Boolean);
|
|
250
|
+
if (windsurfDirs.some(dirExists) || macAppInstalled('Windsurf', home)) {
|
|
251
|
+
detected.push({
|
|
252
|
+
id: 'windsurf',
|
|
253
|
+
label: 'windsurf',
|
|
254
|
+
reason: windsurfDirs.some(dirExists) ? 'windsurf config found' : 'windsurf app found',
|
|
255
|
+
integration: 'rules',
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (dirExists(path.join(home, '.codex')) || commandOnPath('codex')) {
|
|
260
|
+
detected.push({
|
|
261
|
+
id: 'codex',
|
|
262
|
+
label: 'codex',
|
|
263
|
+
reason: dirExists(path.join(home, '.codex')) ? '~/.codex found' : 'codex cli on path',
|
|
264
|
+
integration: 'rules',
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (dirExists(path.join(home, '.commandcode'))) {
|
|
269
|
+
detected.push({
|
|
270
|
+
id: 'command-code',
|
|
271
|
+
label: 'command code',
|
|
272
|
+
reason: '~/.commandcode found',
|
|
273
|
+
integration: 'rules',
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const agDir = path.dirname(antigravityMcpConfigPath(home, isWin));
|
|
278
|
+
if (dirExists(agDir) || macAppInstalled('Antigravity', home)) {
|
|
279
|
+
detected.push({
|
|
280
|
+
id: 'antigravity',
|
|
281
|
+
label: 'antigravity',
|
|
282
|
+
reason: dirExists(agDir) ? '~/.gemini/config found' : 'antigravity app found',
|
|
283
|
+
integration: 'mcp',
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const ocDir = opencodeConfigDir(home, isWin);
|
|
288
|
+
if (dirExists(ocDir) || commandOnPath('opencode')) {
|
|
289
|
+
detected.push({
|
|
290
|
+
id: 'opencode',
|
|
291
|
+
label: 'opencode',
|
|
292
|
+
reason: dirExists(ocDir) ? 'opencode config found' : 'opencode cli on path',
|
|
293
|
+
integration: 'mcp',
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const chatgptDir = chatgptHelperDir(home);
|
|
298
|
+
if (dirExists(chatgptDir) || macAppInstalled('ChatGPT', home)) {
|
|
299
|
+
detected.push({
|
|
300
|
+
id: 'chatgpt',
|
|
301
|
+
label: 'chatgpt desktop',
|
|
302
|
+
reason: dirExists(chatgptDir) ? '~/.chatgpt found' : 'chatgpt app found',
|
|
303
|
+
integration: 'manual',
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return detected;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function agentIdForConfiguredLabel(label) {
|
|
311
|
+
const map = {
|
|
312
|
+
'claude desktop': 'claude-desktop',
|
|
313
|
+
'claude desktop mcp': 'claude-desktop',
|
|
314
|
+
cursor: 'cursor',
|
|
315
|
+
'cursor mcp': 'cursor',
|
|
316
|
+
'claude code': 'claude-code',
|
|
317
|
+
'claude code skill': 'claude-code',
|
|
318
|
+
windsurf: 'windsurf',
|
|
319
|
+
codex: 'codex',
|
|
320
|
+
'command code': 'command-code',
|
|
321
|
+
'antigravity mcp': 'antigravity',
|
|
322
|
+
'opencode mcp': 'opencode',
|
|
323
|
+
'opencode agents': 'opencode',
|
|
324
|
+
'chatgpt connector guide': 'chatgpt',
|
|
325
|
+
};
|
|
326
|
+
return map[label] || null;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function isAgentIdEnabled(agentId, onlyDetected, detectedIds) {
|
|
330
|
+
if (!onlyDetected) return true;
|
|
331
|
+
return detectedIds.includes(agentId);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function readMcpHyvEntry(configFile) {
|
|
335
|
+
if (!fileExists(configFile)) return null;
|
|
336
|
+
try {
|
|
337
|
+
const cfg = JSON.parse(fs.readFileSync(configFile, 'utf-8'));
|
|
338
|
+
return cfg?.mcpServers?.hyv || null;
|
|
339
|
+
} catch {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function isAgentIntegrated(agentId, home = require('os').homedir(), isWin = process.platform === 'win32') {
|
|
345
|
+
switch (agentId) {
|
|
346
|
+
case 'claude-desktop':
|
|
347
|
+
return Boolean(readMcpHyvEntry(path.join(claudeDesktopDir(home, isWin), 'claude_desktop_config.json')));
|
|
348
|
+
case 'cursor':
|
|
349
|
+
return Boolean(readMcpHyvEntry(path.join(home, '.cursor', 'mcp.json')))
|
|
350
|
+
&& fileExists(path.join(home, '.cursor', 'rules', 'hyv.mdc'));
|
|
351
|
+
case 'claude-code':
|
|
352
|
+
return fileExists(path.join(home, '.claude', 'commands', 'hyv.md'))
|
|
353
|
+
&& fileExists(path.join(home, '.claude', 'skills', 'hold-your-voice', 'SKILL.md'));
|
|
354
|
+
case 'windsurf': {
|
|
355
|
+
const dirs = [
|
|
356
|
+
path.join(home, '.windsurf'),
|
|
357
|
+
isWin ? path.join(home, 'AppData', 'Roaming', 'Windsurf') : path.join(home, 'Library', 'Application Support', 'Windsurf'),
|
|
358
|
+
].filter(Boolean);
|
|
359
|
+
return dirs.some((dir) => fileExists(path.join(dir, 'rules', 'hyv.md')));
|
|
360
|
+
}
|
|
361
|
+
case 'codex': {
|
|
362
|
+
const agents = path.join(home, '.codex', 'AGENTS.md');
|
|
363
|
+
return fileExists(agents) && fs.readFileSync(agents, 'utf-8').includes('hyv');
|
|
364
|
+
}
|
|
365
|
+
case 'command-code':
|
|
366
|
+
return fileExists(path.join(home, '.commandcode', 'skills', 'hyv', 'SKILL.md'));
|
|
367
|
+
case 'antigravity':
|
|
368
|
+
return Boolean(readMcpHyvEntry(antigravityMcpConfigPath(home, isWin)));
|
|
369
|
+
case 'opencode': {
|
|
370
|
+
const ocFile = path.join(opencodeConfigDir(home, isWin), 'opencode.jsonc');
|
|
371
|
+
if (!fileExists(ocFile)) return false;
|
|
372
|
+
try {
|
|
373
|
+
const cfg = parseJsonc(fs.readFileSync(ocFile, 'utf-8'));
|
|
374
|
+
return Boolean(cfg.mcp?.hyv);
|
|
375
|
+
} catch {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
case 'chatgpt':
|
|
380
|
+
return fileExists(path.join(chatgptHelperDir(home), 'hyv-mcp-connector.txt'));
|
|
381
|
+
default:
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
154
386
|
function mergeJsonConfigFile(configFile, mutator, { backup = true } = {}) {
|
|
155
387
|
fs.mkdirSync(path.dirname(configFile), { recursive: true });
|
|
156
388
|
const read = readJsonObjectFromFile(configFile, (text) => JSON.parse(text));
|
|
@@ -207,20 +439,35 @@ function claudeDesktopDir(home, isWin) {
|
|
|
207
439
|
|
|
208
440
|
/**
|
|
209
441
|
* Configure MCP + agent instructions for detected IDEs.
|
|
210
|
-
* @returns {{ configured: string[], warnings: string[], pkgVersion: string }}
|
|
442
|
+
* @returns {{ configured: string[], warnings: string[], pkgVersion: string, detected: object[], skipped: string[] }}
|
|
211
443
|
*/
|
|
212
|
-
function setupAgents({
|
|
444
|
+
function setupAgents({
|
|
445
|
+
pkgDir,
|
|
446
|
+
home = require('os').homedir(),
|
|
447
|
+
quiet = false,
|
|
448
|
+
onlyDetected = false,
|
|
449
|
+
force = false,
|
|
450
|
+
} = {}) {
|
|
213
451
|
const configured = [];
|
|
214
452
|
const warnings = [];
|
|
453
|
+
const skipped = [];
|
|
215
454
|
const isWin = process.platform === 'win32';
|
|
216
455
|
const hyvDir = path.join(home, '.hyv');
|
|
217
456
|
const agentsMarker = path.join(hyvDir, 'agents-version.json');
|
|
218
457
|
const pkgVersion = readPkgVersion(pkgDir);
|
|
219
458
|
const mcpCmd = resolveHyvMcpCommand(pkgDir);
|
|
220
459
|
const autoConfigure = process.env.HYV_AUTO_CONFIGURE_AGENTS !== '0';
|
|
460
|
+
const detected = detectInstalledAgents(home, isWin);
|
|
461
|
+
const detectedIds = detected.map((entry) => entry.id);
|
|
221
462
|
|
|
222
463
|
if (!autoConfigure) {
|
|
223
|
-
return {
|
|
464
|
+
return {
|
|
465
|
+
configured,
|
|
466
|
+
warnings: ['HYV_AUTO_CONFIGURE_AGENTS=0 — skipped agent setup'],
|
|
467
|
+
pkgVersion,
|
|
468
|
+
detected,
|
|
469
|
+
skipped: detectedIds,
|
|
470
|
+
};
|
|
224
471
|
}
|
|
225
472
|
|
|
226
473
|
function installAgent(src, dest, transform) {
|
|
@@ -229,11 +476,16 @@ function setupAgents({ pkgDir, home = require('os').homedir(), quiet = false })
|
|
|
229
476
|
}
|
|
230
477
|
|
|
231
478
|
function setupMcpJson(configFile, label) {
|
|
479
|
+
const desired = { command: mcpCmd.command, args: mcpCmd.args };
|
|
232
480
|
const result = mergeJsonConfigFile(configFile, (config) => {
|
|
233
481
|
if (!config.mcpServers) config.mcpServers = {};
|
|
234
|
-
|
|
235
|
-
|
|
482
|
+
const existing = config.mcpServers.hyv;
|
|
483
|
+
if (!existing) {
|
|
484
|
+
config.mcpServers.hyv = desired;
|
|
236
485
|
configured.push(`${label} mcp`);
|
|
486
|
+
} else if (force || !mcpJsonEntriesMatch(existing, desired)) {
|
|
487
|
+
config.mcpServers.hyv = desired;
|
|
488
|
+
configured.push(`${label} mcp (updated)`);
|
|
237
489
|
}
|
|
238
490
|
return config;
|
|
239
491
|
});
|
|
@@ -242,18 +494,22 @@ function setupAgents({ pkgDir, home = require('os').homedir(), quiet = false })
|
|
|
242
494
|
}
|
|
243
495
|
|
|
244
496
|
// Claude Desktop MCP
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
497
|
+
if (isAgentIdEnabled('claude-desktop', onlyDetected, detectedIds)) {
|
|
498
|
+
try {
|
|
499
|
+
const claudeDir = claudeDesktopDir(home, isWin);
|
|
500
|
+
const configFile = path.join(claudeDir, 'claude_desktop_config.json');
|
|
249
501
|
setupMcpJson(configFile, 'claude desktop');
|
|
502
|
+
} catch (err) {
|
|
503
|
+
warnings.push(`claude desktop: ${err.message}`);
|
|
250
504
|
}
|
|
251
|
-
}
|
|
252
|
-
|
|
505
|
+
} else if (onlyDetected) {
|
|
506
|
+
skipped.push('claude-desktop');
|
|
253
507
|
}
|
|
254
508
|
|
|
255
509
|
// Cursor — global MCP + always-on rule (.mdc)
|
|
256
|
-
|
|
510
|
+
if (!isAgentIdEnabled('cursor', onlyDetected, detectedIds)) {
|
|
511
|
+
if (onlyDetected) skipped.push('cursor');
|
|
512
|
+
} else try {
|
|
257
513
|
const cursorDir = path.join(home, '.cursor');
|
|
258
514
|
fs.mkdirSync(cursorDir, { recursive: true });
|
|
259
515
|
const mcpFile = path.join(cursorDir, 'mcp.json');
|
|
@@ -271,7 +527,9 @@ function setupAgents({ pkgDir, home = require('os').homedir(), quiet = false })
|
|
|
271
527
|
}
|
|
272
528
|
|
|
273
529
|
// Claude Code — slash command + skill
|
|
274
|
-
|
|
530
|
+
if (!isAgentIdEnabled('claude-code', onlyDetected, detectedIds)) {
|
|
531
|
+
if (onlyDetected) skipped.push('claude-code');
|
|
532
|
+
} else try {
|
|
275
533
|
const cmdDir = path.join(home, '.claude', 'commands');
|
|
276
534
|
const skillDir = path.join(home, '.claude', 'skills', 'hold-your-voice');
|
|
277
535
|
fs.mkdirSync(cmdDir, { recursive: true });
|
|
@@ -287,7 +545,9 @@ function setupAgents({ pkgDir, home = require('os').homedir(), quiet = false })
|
|
|
287
545
|
}
|
|
288
546
|
|
|
289
547
|
// Windsurf — rules with frontmatter
|
|
290
|
-
|
|
548
|
+
if (!isAgentIdEnabled('windsurf', onlyDetected, detectedIds)) {
|
|
549
|
+
if (onlyDetected) skipped.push('windsurf');
|
|
550
|
+
} else try {
|
|
291
551
|
const wsDirs = [
|
|
292
552
|
isWin ? path.join(home, 'AppData', 'Roaming', 'Windsurf') : path.join(home, '.windsurf'),
|
|
293
553
|
isWin ? null : path.join(home, 'Library', 'Application Support', 'Windsurf'),
|
|
@@ -306,7 +566,9 @@ function setupAgents({ pkgDir, home = require('os').homedir(), quiet = false })
|
|
|
306
566
|
}
|
|
307
567
|
|
|
308
568
|
// Codex — ~/.codex/AGENTS.md
|
|
309
|
-
|
|
569
|
+
if (!isAgentIdEnabled('codex', onlyDetected, detectedIds)) {
|
|
570
|
+
if (onlyDetected) skipped.push('codex');
|
|
571
|
+
} else try {
|
|
310
572
|
const codexDir = path.join(home, '.codex');
|
|
311
573
|
fs.mkdirSync(codexDir, { recursive: true });
|
|
312
574
|
const agentsFile = path.join(codexDir, 'AGENTS.md');
|
|
@@ -327,7 +589,9 @@ function setupAgents({ pkgDir, home = require('os').homedir(), quiet = false })
|
|
|
327
589
|
}
|
|
328
590
|
|
|
329
591
|
// Command Code skill
|
|
330
|
-
|
|
592
|
+
if (!isAgentIdEnabled('command-code', onlyDetected, detectedIds)) {
|
|
593
|
+
if (onlyDetected) skipped.push('command-code');
|
|
594
|
+
} else try {
|
|
331
595
|
const ccSkillDir = path.join(home, '.commandcode', 'skills', 'hyv');
|
|
332
596
|
fs.mkdirSync(ccSkillDir, { recursive: true });
|
|
333
597
|
const skillSrc = path.join(pkgDir, 'skills', 'hold-your-voice', 'SKILL.md');
|
|
@@ -338,14 +602,21 @@ function setupAgents({ pkgDir, home = require('os').homedir(), quiet = false })
|
|
|
338
602
|
}
|
|
339
603
|
|
|
340
604
|
// Antigravity — ~/.gemini/config/mcp_config.json (absolute paths for GUI launch)
|
|
341
|
-
|
|
605
|
+
if (!isAgentIdEnabled('antigravity', onlyDetected, detectedIds)) {
|
|
606
|
+
if (onlyDetected) skipped.push('antigravity');
|
|
607
|
+
} else try {
|
|
342
608
|
const agFile = antigravityMcpConfigPath(home, isWin);
|
|
343
609
|
const mcpCmd = resolveHyvMcpCommand(pkgDir);
|
|
610
|
+
const desired = { command: mcpCmd.command, args: mcpCmd.args };
|
|
344
611
|
const result = mergeJsonConfigFile(agFile, (config) => {
|
|
345
612
|
if (!config.mcpServers) config.mcpServers = {};
|
|
346
|
-
|
|
347
|
-
|
|
613
|
+
const existing = config.mcpServers.hyv;
|
|
614
|
+
if (!existing) {
|
|
615
|
+
config.mcpServers.hyv = desired;
|
|
348
616
|
configured.push('antigravity mcp');
|
|
617
|
+
} else if (force || !mcpJsonEntriesMatch(existing, desired)) {
|
|
618
|
+
config.mcpServers.hyv = desired;
|
|
619
|
+
configured.push('antigravity mcp (updated)');
|
|
349
620
|
}
|
|
350
621
|
return config;
|
|
351
622
|
});
|
|
@@ -355,15 +626,22 @@ function setupAgents({ pkgDir, home = require('os').homedir(), quiet = false })
|
|
|
355
626
|
}
|
|
356
627
|
|
|
357
628
|
// OpenCode — ~/.config/opencode/opencode.jsonc + AGENTS.md
|
|
358
|
-
|
|
629
|
+
if (!isAgentIdEnabled('opencode', onlyDetected, detectedIds)) {
|
|
630
|
+
if (onlyDetected) skipped.push('opencode');
|
|
631
|
+
} else try {
|
|
359
632
|
const ocDir = opencodeConfigDir(home, isWin);
|
|
360
633
|
const ocFile = path.join(ocDir, 'opencode.jsonc');
|
|
361
634
|
const cmdArr = resolveHyvMcpCommandArray(pkgDir);
|
|
362
635
|
const ocResult = mergeJsoncConfigFile(ocFile, (config) => {
|
|
363
636
|
if (!config.mcp) config.mcp = {};
|
|
364
|
-
|
|
365
|
-
|
|
637
|
+
const existing = config.mcp.hyv;
|
|
638
|
+
const desired = { type: 'local', command: cmdArr, enabled: true };
|
|
639
|
+
if (!existing) {
|
|
640
|
+
config.mcp.hyv = desired;
|
|
366
641
|
configured.push('opencode mcp');
|
|
642
|
+
} else if (force || !mcpCommandArrayMatches(existing.command, cmdArr)) {
|
|
643
|
+
config.mcp.hyv = { ...existing, ...desired, command: cmdArr };
|
|
644
|
+
configured.push('opencode mcp (updated)');
|
|
367
645
|
}
|
|
368
646
|
return config;
|
|
369
647
|
});
|
|
@@ -389,7 +667,9 @@ function setupAgents({ pkgDir, home = require('os').homedir(), quiet = false })
|
|
|
389
667
|
}
|
|
390
668
|
|
|
391
669
|
// ChatGPT — connector helper (UI has no config file; write exact values for manual add)
|
|
392
|
-
|
|
670
|
+
if (!isAgentIdEnabled('chatgpt', onlyDetected, detectedIds)) {
|
|
671
|
+
if (onlyDetected) skipped.push('chatgpt');
|
|
672
|
+
} else try {
|
|
393
673
|
const cgDir = chatgptHelperDir(home);
|
|
394
674
|
fs.mkdirSync(cgDir, { recursive: true });
|
|
395
675
|
const mcpCmd = resolveHyvMcpCommand(pkgDir);
|
|
@@ -424,7 +704,7 @@ function setupAgents({ pkgDir, home = require('os').homedir(), quiet = false })
|
|
|
424
704
|
warnings.push(`chatgpt: ${err.message}`);
|
|
425
705
|
}
|
|
426
706
|
|
|
427
|
-
// Generic reference copy
|
|
707
|
+
// Generic reference copy (always when auto-configure runs)
|
|
428
708
|
try {
|
|
429
709
|
const genericSrc = path.join(pkgDir, 'agents', 'generic.md');
|
|
430
710
|
const genericDest = path.join(hyvDir, 'agents', 'generic.md');
|
|
@@ -437,7 +717,31 @@ function setupAgents({ pkgDir, home = require('os').homedir(), quiet = false })
|
|
|
437
717
|
if (!quiet && warnings.length) {
|
|
438
718
|
for (const w of warnings) process.stderr.write(`[hyv postinstall] warning: ${w}\n`);
|
|
439
719
|
}
|
|
440
|
-
return {
|
|
720
|
+
return {
|
|
721
|
+
configured: [...new Set(configured)],
|
|
722
|
+
warnings,
|
|
723
|
+
pkgVersion,
|
|
724
|
+
detected,
|
|
725
|
+
skipped: [...new Set(skipped)],
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Detect installed apps and configure hyv MCP/rules for each one.
|
|
731
|
+
* @returns {{ detected: object[], configured: string[], warnings: string[], skipped: string[] }}
|
|
732
|
+
*/
|
|
733
|
+
function integrateDetectedAgents({ pkgDir, home = require('os').homedir(), quiet = false, force = false } = {}) {
|
|
734
|
+
const detected = detectInstalledAgents(home);
|
|
735
|
+
if (detected.length === 0) {
|
|
736
|
+
return { detected, configured: [], warnings: [], skipped: [] };
|
|
737
|
+
}
|
|
738
|
+
const result = setupAgents({ pkgDir, home, quiet, onlyDetected: true, force });
|
|
739
|
+
return {
|
|
740
|
+
detected: result.detected || detected,
|
|
741
|
+
configured: result.configured || [],
|
|
742
|
+
warnings: result.warnings || [],
|
|
743
|
+
skipped: result.skipped || [],
|
|
744
|
+
};
|
|
441
745
|
}
|
|
442
746
|
|
|
443
747
|
module.exports = {
|
|
@@ -457,6 +761,10 @@ module.exports = {
|
|
|
457
761
|
toCursorMdc,
|
|
458
762
|
toWindsurfRule,
|
|
459
763
|
setupAgents,
|
|
764
|
+
detectInstalledAgents,
|
|
765
|
+
integrateDetectedAgents,
|
|
766
|
+
agentIdForConfiguredLabel,
|
|
767
|
+
isAgentIntegrated,
|
|
460
768
|
claudeDesktopDir,
|
|
461
769
|
antigravityMcpConfigPath,
|
|
462
770
|
opencodeConfigDir,
|