# How Rules Are Loaded in TheFuck: A Deep Dive into the Rule Loading Pipeline

> Discover the four-step pipeline TheFuck uses to load rules from built-in, user, and third-party directories. Understand how Python files are imported and filtered for efficient command fixing.

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

---

**TheFuck loads rules through a four-step pipeline that discovers Python files from built-in, user, and third-party directories, imports them as `Rule` objects, and filters them based on user settings.**

The open-source project `nvbn/thefuck` powers its command-line correction magic through a dynamic, extensible rule system. Understanding how rules are loaded in TheFuck reveals how the tool discovers corrections from its core library, your local configuration, and even third-party Python packages.

## The Four-Step Rule Loading Pipeline

The entry point for rule discovery is `thefuck.corrector`, which orchestrates the loading process through three primary functions: `get_rules_import_paths()`, `get_rules()`, and `get_loaded_rules()`.

### Step 1: Discovering Rule Directories via `get_rules_import_paths()`

The pipeline begins by building a prioritized list of directories to scan. In [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py) (lines 22-38), the function `get_rules_import_paths()` yields paths from three distinct sources:

- **Built-in rules**: The core package at `thefuck/rules`
- **User rules**: The user-specific directory at `~/.config/thefuck/rules` (falling back to the legacy `~/.thefuck/rules`)
- **Third-party rules**: Any installed Python package named `thefuck_contrib_*` found on `sys.path`

### Step 2: Enumerating Python Files via `get_rules()`

Once directories are identified, `get_rules()` (lines 40-49 in [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py)) collects every Python file using `Path.glob('*.py')`. The function sorts these file paths alphabetically and returns the complete list to the loader, ensuring deterministic rule ordering.

### Step 3: Importing and Instantiating Rules via `get_loaded_rules()`

The heavy lifting occurs in `get_loaded_rules()` (lines 8-20 in [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py)). For each file path discovered in Step 2, the system calls `Rule.from_path(path)`. 

Inside [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py) (lines 31-54), `Rule.from_path` uses a helper function `load_source` to dynamically import the Python module. It then extracts the required callables (`match` and `get_new_command`) along with optional metadata (`priority`, `enabled_by_default`, `requires_output`). 

Only rules that pass the `is_enabled` check and are not explicitly excluded by the user are yielded as active `Rule` instances.

### Step 4: Filtering by User Configuration via `is_enabled()`

The final gatekeeper is `Rule.is_enabled` in [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py) (lines 56-66). This method consults the global `settings` object (populated from [`thefuck/conf.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/conf.py), environment variables, and CLI arguments).

A rule is considered enabled when:
- Its name appears in `settings.rules`, **or**
- It is marked `enabled_by_default=True` **and** the special token `ALL_ENABLED` is present in `settings.rules`

Rules listed in `settings.exclude_rules` are ignored unconditionally, regardless of other settings.

## Where Rules Come From: Built-in, User, and Third-Party Sources

TheFuck's architecture supports three distinct rule sources, allowing users to extend functionality without modifying core code.

### Built-in Rules (`thefuck/rules`)

The core distribution includes dozens of rules in the `thefuck/rules` directory. These handle common command-line mistakes like `git_push`, `git_commit`, `cd_parent`, and `sudo`. These rules ship with the package and are updated with each release.

### User Rules (`~/.config/thefuck/rules`)

Users can create custom rules by placing Python files in `~/.config/thefuck/rules` (or the legacy `~/.thefuck/rules`). The `Settings._setup_user_dir()` method in [`thefuck/conf.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/conf.py) (lines 68-75) ensures this directory exists and is included in the import path scan.

### Third-Party Contributions (`thefuck_contrib_*`)

The system automatically discovers installed packages named with the `thefuck_contrib_*` prefix. In [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py) (lines 33-37), the code scans `sys.path` for these packages and includes their `rules/` subdirectories in the loading pipeline, enabling community extensions distributed via PyPI.

## Practical Examples: Working with Rules

### Listing All Loaded Rules

You can inspect which rules are currently active in your Python environment:

```python
from thefuck.corrector import get_rules

# List names of all enabled rules

rule_names = [rule.name for rule in get_rules()]
print(rule_names)

# Output: ['git_push', 'git_commit', 'cd_parent', 'sudo', ...]

```

### Creating a Custom Rule

Create a file at `~/.config/thefuck/rules/my_echo.py`:

```python

# my_echo.py

priority = 100  # Optional: defaults to thefuck.const.DEFAULT_PRIORITY

def match(command):
    """Detect when user types 'ech' instead of 'echo'."""
    return command.script.startswith('ech ')

def get_new_command(command):
    """Correct 'ech ' to 'echo '."""
    return command.script.replace('ech ', 'echo ', 1)

```

After saving, the rule automatically appears in the next `thefuck` invocation because `get_rules_import_paths()` includes your user configuration directory.

### Disabling Specific Rules

Use the environment variable to exclude built-in rules:

```bash
export THEFUCK_EXCLUDE_RULES=git_push,git_commit
thefuck

```

This is processed in [`thefuck/conf.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/conf.py) via `Settings._settings_from_env()` and `Settings._val_from_env()`, then checked in `Rule.is_enabled()`.

### Enabling All Rules Explicitly

To force-load every available rule (including those disabled by default):

```bash
export THEFUCK_RULES=ALL_ENABLED
thefuck

```

The `ALL_ENABLED` token is defined in [`thefuck/const.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/const.py) and evaluated in `Rule.is_enabled()` within [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py).

## Summary

- **TheFuck loads rules through a four-step pipeline** defined in [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py): directory discovery, file enumeration, dynamic import, and user-configurable filtering.
- **Rules originate from three sources**: the built-in `thefuck/rules` package, user-specific files in `~/.config/thefuck/rules`, and third-party packages prefixed with `thefuck_contrib_*`.
- **Activation is controlled by the `Rule.is_enabled()` method** in [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py), which checks against `settings.rules`, `settings.exclude_rules`, and the `ALL_ENABLED` sentinel.
- **Custom rules require only two functions**: `match(command)` and `get_new_command(command)`, placed in a `.py` file in the user rules directory.

## Frequently Asked Questions

### How does TheFuck find custom rules I create?

TheFuck discovers custom rules by scanning `~/.config/thefuck/rules` (or the legacy `~/.thefuck/rules`) during the `get_rules_import_paths()` execution in [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py). Any Python file placed in this directory is automatically imported and evaluated for the required `match` and `get_new_command` functions.

### Can I disable built-in rules without modifying the source code?

Yes, you can disable specific rules using the `THEFUCK_EXCLUDE_RULES` environment variable or the `exclude_rules` setting in your configuration. The `Rule.is_enabled()` method in [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py) checks this exclusion list before activating any rule, allowing you to suppress unwanted corrections like `git_push` or `sudo` without touching the core package files.

### What is the `ALL_ENABLED` setting and when should I use it?

`ALL_ENABLED` is a special sentinel value defined in [`thefuck/const.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/const.py) that forces the loading of every available rule, including those normally disabled by default. When you set `THEFUCK_RULES=ALL_ENABLED` or include `ALL_ENABLED` in your rules list, the `Rule.is_enabled()` logic bypasses the default enablement checks. Use this when you want to experiment with experimental or disabled community rules.

### How do third-party `thefuck_contrib_*` packages work?

Third-party rule packages follow a naming convention: any installed Python package prefixed with `thefuck_contrib_` is automatically discovered by `get_rules_import_paths()` in [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py) (lines 33-37). The loader searches `sys.path` for these packages and includes their `rules/` subdirectories in the scanning process, allowing community contributions to be distributed via PyPI and integrated without manual configuration.