Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 33 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Ruler solves this by providing a **single source of truth** for all your AI agen
| Kilo Code | `.kilocode/rules/ruler_kilocode_instructions.md` | `.kilocode/mcp.json` |
| OpenCode | `AGENTS.md` | `opencode.json`, `~/.config/opencode/opencode.json` |
| Goose | `.goosehints` | - |
| Roo | `.roo/rules/ruler_roo_instructions.md` | - |

## Getting Started

Expand Down Expand Up @@ -164,18 +165,18 @@ The `apply` command looks for `.ruler/` in the current directory tree, reading t

### Options

| Option | Description |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--project-root <path>` | Path to your project's root (default: current directory) |
| `--agents <agent1,agent2,...>` | Comma-separated list of agent names to target (amp, copilot, claude, codex, cursor, windsurf, cline, aider, firebase, gemini-cli, junie, augmentcode, kilocode) |
| `--config <path>` | Path to a custom `ruler.toml` configuration file |
| `--mcp` / `--with-mcp` | Enable applying MCP server configurations (default: true) |
| `--no-mcp` | Disable applying MCP server configurations |
| `--mcp-overwrite` | Overwrite native MCP config entirely instead of merging |
| `--gitignore` | Enable automatic .gitignore updates (default: true) |
| `--no-gitignore` | Disable automatic .gitignore updates |
| `--local-only` | Do not look for configuration in `$XDG_CONFIG_HOME` |
| `--verbose` / `-v` | Display detailed output during execution |
| Option | Description |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--project-root <path>` | Path to your project's root (default: current directory) |
| `--agents <agent1,agent2,...>` | Comma-separated list of agent names to target (amp, copilot, claude, codex, cursor, windsurf, cline, aider, firebase, gemini-cli, junie, augmentcode, kilocode, roo) |
| `--config <path>` | Path to a custom `ruler.toml` configuration file |
| `--mcp` / `--with-mcp` | Enable applying MCP server configurations (default: true) |
| `--no-mcp` | Disable applying MCP server configurations |
| `--mcp-overwrite` | Overwrite native MCP config entirely instead of merging |
| `--gitignore` | Enable automatic .gitignore updates (default: true) |
| `--no-gitignore` | Disable automatic .gitignore updates |
| `--local-only` | Do not look for configuration in `$XDG_CONFIG_HOME` |
| `--verbose` / `-v` | Display detailed output during execution |

### Common Examples

Expand All @@ -197,6 +198,12 @@ ruler apply --agents copilot,claude
ruler apply --agents firebase
```

**Apply rules only to Roo:**

```bash
ruler apply --agents roo
```

**Use a specific configuration file:**

```bash
Expand Down Expand Up @@ -236,15 +243,15 @@ ruler revert [options]

### Options

| Option | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--project-root <path>` | Path to your project's root (default: current directory) |
| `--agents <agent1,agent2,...>` | Comma-separated list of agent names to revert (amp, copilot, claude, codex, cursor, windsurf, cline, aider, firebase, gemini-cli, junie, kilocode, opencode) |
| `--config <path>` | Path to a custom `ruler.toml` configuration file |
| `--keep-backups` | Keep backup files (.bak) after restoration (default: false) |
| `--dry-run` | Preview changes without actually reverting files |
| `--verbose` / `-v` | Display detailed output during execution |
| `--local-only` | Only search for local .ruler directories, ignore global config |
| Option | Description |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--project-root <path>` | Path to your project's root (default: current directory) |
| `--agents <agent1,agent2,...>` | Comma-separated list of agent names to revert (amp, copilot, claude, codex, cursor, windsurf, cline, aider, firebase, gemini-cli, junie, kilocode, opencode, roo) |
| `--config <path>` | Path to a custom `ruler.toml` configuration file |
| `--keep-backups` | Keep backup files (.bak) after restoration (default: false) |
| `--dry-run` | Preview changes without actually reverting files |
| `--verbose` / `-v` | Display detailed output during execution |
| `--local-only` | Only search for local .ruler directories, ignore global config |

### Common Examples

Expand Down Expand Up @@ -354,6 +361,10 @@ enabled = false
[agents.kilocode]
enabled = true
output_path = ".kilocode/rules/ruler_kilocode_instructions.md"

[agents.roo]
enabled = true
output_path = ".roo/rules/ruler_roo_instructions.md"
```

### Configuration Precedence
Expand Down Expand Up @@ -389,10 +400,10 @@ Define your project's MCP servers:
}
```


Ruler uses this file with the `merge` (default) or `overwrite` strategy, controlled by `ruler.toml` or CLI flags.

**Note for OpenAI Codex CLI:** To apply the local Codex CLI MCP configuration, set the `CODEX_HOME` environment variable to your project’s `.codex` directory:

```bash
export CODEX_HOME="$(pwd)/.codex"
```
Expand Down
37 changes: 37 additions & 0 deletions src/agents/RooAgent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as path from 'path';
import { IAgent, IAgentConfig } from './IAgent';
import {
backupFile,
writeGeneratedFile,
ensureDirExists,
} from '../core/FileSystemUtils';

/**
* Roo agent adapter.
*/
export class RooAgent implements IAgent {
getIdentifier(): string {
return 'roo_code';
}

getName(): string {
return 'Roo';
}

async applyRulerConfig(
concatenatedRules: string,
projectRoot: string,
rulerMcpJson: Record<string, unknown> | null, // eslint-disable-line @typescript-eslint/no-unused-vars
agentConfig?: IAgentConfig,
): Promise<void> {
const outputPath =
agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
await ensureDirExists(path.dirname(outputPath));
await backupFile(outputPath);
await writeGeneratedFile(outputPath, concatenatedRules);
}

getDefaultOutputPath(projectRoot: string): string {
return path.join(projectRoot, '.roo', 'rules', 'ruler_roo_instructions.md');
}
}
2 changes: 2 additions & 0 deletions src/agents/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { OpenCodeAgent } from './OpenCodeAgent';
import { CrushAgent } from './CrushAgent';
import { GooseAgent } from './GooseAgent';
import { AmpAgent } from './AmpAgent';
import { RooAgent } from './RooAgent';

export const allAgents: IAgent[] = [
new CopilotAgent(),
Expand All @@ -37,4 +38,5 @@ export const allAgents: IAgent[] = [
new GooseAgent(),
new CrushAgent(),
new AmpAgent(),
new RooAgent(),
];
53 changes: 53 additions & 0 deletions tests/unit/agents/RooAgent.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { promises as fs } from 'fs';
import * as path from 'path';
import os from 'os';

import { RooAgent } from '../../../src/agents/RooAgent';

describe('RooAgent', () => {
let tmpDir: string;

beforeEach(async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'ruler-roo-'));
});

afterEach(async () => {
await fs.rm(tmpDir, { recursive: true, force: true });
});

it('should have the correct identifier', () => {
const agent = new RooAgent();
expect(agent.getIdentifier()).toBe('roo_code');
});

it('should have the correct name', () => {
const agent = new RooAgent();
expect(agent.getName()).toBe('Roo');
});

it('should generate the default output path correctly', () => {
const agent = new RooAgent();
const expectedPath = path.join(
tmpDir,
'.roo',
'rules',
'ruler_roo_instructions.md',
);
expect(agent.getDefaultOutputPath(tmpDir)).toBe(expectedPath);
});

it('should apply ruler config and write to the output path in .roo/rules', async () => {
const agent = new RooAgent();
const rules = 'test rules';
const outputPath = path.join(
tmpDir,
'.roo',
'rules',
'ruler_roo_instructions.md',
);

await agent.applyRulerConfig(rules, tmpDir, null);

expect(await fs.readFile(outputPath, 'utf8')).toBe(rules);
});
});