@intellectronica/ruler 0.3.3 → 0.3.5
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 -0
- package/dist/agents/AmazonQCliAgent.js +103 -0
- package/dist/agents/CodexCliAgent.js +17 -11
- package/dist/agents/CopilotAgent.js +2 -39
- package/dist/agents/TraeAgent.js +54 -0
- package/dist/agents/index.js +4 -0
- package/dist/core/GitignoreUtils.js +11 -2
- package/dist/core/apply-engine.js +3 -5
- package/dist/lib.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -65,6 +65,7 @@ Ruler solves this by providing a **single source of truth** for all your AI agen
|
|
|
65
65
|
| Windsurf | `.windsurf/rules/ruler_windsurf_instructions.md` | - |
|
|
66
66
|
| Cline | `.clinerules` | - |
|
|
67
67
|
| Amp | `AGENTS.md` | - |
|
|
68
|
+
| Amazon Q CLI | `.amazonq/rules/ruler_q_rules.md` | `.amazonq/mcp.json` |
|
|
68
69
|
| Aider | `AGENTS.md`, `.aider.conf.yml` | `.mcp.json` |
|
|
69
70
|
| Firebase Studio | `.idx/airules.md` | - |
|
|
70
71
|
| Open Hands | `.openhands/microagents/repo.md` | `.openhands/config.toml` |
|
|
@@ -0,0 +1,103 @@
|
|
|
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.AmazonQCliAgent = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const fs_1 = require("fs");
|
|
39
|
+
const FileSystemUtils_1 = require("../core/FileSystemUtils");
|
|
40
|
+
const merge_1 = require("../mcp/merge");
|
|
41
|
+
/**
|
|
42
|
+
* Amazon Q CLI agent adapter.
|
|
43
|
+
*/
|
|
44
|
+
class AmazonQCliAgent {
|
|
45
|
+
getIdentifier() {
|
|
46
|
+
return 'amazonqcli';
|
|
47
|
+
}
|
|
48
|
+
getName() {
|
|
49
|
+
return 'Amazon Q CLI';
|
|
50
|
+
}
|
|
51
|
+
async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig, backup = true) {
|
|
52
|
+
const outputPaths = this.getDefaultOutputPath(projectRoot);
|
|
53
|
+
const rulesPath = path.resolve(projectRoot, agentConfig?.outputPath ||
|
|
54
|
+
agentConfig?.outputPathInstructions ||
|
|
55
|
+
outputPaths['instructions']);
|
|
56
|
+
// Write rules file to .amazonq/rules/
|
|
57
|
+
await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(rulesPath));
|
|
58
|
+
if (backup) {
|
|
59
|
+
await (0, FileSystemUtils_1.backupFile)(rulesPath);
|
|
60
|
+
}
|
|
61
|
+
await (0, FileSystemUtils_1.writeGeneratedFile)(rulesPath, concatenatedRules);
|
|
62
|
+
// Handle MCP configuration if enabled and provided
|
|
63
|
+
const mcpEnabled = agentConfig?.mcp?.enabled ?? true;
|
|
64
|
+
if (mcpEnabled && rulerMcpJson) {
|
|
65
|
+
const mcpPath = path.resolve(projectRoot, agentConfig?.outputPathConfig ?? outputPaths['mcp']);
|
|
66
|
+
const mcpStrategy = agentConfig?.mcp?.strategy ?? 'merge';
|
|
67
|
+
await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(mcpPath));
|
|
68
|
+
let existingMcpConfig = {};
|
|
69
|
+
try {
|
|
70
|
+
const raw = await fs_1.promises.readFile(mcpPath, 'utf8');
|
|
71
|
+
existingMcpConfig = JSON.parse(raw);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
if (err.code !== 'ENOENT') {
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
// File doesn't exist, start with empty config
|
|
78
|
+
}
|
|
79
|
+
// Merge the MCP configurations using the standard merge function
|
|
80
|
+
const mergedConfig = (0, merge_1.mergeMcp)(existingMcpConfig, rulerMcpJson, mcpStrategy, 'mcpServers');
|
|
81
|
+
if (backup) {
|
|
82
|
+
await (0, FileSystemUtils_1.backupFile)(mcpPath);
|
|
83
|
+
}
|
|
84
|
+
await (0, FileSystemUtils_1.writeGeneratedFile)(mcpPath, JSON.stringify(mergedConfig, null, 2));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
getDefaultOutputPath(projectRoot) {
|
|
88
|
+
return {
|
|
89
|
+
instructions: path.join(projectRoot, '.amazonq', 'rules', 'ruler_q_rules.md'),
|
|
90
|
+
mcp: path.join(projectRoot, '.amazonq', 'mcp.json'),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
getMcpServerKey() {
|
|
94
|
+
return 'mcpServers';
|
|
95
|
+
}
|
|
96
|
+
supportsMcpStdio() {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
supportsMcpRemote() {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
exports.AmazonQCliAgent = AmazonQCliAgent;
|
|
@@ -44,26 +44,26 @@ const constants_1 = require("../constants");
|
|
|
44
44
|
/**
|
|
45
45
|
* OpenAI Codex CLI agent adapter.
|
|
46
46
|
*/
|
|
47
|
-
class CodexCliAgent
|
|
47
|
+
class CodexCliAgent {
|
|
48
|
+
constructor() {
|
|
49
|
+
this.agentsMdAgent = new AgentsMdAgent_1.AgentsMdAgent();
|
|
50
|
+
}
|
|
48
51
|
getIdentifier() {
|
|
49
52
|
return 'codex';
|
|
50
53
|
}
|
|
51
54
|
getName() {
|
|
52
55
|
return 'OpenAI Codex CLI';
|
|
53
56
|
}
|
|
54
|
-
async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig) {
|
|
55
|
-
// First perform idempotent AGENTS.md write via
|
|
56
|
-
await
|
|
57
|
+
async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig, backup = true) {
|
|
58
|
+
// First perform idempotent AGENTS.md write via composed AgentsMdAgent
|
|
59
|
+
await this.agentsMdAgent.applyRulerConfig(concatenatedRules, projectRoot, null, {
|
|
57
60
|
// Preserve explicit outputPath precedence semantics if provided.
|
|
58
61
|
outputPath: agentConfig?.outputPath ||
|
|
59
62
|
agentConfig?.outputPathInstructions ||
|
|
60
63
|
undefined,
|
|
61
|
-
});
|
|
62
|
-
//
|
|
63
|
-
const defaults =
|
|
64
|
-
instructions: path.join(projectRoot, constants_1.DEFAULT_RULES_FILENAME),
|
|
65
|
-
config: path.join(projectRoot, '.codex', 'config.toml'),
|
|
66
|
-
};
|
|
64
|
+
}, backup);
|
|
65
|
+
// Use proper path resolution from getDefaultOutputPath and agentConfig
|
|
66
|
+
const defaults = this.getDefaultOutputPath(projectRoot);
|
|
67
67
|
const mcpEnabled = agentConfig?.mcp?.enabled ?? true;
|
|
68
68
|
if (mcpEnabled && rulerMcpJson) {
|
|
69
69
|
// Apply MCP server filtering and transformation
|
|
@@ -73,7 +73,7 @@ class CodexCliAgent extends AgentsMdAgent_1.AgentsMdAgent {
|
|
|
73
73
|
return; // No compatible servers found
|
|
74
74
|
}
|
|
75
75
|
const filteredRulerMcpJson = filteredMcpConfig;
|
|
76
|
-
// Determine the config file path
|
|
76
|
+
// Determine the config file path using proper precedence
|
|
77
77
|
const configPath = agentConfig?.outputPathConfig ?? defaults.config;
|
|
78
78
|
// Ensure the parent directory exists
|
|
79
79
|
await fs_1.promises.mkdir(path.dirname(configPath), { recursive: true });
|
|
@@ -177,6 +177,12 @@ class CodexCliAgent extends AgentsMdAgent_1.AgentsMdAgent {
|
|
|
177
177
|
await (0, FileSystemUtils_1.writeGeneratedFile)(configPath, tomlContent);
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
|
+
getDefaultOutputPath(projectRoot) {
|
|
181
|
+
return {
|
|
182
|
+
instructions: path.join(projectRoot, constants_1.DEFAULT_RULES_FILENAME),
|
|
183
|
+
config: path.join(projectRoot, '.codex', 'config.toml'),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
180
186
|
supportsMcpStdio() {
|
|
181
187
|
return true;
|
|
182
188
|
}
|
|
@@ -1,54 +1,17 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.CopilotAgent = void 0;
|
|
37
|
-
const
|
|
38
|
-
const AbstractAgent_1 = require("./AbstractAgent");
|
|
4
|
+
const AgentsMdAgent_1 = require("./AgentsMdAgent");
|
|
39
5
|
/**
|
|
40
6
|
* GitHub Copilot agent adapter.
|
|
41
7
|
*/
|
|
42
|
-
class CopilotAgent extends
|
|
8
|
+
class CopilotAgent extends AgentsMdAgent_1.AgentsMdAgent {
|
|
43
9
|
getIdentifier() {
|
|
44
10
|
return 'copilot';
|
|
45
11
|
}
|
|
46
12
|
getName() {
|
|
47
13
|
return 'GitHub Copilot';
|
|
48
14
|
}
|
|
49
|
-
getDefaultOutputPath(projectRoot) {
|
|
50
|
-
return path.join(projectRoot, '.github', 'copilot-instructions.md');
|
|
51
|
-
}
|
|
52
15
|
getMcpServerKey() {
|
|
53
16
|
return 'servers';
|
|
54
17
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
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.TraeAgent = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const AbstractAgent_1 = require("./AbstractAgent");
|
|
39
|
+
/**
|
|
40
|
+
* Trae AI agent adapter.
|
|
41
|
+
* Generates project_rules.md configuration file.
|
|
42
|
+
*/
|
|
43
|
+
class TraeAgent extends AbstractAgent_1.AbstractAgent {
|
|
44
|
+
getIdentifier() {
|
|
45
|
+
return 'trae';
|
|
46
|
+
}
|
|
47
|
+
getName() {
|
|
48
|
+
return 'Trae AI';
|
|
49
|
+
}
|
|
50
|
+
getDefaultOutputPath(projectRoot) {
|
|
51
|
+
return path.join(projectRoot, '.trae', 'rules', 'project_rules.md');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.TraeAgent = TraeAgent;
|
package/dist/agents/index.js
CHANGED
|
@@ -27,6 +27,8 @@ const QwenCodeAgent_1 = require("./QwenCodeAgent");
|
|
|
27
27
|
const KiroAgent_1 = require("./KiroAgent");
|
|
28
28
|
const WarpAgent_1 = require("./WarpAgent");
|
|
29
29
|
const RooCodeAgent_1 = require("./RooCodeAgent");
|
|
30
|
+
const TraeAgent_1 = require("./TraeAgent");
|
|
31
|
+
const AmazonQCliAgent_1 = require("./AmazonQCliAgent");
|
|
30
32
|
exports.allAgents = [
|
|
31
33
|
new CopilotAgent_1.CopilotAgent(),
|
|
32
34
|
new ClaudeAgent_1.ClaudeAgent(),
|
|
@@ -52,4 +54,6 @@ exports.allAgents = [
|
|
|
52
54
|
new KiroAgent_1.KiroAgent(),
|
|
53
55
|
new WarpAgent_1.WarpAgent(),
|
|
54
56
|
new RooCodeAgent_1.RooCodeAgent(),
|
|
57
|
+
new TraeAgent_1.TraeAgent(),
|
|
58
|
+
new AmazonQCliAgent_1.AmazonQCliAgent(),
|
|
55
59
|
];
|
|
@@ -57,8 +57,9 @@ async function updateGitignore(projectRoot, paths) {
|
|
|
57
57
|
throw err;
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
-
// Convert paths to relative POSIX format
|
|
61
|
-
const relativePaths = paths
|
|
60
|
+
// Convert paths to repo-relative POSIX format with leading /
|
|
61
|
+
const relativePaths = paths
|
|
62
|
+
.map((p) => {
|
|
62
63
|
let relative;
|
|
63
64
|
if (path.isAbsolute(p)) {
|
|
64
65
|
relative = path.relative(projectRoot, p);
|
|
@@ -78,6 +79,14 @@ async function updateGitignore(projectRoot, paths) {
|
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
return relative.replace(/\\/g, '/'); // Convert to POSIX format
|
|
82
|
+
})
|
|
83
|
+
.filter((p) => {
|
|
84
|
+
// Never include any path that resides inside a .ruler directory (inputs, not outputs)
|
|
85
|
+
return !p.includes('/.ruler/') && !p.startsWith('.ruler/');
|
|
86
|
+
})
|
|
87
|
+
.map((p) => {
|
|
88
|
+
// Always write full repository-relative paths (prefix with leading /)
|
|
89
|
+
return p.startsWith('/') ? p : `/${p}`;
|
|
81
90
|
});
|
|
82
91
|
// Get all existing paths from .gitignore (excluding Ruler block)
|
|
83
92
|
const existingPaths = getExistingPathsExcludingRulerBlock(existingContent);
|
|
@@ -402,7 +402,7 @@ async function applyStandardMcpConfiguration(agent, filteredMcpJson, dest, agent
|
|
|
402
402
|
* @param cliGitignoreEnabled CLI gitignore setting
|
|
403
403
|
* @param dryRun Whether to perform a dry run
|
|
404
404
|
*/
|
|
405
|
-
async function updateGitignore(projectRoot, generatedPaths, config, cliGitignoreEnabled, dryRun
|
|
405
|
+
async function updateGitignore(projectRoot, generatedPaths, config, cliGitignoreEnabled, dryRun) {
|
|
406
406
|
// Configuration precedence: CLI > TOML > Default (enabled)
|
|
407
407
|
let gitignoreEnabled;
|
|
408
408
|
if (cliGitignoreEnabled !== undefined) {
|
|
@@ -416,10 +416,8 @@ async function updateGitignore(projectRoot, generatedPaths, config, cliGitignore
|
|
|
416
416
|
}
|
|
417
417
|
if (gitignoreEnabled && generatedPaths.length > 0) {
|
|
418
418
|
const uniquePaths = [...new Set(generatedPaths)];
|
|
419
|
-
//
|
|
420
|
-
|
|
421
|
-
uniquePaths.push('*.bak');
|
|
422
|
-
}
|
|
419
|
+
// Note: Individual backup patterns are added per-file in the collection phase
|
|
420
|
+
// No need to add a broad *.bak pattern here
|
|
423
421
|
if (uniquePaths.length > 0) {
|
|
424
422
|
const prefix = (0, constants_1.actionPrefix)(dryRun);
|
|
425
423
|
if (dryRun) {
|
package/dist/lib.js
CHANGED
|
@@ -53,7 +53,7 @@ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cli
|
|
|
53
53
|
(0, constants_1.logVerbose)(`Selected ${selectedAgents.length} agents: ${selectedAgents.map((a) => a.getName()).join(', ')}`, verbose);
|
|
54
54
|
generatedPaths = await (0, apply_engine_1.processSingleConfiguration)(selectedAgents, singleConfig, projectRoot, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup);
|
|
55
55
|
}
|
|
56
|
-
await (0, apply_engine_1.updateGitignore)(projectRoot, generatedPaths, loadedConfig, cliGitignoreEnabled, dryRun
|
|
56
|
+
await (0, apply_engine_1.updateGitignore)(projectRoot, generatedPaths, loadedConfig, cliGitignoreEnabled, dryRun);
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
59
|
* Normalizes per-agent config keys to agent identifiers for consistent lookup.
|