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

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. This function yields three distinct paths in order of precedence:

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, 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.
  • 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:

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. 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 defines the baseline behavior when no overrides exist.

The _settings_from_file() method dynamically imports your 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":

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:

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

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:

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.
  • 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.
  • Enable or disable rules via 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. 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 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.

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 →