# How the Rule Matching Process Works in TheFuck: From Failed Command to Fix

> Discover how TheFuck's rule matching pipeline generates prioritized corrections by wrapping commands, loading rules, filtering settings, and testing match functions.

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

---

**TheFuck's rule matching pipeline executes four sequential stages: wrapping the failed terminal command in a `Command` object, dynamically loading available rule modules from bundled and user directories, filtering for enabled rules via global settings, and testing each rule's `match` function to generate prioritized corrections.**

The rule matching process is the core algorithm powering [nvbn/thefuck](https://github.com/nvbn/thefuck), the popular command-line tool that suggests corrections for mistyped or failed terminal commands. Implemented across [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py), [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py), and [`thefuck/conf.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/conf.py), this engine evaluates Python-based rules against the failed command context to produce executable fixes.

## Stage 1: Wrapping the Failed Command in a Command Object

The pipeline initiates in [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py) where `Command.from_raw_script` (lines 71‑82) instantiates a `Command` object. This class encapsulates the raw script array, captured stdout/stderr output, and helper properties like `script_parts` which tokenizes the command for downstream analysis. The `Command` instance serves as the immutable input context for all subsequent rule evaluations.

## Stage 2: Discovering and Loading Rule Modules

Rule discovery occurs in [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py) through two key functions. First, `get_rules_import_paths` (lines 22‑38) yields filesystem paths for:

- The bundled `rules` directory distributed with the package
- The user's custom rules directory in their configuration path
- Any installed third-party packages prefixed with `thefuck_contrib_`

Next, `get_loaded_rules` (lines 8‑19) iterates these paths and invokes `Rule.from_path` (lines 31‑53 in [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py)) for each Python file. This method uses `load_source` to dynamically import the module, then extracts the rule's `match` function, `get_new_command` generator, priority value, and `requires_output` flag to construct a typed `Rule` object.

## Stage 3: Filtering Enabled Rules

Before pattern matching executes, `Rule.is_enabled` (lines 55‑66 in [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py)) applies configuration filters. The method consults the global `settings` object loaded from [`thefuck/conf.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/conf.py), checking whether the rule name appears in `settings.rules` or if the `ALL_ENABLED` flag from [`thefuck/const.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/const.py) is active. Users customize this behavior via the `THEFUCK_RULES` environment variable or the `exclude_rules` configuration option.

## Stage 4: Executing Match Logic and Generating Corrections

For each enabled rule, the engine invokes `Rule.is_match` (lines 168‑178 in [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py)), which implements a defensive three-step validation:

1. **Output requirement check**: If the rule specifies `requires_output = True` but `command.output` is `None`, the function returns `False` immediately
2. **Pattern matching**: The rule's custom `match(command)` function executes inside a `try/except` block to catch runtime errors
3. **Command generation**: Upon a `True` result, `Rule.get_corrected_commands` (lines 185‑199) calls the rule's `get_new_command` generator and wraps each suggestion in a `CorrectedCommand` object with calculated priority

For example, [`thefuck/rules/git_commit_add.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/rules/git_commit_add.py) (lines 5‑10) implements `match` by verifying that `"commit"` exists in `command.script_parts` and that the error text contains the substring `"no changes added to commit"`.

## Aggregating and Prioritizing Suggestions

The `get_corrected_commands` function in [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py) (lines 81‑92) aggregates all matched rules into a generator yielding `CorrectedCommand` instances. Subsequently, `organize_commands` (lines 52‑78) deduplicates identical suggestions and sorts them by the rule's priority attribute. The final ordered list surfaces the most likely fix first when the user executes the `fuck` command.

## Programmatic Rule Matching Example

You can interact directly with the rule matching process using the internal API:

```python
from thefuck.types import Command
from thefuck.corrector import get_corrected_commands

# Simulate a failed git commit without staged changes

raw_script = ['git', 'commit']
output = ('On branch master\n'
          'nothing to commit, working tree clean\n'
          'no changes added to commit (use "git add" and/or "git commit -a")')

cmd = Command.from_raw_script(raw_script)

# Retrieve all corrected commands suggested by enabled rules

for corrected in get_corrected_commands(cmd):
    print('Suggested fix:', corrected.script)

```

Executing this script outputs suggestions such as `git commit -a` and `git commit -p`, generated when the `git_commit_add` rule's `match` function returns `True` based on the simulated error context.

## Summary

- **Command encapsulation**: `Command.from_raw_script` in [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py) (lines 71‑82) creates an object containing the script and terminal output.
- **Dynamic discovery**: `get_rules_import_paths` in [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py) (lines 22‑38) locates rules in bundled, user, and third-party directories.
- **Configuration filtering**: `Rule.is_enabled` checks `settings.rules` from [`thefuck/conf.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/conf.py) and the `ALL_ENABLED` flag from [`thefuck/const.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/const.py).
- **Defensive matching**: `Rule.is_match` validates `requires_output` and executes the rule's `match` function inside exception handling.
- **Result optimization**: `organize_commands` in [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py) (lines 52‑78) sorts deduplicated suggestions by priority before display.

## Frequently Asked Questions

### How does TheFuck determine which rules are enabled?

The `Rule.is_enabled` method in [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py) (lines 55‑66) evaluates the global `settings.rules` dictionary managed by [`thefuck/conf.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/conf.py). Rules are active if explicitly included in the configuration or if the `ALL_ENABLED` constant from [`thefuck/const.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/const.py) is set. Users override this via the `THEFUCK_RULES` environment variable or the `exclude_rules` config option.

### What happens when multiple rules match the same command?

When multiple rules return `True` from their `match` functions, `get_corrected_commands` in [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py) (lines 81‑92) collects all suggestions. The `organize_commands` function (lines 52‑78) then removes duplicates and sorts results by the rule's priority attribute, ensuring higher-priority corrections appear first.

### Can individual rules require command output to function?

Yes, rules can define `requires_output = True` in their module. During matching, `Rule.is_match` (lines 168‑178 in [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py)) first checks if `command.output` is `None`. If a rule requires output but the command produced none, the matching logic short-circuits to `False` without executing the rule's `match` function.

### Where are rule modules stored and how are they loaded?

Rule modules reside in three locations: the bundled `thefuck/rules` directory, the user's custom rules directory, and any installed `thefuck_contrib_*` packages. The `get_rules_import_paths` function in [`thefuck/corrector.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/corrector.py) (lines 22‑38) discovers these paths, while `Rule.from_path` (lines 31‑53 in [`thefuck/types.py`](https://github.com/nvbn/thefuck/blob/main/thefuck/types.py)) dynamically imports each Python file using `load_source` and extracts the rule's callables and metadata.