@intellectronica/ruler 0.2.9 → 0.2.10

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/README.md CHANGED
@@ -35,21 +35,21 @@ Ruler solves this by providing a **single source of truth** for all your AI agen
35
35
 
36
36
  ## Supported AI Agents
37
37
 
38
- | Agent | File(s) Created/Updated |
39
- | ---------------- | ------------------------------------------------------------- |
40
- | GitHub Copilot | `.github/copilot-instructions.md` |
41
- | Claude Code | `CLAUDE.md` |
42
- | OpenAI Codex CLI | `AGENTS.md` |
43
- | Jules | `AGENTS.md` |
44
- | Cursor | `.cursor/rules/ruler_cursor_instructions.mdc` |
45
- | Windsurf | `.windsurf/rules/ruler_windsurf_instructions.md` |
46
- | Cline | `.clinerules` |
47
- | Aider | `ruler_aider_instructions.md` and `.aider.conf.yml` |
48
- | Firebase Studio | `.idx/airules.md` |
49
- | Open Hands | `.openhands/microagents/repo.md` and `.openhands/config.toml` |
50
- | Gemini CLI | `GEMINI.md` and `.gemini/settings.json` |
51
- | Junie | `.junie/guidelines.md` |
52
- | AugmentCode | `.augment-guidelines` and `.augmentcode/config.json` |
38
+ | Agent | Rules File(s) | MCP Configuration |
39
+ | ---------------- | ------------------------------------------------ | ------------------------------------------------ |
40
+ | GitHub Copilot | `.github/copilot-instructions.md` | `.vscode/mcp.json` |
41
+ | Claude Code | `CLAUDE.md` | `claude_desktop_config.json` |
42
+ | OpenAI Codex CLI | `AGENTS.md` | `~/.codex/config.json` |
43
+ | Jules | `AGENTS.md` | - |
44
+ | Cursor | `.cursor/rules/ruler_cursor_instructions.mdc` | `.cursor/mcp.json`, `~/.cursor/mcp.json` |
45
+ | Windsurf | `.windsurf/rules/ruler_windsurf_instructions.md` | `~/.codeium/windsurf/mcp_config.json` |
46
+ | Cline | `.clinerules` | - |
47
+ | Aider | `ruler_aider_instructions.md`, `.aider.conf.yml` | `.mcp.json` |
48
+ | Firebase Studio | `.idx/airules.md` | - |
49
+ | Open Hands | `.openhands/microagents/repo.md` | `.openhands/config.toml` |
50
+ | Gemini CLI | `GEMINI.md` | `.gemini/settings.json` |
51
+ | Junie | `.junie/guidelines.md` | - |
52
+ | AugmentCode | `.augment/rules/ruler_augment_instructions.md` | `.vscode/settings.json` |
53
53
 
54
54
  ## Getting Started
55
55
 
@@ -35,12 +35,11 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.AugmentCodeAgent = void 0;
37
37
  const path = __importStar(require("path"));
38
- const fs_1 = require("fs");
39
38
  const FileSystemUtils_1 = require("../core/FileSystemUtils");
40
- const merge_1 = require("../mcp/merge");
39
+ const settings_1 = require("../vscode/settings");
41
40
  /**
42
41
  * AugmentCode agent adapter.
43
- * Generates .augment-guidelines configuration file and supports MCP server configuration.
42
+ * Generates ruler_augment_instructions.md configuration file and updates VSCode settings.json with MCP server configuration.
44
43
  */
45
44
  class AugmentCodeAgent {
46
45
  getIdentifier() {
@@ -53,26 +52,17 @@ class AugmentCodeAgent {
53
52
  const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
54
53
  await (0, FileSystemUtils_1.backupFile)(output);
55
54
  await (0, FileSystemUtils_1.writeGeneratedFile)(output, concatenatedRules);
56
- // Handle MCP configuration if provided
57
55
  if (rulerMcpJson) {
58
- const configPath = path.join(projectRoot, '.augmentcode', 'config.json');
59
- let existingConfig = {};
60
- try {
61
- const existingConfigRaw = await fs_1.promises.readFile(configPath, 'utf8');
62
- existingConfig = JSON.parse(existingConfigRaw);
63
- }
64
- catch (error) {
65
- if (error.code !== 'ENOENT') {
66
- throw error;
67
- }
68
- }
69
- const merged = (0, merge_1.mergeMcp)(existingConfig, rulerMcpJson, agentConfig?.mcp?.strategy ?? 'merge', this.getMcpServerKey());
70
- await fs_1.promises.mkdir(path.dirname(configPath), { recursive: true });
71
- await fs_1.promises.writeFile(configPath, JSON.stringify(merged, null, 2));
56
+ const settingsPath = (0, settings_1.getVSCodeSettingsPath)(projectRoot);
57
+ await (0, FileSystemUtils_1.backupFile)(settingsPath);
58
+ const existingSettings = await (0, settings_1.readVSCodeSettings)(settingsPath);
59
+ const augmentServers = (0, settings_1.transformRulerToAugmentMcp)(rulerMcpJson);
60
+ const mergedSettings = (0, settings_1.mergeAugmentMcpServers)(existingSettings, augmentServers, agentConfig?.mcp?.strategy ?? 'merge');
61
+ await (0, settings_1.writeVSCodeSettings)(settingsPath, mergedSettings);
72
62
  }
73
63
  }
74
64
  getDefaultOutputPath(projectRoot) {
75
- return path.join(projectRoot, '.augment-guidelines');
65
+ return path.join(projectRoot, '.augment', 'rules', 'ruler_augment_instructions.md');
76
66
  }
77
67
  getMcpServerKey() {
78
68
  return 'mcpServers';
package/dist/lib.js CHANGED
@@ -217,7 +217,21 @@ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cli
217
217
  }
218
218
  agentsMdWritten = true;
219
219
  }
220
- await agent.applyRulerConfig(concatenated, projectRoot, rulerMcpJson, agentConfig);
220
+ let finalAgentConfig = agentConfig;
221
+ if (agent.getIdentifier() === 'augmentcode' && rulerMcpJson) {
222
+ const resolvedStrategy = cliMcpStrategy ??
223
+ agentConfig?.mcp?.strategy ??
224
+ config.mcp?.strategy ??
225
+ 'merge';
226
+ finalAgentConfig = {
227
+ ...agentConfig,
228
+ mcp: {
229
+ ...agentConfig?.mcp,
230
+ strategy: resolvedStrategy,
231
+ },
232
+ };
233
+ }
234
+ await agent.applyRulerConfig(concatenated, projectRoot, rulerMcpJson, finalAgentConfig);
221
235
  }
222
236
  const dest = await (0, mcp_1.getNativeMcpPath)(agent.getName(), projectRoot);
223
237
  const mcpEnabledForAgent = cliMcpEnabled &&
@@ -241,6 +255,14 @@ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cli
241
255
  }
242
256
  // Open Hands config file is already included above
243
257
  }
258
+ else if (agent.getIdentifier() === 'augmentcode') {
259
+ // *** Special handling for AugmentCode ***
260
+ // AugmentCode handles MCP configuration internally in applyRulerConfig
261
+ // by updating VSCode settings.json with augment.advanced.mcpServers format
262
+ if (dryRun) {
263
+ (0, constants_1.logVerbose)(`DRY RUN: AugmentCode MCP config handled internally via VSCode settings`, verbose);
264
+ }
265
+ }
244
266
  else {
245
267
  if (rulerMcpJson) {
246
268
  const strategy = cliMcpStrategy ??
package/dist/paths/mcp.js CHANGED
@@ -75,8 +75,7 @@ async function getNativeMcpPath(adapterName, projectRoot) {
75
75
  candidates.push(path.join(projectRoot, '.gemini', 'settings.json'));
76
76
  break;
77
77
  case 'AugmentCode':
78
- candidates.push(path.join(projectRoot, '.augmentcode', 'config.json'));
79
- candidates.push(path.join(home, '.augmentcode', 'config.json'));
78
+ candidates.push(path.join(projectRoot, '.vscode', 'settings.json'));
80
79
  break;
81
80
  default:
82
81
  return null;
package/dist/revert.js CHANGED
@@ -50,8 +50,10 @@ const OpenHandsAgent_1 = require("./agents/OpenHandsAgent");
50
50
  const GeminiCliAgent_1 = require("./agents/GeminiCliAgent");
51
51
  const JulesAgent_1 = require("./agents/JulesAgent");
52
52
  const JunieAgent_1 = require("./agents/JunieAgent");
53
+ const AugmentCodeAgent_1 = require("./agents/AugmentCodeAgent");
53
54
  const mcp_1 = require("./paths/mcp");
54
55
  const constants_1 = require("./constants");
56
+ const settings_1 = require("./vscode/settings");
55
57
  const agents = [
56
58
  new CopilotAgent_1.CopilotAgent(),
57
59
  new ClaudeAgent_1.ClaudeAgent(),
@@ -65,6 +67,7 @@ const agents = [
65
67
  new GeminiCliAgent_1.GeminiCliAgent(),
66
68
  new JulesAgent_1.JulesAgent(),
67
69
  new JunieAgent_1.JunieAgent(),
70
+ new AugmentCodeAgent_1.AugmentCodeAgent(),
68
71
  ];
69
72
  /**
70
73
  * Gets all output paths for an agent, taking into account any config overrides.
@@ -170,9 +173,101 @@ async function removeBackupFile(filePath, verbose, dryRun) {
170
173
  }
171
174
  return true;
172
175
  }
176
+ /**
177
+ * Recursively checks if a directory contains only empty directories
178
+ */
179
+ async function isDirectoryTreeEmpty(dirPath) {
180
+ try {
181
+ const entries = await fs_1.promises.readdir(dirPath);
182
+ if (entries.length === 0) {
183
+ return true;
184
+ }
185
+ for (const entry of entries) {
186
+ const entryPath = path.join(dirPath, entry);
187
+ const entryStat = await fs_1.promises.stat(entryPath);
188
+ if (entryStat.isFile()) {
189
+ return false;
190
+ }
191
+ else if (entryStat.isDirectory()) {
192
+ const isEmpty = await isDirectoryTreeEmpty(entryPath);
193
+ if (!isEmpty) {
194
+ return false;
195
+ }
196
+ }
197
+ }
198
+ return true;
199
+ }
200
+ catch {
201
+ return false;
202
+ }
203
+ }
204
+ /**
205
+ * Helper function to execute directory removal with consistent dry-run handling and logging.
206
+ */
207
+ async function executeDirectoryAction(dirPath, action, verbose, dryRun) {
208
+ const actionPrefix = dryRun ? '[ruler:dry-run]' : '[ruler]';
209
+ const actionText = action === 'remove-tree' ? 'directory tree' : 'directory';
210
+ if (dryRun) {
211
+ (0, constants_1.logVerbose)(`${actionPrefix} Would remove empty ${actionText}: ${dirPath}`, verbose);
212
+ }
213
+ else {
214
+ await fs_1.promises.rm(dirPath, { recursive: true });
215
+ (0, constants_1.logVerbose)(`${actionPrefix} Removed empty ${actionText}: ${dirPath}`, verbose);
216
+ }
217
+ return true;
218
+ }
219
+ /**
220
+ * Attempts to remove a single empty directory if it exists and is empty.
221
+ */
222
+ async function removeEmptyDirectory(dirPath, verbose, dryRun, logMissing = false) {
223
+ try {
224
+ const stat = await fs_1.promises.stat(dirPath);
225
+ if (!stat.isDirectory()) {
226
+ return false;
227
+ }
228
+ const isEmpty = await isDirectoryTreeEmpty(dirPath);
229
+ if (isEmpty) {
230
+ return await executeDirectoryAction(dirPath, 'remove-tree', verbose, dryRun);
231
+ }
232
+ return false;
233
+ }
234
+ catch {
235
+ if (logMissing) {
236
+ (0, constants_1.logVerbose)(`Directory ${dirPath} doesn't exist or can't be accessed`, verbose);
237
+ }
238
+ return false;
239
+ }
240
+ }
241
+ /**
242
+ * Handles special cleanup logic for .augment directory and its rules subdirectory.
243
+ */
244
+ async function removeAugmentDirectory(projectRoot, verbose, dryRun) {
245
+ const augmentDir = path.join(projectRoot, '.augment');
246
+ let directoriesRemoved = 0;
247
+ try {
248
+ const augmentStat = await fs_1.promises.stat(augmentDir);
249
+ if (!augmentStat.isDirectory()) {
250
+ return 0;
251
+ }
252
+ const rulesDir = path.join(augmentDir, 'rules');
253
+ const rulesRemoved = await removeEmptyDirectory(rulesDir, verbose, dryRun);
254
+ if (rulesRemoved) {
255
+ directoriesRemoved++;
256
+ }
257
+ const augmentRemoved = await removeEmptyDirectory(augmentDir, verbose, dryRun);
258
+ if (augmentRemoved) {
259
+ directoriesRemoved++;
260
+ }
261
+ }
262
+ catch {
263
+ // .augment directory doesn't exist, that's fine. leaving comment as catch block can't be kept empty.
264
+ }
265
+ return directoriesRemoved;
266
+ }
173
267
  /**
174
268
  * Removes empty directories that were created by ruler.
175
269
  * Only removes directories if they are empty and were likely created by ruler.
270
+ * Special handling for .augment directory to clean up rules subdirectory.
176
271
  */
177
272
  async function removeEmptyDirectories(projectRoot, verbose, dryRun) {
178
273
  const rulerCreatedDirs = [
@@ -184,58 +279,17 @@ async function removeEmptyDirectories(projectRoot, verbose, dryRun) {
184
279
  '.idx',
185
280
  '.gemini',
186
281
  '.vscode',
282
+ '.augmentcode',
187
283
  ];
188
284
  let directoriesRemoved = 0;
189
- const actionPrefix = dryRun ? '[ruler:dry-run]' : '[ruler]';
285
+ // Handle .augment directory with special logic
286
+ directoriesRemoved += await removeAugmentDirectory(projectRoot, verbose, dryRun);
287
+ // Handle all other ruler-created directories
190
288
  for (const dirName of rulerCreatedDirs) {
191
289
  const dirPath = path.join(projectRoot, dirName);
192
- try {
193
- const stat = await fs_1.promises.stat(dirPath);
194
- if (!stat.isDirectory()) {
195
- continue;
196
- }
197
- const entries = await fs_1.promises.readdir(dirPath);
198
- if (entries.length === 0) {
199
- if (dryRun) {
200
- (0, constants_1.logVerbose)(`${actionPrefix} Would remove empty directory: ${dirPath}`, verbose);
201
- }
202
- else {
203
- await fs_1.promises.rmdir(dirPath);
204
- (0, constants_1.logVerbose)(`${actionPrefix} Removed empty directory: ${dirPath}`, verbose);
205
- }
206
- directoriesRemoved++;
207
- }
208
- else {
209
- let hasNonEmptyContent = false;
210
- for (const entry of entries) {
211
- const entryPath = path.join(dirPath, entry);
212
- const entryStat = await fs_1.promises.stat(entryPath);
213
- if (entryStat.isFile()) {
214
- hasNonEmptyContent = true;
215
- break;
216
- }
217
- else if (entryStat.isDirectory()) {
218
- const subEntries = await fs_1.promises.readdir(entryPath);
219
- if (subEntries.length > 0) {
220
- hasNonEmptyContent = true;
221
- break;
222
- }
223
- }
224
- }
225
- if (!hasNonEmptyContent) {
226
- if (dryRun) {
227
- (0, constants_1.logVerbose)(`${actionPrefix} Would remove directory tree: ${dirPath}`, verbose);
228
- }
229
- else {
230
- await fs_1.promises.rm(dirPath, { recursive: true });
231
- (0, constants_1.logVerbose)(`${actionPrefix} Removed directory tree: ${dirPath}`, verbose);
232
- }
233
- directoriesRemoved++;
234
- }
235
- }
236
- }
237
- catch {
238
- (0, constants_1.logVerbose)(`Directory ${dirPath} doesn't exist or can't be accessed`, verbose);
290
+ const removed = await removeEmptyDirectory(dirPath, verbose, dryRun, true);
291
+ if (removed) {
292
+ directoriesRemoved++;
239
293
  }
240
294
  }
241
295
  return directoriesRemoved;
@@ -283,6 +337,55 @@ async function removeAdditionalAgentFiles(projectRoot, verbose, dryRun) {
283
337
  (0, constants_1.logVerbose)(`Additional file ${fullPath} doesn't exist or can't be accessed`, verbose);
284
338
  }
285
339
  }
340
+ const settingsPath = (0, settings_1.getVSCodeSettingsPath)(projectRoot);
341
+ const backupPath = `${settingsPath}.bak`;
342
+ if (await fileExists(backupPath)) {
343
+ const restored = await restoreFromBackup(settingsPath, verbose, dryRun);
344
+ if (restored) {
345
+ filesRemoved++;
346
+ (0, constants_1.logVerbose)(`${actionPrefix} Restored VSCode settings from backup`, verbose);
347
+ }
348
+ }
349
+ else if (await fileExists(settingsPath)) {
350
+ try {
351
+ if (dryRun) {
352
+ const settings = await (0, settings_1.readVSCodeSettings)(settingsPath);
353
+ if (settings['augment.advanced']) {
354
+ delete settings['augment.advanced'];
355
+ const remainingKeys = Object.keys(settings);
356
+ if (remainingKeys.length === 0) {
357
+ (0, constants_1.logVerbose)(`${actionPrefix} Would remove empty VSCode settings file`, verbose);
358
+ }
359
+ else {
360
+ (0, constants_1.logVerbose)(`${actionPrefix} Would remove augment.advanced section from ${settingsPath}`, verbose);
361
+ }
362
+ filesRemoved++;
363
+ }
364
+ }
365
+ else {
366
+ const settings = await (0, settings_1.readVSCodeSettings)(settingsPath);
367
+ if (settings['augment.advanced']) {
368
+ delete settings['augment.advanced'];
369
+ const remainingKeys = Object.keys(settings);
370
+ if (remainingKeys.length === 0) {
371
+ await fs_1.promises.unlink(settingsPath);
372
+ (0, constants_1.logVerbose)(`${actionPrefix} Removed empty VSCode settings file`, verbose);
373
+ }
374
+ else {
375
+ await (0, settings_1.writeVSCodeSettings)(settingsPath, settings);
376
+ (0, constants_1.logVerbose)(`${actionPrefix} Removed augment.advanced section from VSCode settings`, verbose);
377
+ }
378
+ filesRemoved++;
379
+ }
380
+ else {
381
+ (0, constants_1.logVerbose)(`No augment.advanced section found in ${settingsPath}`, verbose);
382
+ }
383
+ }
384
+ }
385
+ catch (error) {
386
+ (0, constants_1.logVerbose)(`Failed to process VSCode settings.json: ${error}`, verbose);
387
+ }
388
+ }
286
389
  return filesRemoved;
287
390
  }
288
391
  /**
@@ -407,20 +510,26 @@ async function revertAllAgentConfigs(projectRoot, includedAgents, configPath, ke
407
510
  const mcpPath = await (0, mcp_1.getNativeMcpPath)(agent.getName(), projectRoot);
408
511
  if (mcpPath && mcpPath.startsWith(projectRoot)) {
409
512
  totalFilesProcessed++;
410
- const mcpRestored = await restoreFromBackup(mcpPath, verbose, dryRun);
411
- if (mcpRestored) {
412
- totalFilesRestored++;
413
- if (!keepBackups) {
414
- const mcpBackupRemoved = await removeBackupFile(mcpPath, verbose, dryRun);
415
- if (mcpBackupRemoved) {
416
- totalBackupsRemoved++;
417
- }
418
- }
513
+ if (agent.getName() === 'AugmentCode' &&
514
+ mcpPath.endsWith('.vscode/settings.json')) {
515
+ (0, constants_1.logVerbose)(`Skipping MCP handling for AugmentCode settings.json - handled separately`, verbose);
419
516
  }
420
517
  else {
421
- const mcpRemoved = await removeGeneratedFile(mcpPath, verbose, dryRun);
422
- if (mcpRemoved) {
423
- totalFilesRemoved++;
518
+ const mcpRestored = await restoreFromBackup(mcpPath, verbose, dryRun);
519
+ if (mcpRestored) {
520
+ totalFilesRestored++;
521
+ if (!keepBackups) {
522
+ const mcpBackupRemoved = await removeBackupFile(mcpPath, verbose, dryRun);
523
+ if (mcpBackupRemoved) {
524
+ totalBackupsRemoved++;
525
+ }
526
+ }
527
+ }
528
+ else {
529
+ const mcpRemoved = await removeGeneratedFile(mcpPath, verbose, dryRun);
530
+ if (mcpRemoved) {
531
+ totalFilesRemoved++;
532
+ }
424
533
  }
425
534
  }
426
535
  }
@@ -0,0 +1,117 @@
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.readVSCodeSettings = readVSCodeSettings;
37
+ exports.writeVSCodeSettings = writeVSCodeSettings;
38
+ exports.transformRulerToAugmentMcp = transformRulerToAugmentMcp;
39
+ exports.mergeAugmentMcpServers = mergeAugmentMcpServers;
40
+ exports.getVSCodeSettingsPath = getVSCodeSettingsPath;
41
+ const fs_1 = require("fs");
42
+ const path = __importStar(require("path"));
43
+ /**
44
+ * Read VSCode settings.json file
45
+ */
46
+ async function readVSCodeSettings(settingsPath) {
47
+ try {
48
+ const content = await fs_1.promises.readFile(settingsPath, 'utf8');
49
+ return JSON.parse(content);
50
+ }
51
+ catch (error) {
52
+ if (error.code === 'ENOENT') {
53
+ return {};
54
+ }
55
+ throw error;
56
+ }
57
+ }
58
+ /**
59
+ * Write VSCode settings.json file
60
+ */
61
+ async function writeVSCodeSettings(settingsPath, settings) {
62
+ await fs_1.promises.mkdir(path.dirname(settingsPath), { recursive: true });
63
+ await fs_1.promises.writeFile(settingsPath, JSON.stringify(settings, null, 4));
64
+ }
65
+ /**
66
+ * Transform ruler MCP config to Augment MCP server array format
67
+ */
68
+ function transformRulerToAugmentMcp(rulerMcpJson) {
69
+ const servers = [];
70
+ if (rulerMcpJson.mcpServers && typeof rulerMcpJson.mcpServers === 'object') {
71
+ const mcpServers = rulerMcpJson.mcpServers;
72
+ for (const [name, serverConfig] of Object.entries(mcpServers)) {
73
+ const augmentServer = {
74
+ name,
75
+ command: serverConfig.command,
76
+ };
77
+ if (serverConfig.args) {
78
+ augmentServer.args = serverConfig.args;
79
+ }
80
+ if (serverConfig.env) {
81
+ augmentServer.env = serverConfig.env;
82
+ }
83
+ servers.push(augmentServer);
84
+ }
85
+ }
86
+ return servers;
87
+ }
88
+ /**
89
+ * Merge MCP servers into VSCode settings using the specified strategy
90
+ */
91
+ function mergeAugmentMcpServers(existingSettings, newServers, strategy) {
92
+ const result = structuredClone(existingSettings);
93
+ if (!result['augment.advanced']) {
94
+ result['augment.advanced'] = {};
95
+ }
96
+ if (strategy === 'overwrite') {
97
+ result['augment.advanced'].mcpServers = newServers;
98
+ }
99
+ else {
100
+ const existingServers = result['augment.advanced'].mcpServers || [];
101
+ const existingServerMap = new Map();
102
+ for (const server of existingServers) {
103
+ existingServerMap.set(server.name, server);
104
+ }
105
+ for (const newServer of newServers) {
106
+ existingServerMap.set(newServer.name, newServer);
107
+ }
108
+ result['augment.advanced'].mcpServers = Array.from(existingServerMap.values());
109
+ }
110
+ return result;
111
+ }
112
+ /**
113
+ * Get the VSCode settings.json path for a project (local)
114
+ */
115
+ function getVSCodeSettingsPath(projectRoot) {
116
+ return path.join(projectRoot, '.vscode', 'settings.json');
117
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intellectronica/ruler",
3
- "version": "0.2.9",
3
+ "version": "0.2.10",
4
4
  "description": "Ruler — apply the same rules to all coding agents",
5
5
  "main": "dist/lib.js",
6
6
  "scripts": {