@intellectronica/ruler 0.2.12 → 0.2.14

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
@@ -10,6 +10,7 @@
10
10
  ---
11
11
 
12
12
  > **Beta Research Preview**
13
+ >
13
14
  > - Please test this version carefully in your environment
14
15
  > - Report issues at https://github.com/intellectronica/ruler/issues
15
16
 
@@ -35,22 +36,23 @@ Ruler solves this by providing a **single source of truth** for all your AI agen
35
36
 
36
37
  ## Supported AI Agents
37
38
 
38
- | Agent | Rules File(s) | MCP Configuration |
39
- | ---------------- | ------------------------------------------------ | ------------------------------------------------ |
40
- | GitHub Copilot | `.github/copilot-instructions.md` | `.vscode/mcp.json` |
41
- | Claude Code | `CLAUDE.md` | `claude_desktop_config.json` |
42
- | OpenAI Codex CLI | `AGENTS.md` | `~/.codex/config.json` |
43
- | Jules | `AGENTS.md` | - |
44
- | Cursor | `.cursor/rules/ruler_cursor_instructions.mdc` | `.cursor/mcp.json`, `~/.cursor/mcp.json` |
45
- | Windsurf | `.windsurf/rules/ruler_windsurf_instructions.md` | `~/.codeium/windsurf/mcp_config.json` |
46
- | Cline | `.clinerules` | - |
47
- | Aider | `ruler_aider_instructions.md`, `.aider.conf.yml` | `.mcp.json` |
48
- | Firebase Studio | `.idx/airules.md` | - |
49
- | Open Hands | `.openhands/microagents/repo.md` | `.openhands/config.toml` |
50
- | Gemini CLI | `GEMINI.md` | `.gemini/settings.json` |
51
- | Junie | `.junie/guidelines.md` | - |
52
- | AugmentCode | `.augment/rules/ruler_augment_instructions.md` | `.vscode/settings.json` |
53
- | Kilo Code | `.kilocode/rules/ruler_kilocode_instructions.md` | `.kilocode/mcp.json` |
39
+ | Agent | Rules File(s) | MCP Configuration |
40
+ | ---------------- | ------------------------------------------------ | --------------------------------------------------- |
41
+ | GitHub Copilot | `.github/copilot-instructions.md` | `.vscode/mcp.json` |
42
+ | Claude Code | `CLAUDE.md` | `claude_desktop_config.json` |
43
+ | OpenAI Codex CLI | `AGENTS.md` | `~/.codex/config.json` |
44
+ | Jules | `AGENTS.md` | - |
45
+ | Cursor | `.cursor/rules/ruler_cursor_instructions.mdc` | `.cursor/mcp.json`, `~/.cursor/mcp.json` |
46
+ | Windsurf | `.windsurf/rules/ruler_windsurf_instructions.md` | `~/.codeium/windsurf/mcp_config.json` |
47
+ | Cline | `.clinerules` | - |
48
+ | Aider | `ruler_aider_instructions.md`, `.aider.conf.yml` | `.mcp.json` |
49
+ | Firebase Studio | `.idx/airules.md` | - |
50
+ | Open Hands | `.openhands/microagents/repo.md` | `.openhands/config.toml` |
51
+ | Gemini CLI | `GEMINI.md` | `.gemini/settings.json` |
52
+ | Junie | `.junie/guidelines.md` | - |
53
+ | AugmentCode | `.augment/rules/ruler_augment_instructions.md` | `.vscode/settings.json` |
54
+ | Kilo Code | `.kilocode/rules/ruler_kilocode_instructions.md` | `.kilocode/mcp.json` |
55
+ | OpenCode | `AGENTS.md` | `opencode.json`, `~/.config/opencode/opencode.json` |
54
56
 
55
57
  ## Getting Started
56
58
 
@@ -83,6 +85,7 @@ npx @intellectronica/ruler apply
83
85
  - `.ruler/mcp.json`: An example MCP server configuration
84
86
 
85
87
  Additionally, you can create a global configuration to use when no local `.ruler/` directory is found:
88
+
86
89
  ```bash
87
90
  ruler init --global
88
91
  ```
@@ -143,18 +146,18 @@ The `apply` command looks for `.ruler/` in the current directory tree, reading t
143
146
 
144
147
  ### Options
145
148
 
146
- | Option | Description |
147
- | ------------------------------ | --------------------------------------------------------- |
148
- | `--project-root <path>` | Path to your project's root (default: current directory) |
149
+ | Option | Description |
150
+ | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
151
+ | `--project-root <path>` | Path to your project's root (default: current directory) |
149
152
  | `--agents <agent1,agent2,...>` | Comma-separated list of agent names to target (copilot, claude, codex, cursor, windsurf, cline, aider, firebase, gemini-cli, junie, augmentcode, kilocode) |
150
- | `--config <path>` | Path to a custom `ruler.toml` configuration file |
151
- | `--mcp` / `--with-mcp` | Enable applying MCP server configurations (default: true) |
152
- | `--no-mcp` | Disable applying MCP server configurations |
153
- | `--mcp-overwrite` | Overwrite native MCP config entirely instead of merging |
154
- | `--gitignore` | Enable automatic .gitignore updates (default: true) |
155
- | `--no-gitignore` | Disable automatic .gitignore updates |
156
- | `--local-only` | Do not look for configuration in `$XDG_CONFIG_HOME` |
157
- | `--verbose` / `-v` | Display detailed output during execution |
153
+ | `--config <path>` | Path to a custom `ruler.toml` configuration file |
154
+ | `--mcp` / `--with-mcp` | Enable applying MCP server configurations (default: true) |
155
+ | `--no-mcp` | Disable applying MCP server configurations |
156
+ | `--mcp-overwrite` | Overwrite native MCP config entirely instead of merging |
157
+ | `--gitignore` | Enable automatic .gitignore updates (default: true) |
158
+ | `--no-gitignore` | Disable automatic .gitignore updates |
159
+ | `--local-only` | Do not look for configuration in `$XDG_CONFIG_HOME` |
160
+ | `--verbose` / `-v` | Display detailed output during execution |
158
161
 
159
162
  ### Common Examples
160
163
 
@@ -201,6 +204,7 @@ The `revert` command safely undoes all changes made by `ruler apply`, restoring
201
204
  ### Why Revert is Needed
202
205
 
203
206
  When experimenting with different rule configurations or switching between projects, you may want to:
207
+
204
208
  - **Clean slate**: Remove all ruler-generated files to start fresh
205
209
  - **Restore originals**: Revert modified files back to their original state
206
210
  - **Selective cleanup**: Remove configurations for specific agents only
@@ -214,15 +218,15 @@ ruler revert [options]
214
218
 
215
219
  ### Options
216
220
 
217
- | Option | Description |
218
- | ------------------------------ | --------------------------------------------------------- |
219
- | `--project-root <path>` | Path to your project's root (default: current directory) |
220
- | `--agents <agent1,agent2,...>` | Comma-separated list of agent names to revert (copilot, claude, codex, cursor, windsurf, cline, aider, firebase, gemini-cli, junie, kilocode) |
221
- | `--config <path>` | Path to a custom `ruler.toml` configuration file |
222
- | `--keep-backups` | Keep backup files (.bak) after restoration (default: false) |
223
- | `--dry-run` | Preview changes without actually reverting files |
224
- | `--verbose` / `-v` | Display detailed output during execution |
225
- | `--local-only` | Only search for local .ruler directories, ignore global config |
221
+ | Option | Description |
222
+ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
223
+ | `--project-root <path>` | Path to your project's root (default: current directory) |
224
+ | `--agents <agent1,agent2,...>` | Comma-separated list of agent names to revert (copilot, claude, codex, cursor, windsurf, cline, aider, firebase, gemini-cli, junie, kilocode, opencode) |
225
+ | `--config <path>` | Path to a custom `ruler.toml` configuration file |
226
+ | `--keep-backups` | Keep backup files (.bak) after restoration (default: false) |
227
+ | `--dry-run` | Preview changes without actually reverting files |
228
+ | `--verbose` / `-v` | Display detailed output during execution |
229
+ | `--local-only` | Only search for local .ruler directories, ignore global config |
226
230
 
227
231
  ### Common Examples
228
232
 
@@ -0,0 +1,60 @@
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.OpenCodeAgent = void 0;
37
+ const FileSystemUtils_1 = require("../core/FileSystemUtils");
38
+ const path = __importStar(require("path"));
39
+ class OpenCodeAgent {
40
+ getIdentifier() {
41
+ return 'opencode';
42
+ }
43
+ getName() {
44
+ return 'OpenCode';
45
+ }
46
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
47
+ agentConfig) {
48
+ const outputPath = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
49
+ const absolutePath = path.resolve(projectRoot, outputPath);
50
+ await (0, FileSystemUtils_1.backupFile)(absolutePath);
51
+ await (0, FileSystemUtils_1.writeGeneratedFile)(absolutePath, concatenatedRules);
52
+ }
53
+ getDefaultOutputPath(projectRoot) {
54
+ return path.join(projectRoot, 'AGENTS.md');
55
+ }
56
+ getMcpServerKey() {
57
+ return 'mcp';
58
+ }
59
+ }
60
+ exports.OpenCodeAgent = OpenCodeAgent;
@@ -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',
63
+ description: 'Comma-separated list of agent identifiers: copilot, claude, codex, cursor, windsurf, cline, aider, firebase, gemini-cli, junie, kilocode, opencode',
64
64
  });
65
65
  y.option('config', {
66
66
  type: 'string',
@@ -254,7 +254,7 @@ and apply them to your configured AI coding agents.
254
254
  });
255
255
  y.option('agents', {
256
256
  type: 'string',
257
- description: 'Comma-separated list of agent identifiers: copilot, claude, codex, cursor, windsurf, cline, aider, firebase, gemini-cli, junie, kilocode',
257
+ description: 'Comma-separated list of agent identifiers: copilot, claude, codex, cursor, windsurf, cline, aider, firebase, gemini-cli, junie, kilocode, opencode',
258
258
  });
259
259
  y.option('config', {
260
260
  type: 'string',
package/dist/lib.js CHANGED
@@ -54,10 +54,12 @@ const JulesAgent_1 = require("./agents/JulesAgent");
54
54
  const JunieAgent_1 = require("./agents/JunieAgent");
55
55
  const AugmentCodeAgent_1 = require("./agents/AugmentCodeAgent");
56
56
  const KiloCodeAgent_1 = require("./agents/KiloCodeAgent");
57
+ const OpenCodeAgent_1 = require("./agents/OpenCodeAgent");
57
58
  const merge_1 = require("./mcp/merge");
58
59
  const validate_1 = require("./mcp/validate");
59
60
  const mcp_1 = require("./paths/mcp");
60
61
  const propagateOpenHandsMcp_1 = require("./mcp/propagateOpenHandsMcp");
62
+ const propagateOpenCodeMcp_1 = require("./mcp/propagateOpenCodeMcp");
61
63
  const constants_1 = require("./constants");
62
64
  /**
63
65
  * Gets all output paths for an agent, taking into account any config overrides.
@@ -108,6 +110,7 @@ const agents = [
108
110
  new JunieAgent_1.JunieAgent(),
109
111
  new AugmentCodeAgent_1.AugmentCodeAgent(),
110
112
  new KiloCodeAgent_1.KiloCodeAgent(),
113
+ new OpenCodeAgent_1.OpenCodeAgent(),
111
114
  ];
112
115
  /**
113
116
  * Applies ruler configurations for all supported AI agents.
@@ -265,6 +268,15 @@ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cli
265
268
  (0, constants_1.logVerbose)(`DRY RUN: AugmentCode MCP config handled internally via VSCode settings`, verbose);
266
269
  }
267
270
  }
271
+ else if (agent.getIdentifier() === 'opencode') {
272
+ // *** Special handling for OpenCode ***
273
+ if (dryRun) {
274
+ (0, constants_1.logVerbose)(`DRY RUN: Would apply MCP config by updating OpenCode config file: ${dest}`, verbose);
275
+ }
276
+ else {
277
+ await (0, propagateOpenCodeMcp_1.propagateMcpToOpenCode)(rulerMcpFile, dest);
278
+ }
279
+ }
268
280
  else {
269
281
  if (rulerMcpJson) {
270
282
  const strategy = cliMcpStrategy ??
@@ -0,0 +1,113 @@
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.propagateMcpToOpenCode = propagateMcpToOpenCode;
37
+ const fs = __importStar(require("fs/promises"));
38
+ const FileSystemUtils_1 = require("../core/FileSystemUtils");
39
+ const path = __importStar(require("path"));
40
+ /**
41
+ * Transform ruler MCP configuration to OpenCode's specific format
42
+ */
43
+ function transformToOpenCodeFormat(rulerMcp) {
44
+ const rulerServers = rulerMcp.mcpServers || {};
45
+ const openCodeServers = {};
46
+ for (const [name, serverDef] of Object.entries(rulerServers)) {
47
+ const server = serverDef;
48
+ // Determine if this is a local or remote server
49
+ const isRemote = !!server.url;
50
+ const openCodeServer = {
51
+ type: isRemote ? 'remote' : 'local',
52
+ enabled: true, // Always true as per the issue requirements
53
+ };
54
+ if (isRemote) {
55
+ // Remote server configuration
56
+ openCodeServer.url = server.url;
57
+ if (server.headers) {
58
+ openCodeServer.headers = server.headers;
59
+ }
60
+ }
61
+ else {
62
+ // Local server configuration
63
+ if (server.command) {
64
+ // Combine command and args into a single array
65
+ const command = Array.isArray(server.command)
66
+ ? server.command
67
+ : [server.command];
68
+ const args = server.args || [];
69
+ openCodeServer.command = [...command, ...args];
70
+ }
71
+ if (server.env) {
72
+ openCodeServer.environment = server.env;
73
+ }
74
+ }
75
+ openCodeServers[name] = openCodeServer;
76
+ }
77
+ return {
78
+ $schema: 'https://opencode.ai/config.json',
79
+ mcp: openCodeServers,
80
+ };
81
+ }
82
+ async function propagateMcpToOpenCode(rulerMcpPath, openCodeConfigPath) {
83
+ let rulerMcp;
84
+ try {
85
+ const rulerJsonContent = await fs.readFile(rulerMcpPath, 'utf8');
86
+ rulerMcp = JSON.parse(rulerJsonContent);
87
+ }
88
+ catch {
89
+ return;
90
+ }
91
+ // Read existing OpenCode config if it exists
92
+ let existingConfig = {};
93
+ try {
94
+ const existingContent = await fs.readFile(openCodeConfigPath, 'utf8');
95
+ existingConfig = JSON.parse(existingContent);
96
+ }
97
+ catch {
98
+ // File doesn't exist, we'll create it
99
+ }
100
+ // Transform ruler MCP to OpenCode format
101
+ const transformedConfig = transformToOpenCodeFormat(rulerMcp);
102
+ // Merge with existing config, preserving non-MCP settings
103
+ const finalConfig = {
104
+ ...existingConfig,
105
+ $schema: transformedConfig.$schema,
106
+ mcp: {
107
+ ...existingConfig.mcp,
108
+ ...transformedConfig.mcp,
109
+ },
110
+ };
111
+ await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(openCodeConfigPath));
112
+ await fs.writeFile(openCodeConfigPath, JSON.stringify(finalConfig, null, 2) + '\n');
113
+ }
package/dist/paths/mcp.js CHANGED
@@ -80,6 +80,10 @@ async function getNativeMcpPath(adapterName, projectRoot) {
80
80
  case 'Kilo Code':
81
81
  candidates.push(path.join(projectRoot, '.kilocode', 'mcp.json'));
82
82
  break;
83
+ case 'OpenCode':
84
+ candidates.push(path.join(projectRoot, 'opencode.json'));
85
+ candidates.push(path.join(home, '.config', 'opencode', 'opencode.json'));
86
+ break;
83
87
  default:
84
88
  return null;
85
89
  }
package/dist/revert.js CHANGED
@@ -52,6 +52,7 @@ const JulesAgent_1 = require("./agents/JulesAgent");
52
52
  const JunieAgent_1 = require("./agents/JunieAgent");
53
53
  const AugmentCodeAgent_1 = require("./agents/AugmentCodeAgent");
54
54
  const KiloCodeAgent_1 = require("./agents/KiloCodeAgent");
55
+ const OpenCodeAgent_1 = require("./agents/OpenCodeAgent");
55
56
  const mcp_1 = require("./paths/mcp");
56
57
  const constants_1 = require("./constants");
57
58
  const settings_1 = require("./vscode/settings");
@@ -70,6 +71,7 @@ const agents = [
70
71
  new JunieAgent_1.JunieAgent(),
71
72
  new AugmentCodeAgent_1.AugmentCodeAgent(),
72
73
  new KiloCodeAgent_1.KiloCodeAgent(),
74
+ new OpenCodeAgent_1.OpenCodeAgent(),
73
75
  ];
74
76
  /**
75
77
  * Gets all output paths for an agent, taking into account any config overrides.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intellectronica/ruler",
3
- "version": "0.2.12",
3
+ "version": "0.2.14",
4
4
  "description": "Ruler — apply the same rules to all coding agents",
5
5
  "main": "dist/lib.js",
6
6
  "scripts": {