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

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 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 itself:


# 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/):


# 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

The get_rules_import_paths() function in thefuck/corrector.py (lines 28-38) generates the ordered list of directories to scan for rule files:


# 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 (lines 68-75):


# 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:


# ~/.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:


# ~/.config/thefuck/settings.py

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

The Rule.from_path() method in thefuck/types.py (lines 37-40) checks this exclusion list before loading:


# 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 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, 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) 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →