@intellectronica/ruler 0.3.41 → 0.3.43

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.
Files changed (105) hide show
  1. package/README.md +135 -36
  2. package/dist/agents/AbstractAgent.d.ts +53 -0
  3. package/dist/agents/AbstractAgent.js +3 -2
  4. package/dist/agents/AgentsMdAgent.d.ts +14 -0
  5. package/dist/agents/AgentsMdAgent.js +3 -2
  6. package/dist/agents/AiderAgent.d.ts +14 -0
  7. package/dist/agents/AiderAgent.js +7 -4
  8. package/dist/agents/AmazonQCliAgent.d.ts +13 -0
  9. package/dist/agents/AmazonQCliAgent.js +6 -4
  10. package/dist/agents/AmpAgent.d.ts +6 -0
  11. package/dist/agents/AntigravityAgent.d.ts +10 -0
  12. package/dist/agents/AugmentCodeAgent.d.ts +13 -0
  13. package/dist/agents/AugmentCodeAgent.js +3 -2
  14. package/dist/agents/ClaudeAgent.d.ts +13 -0
  15. package/dist/agents/ClineAgent.d.ts +9 -0
  16. package/dist/agents/CodexCliAgent.d.ts +31 -0
  17. package/dist/agents/CodexCliAgent.js +1 -1
  18. package/dist/agents/CopilotAgent.d.ts +20 -0
  19. package/dist/agents/CrushAgent.d.ts +14 -0
  20. package/dist/agents/CrushAgent.js +18 -6
  21. package/dist/agents/CursorAgent.d.ts +17 -0
  22. package/dist/agents/FactoryDroidAgent.d.ts +13 -0
  23. package/dist/agents/FirebaseAgent.d.ts +11 -0
  24. package/dist/agents/FirebenderAgent.d.ts +36 -0
  25. package/dist/agents/FirebenderAgent.js +5 -4
  26. package/dist/agents/GeminiCliAgent.d.ts +12 -0
  27. package/dist/agents/GeminiCliAgent.js +13 -7
  28. package/dist/agents/GooseAgent.d.ts +12 -0
  29. package/dist/agents/IAgent.d.ts +74 -0
  30. package/dist/agents/JetBrainsAiAssistantAgent.d.ts +10 -0
  31. package/dist/agents/JulesAgent.d.ts +5 -0
  32. package/dist/agents/JunieAgent.d.ts +12 -0
  33. package/dist/agents/KiloCodeAgent.d.ts +14 -0
  34. package/dist/agents/KiroAgent.d.ts +8 -0
  35. package/dist/agents/MistralVibeAgent.d.ts +31 -0
  36. package/dist/agents/MistralVibeAgent.js +14 -3
  37. package/dist/agents/OpenCodeAgent.d.ts +11 -0
  38. package/dist/agents/OpenCodeAgent.js +24 -12
  39. package/dist/agents/OpenHandsAgent.d.ts +8 -0
  40. package/dist/agents/PiAgent.d.ts +9 -0
  41. package/dist/agents/QwenCodeAgent.d.ts +11 -0
  42. package/dist/agents/QwenCodeAgent.js +11 -5
  43. package/dist/agents/RooCodeAgent.d.ts +16 -0
  44. package/dist/agents/RooCodeAgent.js +3 -2
  45. package/dist/agents/TraeAgent.d.ts +10 -0
  46. package/dist/agents/WarpAgent.d.ts +12 -0
  47. package/dist/agents/WindsurfAgent.d.ts +13 -0
  48. package/dist/agents/ZedAgent.d.ts +21 -0
  49. package/dist/agents/ZedAgent.js +8 -5
  50. package/dist/agents/agent-utils.d.ts +5 -0
  51. package/dist/agents/agent-utils.js +8 -5
  52. package/dist/agents/index.d.ts +9 -0
  53. package/dist/cli/commands.d.ts +4 -0
  54. package/dist/cli/commands.js +1 -2
  55. package/dist/cli/handlers.d.ts +41 -0
  56. package/dist/cli/handlers.js +75 -59
  57. package/dist/cli/index.d.ts +2 -0
  58. package/dist/constants.d.ts +35 -0
  59. package/dist/constants.js +1 -1
  60. package/dist/core/ConfigLoader.d.ts +59 -0
  61. package/dist/core/ConfigLoader.js +178 -44
  62. package/dist/core/FileSystemUtils.d.ts +53 -0
  63. package/dist/core/FileSystemUtils.js +157 -20
  64. package/dist/core/GitignoreUtils.d.ts +25 -0
  65. package/dist/core/GitignoreUtils.js +94 -32
  66. package/dist/core/RuleProcessor.d.ts +8 -0
  67. package/dist/core/SkillsProcessor.d.ts +127 -0
  68. package/dist/core/SkillsProcessor.js +118 -223
  69. package/dist/core/SkillsUtils.d.ts +26 -0
  70. package/dist/core/SubagentsProcessor.d.ts +38 -0
  71. package/dist/core/SubagentsProcessor.js +8 -5
  72. package/dist/core/SubagentsUtils.d.ts +34 -0
  73. package/dist/core/UnifiedConfigLoader.d.ts +10 -0
  74. package/dist/core/UnifiedConfigLoader.js +115 -33
  75. package/dist/core/UnifiedConfigTypes.d.ts +97 -0
  76. package/dist/core/agent-selection.d.ts +12 -0
  77. package/dist/core/agent-selection.js +17 -7
  78. package/dist/core/apply-engine.d.ts +70 -0
  79. package/dist/core/apply-engine.js +88 -58
  80. package/dist/core/config-utils.d.ts +14 -0
  81. package/dist/core/config-utils.js +9 -3
  82. package/dist/core/hash.d.ts +2 -0
  83. package/dist/core/path-utils.d.ts +1 -0
  84. package/dist/core/path-utils.js +42 -0
  85. package/dist/core/revert-engine.d.ts +37 -0
  86. package/dist/core/revert-engine.js +142 -34
  87. package/dist/lib.d.ts +13 -0
  88. package/dist/lib.js +24 -8
  89. package/dist/mcp/capabilities.d.ts +20 -0
  90. package/dist/mcp/merge.d.ts +10 -0
  91. package/dist/mcp/merge.js +36 -16
  92. package/dist/mcp/propagateOpenCodeMcp.d.ts +2 -0
  93. package/dist/mcp/propagateOpenCodeMcp.js +30 -11
  94. package/dist/mcp/propagateOpenHandsMcp.d.ts +2 -0
  95. package/dist/mcp/propagateOpenHandsMcp.js +48 -21
  96. package/dist/mcp/validate.d.ts +7 -0
  97. package/dist/mcp/validate.js +6 -1
  98. package/dist/paths/mcp.d.ts +8 -0
  99. package/dist/paths/mcp.js +44 -8
  100. package/dist/revert.d.ts +6 -0
  101. package/dist/revert.js +58 -46
  102. package/dist/types.d.ts +87 -0
  103. package/dist/vscode/settings.d.ts +40 -0
  104. package/dist/vscode/settings.js +3 -3
  105. package/package.json +8 -5
package/README.md CHANGED
@@ -18,7 +18,7 @@
18
18
  </p>
19
19
  </td>
20
20
  <td style="vertical-align: top; width:33%;">
21
- <img src="img/ruler-short.gif" alt="Ruler demo" style="width:300px; height:auto; display:block;" />
21
+ <img src="https://raw.githubusercontent.com/intellectronica/ruler/main/img/ruler-short.gif" alt="Ruler demo" style="width:300px; height:auto; display:block;" />
22
22
  </td>
23
23
  </tr>
24
24
  </table>
@@ -59,7 +59,7 @@ Ruler solves this by providing a **single source of truth** for all your AI agen
59
59
  | AGENTS.md | `AGENTS.md` | (pseudo-agent ensuring root `AGENTS.md` exists) | - | - |
60
60
  | GitHub Copilot | `AGENTS.md` | `.vscode/mcp.json` | `.claude/skills/` | `.github/agents/` |
61
61
  | Claude Code | `CLAUDE.md` | `.mcp.json` | `.claude/skills/` | `.claude/agents/` |
62
- | OpenAI Codex CLI | `AGENTS.md` | `.codex/config.toml` | `.codex/skills/` | `.codex/agents/` (`.toml`) |
62
+ | OpenAI Codex CLI | `AGENTS.md` | `.codex/config.toml` | `.agents/skills/` | `.codex/agents/` (`.toml`) |
63
63
  | Pi Coding Agent | `AGENTS.md` | - | `.pi/skills/` | - |
64
64
  | Jules | `AGENTS.md` | - | - | - |
65
65
  | Cursor | `AGENTS.md` | `.cursor/mcp.json` | `.cursor/skills/` | `.cursor/agents/` |
@@ -80,7 +80,7 @@ Ruler solves this by providing a **single source of truth** for all your AI agen
80
80
  | Goose | `.goosehints` | - | `.agents/skills/` | - |
81
81
  | Qwen Code | `AGENTS.md` | `.qwen/settings.json` | - | - |
82
82
  | RooCode | `AGENTS.md` | `.roo/mcp.json` | `.roo/skills/` | - |
83
- | Zed | `AGENTS.md` | `.zed/settings.json` (project root, never $HOME) | - | - |
83
+ | Zed | `AGENTS.md` | `.zed/settings.json` (project root, never $HOME) | `.agents/skills/` | - |
84
84
  | Trae AI | `.trae/rules/project_rules.md` | - | - | - |
85
85
  | Warp | `WARP.md` | - | - | - |
86
86
  | Kiro | `.kiro/steering/ruler_kiro_instructions.md` | `.kiro/settings/mcp.json` | - | - |
@@ -224,32 +224,32 @@ project/
224
224
  ruler apply [options]
225
225
  ```
226
226
 
227
- The `apply` command looks for `.ruler/` in the current directory tree, reading the first match. If no such directory is found, it will look for a global configuration in `$XDG_CONFIG_HOME/ruler`.
227
+ The `apply` command searches upward from `--project-root` (default: current directory) for the nearest `.ruler/` directory. If no local `.ruler/` directory is found, it falls back to `$XDG_CONFIG_HOME/ruler`.
228
228
 
229
229
  ### Options
230
230
 
231
- | Option | Description |
232
- | ------------------------------ | ---------------------------------------------------------------------- |
233
- | `--project-root <path>` | Project root path (default: current directory). |
234
- | `--agents <agent1,agent2,...>` | Comma-separated agent names to target (see supported list below). |
235
- | `--config <path>` | Custom `ruler.toml` path. |
236
- | `--mcp` / `--with-mcp` | Enable applying MCP server configurations (default: true). |
237
- | `--no-mcp` | Disable applying MCP server configurations. |
238
- | `--mcp-overwrite` | Overwrite native MCP config instead of merging. |
239
- | `--gitignore` | Enable automatic .gitignore updates (default: true). |
240
- | `--no-gitignore` | Disable automatic .gitignore updates. |
241
- | `--gitignore-local` | Write managed ignore entries to `.git/info/exclude` instead. |
242
- | `--nested` | Enable nested rule loading (default: inherit from config or disabled). |
243
- | `--no-nested` | Disable nested rule loading even if `nested = true` in config. |
244
- | `--backup` | Enable creation of `.bak` backup files (default: enabled). |
245
- | `--no-backup` | Disable creation of `.bak` backup files. |
246
- | `--skills` | Enable skills support (experimental, default: enabled). |
247
- | `--no-skills` | Disable skills support. |
248
- | `--subagents` | Enable subagents support (experimental, default: disabled). |
249
- | `--no-subagents` | Disable subagents support. |
250
- | `--dry-run` | Preview changes without writing files. |
251
- | `--local-only` | Skip `$XDG_CONFIG_HOME` when looking for configuration. |
252
- | `--verbose` / `-v` | Display detailed output during execution. |
231
+ | Option | Description |
232
+ | ------------------------------ | ------------------------------------------------------------------------- |
233
+ | `--project-root <path>` | Project root path (default: current directory). |
234
+ | `--agents <agent1,agent2,...>` | Comma-separated agent names to target (see supported list below). |
235
+ | `--config <path>` | Custom `ruler.toml` path. |
236
+ | `--mcp` / `--with-mcp` | Enable applying MCP server configurations (default: true). |
237
+ | `--no-mcp` | Disable applying MCP server configurations. |
238
+ | `--mcp-overwrite` | Overwrite native MCP config instead of merging. |
239
+ | `--gitignore` | Enable automatic .gitignore updates (default: true). |
240
+ | `--no-gitignore` | Disable automatic .gitignore updates. |
241
+ | `--gitignore-local` | Write managed ignore entries to `.git/info/exclude` instead. |
242
+ | `--nested` | Enable nested rule loading (default: inherit from config or disabled). |
243
+ | `--no-nested` | Disable nested rule loading even if `nested = true` in config. |
244
+ | `--backup` | Enable creation of `.bak` backup files (default: from config or enabled). |
245
+ | `--no-backup` | Disable creation of `.bak` backup files. |
246
+ | `--skills` | Enable skills support (experimental, default: enabled). |
247
+ | `--no-skills` | Disable skills support. |
248
+ | `--subagents` | Enable subagents support (experimental, default: disabled). |
249
+ | `--no-subagents` | Disable subagents support. |
250
+ | `--dry-run` | Preview changes without writing files. |
251
+ | `--local-only` | Skip `$XDG_CONFIG_HOME` when looking for configuration. |
252
+ | `--verbose` / `-v` | Display detailed output during execution. |
253
253
 
254
254
  ### Common Examples
255
255
 
@@ -412,6 +412,11 @@ enabled = true
412
412
  # Write managed entries to .git/info/exclude instead of .gitignore (default: false)
413
413
  local = false
414
414
 
415
+ # --- Backup Configuration ---
416
+ [backup]
417
+ # Enable/disable creation of .bak backup files (default: true)
418
+ enabled = true
419
+
415
420
  # --- Agent-Specific Configurations ---
416
421
  [agents.copilot]
417
422
  enabled = true
@@ -509,6 +514,20 @@ Authorization = "Bearer your-token"
509
514
  "X-API-Version" = "v1"
510
515
  ```
511
516
 
517
+ Agent-specific MCP servers can be defined under `[agents.<agent>.mcp_servers.<name>]`.
518
+ They are applied only to that agent and override global servers with the same name:
519
+
520
+ ```toml
521
+ [agents.cursor.mcp_servers.slack]
522
+ url = "https://mcp.slack.com/mcp"
523
+ auth = { CLIENT_ID = "CURSOR_ID" }
524
+
525
+ [agents.claude.mcp_servers.slack]
526
+ type = "http"
527
+ url = "https://mcp.slack.com/mcp"
528
+ oauth = { clientId = "CLAUDE_ID", callbackPort = 3118 }
529
+ ```
530
+
512
531
  ### Legacy `.ruler/mcp.json` (Deprecated)
513
532
 
514
533
  For backward compatibility, you can still use the JSON format; a warning is issued encouraging migration to TOML. The file is no longer created during `ruler init`.
@@ -587,11 +606,12 @@ Skills are specialized knowledge packages that extend AI agent capabilities with
587
606
  - **Claude Code**: `.claude/skills/`
588
607
  - **GitHub Copilot**: `.claude/skills/` (shared with Claude Code)
589
608
  - **Kilo Code**: `.claude/skills/` (shared with Claude Code)
590
- - **OpenAI Codex CLI**: `.codex/skills/`
609
+ - **OpenAI Codex CLI**: `.agents/skills/` (shared with Goose, Amp, and Zed)
591
610
  - **OpenCode**: `.opencode/skills/`
592
611
  - **Pi Coding Agent**: `.pi/skills/`
593
612
  - **Goose**: `.agents/skills/`
594
613
  - **Amp**: `.agents/skills/` (shared with Goose)
614
+ - **Zed**: `.agents/skills/` (shared with Goose)
595
615
  - **Antigravity**: `.agent/skills/`
596
616
  - **Factory Droid**: `.factory/skills/`
597
617
  - **Mistral Vibe**: `.vibe/skills/`
@@ -655,10 +675,9 @@ If you run Ruler for agents that do not support native skills, Ruler logs a warn
655
675
  When skills support is enabled and gitignore integration is active, Ruler automatically adds:
656
676
 
657
677
  - `.claude/skills/` (for Claude Code, GitHub Copilot, and Kilo Code)
658
- - `.codex/skills/` (for OpenAI Codex CLI)
678
+ - `.agents/skills/` (for OpenAI Codex CLI, Goose, Amp, and Zed)
659
679
  - `.opencode/skills/` (for OpenCode)
660
680
  - `.pi/skills/` (for Pi Coding Agent)
661
- - `.agents/skills/` (for Goose and Amp)
662
681
  - `.agent/skills/` (for Antigravity)
663
682
  - `.factory/skills/` (for Factory Droid)
664
683
  - `.vibe/skills/` (for Mistral Vibe)
@@ -672,7 +691,7 @@ to your `.gitignore` file within the managed Ruler block.
672
691
 
673
692
  ### Requirements
674
693
 
675
- - **For agents with native skills support** (Claude Code, GitHub Copilot, Kilo Code, OpenAI Codex CLI, OpenCode, Pi Coding Agent, Goose, Amp, Antigravity, Factory Droid, Mistral Vibe, Roo Code, Gemini CLI, Junie, Cursor, Windsurf): No additional requirements.
694
+ - **For agents with native skills support** (Claude Code, GitHub Copilot, Kilo Code, OpenAI Codex CLI, OpenCode, Pi Coding Agent, Goose, Amp, Zed, Antigravity, Factory Droid, Mistral Vibe, Roo Code, Gemini CLI, Junie, Cursor, Windsurf): No additional requirements.
676
695
 
677
696
  ### Validation
678
697
 
@@ -716,10 +735,10 @@ ruler apply
716
735
 
717
736
  # 3. Skills are now available to compatible agents:
718
737
  # - Claude Code, GitHub Copilot & Kilo Code: .claude/skills/my-skill/
719
- # - OpenAI Codex CLI: .codex/skills/my-skill/
738
+ # - OpenAI Codex CLI: .agents/skills/my-skill/ (shared with Goose, Amp & Zed)
720
739
  # - OpenCode: .opencode/skills/my-skill/
721
740
  # - Pi Coding Agent: .pi/skills/my-skill/
722
- # - Goose & Amp: .agents/skills/my-skill/
741
+ # - Goose, Amp, Zed & OpenAI Codex CLI: .agents/skills/my-skill/
723
742
  # - Antigravity: .agent/skills/my-skill/
724
743
  # - Factory Droid: .factory/skills/my-skill/
725
744
  # - Mistral Vibe: .vibe/skills/my-skill/
@@ -909,6 +928,12 @@ dist/
909
928
  - **Configuration**: `[gitignore].enabled` and `[gitignore].local` in `ruler.toml`
910
929
  - **Default**: enabled
911
930
 
931
+ ### Backup Control
932
+
933
+ - **CLI flags**: `--backup`, `--no-backup`
934
+ - **Configuration**: `[backup].enabled` in `ruler.toml`
935
+ - **Default**: enabled
936
+
912
937
  ## Practical Usage Scenarios
913
938
 
914
939
  ### Scenario 1: Getting Started Quickly
@@ -926,7 +951,81 @@ ruler init
926
951
  ruler apply
927
952
  ```
928
953
 
929
- ### Scenario 2: Complex Projects with Nested Rules
954
+ ### Scenario 2: Working with worktrees
955
+
956
+ When using the default `git add worktree` command (which is also run by agents apps such as Claude code or Codex through the interface), the gitignored files are not copied over. You will need to ask your agent to run `ruler apply` at the start of every session.
957
+
958
+ As an alternative you can commit your default agents files to source control.
959
+
960
+ ```toml
961
+ # .ruler/ruler.toml
962
+ default_agents = ["claude", "codex"]
963
+
964
+ [gitignore]
965
+ enabled = false
966
+ ```
967
+
968
+ ```ignore
969
+ # Do not ignore AGENTS.md and CLAUDE.md
970
+ /.claude/*
971
+ !/.claude/skills/
972
+ /.codex/*
973
+ !/.codex/skills/
974
+ /.cursor
975
+ /AGENTS.md.bak
976
+ /CLAUDE.md.bak
977
+ ```
978
+
979
+ To avoid having other contributors commit instructions outside of .ruler you can setup a github action to check there is no diff when running `ruler apply` in CI.
980
+
981
+ ```yml
982
+ # .github/workflows/ruler-check/yml
983
+
984
+ # Verifies the committed agent files (AGENTS.md, CLAUDE.md, skills) match the .ruler/ source.
985
+ # They are committed so a fresh clone/worktree has guidance immediately; this guards against drift.
986
+ name: Ruler guidance in sync
987
+
988
+ on:
989
+ pull_request:
990
+ push:
991
+ branches:
992
+ - main
993
+ - 'build/**'
994
+
995
+ permissions:
996
+ contents: read
997
+
998
+ env:
999
+ CI_NODE_VERSION: 24.15.0
1000
+
1001
+ jobs:
1002
+ ruler-check:
1003
+ runs-on: ubuntu-latest
1004
+ steps:
1005
+ - name: Checkout
1006
+ uses: actions/checkout@v6
1007
+
1008
+ - uses: pnpm/action-setup@v5
1009
+ - name: Setup Node
1010
+ uses: actions/setup-node@v6
1011
+ with:
1012
+ node-version: ${{ env.CI_NODE_VERSION }}
1013
+ cache: 'pnpm'
1014
+
1015
+ - name: Verify committed agent files match .ruler/
1016
+ run: |
1017
+ pnpm dlx @intellectronica/ruler@0.3.42 apply --no-gitignore --no-mcp
1018
+ DRIFT="$(git status --porcelain -- AGENTS.md CLAUDE.md .claude/skills .codex/skills)"
1019
+ if [ -n "$DRIFT" ]; then
1020
+ echo "::error::Committed agent files are out of sync with .ruler/. Run 'pnpm dlx @intellectronica/ruler apply --no-gitignore --no-mcp' and commit the result."
1021
+ echo "$DRIFT"
1022
+ git --no-pager diff -- AGENTS.md CLAUDE.md .claude/skills .codex/skills
1023
+ exit 1
1024
+ fi
1025
+ echo "Agent files are in sync with .ruler/."
1026
+ ```
1027
+
1028
+ ### Scenario 3: Complex Projects with Nested Rules
930
1029
 
931
1030
  For large projects with multiple components or services, enable nested rule loading so each directory keeps its own rules and MCP bundle:
932
1031
 
@@ -958,13 +1057,13 @@ This creates context-specific instructions for different parts of your project w
958
1057
  > [!NOTE]
959
1058
  > The CLI prints "Nested mode is experimental and may change in future releases." the first time nested processing runs. Expect refinements in future versions.
960
1059
 
961
- ### Scenario 3: Team Standardization
1060
+ ### Scenario 4: Team Standardization
962
1061
 
963
1062
  1. Create `.ruler/coding_standards.md`, `.ruler/api_usage.md`
964
1063
  2. Commit the `.ruler` directory to your repository
965
1064
  3. Team members pull changes and run `ruler apply` to update their local AI agent configurations
966
1065
 
967
- ### Scenario 4: Project-Specific Context for AI
1066
+ ### Scenario 5: Project-Specific Context for AI
968
1067
 
969
1068
  1. Detail your project's architecture in `.ruler/project_overview.md`
970
1069
  2. Describe primary data structures in `.ruler/data_models.md`
@@ -1079,7 +1178,7 @@ A: Simply rename `.ruler/instructions.md` to `.ruler/AGENTS.md` (recommended). I
1079
1178
  A: Local stdio servers become `stdio_servers`. Remote URLs containing `/sse` are classified as `sse_servers`; others become `shttp_servers`. Bearer tokens in an `Authorization` header are extracted into `api_key` where possible.
1080
1179
 
1081
1180
  **Q: Where is Zed configuration written now?**
1082
- A: Ruler writes a `settings.json` in the project root (not the user home dir) and transforms MCP server definitions to Zed's `context_servers` format including `source: "custom"`.
1181
+ A: Ruler writes `.zed/settings.json` inside the project root (not the user home dir) and transforms MCP server definitions to Zed's `context_servers` format including `source: "custom"`.
1083
1182
 
1084
1183
  **Q: What changed about MCP initialization?**
1085
1184
  A: `ruler init` now only adds example MCP server sections to `ruler.toml` instead of creating `.ruler/mcp.json`. The JSON file is still consumed if present, but TOML servers win on name conflicts.
@@ -0,0 +1,53 @@
1
+ import { IAgent, IAgentConfig } from './IAgent';
2
+ /**
3
+ * Abstract base class for agents that write to a single configuration file.
4
+ * Implements common logic for applying ruler configuration.
5
+ */
6
+ export declare abstract class AbstractAgent implements IAgent {
7
+ /**
8
+ * Returns the lowercase identifier of the agent.
9
+ */
10
+ abstract getIdentifier(): string;
11
+ /**
12
+ * Returns the display name of the agent.
13
+ */
14
+ abstract getName(): string;
15
+ /**
16
+ * Returns the default output path for this agent given the project root.
17
+ */
18
+ abstract getDefaultOutputPath(projectRoot: string): string;
19
+ /**
20
+ * Applies the concatenated ruler rules to the agent's configuration.
21
+ * This implementation handles the common pattern of:
22
+ * 1. Determining the output path
23
+ * 2. Ensuring the parent directory exists
24
+ * 3. Backing up the existing file
25
+ * 4. Writing the new content
26
+ */
27
+ applyRulerConfig(concatenatedRules: string, projectRoot: string, _rulerMcpJson: Record<string, unknown> | null, agentConfig?: IAgentConfig, backup?: boolean): Promise<void>;
28
+ /**
29
+ * Returns the specific key to be used for the server object in MCP JSON.
30
+ * Defaults to 'mcpServers' if not overridden.
31
+ */
32
+ getMcpServerKey(): string;
33
+ /**
34
+ * Returns whether this agent supports MCP STDIO servers.
35
+ * Defaults to false if not overridden.
36
+ */
37
+ supportsMcpStdio(): boolean;
38
+ /**
39
+ * Returns whether this agent supports MCP remote servers.
40
+ * Defaults to false if not overridden.
41
+ */
42
+ supportsMcpRemote(): boolean;
43
+ /**
44
+ * Returns whether this agent supports MCP server timeout configuration.
45
+ * Defaults to false if not overridden.
46
+ */
47
+ supportsMcpTimeout(): boolean;
48
+ /**
49
+ * Returns whether this agent has native skills support.
50
+ * Defaults to false if not overridden.
51
+ */
52
+ supportsNativeSkills(): boolean;
53
+ }
@@ -52,11 +52,12 @@ class AbstractAgent {
52
52
  async applyRulerConfig(concatenatedRules, projectRoot, _rulerMcpJson, agentConfig, backup = true) {
53
53
  const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
54
54
  const absolutePath = path.resolve(projectRoot, output);
55
+ await (0, FileSystemUtils_1.assertManagedPathInsideRoot)(absolutePath, projectRoot, 'Refusing to write generated file outside project');
55
56
  await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(absolutePath));
56
57
  if (backup) {
57
- await (0, FileSystemUtils_1.backupFile)(absolutePath);
58
+ await (0, FileSystemUtils_1.backupFile)(absolutePath, projectRoot);
58
59
  }
59
- await (0, FileSystemUtils_1.writeGeneratedFile)(absolutePath, concatenatedRules);
60
+ await (0, FileSystemUtils_1.writeGeneratedFile)(absolutePath, concatenatedRules, projectRoot);
60
61
  }
61
62
  /**
62
63
  * Returns the specific key to be used for the server object in MCP JSON.
@@ -0,0 +1,14 @@
1
+ import { AbstractAgent } from './AbstractAgent';
2
+ import { IAgentConfig } from './IAgent';
3
+ /**
4
+ * Pseudo-agent that ensures the concatenated rules are written to root-level `AGENTS.md`.
5
+ * Does not participate in MCP propagation. Idempotent: only writes (and creates a backup)
6
+ * when content differs from existing file.
7
+ */
8
+ export declare class AgentsMdAgent extends AbstractAgent {
9
+ getIdentifier(): string;
10
+ getName(): string;
11
+ getDefaultOutputPath(projectRoot: string): string;
12
+ applyRulerConfig(concatenatedRules: string, projectRoot: string, _rulerMcpJson: Record<string, unknown> | null, agentConfig?: IAgentConfig, backup?: boolean): Promise<void>;
13
+ getMcpServerKey(): string;
14
+ }
@@ -56,6 +56,7 @@ class AgentsMdAgent extends AbstractAgent_1.AbstractAgent {
56
56
  async applyRulerConfig(concatenatedRules, projectRoot, _rulerMcpJson, agentConfig, backup = true) {
57
57
  const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
58
58
  const absolutePath = path.resolve(projectRoot, output);
59
+ await (0, FileSystemUtils_1.assertManagedPathInsideRoot)(absolutePath, projectRoot, 'Refusing to write generated file outside project');
59
60
  await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(absolutePath));
60
61
  // Add marker comment to the content to identify it as generated
61
62
  const contentWithMarker = `<!-- Generated by Ruler -->\n${concatenatedRules}`;
@@ -73,9 +74,9 @@ class AgentsMdAgent extends AbstractAgent_1.AbstractAgent {
73
74
  }
74
75
  // Backup (only if file existed and backup is enabled) then write new content
75
76
  if (backup) {
76
- await (0, FileSystemUtils_1.backupFile)(absolutePath);
77
+ await (0, FileSystemUtils_1.backupFile)(absolutePath, projectRoot);
77
78
  }
78
- await (0, FileSystemUtils_1.writeGeneratedFile)(absolutePath, contentWithMarker);
79
+ await (0, FileSystemUtils_1.writeGeneratedFile)(absolutePath, contentWithMarker, projectRoot);
79
80
  }
80
81
  getMcpServerKey() {
81
82
  // No MCP configuration for this pseudo-agent
@@ -0,0 +1,14 @@
1
+ import { IAgent, IAgentConfig } from './IAgent';
2
+ /**
3
+ * Aider agent adapter that uses AGENTS.md for instructions and .aider.conf.yml for configuration.
4
+ */
5
+ export declare class AiderAgent implements IAgent {
6
+ private agentsMdAgent;
7
+ getIdentifier(): string;
8
+ getName(): string;
9
+ applyRulerConfig(concatenatedRules: string, projectRoot: string, rulerMcpJson: Record<string, unknown> | null, agentConfig?: IAgentConfig, backup?: boolean): Promise<void>;
10
+ getDefaultOutputPath(projectRoot: string): Record<string, string>;
11
+ getMcpServerKey(): string;
12
+ supportsMcpStdio(): boolean;
13
+ supportsMcpRemote(): boolean;
14
+ }
@@ -67,7 +67,7 @@ class AiderAgent {
67
67
  try {
68
68
  await fs.access(cfgPath);
69
69
  if (backup) {
70
- await (0, FileSystemUtils_1.backupFile)(cfgPath);
70
+ await (0, FileSystemUtils_1.backupFile)(cfgPath, projectRoot);
71
71
  }
72
72
  const raw = await fs.readFile(cfgPath, 'utf8');
73
73
  doc = (yaml.load(raw) || {});
@@ -82,21 +82,24 @@ class AiderAgent {
82
82
  const agentsPath = agentConfig?.outputPath ||
83
83
  agentConfig?.outputPathInstructions ||
84
84
  this.getDefaultOutputPath(projectRoot).instructions;
85
- const name = path.basename(agentsPath);
85
+ const name = path
86
+ .relative(projectRoot, path.resolve(projectRoot, agentsPath))
87
+ .replace(/\\/g, '/');
86
88
  if (!doc.read.includes(name)) {
87
89
  doc.read.push(name);
88
90
  }
89
91
  const yamlStr = yaml.dump(doc);
90
- await (0, FileSystemUtils_1.writeGeneratedFile)(cfgPath, yamlStr);
92
+ await (0, FileSystemUtils_1.writeGeneratedFile)(cfgPath, yamlStr, projectRoot);
91
93
  }
92
94
  getDefaultOutputPath(projectRoot) {
93
95
  return {
94
96
  instructions: path.join(projectRoot, 'AGENTS.md'),
95
97
  config: path.join(projectRoot, '.aider.conf.yml'),
98
+ mcp: path.join(projectRoot, '.mcp.json'),
96
99
  };
97
100
  }
98
101
  getMcpServerKey() {
99
- return this.agentsMdAgent.getMcpServerKey();
102
+ return 'mcpServers';
100
103
  }
101
104
  supportsMcpStdio() {
102
105
  return true;
@@ -0,0 +1,13 @@
1
+ import { IAgent, IAgentConfig } from './IAgent';
2
+ /**
3
+ * Amazon Q CLI agent adapter.
4
+ */
5
+ export declare class AmazonQCliAgent implements IAgent {
6
+ getIdentifier(): string;
7
+ getName(): string;
8
+ applyRulerConfig(concatenatedRules: string, projectRoot: string, rulerMcpJson: Record<string, unknown> | null, agentConfig?: IAgentConfig, backup?: boolean): Promise<void>;
9
+ getDefaultOutputPath(projectRoot: string): Record<string, string>;
10
+ getMcpServerKey(): string;
11
+ supportsMcpStdio(): boolean;
12
+ supportsMcpRemote(): boolean;
13
+ }
@@ -54,16 +54,18 @@ class AmazonQCliAgent {
54
54
  agentConfig?.outputPathInstructions ||
55
55
  outputPaths['instructions']);
56
56
  // Write rules file to .amazonq/rules/
57
+ await (0, FileSystemUtils_1.assertManagedPathInsideRoot)(rulesPath, projectRoot, 'Refusing to write generated file outside project');
57
58
  await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(rulesPath));
58
59
  if (backup) {
59
- await (0, FileSystemUtils_1.backupFile)(rulesPath);
60
+ await (0, FileSystemUtils_1.backupFile)(rulesPath, projectRoot);
60
61
  }
61
- await (0, FileSystemUtils_1.writeGeneratedFile)(rulesPath, concatenatedRules);
62
+ await (0, FileSystemUtils_1.writeGeneratedFile)(rulesPath, concatenatedRules, projectRoot);
62
63
  // Handle MCP configuration if enabled and provided
63
64
  const mcpEnabled = agentConfig?.mcp?.enabled ?? true;
64
65
  if (mcpEnabled && rulerMcpJson) {
65
66
  const mcpPath = path.resolve(projectRoot, agentConfig?.outputPathConfig ?? outputPaths['mcp']);
66
67
  const mcpStrategy = agentConfig?.mcp?.strategy ?? 'merge';
68
+ await (0, FileSystemUtils_1.assertManagedPathInsideRoot)(mcpPath, projectRoot, 'Refusing to write generated file outside project');
67
69
  await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(mcpPath));
68
70
  let existingMcpConfig = {};
69
71
  try {
@@ -79,9 +81,9 @@ class AmazonQCliAgent {
79
81
  // Merge the MCP configurations using the standard merge function
80
82
  const mergedConfig = (0, merge_1.mergeMcp)(existingMcpConfig, rulerMcpJson, mcpStrategy, 'mcpServers');
81
83
  if (backup) {
82
- await (0, FileSystemUtils_1.backupFile)(mcpPath);
84
+ await (0, FileSystemUtils_1.backupFile)(mcpPath, projectRoot);
83
85
  }
84
- await (0, FileSystemUtils_1.writeGeneratedFile)(mcpPath, JSON.stringify(mergedConfig, null, 2));
86
+ await (0, FileSystemUtils_1.writeGeneratedFile)(mcpPath, JSON.stringify(mergedConfig, null, 2), projectRoot);
85
87
  }
86
88
  }
87
89
  getDefaultOutputPath(projectRoot) {
@@ -0,0 +1,6 @@
1
+ import { AgentsMdAgent } from './AgentsMdAgent';
2
+ export declare class AmpAgent extends AgentsMdAgent {
3
+ getIdentifier(): string;
4
+ getName(): string;
5
+ supportsNativeSkills(): boolean;
6
+ }
@@ -0,0 +1,10 @@
1
+ import { AbstractAgent } from './AbstractAgent';
2
+ /**
3
+ * Antigravity agent adapter.
4
+ */
5
+ export declare class AntigravityAgent extends AbstractAgent {
6
+ getIdentifier(): string;
7
+ getName(): string;
8
+ getDefaultOutputPath(projectRoot: string): string;
9
+ supportsNativeSkills(): boolean;
10
+ }
@@ -0,0 +1,13 @@
1
+ import { IAgent, IAgentConfig } from './IAgent';
2
+ /**
3
+ * AugmentCode agent adapter.
4
+ * Generates ruler_augment_instructions.md configuration file and updates VSCode settings.json with MCP server configuration.
5
+ */
6
+ export declare class AugmentCodeAgent implements IAgent {
7
+ getIdentifier(): string;
8
+ getName(): string;
9
+ applyRulerConfig(concatenatedRules: string, projectRoot: string, _rulerMcpJson: Record<string, unknown> | null, agentConfig?: IAgentConfig, backup?: boolean): Promise<void>;
10
+ getDefaultOutputPath(projectRoot: string): string;
11
+ supportsMcpStdio(): boolean;
12
+ supportsMcpRemote(): boolean;
13
+ }
@@ -49,10 +49,11 @@ class AugmentCodeAgent {
49
49
  }
50
50
  async applyRulerConfig(concatenatedRules, projectRoot, _rulerMcpJson, agentConfig, backup = true) {
51
51
  const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
52
+ const absolutePath = path.resolve(projectRoot, output);
52
53
  if (backup) {
53
- await (0, FileSystemUtils_1.backupFile)(output);
54
+ await (0, FileSystemUtils_1.backupFile)(absolutePath, projectRoot);
54
55
  }
55
- await (0, FileSystemUtils_1.writeGeneratedFile)(output, concatenatedRules);
56
+ await (0, FileSystemUtils_1.writeGeneratedFile)(absolutePath, concatenatedRules, projectRoot);
56
57
  // AugmentCode does not support MCP servers
57
58
  // MCP configuration is ignored for this agent
58
59
  }
@@ -0,0 +1,13 @@
1
+ import { AbstractAgent } from './AbstractAgent';
2
+ /**
3
+ * Claude Code agent adapter.
4
+ */
5
+ export declare class ClaudeAgent extends AbstractAgent {
6
+ getIdentifier(): string;
7
+ getName(): string;
8
+ getDefaultOutputPath(projectRoot: string): string;
9
+ supportsMcpStdio(): boolean;
10
+ supportsMcpRemote(): boolean;
11
+ supportsNativeSkills(): boolean;
12
+ supportsNativeSubagents(): boolean;
13
+ }
@@ -0,0 +1,9 @@
1
+ import { AbstractAgent } from './AbstractAgent';
2
+ /**
3
+ * Cline agent adapter.
4
+ */
5
+ export declare class ClineAgent extends AbstractAgent {
6
+ getIdentifier(): string;
7
+ getName(): string;
8
+ getDefaultOutputPath(projectRoot: string): string;
9
+ }
@@ -0,0 +1,31 @@
1
+ import { IAgent, IAgentConfig } from './IAgent';
2
+ /**
3
+ * MCP server definition for Codex CLI (stdio uses command, remote uses url).
4
+ */
5
+ interface McpServer {
6
+ command?: string;
7
+ args?: string[];
8
+ env?: Record<string, string>;
9
+ url?: string;
10
+ headers?: Record<string, string>;
11
+ [key: string]: any;
12
+ }
13
+ interface RulerMcp {
14
+ mcpServers?: Record<string, McpServer>;
15
+ }
16
+ /**
17
+ * OpenAI Codex CLI agent adapter.
18
+ */
19
+ export declare class CodexCliAgent implements IAgent {
20
+ private agentsMdAgent;
21
+ getIdentifier(): string;
22
+ getName(): string;
23
+ applyRulerConfig(concatenatedRules: string, projectRoot: string, rulerMcpJson: RulerMcp | null, agentConfig?: IAgentConfig, backup?: boolean): Promise<void>;
24
+ getDefaultOutputPath(projectRoot: string): Record<string, string>;
25
+ getMcpServerKey(): string;
26
+ supportsMcpStdio(): boolean;
27
+ supportsMcpRemote(): boolean;
28
+ supportsNativeSkills(): boolean;
29
+ supportsNativeSubagents(): boolean;
30
+ }
31
+ export {};
@@ -128,7 +128,7 @@ class CodexCliAgent {
128
128
  const finalConfig = { ...updatedConfig };
129
129
  // @iarna/toml should handle the formatting properly
130
130
  const tomlContent = (0, toml_1.stringify)(finalConfig);
131
- await (0, FileSystemUtils_1.writeGeneratedFile)(configPath, tomlContent);
131
+ await (0, FileSystemUtils_1.writeGeneratedFile)(configPath, tomlContent, projectRoot);
132
132
  }
133
133
  }
134
134
  getDefaultOutputPath(projectRoot) {
@@ -0,0 +1,20 @@
1
+ import { IAgent, IAgentConfig } from './IAgent';
2
+ /**
3
+ * GitHub Copilot agent adapter.
4
+ * Writes to AGENTS.md for both web-based GitHub Copilot and VS Code extension.
5
+ */
6
+ export declare class CopilotAgent implements IAgent {
7
+ private agentsMdAgent;
8
+ getIdentifier(): string;
9
+ getName(): string;
10
+ /**
11
+ * Returns the default output path for AGENTS.md.
12
+ */
13
+ getDefaultOutputPath(projectRoot: string): string;
14
+ applyRulerConfig(concatenatedRules: string, projectRoot: string, rulerMcpJson: Record<string, unknown> | null, agentConfig?: IAgentConfig, backup?: boolean): Promise<void>;
15
+ getMcpServerKey(): string;
16
+ supportsMcpStdio(): boolean;
17
+ supportsMcpRemote(): boolean;
18
+ supportsNativeSkills(): boolean;
19
+ supportsNativeSubagents(): boolean;
20
+ }
@@ -0,0 +1,14 @@
1
+ import { IAgent, IAgentConfig } from './IAgent';
2
+ export declare class CrushAgent implements IAgent {
3
+ getIdentifier(): string;
4
+ getName(): string;
5
+ getDefaultOutputPath(projectRoot: string): Record<string, string>;
6
+ /**
7
+ * Transform MCP server types for Crush compatibility.
8
+ * Crush expects "http" for HTTP servers and "sse" for SSE servers, not "remote".
9
+ */
10
+ private transformMcpServersForCrush;
11
+ applyRulerConfig(concatenatedRules: string, projectRoot: string, rulerMcpJson: Record<string, unknown> | null, agentConfig?: IAgentConfig, backup?: boolean): Promise<void>;
12
+ supportsMcpStdio(): boolean;
13
+ supportsMcpRemote(): boolean;
14
+ }