@intellectronica/ruler 0.2.16 → 0.2.18
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 +1 -1
- package/dist/agents/CrushAgent.js +88 -0
- package/dist/cli/commands.js +9 -4
- package/dist/lib.js +4 -2
- package/dist/mcp/merge.js +11 -3
- package/dist/mcp/propagateOpenHandsMcp.js +1 -0
- package/dist/mcp/validate.js +1 -1
- package/dist/paths/mcp.js +1 -1
- package/dist/revert.js +0 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ Ruler solves this by providing a **single source of truth** for all your AI agen
|
|
|
39
39
|
| Agent | Rules File(s) | MCP Configuration |
|
|
40
40
|
| ---------------- | ------------------------------------------------ | --------------------------------------------------- |
|
|
41
41
|
| GitHub Copilot | `.github/copilot-instructions.md` | `.vscode/mcp.json` |
|
|
42
|
-
| Claude Code | `CLAUDE.md` |
|
|
42
|
+
| Claude Code | `CLAUDE.md` | `.mcp.json` |
|
|
43
43
|
| OpenAI Codex CLI | `AGENTS.md` | `.codex/config.toml`, `~/.codex/config.json` |
|
|
44
44
|
| Jules | `AGENTS.md` | - |
|
|
45
45
|
| Cursor | `.cursor/rules/ruler_cursor_instructions.mdc` | `.cursor/mcp.json`, `~/.cursor/mcp.json` |
|
|
@@ -0,0 +1,88 @@
|
|
|
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.CrushAgent = void 0;
|
|
37
|
+
const fs = __importStar(require("fs/promises"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
class CrushAgent {
|
|
40
|
+
getIdentifier() {
|
|
41
|
+
return 'crush';
|
|
42
|
+
}
|
|
43
|
+
getName() {
|
|
44
|
+
return 'Crush';
|
|
45
|
+
}
|
|
46
|
+
getDefaultOutputPath(projectRoot) {
|
|
47
|
+
return {
|
|
48
|
+
instructions: path.join(projectRoot, 'CRUSH.md'),
|
|
49
|
+
mcp: path.join(projectRoot, '.crush.json'),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig) {
|
|
53
|
+
const outputPaths = this.getDefaultOutputPath(projectRoot);
|
|
54
|
+
const instructionsPath = agentConfig?.outputPathInstructions ?? outputPaths['instructions'];
|
|
55
|
+
const mcpPath = agentConfig?.outputPathConfig ?? outputPaths['mcp'];
|
|
56
|
+
await fs.writeFile(instructionsPath, concatenatedRules);
|
|
57
|
+
// Always transform from mcpServers ({ mcpServers: ... }) to { mcp: ... } for Crush
|
|
58
|
+
let finalMcpConfig = { mcp: {} };
|
|
59
|
+
try {
|
|
60
|
+
const existingMcpConfig = JSON.parse(await fs.readFile(mcpPath, 'utf-8'));
|
|
61
|
+
if (existingMcpConfig && typeof existingMcpConfig === 'object') {
|
|
62
|
+
finalMcpConfig = {
|
|
63
|
+
...existingMcpConfig,
|
|
64
|
+
mcp: {
|
|
65
|
+
...(existingMcpConfig.mcp || {}),
|
|
66
|
+
...(rulerMcpJson?.mcpServers ?? {}),
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
else if (rulerMcpJson) {
|
|
71
|
+
finalMcpConfig = {
|
|
72
|
+
mcp: (rulerMcpJson?.mcpServers ?? {}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
if (rulerMcpJson) {
|
|
78
|
+
finalMcpConfig = {
|
|
79
|
+
mcp: (rulerMcpJson?.mcpServers ?? {}),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (Object.keys(finalMcpConfig.mcp).length > 0) {
|
|
84
|
+
await fs.writeFile(mcpPath, JSON.stringify(finalMcpConfig, null, 2));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.CrushAgent = CrushAgent;
|
package/dist/cli/commands.js
CHANGED
|
@@ -60,7 +60,7 @@ function run() {
|
|
|
60
60
|
});
|
|
61
61
|
y.option('agents', {
|
|
62
62
|
type: 'string',
|
|
63
|
-
description: 'Comma-separated list of agent identifiers: copilot, claude, codex, cursor, windsurf, cline, aider, firebase, gemini-cli, junie, kilocode, opencode',
|
|
63
|
+
description: 'Comma-separated list of agent identifiers: copilot, claude, codex, cursor, windsurf, cline, aider, firebase, gemini-cli, junie, kilocode, opencode, crush',
|
|
64
64
|
});
|
|
65
65
|
y.option('config', {
|
|
66
66
|
type: 'string',
|
|
@@ -234,10 +234,15 @@ and apply them to your configured AI coding agents.
|
|
|
234
234
|
const DEFAULT_MCP_JSON = `{
|
|
235
235
|
"mcpServers": {
|
|
236
236
|
"example": {
|
|
237
|
-
"
|
|
237
|
+
"type": "stdio",
|
|
238
|
+
"command": "node",
|
|
239
|
+
"args": ["/path/to/mcp-server.js"],
|
|
240
|
+
"env": {
|
|
241
|
+
"NODE_ENV": "production"
|
|
242
|
+
}
|
|
238
243
|
}
|
|
239
244
|
}
|
|
240
|
-
}`;
|
|
245
|
+
}\n`;
|
|
241
246
|
if (!(await exists(mcpPath))) {
|
|
242
247
|
await fs_1.promises.writeFile(mcpPath, DEFAULT_MCP_JSON);
|
|
243
248
|
console.log(`[ruler] Created ${mcpPath}`);
|
|
@@ -254,7 +259,7 @@ and apply them to your configured AI coding agents.
|
|
|
254
259
|
});
|
|
255
260
|
y.option('agents', {
|
|
256
261
|
type: 'string',
|
|
257
|
-
description: 'Comma-separated list of agent identifiers: copilot, claude, codex, cursor, windsurf, cline, aider, firebase, gemini-cli, junie, kilocode, opencode',
|
|
262
|
+
description: 'Comma-separated list of agent identifiers: copilot, claude, codex, cursor, windsurf, cline, aider, firebase, gemini-cli, junie, kilocode, opencode, crush',
|
|
258
263
|
});
|
|
259
264
|
y.option('config', {
|
|
260
265
|
type: 'string',
|
package/dist/lib.js
CHANGED
|
@@ -45,7 +45,7 @@ const ClaudeAgent_1 = require("./agents/ClaudeAgent");
|
|
|
45
45
|
const CodexCliAgent_1 = require("./agents/CodexCliAgent");
|
|
46
46
|
const CursorAgent_1 = require("./agents/CursorAgent");
|
|
47
47
|
const WindsurfAgent_1 = require("./agents/WindsurfAgent");
|
|
48
|
-
const
|
|
48
|
+
const ClineAgent_1 = require("./agents/ClineAgent");
|
|
49
49
|
const AiderAgent_1 = require("./agents/AiderAgent");
|
|
50
50
|
const FirebaseAgent_1 = require("./agents/FirebaseAgent");
|
|
51
51
|
const OpenHandsAgent_1 = require("./agents/OpenHandsAgent");
|
|
@@ -55,6 +55,7 @@ const JunieAgent_1 = require("./agents/JunieAgent");
|
|
|
55
55
|
const AugmentCodeAgent_1 = require("./agents/AugmentCodeAgent");
|
|
56
56
|
const KiloCodeAgent_1 = require("./agents/KiloCodeAgent");
|
|
57
57
|
const OpenCodeAgent_1 = require("./agents/OpenCodeAgent");
|
|
58
|
+
const CrushAgent_1 = require("./agents/CrushAgent");
|
|
58
59
|
const GooseAgent_1 = require("./agents/GooseAgent");
|
|
59
60
|
const merge_1 = require("./mcp/merge");
|
|
60
61
|
const validate_1 = require("./mcp/validate");
|
|
@@ -102,7 +103,7 @@ const agents = [
|
|
|
102
103
|
new CodexCliAgent_1.CodexCliAgent(),
|
|
103
104
|
new CursorAgent_1.CursorAgent(),
|
|
104
105
|
new WindsurfAgent_1.WindsurfAgent(),
|
|
105
|
-
new
|
|
106
|
+
new ClineAgent_1.ClineAgent(),
|
|
106
107
|
new AiderAgent_1.AiderAgent(),
|
|
107
108
|
new FirebaseAgent_1.FirebaseAgent(),
|
|
108
109
|
new OpenHandsAgent_1.OpenHandsAgent(),
|
|
@@ -113,6 +114,7 @@ const agents = [
|
|
|
113
114
|
new KiloCodeAgent_1.KiloCodeAgent(),
|
|
114
115
|
new OpenCodeAgent_1.OpenCodeAgent(),
|
|
115
116
|
new GooseAgent_1.GooseAgent(),
|
|
117
|
+
new CrushAgent_1.CrushAgent(),
|
|
116
118
|
];
|
|
117
119
|
/**
|
|
118
120
|
* Applies ruler configurations for all supported AI agents.
|
package/dist/mcp/merge.js
CHANGED
|
@@ -12,15 +12,23 @@ exports.mergeMcp = mergeMcp;
|
|
|
12
12
|
function mergeMcp(base, incoming, strategy, serverKey) {
|
|
13
13
|
if (strategy === 'overwrite') {
|
|
14
14
|
// Ensure the incoming object uses the correct server key.
|
|
15
|
-
|
|
15
|
+
// Transform from the standard (Crush) MCP config format
|
|
16
|
+
const incomingServers = incoming[serverKey] ||
|
|
17
|
+
incoming.mcpServers ||
|
|
18
|
+
incoming.mcp ||
|
|
19
|
+
{};
|
|
16
20
|
return {
|
|
17
21
|
[serverKey]: incomingServers,
|
|
18
22
|
};
|
|
19
23
|
}
|
|
20
24
|
const baseServers = base[serverKey] ||
|
|
21
25
|
base.mcpServers ||
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
base.mcp ||
|
|
27
|
+
{};
|
|
28
|
+
const incomingServers = incoming[serverKey] ||
|
|
29
|
+
incoming.mcpServers ||
|
|
30
|
+
incoming.mcp ||
|
|
31
|
+
{};
|
|
24
32
|
const mergedServers = { ...baseServers, ...incomingServers };
|
|
25
33
|
const newBase = { ...base };
|
|
26
34
|
delete newBase.mcpServers; // Remove old key if present
|
|
@@ -48,6 +48,7 @@ async function propagateMcpToOpenHands(rulerMcpPath, openHandsConfigPath) {
|
|
|
48
48
|
catch {
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
|
+
// Always use the legacy Ruler MCP config format as input (top-level "mcpServers" key)
|
|
51
52
|
const rulerServers = rulerMcp.mcpServers || {};
|
|
52
53
|
let config = {};
|
|
53
54
|
try {
|
package/dist/mcp/validate.js
CHANGED
|
@@ -12,6 +12,6 @@ function validateMcp(data) {
|
|
|
12
12
|
typeof data !== 'object' ||
|
|
13
13
|
!('mcpServers' in data) ||
|
|
14
14
|
typeof data.mcpServers !== 'object') {
|
|
15
|
-
throw new Error('[ruler] Invalid
|
|
15
|
+
throw new Error('[ruler] Invalid MCP config: must contain an object property "mcpServers" (Ruler style)');
|
|
16
16
|
}
|
|
17
17
|
}
|
package/dist/paths/mcp.js
CHANGED
|
@@ -59,7 +59,7 @@ async function getNativeMcpPath(adapterName, projectRoot) {
|
|
|
59
59
|
candidates.push(path.join(home, '.codeium', 'windsurf', 'mcp_config.json'));
|
|
60
60
|
break;
|
|
61
61
|
case 'Claude Code':
|
|
62
|
-
candidates.push(path.join(projectRoot, '
|
|
62
|
+
candidates.push(path.join(projectRoot, '.mcp.json'));
|
|
63
63
|
break;
|
|
64
64
|
case 'OpenAI Codex CLI':
|
|
65
65
|
candidates.push(path.join(home, '.codex', 'config.json'));
|
package/dist/revert.js
CHANGED
|
@@ -307,7 +307,6 @@ async function removeEmptyDirectories(projectRoot, verbose, dryRun) {
|
|
|
307
307
|
async function removeAdditionalAgentFiles(projectRoot, verbose, dryRun) {
|
|
308
308
|
const additionalFiles = [
|
|
309
309
|
'.gemini/settings.json',
|
|
310
|
-
'claude_desktop_config.json',
|
|
311
310
|
'.mcp.json',
|
|
312
311
|
'.vscode/mcp.json',
|
|
313
312
|
'.cursor/mcp.json',
|