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
97 changes: 97 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,103 @@ Ruler uses this configuration with the `merge` (default) or `overwrite` strategy
export CODEX_HOME="$(pwd)/.codex"
```

## Custom Commands Configuration

Ruler supports custom commands that can be propagated to compatible AI agents. These commands allow you to define reusable prompts and workflows that agents can execute.

### Command Directory

Custom command prompt files are stored in a designated directory (default: `commands`). You can configure this location in `ruler.toml`:

```toml
# Specify where command prompt files are located (default: "commands")
command_directory = "commands"
```

### Command Configuration

Commands are defined in the `[commands]` section of `ruler.toml`. Each command requires:

- **name**: The command identifier
- **description**: Brief description of what the command does
- **prompt_file**: Relative path to the markdown file containing the command prompt
- **type**: Command type (`slash`, `workflow`, `prompt-file`, or `instruction`)

### Example Configuration

```toml
# --- Custom Commands ---
[commands.pr-review]
name = "pr-review"
description = "Review code for best practices and security"
prompt_file = "commands/pr-review.md"
type = "slash"

[commands.commit]
name = "commit"
description = "Create semantic git commits with proper formatting"
prompt_file = "commands/commit.md"
type = "slash"

[commands.refactor]
name = "refactor"
description = "Refactor code following best practices"
prompt_file = "commands/refactor.md"
type = "workflow"
```

### Command Types

- **`slash`**: Slash commands that can be invoked with `/command-name`
- **`workflow`**: Multi-step workflows for complex tasks
- **`prompt-file`**: Simple prompt-based commands
- **`instruction`**: Instructional commands for guidance

### Creating Command Prompt Files

Command prompt files are markdown files that contain detailed instructions for the AI agent. They should be placed in your configured `command_directory` (default: `commands/`).

**Example: `commands/pr-review.md`**

```markdown
# PR Review Command

You have access to the `gh` terminal command. Please review the PR that I asked you to review.

## Review Process

1. **Gather PR Information**:
- Get PR title, description, and comments
- Review the full diff
- Identify modified files

2. **Analyze Changes**:
- Understand what was changed and why
- Check for code quality issues
- Verify security considerations
- Ensure best practices are followed

3. **Provide Feedback**:
- Highlight positive aspects
- Identify areas for improvement
- Suggest specific changes if needed
- Check for potential bugs or issues

Focus on code quality, security, maintainability, and adherence to project standards.
```

### Supported Agents

Custom commands are supported by agents that have command processing capabilities. Check the agent-specific documentation for command support details.

### Usage

Once configured, custom commands become available to compatible AI agents. The exact invocation method depends on the agent:

- **Cursor**: Available as slash commands in the chat interface
- **Claude Code**: Accessible through the command palette
- **Other agents**: Check agent-specific documentation for usage instructions

## `.gitignore` Integration

Ruler automatically manages your `.gitignore` file to keep generated agent configuration files out of version control.
Expand Down
26 changes: 26 additions & 0 deletions src/agents/AbstractAgent.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as path from 'path';
import { IAgent, IAgentConfig } from './IAgent';
import { CommandConfig } from '../types';
import { applyCommandsToDirectory } from '../core/CommandProcessor';
import {
backupFile,
writeGeneratedFile,
Expand Down Expand Up @@ -74,4 +76,28 @@ export abstract class AbstractAgent implements IAgent {
supportsMcpRemote(): boolean {
return false;
}

/**
* Shared implementation for applying commands to a specific directory.
* @param commands Command configurations from ruler.toml
* @param commandContents Command markdown file contents
* @param projectRoot The root directory of the project
* @param commandDir The command directory path (e.g., '.claude/commands')
* @param backup Whether to backup existing files
*/
protected async applyCommandsToDirectory(
commands: Record<string, CommandConfig>,
commandContents: Record<string, string>,
projectRoot: string,
commandDir: string,
backup = true,
): Promise<void> {
await applyCommandsToDirectory(
commands,
commandContents,
projectRoot,
commandDir,
backup,
);
}
}
17 changes: 17 additions & 0 deletions src/agents/AugmentCodeAgent.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as path from 'path';
import { IAgent, IAgentConfig } from './IAgent';
import { CommandConfig } from '../types';
import { applyCommandsToDirectory } from '../core/CommandProcessor';
import { backupFile, writeGeneratedFile } from '../core/FileSystemUtils';

/**
Expand Down Expand Up @@ -50,4 +52,19 @@ export class AugmentCodeAgent implements IAgent {
supportsMcpRemote(): boolean {
return false;
}

async applyCommands(
commands: Record<string, CommandConfig>,
commandContents: Record<string, string>,
projectRoot: string,
backup = true,
): Promise<void> {
await applyCommandsToDirectory(
commands,
commandContents,
projectRoot,
'.augment/commands',
backup,
);
}
}
16 changes: 16 additions & 0 deletions src/agents/ClaudeAgent.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as path from 'path';
import { AbstractAgent } from './AbstractAgent';
import { CommandConfig } from '../types';

/**
* Claude Code agent adapter.
Expand All @@ -24,4 +25,19 @@ export class ClaudeAgent extends AbstractAgent {
supportsMcpRemote(): boolean {
return true;
}

async applyCommands(
commands: Record<string, CommandConfig>,
commandContents: Record<string, string>,
projectRoot: string,
backup = true,
): Promise<void> {
await this.applyCommandsToDirectory(
commands,
commandContents,
projectRoot,
'.claude/commands',
backup,
);
}
}
17 changes: 17 additions & 0 deletions src/agents/CodexCliAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { promises as fs } from 'fs';
import { parse as parseTOML, stringify } from '@iarna/toml';
import { IAgent, IAgentConfig } from './IAgent';
import { AgentsMdAgent } from './AgentsMdAgent';
import { CommandConfig } from '../types';
import { writeGeneratedFile } from '../core/FileSystemUtils';
import { applyCommandsToDirectory } from '../core/CommandProcessor';
import { DEFAULT_RULES_FILENAME } from '../constants';

interface McpServer {
Expand Down Expand Up @@ -158,4 +160,19 @@ export class CodexCliAgent implements IAgent {
supportsMcpRemote(): boolean {
return false; // Codex CLI only supports STDIO based on PR description
}

async applyCommands(
commands: Record<string, CommandConfig>,
commandContents: Record<string, string>,
projectRoot: string,
backup = true,
): Promise<void> {
await applyCommandsToDirectory(
commands,
commandContents,
projectRoot,
'.codex/prompts',
backup,
);
}
}
16 changes: 16 additions & 0 deletions src/agents/CursorAgent.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as path from 'path';
import { AbstractAgent } from './AbstractAgent';
import { IAgentConfig } from './IAgent';
import { CommandConfig } from '../types';
import {
backupFile,
writeGeneratedFile,
Expand Down Expand Up @@ -58,4 +59,19 @@ export class CursorAgent extends AbstractAgent {
supportsMcpRemote(): boolean {
return true;
}

async applyCommands(
commands: Record<string, CommandConfig>,
commandContents: Record<string, string>,
projectRoot: string,
backup = true,
): Promise<void> {
await this.applyCommandsToDirectory(
commands,
commandContents,
projectRoot,
'.cursor/commands',
backup,
);
}
}
16 changes: 15 additions & 1 deletion src/agents/IAgent.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Interface defining an AI agent configuration adapter.
*/
import { McpConfig } from '../types';
import { McpConfig, CommandConfig } from '../types';
/**
* Configuration overrides for a specific agent.
*/
Expand Down Expand Up @@ -64,4 +64,18 @@ export interface IAgent {
* Defaults to false if not implemented.
*/
supportsMcpRemote?(): boolean;

/**
* Applies custom commands to the agent's command directory.
* @param commands Command configurations from ruler.toml
* @param commandContents Command markdown file contents
* @param projectRoot The root directory of the project
* @param backup Whether to backup existing files
*/
applyCommands?(
commands: Record<string, CommandConfig>,
commandContents: Record<string, string>,
projectRoot: string,
backup?: boolean,
): Promise<void>;
}
16 changes: 16 additions & 0 deletions src/agents/WindsurfAgent.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as path from 'path';
import { AbstractAgent } from './AbstractAgent';
import { IAgentConfig } from './IAgent';
import { CommandConfig } from '../types';
import {
backupFile,
writeGeneratedFile,
Expand Down Expand Up @@ -179,4 +180,19 @@ export class WindsurfAgent extends AbstractAgent {

return files;
}

async applyCommands(
commands: Record<string, CommandConfig>,
commandContents: Record<string, string>,
projectRoot: string,
backup = true,
): Promise<void> {
await this.applyCommandsToDirectory(
commands,
commandContents,
projectRoot,
'.windsurf/workflows',
backup,
);
}
}
23 changes: 23 additions & 0 deletions src/cli/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,29 @@ export async function initHandler(argv: InitArgs): Promise<void> {
# [mcp_servers.example_remote]
# url = "https://api.example.com/mcp"
# headers = { Authorization = "Bearer REPLACE_ME" }

# --- Custom Commands Directory ---
# Specify where command prompt files are located (default: "commands")
# command_directory = "commands"

# --- Custom Commands ---
# Define custom commands that will be propagated to supported agents
# Each command requires: name, description, type, and either 'prompt' OR 'prompt_file'
# Available types: "slash", "workflow", "prompt-file", "instruction"

# Example with inline prompt:
# [commands.pr-review]
# name = "pr-review"
# description = "Review code for best practices and security"
# prompt = "Please review the current code for best practices, security issues, and potential improvements."
# type = "slash"

# Example with file-based prompt:
# [commands.commit]
# name = "commit"
# description = "Create semantic git commits with proper formatting"
# prompt_file = "commands/commit.md"
# type = "slash"
`;
if (!(await exists(instructionsPath))) {
// Create new AGENTS.md regardless of legacy presence.
Expand Down
Loading