# How Bundled Rules Are Distinguished from User-Defined Rules in TheFuck

> Learn how TheFuck distinguishes bundled rules from user-defined rules by their file system origin. Understand the key differences for better command-line error correction.

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

---

**Bundled rules are distinguished from user-defined rules by their file system origin: bundled rules reside in the package's internal `thefuck/rules/` directory, while user-defined rules are loaded from the user's configuration directory at `~/.config/thefuck/rules/`.**

TheFuck is a command-line correction tool that suggests fixes for mistyped console commands. It supports three types of rules: bundled (built-in) rules shipped with the package, user-defined rules created locally, and third-party contributed rules from external packages. Understanding how the system distinguishes between bundled and user-defined rules is essential for customizing behavior without modifying core library files.

## How TheFuck Loads Rules from Different Sources

The rule loading mechanism in [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py) treats each source differently based on its physical location on the filesystem.

### Bundled Rules Location

Bundled rules are stored in the `rules/` subdirectory inside the installed package. The system identifies these by resolving the path relative to [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py) itself:

```python

# thefuck/corrector.py

yield Path(__file__).parent.joinpath('rules')

```

This path points to the directory containing the library's source code, making it clear these are built-in rules distributed with the package.

### User-Defined Rules Location

User-defined rules are identified by their location in the user's configuration directory. The system creates and checks `~/.config/thefuck/rules/` (or `$XDG_CONFIG_HOME/thefuck/rules/`):

```python

# thefuck/corrector.py

yield settings.user_dir.joinpath('rules')

```

Because this path originates from `settings.user_dir` rather than the package directory, the system implicitly distinguishes these as user-created extensions.

### Third-Party Contributed Rules

While not part of the bundled vs. user distinction, the system also scans for external packages named `thefuck_contrib_*` on `sys.path`, loading their `rules` subdirectories after processing bundled and user rules.

## Code Implementation: Path Resolution in [`corrector.py`](https://github.com/nvbn/thefuck/blob/main/corrector.py)

The `get_rules_import_paths()` function in [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py) (lines 28-38) generates the ordered list of directories to scan for rule files:

```python

# thefuck/corrector.py

def get_rules_import_paths():
    # Bundled rules: package directory

    yield Path(__file__).parent.joinpath('rules')
    # User-defined rules: config directory

    yield settings.user_dir.joinpath('rules')
    # Third-party packages:

    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

```

The distinction is purely path-based: bundled rules come from the package installation directory, while user rules come from the configuration directory returned by `settings.user_dir`.

## User Configuration Directory Setup

The system ensures the user rules directory exists through the `_setup_user_dir()` method in [`thefuck/conf.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/conf.py) (lines 68-75):

```python

# thefuck/conf.py

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 creates `~/.config/thefuck/rules/` on first run, establishing the location where user-defined rules will be loaded from.

## Practical Examples

### Creating a User-Defined Rule

Place a Python file in `~/.config/thefuck/rules/` to create a custom rule that the system will distinguish from bundled rules:

```python

# ~/.config/thefuck/rules/hello_world.py

priority = 100  # optional, higher runs earlier

def match(command):
    # Trigger when the user types `hello`

    return command.script == 'hello'

def get_new_command(command):
    # Suggest the corrected command

    return 'echo "Hello, World!"'

```

Because this file resides in the user configuration directory, `get_rules_import_paths()` yields it separately from the bundled rules in the package directory.

### Disabling a Bundled Rule

You can disable specific bundled rules by adding them to the exclusion list in `~/.config/thefuck/settings.py`:

```python

# ~/.config/thefuck/settings.py

exclude_rules = ['git_push']   # disables the bundled `git_push` rule

```

The `Rule.from_path()` method in [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py) (lines 37-40) checks this exclusion list before loading:

```python

# thefuck/types.py

if name in settings.exclude_rules:
    logs.debug(u'Ignoring excluded rule: {}'.format(name))
    return

```

This mechanism works uniformly for both bundled and user-defined rules, though it is most commonly used to suppress specific bundled behaviors.

## Summary

- **Path-based distinction**: Bundled rules reside in `thefuck/rules/` inside the package installation, while user-defined rules live in `~/.config/thefuck/rules/`.
- **Loading order**: The `get_rules_import_paths()` function in [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py) yields bundled paths first, then user paths, then third-party contributions.
- **Unified interface**: Both rule types implement the same `match()` and `get_new_command()` interface and are loaded by the same `Rule.from_path()` mechanism.
- **Configuration control**: Users can disable any rule (bundled or custom) via `exclude_rules` in [`settings.py`](https://github.com/nvbn/thefuck/blob/main/settings.py), regardless of its origin.

## Frequently Asked Questions

### How does TheFuck know which rules are built-in versus custom?

TheFuck distinguishes bundled from user-defined rules by their file system location. Bundled rules are loaded from the package's internal `thefuck/rules/` directory (resolved via `Path(__file__).parent.joinpath('rules')`), while user-defined rules are loaded from `~/.config/thefuck/rules/` (resolved via `settings.user_dir.joinpath('rules')`).

### Can I override a bundled rule with my own version?

Yes. If you create a user-defined rule with the same filename as a bundled rule (e.g., [`git_push.py`](https://github.com/nvbn/thefuck/blob/main/git_push.py)) in your `~/.config/thefuck/rules/` directory, both rules will be loaded. TheFuck merges all rules into a single list ordered by priority, so your custom rule can take precedence by setting a higher `priority` value in the module.

### Where should I place my custom rules to ensure TheFuck finds them?

Place your custom rule files (Python modules ending in `.py`) in the `rules` subdirectory of your TheFuck configuration directory, typically located at `~/.config/thefuck/rules/` on Linux/macOS or `%APPDATA%\thefuck\rules\` on Windows. The `get_rules_import_paths()` function automatically yields this path when scanning for available rules.

### Do bundled and user-defined rules use the same programming interface?

Yes. Both bundled and user-defined rules implement the same interface: they must define a `match(command)` function that returns `True` when the rule should apply, and a `get_new_command(command)` function that returns the corrected command string. Both can optionally define a `priority` integer to control execution order, and both are loaded through the same `Rule.from_path()` mechanism in [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py).