@intellectronica/ruler 0.2.18 → 0.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/README.md +190 -30
- package/dist/agents/AbstractAgent.js +82 -0
- package/dist/agents/AgentsMdAgent.js +82 -0
- package/dist/agents/AiderAgent.js +29 -9
- package/dist/agents/AmpAgent.js +13 -0
- package/dist/agents/AugmentCodeAgent.js +10 -12
- package/dist/agents/ClaudeAgent.js +9 -9
- package/dist/agents/ClineAgent.js +3 -9
- package/dist/agents/CodexCliAgent.js +27 -21
- package/dist/agents/CopilotAgent.js +9 -10
- package/dist/agents/CrushAgent.js +6 -0
- package/dist/agents/CursorAgent.js +13 -5
- package/dist/agents/FirebaseAgent.js +2 -8
- package/dist/agents/GeminiCliAgent.js +31 -22
- package/dist/agents/GooseAgent.js +2 -10
- package/dist/agents/JulesAgent.js +3 -44
- package/dist/agents/JunieAgent.js +2 -9
- package/dist/agents/KiloCodeAgent.js +8 -9
- package/dist/agents/KiroAgent.js +50 -0
- package/dist/agents/OpenCodeAgent.js +50 -11
- package/dist/agents/OpenHandsAgent.js +8 -9
- package/dist/agents/QwenCodeAgent.js +83 -0
- package/dist/agents/WarpAgent.js +61 -0
- package/dist/agents/WindsurfAgent.js +9 -10
- package/dist/agents/ZedAgent.js +132 -0
- package/dist/agents/agent-utils.js +37 -0
- package/dist/agents/index.js +53 -0
- package/dist/cli/commands.js +48 -242
- package/dist/cli/handlers.js +176 -0
- package/dist/constants.js +9 -2
- package/dist/core/ConfigLoader.js +1 -1
- package/dist/core/FileSystemUtils.js +51 -4
- package/dist/core/RuleProcessor.js +15 -3
- package/dist/core/UnifiedConfigLoader.js +357 -0
- package/dist/core/UnifiedConfigTypes.js +2 -0
- package/dist/core/agent-selection.js +52 -0
- package/dist/core/apply-engine.js +302 -0
- package/dist/core/config-utils.js +30 -0
- package/dist/core/hash.js +24 -0
- package/dist/core/revert-engine.js +413 -0
- package/dist/lib.js +20 -312
- package/dist/mcp/capabilities.js +51 -0
- package/dist/mcp/propagateOpenCodeMcp.js +30 -31
- package/dist/mcp/propagateOpenHandsMcp.js +101 -17
- package/dist/paths/mcp.js +7 -3
- package/dist/revert.js +96 -479
- package/package.json +2 -1
|
@@ -0,0 +1,413 @@
|
|
|
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.revertAgentConfiguration = revertAgentConfiguration;
|
|
37
|
+
exports.cleanUpAuxiliaryFiles = cleanUpAuxiliaryFiles;
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const fs_1 = require("fs");
|
|
40
|
+
const agent_utils_1 = require("../agents/agent-utils");
|
|
41
|
+
const mcp_1 = require("../paths/mcp");
|
|
42
|
+
const constants_1 = require("../constants");
|
|
43
|
+
const settings_1 = require("../vscode/settings");
|
|
44
|
+
/**
|
|
45
|
+
* Checks if a file exists.
|
|
46
|
+
*/
|
|
47
|
+
async function fileExists(filePath) {
|
|
48
|
+
try {
|
|
49
|
+
await fs_1.promises.access(filePath);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Restores a file from its backup if the backup exists.
|
|
58
|
+
*/
|
|
59
|
+
async function restoreFromBackup(filePath, verbose, dryRun) {
|
|
60
|
+
const backupPath = `${filePath}.bak`;
|
|
61
|
+
const backupExists = await fileExists(backupPath);
|
|
62
|
+
if (!backupExists) {
|
|
63
|
+
(0, constants_1.logVerbose)(`No backup found for: ${filePath}`, verbose);
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const prefix = (0, constants_1.actionPrefix)(dryRun);
|
|
67
|
+
if (dryRun) {
|
|
68
|
+
(0, constants_1.logVerbose)(`${prefix} Would restore: ${filePath} from backup`, verbose);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
await fs_1.promises.copyFile(backupPath, filePath);
|
|
72
|
+
(0, constants_1.logVerbose)(`${prefix} Restored: ${filePath} from backup`, verbose);
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Removes a file if it exists and has no backup (meaning it was generated by ruler).
|
|
78
|
+
*/
|
|
79
|
+
async function removeGeneratedFile(filePath, verbose, dryRun) {
|
|
80
|
+
const fileExistsFlag = await fileExists(filePath);
|
|
81
|
+
const backupExists = await fileExists(`${filePath}.bak`);
|
|
82
|
+
if (!fileExistsFlag) {
|
|
83
|
+
(0, constants_1.logVerbose)(`File does not exist: ${filePath}`, verbose);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
if (backupExists) {
|
|
87
|
+
(0, constants_1.logVerbose)(`File has backup, skipping removal: ${filePath}`, verbose);
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const prefix = (0, constants_1.actionPrefix)(dryRun);
|
|
91
|
+
if (dryRun) {
|
|
92
|
+
(0, constants_1.logVerbose)(`${prefix} Would remove generated file: ${filePath}`, verbose);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
await fs_1.promises.unlink(filePath);
|
|
96
|
+
(0, constants_1.logVerbose)(`${prefix} Removed generated file: ${filePath}`, verbose);
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Removes backup files.
|
|
102
|
+
*/
|
|
103
|
+
async function removeBackupFile(filePath, verbose, dryRun) {
|
|
104
|
+
const backupPath = `${filePath}.bak`;
|
|
105
|
+
const backupExists = await fileExists(backupPath);
|
|
106
|
+
if (!backupExists) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
const prefix = (0, constants_1.actionPrefix)(dryRun);
|
|
110
|
+
if (dryRun) {
|
|
111
|
+
(0, constants_1.logVerbose)(`${prefix} Would remove backup file: ${backupPath}`, verbose);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
await fs_1.promises.unlink(backupPath);
|
|
115
|
+
(0, constants_1.logVerbose)(`${prefix} Removed backup file: ${backupPath}`, verbose);
|
|
116
|
+
}
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Recursively checks if a directory contains only empty directories
|
|
121
|
+
*/
|
|
122
|
+
async function isDirectoryTreeEmpty(dirPath) {
|
|
123
|
+
try {
|
|
124
|
+
const entries = await fs_1.promises.readdir(dirPath);
|
|
125
|
+
if (entries.length === 0) {
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
for (const entry of entries) {
|
|
129
|
+
const entryPath = path.join(dirPath, entry);
|
|
130
|
+
const entryStat = await fs_1.promises.stat(entryPath);
|
|
131
|
+
if (entryStat.isFile()) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
else if (entryStat.isDirectory()) {
|
|
135
|
+
const isEmpty = await isDirectoryTreeEmpty(entryPath);
|
|
136
|
+
if (!isEmpty) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Helper function to execute directory removal with consistent dry-run handling and logging.
|
|
149
|
+
*/
|
|
150
|
+
async function executeDirectoryAction(dirPath, action, verbose, dryRun) {
|
|
151
|
+
const prefix = (0, constants_1.actionPrefix)(dryRun);
|
|
152
|
+
const actionText = action === 'remove-tree' ? 'directory tree' : 'directory';
|
|
153
|
+
if (dryRun) {
|
|
154
|
+
(0, constants_1.logVerbose)(`${prefix} Would remove empty ${actionText}: ${dirPath}`, verbose);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
await fs_1.promises.rm(dirPath, { recursive: true });
|
|
158
|
+
(0, constants_1.logVerbose)(`${prefix} Removed empty ${actionText}: ${dirPath}`, verbose);
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Attempts to remove a single empty directory if it exists and is empty.
|
|
164
|
+
*/
|
|
165
|
+
async function removeEmptyDirectory(dirPath, verbose, dryRun, logMissing = false) {
|
|
166
|
+
try {
|
|
167
|
+
const stat = await fs_1.promises.stat(dirPath);
|
|
168
|
+
if (!stat.isDirectory()) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
const isEmpty = await isDirectoryTreeEmpty(dirPath);
|
|
172
|
+
if (isEmpty) {
|
|
173
|
+
return await executeDirectoryAction(dirPath, 'remove-tree', verbose, dryRun);
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
if (logMissing) {
|
|
179
|
+
(0, constants_1.logVerbose)(`Directory ${dirPath} doesn't exist or can't be accessed`, verbose);
|
|
180
|
+
}
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Handles special cleanup logic for .augment directory and its rules subdirectory.
|
|
186
|
+
*/
|
|
187
|
+
async function removeAugmentDirectory(projectRoot, verbose, dryRun) {
|
|
188
|
+
const augmentDir = path.join(projectRoot, '.augment');
|
|
189
|
+
let directoriesRemoved = 0;
|
|
190
|
+
try {
|
|
191
|
+
const augmentStat = await fs_1.promises.stat(augmentDir);
|
|
192
|
+
if (!augmentStat.isDirectory()) {
|
|
193
|
+
return 0;
|
|
194
|
+
}
|
|
195
|
+
const rulesDir = path.join(augmentDir, 'rules');
|
|
196
|
+
const rulesRemoved = await removeEmptyDirectory(rulesDir, verbose, dryRun);
|
|
197
|
+
if (rulesRemoved) {
|
|
198
|
+
directoriesRemoved++;
|
|
199
|
+
}
|
|
200
|
+
const augmentRemoved = await removeEmptyDirectory(augmentDir, verbose, dryRun);
|
|
201
|
+
if (augmentRemoved) {
|
|
202
|
+
directoriesRemoved++;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// .augment directory doesn't exist, that's fine. leaving comment as catch block can't be kept empty.
|
|
207
|
+
}
|
|
208
|
+
return directoriesRemoved;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Removes empty directories that were created by ruler.
|
|
212
|
+
* Only removes directories if they are empty and were likely created by ruler.
|
|
213
|
+
* Special handling for .augment directory to clean up rules subdirectory.
|
|
214
|
+
*/
|
|
215
|
+
async function removeEmptyDirectories(projectRoot, verbose, dryRun) {
|
|
216
|
+
const rulerCreatedDirs = [
|
|
217
|
+
'.github',
|
|
218
|
+
'.cursor',
|
|
219
|
+
'.windsurf',
|
|
220
|
+
'.junie',
|
|
221
|
+
'.openhands',
|
|
222
|
+
'.idx',
|
|
223
|
+
'.gemini',
|
|
224
|
+
'.vscode',
|
|
225
|
+
'.augmentcode',
|
|
226
|
+
'.kilocode',
|
|
227
|
+
];
|
|
228
|
+
let directoriesRemoved = 0;
|
|
229
|
+
// Handle .augment directory with special logic
|
|
230
|
+
directoriesRemoved += await removeAugmentDirectory(projectRoot, verbose, dryRun);
|
|
231
|
+
// Handle all other ruler-created directories
|
|
232
|
+
for (const dirName of rulerCreatedDirs) {
|
|
233
|
+
const dirPath = path.join(projectRoot, dirName);
|
|
234
|
+
const removed = await removeEmptyDirectory(dirPath, verbose, dryRun, true);
|
|
235
|
+
if (removed) {
|
|
236
|
+
directoriesRemoved++;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return directoriesRemoved;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Removes additional files created by specific agents that aren't covered by their main output paths.
|
|
243
|
+
*/
|
|
244
|
+
async function removeAdditionalAgentFiles(projectRoot, verbose, dryRun) {
|
|
245
|
+
const additionalFiles = [
|
|
246
|
+
'.gemini/settings.json',
|
|
247
|
+
'.mcp.json',
|
|
248
|
+
'.vscode/mcp.json',
|
|
249
|
+
'.cursor/mcp.json',
|
|
250
|
+
'.kilocode/mcp.json',
|
|
251
|
+
'config.toml',
|
|
252
|
+
];
|
|
253
|
+
let filesRemoved = 0;
|
|
254
|
+
const prefix = (0, constants_1.actionPrefix)(dryRun);
|
|
255
|
+
for (const filePath of additionalFiles) {
|
|
256
|
+
const fullPath = path.join(projectRoot, filePath);
|
|
257
|
+
try {
|
|
258
|
+
const fileExistsFlag = await fileExists(fullPath);
|
|
259
|
+
if (!fileExistsFlag) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
const backupExists = await fileExists(`${fullPath}.bak`);
|
|
263
|
+
if (backupExists) {
|
|
264
|
+
const restored = await restoreFromBackup(fullPath, verbose, dryRun);
|
|
265
|
+
if (restored) {
|
|
266
|
+
filesRemoved++;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
if (dryRun) {
|
|
271
|
+
(0, constants_1.logVerbose)(`${prefix} Would remove additional file: ${fullPath}`, verbose);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
await fs_1.promises.unlink(fullPath);
|
|
275
|
+
(0, constants_1.logVerbose)(`${prefix} Removed additional file: ${fullPath}`, verbose);
|
|
276
|
+
}
|
|
277
|
+
filesRemoved++;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
(0, constants_1.logVerbose)(`Additional file ${fullPath} doesn't exist or can't be accessed`, verbose);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const settingsPath = (0, settings_1.getVSCodeSettingsPath)(projectRoot);
|
|
285
|
+
const backupPath = `${settingsPath}.bak`;
|
|
286
|
+
if (await fileExists(backupPath)) {
|
|
287
|
+
const restored = await restoreFromBackup(settingsPath, verbose, dryRun);
|
|
288
|
+
if (restored) {
|
|
289
|
+
filesRemoved++;
|
|
290
|
+
(0, constants_1.logVerbose)(`${constants_1.actionPrefix} Restored VSCode settings from backup`, verbose);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
else if (await fileExists(settingsPath)) {
|
|
294
|
+
try {
|
|
295
|
+
if (dryRun) {
|
|
296
|
+
const settings = await (0, settings_1.readVSCodeSettings)(settingsPath);
|
|
297
|
+
if (settings['augment.advanced']) {
|
|
298
|
+
delete settings['augment.advanced'];
|
|
299
|
+
const remainingKeys = Object.keys(settings);
|
|
300
|
+
if (remainingKeys.length === 0) {
|
|
301
|
+
(0, constants_1.logVerbose)(`${constants_1.actionPrefix} Would remove empty VSCode settings file`, verbose);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
(0, constants_1.logVerbose)(`${constants_1.actionPrefix} Would remove augment.advanced section from ${settingsPath}`, verbose);
|
|
305
|
+
}
|
|
306
|
+
filesRemoved++;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
const settings = await (0, settings_1.readVSCodeSettings)(settingsPath);
|
|
311
|
+
if (settings['augment.advanced']) {
|
|
312
|
+
delete settings['augment.advanced'];
|
|
313
|
+
const remainingKeys = Object.keys(settings);
|
|
314
|
+
if (remainingKeys.length === 0) {
|
|
315
|
+
await fs_1.promises.unlink(settingsPath);
|
|
316
|
+
(0, constants_1.logVerbose)(`${constants_1.actionPrefix} Removed empty VSCode settings file`, verbose);
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
await (0, settings_1.writeVSCodeSettings)(settingsPath, settings);
|
|
320
|
+
(0, constants_1.logVerbose)(`${constants_1.actionPrefix} Removed augment.advanced section from VSCode settings`, verbose);
|
|
321
|
+
}
|
|
322
|
+
filesRemoved++;
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
(0, constants_1.logVerbose)(`No augment.advanced section found in ${settingsPath}`, verbose);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
(0, constants_1.logVerbose)(`Failed to process VSCode settings.json: ${error}`, verbose);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return filesRemoved;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Reverts configuration for a single agent.
|
|
337
|
+
* @param agent The agent to revert
|
|
338
|
+
* @param projectRoot Root directory of the project
|
|
339
|
+
* @param agentConfig Agent-specific configuration
|
|
340
|
+
* @param keepBackups Whether to keep backup files
|
|
341
|
+
* @param verbose Whether to enable verbose logging
|
|
342
|
+
* @param dryRun Whether to perform a dry run
|
|
343
|
+
* @returns Promise resolving to revert statistics
|
|
344
|
+
*/
|
|
345
|
+
async function revertAgentConfiguration(agent, projectRoot, agentConfig, keepBackups, verbose, dryRun) {
|
|
346
|
+
const result = {
|
|
347
|
+
restored: 0,
|
|
348
|
+
removed: 0,
|
|
349
|
+
backupsRemoved: 0,
|
|
350
|
+
};
|
|
351
|
+
const outputPaths = (0, agent_utils_1.getAgentOutputPaths)(agent, projectRoot, agentConfig);
|
|
352
|
+
(0, constants_1.logVerbose)(`Agent ${agent.getName()} output paths: ${outputPaths.join(', ')}`, verbose);
|
|
353
|
+
for (const outputPath of outputPaths) {
|
|
354
|
+
const restored = await restoreFromBackup(outputPath, verbose, dryRun);
|
|
355
|
+
if (restored) {
|
|
356
|
+
result.restored++;
|
|
357
|
+
if (!keepBackups) {
|
|
358
|
+
const backupRemoved = await removeBackupFile(outputPath, verbose, dryRun);
|
|
359
|
+
if (backupRemoved) {
|
|
360
|
+
result.backupsRemoved++;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
const removed = await removeGeneratedFile(outputPath, verbose, dryRun);
|
|
366
|
+
if (removed) {
|
|
367
|
+
result.removed++;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// Handle MCP files
|
|
372
|
+
const mcpPath = await (0, mcp_1.getNativeMcpPath)(agent.getName(), projectRoot);
|
|
373
|
+
if (mcpPath && mcpPath.startsWith(projectRoot)) {
|
|
374
|
+
if (agent.getName() === 'AugmentCode' &&
|
|
375
|
+
mcpPath.endsWith('.vscode/settings.json')) {
|
|
376
|
+
(0, constants_1.logVerbose)(`Skipping MCP handling for AugmentCode settings.json - handled separately`, verbose);
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
const mcpRestored = await restoreFromBackup(mcpPath, verbose, dryRun);
|
|
380
|
+
if (mcpRestored) {
|
|
381
|
+
result.restored++;
|
|
382
|
+
if (!keepBackups) {
|
|
383
|
+
const mcpBackupRemoved = await removeBackupFile(mcpPath, verbose, dryRun);
|
|
384
|
+
if (mcpBackupRemoved) {
|
|
385
|
+
result.backupsRemoved++;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
const mcpRemoved = await removeGeneratedFile(mcpPath, verbose, dryRun);
|
|
391
|
+
if (mcpRemoved) {
|
|
392
|
+
result.removed++;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return result;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Cleans up auxiliary files and directories.
|
|
401
|
+
* @param projectRoot Root directory of the project
|
|
402
|
+
* @param verbose Whether to enable verbose logging
|
|
403
|
+
* @param dryRun Whether to perform a dry run
|
|
404
|
+
* @returns Promise resolving to cleanup statistics
|
|
405
|
+
*/
|
|
406
|
+
async function cleanUpAuxiliaryFiles(projectRoot, verbose, dryRun) {
|
|
407
|
+
const additionalFilesRemoved = await removeAdditionalAgentFiles(projectRoot, verbose, dryRun);
|
|
408
|
+
const directoriesRemoved = await removeEmptyDirectories(projectRoot, verbose, dryRun);
|
|
409
|
+
return {
|
|
410
|
+
additionalFilesRemoved,
|
|
411
|
+
directoriesRemoved,
|
|
412
|
+
};
|
|
413
|
+
}
|