# How to Create Custom Rules for The Fuck: A Complete Developer Guide

> Learn to create custom rules for thefuck. This guide shows developers how to extend the command-line tool with Python modules for personalized automation.

- Repository: [Vladimir Iakovlev/thefuck](https://github.com/nvbn/thefuck)
- Tags: how-to-guide
- Published: 2026-02-27

---

**Yes, users can create custom rules for thefuck by placing Python modules containing `match()` and `get_new_command()` functions in the `~/.config/thefuck/rules` directory, which the tool automatically discovers and loads alongside built-in rules.**

The open-source command-line corrector **thefuck** (nvbn/thefuck) is designed with extensibility at its core, allowing developers to create custom rules for thefuck that fix their specific workflow mistakes. Unlike rigid tools that only ship with predefined corrections, thefuck's architecture treats rules as pluggable Python modules, enabling you to teach the tool new tricks without modifying the core codebase. This guide explains exactly how the rule-loading mechanism works, where to place your files, and how to structure the required code.

## Where The Fuck Discovers Custom Rules

The corrector engine locates rule modules through the `get_rules_import_paths()` generator in [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py). This function yields three distinct paths in order of precedence:

```python
def get_rules_import_paths():
    # Bundled rules:

    yield Path(__file__).parent.joinpath('rules')
    # Rules defined by user:

    yield settings.user_dir.joinpath('rules')
    # Packages with third‑party rules:

    for path in sys.path:
        for contrib_module in Path(path).glob('thefuck_contrib_*'):
            contrib_rules = contrib_module.joinpath('rules')
            if contrib_rules.is_dir():
                yield contrib_rules

```

This implementation reveals three extension points:

- **Built-in rules**: Shipped with the package inside the library's own `rules` directory.
- **User rules**: Located at `~/.config/thefuck/rules` by default (falling back to legacy `~/.thefuck/rules` on older installations).
- **Third-party contributions**: Any installed Python package exposing a `thefuck_contrib_*` directory containing a `rules` subdirectory.

The `get_rules()` function subsequently scans these directories, loading every `*.py` file found and sorting them by priority.

## The Structure of a Rule Module

According to [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py), the `Rule` class constructs rule objects via `Rule.from_path()`, which dynamically imports user files. Your custom module must define at least two callables:

| Function | Purpose |
|----------|---------|
| `match(command)` | Returns `True` if the rule applies to the given **Command** object. |
| `get_new_command(command)` | Returns the corrected command string or list of strings. |

Optional module-level attributes control execution behavior:

- **`priority`**: Integer determining sort order (higher values run first). Defaults to `DEFAULT_PRIORITY` from [`thefuck/const.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/const.py).
- **`enabled_by_default`**: Boolean controlling whether the rule is active without explicit user configuration. Defaults to `True`.
- **`requires_output`**: Boolean indicating if the rule needs command output to function. Defaults to `True`; set to `False` for rules that correct syntax errors before execution.
- **`side_effect`**: Callable executed after running the corrected command, useful for cleanup or state changes.

When `thefuck` processes a command, `corrector.get_corrected_commands()` iterates over all enabled rules returned by `get_rules()` and yields corrections in priority order.

## Initializing Your User Rules Directory

You do not need to manually create the rules folder. During initialization, `conf.Settings.init()` invokes `_setup_user_dir()` in [`thefuck/conf.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/conf.py):

```python
def _setup_user_dir(self):
    """Returns user config dir, create it when it doesn't exist."""
    user_dir = self._get_user_dir_path()
    rules_dir = user_dir.joinpath('rules')
    if not rules_dir.is_dir():
        rules_dir.mkdir(parents=True)
    self.user_dir = user_dir

```

This guarantees that `~/.config/thefuck/rules` exists on first run, providing a drop-in location for your custom Python files.

## Enabling, Disabling, and Prioritizing Rules

Rule activation is controlled through the **settings** object in [`thefuck/conf.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/conf.py). The tool loads configuration from three sources:

1. **User settings file**: Located at `~/.config/thefuck/settings.py`, created automatically with commented defaults. Define `rules = ['rule_name', ...]` to whitelist specific rules, or `exclude_rules = ['rule_name', ...]` to blacklist them.
2. **Environment variables**: Set `THEFUCK_RULES` to a colon-separated list of rule names to enable for a specific session, or `THEFUCK_EXCLUDE_RULES` to disable specific ones.
3. **Built-in defaults**: The `DEFAULT_RULES` constant in [`thefuck/const.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/const.py) defines the baseline behavior when no overrides exist.

The `_settings_from_file()` method dynamically imports your [`settings.py`](https://github.com/nvbn/thefuck/blob/main/settings.py) using `load_source()`, making the configuration immediately available to the corrector engine.

## Complete Custom Rule Examples

### Fixing a Common Typo

Create `~/.config/thefuck/rules/gt.py` to correct "gt" to "git":

```python
def match(command):
    # Trigger when the first word is "gt"

    return command.script.split()[0] == 'gt'

def get_new_command(command):
    # Replace the first word with "git"

    parts = command.script.split()
    parts[0] = 'git'
    return ' '.join(parts)

```

After saving, the rule is active immediately:

```bash
$ gt status
git: 'status' is not a git command. See 'git --help'.

$ thefuck
git status

```

### Adding Side Effects for Cleanup

Use the optional `side_effect` parameter to execute logic after the corrected command runs:

```python
def match(command):
    return 'temp.txt' in command.script

def get_new_command(command):
    return command.script.replace('temp.txt', 'permanent.txt')

def side_effect(old_cmd, new_cmd):
    import os
    if os.path.exists('temp.txt'):
        os.remove('temp.txt')

```

### Controlling Execution Priority

Force your rule to evaluate before others by setting a high priority value:

```python
priority = 2000  # Higher than default 1000

def match(command):
    return command.script.startswith('vim')

def get_new_command(command):
    return command.script.replace('vim', 'nvim', 1)

```

## Summary

- **thefuck** discovers custom rules from `~/.config/thefuck/rules` (and legacy paths) via the `get_rules_import_paths()` function in [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py).
- Every rule module must define `match(command)` and `get_new_command(command)`; optional attributes include `priority`, `enabled_by_default`, `requires_output`, and `side_effect`.
- The user directory and settings file are auto-initialized by `_setup_user_dir()` in [`thefuck/conf.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/conf.py).
- Enable or disable rules via [`settings.py`](https://github.com/nvbn/thefuck/blob/main/settings.py), environment variables (`THEFUCK_RULES`, `THEFUCK_EXCLUDE_RULES`), or the `exclude_rules` list.
- Rules are loaded dynamically at runtime, so changes take effect immediately without reinstalling the tool.

## Frequently Asked Questions

### Where do I place custom rules for thefuck?

Place Python files (`.py`) in the `~/.config/thefuck/rules` directory. The tool automatically creates this directory on first run via `_setup_user_dir()` in [`thefuck/conf.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/conf.py). On legacy systems, the path `~/.thefuck/rules` is also supported.

### What functions must a custom rule define?

Every rule module must expose two functions: `match(command)`, which returns `True` when the rule should apply, and `get_new_command(command)`, which returns the corrected command string or list of strings. The `command` object provides access to the original script, output, and exit status.

### Can I disable built-in rules while keeping custom ones?

Yes. Add the built-in rule names to the `exclude_rules` list in `~/.config/thefuck/settings.py`, or set the `THEFUCK_EXCLUDE_RULES` environment variable. To run only specific rules, set `rules = ['your_custom_rule']` in settings or use `THEFUCK_RULES=your_custom_rule` for one-off execution.

### Do custom rules work if the original command produces no output?

Yes, but only if you set `requires_output = False` at the module level. By default, the `Rule.from_path()` loader in [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py) assumes rules need command output to analyze; setting this attribute to `False` allows your rule to correct commands that fail immediately or produce no stderr/stdout.