@intellectronica/ruler 0.3.41 → 0.3.43

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.
Files changed (105) hide show
  1. package/README.md +135 -36
  2. package/dist/agents/AbstractAgent.d.ts +53 -0
  3. package/dist/agents/AbstractAgent.js +3 -2
  4. package/dist/agents/AgentsMdAgent.d.ts +14 -0
  5. package/dist/agents/AgentsMdAgent.js +3 -2
  6. package/dist/agents/AiderAgent.d.ts +14 -0
  7. package/dist/agents/AiderAgent.js +7 -4
  8. package/dist/agents/AmazonQCliAgent.d.ts +13 -0
  9. package/dist/agents/AmazonQCliAgent.js +6 -4
  10. package/dist/agents/AmpAgent.d.ts +6 -0
  11. package/dist/agents/AntigravityAgent.d.ts +10 -0
  12. package/dist/agents/AugmentCodeAgent.d.ts +13 -0
  13. package/dist/agents/AugmentCodeAgent.js +3 -2
  14. package/dist/agents/ClaudeAgent.d.ts +13 -0
  15. package/dist/agents/ClineAgent.d.ts +9 -0
  16. package/dist/agents/CodexCliAgent.d.ts +31 -0
  17. package/dist/agents/CodexCliAgent.js +1 -1
  18. package/dist/agents/CopilotAgent.d.ts +20 -0
  19. package/dist/agents/CrushAgent.d.ts +14 -0
  20. package/dist/agents/CrushAgent.js +18 -6
  21. package/dist/agents/CursorAgent.d.ts +17 -0
  22. package/dist/agents/FactoryDroidAgent.d.ts +13 -0
  23. package/dist/agents/FirebaseAgent.d.ts +11 -0
  24. package/dist/agents/FirebenderAgent.d.ts +36 -0
  25. package/dist/agents/FirebenderAgent.js +5 -4
  26. package/dist/agents/GeminiCliAgent.d.ts +12 -0
  27. package/dist/agents/GeminiCliAgent.js +13 -7
  28. package/dist/agents/GooseAgent.d.ts +12 -0
  29. package/dist/agents/IAgent.d.ts +74 -0
  30. package/dist/agents/JetBrainsAiAssistantAgent.d.ts +10 -0
  31. package/dist/agents/JulesAgent.d.ts +5 -0
  32. package/dist/agents/JunieAgent.d.ts +12 -0
  33. package/dist/agents/KiloCodeAgent.d.ts +14 -0
  34. package/dist/agents/KiroAgent.d.ts +8 -0
  35. package/dist/agents/MistralVibeAgent.d.ts +31 -0
  36. package/dist/agents/MistralVibeAgent.js +14 -3
  37. package/dist/agents/OpenCodeAgent.d.ts +11 -0
  38. package/dist/agents/OpenCodeAgent.js +24 -12
  39. package/dist/agents/OpenHandsAgent.d.ts +8 -0
  40. package/dist/agents/PiAgent.d.ts +9 -0
  41. package/dist/agents/QwenCodeAgent.d.ts +11 -0
  42. package/dist/agents/QwenCodeAgent.js +11 -5
  43. package/dist/agents/RooCodeAgent.d.ts +16 -0
  44. package/dist/agents/RooCodeAgent.js +3 -2
  45. package/dist/agents/TraeAgent.d.ts +10 -0
  46. package/dist/agents/WarpAgent.d.ts +12 -0
  47. package/dist/agents/WindsurfAgent.d.ts +13 -0
  48. package/dist/agents/ZedAgent.d.ts +21 -0
  49. package/dist/agents/ZedAgent.js +8 -5
  50. package/dist/agents/agent-utils.d.ts +5 -0
  51. package/dist/agents/agent-utils.js +8 -5
  52. package/dist/agents/index.d.ts +9 -0
  53. package/dist/cli/commands.d.ts +4 -0
  54. package/dist/cli/commands.js +1 -2
  55. package/dist/cli/handlers.d.ts +41 -0
  56. package/dist/cli/handlers.js +75 -59
  57. package/dist/cli/index.d.ts +2 -0
  58. package/dist/constants.d.ts +35 -0
  59. package/dist/constants.js +1 -1
  60. package/dist/core/ConfigLoader.d.ts +59 -0
  61. package/dist/core/ConfigLoader.js +178 -44
  62. package/dist/core/FileSystemUtils.d.ts +53 -0
  63. package/dist/core/FileSystemUtils.js +157 -20
  64. package/dist/core/GitignoreUtils.d.ts +25 -0
  65. package/dist/core/GitignoreUtils.js +94 -32
  66. package/dist/core/RuleProcessor.d.ts +8 -0
  67. package/dist/core/SkillsProcessor.d.ts +127 -0
  68. package/dist/core/SkillsProcessor.js +118 -223
  69. package/dist/core/SkillsUtils.d.ts +26 -0
  70. package/dist/core/SubagentsProcessor.d.ts +38 -0
  71. package/dist/core/SubagentsProcessor.js +8 -5
  72. package/dist/core/SubagentsUtils.d.ts +34 -0
  73. package/dist/core/UnifiedConfigLoader.d.ts +10 -0
  74. package/dist/core/UnifiedConfigLoader.js +115 -33
  75. package/dist/core/UnifiedConfigTypes.d.ts +97 -0
  76. package/dist/core/agent-selection.d.ts +12 -0
  77. package/dist/core/agent-selection.js +17 -7
  78. package/dist/core/apply-engine.d.ts +70 -0
  79. package/dist/core/apply-engine.js +88 -58
  80. package/dist/core/config-utils.d.ts +14 -0
  81. package/dist/core/config-utils.js +9 -3
  82. package/dist/core/hash.d.ts +2 -0
  83. package/dist/core/path-utils.d.ts +1 -0
  84. package/dist/core/path-utils.js +42 -0
  85. package/dist/core/revert-engine.d.ts +37 -0
  86. package/dist/core/revert-engine.js +142 -34
  87. package/dist/lib.d.ts +13 -0
  88. package/dist/lib.js +24 -8
  89. package/dist/mcp/capabilities.d.ts +20 -0
  90. package/dist/mcp/merge.d.ts +10 -0
  91. package/dist/mcp/merge.js +36 -16
  92. package/dist/mcp/propagateOpenCodeMcp.d.ts +2 -0
  93. package/dist/mcp/propagateOpenCodeMcp.js +30 -11
  94. package/dist/mcp/propagateOpenHandsMcp.d.ts +2 -0
  95. package/dist/mcp/propagateOpenHandsMcp.js +48 -21
  96. package/dist/mcp/validate.d.ts +7 -0
  97. package/dist/mcp/validate.js +6 -1
  98. package/dist/paths/mcp.d.ts +8 -0
  99. package/dist/paths/mcp.js +44 -8
  100. package/dist/revert.d.ts +6 -0
  101. package/dist/revert.js +58 -46
  102. package/dist/types.d.ts +87 -0
  103. package/dist/vscode/settings.d.ts +40 -0
  104. package/dist/vscode/settings.js +3 -3
  105. package/package.json +8 -5
@@ -52,6 +52,7 @@ const propagateOpenHandsMcp_1 = require("../mcp/propagateOpenHandsMcp");
52
52
  const propagateOpenCodeMcp_1 = require("../mcp/propagateOpenCodeMcp");
53
53
  const agent_utils_1 = require("../agents/agent-utils");
54
54
  const capabilities_1 = require("../mcp/capabilities");
55
+ const path_utils_1 = require("./path-utils");
55
56
  const constants_1 = require("../constants");
56
57
  async function loadNestedConfigurations(projectRoot, configPath, localOnly, resolvedNested) {
57
58
  const { dirs: rulerDirs } = await findRulerDirectories(projectRoot, localOnly, true);
@@ -59,11 +60,11 @@ async function loadNestedConfigurations(projectRoot, configPath, localOnly, reso
59
60
  // Load config first so we know whether `.ruler/agents/` should be included
60
61
  // in the rule concatenation for each directory.
61
62
  for (const rulerDir of rulerDirs) {
62
- const config = await loadConfigForRulerDir(rulerDir, configPath, resolvedNested);
63
+ const config = await loadConfigForRulerDir(rulerDir, configPath, resolvedNested, localOnly);
63
64
  const files = await FileSystemUtils.readMarkdownFiles(rulerDir, {
64
65
  includeAgents: shouldIncludeAgentsInRules(config),
65
66
  });
66
- results.push(await createHierarchicalConfiguration(rulerDir, files, config, configPath));
67
+ results.push(await createHierarchicalConfiguration(rulerDir, files, config, configPath, localOnly));
67
68
  }
68
69
  return results;
69
70
  }
@@ -74,7 +75,7 @@ async function loadNestedConfigurations(projectRoot, configPath, localOnly, reso
74
75
  function shouldIncludeAgentsInRules(config) {
75
76
  return config.subagents?.include_in_rules === true;
76
77
  }
77
- async function createHierarchicalConfiguration(rulerDir, files, config, cliConfigPath) {
78
+ async function createHierarchicalConfiguration(rulerDir, files, config, cliConfigPath, localOnly) {
78
79
  await warnAboutLegacyMcpJson(rulerDir);
79
80
  const concatenatedRules = (0, RuleProcessor_1.concatenateRules)(files, path.dirname(rulerDir));
80
81
  const directoryRoot = path.dirname(rulerDir);
@@ -91,6 +92,7 @@ async function createHierarchicalConfiguration(rulerDir, files, config, cliConfi
91
92
  const unifiedConfig = await loadUnifiedConfig({
92
93
  projectRoot: directoryRoot,
93
94
  configPath: configPathToUse,
95
+ checkGlobal: !localOnly,
94
96
  });
95
97
  let rulerMcpJson = null;
96
98
  if (unifiedConfig.mcp && Object.keys(unifiedConfig.mcp.servers).length > 0) {
@@ -103,9 +105,10 @@ async function createHierarchicalConfiguration(rulerDir, files, config, cliConfi
103
105
  config,
104
106
  concatenatedRules,
105
107
  rulerMcpJson,
108
+ projectRoot: directoryRoot,
106
109
  };
107
110
  }
108
- async function loadConfigForRulerDir(rulerDir, cliConfigPath, resolvedNested) {
111
+ async function loadConfigForRulerDir(rulerDir, cliConfigPath, resolvedNested, localOnly) {
109
112
  const directoryRoot = path.dirname(rulerDir);
110
113
  const localConfigPath = path.join(rulerDir, 'ruler.toml');
111
114
  let hasLocalConfig = false;
@@ -119,6 +122,7 @@ async function loadConfigForRulerDir(rulerDir, cliConfigPath, resolvedNested) {
119
122
  const loaded = await (0, ConfigLoader_1.loadConfig)({
120
123
  projectRoot: directoryRoot,
121
124
  configPath: hasLocalConfig ? localConfigPath : cliConfigPath,
125
+ checkGlobal: !localOnly,
122
126
  });
123
127
  const cloned = cloneLoadedConfig(loaded);
124
128
  if (resolvedNested) {
@@ -136,6 +140,12 @@ function cloneLoadedConfig(config) {
136
140
  clonedAgentConfigs[agent] = {
137
141
  ...agentConfig,
138
142
  mcp: agentConfig.mcp ? { ...agentConfig.mcp } : undefined,
143
+ mcpServers: agentConfig.mcpServers
144
+ ? Object.fromEntries(Object.entries(agentConfig.mcpServers).map(([name, server]) => [
145
+ name,
146
+ { ...server },
147
+ ]))
148
+ : undefined,
139
149
  };
140
150
  }
141
151
  return {
@@ -144,6 +154,7 @@ function cloneLoadedConfig(config) {
144
154
  cliAgents: config.cliAgents ? [...config.cliAgents] : undefined,
145
155
  mcp: config.mcp ? { ...config.mcp } : undefined,
146
156
  gitignore: config.gitignore ? { ...config.gitignore } : undefined,
157
+ backup: config.backup ? { ...config.backup } : undefined,
147
158
  skills: config.skills ? { ...config.skills } : undefined,
148
159
  subagents: config.subagents ? { ...config.subagents } : undefined,
149
160
  nested: config.nested,
@@ -156,18 +167,10 @@ function cloneLoadedConfig(config) {
156
167
  async function findRulerDirectories(projectRoot, localOnly, hierarchical) {
157
168
  if (hierarchical) {
158
169
  const dirs = await FileSystemUtils.findAllRulerDirs(projectRoot);
159
- const allDirs = [...dirs];
160
- // Add global config if not local-only
161
- if (!localOnly) {
162
- const globalDir = await FileSystemUtils.findGlobalRulerDir();
163
- if (globalDir) {
164
- allDirs.push(globalDir);
165
- }
166
- }
167
- if (allDirs.length === 0) {
170
+ if (dirs.length === 0) {
168
171
  throw (0, constants_1.createRulerError)(`.ruler directory not found`, `Searched from: ${projectRoot}`);
169
172
  }
170
- return { dirs: allDirs, primaryDir: allDirs[0] };
173
+ return { dirs, primaryDir: dirs[0] };
171
174
  }
172
175
  else {
173
176
  const dir = await FileSystemUtils.findRulerDir(projectRoot, !localOnly);
@@ -198,10 +201,12 @@ async function loadSingleConfiguration(projectRoot, configPath, localOnly) {
198
201
  const { dirs: rulerDirs, primaryDir } = await findRulerDirectories(projectRoot, localOnly, false);
199
202
  // Warn about legacy mcp.json
200
203
  await warnAboutLegacyMcpJson(primaryDir);
204
+ const effectiveProjectRoot = FileSystemUtils.resolveProjectRootForRulerDir(projectRoot, primaryDir);
201
205
  // Load the ruler.toml configuration
202
206
  const config = await (0, ConfigLoader_1.loadConfig)({
203
- projectRoot,
207
+ projectRoot: effectiveProjectRoot,
204
208
  configPath,
209
+ checkGlobal: !localOnly,
205
210
  });
206
211
  // Read rule files. `.ruler/agents/` is only included when
207
212
  // `[agents] include_in_rules = true`.
@@ -212,7 +217,11 @@ async function loadSingleConfiguration(projectRoot, configPath, localOnly) {
212
217
  const concatenatedRules = (0, RuleProcessor_1.concatenateRules)(files, path.dirname(primaryDir));
213
218
  // Load unified config to get merged MCP configuration
214
219
  const { loadUnifiedConfig } = await Promise.resolve().then(() => __importStar(require('./UnifiedConfigLoader')));
215
- const unifiedConfig = await loadUnifiedConfig({ projectRoot, configPath });
220
+ const unifiedConfig = await loadUnifiedConfig({
221
+ projectRoot: effectiveProjectRoot,
222
+ configPath,
223
+ checkGlobal: !localOnly,
224
+ });
216
225
  // Synthesize rulerMcpJson from unified MCP bundle for backward compatibility
217
226
  let rulerMcpJson = null;
218
227
  if (unifiedConfig.mcp && Object.keys(unifiedConfig.mcp.servers).length > 0) {
@@ -224,6 +233,7 @@ async function loadSingleConfiguration(projectRoot, configPath, localOnly) {
224
233
  config,
225
234
  concatenatedRules,
226
235
  rulerMcpJson,
236
+ projectRoot: effectiveProjectRoot,
227
237
  };
228
238
  }
229
239
  /**
@@ -281,7 +291,7 @@ async function applyConfigurationsToAgents(agents, concatenatedRules, rulerMcpJs
281
291
  (0, constants_1.logInfo)(`Applying rules for ${agent.getName()}...`, dryRun);
282
292
  (0, constants_1.logVerbose)(`Processing agent: ${agent.getName()}`, verbose);
283
293
  const agentConfig = config.agentConfigs[agent.getIdentifier()];
284
- const agentRulerMcpJson = rulerMcpJson;
294
+ const agentRulerMcpJson = mergeAgentMcpServers(rulerMcpJson, agentConfig);
285
295
  // Collect output paths for .gitignore
286
296
  const outputPaths = (0, agent_utils_1.getAgentOutputPaths)(agent, projectRoot, agentConfig);
287
297
  (0, constants_1.logVerbose)(`Agent ${agent.getName()} output paths: ${outputPaths.join(', ')}`, verbose);
@@ -306,22 +316,27 @@ async function applyConfigurationsToAgents(agents, concatenatedRules, rulerMcpJs
306
316
  agentsMdWritten = true;
307
317
  }
308
318
  }
309
- let finalAgentConfig = agentConfig;
310
- if (agent.getIdentifier() === 'augmentcode' && agentRulerMcpJson) {
311
- const resolvedStrategy = cliMcpStrategy ??
312
- agentConfig?.mcp?.strategy ??
313
- config.mcp?.strategy ??
314
- 'merge';
315
- finalAgentConfig = {
316
- ...agentConfig,
317
- mcp: {
318
- ...agentConfig?.mcp,
319
- strategy: resolvedStrategy,
320
- },
321
- };
322
- }
319
+ const effectiveMcpEnabled = cliMcpEnabled &&
320
+ (agentConfig?.mcp?.enabled ?? config.mcp?.enabled ?? true);
321
+ const effectiveMcpStrategy = cliMcpStrategy ??
322
+ agentConfig?.mcp?.strategy ??
323
+ config.mcp?.strategy ??
324
+ 'merge';
325
+ const finalAgentConfig = {
326
+ ...agentConfig,
327
+ mcp: {
328
+ ...agentConfig?.mcp,
329
+ enabled: effectiveMcpEnabled,
330
+ strategy: effectiveMcpStrategy,
331
+ },
332
+ };
333
+ const mcpForAgentApply = shouldUseEngineManagedMcp(agent)
334
+ ? null
335
+ : effectiveMcpEnabled
336
+ ? agentRulerMcpJson
337
+ : null;
323
338
  if (!skipApplyForThisAgent) {
324
- await agent.applyRulerConfig(concatenatedRules, projectRoot, agentRulerMcpJson, finalAgentConfig, backup);
339
+ await agent.applyRulerConfig(concatenatedRules, projectRoot, mcpForAgentApply, finalAgentConfig, backup);
325
340
  }
326
341
  }
327
342
  // Handle MCP configuration
@@ -334,7 +349,7 @@ async function handleMcpConfiguration(agent, agentConfig, config, rulerMcpJson,
334
349
  (0, constants_1.logVerbose)(`Agent ${agent.getName()} does not support MCP - skipping MCP configuration`, verbose);
335
350
  return;
336
351
  }
337
- const dest = await (0, mcp_1.getNativeMcpPath)(agent.getName(), projectRoot);
352
+ const dest = await resolveMcpDestination(agent, agentConfig, projectRoot);
338
353
  const mcpEnabledForAgent = cliMcpEnabled && (agentConfig?.mcp?.enabled ?? config.mcp?.enabled ?? true);
339
354
  if (!dest || !mcpEnabledForAgent) {
340
355
  return;
@@ -349,8 +364,30 @@ async function handleMcpConfiguration(agent, agentConfig, config, rulerMcpJson,
349
364
  await updateGitignoreForMcpFile(dest, projectRoot, generatedPaths, backup);
350
365
  await applyMcpConfiguration(agent, filteredMcpJson, dest, agentConfig, config, projectRoot, cliMcpStrategy, dryRun, verbose, backup);
351
366
  }
367
+ function shouldUseEngineManagedMcp(agent) {
368
+ return (agent.getIdentifier() === 'codex' || agent.getIdentifier() === 'opencode');
369
+ }
370
+ function mergeAgentMcpServers(rulerMcpJson, agentConfig) {
371
+ const baseServers = rulerMcpJson?.mcpServers && typeof rulerMcpJson.mcpServers === 'object'
372
+ ? rulerMcpJson.mcpServers
373
+ : {};
374
+ const agentServers = agentConfig?.mcpServers ?? {};
375
+ const mergedServers = {
376
+ ...baseServers,
377
+ ...agentServers,
378
+ };
379
+ return Object.keys(mergedServers).length > 0
380
+ ? { mcpServers: mergedServers }
381
+ : null;
382
+ }
383
+ async function resolveMcpDestination(agent, agentConfig, projectRoot) {
384
+ if (agentConfig?.outputPathConfig) {
385
+ return path.resolve(projectRoot, agentConfig.outputPathConfig);
386
+ }
387
+ return await (0, mcp_1.getNativeMcpPath)(agent.getName(), projectRoot);
388
+ }
352
389
  async function updateGitignoreForMcpFile(dest, projectRoot, generatedPaths, backup = true) {
353
- if (dest.startsWith(projectRoot)) {
390
+ if ((0, path_utils_1.isPathInsideOrEqual)(projectRoot, dest)) {
354
391
  const relativeDest = path.relative(projectRoot, dest);
355
392
  generatedPaths.push(relativeDest);
356
393
  if (backup) {
@@ -391,16 +428,16 @@ function sanitizeMcpTimeoutsForAgent(agent, mcpJson, dryRun) {
391
428
  }
392
429
  async function applyMcpConfiguration(agent, filteredMcpJson, dest, agentConfig, config, projectRoot, cliMcpStrategy, dryRun, verbose, backup = true) {
393
430
  // Prevent writing MCP configs outside the project root (e.g., legacy home-directory targets)
394
- if (!dest.startsWith(projectRoot)) {
431
+ if (!(0, path_utils_1.isPathInsideOrEqual)(projectRoot, dest)) {
395
432
  (0, constants_1.logVerbose)(`Skipping MCP config for ${agent.getName()} because target path is outside project: ${dest}`, verbose);
396
433
  return;
397
434
  }
398
435
  const agentMcpJson = sanitizeMcpTimeoutsForAgent(agent, filteredMcpJson, dryRun);
399
436
  if (agent.getIdentifier() === 'openhands') {
400
- return await applyOpenHandsMcpConfiguration(agentMcpJson, dest, dryRun, verbose, backup);
437
+ return await applyOpenHandsMcpConfiguration(agentMcpJson, dest, projectRoot, cliMcpStrategy ?? agentConfig?.mcp?.strategy ?? config.mcp?.strategy, dryRun, verbose, backup);
401
438
  }
402
439
  if (agent.getIdentifier() === 'opencode') {
403
- return await applyOpenCodeMcpConfiguration(agentMcpJson, dest, dryRun, verbose, backup);
440
+ return await applyOpenCodeMcpConfiguration(agentMcpJson, dest, projectRoot, cliMcpStrategy ?? agentConfig?.mcp?.strategy ?? config.mcp?.strategy, dryRun, verbose, backup);
404
441
  }
405
442
  // Agents that handle MCP configuration internally should not have external MCP handling
406
443
  if (agent.getIdentifier() === 'zed' ||
@@ -410,22 +447,22 @@ async function applyMcpConfiguration(agent, filteredMcpJson, dest, agentConfig,
410
447
  (0, constants_1.logVerbose)(`Skipping external MCP config for ${agent.getName()} - handled internally by agent`, verbose);
411
448
  return;
412
449
  }
413
- return await applyStandardMcpConfiguration(agent, agentMcpJson, dest, agentConfig, config, cliMcpStrategy, dryRun, verbose, backup);
450
+ return await applyStandardMcpConfiguration(agent, agentMcpJson, dest, agentConfig, config, projectRoot, cliMcpStrategy, dryRun, verbose, backup);
414
451
  }
415
- async function applyOpenHandsMcpConfiguration(filteredMcpJson, dest, dryRun, verbose, backup = true) {
452
+ async function applyOpenHandsMcpConfiguration(filteredMcpJson, dest, projectRoot, strategy, dryRun, verbose, backup = true) {
416
453
  if (dryRun) {
417
454
  (0, constants_1.logVerbose)(`DRY RUN: Would apply MCP config by updating TOML file: ${dest}`, verbose);
418
455
  }
419
456
  else {
420
- await (0, propagateOpenHandsMcp_1.propagateMcpToOpenHands)(filteredMcpJson, dest, backup);
457
+ await (0, propagateOpenHandsMcp_1.propagateMcpToOpenHands)(filteredMcpJson, dest, backup, strategy, projectRoot);
421
458
  }
422
459
  }
423
- async function applyOpenCodeMcpConfiguration(filteredMcpJson, dest, dryRun, verbose, backup = true) {
460
+ async function applyOpenCodeMcpConfiguration(filteredMcpJson, dest, projectRoot, strategy, dryRun, verbose, backup = true) {
424
461
  if (dryRun) {
425
462
  (0, constants_1.logVerbose)(`DRY RUN: Would apply MCP config by updating OpenCode config file: ${dest}`, verbose);
426
463
  }
427
464
  else {
428
- await (0, propagateOpenCodeMcp_1.propagateMcpToOpenCode)(filteredMcpJson, dest, backup);
465
+ await (0, propagateOpenCodeMcp_1.propagateMcpToOpenCode)(filteredMcpJson, dest, backup, strategy, projectRoot);
429
466
  }
430
467
  }
431
468
  /**
@@ -521,7 +558,7 @@ function transformMcpForFactoryDroid(mcpJson) {
521
558
  transformedMcp.mcpServers = transformedServers;
522
559
  return transformedMcp;
523
560
  }
524
- async function applyStandardMcpConfiguration(agent, filteredMcpJson, dest, agentConfig, config, cliMcpStrategy, dryRun, verbose, backup = true) {
561
+ async function applyStandardMcpConfiguration(agent, filteredMcpJson, dest, agentConfig, config, projectRoot, cliMcpStrategy, dryRun, verbose, backup = true) {
525
562
  const strategy = cliMcpStrategy ??
526
563
  agentConfig?.mcp?.strategy ??
527
564
  config.mcp?.strategy ??
@@ -539,7 +576,8 @@ async function applyStandardMcpConfiguration(agent, filteredMcpJson, dest, agent
539
576
  else {
540
577
  // Transform MCP config for agent-specific compatibility
541
578
  let mcpToMerge = filteredMcpJson;
542
- if (agent.getIdentifier() === 'claude') {
579
+ if (agent.getIdentifier() === 'claude' ||
580
+ agent.getIdentifier() === 'aider') {
543
581
  mcpToMerge = transformMcpForClaude(filteredMcpJson);
544
582
  }
545
583
  else if (agent.getIdentifier() === 'kilocode') {
@@ -550,17 +588,9 @@ async function applyStandardMcpConfiguration(agent, filteredMcpJson, dest, agent
550
588
  }
551
589
  const CODEX_AGENT_ID = 'codex';
552
590
  const isCodexToml = agent.getIdentifier() === CODEX_AGENT_ID && dest.endsWith('.toml');
553
- let existing = await (0, mcp_1.readNativeMcp)(dest);
554
- if (isCodexToml) {
555
- try {
556
- const tomlContent = await fs_1.promises.readFile(dest, 'utf8');
557
- existing = (0, toml_1.parse)(tomlContent);
558
- }
559
- catch (error) {
560
- (0, constants_1.logVerbose)(`Failed to read Codex MCP TOML at ${dest}: ${error.message}`, verbose);
561
- // ignore missing or invalid TOML, fall back to previously read value
562
- }
563
- }
591
+ const existing = isCodexToml
592
+ ? await (0, mcp_1.readNativeMcpToml)(dest, (text) => (0, toml_1.parse)(text))
593
+ : await (0, mcp_1.readNativeMcp)(dest);
564
594
  let merged = (0, merge_1.mergeMcp)(existing, mcpToMerge, strategy, serverKey);
565
595
  if (isCodexToml) {
566
596
  const { [serverKey]: servers, ...rest } = merged;
@@ -626,13 +656,13 @@ async function applyStandardMcpConfiguration(agent, filteredMcpJson, dest, agent
626
656
  if (currentContent !== newContent) {
627
657
  if (backup) {
628
658
  const { backupFile } = await Promise.resolve().then(() => __importStar(require('../core/FileSystemUtils')));
629
- await backupFile(dest);
659
+ await backupFile(dest, projectRoot);
630
660
  }
631
661
  if (isCodexToml) {
632
- await FileSystemUtils.writeGeneratedFile(dest, (0, toml_1.stringify)(toWrite));
662
+ await FileSystemUtils.writeGeneratedFile(dest, (0, toml_1.stringify)(toWrite), projectRoot);
633
663
  }
634
664
  else {
635
- await (0, mcp_1.writeNativeMcp)(dest, toWrite);
665
+ await (0, mcp_1.writeNativeMcp)(dest, toWrite, projectRoot);
636
666
  }
637
667
  }
638
668
  else {
@@ -0,0 +1,14 @@
1
+ import { IAgent, IAgentConfig } from '../agents/IAgent';
2
+ /**
3
+ * Maps raw agent configuration keys to their corresponding agent identifiers.
4
+ *
5
+ * This function normalizes configuration keys by matching them against agent identifiers
6
+ * and display names. It performs both exact matching (case-insensitive) with agent
7
+ * identifiers and substring matching (case-insensitive) with agent display names
8
+ * for backwards compatibility.
9
+ *
10
+ * @param raw Raw agent configurations with user-provided keys
11
+ * @param agents Array of all available agents
12
+ * @returns Record with agent identifiers as keys and their configurations as values
13
+ */
14
+ export declare function mapRawAgentConfigs(raw: Record<string, IAgentConfig>, agents: IAgent[]): Record<string, IAgentConfig>;
@@ -17,11 +17,17 @@ function mapRawAgentConfigs(raw, agents) {
17
17
  const mappedConfigs = {};
18
18
  for (const [key, cfg] of Object.entries(raw)) {
19
19
  const lowerKey = key.toLowerCase();
20
+ const exactMatches = agents.filter((agent) => agent.getIdentifier().toLowerCase() === lowerKey);
21
+ // Exact identifier matches take precedence over fuzzy display-name matching.
22
+ if (exactMatches.length > 0) {
23
+ for (const agent of exactMatches) {
24
+ mappedConfigs[agent.getIdentifier()] = cfg;
25
+ }
26
+ continue;
27
+ }
20
28
  for (const agent of agents) {
21
29
  const identifier = agent.getIdentifier();
22
- // Exact match with identifier or substring match with display name for backwards compatibility
23
- if (identifier === lowerKey ||
24
- agent.getName().toLowerCase().includes(lowerKey)) {
30
+ if (agent.getName().toLowerCase().includes(lowerKey)) {
25
31
  mappedConfigs[identifier] = cfg;
26
32
  }
27
33
  }
@@ -0,0 +1,2 @@
1
+ export declare function sha256(data: string): string;
2
+ export declare function stableJson(value: unknown): string;
@@ -0,0 +1 @@
1
+ export declare function isPathInsideOrEqual(parentPath: string, targetPath: string): boolean;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.isPathInsideOrEqual = isPathInsideOrEqual;
37
+ const path = __importStar(require("path"));
38
+ function isPathInsideOrEqual(parentPath, targetPath) {
39
+ const relative = path.relative(path.resolve(parentPath), path.resolve(targetPath));
40
+ return (relative === '' ||
41
+ (!relative.startsWith('..') && !path.isAbsolute(relative)));
42
+ }
@@ -0,0 +1,37 @@
1
+ import { IAgent } from '../agents/IAgent';
2
+ import { IAgentConfig } from './ConfigLoader';
3
+ /**
4
+ * Result of reverting an agent configuration
5
+ */
6
+ export interface RevertAgentResult {
7
+ restored: number;
8
+ removed: number;
9
+ backupsRemoved: number;
10
+ }
11
+ /**
12
+ * Result of cleaning up auxiliary files
13
+ */
14
+ export interface CleanUpResult {
15
+ additionalFilesRemoved: number;
16
+ directoriesRemoved: number;
17
+ }
18
+ /**
19
+ * Reverts configuration for a single agent.
20
+ * @param agent The agent to revert
21
+ * @param projectRoot Root directory of the project
22
+ * @param agentConfig Agent-specific configuration
23
+ * @param keepBackups Whether restored backup files should be preserved
24
+ * @param verbose Whether to enable verbose logging
25
+ * @param dryRun Whether to perform a dry run
26
+ * @returns Promise resolving to revert statistics
27
+ */
28
+ export declare function revertAgentConfiguration(agent: IAgent, projectRoot: string, agentConfig: IAgentConfig | undefined, keepBackups: boolean, verbose: boolean, dryRun: boolean): Promise<RevertAgentResult>;
29
+ /**
30
+ * Cleans up auxiliary files and directories.
31
+ * @param projectRoot Root directory of the project
32
+ * @param verbose Whether to enable verbose logging
33
+ * @param dryRun Whether to perform a dry run
34
+ * @returns Promise resolving to cleanup statistics
35
+ */
36
+ export declare function cleanUpAuxiliaryFiles(projectRoot: string, verbose: boolean, dryRun: boolean): Promise<CleanUpResult>;
37
+ export declare function cleanUpAgentDirectories(agent: IAgent, projectRoot: string, agentConfig: IAgentConfig | undefined, verbose: boolean, dryRun: boolean): Promise<number>;