skiller 0.4.3

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 (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +989 -0
  3. package/dist/agents/AbstractAgent.js +92 -0
  4. package/dist/agents/AgentsMdAgent.js +85 -0
  5. package/dist/agents/AiderAgent.js +108 -0
  6. package/dist/agents/AmazonQCliAgent.js +103 -0
  7. package/dist/agents/AmpAgent.js +13 -0
  8. package/dist/agents/AugmentCodeAgent.js +70 -0
  9. package/dist/agents/ClaudeAgent.js +95 -0
  10. package/dist/agents/ClineAgent.js +53 -0
  11. package/dist/agents/CodexCliAgent.js +143 -0
  12. package/dist/agents/CopilotAgent.js +43 -0
  13. package/dist/agents/CrushAgent.js +128 -0
  14. package/dist/agents/CursorAgent.js +93 -0
  15. package/dist/agents/FirebaseAgent.js +61 -0
  16. package/dist/agents/FirebenderAgent.js +205 -0
  17. package/dist/agents/GeminiCliAgent.js +99 -0
  18. package/dist/agents/GooseAgent.js +58 -0
  19. package/dist/agents/IAgent.js +2 -0
  20. package/dist/agents/JulesAgent.js +14 -0
  21. package/dist/agents/JunieAgent.js +53 -0
  22. package/dist/agents/KiloCodeAgent.js +63 -0
  23. package/dist/agents/KiroAgent.js +50 -0
  24. package/dist/agents/OpenCodeAgent.js +99 -0
  25. package/dist/agents/OpenHandsAgent.js +56 -0
  26. package/dist/agents/QwenCodeAgent.js +82 -0
  27. package/dist/agents/RooCodeAgent.js +139 -0
  28. package/dist/agents/TraeAgent.js +54 -0
  29. package/dist/agents/WarpAgent.js +61 -0
  30. package/dist/agents/WindsurfAgent.js +27 -0
  31. package/dist/agents/ZedAgent.js +132 -0
  32. package/dist/agents/agent-utils.js +37 -0
  33. package/dist/agents/index.js +77 -0
  34. package/dist/cli/commands.js +136 -0
  35. package/dist/cli/handlers.js +221 -0
  36. package/dist/cli/index.js +5 -0
  37. package/dist/constants.js +58 -0
  38. package/dist/core/ConfigLoader.js +274 -0
  39. package/dist/core/FileSystemUtils.js +421 -0
  40. package/dist/core/FrontmatterParser.js +142 -0
  41. package/dist/core/GitignoreUtils.js +171 -0
  42. package/dist/core/RuleProcessor.js +60 -0
  43. package/dist/core/SkillsProcessor.js +528 -0
  44. package/dist/core/SkillsUtils.js +230 -0
  45. package/dist/core/UnifiedConfigLoader.js +432 -0
  46. package/dist/core/UnifiedConfigTypes.js +2 -0
  47. package/dist/core/agent-selection.js +52 -0
  48. package/dist/core/apply-engine.js +668 -0
  49. package/dist/core/config-utils.js +30 -0
  50. package/dist/core/hash.js +24 -0
  51. package/dist/core/revert-engine.js +413 -0
  52. package/dist/lib.js +196 -0
  53. package/dist/mcp/capabilities.js +65 -0
  54. package/dist/mcp/merge.js +39 -0
  55. package/dist/mcp/propagateOpenCodeMcp.js +116 -0
  56. package/dist/mcp/propagateOpenHandsMcp.js +169 -0
  57. package/dist/mcp/validate.js +17 -0
  58. package/dist/paths/mcp.js +120 -0
  59. package/dist/revert.js +186 -0
  60. package/dist/types.js +2 -0
  61. package/dist/vscode/settings.js +117 -0
  62. package/package.json +77 -0
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mapRawAgentConfigs = mapRawAgentConfigs;
4
+ /**
5
+ * Maps raw agent configuration keys to their corresponding agent identifiers.
6
+ *
7
+ * This function normalizes configuration keys by matching them against agent identifiers
8
+ * and display names. It performs both exact matching (case-insensitive) with agent
9
+ * identifiers and substring matching (case-insensitive) with agent display names
10
+ * for backwards compatibility.
11
+ *
12
+ * @param raw Raw agent configurations with user-provided keys
13
+ * @param agents Array of all available agents
14
+ * @returns Record with agent identifiers as keys and their configurations as values
15
+ */
16
+ function mapRawAgentConfigs(raw, agents) {
17
+ const mappedConfigs = {};
18
+ for (const [key, cfg] of Object.entries(raw)) {
19
+ const lowerKey = key.toLowerCase();
20
+ for (const agent of agents) {
21
+ 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)) {
25
+ mappedConfigs[identifier] = cfg;
26
+ }
27
+ }
28
+ }
29
+ return mappedConfigs;
30
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sha256 = sha256;
4
+ exports.stableJson = stableJson;
5
+ const crypto_1 = require("crypto");
6
+ function sha256(data) {
7
+ return (0, crypto_1.createHash)('sha256').update(data, 'utf8').digest('hex');
8
+ }
9
+ // Stable JSON stringify: sorts object keys recursively.
10
+ function stableJson(value) {
11
+ return JSON.stringify(sortValue(value));
12
+ }
13
+ function sortValue(value) {
14
+ if (Array.isArray(value)) {
15
+ return value.map(sortValue);
16
+ }
17
+ if (value && typeof value === 'object') {
18
+ const entries = Object.entries(value)
19
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
20
+ .map(([k, v]) => [k, sortValue(v)]);
21
+ return Object.fromEntries(entries);
22
+ }
23
+ return value;
24
+ }
@@ -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
+ }
package/dist/lib.js ADDED
@@ -0,0 +1,196 @@
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.allAgents = void 0;
37
+ exports.applyAllAgentConfigs = applyAllAgentConfigs;
38
+ const path = __importStar(require("path"));
39
+ const agents_1 = require("./agents");
40
+ Object.defineProperty(exports, "allAgents", { enumerable: true, get: function () { return agents_1.allAgents; } });
41
+ const constants_1 = require("./constants");
42
+ const apply_engine_1 = require("./core/apply-engine");
43
+ const config_utils_1 = require("./core/config-utils");
44
+ const agent_selection_1 = require("./core/agent-selection");
45
+ const agents = agents_1.allAgents;
46
+ /**
47
+ * Resolves skills enabled state based on precedence: CLI flag > ruler.toml > default (enabled)
48
+ */
49
+ function resolveSkillsEnabled(cliFlag, configSetting) {
50
+ return cliFlag !== undefined
51
+ ? cliFlag
52
+ : configSetting !== undefined
53
+ ? configSetting
54
+ : true; // default to enabled
55
+ }
56
+ /**
57
+ * Resolves backup setting from CLI flag, config file, or default.
58
+ * Precedence: CLI > Config > Default (true)
59
+ */
60
+ function resolveBackupEnabled(cliFlag, configSetting) {
61
+ return cliFlag !== undefined
62
+ ? cliFlag
63
+ : configSetting !== undefined
64
+ ? configSetting
65
+ : true; // default to enabled
66
+ }
67
+ /**
68
+ * Applies ruler configurations for all supported AI agents.
69
+ * @param projectRoot Root directory of the project
70
+ */
71
+ /**
72
+ * Applies ruler configurations for selected AI agents.
73
+ * @param projectRoot Root directory of the project
74
+ * @param includedAgents Optional list of agent name filters (case-insensitive substrings)
75
+ */
76
+ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cliMcpEnabled = true, cliMcpStrategy, cliGitignoreEnabled, verbose = false, dryRun = false, localOnly = false, nested = false, cliBackupEnabled, skillsEnabled) {
77
+ // Load configuration and rules
78
+ (0, constants_1.logVerbose)(`Loading configuration from project root: ${projectRoot}`, verbose);
79
+ if (configPath) {
80
+ (0, constants_1.logVerbose)(`Using custom config path: ${configPath}`, verbose);
81
+ }
82
+ let selectedAgents;
83
+ let generatedPaths;
84
+ let loadedConfig;
85
+ if (nested) {
86
+ const hierarchicalConfigs = await (0, apply_engine_1.loadNestedConfigurations)(projectRoot, configPath, localOnly, nested);
87
+ if (hierarchicalConfigs.length === 0) {
88
+ throw new Error('No .ruler directories found');
89
+ }
90
+ (0, constants_1.logWarn)('Nested mode is experimental and may change in future releases.', dryRun);
91
+ // Use the root config for agent selection (all levels share the same agent settings)
92
+ const rootConfigEntry = selectRootConfiguration(hierarchicalConfigs, projectRoot);
93
+ const rootConfig = rootConfigEntry.config;
94
+ loadedConfig = rootConfig;
95
+ rootConfig.cliAgents = includedAgents;
96
+ (0, constants_1.logVerbose)(`Loaded ${hierarchicalConfigs.length} .ruler directory configurations`, verbose);
97
+ (0, constants_1.logVerbose)(`Root configuration has ${Object.keys(rootConfig.agentConfigs).length} agent configs`, verbose);
98
+ for (const configEntry of hierarchicalConfigs) {
99
+ normalizeAgentConfigs(configEntry.config, agents);
100
+ }
101
+ selectedAgents = (0, agent_selection_1.resolveSelectedAgents)(rootConfig, agents);
102
+ (0, constants_1.logVerbose)(`Selected ${selectedAgents.length} agents: ${selectedAgents.map((a) => a.getName()).join(', ')}`, verbose);
103
+ // Propagate skills if enabled - do this for each nested directory
104
+ const skillsEnabledResolved = resolveSkillsEnabled(skillsEnabled, rootConfig.skills?.enabled);
105
+ if (skillsEnabledResolved) {
106
+ const { propagateSkills, generateSkillsFromRules } = await Promise.resolve().then(() => __importStar(require('./core/SkillsProcessor')));
107
+ // Generate skills from .mdc files if enabled
108
+ const generateFromRules = rootConfig.skills?.generate_from_rules ?? false;
109
+ if (generateFromRules) {
110
+ for (const configEntry of hierarchicalConfigs) {
111
+ const nestedRoot = path.dirname(configEntry.rulerDir);
112
+ (0, constants_1.logVerbose)(`Generating skills from .mdc files for nested directory: ${nestedRoot}`, verbose);
113
+ await generateSkillsFromRules(nestedRoot, configEntry.rulerDir, verbose, dryRun);
114
+ }
115
+ }
116
+ // Propagate skills for each nested .ruler directory
117
+ for (const configEntry of hierarchicalConfigs) {
118
+ const nestedRoot = path.dirname(configEntry.rulerDir);
119
+ (0, constants_1.logVerbose)(`Propagating skills for nested directory: ${nestedRoot}`, verbose);
120
+ await propagateSkills(nestedRoot, selectedAgents, skillsEnabledResolved, verbose, dryRun, configEntry.rulerDir);
121
+ }
122
+ }
123
+ // Resolve backup setting: CLI > Config > Default (true)
124
+ const backupResolved = resolveBackupEnabled(cliBackupEnabled, rootConfig.backup?.enabled);
125
+ generatedPaths = await (0, apply_engine_1.processHierarchicalConfigurations)(selectedAgents, hierarchicalConfigs, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backupResolved, skillsEnabledResolved);
126
+ }
127
+ else {
128
+ const singleConfig = await (0, apply_engine_1.loadSingleConfiguration)(projectRoot, configPath, localOnly);
129
+ loadedConfig = singleConfig.config;
130
+ singleConfig.config.cliAgents = includedAgents;
131
+ (0, constants_1.logVerbose)(`Loaded configuration with ${Object.keys(singleConfig.config.agentConfigs).length} agent configs`, verbose);
132
+ (0, constants_1.logVerbose)(`Found .ruler directory with ${singleConfig.concatenatedRules.length} characters of rules`, verbose);
133
+ normalizeAgentConfigs(singleConfig.config, agents);
134
+ selectedAgents = (0, agent_selection_1.resolveSelectedAgents)(singleConfig.config, agents);
135
+ (0, constants_1.logVerbose)(`Selected ${selectedAgents.length} agents: ${selectedAgents.map((a) => a.getName()).join(', ')}`, verbose);
136
+ // Propagate skills if enabled
137
+ const skillsEnabledResolved = resolveSkillsEnabled(skillsEnabled, singleConfig.config.skills?.enabled);
138
+ if (skillsEnabledResolved) {
139
+ const { propagateSkills, generateSkillsFromRules } = await Promise.resolve().then(() => __importStar(require('./core/SkillsProcessor')));
140
+ // Generate skills from .mdc files if enabled
141
+ const generateFromRules = singleConfig.config.skills?.generate_from_rules ?? false;
142
+ if (generateFromRules) {
143
+ (0, constants_1.logVerbose)('Generating skills from .mdc files', verbose);
144
+ await generateSkillsFromRules(projectRoot, singleConfig.rulerDir, verbose, dryRun);
145
+ }
146
+ await propagateSkills(projectRoot, selectedAgents, skillsEnabledResolved, verbose, dryRun, singleConfig.rulerDir);
147
+ }
148
+ // Resolve backup setting: CLI > Config > Default (true)
149
+ const backupResolved = resolveBackupEnabled(cliBackupEnabled, singleConfig.config.backup?.enabled);
150
+ generatedPaths = await (0, apply_engine_1.processSingleConfiguration)(selectedAgents, singleConfig, projectRoot, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backupResolved, skillsEnabledResolved);
151
+ }
152
+ // Add skills-generated paths to gitignore if skills are enabled
153
+ let allGeneratedPaths = generatedPaths;
154
+ const skillsEnabledForGitignore = resolveSkillsEnabled(skillsEnabled, loadedConfig.skills?.enabled);
155
+ if (skillsEnabledForGitignore) {
156
+ // Skills enabled by default or explicitly
157
+ const { getSkillsGitignorePaths } = await Promise.resolve().then(() => __importStar(require('./core/SkillsProcessor')));
158
+ const skillsPaths = await getSkillsGitignorePaths(projectRoot);
159
+ allGeneratedPaths = [...generatedPaths, ...skillsPaths];
160
+ }
161
+ await (0, apply_engine_1.updateGitignore)(projectRoot, allGeneratedPaths, loadedConfig, cliGitignoreEnabled, dryRun);
162
+ }
163
+ /**
164
+ * Normalizes per-agent config keys to agent identifiers for consistent lookup.
165
+ * Maps both exact identifier matches and substring matches with agent names.
166
+ * @param config The configuration object to normalize
167
+ * @param agents Array of available agents
168
+ */
169
+ function normalizeAgentConfigs(config, agents) {
170
+ // Normalize per-agent config keys to agent identifiers (exact match or substring match)
171
+ config.agentConfigs = (0, config_utils_1.mapRawAgentConfigs)(config.agentConfigs, agents);
172
+ }
173
+ function selectRootConfiguration(configurations, projectRoot) {
174
+ if (configurations.length === 0) {
175
+ throw new Error('No hierarchical configurations available');
176
+ }
177
+ const normalizedProjectRoot = path.resolve(projectRoot);
178
+ let bestIndex = -1;
179
+ let bestDepth = Number.POSITIVE_INFINITY;
180
+ for (let i = 0; i < configurations.length; i++) {
181
+ const entry = configurations[i];
182
+ const normalizedDir = path.resolve(entry.rulerDir);
183
+ if (!normalizedDir.startsWith(normalizedProjectRoot)) {
184
+ continue;
185
+ }
186
+ const depth = normalizedDir.split(path.sep).length;
187
+ if (depth < bestDepth) {
188
+ bestDepth = depth;
189
+ bestIndex = i;
190
+ }
191
+ }
192
+ if (bestIndex === -1) {
193
+ return configurations[0];
194
+ }
195
+ return configurations[bestIndex];
196
+ }