@intellectronica/ruler 0.1.3 → 0.2.0
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 +334 -135
- package/dist/agents/AiderAgent.js +3 -0
- package/dist/agents/ClaudeAgent.js +3 -0
- package/dist/agents/ClineAgent.js +3 -0
- package/dist/agents/CodexCliAgent.js +3 -0
- package/dist/agents/CopilotAgent.js +3 -0
- package/dist/agents/CursorAgent.js +3 -0
- package/dist/agents/WindsurfAgent.js +3 -0
- package/dist/cli/commands.js +26 -11
- package/dist/constants.js +17 -0
- package/dist/core/ConfigLoader.js +40 -0
- package/dist/core/GitignoreUtils.js +18 -1
- package/dist/lib.js +57 -20
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,207 +1,396 @@
|
|
|
1
|
-
|
|
2
|
-
> - Please test this version with caution in your own setup
|
|
3
|
-
> - File issues at https://github.com/intellectronica/ruler/issues
|
|
1
|
+
# Ruler: Centralise Your AI Coding Assistant Instructions
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
[](https://github.com/intellectronica/ruler/actions/workflows/ci.yml)
|
|
4
|
+
[](https://badge.fury.io/js/%40intellectronica%2Fruler)
|
|
5
|
+

|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- **GitHub**: [intellectronica/ruler](https://github.com/intellectronica/ruler)
|
|
8
|
+
- **NPM**: [@intellectronica/ruler](https://www.npmjs.com/package/@intellectronica/ruler)
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
> **Beta Research Preview**
|
|
13
|
+
> - Please test this version carefully in your environment
|
|
14
|
+
> - Report issues at https://github.com/intellectronica/ruler/issues
|
|
15
|
+
|
|
16
|
+
## Why Ruler?
|
|
17
|
+
|
|
18
|
+
Managing instructions across multiple AI coding tools becomes complex as your team grows. Different agents (GitHub Copilot, Claude, Cursor, Aider, etc.) require their own configuration files, leading to:
|
|
19
|
+
|
|
20
|
+
- **Inconsistent guidance** across AI tools
|
|
21
|
+
- **Duplicated effort** maintaining multiple config files
|
|
22
|
+
- **Context drift** as project requirements evolve
|
|
23
|
+
- **Onboarding friction** for new AI tools
|
|
24
|
+
|
|
25
|
+
Ruler solves this by providing a **single source of truth** for all your AI agent instructions, automatically distributing them to the right configuration files.
|
|
26
|
+
|
|
27
|
+
## Core Features
|
|
28
|
+
|
|
29
|
+
- **Centralised Rule Management**: Store all AI instructions in a dedicated `.ruler/` directory using Markdown files
|
|
30
|
+
- **Automatic Distribution**: Ruler applies these rules to configuration files of supported AI agents
|
|
31
|
+
- **Targeted Agent Configuration**: Fine-tune which agents are affected and their specific output paths via `ruler.toml`
|
|
32
|
+
- **MCP Server Propagation**: Manage and distribute Model Context Protocol (MCP) server settings
|
|
33
|
+
- **`.gitignore` Automation**: Keeps generated agent config files out of version control automatically
|
|
34
|
+
- **Simple CLI**: Easy-to-use commands for initialising and applying configurations
|
|
35
|
+
|
|
36
|
+
## Supported AI Agents
|
|
37
|
+
|
|
38
|
+
| Agent | File(s) Created/Updated |
|
|
39
|
+
| ---------------------- | ----------------------------------------------------------- |
|
|
40
|
+
| GitHub Copilot | `.github/copilot-instructions.md` |
|
|
41
|
+
| Claude Code | `CLAUDE.md` |
|
|
42
|
+
| OpenAI Codex CLI | `AGENTS.md` |
|
|
43
|
+
| Cursor | `.cursor/rules/ruler_cursor_instructions.md` |
|
|
44
|
+
| Windsurf | `.windsurf/rules/ruler_windsurf_instructions.md` |
|
|
45
|
+
| Cline | `.clinerules` |
|
|
46
|
+
| Aider | `ruler_aider_instructions.md` and `.aider.conf.yml` |
|
|
10
47
|
|
|
11
|
-
|
|
12
|
-
- Distribute rules to supported agents (GitHub Copilot, Claude Code, OpenAI Codex CLI, Cursor, Windsurf, Cline, Aider)
|
|
13
|
-
- Extensible architecture: add new agent adapters easily
|
|
48
|
+
## Getting Started
|
|
14
49
|
|
|
15
|
-
|
|
50
|
+
### Prerequisites
|
|
16
51
|
|
|
17
|
-
|
|
52
|
+
Node.js 18.x or higher is required.
|
|
18
53
|
|
|
54
|
+
### Installation
|
|
55
|
+
|
|
56
|
+
**Global Installation (Recommended for CLI use):**
|
|
19
57
|
```bash
|
|
20
58
|
npm install -g @intellectronica/ruler
|
|
21
59
|
```
|
|
22
60
|
|
|
23
|
-
|
|
24
|
-
|
|
61
|
+
**Using `npx` (for one-off commands):**
|
|
25
62
|
```bash
|
|
26
63
|
npx @intellectronica/ruler apply
|
|
27
64
|
```
|
|
28
65
|
|
|
29
|
-
|
|
66
|
+
### Project Initialisation
|
|
30
67
|
|
|
31
|
-
|
|
68
|
+
1. Navigate to your project's root directory
|
|
69
|
+
2. Run `ruler init`
|
|
70
|
+
3. This creates:
|
|
71
|
+
- `.ruler/` directory
|
|
72
|
+
- `.ruler/instructions.md`: A starter Markdown file for your rules
|
|
73
|
+
- `.ruler/ruler.toml`: The main configuration file for Ruler
|
|
74
|
+
- `.ruler/mcp.json`: An example MCP server configuration
|
|
32
75
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
76
|
+
## Core Concepts
|
|
77
|
+
|
|
78
|
+
### The `.ruler/` Directory
|
|
79
|
+
|
|
80
|
+
This is your central hub for all AI agent instructions:
|
|
81
|
+
|
|
82
|
+
- **Rule Files (`*.md`)**: Discovered recursively from `.ruler/` and alphabetically concatenated
|
|
83
|
+
- **Concatenation Marker**: Each file's content is prepended with `--- Source: <relative_path_to_md_file> ---` for traceability
|
|
84
|
+
- **`ruler.toml`**: Master configuration for Ruler's behavior, agent selection, and output paths
|
|
85
|
+
- **`mcp.json`**: Shared MCP server settings
|
|
86
|
+
|
|
87
|
+
### Best Practices for Rule Files
|
|
88
|
+
|
|
89
|
+
**Granularity**: Break down complex instructions into focused `.md` files:
|
|
90
|
+
- `coding_style.md`
|
|
91
|
+
- `api_conventions.md`
|
|
92
|
+
- `project_architecture.md`
|
|
93
|
+
- `security_guidelines.md`
|
|
94
|
+
|
|
95
|
+
**Example rule file (`.ruler/python_guidelines.md`):**
|
|
96
|
+
```markdown
|
|
97
|
+
# Python Project Guidelines
|
|
98
|
+
|
|
99
|
+
## General Style
|
|
100
|
+
- Follow PEP 8 for all Python code
|
|
101
|
+
- Use type hints for all function signatures and complex variables
|
|
102
|
+
- Keep functions short and focused on a single task
|
|
103
|
+
|
|
104
|
+
## Error Handling
|
|
105
|
+
- Use specific exception types rather than generic `Exception`
|
|
106
|
+
- Log errors effectively with context
|
|
107
|
+
|
|
108
|
+
## Security
|
|
109
|
+
- Always validate and sanitize user input
|
|
110
|
+
- Be mindful of potential injection vulnerabilities
|
|
37
111
|
```
|
|
38
112
|
|
|
39
|
-
|
|
113
|
+
## Usage: The `apply` Command
|
|
40
114
|
|
|
115
|
+
### Primary Command
|
|
41
116
|
```bash
|
|
42
|
-
ruler apply [
|
|
117
|
+
ruler apply [options]
|
|
43
118
|
```
|
|
44
119
|
|
|
120
|
+
### Options
|
|
45
121
|
|
|
46
|
-
|
|
122
|
+
| Option | Description |
|
|
123
|
+
|--------|-------------|
|
|
124
|
+
| `--project-root <path>` | Path to your project's root (default: current directory) |
|
|
125
|
+
| `--agents <agent1,agent2,...>` | Comma-separated list of agent names to target |
|
|
126
|
+
| `--config <path>` | Path to a custom `ruler.toml` configuration file |
|
|
127
|
+
| `--mcp` / `--with-mcp` | Enable applying MCP server configurations (default: true) |
|
|
128
|
+
| `--no-mcp` | Disable applying MCP server configurations |
|
|
129
|
+
| `--mcp-overwrite` | Overwrite native MCP config entirely instead of merging |
|
|
130
|
+
| `--gitignore` | Enable automatic .gitignore updates (default: true) |
|
|
131
|
+
| `--no-gitignore` | Disable automatic .gitignore updates |
|
|
132
|
+
| `--verbose` / `-v` | Display detailed output during execution |
|
|
47
133
|
|
|
134
|
+
### Common Examples
|
|
135
|
+
|
|
136
|
+
**Apply rules to all configured agents:**
|
|
48
137
|
```bash
|
|
49
|
-
ruler
|
|
138
|
+
ruler apply
|
|
50
139
|
```
|
|
51
140
|
|
|
52
|
-
|
|
141
|
+
**Apply rules only to GitHub Copilot and Claude:**
|
|
142
|
+
```bash
|
|
143
|
+
ruler apply --agents copilot,claude
|
|
144
|
+
```
|
|
53
145
|
|
|
54
|
-
|
|
146
|
+
**Use a specific configuration file:**
|
|
147
|
+
```bash
|
|
148
|
+
ruler apply --config ./team-configs/ruler.frontend.toml
|
|
149
|
+
```
|
|
55
150
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
| OpenAI Codex CLI | `AGENTS.md` |
|
|
61
|
-
| Cursor | `.cursor/rules/ruler_cursor_instructions.md` |
|
|
62
|
-
| Windsurf | `.windsurf/rules/ruler_windsurf_instructions.md` |
|
|
63
|
-
| Cline | `.clinerules` |
|
|
64
|
-
| Aider | `ruler_aider_instructions.md` <br>and updates `.aider.conf.yml` |
|
|
151
|
+
**Apply rules with verbose output:**
|
|
152
|
+
```bash
|
|
153
|
+
ruler apply --verbose
|
|
154
|
+
```
|
|
65
155
|
|
|
66
|
-
|
|
156
|
+
**Apply rules but skip MCP and .gitignore updates:**
|
|
157
|
+
```bash
|
|
158
|
+
ruler apply --no-mcp --no-gitignore
|
|
159
|
+
```
|
|
67
160
|
|
|
68
|
-
|
|
161
|
+
## Configuration (`ruler.toml`) in Detail
|
|
69
162
|
|
|
70
|
-
###
|
|
163
|
+
### Location
|
|
164
|
+
Defaults to `.ruler/ruler.toml` in the project root. Override with `--config` CLI option.
|
|
71
165
|
|
|
166
|
+
### Complete Example
|
|
72
167
|
```toml
|
|
73
|
-
#
|
|
74
|
-
#
|
|
168
|
+
# Default agents to run when --agents is not specified
|
|
169
|
+
# Uses case-insensitive substring matching
|
|
170
|
+
default_agents = ["copilot", "claude", "aider"]
|
|
75
171
|
|
|
76
|
-
|
|
172
|
+
# --- Global MCP Server Configuration ---
|
|
173
|
+
[mcp]
|
|
174
|
+
# Enable/disable MCP propagation globally (default: true)
|
|
175
|
+
enabled = true
|
|
176
|
+
# Global merge strategy: 'merge' or 'overwrite' (default: 'merge')
|
|
177
|
+
merge_strategy = "merge"
|
|
178
|
+
|
|
179
|
+
# --- Global .gitignore Configuration ---
|
|
180
|
+
[gitignore]
|
|
181
|
+
# Enable/disable automatic .gitignore updates (default: true)
|
|
182
|
+
enabled = true
|
|
183
|
+
|
|
184
|
+
# --- Agent-Specific Configurations ---
|
|
185
|
+
[agents.copilot]
|
|
77
186
|
enabled = true
|
|
78
187
|
output_path = ".github/copilot-instructions.md"
|
|
79
188
|
|
|
80
|
-
[agents.
|
|
189
|
+
[agents.claude]
|
|
190
|
+
enabled = true
|
|
191
|
+
output_path = "CLAUDE.md"
|
|
192
|
+
|
|
193
|
+
[agents.aider]
|
|
81
194
|
enabled = true
|
|
82
|
-
|
|
195
|
+
output_path_instructions = "ruler_aider_instructions.md"
|
|
196
|
+
output_path_config = ".aider.conf.yml"
|
|
83
197
|
|
|
84
|
-
|
|
198
|
+
# Agent-specific MCP configuration
|
|
199
|
+
[agents.cursor.mcp]
|
|
200
|
+
enabled = true
|
|
201
|
+
merge_strategy = "merge"
|
|
202
|
+
|
|
203
|
+
# Disable specific agents
|
|
204
|
+
[agents.windsurf]
|
|
85
205
|
enabled = false
|
|
86
|
-
# output_path_instructions = "ruler_aider_instructions.md"
|
|
87
|
-
# output_path_config = ".aider.conf.yml"
|
|
88
206
|
```
|
|
89
207
|
|
|
90
|
-
|
|
91
|
-
- `[agents.<AgentName>]`: per-agent settings:
|
|
92
|
-
- `enabled` (boolean): enable or disable this agent.
|
|
93
|
-
- `output_path` (string): custom path for agents that produce a single file.
|
|
94
|
-
- `output_path_instructions`/`output_path_config`: custom paths for Aider's instruction and config files.
|
|
95
|
-
|
|
96
|
-
### Precedence
|
|
208
|
+
### Configuration Precedence
|
|
97
209
|
|
|
98
|
-
1. CLI `--agents
|
|
99
|
-
2.
|
|
100
|
-
3.
|
|
210
|
+
1. **CLI flags** (e.g., `--agents`, `--no-mcp`, `--mcp-overwrite`, `--no-gitignore`)
|
|
211
|
+
2. **Settings in `ruler.toml`** (`default_agents`, specific agent settings, global sections)
|
|
212
|
+
3. **Ruler's built-in defaults** (all agents enabled, standard output paths, MCP enabled with 'merge')
|
|
101
213
|
|
|
102
|
-
## MCP
|
|
214
|
+
## MCP (Model Context Protocol) Server Configuration
|
|
103
215
|
|
|
104
|
-
|
|
216
|
+
MCP provides broader context to AI models through server configurations. Ruler can manage and distribute these settings across compatible agents.
|
|
105
217
|
|
|
106
218
|
### `.ruler/mcp.json`
|
|
107
|
-
|
|
108
|
-
Place your MCP servers config in a file at `.ruler/mcp.json`:
|
|
109
|
-
|
|
219
|
+
Define your project's MCP servers:
|
|
110
220
|
```json
|
|
111
221
|
{
|
|
112
222
|
"mcpServers": {
|
|
113
|
-
"
|
|
114
|
-
"
|
|
223
|
+
"filesystem": {
|
|
224
|
+
"command": "npx",
|
|
225
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"]
|
|
226
|
+
},
|
|
227
|
+
"git": {
|
|
228
|
+
"command": "npx",
|
|
229
|
+
"args": ["-y", "@modelcontextprotocol/server-git", "--repository", "."]
|
|
115
230
|
}
|
|
116
231
|
}
|
|
117
232
|
}
|
|
118
233
|
```
|
|
119
234
|
|
|
120
|
-
|
|
235
|
+
Ruler uses this file with the `merge` (default) or `overwrite` strategy, controlled by `ruler.toml` or CLI flags.
|
|
121
236
|
|
|
122
|
-
|
|
123
|
-
|-------------------|--------------------------------------------------------------|
|
|
124
|
-
| `--with-mcp` | Enable writing MCP configs for all agents (default) |
|
|
125
|
-
| `--no-mcp` | Disable writing MCP configs |
|
|
126
|
-
| `--mcp-overwrite` | Overwrite native MCP configs instead of merging |
|
|
237
|
+
## `.gitignore` Integration
|
|
127
238
|
|
|
128
|
-
|
|
239
|
+
Ruler automatically manages your `.gitignore` file to keep generated agent configuration files out of version control.
|
|
129
240
|
|
|
130
|
-
|
|
241
|
+
### How it Works
|
|
242
|
+
- Creates or updates `.gitignore` in your project root
|
|
243
|
+
- Adds paths to a managed block marked with `# START Ruler Generated Files` and `# END Ruler Generated Files`
|
|
244
|
+
- Preserves existing content outside this block
|
|
245
|
+
- Sorts paths alphabetically and uses relative POSIX-style paths
|
|
131
246
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
247
|
+
### Example `.gitignore` Section
|
|
248
|
+
```gitignore
|
|
249
|
+
# Your existing rules
|
|
250
|
+
node_modules/
|
|
251
|
+
*.log
|
|
136
252
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
253
|
+
# START Ruler Generated Files
|
|
254
|
+
.aider.conf.yml
|
|
255
|
+
.clinerules
|
|
256
|
+
.cursor/rules/ruler_cursor_instructions.md
|
|
257
|
+
.github/copilot-instructions.md
|
|
258
|
+
.windsurf/rules/ruler_windsurf_instructions.md
|
|
259
|
+
AGENTS.md
|
|
260
|
+
CLAUDE.md
|
|
261
|
+
ruler_aider_instructions.md
|
|
262
|
+
# END Ruler Generated Files
|
|
263
|
+
|
|
264
|
+
dist/
|
|
140
265
|
```
|
|
141
266
|
|
|
142
|
-
|
|
267
|
+
### Control Options
|
|
268
|
+
- **CLI flags**: `--gitignore` or `--no-gitignore`
|
|
269
|
+
- **Configuration**: `[gitignore].enabled` in `ruler.toml`
|
|
270
|
+
- **Default**: enabled
|
|
143
271
|
|
|
144
|
-
|
|
272
|
+
## Practical Usage Scenarios
|
|
145
273
|
|
|
146
|
-
###
|
|
274
|
+
### Scenario 1: Getting Started Quickly
|
|
275
|
+
```bash
|
|
276
|
+
# Initialize Ruler in your project
|
|
277
|
+
cd your-project
|
|
278
|
+
ruler init
|
|
147
279
|
|
|
148
|
-
|
|
149
|
-
-
|
|
150
|
-
-
|
|
151
|
-
- Preserve any existing `.gitignore` content outside the managed block
|
|
152
|
-
- Sort paths alphabetically within the Ruler block
|
|
153
|
-
- Use relative POSIX-style paths (forward slashes)
|
|
280
|
+
# Edit the generated files
|
|
281
|
+
# - Add your coding guidelines to .ruler/instructions.md
|
|
282
|
+
# - Customize .ruler/ruler.toml if needed
|
|
154
283
|
|
|
155
|
-
|
|
284
|
+
# Apply rules to all AI agents
|
|
285
|
+
ruler apply
|
|
286
|
+
```
|
|
156
287
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
288
|
+
### Scenario 2: Team Standardization
|
|
289
|
+
1. Create `.ruler/coding_standards.md`, `.ruler/api_usage.md`
|
|
290
|
+
2. Commit the `.ruler` directory to your repository
|
|
291
|
+
3. Team members pull changes and run `ruler apply` to update their local AI agent configurations
|
|
161
292
|
|
|
162
|
-
###
|
|
293
|
+
### Scenario 3: Project-Specific Context for AI
|
|
294
|
+
1. Detail your project's architecture in `.ruler/project_overview.md`
|
|
295
|
+
2. Describe primary data structures in `.ruler/data_models.md`
|
|
296
|
+
3. Run `ruler apply` to help AI tools provide more relevant suggestions
|
|
163
297
|
|
|
164
|
-
|
|
298
|
+
### Integration with NPM Scripts
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"scripts": {
|
|
302
|
+
"ruler:apply": "ruler apply",
|
|
303
|
+
"dev": "npm run ruler:apply && your_dev_command",
|
|
304
|
+
"precommit": "npm run ruler:apply"
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
```
|
|
165
308
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
309
|
+
### Integration with GitHub Actions
|
|
310
|
+
```yaml
|
|
311
|
+
# .github/workflows/ruler-check.yml
|
|
312
|
+
name: Check Ruler Configuration
|
|
313
|
+
on:
|
|
314
|
+
pull_request:
|
|
315
|
+
paths: ['.ruler/**']
|
|
316
|
+
|
|
317
|
+
jobs:
|
|
318
|
+
check-ruler:
|
|
319
|
+
runs-on: ubuntu-latest
|
|
320
|
+
steps:
|
|
321
|
+
- uses: actions/checkout@v4
|
|
322
|
+
- uses: actions/setup-node@v4
|
|
323
|
+
with:
|
|
324
|
+
node-version: '18'
|
|
325
|
+
cache: 'npm'
|
|
326
|
+
|
|
327
|
+
- name: Install Ruler
|
|
328
|
+
run: npm install -g @intellectronica/ruler
|
|
329
|
+
|
|
330
|
+
- name: Apply Ruler configuration
|
|
331
|
+
run: ruler apply --no-gitignore
|
|
332
|
+
|
|
333
|
+
- name: Check for uncommitted changes
|
|
334
|
+
run: |
|
|
335
|
+
if [[ -n $(git status --porcelain) ]]; then
|
|
336
|
+
echo "::error::Ruler configuration is out of sync!"
|
|
337
|
+
echo "Please run 'ruler apply' locally and commit the changes."
|
|
338
|
+
exit 1
|
|
339
|
+
fi
|
|
169
340
|
```
|
|
170
341
|
|
|
171
|
-
|
|
342
|
+
## Troubleshooting
|
|
172
343
|
|
|
173
|
-
|
|
344
|
+
### Common Issues
|
|
174
345
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
346
|
+
**"Cannot find module" errors:**
|
|
347
|
+
- Ensure Ruler is installed globally: `npm install -g @intellectronica/ruler`
|
|
348
|
+
- Or use `npx @intellectronica/ruler`
|
|
178
349
|
|
|
179
|
-
|
|
350
|
+
**Permission denied errors:**
|
|
351
|
+
- On Unix systems, you may need `sudo` for global installation
|
|
180
352
|
|
|
181
|
-
|
|
353
|
+
**Agent files not updating:**
|
|
354
|
+
- Check if the agent is enabled in `ruler.toml`
|
|
355
|
+
- Verify agent isn't excluded by `--agents` flag
|
|
356
|
+
- Use `--verbose` to see detailed execution logs
|
|
182
357
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
358
|
+
**Configuration validation errors:**
|
|
359
|
+
- Ruler now validates `ruler.toml` format and will show specific error details
|
|
360
|
+
- Check that all configuration values match the expected types and formats
|
|
186
361
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
.github/copilot-instructions.md
|
|
192
|
-
.windsurf/rules/ruler_windsurf_instructions.md
|
|
193
|
-
AGENTS.md
|
|
194
|
-
CLAUDE.md
|
|
195
|
-
ruler_aider_instructions.md
|
|
196
|
-
# END Ruler Generated Files
|
|
197
|
-
|
|
198
|
-
dist/
|
|
362
|
+
### Debug Mode
|
|
363
|
+
Use `--verbose` flag to see detailed execution logs:
|
|
364
|
+
```bash
|
|
365
|
+
ruler apply --verbose
|
|
199
366
|
```
|
|
200
367
|
|
|
201
|
-
|
|
368
|
+
This shows:
|
|
369
|
+
- Configuration loading details
|
|
370
|
+
- Agent selection logic
|
|
371
|
+
- File processing information
|
|
372
|
+
- MCP configuration steps
|
|
373
|
+
|
|
374
|
+
## FAQ
|
|
375
|
+
|
|
376
|
+
**Q: Can I use different rules for different agents?**
|
|
377
|
+
A: Currently, all agents receive the same concatenated rules. For agent-specific instructions, include sections in your rule files like "## GitHub Copilot Specific" or "## Aider Configuration".
|
|
202
378
|
|
|
203
|
-
|
|
379
|
+
**Q: How do I temporarily disable Ruler for an agent?**
|
|
380
|
+
A: Set `enabled = false` in `ruler.toml` under `[agents.agentname]`, or use `--agents` flag to specify only the agents you want.
|
|
204
381
|
|
|
382
|
+
**Q: What happens to my existing agent configuration files?**
|
|
383
|
+
A: Ruler creates backups with `.bak` extension before overwriting any existing files.
|
|
384
|
+
|
|
385
|
+
**Q: Can I run Ruler in CI/CD pipelines?**
|
|
386
|
+
A: Yes! Use `ruler apply --no-gitignore` in CI to avoid modifying `.gitignore`. See the GitHub Actions example above.
|
|
387
|
+
|
|
388
|
+
**Q: How do I migrate from version 0.1.x to 0.2.0?**
|
|
389
|
+
A: Version 0.2.0 is backward compatible. Your existing `.ruler/` directory and `ruler.toml` will continue to work. New features like verbose logging and improved error messages are opt-in.
|
|
390
|
+
|
|
391
|
+
## Development
|
|
392
|
+
|
|
393
|
+
### Setup
|
|
205
394
|
```bash
|
|
206
395
|
git clone https://github.com/intellectronica/ruler.git
|
|
207
396
|
cd ruler
|
|
@@ -209,28 +398,39 @@ npm install
|
|
|
209
398
|
npm run build
|
|
210
399
|
```
|
|
211
400
|
|
|
212
|
-
|
|
213
|
-
|
|
401
|
+
### Testing
|
|
214
402
|
```bash
|
|
215
|
-
|
|
216
|
-
npm
|
|
217
|
-
```
|
|
403
|
+
# Run all tests
|
|
404
|
+
npm test
|
|
218
405
|
|
|
219
|
-
Run tests
|
|
406
|
+
# Run tests with coverage
|
|
407
|
+
npm run test:coverage
|
|
220
408
|
|
|
221
|
-
|
|
222
|
-
npm test
|
|
409
|
+
# Run tests in watch mode
|
|
410
|
+
npm run test:watch
|
|
223
411
|
```
|
|
224
412
|
|
|
225
|
-
|
|
226
|
-
|
|
413
|
+
### Code Quality
|
|
227
414
|
```bash
|
|
228
|
-
|
|
415
|
+
# Run linting
|
|
416
|
+
npm run lint
|
|
417
|
+
|
|
418
|
+
# Run formatting
|
|
419
|
+
npm run format
|
|
229
420
|
```
|
|
230
421
|
|
|
231
422
|
## Contributing
|
|
232
423
|
|
|
233
|
-
Contributions are welcome! Please
|
|
424
|
+
Contributions are welcome! Please:
|
|
425
|
+
|
|
426
|
+
1. Fork the repository
|
|
427
|
+
2. Create a feature branch
|
|
428
|
+
3. Make your changes
|
|
429
|
+
4. Add tests for new functionality
|
|
430
|
+
5. Ensure all tests pass
|
|
431
|
+
6. Submit a pull request
|
|
432
|
+
|
|
433
|
+
For bugs and feature requests, please [open an issue](https://github.com/intellectronica/ruler/issues).
|
|
234
434
|
|
|
235
435
|
## License
|
|
236
436
|
|
|
@@ -238,6 +438,5 @@ MIT
|
|
|
238
438
|
|
|
239
439
|
---
|
|
240
440
|
|
|
241
|
-
© Eleanor Berger
|
|
242
|
-
|
|
243
|
-
[ai.intellectronica.net](https://ai.intellectronica.net/)
|
|
441
|
+
© Eleanor Berger
|
|
442
|
+
[ai.intellectronica.net](https://ai.intellectronica.net/)
|
package/dist/cli/commands.js
CHANGED
|
@@ -42,6 +42,7 @@ const helpers_1 = require("yargs/helpers");
|
|
|
42
42
|
const lib_1 = require("../lib");
|
|
43
43
|
const path = __importStar(require("path"));
|
|
44
44
|
const fs_1 = require("fs");
|
|
45
|
+
const constants_1 = require("../constants");
|
|
45
46
|
/**
|
|
46
47
|
* Sets up and parses CLI commands.
|
|
47
48
|
*/
|
|
@@ -57,7 +58,7 @@ function run() {
|
|
|
57
58
|
});
|
|
58
59
|
y.option('agents', {
|
|
59
60
|
type: 'string',
|
|
60
|
-
description: 'Comma-separated list of agent
|
|
61
|
+
description: 'Comma-separated list of agent identifiers: copilot, claude, codex, cursor, windsurf, cline, aider',
|
|
61
62
|
});
|
|
62
63
|
y.option('config', {
|
|
63
64
|
type: 'string',
|
|
@@ -78,6 +79,17 @@ function run() {
|
|
|
78
79
|
type: 'boolean',
|
|
79
80
|
description: 'Enable/disable automatic .gitignore updates (default: enabled)',
|
|
80
81
|
});
|
|
82
|
+
y.option('verbose', {
|
|
83
|
+
type: 'boolean',
|
|
84
|
+
description: 'Enable verbose logging',
|
|
85
|
+
default: false,
|
|
86
|
+
});
|
|
87
|
+
y.alias('verbose', 'v');
|
|
88
|
+
y.option('dry-run', {
|
|
89
|
+
type: 'boolean',
|
|
90
|
+
description: 'Preview changes without writing files',
|
|
91
|
+
default: false,
|
|
92
|
+
});
|
|
81
93
|
}, async (argv) => {
|
|
82
94
|
const projectRoot = argv['project-root'];
|
|
83
95
|
const agents = argv.agents
|
|
@@ -88,6 +100,8 @@ function run() {
|
|
|
88
100
|
const mcpStrategy = argv['mcp-overwrite']
|
|
89
101
|
? 'overwrite'
|
|
90
102
|
: undefined;
|
|
103
|
+
const verbose = argv.verbose;
|
|
104
|
+
const dryRun = argv['dry-run'];
|
|
91
105
|
// Determine gitignore preference: CLI > TOML > Default (enabled)
|
|
92
106
|
// yargs handles --no-gitignore by setting gitignore to false
|
|
93
107
|
let gitignorePreference;
|
|
@@ -98,12 +112,12 @@ function run() {
|
|
|
98
112
|
gitignorePreference = undefined; // Let TOML/default decide
|
|
99
113
|
}
|
|
100
114
|
try {
|
|
101
|
-
await (0, lib_1.applyAllAgentConfigs)(projectRoot, agents, configPath, mcpEnabled, mcpStrategy, gitignorePreference);
|
|
115
|
+
await (0, lib_1.applyAllAgentConfigs)(projectRoot, agents, configPath, mcpEnabled, mcpStrategy, gitignorePreference, verbose, dryRun);
|
|
102
116
|
console.log('Ruler apply completed successfully.');
|
|
103
117
|
}
|
|
104
118
|
catch (err) {
|
|
105
119
|
const message = err instanceof Error ? err.message : String(err);
|
|
106
|
-
console.error(
|
|
120
|
+
console.error(`${constants_1.ERROR_PREFIX} ${message}`);
|
|
107
121
|
process.exit(1);
|
|
108
122
|
}
|
|
109
123
|
})
|
|
@@ -141,36 +155,37 @@ and apply them to your configured AI coding agents.
|
|
|
141
155
|
|
|
142
156
|
# To specify which agents are active by default when --agents is not used,
|
|
143
157
|
# uncomment and populate the following line. If omitted, all agents are active.
|
|
144
|
-
# default_agents = ["
|
|
158
|
+
# default_agents = ["copilot", "claude"]
|
|
145
159
|
|
|
146
160
|
# --- Agent Specific Configurations ---
|
|
147
161
|
# You can enable/disable agents and override their default output paths here.
|
|
162
|
+
# Use lowercase agent identifiers: copilot, claude, codex, cursor, windsurf, cline, aider
|
|
148
163
|
|
|
149
|
-
# [agents.
|
|
164
|
+
# [agents.copilot]
|
|
150
165
|
# enabled = true
|
|
151
166
|
# output_path = ".github/copilot-instructions.md"
|
|
152
167
|
|
|
153
|
-
# [agents.
|
|
168
|
+
# [agents.claude]
|
|
154
169
|
# enabled = true
|
|
155
170
|
# output_path = "CLAUDE.md"
|
|
156
171
|
|
|
157
|
-
# [agents.
|
|
172
|
+
# [agents.codex]
|
|
158
173
|
# enabled = true
|
|
159
174
|
# output_path = "AGENTS.md"
|
|
160
175
|
|
|
161
|
-
# [agents.
|
|
176
|
+
# [agents.cursor]
|
|
162
177
|
# enabled = true
|
|
163
178
|
# output_path = ".cursor/rules/ruler_cursor_instructions.md"
|
|
164
179
|
|
|
165
|
-
# [agents.
|
|
180
|
+
# [agents.windsurf]
|
|
166
181
|
# enabled = true
|
|
167
182
|
# output_path = ".windsurf/rules/ruler_windsurf_instructions.md"
|
|
168
183
|
|
|
169
|
-
# [agents.
|
|
184
|
+
# [agents.cline]
|
|
170
185
|
# enabled = true
|
|
171
186
|
# output_path = ".clinerules"
|
|
172
187
|
|
|
173
|
-
# [agents.
|
|
188
|
+
# [agents.aider]
|
|
174
189
|
# enabled = true
|
|
175
190
|
# output_path_instructions = "ruler_aider_instructions.md"
|
|
176
191
|
# output_path_config = ".aider.conf.yml"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ERROR_PREFIX = void 0;
|
|
4
|
+
exports.createRulerError = createRulerError;
|
|
5
|
+
exports.logVerbose = logVerbose;
|
|
6
|
+
exports.ERROR_PREFIX = '[RulerError]';
|
|
7
|
+
function createRulerError(message, context) {
|
|
8
|
+
const fullMessage = context
|
|
9
|
+
? `${exports.ERROR_PREFIX} ${message} (Context: ${context})`
|
|
10
|
+
: `${exports.ERROR_PREFIX} ${message}`;
|
|
11
|
+
return new Error(fullMessage);
|
|
12
|
+
}
|
|
13
|
+
function logVerbose(message, isVerbose) {
|
|
14
|
+
if (isVerbose) {
|
|
15
|
+
console.log(`[ruler:verbose] ${message}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -40,6 +40,38 @@ exports.loadConfig = loadConfig;
|
|
|
40
40
|
const fs_1 = require("fs");
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
42
|
const toml_1 = __importDefault(require("toml"));
|
|
43
|
+
const zod_1 = require("zod");
|
|
44
|
+
const constants_1 = require("../constants");
|
|
45
|
+
const mcpConfigSchema = zod_1.z
|
|
46
|
+
.object({
|
|
47
|
+
enabled: zod_1.z.boolean().optional(),
|
|
48
|
+
merge_strategy: zod_1.z.enum(['merge', 'overwrite']).optional(),
|
|
49
|
+
})
|
|
50
|
+
.optional();
|
|
51
|
+
const agentConfigSchema = zod_1.z
|
|
52
|
+
.object({
|
|
53
|
+
enabled: zod_1.z.boolean().optional(),
|
|
54
|
+
output_path: zod_1.z.string().optional(),
|
|
55
|
+
output_path_instructions: zod_1.z.string().optional(),
|
|
56
|
+
output_path_config: zod_1.z.string().optional(),
|
|
57
|
+
mcp: mcpConfigSchema,
|
|
58
|
+
})
|
|
59
|
+
.optional();
|
|
60
|
+
const rulerConfigSchema = zod_1.z.object({
|
|
61
|
+
default_agents: zod_1.z.array(zod_1.z.string()).optional(),
|
|
62
|
+
agents: zod_1.z.record(zod_1.z.string(), agentConfigSchema).optional(),
|
|
63
|
+
mcp: zod_1.z
|
|
64
|
+
.object({
|
|
65
|
+
enabled: zod_1.z.boolean().optional(),
|
|
66
|
+
merge_strategy: zod_1.z.enum(['merge', 'overwrite']).optional(),
|
|
67
|
+
})
|
|
68
|
+
.optional(),
|
|
69
|
+
gitignore: zod_1.z
|
|
70
|
+
.object({
|
|
71
|
+
enabled: zod_1.z.boolean().optional(),
|
|
72
|
+
})
|
|
73
|
+
.optional(),
|
|
74
|
+
});
|
|
43
75
|
/**
|
|
44
76
|
* Loads and parses the ruler TOML configuration file, applying defaults.
|
|
45
77
|
* If the file is missing or invalid, returns empty/default config.
|
|
@@ -53,9 +85,17 @@ async function loadConfig(options) {
|
|
|
53
85
|
try {
|
|
54
86
|
const text = await fs_1.promises.readFile(configFile, 'utf8');
|
|
55
87
|
raw = text.trim() ? toml_1.default.parse(text) : {};
|
|
88
|
+
// Validate the configuration with zod
|
|
89
|
+
const validationResult = rulerConfigSchema.safeParse(raw);
|
|
90
|
+
if (!validationResult.success) {
|
|
91
|
+
throw (0, constants_1.createRulerError)('Invalid configuration file format', `File: ${configFile}, Errors: ${validationResult.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join(', ')}`);
|
|
92
|
+
}
|
|
56
93
|
}
|
|
57
94
|
catch (err) {
|
|
58
95
|
if (err instanceof Error && err.code !== 'ENOENT') {
|
|
96
|
+
if (err.message.includes('[RulerError]')) {
|
|
97
|
+
throw err; // Re-throw validation errors
|
|
98
|
+
}
|
|
59
99
|
console.warn(`[ruler] Warning: could not read config file at ${configFile}: ${err.message}`);
|
|
60
100
|
}
|
|
61
101
|
raw = {};
|
|
@@ -59,7 +59,24 @@ async function updateGitignore(projectRoot, paths) {
|
|
|
59
59
|
}
|
|
60
60
|
// Convert paths to relative POSIX format
|
|
61
61
|
const relativePaths = paths.map((p) => {
|
|
62
|
-
|
|
62
|
+
let relative;
|
|
63
|
+
if (path.isAbsolute(p)) {
|
|
64
|
+
relative = path.relative(projectRoot, p);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// Handle relative paths that might include the project root prefix
|
|
68
|
+
const normalizedProjectRoot = path.normalize(projectRoot);
|
|
69
|
+
const normalizedPath = path.normalize(p);
|
|
70
|
+
// Get the basename of the project root to match against path prefixes
|
|
71
|
+
const projectBasename = path.basename(normalizedProjectRoot);
|
|
72
|
+
// If the path starts with the project basename, remove it
|
|
73
|
+
if (normalizedPath.startsWith(projectBasename + path.sep)) {
|
|
74
|
+
relative = normalizedPath.substring(projectBasename.length + 1);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
relative = normalizedPath;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
63
80
|
return relative.replace(/\\/g, '/'); // Convert to POSIX format
|
|
64
81
|
});
|
|
65
82
|
// Get all existing paths from .gitignore (excluding Ruler block)
|
package/dist/lib.js
CHANGED
|
@@ -50,6 +50,7 @@ const AiderAgent_1 = require("./agents/AiderAgent");
|
|
|
50
50
|
const merge_1 = require("./mcp/merge");
|
|
51
51
|
const validate_1 = require("./mcp/validate");
|
|
52
52
|
const mcp_1 = require("./paths/mcp");
|
|
53
|
+
const constants_1 = require("./constants");
|
|
53
54
|
/**
|
|
54
55
|
* Gets all output paths for an agent, taking into account any config overrides.
|
|
55
56
|
*/
|
|
@@ -102,74 +103,98 @@ const agents = [
|
|
|
102
103
|
* @param projectRoot Root directory of the project
|
|
103
104
|
* @param includedAgents Optional list of agent name filters (case-insensitive substrings)
|
|
104
105
|
*/
|
|
105
|
-
async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cliMcpEnabled = true, cliMcpStrategy, cliGitignoreEnabled) {
|
|
106
|
+
async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cliMcpEnabled = true, cliMcpStrategy, cliGitignoreEnabled, verbose = false, dryRun = false) {
|
|
106
107
|
// Load configuration (default_agents, per-agent overrides, CLI filters)
|
|
108
|
+
(0, constants_1.logVerbose)(`Loading configuration from project root: ${projectRoot}`, verbose);
|
|
109
|
+
if (configPath) {
|
|
110
|
+
(0, constants_1.logVerbose)(`Using custom config path: ${configPath}`, verbose);
|
|
111
|
+
}
|
|
107
112
|
const config = await (0, ConfigLoader_1.loadConfig)({
|
|
108
113
|
projectRoot,
|
|
109
114
|
cliAgents: includedAgents,
|
|
110
115
|
configPath,
|
|
111
116
|
});
|
|
112
|
-
|
|
117
|
+
(0, constants_1.logVerbose)(`Loaded configuration with ${Object.keys(config.agentConfigs).length} agent configs`, verbose);
|
|
118
|
+
// Normalize per-agent config keys to agent identifiers (exact match or substring match)
|
|
113
119
|
const rawConfigs = config.agentConfigs;
|
|
114
120
|
const mappedConfigs = {};
|
|
115
121
|
for (const [key, cfg] of Object.entries(rawConfigs)) {
|
|
116
122
|
const lowerKey = key.toLowerCase();
|
|
117
123
|
for (const agent of agents) {
|
|
118
|
-
|
|
119
|
-
|
|
124
|
+
const identifier = agent.getIdentifier();
|
|
125
|
+
// Exact match with identifier or substring match with display name for backwards compatibility
|
|
126
|
+
if (identifier === lowerKey ||
|
|
127
|
+
agent.getName().toLowerCase().includes(lowerKey)) {
|
|
128
|
+
mappedConfigs[identifier] = cfg;
|
|
120
129
|
}
|
|
121
130
|
}
|
|
122
131
|
}
|
|
123
132
|
config.agentConfigs = mappedConfigs;
|
|
124
133
|
const rulerDir = await FileSystemUtils.findRulerDir(projectRoot);
|
|
125
134
|
if (!rulerDir) {
|
|
126
|
-
throw
|
|
135
|
+
throw (0, constants_1.createRulerError)(`.ruler directory not found`, `Searched from: ${projectRoot}`);
|
|
127
136
|
}
|
|
128
|
-
|
|
137
|
+
(0, constants_1.logVerbose)(`Found .ruler directory at: ${rulerDir}`, verbose);
|
|
129
138
|
const files = await FileSystemUtils.readMarkdownFiles(rulerDir);
|
|
139
|
+
(0, constants_1.logVerbose)(`Found ${files.length} markdown files in .ruler directory`, verbose);
|
|
130
140
|
const concatenated = (0, RuleProcessor_1.concatenateRules)(files);
|
|
141
|
+
(0, constants_1.logVerbose)(`Concatenated rules length: ${concatenated.length} characters`, verbose);
|
|
131
142
|
const mcpFile = path.join(rulerDir, 'mcp.json');
|
|
132
143
|
let rulerMcpJson = null;
|
|
133
144
|
try {
|
|
134
145
|
const raw = await fs_1.promises.readFile(mcpFile, 'utf8');
|
|
135
146
|
rulerMcpJson = JSON.parse(raw);
|
|
136
147
|
(0, validate_1.validateMcp)(rulerMcpJson);
|
|
148
|
+
(0, constants_1.logVerbose)(`Loaded MCP configuration from: ${mcpFile}`, verbose);
|
|
137
149
|
}
|
|
138
150
|
catch (err) {
|
|
139
151
|
if (err.code !== 'ENOENT') {
|
|
140
|
-
throw err;
|
|
152
|
+
throw (0, constants_1.createRulerError)(`Failed to load MCP configuration`, `File: ${mcpFile}, Error: ${err.message}`);
|
|
141
153
|
}
|
|
154
|
+
(0, constants_1.logVerbose)(`No MCP configuration found at: ${mcpFile}`, verbose);
|
|
142
155
|
}
|
|
143
156
|
// Determine which agents to run:
|
|
144
157
|
// CLI --agents > config.default_agents > per-agent.enabled flags > default all
|
|
145
158
|
let selected = agents;
|
|
146
159
|
if (config.cliAgents && config.cliAgents.length > 0) {
|
|
147
160
|
const filters = config.cliAgents.map((n) => n.toLowerCase());
|
|
148
|
-
selected = agents.filter((agent) => filters.some((f) => agent.
|
|
161
|
+
selected = agents.filter((agent) => filters.some((f) => agent.getIdentifier() === f ||
|
|
162
|
+
agent.getName().toLowerCase().includes(f)));
|
|
163
|
+
(0, constants_1.logVerbose)(`Selected agents via CLI filter: ${selected.map((a) => a.getName()).join(', ')}`, verbose);
|
|
149
164
|
}
|
|
150
165
|
else if (config.defaultAgents && config.defaultAgents.length > 0) {
|
|
151
166
|
const defaults = config.defaultAgents.map((n) => n.toLowerCase());
|
|
152
167
|
selected = agents.filter((agent) => {
|
|
153
|
-
const
|
|
154
|
-
const override = config.agentConfigs[
|
|
168
|
+
const identifier = agent.getIdentifier();
|
|
169
|
+
const override = config.agentConfigs[identifier]?.enabled;
|
|
155
170
|
if (override !== undefined) {
|
|
156
171
|
return override;
|
|
157
172
|
}
|
|
158
|
-
return defaults.
|
|
173
|
+
return defaults.some((d) => identifier === d || agent.getName().toLowerCase().includes(d));
|
|
159
174
|
});
|
|
175
|
+
(0, constants_1.logVerbose)(`Selected agents via config default_agents: ${selected.map((a) => a.getName()).join(', ')}`, verbose);
|
|
160
176
|
}
|
|
161
177
|
else {
|
|
162
|
-
selected = agents.filter((agent) => config.agentConfigs[agent.
|
|
178
|
+
selected = agents.filter((agent) => config.agentConfigs[agent.getIdentifier()]?.enabled !== false);
|
|
179
|
+
(0, constants_1.logVerbose)(`Selected all enabled agents: ${selected.map((a) => a.getName()).join(', ')}`, verbose);
|
|
163
180
|
}
|
|
164
181
|
// Collect all generated file paths for .gitignore
|
|
165
182
|
const generatedPaths = [];
|
|
166
183
|
for (const agent of selected) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
184
|
+
const actionPrefix = dryRun ? '[ruler:dry-run]' : '[ruler]';
|
|
185
|
+
console.log(`${actionPrefix} Applying rules for ${agent.getName()}...`);
|
|
186
|
+
(0, constants_1.logVerbose)(`Processing agent: ${agent.getName()}`, verbose);
|
|
187
|
+
const agentConfig = config.agentConfigs[agent.getIdentifier()];
|
|
170
188
|
// Collect output paths for .gitignore
|
|
171
189
|
const outputPaths = getAgentOutputPaths(agent, projectRoot, agentConfig);
|
|
190
|
+
(0, constants_1.logVerbose)(`Agent ${agent.getName()} output paths: ${outputPaths.join(', ')}`, verbose);
|
|
172
191
|
generatedPaths.push(...outputPaths);
|
|
192
|
+
if (dryRun) {
|
|
193
|
+
(0, constants_1.logVerbose)(`DRY RUN: Would write rules to: ${outputPaths.join(', ')}`, true);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
await agent.applyRulerConfig(concatenated, projectRoot, agentConfig);
|
|
197
|
+
}
|
|
173
198
|
const dest = await (0, mcp_1.getNativeMcpPath)(agent.getName(), projectRoot);
|
|
174
199
|
const enabled = cliMcpEnabled &&
|
|
175
200
|
(agentConfig?.mcp?.enabled ?? config.mcp?.enabled ?? true);
|
|
@@ -178,9 +203,15 @@ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cli
|
|
|
178
203
|
agentConfig?.mcp?.strategy ??
|
|
179
204
|
config.mcp?.strategy ??
|
|
180
205
|
'merge';
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
206
|
+
(0, constants_1.logVerbose)(`Applying MCP config for ${agent.getName()} with strategy: ${strategy}`, verbose);
|
|
207
|
+
if (dryRun) {
|
|
208
|
+
(0, constants_1.logVerbose)(`DRY RUN: Would apply MCP config to: ${dest}`, true);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
const existing = await (0, mcp_1.readNativeMcp)(dest);
|
|
212
|
+
const merged = (0, merge_1.mergeMcp)(existing, rulerMcpJson, strategy);
|
|
213
|
+
await (0, mcp_1.writeNativeMcp)(dest, merged);
|
|
214
|
+
}
|
|
184
215
|
}
|
|
185
216
|
}
|
|
186
217
|
// Handle .gitignore updates
|
|
@@ -200,8 +231,14 @@ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cli
|
|
|
200
231
|
const pathsToIgnore = generatedPaths.filter((p) => !p.endsWith('.bak'));
|
|
201
232
|
const uniquePaths = [...new Set(pathsToIgnore)];
|
|
202
233
|
if (uniquePaths.length > 0) {
|
|
203
|
-
|
|
204
|
-
|
|
234
|
+
const actionPrefix = dryRun ? '[ruler:dry-run]' : '[ruler]';
|
|
235
|
+
if (dryRun) {
|
|
236
|
+
console.log(`${actionPrefix} Would update .gitignore with ${uniquePaths.length} unique path(s): ${uniquePaths.join(', ')}`);
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
await (0, GitignoreUtils_1.updateGitignore)(projectRoot, uniquePaths);
|
|
240
|
+
console.log(`${actionPrefix} Updated .gitignore with ${uniquePaths.length} unique path(s) in the Ruler block.`);
|
|
241
|
+
}
|
|
205
242
|
}
|
|
206
243
|
}
|
|
207
244
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intellectronica/ruler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Ruler — apply the same rules to all coding agents",
|
|
5
5
|
"main": "dist/lib.js",
|
|
6
6
|
"scripts": {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"format": "prettier --write \"src/**/*.{ts,tsx,json,md}\"",
|
|
9
9
|
"test": "jest",
|
|
10
10
|
"test:watch": "jest --watch",
|
|
11
|
+
"test:coverage": "jest --coverage",
|
|
11
12
|
"build": "tsc",
|
|
12
13
|
"prepare": "npm run build"
|
|
13
14
|
},
|
|
@@ -58,6 +59,7 @@
|
|
|
58
59
|
"dependencies": {
|
|
59
60
|
"js-yaml": "^4.1.0",
|
|
60
61
|
"toml": "^3.0.0",
|
|
61
|
-
"yargs": "^17.7.2"
|
|
62
|
+
"yargs": "^17.7.2",
|
|
63
|
+
"zod": "^3.25.28"
|
|
62
64
|
}
|
|
63
65
|
}
|