@intellectronica/ruler 0.2.4 → 0.2.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
@@ -46,6 +46,7 @@ Ruler solves this by providing a **single source of truth** for all your AI agen
46
46
  | Aider | `ruler_aider_instructions.md` and `.aider.conf.yml` |
47
47
  | Firebase Studio | `.idx/airules.md` |
48
48
  | Open Hands | `.openhands/microagents/repo.md` and `.openhands/config.toml` |
49
+ | Gemini CLI | `GEMINI.md` and `.gemini/settings.json` |
49
50
 
50
51
  ## Getting Started
51
52
 
@@ -222,6 +223,9 @@ output_path_config = ".aider.conf.yml"
222
223
  enabled = true
223
224
  output_path = ".idx/airules.md"
224
225
 
226
+ [agents.gemini-cli]
227
+ enabled = true
228
+
225
229
  # Agent-specific MCP configuration
226
230
  [agents.cursor.mcp]
227
231
  enabled = true
@@ -48,7 +48,8 @@ class AiderAgent {
48
48
  getName() {
49
49
  return 'Aider';
50
50
  }
51
- async applyRulerConfig(concatenatedRules, projectRoot, agentConfig) {
51
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
52
+ agentConfig) {
52
53
  const mdPath = agentConfig?.outputPathInstructions ??
53
54
  this.getDefaultOutputPath(projectRoot).instructions;
54
55
  await (0, FileSystemUtils_1.backupFile)(mdPath);
@@ -46,7 +46,8 @@ class ClaudeAgent {
46
46
  getName() {
47
47
  return 'Claude Code';
48
48
  }
49
- async applyRulerConfig(concatenatedRules, projectRoot, agentConfig) {
49
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
50
+ agentConfig) {
50
51
  const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
51
52
  await (0, FileSystemUtils_1.backupFile)(output);
52
53
  await (0, FileSystemUtils_1.writeGeneratedFile)(output, concatenatedRules);
@@ -46,7 +46,8 @@ class ClineAgent {
46
46
  getName() {
47
47
  return 'Cline';
48
48
  }
49
- async applyRulerConfig(concatenatedRules, projectRoot, agentConfig) {
49
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
50
+ agentConfig) {
50
51
  const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
51
52
  await (0, FileSystemUtils_1.backupFile)(output);
52
53
  await (0, FileSystemUtils_1.writeGeneratedFile)(output, concatenatedRules);
@@ -46,7 +46,8 @@ class CodexCliAgent {
46
46
  getName() {
47
47
  return 'OpenAI Codex CLI';
48
48
  }
49
- async applyRulerConfig(concatenatedRules, projectRoot, agentConfig) {
49
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
50
+ agentConfig) {
50
51
  const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
51
52
  await (0, FileSystemUtils_1.backupFile)(output);
52
53
  await (0, FileSystemUtils_1.writeGeneratedFile)(output, concatenatedRules);
@@ -46,7 +46,8 @@ class CopilotAgent {
46
46
  getName() {
47
47
  return 'GitHub Copilot';
48
48
  }
49
- async applyRulerConfig(concatenatedRules, projectRoot, agentConfig) {
49
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
50
+ agentConfig) {
50
51
  const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
51
52
  await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(output));
52
53
  await (0, FileSystemUtils_1.backupFile)(output);
@@ -46,7 +46,8 @@ class CursorAgent {
46
46
  getName() {
47
47
  return 'Cursor';
48
48
  }
49
- async applyRulerConfig(concatenatedRules, projectRoot, agentConfig) {
49
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
50
+ agentConfig) {
50
51
  const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
51
52
  await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(output));
52
53
  await (0, FileSystemUtils_1.backupFile)(output);
@@ -46,7 +46,8 @@ class FirebaseAgent {
46
46
  getName() {
47
47
  return 'Firebase Studio';
48
48
  }
49
- async applyRulerConfig(concatenatedRules, projectRoot, agentConfig) {
49
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
50
+ agentConfig) {
50
51
  const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
51
52
  await (0, FileSystemUtils_1.backupFile)(output);
52
53
  await (0, FileSystemUtils_1.writeGeneratedFile)(output, concatenatedRules);
@@ -0,0 +1,74 @@
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.GeminiCliAgent = void 0;
37
+ const path = __importStar(require("path"));
38
+ const fs_1 = require("fs");
39
+ const merge_1 = require("../mcp/merge");
40
+ class GeminiCliAgent {
41
+ getIdentifier() {
42
+ return 'gemini-cli';
43
+ }
44
+ getName() {
45
+ return 'Gemini CLI';
46
+ }
47
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig) {
48
+ const outputPath = this.getDefaultOutputPath(projectRoot);
49
+ await fs_1.promises.writeFile(outputPath, concatenatedRules);
50
+ if (rulerMcpJson) {
51
+ const settingsPath = path.join(projectRoot, '.gemini', 'settings.json');
52
+ let existingSettings = {};
53
+ try {
54
+ const existingSettingsRaw = await fs_1.promises.readFile(settingsPath, 'utf8');
55
+ existingSettings = JSON.parse(existingSettingsRaw);
56
+ }
57
+ catch (error) {
58
+ if (error.code !== 'ENOENT') {
59
+ throw error;
60
+ }
61
+ }
62
+ const merged = (0, merge_1.mergeMcp)(existingSettings, rulerMcpJson, agentConfig?.mcp?.strategy ?? 'merge', this.getMcpServerKey());
63
+ await fs_1.promises.mkdir(path.dirname(settingsPath), { recursive: true });
64
+ await fs_1.promises.writeFile(settingsPath, JSON.stringify(merged, null, 2));
65
+ }
66
+ }
67
+ getDefaultOutputPath(projectRoot) {
68
+ return path.join(projectRoot, 'GEMINI.md');
69
+ }
70
+ getMcpServerKey() {
71
+ return 'mcpServers';
72
+ }
73
+ }
74
+ exports.GeminiCliAgent = GeminiCliAgent;
@@ -43,7 +43,8 @@ class OpenHandsAgent {
43
43
  getName() {
44
44
  return 'Open Hands';
45
45
  }
46
- async applyRulerConfig(concatenatedRules, projectRoot, agentConfig) {
46
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
47
+ agentConfig) {
47
48
  const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
48
49
  await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(output));
49
50
  await (0, FileSystemUtils_1.backupFile)(output);
@@ -46,7 +46,8 @@ class WindsurfAgent {
46
46
  getName() {
47
47
  return 'Windsurf';
48
48
  }
49
- async applyRulerConfig(concatenatedRules, projectRoot, agentConfig) {
49
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
50
+ agentConfig) {
50
51
  const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
51
52
  await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(output));
52
53
  await (0, FileSystemUtils_1.backupFile)(output);
@@ -58,7 +58,7 @@ function run() {
58
58
  });
59
59
  y.option('agents', {
60
60
  type: 'string',
61
- description: 'Comma-separated list of agent identifiers: copilot, claude, codex, cursor, windsurf, cline, aider, firebase',
61
+ description: 'Comma-separated list of agent identifiers: copilot, claude, codex, cursor, windsurf, cline, aider, firebase, gemini-cli',
62
62
  });
63
63
  y.option('config', {
64
64
  type: 'string',
@@ -193,6 +193,9 @@ and apply them to your configured AI coding agents.
193
193
  # [agents.firebase]
194
194
  # enabled = true
195
195
  # output_path = ".idx/airules.md"
196
+
197
+ # [agents.gemini-cli]
198
+ # enabled = true
196
199
  `;
197
200
  if (!(await exists(instructionsPath))) {
198
201
  await fs_1.promises.writeFile(instructionsPath, DEFAULT_INSTRUCTIONS);
package/dist/lib.js CHANGED
@@ -45,10 +45,11 @@ 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 ClineAgent_1 = require("./agents/ClineAgent");
48
+ const ClineAgent = __importStar(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");
52
+ const GeminiCliAgent_1 = require("./agents/GeminiCliAgent");
52
53
  const merge_1 = require("./mcp/merge");
53
54
  const validate_1 = require("./mcp/validate");
54
55
  const mcp_1 = require("./paths/mcp");
@@ -94,10 +95,11 @@ const agents = [
94
95
  new CodexCliAgent_1.CodexCliAgent(),
95
96
  new CursorAgent_1.CursorAgent(),
96
97
  new WindsurfAgent_1.WindsurfAgent(),
97
- new ClineAgent_1.ClineAgent(),
98
+ new ClineAgent.ClineAgent(),
98
99
  new AiderAgent_1.AiderAgent(),
99
100
  new FirebaseAgent_1.FirebaseAgent(),
100
101
  new OpenHandsAgent_1.OpenHandsAgent(),
102
+ new GeminiCliAgent_1.GeminiCliAgent(),
101
103
  ];
102
104
  /**
103
105
  * Applies ruler configurations for all supported AI agents.
@@ -198,7 +200,7 @@ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cli
198
200
  (0, constants_1.logVerbose)(`DRY RUN: Would write rules to: ${outputPaths.join(', ')}`, verbose);
199
201
  }
200
202
  else {
201
- await agent.applyRulerConfig(concatenated, projectRoot, agentConfig);
203
+ await agent.applyRulerConfig(concatenated, projectRoot, rulerMcpJson, agentConfig);
202
204
  }
203
205
  const dest = await (0, mcp_1.getNativeMcpPath)(agent.getName(), projectRoot);
204
206
  const mcpEnabledForAgent = cliMcpEnabled &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intellectronica/ruler",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Ruler — apply the same rules to all coding agents",
5
5
  "main": "dist/lib.js",
6
6
  "scripts": {