Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
11 changes: 11 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"permissions": {
"allow": [
"Bash(gh pr view:*)",
"Bash(npm run lint)",
"Bash(npm install)",
"Bash(git add:*)"
],
"deny": []
}
}
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ The `apply` command looks for `.ruler/` in the current directory tree, reading t
| 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) |
| `--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, opencode, crush, goose) |
| `--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 |
Expand All @@ -160,6 +160,7 @@ The `apply` command looks for `.ruler/` in the current directory tree, reading t
| `--no-gitignore` | Disable automatic .gitignore updates |
| `--local-only` | Do not look for configuration in `$XDG_CONFIG_HOME` |
| `--verbose` / `-v` | Display detailed output during execution |
| `--disable-backup` | Disable creation of `.bak` backup files when applying rules (default: false) |

### Common Examples

Expand Down Expand Up @@ -199,6 +200,12 @@ ruler apply --verbose
ruler apply --no-mcp --no-gitignore
```

**Apply rules without creating backup files:**

```bash
ruler apply --disable-backup
```

## Usage: The `revert` Command

The `revert` command safely undoes all changes made by `ruler apply`, restoring your project to its pre-ruler state. It intelligently restores files from backups (`.bak` files) when available, or removes generated files that didn't exist before.
Expand Down
8 changes: 6 additions & 2 deletions src/agents/AiderAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export class AiderAgent implements IAgent {
const mdPath =
agentConfig?.outputPathInstructions ??
this.getDefaultOutputPath(projectRoot).instructions;
await backupFile(mdPath);
if (!agentConfig?.disableBackup) {
await backupFile(mdPath);
}
await writeGeneratedFile(mdPath, concatenatedRules);

const cfgPath =
Expand All @@ -38,7 +40,9 @@ export class AiderAgent implements IAgent {
let doc: AiderConfig = {} as AiderConfig;
try {
await fs.access(cfgPath);
await backupFile(cfgPath);
if (!agentConfig?.disableBackup) {
await backupFile(cfgPath);
}
const raw = await fs.readFile(cfgPath, 'utf8');
doc = (yaml.load(raw) || {}) as AiderConfig;
} catch {
Expand Down
8 changes: 6 additions & 2 deletions src/agents/AugmentCodeAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,16 @@ export class AugmentCodeAgent implements IAgent {
): Promise<void> {
const output =
agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
await backupFile(output);
if (!agentConfig?.disableBackup) {
await backupFile(output);
}
await writeGeneratedFile(output, concatenatedRules);

if (rulerMcpJson) {
const settingsPath = getVSCodeSettingsPath(projectRoot);
await backupFile(settingsPath);
if (!agentConfig?.disableBackup) {
await backupFile(settingsPath);
}

const existingSettings = await readVSCodeSettings(settingsPath);
const augmentServers = transformRulerToAugmentMcp(rulerMcpJson);
Expand Down
4 changes: 3 additions & 1 deletion src/agents/ClaudeAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export class ClaudeAgent implements IAgent {
): Promise<void> {
const output =
agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
await backupFile(output);
if (!agentConfig?.disableBackup) {
await backupFile(output);
}
await writeGeneratedFile(output, concatenatedRules);
}
getDefaultOutputPath(projectRoot: string): string {
Expand Down
4 changes: 3 additions & 1 deletion src/agents/ClineAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export class ClineAgent implements IAgent {
): Promise<void> {
const output =
agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
await backupFile(output);
if (!agentConfig?.disableBackup) {
await backupFile(output);
}
await writeGeneratedFile(output, concatenatedRules);
}
getDefaultOutputPath(projectRoot: string): string {
Expand Down
4 changes: 3 additions & 1 deletion src/agents/CodexCliAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ export class CodexCliAgent implements IAgent {
defaults.instructions;

// Write the instructions file
await backupFile(instructionsPath);
if (!agentConfig?.disableBackup) {
await backupFile(instructionsPath);
}
await writeGeneratedFile(instructionsPath, concatenatedRules);

// Handle MCP configuration if enabled
Expand Down
4 changes: 3 additions & 1 deletion src/agents/CopilotAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ export class CopilotAgent implements IAgent {
const output =
agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
await ensureDirExists(path.dirname(output));
await backupFile(output);
if (!agentConfig?.disableBackup) {
await backupFile(output);
}
await writeGeneratedFile(output, concatenatedRules);
}
getDefaultOutputPath(projectRoot: string): string {
Expand Down
4 changes: 3 additions & 1 deletion src/agents/CursorAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ export class CursorAgent implements IAgent {
const content = `${frontMatter}${concatenatedRules.trimStart()}`;

await ensureDirExists(path.dirname(output));
await backupFile(output);
if (!agentConfig?.disableBackup) {
await backupFile(output);
}
await writeGeneratedFile(output, content);
}
getDefaultOutputPath(projectRoot: string): string {
Expand Down
4 changes: 3 additions & 1 deletion src/agents/FirebaseAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export class FirebaseAgent implements IAgent {
): Promise<void> {
const output =
agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
await backupFile(output);
if (!agentConfig?.disableBackup) {
await backupFile(output);
}
await writeGeneratedFile(output, concatenatedRules);
}

Expand Down
2 changes: 2 additions & 0 deletions src/agents/IAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export interface IAgentConfig {
outputPathConfig?: string;
/** MCP propagation config for this agent. */
mcp?: McpConfig;
/** Disable backup file creation for this agent */
disableBackup?: boolean;
}

export interface IAgent {
Expand Down
4 changes: 3 additions & 1 deletion src/agents/JunieAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ export class JunieAgent implements IAgent {
const output =
agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
await ensureDirExists(path.dirname(output));
await backupFile(output);
if (!agentConfig?.disableBackup) {
await backupFile(output);
}
await writeGeneratedFile(output, concatenatedRules);
}
getDefaultOutputPath(projectRoot: string): string {
Expand Down
4 changes: 3 additions & 1 deletion src/agents/KiloCodeAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ export class KiloCodeAgent implements IAgent {
const output =
agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
await ensureDirExists(path.dirname(output));
await backupFile(output);
if (!agentConfig?.disableBackup) {
await backupFile(output);
}
await writeGeneratedFile(output, concatenatedRules);
}

Expand Down
4 changes: 3 additions & 1 deletion src/agents/OpenHandsAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export class OpenHandsAgent implements IAgent {
const output =
agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
await ensureDirExists(path.dirname(output));
await backupFile(output);
if (!agentConfig?.disableBackup) {
await backupFile(output);
}
await writeGeneratedFile(output, concatenatedRules);
}
getDefaultOutputPath(projectRoot: string): string {
Expand Down
4 changes: 3 additions & 1 deletion src/agents/WindsurfAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ export class WindsurfAgent implements IAgent {
const output =
agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
await ensureDirExists(path.dirname(output));
await backupFile(output);
if (!agentConfig?.disableBackup) {
await backupFile(output);
}
await writeGeneratedFile(output, concatenatedRules);
}
getDefaultOutputPath(projectRoot: string): string {
Expand Down
8 changes: 8 additions & 0 deletions src/cli/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ export function run(): void {
'Only search for local .ruler directories, ignore global config',
default: false,
});
y.option('disable-backup', {
type: 'boolean',
description:
'Disable creation of backup files before applying changes',
default: false,
});
},
async (argv) => {
const projectRoot = argv['project-root'] as string;
Expand All @@ -79,6 +85,7 @@ export function run(): void {
const verbose = argv.verbose as boolean;
const dryRun = argv['dry-run'] as boolean;
const localOnly = argv['local-only'] as boolean;
const disableBackup = argv['disable-backup'] as boolean;

// Determine gitignore preference: CLI > TOML > Default (enabled)
// yargs handles --no-gitignore by setting gitignore to false
Expand All @@ -99,6 +106,7 @@ export function run(): void {
verbose,
dryRun,
localOnly,
disableBackup,
);
console.log('Ruler apply completed successfully.');
} catch (err: unknown) {
Expand Down
6 changes: 4 additions & 2 deletions src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export async function applyAllAgentConfigs(
verbose = false,
dryRun = false,
localOnly = false,
disableBackup = false,
): Promise<void> {
// Load configuration (default_agents, per-agent overrides, CLI filters)
logVerbose(
Expand Down Expand Up @@ -312,7 +313,8 @@ export async function applyAllAgentConfigs(
}
agentsMdWritten = true;
}
let finalAgentConfig = agentConfig;
// Propagate disableBackup to agent config
let finalAgentConfig = { ...agentConfig, disableBackup };
if (agent.getIdentifier() === 'augmentcode' && rulerMcpJson) {
const resolvedStrategy =
cliMcpStrategy ??
Expand All @@ -321,7 +323,7 @@ export async function applyAllAgentConfigs(
'merge';

finalAgentConfig = {
...agentConfig,
...finalAgentConfig,
mcp: {
...agentConfig?.mcp,
strategy: resolvedStrategy,
Expand Down
Loading