@intellectronica/ruler 0.3.42 → 0.3.44
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 +98 -11
- package/dist/agents/AbstractAgent.js +3 -2
- package/dist/agents/AgentsMdAgent.js +3 -2
- package/dist/agents/AiderAgent.js +4 -3
- package/dist/agents/AmazonQCliAgent.js +6 -4
- package/dist/agents/AugmentCodeAgent.js +3 -2
- package/dist/agents/CodexCliAgent.js +1 -1
- package/dist/agents/CopilotAgent.js +1 -1
- package/dist/agents/CrushAgent.d.ts +1 -1
- package/dist/agents/CrushAgent.js +15 -6
- package/dist/agents/FirebenderAgent.js +5 -4
- package/dist/agents/GeminiCliAgent.d.ts +1 -0
- package/dist/agents/GeminiCliAgent.js +11 -5
- package/dist/agents/IAgent.d.ts +2 -0
- package/dist/agents/MistralVibeAgent.js +14 -3
- package/dist/agents/OpenCodeAgent.d.ts +1 -1
- package/dist/agents/OpenCodeAgent.js +10 -3
- package/dist/agents/QwenCodeAgent.d.ts +1 -0
- package/dist/agents/QwenCodeAgent.js +9 -3
- package/dist/agents/RooCodeAgent.js +3 -2
- package/dist/agents/ZedAgent.js +3 -3
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/core/ConfigLoader.d.ts +2 -0
- package/dist/core/ConfigLoader.js +87 -6
- package/dist/core/FileSystemUtils.d.ts +5 -2
- package/dist/core/FileSystemUtils.js +121 -3
- package/dist/core/GitignoreUtils.d.ts +10 -0
- package/dist/core/GitignoreUtils.js +62 -31
- package/dist/core/SkillsProcessor.d.ts +2 -2
- package/dist/core/SkillsProcessor.js +46 -37
- package/dist/core/SkillsUtils.js +4 -1
- package/dist/core/SubagentsProcessor.js +8 -5
- package/dist/core/UnifiedConfigLoader.js +96 -11
- package/dist/core/UnifiedConfigTypes.d.ts +3 -1
- package/dist/core/agent-selection.js +6 -4
- package/dist/core/apply-engine.d.ts +1 -0
- package/dist/core/apply-engine.js +38 -15
- package/dist/core/revert-engine.d.ts +2 -1
- package/dist/core/revert-engine.js +79 -27
- package/dist/lib.js +9 -6
- package/dist/mcp/capabilities.js +2 -2
- package/dist/mcp/merge.js +28 -26
- package/dist/mcp/propagateOpenCodeMcp.d.ts +1 -1
- package/dist/mcp/propagateOpenCodeMcp.js +10 -3
- package/dist/mcp/propagateOpenHandsMcp.d.ts +1 -1
- package/dist/mcp/propagateOpenHandsMcp.js +18 -7
- package/dist/paths/mcp.d.ts +1 -1
- package/dist/paths/mcp.js +12 -5
- package/dist/revert.js +29 -27
- package/dist/vscode/settings.d.ts +1 -1
- package/dist/vscode/settings.js +3 -3
- package/package.json +6 -4
package/dist/paths/mcp.js
CHANGED
|
@@ -39,12 +39,16 @@ exports.readNativeMcpToml = readNativeMcpToml;
|
|
|
39
39
|
exports.writeNativeMcp = writeNativeMcp;
|
|
40
40
|
const path = __importStar(require("path"));
|
|
41
41
|
const fs_1 = require("fs");
|
|
42
|
+
const FileSystemUtils_1 = require("../core/FileSystemUtils");
|
|
43
|
+
function isRecord(value) {
|
|
44
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
45
|
+
}
|
|
42
46
|
/** Determine the native MCP config path for a given agent. */
|
|
43
47
|
async function getNativeMcpPath(adapterName, projectRoot) {
|
|
44
48
|
const candidates = [];
|
|
45
49
|
switch (adapterName) {
|
|
46
50
|
case 'GitHub Copilot':
|
|
47
|
-
candidates.push(path.join(projectRoot, '.
|
|
51
|
+
candidates.push(path.join(projectRoot, '.mcp.json'));
|
|
48
52
|
break;
|
|
49
53
|
case 'Visual Studio':
|
|
50
54
|
candidates.push(path.join(projectRoot, '.mcp.json'));
|
|
@@ -125,7 +129,11 @@ async function readNativeMcp(filePath) {
|
|
|
125
129
|
throw new Error(`Could not read MCP config at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
126
130
|
}
|
|
127
131
|
try {
|
|
128
|
-
|
|
132
|
+
const parsed = JSON.parse(text);
|
|
133
|
+
if (!isRecord(parsed)) {
|
|
134
|
+
throw new Error('must be a JSON object');
|
|
135
|
+
}
|
|
136
|
+
return parsed;
|
|
129
137
|
}
|
|
130
138
|
catch (error) {
|
|
131
139
|
throw new Error(`Invalid MCP config at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -151,8 +159,7 @@ async function readNativeMcpToml(filePath, parseToml) {
|
|
|
151
159
|
}
|
|
152
160
|
}
|
|
153
161
|
/** Write native MCP config to disk, creating parent directories as needed. */
|
|
154
|
-
async function writeNativeMcp(filePath, data) {
|
|
155
|
-
await fs_1.promises.mkdir(path.dirname(filePath), { recursive: true });
|
|
162
|
+
async function writeNativeMcp(filePath, data, containmentRoot) {
|
|
156
163
|
const text = JSON.stringify(data, null, 2) + '\n';
|
|
157
|
-
await
|
|
164
|
+
await (0, FileSystemUtils_1.writeGeneratedFile)(filePath, text, containmentRoot);
|
|
158
165
|
}
|
package/dist/revert.js
CHANGED
|
@@ -46,24 +46,23 @@ const agent_selection_1 = require("./core/agent-selection");
|
|
|
46
46
|
const config_utils_1 = require("./core/config-utils");
|
|
47
47
|
const GitignoreUtils_1 = require("./core/GitignoreUtils");
|
|
48
48
|
const agents = agents_1.allAgents;
|
|
49
|
-
const RULER_IGNORE_START_MARKER = '# START Ruler Generated Files';
|
|
50
|
-
const RULER_IGNORE_END_MARKER = '# END Ruler Generated Files';
|
|
51
49
|
const MANAGED_IGNORE_FILES = ['.gitignore', '.git/info/exclude'];
|
|
52
50
|
/**
|
|
53
51
|
* Reverts ruler configurations for selected AI agents.
|
|
54
52
|
*/
|
|
55
53
|
async function revertAllAgentConfigs(projectRoot, includedAgents, configPath, keepBackups = false, verbose = false, dryRun = false, localOnly = false) {
|
|
56
|
-
|
|
54
|
+
const rulerDir = await FileSystemUtils.findRulerDir(projectRoot, !localOnly);
|
|
55
|
+
if (!rulerDir) {
|
|
56
|
+
throw (0, constants_1.createRulerError)(`.ruler directory not found`, `Searched from: ${projectRoot}`);
|
|
57
|
+
}
|
|
58
|
+
const effectiveProjectRoot = FileSystemUtils.resolveProjectRootForRulerDir(projectRoot, rulerDir);
|
|
59
|
+
(0, constants_1.logVerbose)(`Loading configuration for revert from project root: ${effectiveProjectRoot}`, verbose);
|
|
57
60
|
const config = await (0, ConfigLoader_1.loadConfig)({
|
|
58
|
-
projectRoot,
|
|
61
|
+
projectRoot: effectiveProjectRoot,
|
|
59
62
|
cliAgents: includedAgents,
|
|
60
63
|
configPath,
|
|
61
64
|
checkGlobal: !localOnly,
|
|
62
65
|
});
|
|
63
|
-
const rulerDir = await FileSystemUtils.findRulerDir(projectRoot, !localOnly);
|
|
64
|
-
if (!rulerDir) {
|
|
65
|
-
throw (0, constants_1.createRulerError)(`.ruler directory not found`, `Searched from: ${projectRoot}`);
|
|
66
|
-
}
|
|
67
66
|
(0, constants_1.logVerbose)(`Found .ruler directory at: ${rulerDir}`, verbose);
|
|
68
67
|
// Normalize per-agent config keys to agent identifiers
|
|
69
68
|
config.agentConfigs = (0, config_utils_1.mapRawAgentConfigs)(config.agentConfigs, agents);
|
|
@@ -105,27 +104,35 @@ async function revertAllAgentConfigs(projectRoot, includedAgents, configPath, ke
|
|
|
105
104
|
}
|
|
106
105
|
}
|
|
107
106
|
(0, constants_1.logVerbose)(`Selected agents: ${selected.map((a) => a.getName()).join(', ')}`, verbose);
|
|
107
|
+
const isFullRevert = !config.cliAgents || config.cliAgents.length === 0;
|
|
108
108
|
// Revert configurations for each agent
|
|
109
109
|
let totalFilesProcessed = 0;
|
|
110
110
|
let totalFilesRestored = 0;
|
|
111
111
|
let totalFilesRemoved = 0;
|
|
112
112
|
let totalBackupsRemoved = 0;
|
|
113
|
+
let totalDirectoriesRemoved = 0;
|
|
113
114
|
for (const agent of selected) {
|
|
114
115
|
const prefix = (0, constants_1.actionPrefix)(dryRun);
|
|
115
116
|
console.log(`${prefix} Reverting ${agent.getName()}...`);
|
|
116
117
|
const agentConfig = config.agentConfigs[agent.getIdentifier()];
|
|
117
|
-
const result = await (0, revert_engine_1.revertAgentConfiguration)(agent,
|
|
118
|
+
const result = await (0, revert_engine_1.revertAgentConfiguration)(agent, effectiveProjectRoot, agentConfig, keepBackups, verbose, dryRun);
|
|
118
119
|
totalFilesProcessed += result.restored + result.removed;
|
|
119
120
|
totalFilesRestored += result.restored;
|
|
120
121
|
totalFilesRemoved += result.removed;
|
|
121
122
|
totalBackupsRemoved += result.backupsRemoved;
|
|
123
|
+
if (!isFullRevert) {
|
|
124
|
+
totalDirectoriesRemoved += await (0, revert_engine_1.cleanUpAgentDirectories)(agent, effectiveProjectRoot, agentConfig, verbose, dryRun);
|
|
125
|
+
}
|
|
122
126
|
}
|
|
123
|
-
// Clean up auxiliary files and directories
|
|
124
|
-
const cleanupResult =
|
|
127
|
+
// Clean up auxiliary files and directories only when reverting all agents.
|
|
128
|
+
const cleanupResult = isFullRevert
|
|
129
|
+
? await (0, revert_engine_1.cleanUpAuxiliaryFiles)(effectiveProjectRoot, verbose, dryRun)
|
|
130
|
+
: { additionalFilesRemoved: 0, directoriesRemoved: 0 };
|
|
125
131
|
totalFilesRemoved += cleanupResult.additionalFilesRemoved;
|
|
132
|
+
totalDirectoriesRemoved += cleanupResult.directoriesRemoved;
|
|
126
133
|
// Clean managed ignore blocks if reverting all agents.
|
|
127
|
-
const cleanedIgnoreFiles =
|
|
128
|
-
? await cleanManagedIgnoreFiles(
|
|
134
|
+
const cleanedIgnoreFiles = isFullRevert
|
|
135
|
+
? await cleanManagedIgnoreFiles(effectiveProjectRoot, verbose, dryRun)
|
|
129
136
|
: [];
|
|
130
137
|
// Display summary
|
|
131
138
|
const prefix = (0, constants_1.actionPrefix)(dryRun);
|
|
@@ -138,11 +145,9 @@ async function revertAllAgentConfigs(projectRoot, includedAgents, configPath, ke
|
|
|
138
145
|
console.log(` Files processed: ${totalFilesProcessed}`);
|
|
139
146
|
console.log(` Files restored from backup: ${totalFilesRestored}`);
|
|
140
147
|
console.log(` Generated files removed: ${totalFilesRemoved}`);
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (cleanupResult.directoriesRemoved > 0) {
|
|
145
|
-
console.log(` Empty directories removed: ${cleanupResult.directoriesRemoved}`);
|
|
148
|
+
console.log(` Backup files removed: ${totalBackupsRemoved}`);
|
|
149
|
+
if (totalDirectoriesRemoved > 0) {
|
|
150
|
+
console.log(` Empty directories removed: ${totalDirectoriesRemoved}`);
|
|
146
151
|
}
|
|
147
152
|
for (const ignoreFile of cleanedIgnoreFiles) {
|
|
148
153
|
console.log(` ${ignoreFile} cleaned: yes`);
|
|
@@ -169,10 +174,11 @@ async function cleanIgnoreFile(projectRoot, ignoreFile, verbose, dryRun) {
|
|
|
169
174
|
(0, constants_1.logVerbose)(`No ${ignoreFile} file found`, verbose);
|
|
170
175
|
return false;
|
|
171
176
|
}
|
|
177
|
+
await FileSystemUtils.assertManagedPathInsideRoot(ignorePath, projectRoot, `Refusing to clean ${ignoreFile} through symlinked path`);
|
|
178
|
+
await FileSystemUtils.assertNotSymbolicLink(ignorePath, `Refusing to clean symlinked ${ignoreFile}`);
|
|
172
179
|
const content = await fs_1.promises.readFile(ignorePath, 'utf8');
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
if (startIndex === -1 || endIndex === -1) {
|
|
180
|
+
const cleaned = (0, GitignoreUtils_1.removeCompleteRulerBlocks)(content);
|
|
181
|
+
if (!cleaned.removed) {
|
|
176
182
|
(0, constants_1.logVerbose)(`No ruler-managed block found in ${ignoreFile}`, verbose);
|
|
177
183
|
return false;
|
|
178
184
|
}
|
|
@@ -181,16 +187,12 @@ async function cleanIgnoreFile(projectRoot, ignoreFile, verbose, dryRun) {
|
|
|
181
187
|
(0, constants_1.logVerbose)(`${prefix} Would remove ruler block from ${ignoreFile}`, verbose);
|
|
182
188
|
}
|
|
183
189
|
else {
|
|
184
|
-
|
|
185
|
-
const afterBlock = content.substring(endIndex + RULER_IGNORE_END_MARKER.length);
|
|
186
|
-
let newContent = beforeBlock + afterBlock;
|
|
187
|
-
newContent = newContent.replace(/\n{3,}/g, '\n\n'); // Replace 3+ newlines with 2
|
|
188
|
-
if (newContent.trim() === '') {
|
|
190
|
+
if (cleaned.content.trim() === '') {
|
|
189
191
|
await fs_1.promises.unlink(ignorePath);
|
|
190
192
|
(0, constants_1.logVerbose)(`${prefix} Removed empty ${ignoreFile} file`, verbose);
|
|
191
193
|
}
|
|
192
194
|
else {
|
|
193
|
-
await fs_1.promises.writeFile(ignorePath,
|
|
195
|
+
await fs_1.promises.writeFile(ignorePath, cleaned.content);
|
|
194
196
|
(0, constants_1.logVerbose)(`${prefix} Removed ruler block from ${ignoreFile}`, verbose);
|
|
195
197
|
}
|
|
196
198
|
}
|
|
@@ -25,7 +25,7 @@ export declare function readVSCodeSettings(settingsPath: string): Promise<VSCode
|
|
|
25
25
|
/**
|
|
26
26
|
* Write VSCode settings.json file
|
|
27
27
|
*/
|
|
28
|
-
export declare function writeVSCodeSettings(settingsPath: string, settings: VSCodeSettings): Promise<void>;
|
|
28
|
+
export declare function writeVSCodeSettings(settingsPath: string, settings: VSCodeSettings, containmentRoot?: string): Promise<void>;
|
|
29
29
|
/**
|
|
30
30
|
* Transform ruler MCP config to Augment MCP server array format
|
|
31
31
|
*/
|
package/dist/vscode/settings.js
CHANGED
|
@@ -40,6 +40,7 @@ exports.mergeAugmentMcpServers = mergeAugmentMcpServers;
|
|
|
40
40
|
exports.getVSCodeSettingsPath = getVSCodeSettingsPath;
|
|
41
41
|
const fs_1 = require("fs");
|
|
42
42
|
const path = __importStar(require("path"));
|
|
43
|
+
const FileSystemUtils_1 = require("../core/FileSystemUtils");
|
|
43
44
|
/**
|
|
44
45
|
* Read VSCode settings.json file
|
|
45
46
|
*/
|
|
@@ -58,9 +59,8 @@ async function readVSCodeSettings(settingsPath) {
|
|
|
58
59
|
/**
|
|
59
60
|
* Write VSCode settings.json file
|
|
60
61
|
*/
|
|
61
|
-
async function writeVSCodeSettings(settingsPath, settings) {
|
|
62
|
-
await
|
|
63
|
-
await fs_1.promises.writeFile(settingsPath, JSON.stringify(settings, null, 4));
|
|
62
|
+
async function writeVSCodeSettings(settingsPath, settings, containmentRoot) {
|
|
63
|
+
await (0, FileSystemUtils_1.writeGeneratedFile)(settingsPath, JSON.stringify(settings, null, 4), containmentRoot);
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
66
|
* Transform ruler MCP config to Augment MCP server array format
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intellectronica/ruler",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.44",
|
|
4
4
|
"description": "Ruler — apply the same rules to all coding agents",
|
|
5
5
|
"main": "dist/lib.js",
|
|
6
6
|
"types": "dist/lib.d.ts",
|
|
@@ -12,8 +12,10 @@
|
|
|
12
12
|
"test:watch": "jest --watch",
|
|
13
13
|
"test:coverage": "jest --coverage",
|
|
14
14
|
"test:integration": "jest tests/e2e/ruler.integration.test.ts --verbose",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
15
|
+
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
16
|
+
"build": "npm run clean && tsc",
|
|
17
|
+
"check:package-lock": "node -e \"const pkg=require('./package.json'); const lock=require('./package-lock.json'); const root=lock.packages?.['']; if (lock.version !== pkg.version || root?.version !== pkg.version) { console.error('package-lock.json version metadata must match package.json version'); process.exit(1); }\"",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
17
19
|
},
|
|
18
20
|
"repository": {
|
|
19
21
|
"type": "git",
|
|
@@ -68,7 +70,7 @@
|
|
|
68
70
|
},
|
|
69
71
|
"dependencies": {
|
|
70
72
|
"@iarna/toml": "^2.2.5",
|
|
71
|
-
"js-yaml": "^4.
|
|
73
|
+
"js-yaml": "^4.2.0",
|
|
72
74
|
"yargs": "^18.0.0",
|
|
73
75
|
"zod": "^4.1.12"
|
|
74
76
|
}
|