@patchen0518/agentbrew 1.2.7 → 1.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/dist/sync.js CHANGED
@@ -36,8 +36,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.INSTRUCTIONS_FILE = exports.MARKER_END = exports.MARKER_START = void 0;
39
+ exports.unsyncInstructions = exports.syncInstructions = exports.removeFromFile = exports.injectIntoFile = exports.buildInjectedSection = exports.getInstructionsPath = exports.getDefaultTargets = exports.INSTRUCTIONS_FILE = exports.MARKER_END = exports.MARKER_START = exports.unsyncMcpServerFromKiro = exports.syncMcpServerToKiro = exports.unsyncMcpServerFromCodex = exports.syncMcpServerToCodex = exports.unsyncMcpServerFromCursor = exports.syncMcpServerToCursor = void 0;
40
40
  exports.extractSkillEntries = extractSkillEntries;
41
+ exports.syncSkillsToAgent = syncSkillsToAgent;
42
+ exports.unsyncSkillsFromAgent = unsyncSkillsFromAgent;
41
43
  exports.syncSkillsToClaudeCode = syncSkillsToClaudeCode;
42
44
  exports.unsyncSkillsFromClaudeCode = unsyncSkillsFromClaudeCode;
43
45
  exports.syncSkillsToGeminiCLI = syncSkillsToGeminiCLI;
@@ -46,31 +48,18 @@ exports.syncSkillsToWindsurf = syncSkillsToWindsurf;
46
48
  exports.unsyncSkillsFromWindsurf = unsyncSkillsFromWindsurf;
47
49
  exports.syncSkillsToAntigravityCLI = syncSkillsToAntigravityCLI;
48
50
  exports.unsyncSkillsFromAntigravityCLI = unsyncSkillsFromAntigravityCLI;
51
+ exports.syncSkillsToKiro = syncSkillsToKiro;
52
+ exports.unsyncSkillsFromKiro = unsyncSkillsFromKiro;
49
53
  exports.cleanOrphanSkills = cleanOrphanSkills;
50
- exports.syncMcpServerToCursor = syncMcpServerToCursor;
51
- exports.unsyncMcpServerFromCursor = unsyncMcpServerFromCursor;
52
- exports.syncMcpServerToCodex = syncMcpServerToCodex;
53
- exports.unsyncMcpServerFromCodex = unsyncMcpServerFromCodex;
54
54
  exports.syncSkillsToCursor = syncSkillsToCursor;
55
55
  exports.unsyncSkillsFromCursor = unsyncSkillsFromCursor;
56
- exports.syncSkillsToKiro = syncSkillsToKiro;
57
- exports.unsyncSkillsFromKiro = unsyncSkillsFromKiro;
58
- exports.syncMcpServerToKiro = syncMcpServerToKiro;
59
- exports.unsyncMcpServerFromKiro = unsyncMcpServerFromKiro;
60
- exports.getDefaultTargets = getDefaultTargets;
61
- exports.getInstructionsPath = getInstructionsPath;
62
- exports.buildInjectedSection = buildInjectedSection;
63
- exports.injectIntoFile = injectIntoFile;
64
- exports.removeFromFile = removeFromFile;
65
- exports.syncInstructions = syncInstructions;
66
- exports.unsyncInstructions = unsyncInstructions;
67
56
  const fs_1 = __importDefault(require("fs"));
68
57
  const os_1 = __importDefault(require("os"));
69
58
  const path_1 = __importDefault(require("path"));
70
59
  const toml = __importStar(require("smol-toml"));
71
- const config_1 = require("./config");
60
+ const sync_state_1 = require("./sync-state");
61
+ const agent_registry_1 = require("./agent-registry");
72
62
  const logger_1 = require("./logger");
73
- const package_json_1 = __importDefault(require("../package.json"));
74
63
  /**
75
64
  * Extracts SkillEntry objects from discovered packages by scanning for SKILL.md prompts.
76
65
  */
@@ -92,40 +81,7 @@ function extractSkillEntries(packages) {
92
81
  }
93
82
  return skills;
94
83
  }
95
- const SYNCED_SKILLS_FILE = 'synced-skills.json';
96
- const AGENTBREW_EXTENSION_NAME = 'agentbrew';
97
84
  const CURSOR_SKILLS_INDEX_FILE = 'agentbrew-skills-index.md';
98
- function getSyncedSkillsPath(brewRoot) {
99
- return path_1.default.join(brewRoot ?? (0, config_1.getBrewRoot)(), SYNCED_SKILLS_FILE);
100
- }
101
- function loadSyncedState(brewRoot) {
102
- try {
103
- const raw = JSON.parse(fs_1.default.readFileSync(getSyncedSkillsPath(brewRoot), 'utf-8'));
104
- // Migrate old flat format: { skills: [...] } → { claude: [...] }
105
- if (raw.skills && !raw.claude) {
106
- return { claude: raw.skills, gemini: [], windsurf: [], cursor: false, cursorMcp: false, antigravity: [], codexMcp: false, kiro: [], kiroMcp: false };
107
- }
108
- return {
109
- claude: raw.claude ?? [],
110
- gemini: raw.gemini ?? [],
111
- windsurf: raw.windsurf ?? [],
112
- cursor: raw.cursor ?? false,
113
- cursorMcp: raw.cursorMcp ?? false,
114
- antigravity: raw.antigravity ?? [],
115
- codexMcp: raw.codexMcp ?? false,
116
- kiro: raw.kiro ?? [],
117
- kiroMcp: raw.kiroMcp ?? false,
118
- };
119
- }
120
- catch {
121
- return { claude: [], gemini: [], windsurf: [], cursor: false, cursorMcp: false, antigravity: [], codexMcp: false, kiro: [], kiroMcp: false };
122
- }
123
- }
124
- function saveSyncedState(state, brewRoot) {
125
- const p = getSyncedSkillsPath(brewRoot);
126
- fs_1.default.mkdirSync(path_1.default.dirname(p), { recursive: true });
127
- fs_1.default.writeFileSync(p, JSON.stringify(state, null, 2), 'utf-8');
128
- }
129
85
  function symlinkSkills(skills, targetDir, state, agentKey, brewRoot) {
130
86
  const results = [];
131
87
  const newEntries = [];
@@ -141,13 +97,19 @@ function symlinkSkills(skills, targetDir, state, agentKey, brewRoot) {
141
97
  fs_1.default.lstatSync(entryPath);
142
98
  exists = true;
143
99
  }
144
- catch { }
100
+ catch (e) {
101
+ if (e.code !== 'ENOENT')
102
+ logger_1.Logger.warn(`Unexpected error checking ${entryName}: ${e.message}`);
103
+ }
145
104
  if (exists) {
146
105
  let currentTarget = null;
147
106
  try {
148
107
  currentTarget = fs_1.default.readlinkSync(entryPath);
149
108
  }
150
- catch { }
109
+ catch (e) {
110
+ if (e.code !== 'ENOENT')
111
+ logger_1.Logger.warn(`Unexpected error checking ${entryName}: ${e.message}`);
112
+ }
151
113
  if (currentTarget === null) {
152
114
  // Not a symlink — not created by AgentBrew, leave it alone
153
115
  results.push({ entryName, status: 'skipped', note: 'Path exists and is not a symlink' });
@@ -162,7 +124,9 @@ function symlinkSkills(skills, targetDir, state, agentKey, brewRoot) {
162
124
  try {
163
125
  fs_1.default.rmSync(entryPath, { force: true });
164
126
  }
165
- catch { }
127
+ catch (e) {
128
+ logger_1.Logger.warn(`Could not remove ${entryPath}: ${e.message}`);
129
+ }
166
130
  }
167
131
  try {
168
132
  fs_1.default.symlinkSync(skill.skillDir, entryPath);
@@ -174,7 +138,7 @@ function symlinkSkills(skills, targetDir, state, agentKey, brewRoot) {
174
138
  }
175
139
  }
176
140
  state[agentKey] = [...new Set([...state[agentKey], ...newEntries])];
177
- saveSyncedState(state, brewRoot);
141
+ (0, sync_state_1.saveSyncedState)(state, brewRoot);
178
142
  return results;
179
143
  }
180
144
  function removeTrackedSymlinks(tracked, skillsDir) {
@@ -186,7 +150,10 @@ function removeTrackedSymlinks(tracked, skillsDir) {
186
150
  fs_1.default.lstatSync(entryPath);
187
151
  exists = true;
188
152
  }
189
- catch { }
153
+ catch (e) {
154
+ if (e.code !== 'ENOENT')
155
+ logger_1.Logger.warn(`Unexpected error checking ${entryName}: ${e.message}`);
156
+ }
190
157
  if (!exists) {
191
158
  results.push({ entryName, status: 'skipped', note: 'Not found' });
192
159
  continue;
@@ -201,157 +168,68 @@ function removeTrackedSymlinks(tracked, skillsDir) {
201
168
  }
202
169
  return results;
203
170
  }
204
- // ─── Claude Code ────────────────────────────────────────────────────────────
205
171
  /**
206
- * Symlinks each skill directory into ~/.claude/skills/<pkgName>-<skillName>
207
- * so Claude Code can discover them as invocable skills.
172
+ * Generic skill sync for any agent in AGENT_SKILL_REGISTRY.
208
173
  */
209
- function syncSkillsToClaudeCode(skills, brewRoot) {
210
- const claudeDir = path_1.default.join(os_1.default.homedir(), '.claude');
211
- if (!fs_1.default.existsSync(claudeDir))
174
+ function syncSkillsToAgent(agent, skills, brewRoot) {
175
+ const rootDir = agent.agentRootDir?.();
176
+ if (rootDir && !fs_1.default.existsSync(rootDir))
212
177
  return [];
213
- const skillsDir = path_1.default.join(claudeDir, 'skills');
178
+ const skillsDir = agent.skillsDir();
214
179
  fs_1.default.mkdirSync(skillsDir, { recursive: true });
215
- const state = loadSyncedState(brewRoot);
216
- return symlinkSkills(skills, skillsDir, state, 'claude', brewRoot);
180
+ const state = (0, sync_state_1.loadSyncedState)(brewRoot);
181
+ const results = symlinkSkills(skills, skillsDir, state, agent.key, brewRoot);
182
+ if (results.some(r => r.status === 'linked' || r.status === 'already_exists')) {
183
+ agent.onAfterSync?.();
184
+ }
185
+ return results;
217
186
  }
218
187
  /**
219
- * Removes all skill symlinks previously created by syncSkillsToClaudeCode.
188
+ * Generic skill unsync for any agent in AGENT_SKILL_REGISTRY.
220
189
  */
221
- function unsyncSkillsFromClaudeCode(brewRoot) {
222
- const skillsDir = path_1.default.join(os_1.default.homedir(), '.claude', 'skills');
223
- const state = loadSyncedState(brewRoot);
224
- const results = removeTrackedSymlinks(state.claude, skillsDir);
225
- state.claude = [];
226
- saveSyncedState(state, brewRoot);
190
+ function unsyncSkillsFromAgent(agent, brewRoot) {
191
+ const skillsDir = agent.skillsDir();
192
+ const state = (0, sync_state_1.loadSyncedState)(brewRoot);
193
+ const results = removeTrackedSymlinks(state[agent.key], skillsDir);
194
+ state[agent.key] = [];
195
+ (0, sync_state_1.saveSyncedState)(state, brewRoot);
196
+ agent.onAfterUnsync?.();
227
197
  return results;
228
198
  }
229
- // ─── Gemini CLI ──────────────────────────────────────────────────────────────
230
- /**
231
- * Registers skills with Gemini CLI by creating an agentbrew extension at
232
- * ~/.gemini/extensions/agentbrew/ and symlinking each skill's directory into
233
- * ~/.gemini/extensions/agentbrew/skills/<pkgName>-<skillName>.
234
- */
235
- function syncSkillsToGeminiCLI(skills, brewRoot) {
236
- const geminiDir = path_1.default.join(os_1.default.homedir(), '.gemini');
237
- if (!fs_1.default.existsSync(geminiDir))
238
- return [];
239
- const extensionDir = path_1.default.join(geminiDir, 'extensions', AGENTBREW_EXTENSION_NAME);
240
- const skillsDir = path_1.default.join(extensionDir, 'skills');
241
- fs_1.default.mkdirSync(skillsDir, { recursive: true });
242
- // Write extension manifest (we own this file)
243
- const manifestPath = path_1.default.join(extensionDir, 'gemini-extension.json');
244
- fs_1.default.writeFileSync(manifestPath, JSON.stringify({ name: AGENTBREW_EXTENSION_NAME, version: package_json_1.default.version }, null, 2), 'utf-8');
245
- // Enable extension in extension-enablement.json
246
- _enableGeminiExtension(geminiDir);
247
- const state = loadSyncedState(brewRoot);
248
- return symlinkSkills(skills, skillsDir, state, 'gemini', brewRoot);
199
+ // ─── Claude Code ────────────────────────────────────────────────────────────
200
+ function syncSkillsToClaudeCode(skills, brewRoot) {
201
+ return syncSkillsToAgent(agent_registry_1.AGENT_SKILL_REGISTRY.find(a => a.key === 'claude'), skills, brewRoot);
249
202
  }
250
- /**
251
- * Removes Gemini CLI skill symlinks, the extension manifest, and the
252
- * agentbrew extension entry from extension-enablement.json.
253
- */
254
- function unsyncSkillsFromGeminiCLI(brewRoot) {
255
- const geminiDir = path_1.default.join(os_1.default.homedir(), '.gemini');
256
- const extensionDir = path_1.default.join(geminiDir, 'extensions', AGENTBREW_EXTENSION_NAME);
257
- const skillsDir = path_1.default.join(extensionDir, 'skills');
258
- const state = loadSyncedState(brewRoot);
259
- const results = removeTrackedSymlinks(state.gemini, skillsDir);
260
- // Clean up extension dir (best-effort; ignores non-empty)
261
- try {
262
- fs_1.default.rmSync(path_1.default.join(extensionDir, 'gemini-extension.json'), { force: true });
263
- }
264
- catch { }
265
- try {
266
- fs_1.default.rmdirSync(skillsDir);
267
- }
268
- catch { }
269
- try {
270
- fs_1.default.rmdirSync(extensionDir);
271
- }
272
- catch { }
273
- _disableGeminiExtension(geminiDir);
274
- state.gemini = [];
275
- saveSyncedState(state, brewRoot);
276
- return results;
203
+ function unsyncSkillsFromClaudeCode(brewRoot) {
204
+ return unsyncSkillsFromAgent(agent_registry_1.AGENT_SKILL_REGISTRY.find(a => a.key === 'claude'), brewRoot);
277
205
  }
278
- function _enableGeminiExtension(geminiDir) {
279
- const enablementPath = path_1.default.join(geminiDir, 'extensions', 'extension-enablement.json');
280
- let data = {};
281
- try {
282
- data = JSON.parse(fs_1.default.readFileSync(enablementPath, 'utf-8'));
283
- }
284
- catch { }
285
- if (!data[AGENTBREW_EXTENSION_NAME]) {
286
- data[AGENTBREW_EXTENSION_NAME] = { overrides: [`${os_1.default.homedir()}/*`] };
287
- fs_1.default.writeFileSync(enablementPath, JSON.stringify(data, null, 2), 'utf-8');
288
- }
206
+ // ─── Gemini CLI ───────────────────────────────────────────────────────────────
207
+ function syncSkillsToGeminiCLI(skills, brewRoot) {
208
+ return syncSkillsToAgent(agent_registry_1.AGENT_SKILL_REGISTRY.find(a => a.key === 'gemini'), skills, brewRoot);
289
209
  }
290
- function _disableGeminiExtension(geminiDir) {
291
- const enablementPath = path_1.default.join(geminiDir, 'extensions', 'extension-enablement.json');
292
- try {
293
- const data = JSON.parse(fs_1.default.readFileSync(enablementPath, 'utf-8'));
294
- if (AGENTBREW_EXTENSION_NAME in data) {
295
- delete data[AGENTBREW_EXTENSION_NAME];
296
- if (Object.keys(data).length === 0) {
297
- fs_1.default.rmSync(enablementPath, { force: true });
298
- }
299
- else {
300
- fs_1.default.writeFileSync(enablementPath, JSON.stringify(data, null, 2), 'utf-8');
301
- }
302
- }
303
- }
304
- catch { }
210
+ function unsyncSkillsFromGeminiCLI(brewRoot) {
211
+ return unsyncSkillsFromAgent(agent_registry_1.AGENT_SKILL_REGISTRY.find(a => a.key === 'gemini'), brewRoot);
305
212
  }
306
- // ─── Windsurf ────────────────────────────────────────────────────────────────
307
- /**
308
- * Symlinks each skill directory into ~/.codeium/windsurf/skills/<pkgName>-<skillName>
309
- * so Windsurf can discover them.
310
- */
213
+ // ─── Windsurf ─────────────────────────────────────────────────────────────────
311
214
  function syncSkillsToWindsurf(skills, brewRoot) {
312
- const windsurfDir = path_1.default.join(os_1.default.homedir(), '.codeium', 'windsurf');
313
- if (!fs_1.default.existsSync(windsurfDir))
314
- return [];
315
- const skillsDir = path_1.default.join(windsurfDir, 'skills');
316
- fs_1.default.mkdirSync(skillsDir, { recursive: true });
317
- const state = loadSyncedState(brewRoot);
318
- return symlinkSkills(skills, skillsDir, state, 'windsurf', brewRoot);
215
+ return syncSkillsToAgent(agent_registry_1.AGENT_SKILL_REGISTRY.find(a => a.key === 'windsurf'), skills, brewRoot);
319
216
  }
320
- /**
321
- * Removes all Windsurf skill symlinks previously created by syncSkillsToWindsurf.
322
- */
323
217
  function unsyncSkillsFromWindsurf(brewRoot) {
324
- const skillsDir = path_1.default.join(os_1.default.homedir(), '.codeium', 'windsurf', 'skills');
325
- const state = loadSyncedState(brewRoot);
326
- const results = removeTrackedSymlinks(state.windsurf, skillsDir);
327
- state.windsurf = [];
328
- saveSyncedState(state, brewRoot);
329
- return results;
218
+ return unsyncSkillsFromAgent(agent_registry_1.AGENT_SKILL_REGISTRY.find(a => a.key === 'windsurf'), brewRoot);
330
219
  }
331
- // ─── Antigravity CLI ─────────────────────────────────────────────────────────
332
- /**
333
- * Symlinks each skill directory into ~/.gemini/antigravity-cli/skills/<pkgName>-<skillName>
334
- * so Antigravity CLI can auto-discover them.
335
- */
220
+ // ─── Antigravity CLI ──────────────────────────────────────────────────────────
336
221
  function syncSkillsToAntigravityCLI(skills, brewRoot) {
337
- const antigravityDir = path_1.default.join(os_1.default.homedir(), '.gemini', 'antigravity-cli');
338
- if (!fs_1.default.existsSync(antigravityDir))
339
- return [];
340
- const skillsDir = path_1.default.join(antigravityDir, 'skills');
341
- fs_1.default.mkdirSync(skillsDir, { recursive: true });
342
- const state = loadSyncedState(brewRoot);
343
- return symlinkSkills(skills, skillsDir, state, 'antigravity', brewRoot);
222
+ return syncSkillsToAgent(agent_registry_1.AGENT_SKILL_REGISTRY.find(a => a.key === 'antigravity'), skills, brewRoot);
344
223
  }
345
- /**
346
- * Removes all Antigravity CLI skill symlinks previously created by syncSkillsToAntigravityCLI.
347
- */
348
224
  function unsyncSkillsFromAntigravityCLI(brewRoot) {
349
- const skillsDir = path_1.default.join(os_1.default.homedir(), '.gemini', 'antigravity-cli', 'skills');
350
- const state = loadSyncedState(brewRoot);
351
- const results = removeTrackedSymlinks(state.antigravity, skillsDir);
352
- state.antigravity = [];
353
- saveSyncedState(state, brewRoot);
354
- return results;
225
+ return unsyncSkillsFromAgent(agent_registry_1.AGENT_SKILL_REGISTRY.find(a => a.key === 'antigravity'), brewRoot);
226
+ }
227
+ // ─── Kiro ─────────────────────────────────────────────────────────────────────
228
+ function syncSkillsToKiro(skills, brewRoot) {
229
+ return syncSkillsToAgent(agent_registry_1.AGENT_SKILL_REGISTRY.find(a => a.key === 'kiro'), skills, brewRoot);
230
+ }
231
+ function unsyncSkillsFromKiro(brewRoot) {
232
+ return unsyncSkillsFromAgent(agent_registry_1.AGENT_SKILL_REGISTRY.find(a => a.key === 'kiro'), brewRoot);
355
233
  }
356
234
  // ─── Orphan cleanup ──────────────────────────────────────────────────────────
357
235
  /**
@@ -359,24 +237,21 @@ function unsyncSkillsFromAntigravityCLI(brewRoot) {
359
237
  * Call after `agentbrew uninstall` to prevent stale entries.
360
238
  */
361
239
  function cleanOrphanSkills(brewRoot) {
362
- const state = loadSyncedState(brewRoot);
240
+ const state = (0, sync_state_1.loadSyncedState)(brewRoot);
363
241
  const results = [];
364
- const agentDirs = [
365
- { key: 'claude', dir: path_1.default.join(os_1.default.homedir(), '.claude', 'skills') },
366
- { key: 'gemini', dir: path_1.default.join(os_1.default.homedir(), '.gemini', 'extensions', AGENTBREW_EXTENSION_NAME, 'skills') },
367
- { key: 'windsurf', dir: path_1.default.join(os_1.default.homedir(), '.codeium', 'windsurf', 'skills') },
368
- { key: 'antigravity', dir: path_1.default.join(os_1.default.homedir(), '.gemini', 'antigravity-cli', 'skills') },
369
- { key: 'kiro', dir: path_1.default.join(os_1.default.homedir(), '.kiro', 'skills') },
370
- ];
371
- for (const { key, dir } of agentDirs) {
242
+ for (const agent of agent_registry_1.AGENT_SKILL_REGISTRY) {
243
+ const dir = agent.skillsDir();
372
244
  const remaining = [];
373
- for (const entryName of state[key]) {
245
+ for (const entryName of state[agent.key]) {
374
246
  const entryPath = path_1.default.join(dir, entryName);
375
247
  let symlinkTarget = null;
376
248
  try {
377
249
  symlinkTarget = fs_1.default.readlinkSync(entryPath);
378
250
  }
379
- catch { }
251
+ catch (e) {
252
+ if (e.code !== 'ENOENT')
253
+ logger_1.Logger.warn(`Unexpected error checking ${entryName}: ${e.message}`);
254
+ }
380
255
  if (symlinkTarget !== null && !fs_1.default.existsSync(symlinkTarget)) {
381
256
  try {
382
257
  fs_1.default.rmSync(entryPath, { force: true });
@@ -391,7 +266,7 @@ function cleanOrphanSkills(brewRoot) {
391
266
  remaining.push(entryName);
392
267
  }
393
268
  }
394
- state[key] = remaining;
269
+ state[agent.key] = remaining;
395
270
  }
396
271
  // Handle Cursor index file: parse referenced SKILL.md paths and remove the file if any are stale
397
272
  if (state.cursor) {
@@ -440,190 +315,9 @@ function cleanOrphanSkills(brewRoot) {
440
315
  if (!kiroEntryPresent)
441
316
  state.kiroMcp = false;
442
317
  }
443
- saveSyncedState(state, brewRoot);
318
+ (0, sync_state_1.saveSyncedState)(state, brewRoot);
444
319
  return results;
445
320
  }
446
- // ─── Cursor MCP server registration ─────────────────────────────────────────
447
- const CURSOR_MCP_ENTRY = 'agentbrew';
448
- /**
449
- * Adds agentbrew to ~/.cursor/mcp.json so Cursor can discover MCP tools directly.
450
- * Merges into any existing config without disturbing other servers.
451
- */
452
- function syncMcpServerToCursor(brewRoot) {
453
- const cursorDir = path_1.default.join(os_1.default.homedir(), '.cursor');
454
- if (!fs_1.default.existsSync(cursorDir))
455
- return [];
456
- const mcpJsonPath = path_1.default.join(cursorDir, 'mcp.json');
457
- const entryName = 'agentbrew (Cursor MCP)';
458
- let config = {};
459
- try {
460
- config = JSON.parse(fs_1.default.readFileSync(mcpJsonPath, 'utf-8'));
461
- }
462
- catch { }
463
- const mcpServers = config.mcpServers ?? {};
464
- const existing = mcpServers[CURSOR_MCP_ENTRY];
465
- if (existing?.command === 'agentbrew') {
466
- const state = loadSyncedState(brewRoot);
467
- state.cursorMcp = true;
468
- saveSyncedState(state, brewRoot);
469
- return [{ entryName, status: 'already_exists', path: mcpJsonPath }];
470
- }
471
- config.mcpServers = { ...mcpServers, [CURSOR_MCP_ENTRY]: { command: 'agentbrew' } };
472
- try {
473
- fs_1.default.writeFileSync(mcpJsonPath, JSON.stringify(config, null, 2), 'utf-8');
474
- const state = loadSyncedState(brewRoot);
475
- state.cursorMcp = true;
476
- saveSyncedState(state, brewRoot);
477
- return [{ entryName, status: 'linked', path: mcpJsonPath }];
478
- }
479
- catch (e) {
480
- return [{ entryName, status: 'error', note: e.message }];
481
- }
482
- }
483
- /**
484
- * Removes the agentbrew entry from ~/.cursor/mcp.json.
485
- * Leaves other servers intact; removes the file only if it becomes empty.
486
- */
487
- function unsyncMcpServerFromCursor(brewRoot) {
488
- const state = loadSyncedState(brewRoot);
489
- if (!state.cursorMcp)
490
- return [];
491
- const mcpJsonPath = path_1.default.join(os_1.default.homedir(), '.cursor', 'mcp.json');
492
- const entryName = 'agentbrew (Cursor MCP)';
493
- let config = {};
494
- try {
495
- config = JSON.parse(fs_1.default.readFileSync(mcpJsonPath, 'utf-8'));
496
- }
497
- catch {
498
- state.cursorMcp = false;
499
- saveSyncedState(state, brewRoot);
500
- return [{ entryName, status: 'skipped', note: 'Not found' }];
501
- }
502
- if (!config.mcpServers?.[CURSOR_MCP_ENTRY]) {
503
- state.cursorMcp = false;
504
- saveSyncedState(state, brewRoot);
505
- return [{ entryName, status: 'skipped', note: 'Not found' }];
506
- }
507
- delete config.mcpServers[CURSOR_MCP_ENTRY];
508
- if (Object.keys(config.mcpServers).length === 0)
509
- delete config.mcpServers;
510
- try {
511
- if (Object.keys(config).length === 0) {
512
- fs_1.default.rmSync(mcpJsonPath, { force: true });
513
- }
514
- else {
515
- fs_1.default.writeFileSync(mcpJsonPath, JSON.stringify(config, null, 2), 'utf-8');
516
- }
517
- state.cursorMcp = false;
518
- saveSyncedState(state, brewRoot);
519
- return [{ entryName, status: 'removed', path: mcpJsonPath }];
520
- }
521
- catch (e) {
522
- return [{ entryName, status: 'error', note: e.message }];
523
- }
524
- }
525
- // ─── Codex MCP server registration ──────────────────────────────────────────
526
- function _removeTomlSection(content, sectionHeader) {
527
- const lines = content.split('\n');
528
- const headerLine = `[${sectionHeader}]`;
529
- const subHeaderPrefix = `[${sectionHeader}.`;
530
- const startIdx = lines.findIndex(l => l.trim() === headerLine);
531
- if (startIdx === -1)
532
- return content;
533
- let endIdx = lines.length;
534
- for (let i = startIdx + 1; i < lines.length; i++) {
535
- const trimmed = lines[i].trim();
536
- // Stop at sibling or parent sections, but continue through subsections of this section
537
- if (trimmed.startsWith('[') && !trimmed.startsWith(subHeaderPrefix)) {
538
- endIdx = i;
539
- break;
540
- }
541
- }
542
- // Also absorb a preceding blank line
543
- let removeFrom = startIdx;
544
- if (removeFrom > 0 && lines[removeFrom - 1].trim() === '')
545
- removeFrom--;
546
- return [...lines.slice(0, removeFrom), ...lines.slice(endIdx)].join('\n');
547
- }
548
- /**
549
- * Adds agentbrew to ~/.codex/config.toml so Codex CLI can discover MCP tools directly.
550
- * Appends the [mcp_servers.agentbrew] section without disturbing existing content.
551
- */
552
- function syncMcpServerToCodex(brewRoot) {
553
- const codexDir = path_1.default.join(os_1.default.homedir(), '.codex');
554
- if (!fs_1.default.existsSync(codexDir))
555
- return [];
556
- const configPath = path_1.default.join(codexDir, 'config.toml');
557
- const entryName = 'agentbrew (Codex MCP)';
558
- let raw = '';
559
- try {
560
- raw = fs_1.default.readFileSync(configPath, 'utf-8');
561
- }
562
- catch { }
563
- // Check if already registered with the right command
564
- try {
565
- const parsed = toml.parse(raw);
566
- if (parsed?.mcp_servers?.agentbrew?.command === 'agentbrew') {
567
- const state = loadSyncedState(brewRoot);
568
- state.codexMcp = true;
569
- saveSyncedState(state, brewRoot);
570
- return [{ entryName, status: 'already_exists', path: configPath }];
571
- }
572
- }
573
- catch (e) {
574
- logger_1.Logger.warn(`config.toml at ${configPath} has invalid TOML syntax: ${e.message}. Attempting text-based repair.`);
575
- }
576
- // Remove any stale entry (e.g. wrong command) before re-adding to avoid duplicate TOML table headers
577
- const cleaned = _removeTomlSection(raw, 'mcp_servers.agentbrew');
578
- const sep = cleaned.length > 0 && !cleaned.endsWith('\n') ? '\n' : '';
579
- const newContent = cleaned + sep + '\n[mcp_servers.agentbrew]\ncommand = "agentbrew"\n';
580
- try {
581
- fs_1.default.writeFileSync(configPath, newContent, 'utf-8');
582
- const state = loadSyncedState(brewRoot);
583
- state.codexMcp = true;
584
- saveSyncedState(state, brewRoot);
585
- return [{ entryName, status: 'linked', path: configPath }];
586
- }
587
- catch (e) {
588
- return [{ entryName, status: 'error', note: e.message }];
589
- }
590
- }
591
- /**
592
- * Removes the agentbrew entry from ~/.codex/config.toml.
593
- * Leaves the file in place; other sections are untouched.
594
- */
595
- function unsyncMcpServerFromCodex(brewRoot) {
596
- const state = loadSyncedState(brewRoot);
597
- if (!state.codexMcp)
598
- return [];
599
- const configPath = path_1.default.join(os_1.default.homedir(), '.codex', 'config.toml');
600
- const entryName = 'agentbrew (Codex MCP)';
601
- let raw;
602
- try {
603
- raw = fs_1.default.readFileSync(configPath, 'utf-8');
604
- }
605
- catch {
606
- state.codexMcp = false;
607
- saveSyncedState(state, brewRoot);
608
- return [{ entryName, status: 'skipped', note: 'Not found' }];
609
- }
610
- const cleaned = _removeTomlSection(raw, 'mcp_servers.agentbrew');
611
- if (cleaned === raw) {
612
- state.codexMcp = false;
613
- saveSyncedState(state, brewRoot);
614
- return [{ entryName, status: 'skipped', note: 'Not found' }];
615
- }
616
- try {
617
- const trimmed = cleaned.trimEnd();
618
- fs_1.default.writeFileSync(configPath, trimmed ? trimmed + '\n' : '', 'utf-8');
619
- state.codexMcp = false;
620
- saveSyncedState(state, brewRoot);
621
- return [{ entryName, status: 'removed', path: configPath }];
622
- }
623
- catch (e) {
624
- return [{ entryName, status: 'error', note: e.message }];
625
- }
626
- }
627
321
  // ─── Cursor ──────────────────────────────────────────────────────────────────
628
322
  function buildCursorSkillsIndex(skills) {
629
323
  const lines = [
@@ -659,11 +353,11 @@ function syncSkillsToCursor(skills, brewRoot) {
659
353
  fs_1.default.mkdirSync(rulesDir, { recursive: true });
660
354
  const indexPath = path_1.default.join(rulesDir, CURSOR_SKILLS_INDEX_FILE);
661
355
  const content = buildCursorSkillsIndex(skills);
662
- const state = loadSyncedState(brewRoot);
356
+ const state = (0, sync_state_1.loadSyncedState)(brewRoot);
663
357
  try {
664
358
  fs_1.default.writeFileSync(indexPath, content, 'utf-8');
665
359
  state.cursor = true;
666
- saveSyncedState(state, brewRoot);
360
+ (0, sync_state_1.saveSyncedState)(state, brewRoot);
667
361
  return [{ entryName: CURSOR_SKILLS_INDEX_FILE, status: 'linked', path: indexPath }];
668
362
  }
669
363
  catch (e) {
@@ -674,7 +368,7 @@ function syncSkillsToCursor(skills, brewRoot) {
674
368
  * Removes the AgentBrew skills index file from ~/.cursor/rules/.
675
369
  */
676
370
  function unsyncSkillsFromCursor(brewRoot) {
677
- const state = loadSyncedState(brewRoot);
371
+ const state = (0, sync_state_1.loadSyncedState)(brewRoot);
678
372
  if (!state.cursor)
679
373
  return [];
680
374
  const indexPath = path_1.default.join(os_1.default.homedir(), '.cursor', 'rules', CURSOR_SKILLS_INDEX_FILE);
@@ -683,322 +377,43 @@ function unsyncSkillsFromCursor(brewRoot) {
683
377
  fs_1.default.lstatSync(indexPath);
684
378
  exists = true;
685
379
  }
686
- catch { }
380
+ catch (e) {
381
+ if (e.code !== 'ENOENT')
382
+ logger_1.Logger.warn(`Unexpected error checking ${CURSOR_SKILLS_INDEX_FILE}: ${e.message}`);
383
+ }
687
384
  if (!exists) {
688
385
  state.cursor = false;
689
- saveSyncedState(state, brewRoot);
386
+ (0, sync_state_1.saveSyncedState)(state, brewRoot);
690
387
  return [{ entryName: CURSOR_SKILLS_INDEX_FILE, status: 'skipped', note: 'Not found' }];
691
388
  }
692
389
  try {
693
390
  fs_1.default.rmSync(indexPath, { force: true });
694
391
  state.cursor = false;
695
- saveSyncedState(state, brewRoot);
392
+ (0, sync_state_1.saveSyncedState)(state, brewRoot);
696
393
  return [{ entryName: CURSOR_SKILLS_INDEX_FILE, status: 'removed', path: indexPath }];
697
394
  }
698
395
  catch (e) {
699
396
  return [{ entryName: CURSOR_SKILLS_INDEX_FILE, status: 'error', note: e.message }];
700
397
  }
701
398
  }
702
- // ─── Kiro ────────────────────────────────────────────────────────────────────
703
- /**
704
- * Symlinks each skill directory into ~/.kiro/skills/<pkgName>-<skillName>
705
- * so Kiro can discover them as invocable skills.
706
- */
707
- function syncSkillsToKiro(skills, brewRoot) {
708
- const kiroDir = path_1.default.join(os_1.default.homedir(), '.kiro');
709
- if (!fs_1.default.existsSync(kiroDir))
710
- return [];
711
- const skillsDir = path_1.default.join(kiroDir, 'skills');
712
- fs_1.default.mkdirSync(skillsDir, { recursive: true });
713
- const state = loadSyncedState(brewRoot);
714
- return symlinkSkills(skills, skillsDir, state, 'kiro', brewRoot);
715
- }
716
- /**
717
- * Removes all Kiro skill symlinks previously created by syncSkillsToKiro.
718
- */
719
- function unsyncSkillsFromKiro(brewRoot) {
720
- const skillsDir = path_1.default.join(os_1.default.homedir(), '.kiro', 'skills');
721
- const state = loadSyncedState(brewRoot);
722
- const results = removeTrackedSymlinks(state.kiro, skillsDir);
723
- state.kiro = [];
724
- saveSyncedState(state, brewRoot);
725
- return results;
726
- }
727
- // ─── Kiro MCP server registration ────────────────────────────────────────────
728
- const KIRO_MCP_ENTRY = 'agentbrew';
729
- /**
730
- * Adds agentbrew to ~/.kiro/settings/mcp.json so Kiro can discover MCP tools directly.
731
- * Merges into any existing config without disturbing other servers.
732
- */
733
- function syncMcpServerToKiro(brewRoot) {
734
- const kiroDir = path_1.default.join(os_1.default.homedir(), '.kiro');
735
- if (!fs_1.default.existsSync(kiroDir))
736
- return [];
737
- const settingsDir = path_1.default.join(kiroDir, 'settings');
738
- fs_1.default.mkdirSync(settingsDir, { recursive: true });
739
- const mcpJsonPath = path_1.default.join(settingsDir, 'mcp.json');
740
- const entryName = 'agentbrew (Kiro MCP)';
741
- let config = {};
742
- try {
743
- config = JSON.parse(fs_1.default.readFileSync(mcpJsonPath, 'utf-8'));
744
- }
745
- catch { }
746
- const mcpServers = config.mcpServers ?? {};
747
- const existing = mcpServers[KIRO_MCP_ENTRY];
748
- if (existing?.command === 'agentbrew') {
749
- const state = loadSyncedState(brewRoot);
750
- state.kiroMcp = true;
751
- saveSyncedState(state, brewRoot);
752
- return [{ entryName, status: 'already_exists', path: mcpJsonPath }];
753
- }
754
- config.mcpServers = { ...mcpServers, [KIRO_MCP_ENTRY]: { command: 'agentbrew' } };
755
- try {
756
- fs_1.default.writeFileSync(mcpJsonPath, JSON.stringify(config, null, 2), 'utf-8');
757
- const state = loadSyncedState(brewRoot);
758
- state.kiroMcp = true;
759
- saveSyncedState(state, brewRoot);
760
- return [{ entryName, status: 'linked', path: mcpJsonPath }];
761
- }
762
- catch (e) {
763
- return [{ entryName, status: 'error', note: e.message }];
764
- }
765
- }
766
- /**
767
- * Removes the agentbrew entry from ~/.kiro/settings/mcp.json.
768
- * Leaves other servers intact; removes the file only if it becomes empty.
769
- */
770
- function unsyncMcpServerFromKiro(brewRoot) {
771
- const state = loadSyncedState(brewRoot);
772
- if (!state.kiroMcp)
773
- return [];
774
- const mcpJsonPath = path_1.default.join(os_1.default.homedir(), '.kiro', 'settings', 'mcp.json');
775
- const entryName = 'agentbrew (Kiro MCP)';
776
- let config = {};
777
- try {
778
- config = JSON.parse(fs_1.default.readFileSync(mcpJsonPath, 'utf-8'));
779
- }
780
- catch {
781
- state.kiroMcp = false;
782
- saveSyncedState(state, brewRoot);
783
- return [{ entryName, status: 'skipped', note: 'Not found' }];
784
- }
785
- if (!config.mcpServers?.[KIRO_MCP_ENTRY]) {
786
- state.kiroMcp = false;
787
- saveSyncedState(state, brewRoot);
788
- return [{ entryName, status: 'skipped', note: 'Not found' }];
789
- }
790
- delete config.mcpServers[KIRO_MCP_ENTRY];
791
- if (Object.keys(config.mcpServers).length === 0)
792
- delete config.mcpServers;
793
- try {
794
- if (Object.keys(config).length === 0) {
795
- fs_1.default.rmSync(mcpJsonPath, { force: true });
796
- }
797
- else {
798
- fs_1.default.writeFileSync(mcpJsonPath, JSON.stringify(config, null, 2), 'utf-8');
799
- }
800
- state.kiroMcp = false;
801
- saveSyncedState(state, brewRoot);
802
- return [{ entryName, status: 'removed', path: mcpJsonPath }];
803
- }
804
- catch (e) {
805
- return [{ entryName, status: 'error', note: e.message }];
806
- }
807
- }
808
- // ─── Instruction sync (unchanged) ───────────────────────────────────────────
809
- exports.MARKER_START = '<!-- agentbrew:shared:start -->';
810
- exports.MARKER_END = '<!-- agentbrew:shared:end -->';
811
- exports.INSTRUCTIONS_FILE = 'INSTRUCTIONS.md';
812
- const EXAMPLE_INSTRUCTIONS = `# AgentBrew Shared Instructions
813
-
814
- These instructions are shared across all your AI agents via AgentBrew.
815
- Edit this file and run \`agentbrew sync\` to push updates to all agent configs.
816
-
817
- ## Example: API Usage Policy
818
- - Always use Context7 (the \`context7\` MCP tool) to fetch live API documentation
819
- before writing code that calls an external library. This prevents using stale or
820
- hallucinated function signatures.
821
-
822
- ## Notes
823
- - This file is global. For project-specific context, add it directly to the
824
- project's CLAUDE.md or GEMINI.md — or reference project docs from within this file.
825
- `;
826
- function getDefaultTargets() {
827
- const home = os_1.default.homedir();
828
- return [
829
- {
830
- name: 'Claude Code',
831
- configPath: path_1.default.join(home, '.claude', 'CLAUDE.md'),
832
- isFileOwned: false,
833
- },
834
- {
835
- name: 'Gemini CLI',
836
- configPath: path_1.default.join(home, '.gemini', 'GEMINI.md'),
837
- isFileOwned: false,
838
- },
839
- {
840
- name: 'OpenAI Codex CLI',
841
- // Codex CLI's canonical instruction file is AGENTS.md (instructions.md is a legacy fallback)
842
- configPath: path_1.default.join(home, '.codex', 'AGENTS.md'),
843
- isFileOwned: false,
844
- },
845
- {
846
- name: 'Cursor',
847
- // Cursor "User Rules" directory (Cursor 0.47+): each .md file in this dir is a global rule.
848
- // We own this specific file entirely — no markers needed.
849
- configPath: path_1.default.join(home, '.cursor', 'rules', 'agentbrew-shared.md'),
850
- isFileOwned: true,
851
- agentRootDir: path_1.default.join(home, '.cursor'),
852
- },
853
- {
854
- name: 'Windsurf',
855
- configPath: path_1.default.join(home, '.codeium', 'windsurf', 'memories', 'global_rules.md'),
856
- isFileOwned: false,
857
- },
858
- {
859
- name: 'Kiro',
860
- // Steering files in ~/.kiro/steering/ are auto-loaded in every Kiro interaction.
861
- // We own this file entirely and must place the frontmatter at the very top.
862
- configPath: path_1.default.join(home, '.kiro', 'steering', 'agentbrew-shared.md'),
863
- isFileOwned: true,
864
- frontmatter: '---\ninclusion: always\n---',
865
- agentRootDir: path_1.default.join(home, '.kiro'),
866
- },
867
- ];
868
- }
869
- function getInstructionsPath(brewRoot) {
870
- return path_1.default.join(brewRoot ?? (0, config_1.getBrewRoot)(), exports.INSTRUCTIONS_FILE);
871
- }
872
- function buildInjectedSection(content) {
873
- const warning = `> ⚠️ Managed by AgentBrew. Edit \`~/.agentbrew/INSTRUCTIONS.md\` and run \`agentbrew sync\` to update.`;
874
- return `${exports.MARKER_START}\n${warning}\n\n${content.trim()}\n${exports.MARKER_END}`;
875
- }
876
- /**
877
- * Injects (or updates) the agentbrew section in a file.
878
- * Creates the file and any parent directories if they don't exist.
879
- */
880
- function injectIntoFile(filePath, content) {
881
- const section = buildInjectedSection(content);
882
- if (!fs_1.default.existsSync(filePath)) {
883
- fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
884
- fs_1.default.writeFileSync(filePath, section + '\n', 'utf-8');
885
- return 'created';
886
- }
887
- const existing = fs_1.default.readFileSync(filePath, 'utf-8');
888
- const startIdx = existing.indexOf(exports.MARKER_START);
889
- const endIdx = existing.indexOf(exports.MARKER_END);
890
- if (startIdx !== -1 && endIdx !== -1) {
891
- const replaced = existing.substring(0, startIdx) +
892
- section +
893
- existing.substring(endIdx + exports.MARKER_END.length);
894
- if (replaced === existing)
895
- return 'unchanged';
896
- fs_1.default.writeFileSync(filePath, replaced, 'utf-8');
897
- return 'updated';
898
- }
899
- // No markers yet — append to end
900
- const appended = existing.trimEnd() + '\n\n' + section + '\n';
901
- fs_1.default.writeFileSync(filePath, appended, 'utf-8');
902
- return 'updated';
903
- }
904
- /**
905
- * Removes the agentbrew section from a file, leaving surrounding content intact.
906
- */
907
- function removeFromFile(filePath) {
908
- if (!fs_1.default.existsSync(filePath))
909
- return 'not_found';
910
- const existing = fs_1.default.readFileSync(filePath, 'utf-8');
911
- const startIdx = existing.indexOf(exports.MARKER_START);
912
- const endIdx = existing.indexOf(exports.MARKER_END);
913
- if (startIdx === -1 || endIdx === -1)
914
- return 'no_section';
915
- const before = existing.substring(0, startIdx).trimEnd();
916
- const after = existing.substring(endIdx + exports.MARKER_END.length).trimStart();
917
- let result = before;
918
- if (after)
919
- result += '\n\n' + after;
920
- result = result.trimEnd() + '\n';
921
- fs_1.default.writeFileSync(filePath, result, 'utf-8');
922
- return 'removed';
923
- }
924
- /**
925
- * Syncs ~/.agentbrew/INSTRUCTIONS.md into each target agent's global config file.
926
- * Accepts an optional `targets` override (used in tests).
927
- */
928
- function syncInstructions(targets, brewRoot) {
929
- const instructionsPath = getInstructionsPath(brewRoot);
930
- if (!fs_1.default.existsSync(instructionsPath)) {
931
- fs_1.default.mkdirSync(path_1.default.dirname(instructionsPath), { recursive: true });
932
- fs_1.default.writeFileSync(instructionsPath, EXAMPLE_INSTRUCTIONS, 'utf-8');
933
- return [];
934
- }
935
- const content = fs_1.default.readFileSync(instructionsPath, 'utf-8');
936
- const resolvedTargets = targets ?? getDefaultTargets();
937
- const results = [];
938
- for (const target of resolvedTargets) {
939
- if (target.configPath === null) {
940
- results.push({ agent: target.name, status: 'manual', note: target.manualInstructions });
941
- continue;
942
- }
943
- if (target.isFileOwned) {
944
- // Skip if the agent's root directory doesn't exist (agent not installed).
945
- // Without this guard, mkdirSync below would create the directory unconditionally.
946
- if (target.agentRootDir && !fs_1.default.existsSync(target.agentRootDir)) {
947
- results.push({ agent: target.name, status: 'skipped', note: 'Agent not installed (config directory not found)' });
948
- continue;
949
- }
950
- // We own this file entirely — write raw content, no markers needed.
951
- // unsync deletes the file; there is no surrounding user content to delimit around.
952
- const header = `> ⚠️ Managed by AgentBrew. Edit \`~/.agentbrew/INSTRUCTIONS.md\` and run \`agentbrew sync\` to update.\n`;
953
- const prefix = target.frontmatter ? target.frontmatter + '\n' : '';
954
- const fileContent = prefix + header + '\n' + content.trim() + '\n';
955
- fs_1.default.mkdirSync(path_1.default.dirname(target.configPath), { recursive: true });
956
- const existing = fs_1.default.existsSync(target.configPath)
957
- ? fs_1.default.readFileSync(target.configPath, 'utf-8')
958
- : null;
959
- if (existing === fileContent) {
960
- results.push({ agent: target.name, status: 'unchanged', path: target.configPath });
961
- }
962
- else {
963
- fs_1.default.writeFileSync(target.configPath, fileContent, 'utf-8');
964
- results.push({ agent: target.name, status: existing !== null ? 'updated' : 'created', path: target.configPath });
965
- }
966
- continue;
967
- }
968
- // Skip agents that aren't installed (config parent dir absent)
969
- if (!fs_1.default.existsSync(path_1.default.dirname(target.configPath))) {
970
- results.push({ agent: target.name, status: 'skipped', note: 'Agent not installed (config directory not found)' });
971
- continue;
972
- }
973
- const status = injectIntoFile(target.configPath, content);
974
- results.push({ agent: target.name, status, path: target.configPath });
975
- }
976
- return results;
977
- }
978
- /**
979
- * Removes the agentbrew section from all target agent config files.
980
- */
981
- function unsyncInstructions(targets) {
982
- const resolvedTargets = targets ?? getDefaultTargets();
983
- const results = [];
984
- for (const target of resolvedTargets) {
985
- if (target.configPath === null) {
986
- results.push({ agent: target.name, status: 'manual', note: 'Remove manually from your agent UI settings.' });
987
- continue;
988
- }
989
- if (target.isFileOwned) {
990
- if (fs_1.default.existsSync(target.configPath)) {
991
- fs_1.default.rmSync(target.configPath);
992
- results.push({ agent: target.name, status: 'removed', path: target.configPath });
993
- }
994
- else {
995
- results.push({ agent: target.name, status: 'not_found', path: target.configPath });
996
- }
997
- continue;
998
- }
999
- const status = removeFromFile(target.configPath);
1000
- results.push({ agent: target.name, status, path: target.configPath });
1001
- }
1002
- return results;
1003
- }
399
+ // ─── MCP registration ────────────────────────────────────────────────────────
400
+ var sync_mcp_1 = require("./sync-mcp");
401
+ Object.defineProperty(exports, "syncMcpServerToCursor", { enumerable: true, get: function () { return sync_mcp_1.syncMcpServerToCursor; } });
402
+ Object.defineProperty(exports, "unsyncMcpServerFromCursor", { enumerable: true, get: function () { return sync_mcp_1.unsyncMcpServerFromCursor; } });
403
+ Object.defineProperty(exports, "syncMcpServerToCodex", { enumerable: true, get: function () { return sync_mcp_1.syncMcpServerToCodex; } });
404
+ Object.defineProperty(exports, "unsyncMcpServerFromCodex", { enumerable: true, get: function () { return sync_mcp_1.unsyncMcpServerFromCodex; } });
405
+ Object.defineProperty(exports, "syncMcpServerToKiro", { enumerable: true, get: function () { return sync_mcp_1.syncMcpServerToKiro; } });
406
+ Object.defineProperty(exports, "unsyncMcpServerFromKiro", { enumerable: true, get: function () { return sync_mcp_1.unsyncMcpServerFromKiro; } });
407
+ // ─── Instruction sync ────────────────────────────────────────────────────────
408
+ var sync_instructions_1 = require("./sync-instructions");
409
+ Object.defineProperty(exports, "MARKER_START", { enumerable: true, get: function () { return sync_instructions_1.MARKER_START; } });
410
+ Object.defineProperty(exports, "MARKER_END", { enumerable: true, get: function () { return sync_instructions_1.MARKER_END; } });
411
+ Object.defineProperty(exports, "INSTRUCTIONS_FILE", { enumerable: true, get: function () { return sync_instructions_1.INSTRUCTIONS_FILE; } });
412
+ Object.defineProperty(exports, "getDefaultTargets", { enumerable: true, get: function () { return sync_instructions_1.getDefaultTargets; } });
413
+ Object.defineProperty(exports, "getInstructionsPath", { enumerable: true, get: function () { return sync_instructions_1.getInstructionsPath; } });
414
+ Object.defineProperty(exports, "buildInjectedSection", { enumerable: true, get: function () { return sync_instructions_1.buildInjectedSection; } });
415
+ Object.defineProperty(exports, "injectIntoFile", { enumerable: true, get: function () { return sync_instructions_1.injectIntoFile; } });
416
+ Object.defineProperty(exports, "removeFromFile", { enumerable: true, get: function () { return sync_instructions_1.removeFromFile; } });
417
+ Object.defineProperty(exports, "syncInstructions", { enumerable: true, get: function () { return sync_instructions_1.syncInstructions; } });
418
+ Object.defineProperty(exports, "unsyncInstructions", { enumerable: true, get: function () { return sync_instructions_1.unsyncInstructions; } });
1004
419
  //# sourceMappingURL=sync.js.map