@intellectronica/ruler 0.3.29 → 0.3.31

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
@@ -54,40 +54,40 @@ Ruler solves this by providing a **single source of truth** for all your AI agen
54
54
 
55
55
  ## Supported AI Agents
56
56
 
57
- | Agent | Rules File(s) | MCP Configuration / Notes | Skills Support / Location |
58
- | ---------------- | ---------------------------------------------- | ------------------------------------------------ | ------------------------- |
59
- | AGENTS.md | `AGENTS.md` | (pseudo-agent ensuring root `AGENTS.md` exists) | - |
60
- | GitHub Copilot | `AGENTS.md` | `.vscode/mcp.json` | `.claude/skills/` |
61
- | Claude Code | `CLAUDE.md` | `.mcp.json` | `.claude/skills/` |
62
- | OpenAI Codex CLI | `AGENTS.md` | `.codex/config.toml` | `.codex/skills/` |
63
- | Pi Coding Agent | `AGENTS.md` | - | `.pi/skills/` |
64
- | Jules | `AGENTS.md` | - | - |
65
- | Cursor | `AGENTS.md` | `.cursor/mcp.json` | `.cursor/skills/` |
66
- | Windsurf | `AGENTS.md` | `.windsurf/mcp_config.json` | - |
67
- | Cline | `.clinerules` | - | - |
68
- | Crush | `CRUSH.md` | `.crush.json` | - |
69
- | Amp | `AGENTS.md` | - | `.agents/skills/` |
70
- | Antigravity | `.agent/rules/ruler.md` | - | `.agent/skills/` |
71
- | Amazon Q CLI | `.amazonq/rules/ruler_q_rules.md` | `.amazonq/mcp.json` | - |
72
- | Aider | `AGENTS.md`, `.aider.conf.yml` | `.mcp.json` | - |
73
- | Firebase Studio | `.idx/airules.md` | `.idx/mcp.json` | - |
74
- | Open Hands | `.openhands/microagents/repo.md` | `config.toml` | - |
75
- | Gemini CLI | `AGENTS.md` | `.gemini/settings.json` | `.gemini/skills/` |
76
- | Junie | `.junie/guidelines.md` | - | - |
77
- | AugmentCode | `.augment/rules/ruler_augment_instructions.md` | - | - |
78
- | Kilo Code | `AGENTS.md` | `.kilocode/mcp.json` | `.claude/skills/` |
79
- | OpenCode | `AGENTS.md` | `opencode.json` | `.opencode/skill/` |
80
- | Goose | `.goosehints` | - | `.agents/skills/` |
81
- | Qwen Code | `AGENTS.md` | `.qwen/settings.json` | - |
82
- | RooCode | `AGENTS.md` | `.roo/mcp.json` | `.roo/skills/` |
83
- | Zed | `AGENTS.md` | `.zed/settings.json` (project root, never $HOME) | - |
84
- | Trae AI | `.trae/rules/project_rules.md` | - | - |
85
- | Warp | `WARP.md` | - | - |
86
- | Kiro | `.kiro/steering/ruler_kiro_instructions.md` | `.kiro/settings/mcp.json` | - |
87
- | Firebender | `firebender.json` | `firebender.json` (rules and MCP in same file) | - |
88
- | Factory Droid | `AGENTS.md` | `.factory/mcp.json` | `.factory/skills/` |
89
- | Mistral Vibe | `AGENTS.md` | `.vibe/config.toml` | `.vibe/skills/` |
90
- | JetBrains AI Assistant | `.aiassistant/rules/AGENTS.md` | - | - |
57
+ | Agent | Rules File(s) | MCP Configuration / Notes | Skills Support / Location |
58
+ | ---------------------- | ---------------------------------------------- | ------------------------------------------------ | ------------------------- |
59
+ | AGENTS.md | `AGENTS.md` | (pseudo-agent ensuring root `AGENTS.md` exists) | - |
60
+ | GitHub Copilot | `AGENTS.md` | `.vscode/mcp.json` | `.claude/skills/` |
61
+ | Claude Code | `CLAUDE.md` | `.mcp.json` | `.claude/skills/` |
62
+ | OpenAI Codex CLI | `AGENTS.md` | `.codex/config.toml` | `.codex/skills/` |
63
+ | Pi Coding Agent | `AGENTS.md` | - | `.pi/skills/` |
64
+ | Jules | `AGENTS.md` | - | - |
65
+ | Cursor | `AGENTS.md` | `.cursor/mcp.json` | `.cursor/skills/` |
66
+ | Windsurf | `AGENTS.md` | `.windsurf/mcp_config.json` | - |
67
+ | Cline | `.clinerules` | - | - |
68
+ | Crush | `CRUSH.md` | `.crush.json` | - |
69
+ | Amp | `AGENTS.md` | - | `.agents/skills/` |
70
+ | Antigravity | `.agent/rules/ruler.md` | - | `.agent/skills/` |
71
+ | Amazon Q CLI | `.amazonq/rules/ruler_q_rules.md` | `.amazonq/mcp.json` | - |
72
+ | Aider | `AGENTS.md`, `.aider.conf.yml` | `.mcp.json` | - |
73
+ | Firebase Studio | `.idx/airules.md` | `.idx/mcp.json` | - |
74
+ | Open Hands | `.openhands/microagents/repo.md` | `config.toml` | - |
75
+ | Gemini CLI | `AGENTS.md` | `.gemini/settings.json` | `.gemini/skills/` |
76
+ | Junie | `.junie/guidelines.md` | - | - |
77
+ | AugmentCode | `.augment/rules/ruler_augment_instructions.md` | - | - |
78
+ | Kilo Code | `AGENTS.md` | `.kilocode/mcp.json` | `.claude/skills/` |
79
+ | OpenCode | `AGENTS.md` | `opencode.json` | `.opencode/skill/` |
80
+ | Goose | `.goosehints` | - | `.agents/skills/` |
81
+ | Qwen Code | `AGENTS.md` | `.qwen/settings.json` | - |
82
+ | RooCode | `AGENTS.md` | `.roo/mcp.json` | `.roo/skills/` |
83
+ | Zed | `AGENTS.md` | `.zed/settings.json` (project root, never $HOME) | - |
84
+ | Trae AI | `.trae/rules/project_rules.md` | - | - |
85
+ | Warp | `WARP.md` | - | - |
86
+ | Kiro | `.kiro/steering/ruler_kiro_instructions.md` | `.kiro/settings/mcp.json` | - |
87
+ | Firebender | `firebender.json` | `firebender.json` (rules and MCP in same file) | - |
88
+ | Factory Droid | `AGENTS.md` | `.factory/mcp.json` | `.factory/skills/` |
89
+ | Mistral Vibe | `AGENTS.md` | `.vibe/config.toml` | `.vibe/skills/` |
90
+ | JetBrains AI Assistant | `.aiassistant/rules/AGENTS.md` | - | - |
91
91
 
92
92
  ## Getting Started
93
93
 
@@ -139,8 +139,8 @@ This is your central hub for all AI agent instructions:
139
139
  4. Remaining discovered `.md` files under `.ruler/` (and subdirectories) in sorted order
140
140
  - **Rule Files (`*.md`)**: Discovered recursively from `.ruler/` or `$XDG_CONFIG_HOME/ruler` and concatenated in the order above
141
141
  - **Concatenation Marker**: Each file's content is prepended with `<!-- Source: <relative_path_to_md_file> -->` for traceability
142
- - **`ruler.toml`**: Master configuration for Ruler's behavior, agent selection, and output paths
143
- - **`mcp.json`**: Shared MCP server settings
142
+ - **`ruler.toml`**: Master configuration for Ruler's behavior, agent selection, output paths, and MCP server settings
143
+ - **`mcp.json`**: (Legacy, deprecated) Shared MCP server settings - no longer scaffolded but still supported for backward compatibility
144
144
 
145
145
  This ordering lets you keep a short, high-impact root `AGENTS.md` (e.g. executive project summary) while housing detailed guidance inside `.ruler/`.
146
146
 
@@ -241,6 +241,8 @@ The `apply` command looks for `.ruler/` in the current directory tree, reading t
241
241
  | `--nested` | Enable nested rule loading (default: inherit from config or disabled). |
242
242
  | `--no-nested` | Disable nested rule loading even if `nested = true` in config. |
243
243
  | `--backup` | Toggle creation of `.bak` backup files (default: enabled). |
244
+ | `--skills` | Enable skills support (experimental, default: enabled). |
245
+ | `--no-skills` | Disable skills support. |
244
246
  | `--dry-run` | Preview changes without writing files. |
245
247
  | `--local-only` | Skip `$XDG_CONFIG_HOME` when looking for configuration. |
246
248
  | `--verbose` / `-v` | Display detailed output during execution. |
@@ -102,9 +102,13 @@ class CodexCliAgent {
102
102
  // Add the ruler servers
103
103
  for (const [serverName, serverConfig] of Object.entries(rulerServers)) {
104
104
  // Create a properly formatted MCP server entry
105
- const mcpServer = {
106
- command: serverConfig.command,
107
- };
105
+ const mcpServer = {};
106
+ if (serverConfig.command) {
107
+ mcpServer.command = serverConfig.command;
108
+ }
109
+ if (serverConfig.url) {
110
+ mcpServer.url = serverConfig.url;
111
+ }
108
112
  if (serverConfig.args) {
109
113
  mcpServer.args = serverConfig.args;
110
114
  }
@@ -133,11 +137,14 @@ class CodexCliAgent {
133
137
  config: path.join(projectRoot, '.codex', 'config.toml'),
134
138
  };
135
139
  }
140
+ getMcpServerKey() {
141
+ return 'mcp_servers';
142
+ }
136
143
  supportsMcpStdio() {
137
144
  return true;
138
145
  }
139
146
  supportsMcpRemote() {
140
- return false; // Codex CLI only supports STDIO based on PR description
147
+ return true;
141
148
  }
142
149
  supportsNativeSkills() {
143
150
  return true;
@@ -226,6 +226,7 @@ async function findGlobalRulerDir() {
226
226
  */
227
227
  async function findAllRulerDirs(startPath) {
228
228
  const rulerDirs = [];
229
+ const rootPath = path.resolve(startPath);
229
230
  // Search the entire directory tree downwards from startPath
230
231
  async function findRulerDirs(dir) {
231
232
  try {
@@ -239,6 +240,18 @@ async function findAllRulerDirs(startPath) {
239
240
  else {
240
241
  // Recursively search subdirectories (but skip hidden directories like .git)
241
242
  if (!entry.name.startsWith('.')) {
243
+ // Do not cross git repository boundaries (except the starting root)
244
+ const gitDir = path.join(fullPath, '.git');
245
+ try {
246
+ const gitStat = await fs_1.promises.stat(gitDir);
247
+ if (gitStat.isDirectory() &&
248
+ path.resolve(fullPath) !== rootPath) {
249
+ continue;
250
+ }
251
+ }
252
+ catch {
253
+ // no .git boundary, continue traversal
254
+ }
242
255
  await findRulerDirs(fullPath);
243
256
  }
244
257
  }
@@ -41,6 +41,7 @@ exports.applyConfigurationsToAgents = applyConfigurationsToAgents;
41
41
  exports.updateGitignore = updateGitignore;
42
42
  const path = __importStar(require("path"));
43
43
  const fs_1 = require("fs");
44
+ const toml_1 = require("@iarna/toml");
44
45
  const FileSystemUtils = __importStar(require("./FileSystemUtils"));
45
46
  const RuleProcessor_1 = require("./RuleProcessor");
46
47
  const ConfigLoader_1 = require("./ConfigLoader");
@@ -544,8 +545,28 @@ async function applyStandardMcpConfiguration(agent, filteredMcpJson, dest, agent
544
545
  else if (agent.getIdentifier() === 'factory') {
545
546
  mcpToMerge = transformMcpForFactoryDroid(filteredMcpJson);
546
547
  }
547
- const existing = await (0, mcp_1.readNativeMcp)(dest);
548
- const merged = (0, merge_1.mergeMcp)(existing, mcpToMerge, strategy, serverKey);
548
+ const CODEX_AGENT_ID = 'codex';
549
+ const isCodexToml = agent.getIdentifier() === CODEX_AGENT_ID && dest.endsWith('.toml');
550
+ let existing = await (0, mcp_1.readNativeMcp)(dest);
551
+ if (isCodexToml) {
552
+ try {
553
+ const tomlContent = await fs_1.promises.readFile(dest, 'utf8');
554
+ existing = (0, toml_1.parse)(tomlContent);
555
+ }
556
+ catch (error) {
557
+ (0, constants_1.logVerbose)(`Failed to read Codex MCP TOML at ${dest}: ${error.message}`, verbose);
558
+ // ignore missing or invalid TOML, fall back to previously read value
559
+ }
560
+ }
561
+ let merged = (0, merge_1.mergeMcp)(existing, mcpToMerge, strategy, serverKey);
562
+ if (isCodexToml) {
563
+ const { [serverKey]: servers, ...rest } = merged;
564
+ merged = {
565
+ ...rest,
566
+ // Codex CLI expects MCP servers under mcp_servers in config.toml.
567
+ mcp_servers: servers ?? {},
568
+ };
569
+ }
549
570
  // Firebase Studio (IDX) expects no "type" fields in .idx/mcp.json server entries.
550
571
  // Sanitize merged config by stripping 'type' from each server when targeting Firebase.
551
572
  const sanitizeForFirebase = (obj) => {
@@ -593,14 +614,23 @@ async function applyStandardMcpConfiguration(agent, filteredMcpJson, dest, agent
593
614
  let toWrite = sanitizeForFirebase(merged);
594
615
  toWrite = sanitizeForGemini(toWrite);
595
616
  // Only backup and write if content would actually change (idempotent)
596
- const currentContent = JSON.stringify(existing, null, 2);
597
- const newContent = JSON.stringify(toWrite, null, 2);
617
+ const currentContent = isCodexToml
618
+ ? (0, toml_1.stringify)(existing)
619
+ : JSON.stringify(existing, null, 2);
620
+ const newContent = isCodexToml
621
+ ? (0, toml_1.stringify)(toWrite)
622
+ : JSON.stringify(toWrite, null, 2);
598
623
  if (currentContent !== newContent) {
599
624
  if (backup) {
600
625
  const { backupFile } = await Promise.resolve().then(() => __importStar(require('../core/FileSystemUtils')));
601
626
  await backupFile(dest);
602
627
  }
603
- await (0, mcp_1.writeNativeMcp)(dest, toWrite);
628
+ if (isCodexToml) {
629
+ await FileSystemUtils.writeGeneratedFile(dest, (0, toml_1.stringify)(toWrite));
630
+ }
631
+ else {
632
+ await (0, mcp_1.writeNativeMcp)(dest, toWrite);
633
+ }
604
634
  }
605
635
  else {
606
636
  (0, constants_1.logVerbose)(`MCP config for ${agent.getName()} is already up to date - skipping backup and write`, verbose);
package/dist/paths/mcp.js CHANGED
@@ -59,7 +59,7 @@ async function getNativeMcpPath(adapterName, projectRoot) {
59
59
  candidates.push(path.join(projectRoot, '.mcp.json'));
60
60
  break;
61
61
  case 'OpenAI Codex CLI':
62
- candidates.push(path.join(projectRoot, '.codex', 'config.json'));
62
+ candidates.push(path.join(projectRoot, '.codex', 'config.toml'));
63
63
  break;
64
64
  case 'Aider':
65
65
  candidates.push(path.join(projectRoot, '.mcp.json'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intellectronica/ruler",
3
- "version": "0.3.29",
3
+ "version": "0.3.31",
4
4
  "description": "Ruler — apply the same rules to all coding agents",
5
5
  "main": "dist/lib.js",
6
6
  "scripts": {