@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 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 extends AgentsMdAgent_1.AgentsMdAgent {
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 base class (instructions file).
56
- await super.applyRulerConfig(concatenatedRules, projectRoot, null, {
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
- // Resolve config path helper (mirrors previous logic)
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 path = __importStar(require("path"));
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 AbstractAgent_1.AbstractAgent {
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;
@@ -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.map((p) => {
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, backup = true) {
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
- // Add wildcard pattern for backup files only if backup is enabled
420
- if (backup) {
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, backup);
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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intellectronica/ruler",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Ruler — apply the same rules to all coding agents",
5
5
  "main": "dist/lib.js",
6
6
  "scripts": {