@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 +15 -15
- package/dist/agents/AugmentCodeAgent.js +9 -19
- package/dist/lib.js +23 -1
- package/dist/paths/mcp.js +1 -2
- package/dist/revert.js +169 -60
- package/dist/vscode/settings.js +117 -0
- package/package.json +1 -1
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)
|
|
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
|
|
48
|
-
| Firebase Studio | `.idx/airules.md`
|
|
49
|
-
| Open Hands | `.openhands/microagents/repo.md`
|
|
50
|
-
| Gemini CLI | `GEMINI.md`
|
|
51
|
-
| Junie | `.junie/guidelines.md`
|
|
52
|
-
| AugmentCode | `.augment
|
|
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
|
|
39
|
+
const settings_1 = require("../vscode/settings");
|
|
41
40
|
/**
|
|
42
41
|
* AugmentCode agent adapter.
|
|
43
|
-
* Generates .
|
|
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
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
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
|
-
|
|
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, '.
|
|
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
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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
|
|
422
|
-
if (
|
|
423
|
-
|
|
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
|
+
}
|