Magnificent app which corrects your previous console command
Creating custom correction rules, rule structure, and integration patterns. This guide enables extending the correction system with new command-specific logic to handle additional CLI tools and error patterns not covered by built-in rules.
Every rule module must implement these core functions for integration with the correction system.
def match(command, settings):
"""
Determines if this rule applies to the given command.
Parameters:
- command (Command): The failed command with script, stdout, stderr
- settings (Settings): Application settings and configuration
Returns:
bool: True if this rule can provide corrections for the command
This function should analyze the command to determine applicability.
Common patterns:
- Check command.script for command name or arguments
- Check command.stderr for specific error messages
- Check command.stdout for output patterns
- Use settings for rule configuration
"""
def get_new_command(command, settings):
"""
Generates corrected command(s) for the matched command.
Parameters:
- command (Command): The failed command to correct
- settings (Settings): Application settings and configuration
Returns:
str or list[str]: Single correction or list of possible corrections
This function should return the corrected command string(s).
Multiple corrections are ranked by order in the list.
"""Rules can define optional attributes to customize their behavior and integration.
enabled_by_default = True
"""
bool: Whether this rule should be enabled by default.
Default: True if not specified
Set to False for experimental rules or rules that may conflict
"""
priority = 1000
"""
int: Rule priority for ordering corrections.
Lower numbers = higher priority (executed first)
Default: conf.DEFAULT_PRIORITY (1000) if not specified
Priority guidelines:
- 100-500: Critical fixes (sudo, permission issues)
- 500-1000: Common command corrections
- 1000-1500: Typo fixes and suggestions
- 1500+: Experimental or low-confidence corrections
"""
requires_output = True
"""
bool: Whether this rule needs command output to function.
Default: True if not specified
Set to False for rules that only analyze command.script
Rules with requires_output=False can work with:
- Commands that produce no output
- Timeouts or killed processes
- Script-only analysis
"""
def side_effect(old_cmd, new_cmd, settings):
"""
Optional function executed when the corrected command runs.
Parameters:
- old_cmd (Command): Original failed command
- new_cmd (str): Corrected command being executed
- settings (Settings): Application settings
Returns:
None
Use for additional actions like:
- Updating configuration files
- Creating directories or files
- Logging corrections
- Cleaning up state
"""Simple rules that match based on command name or structure.
# Example: Fix 'gti' typo to 'git'
def match(command, settings):
return command.script.startswith('gti ')
def get_new_command(command, settings):
return command.script.replace('gti ', 'git ', 1)
enabled_by_default = True
priority = 900
requires_output = FalseRules that analyze error output to determine corrections.
# Example: Add sudo for permission denied errors
def match(command, settings):
return ('permission denied' in command.stderr.lower() or
'operation not permitted' in command.stderr.lower())
def get_new_command(command, settings):
return f'sudo {command.script}'
enabled_by_default = True
priority = 100 # High priority for permission fixesAdvanced rules that provide multiple correction options.
import re
from thefuck.utils import get_closest
def match(command, settings):
return (command.script.startswith('git ') and
'is not a git command' in command.stderr)
def get_new_command(command, settings):
# Extract the invalid git command
match = re.search(r"git: '(\w+)' is not a git command", command.stderr)
if not match:
return []
invalid_cmd = match.group(1)
git_commands = ['push', 'pull', 'commit', 'checkout', 'branch', 'merge', 'status']
# Find similar commands
closest = get_closest(invalid_cmd, git_commands, cutoff=0.6)
if closest:
base_cmd = command.script.replace(invalid_cmd, closest, 1)
return [base_cmd]
return []
enabled_by_default = True
priority = 1000Rules using utility decorators for cleaner code.
from thefuck.utils import for_app
from thefuck.specific.sudo import sudo_support
@sudo_support
@for_app('docker')
def match(command, settings):
return 'permission denied' in command.stderr.lower()
@sudo_support
def get_new_command(command, settings):
# sudo_support decorator automatically adds/removes sudo
return command.script
enabled_by_default = True
priority = 200Rules that perform additional actions when corrections are executed.
import os
from pathlib import Path
def match(command, settings):
return (command.script.startswith('cd ') and
'no such file or directory' in command.stderr.lower())
def get_new_command(command, settings):
# Extract the directory name
parts = command.script.split()
if len(parts) > 1:
directory = parts[1]
return f'mkdir -p {directory} && cd {directory}'
return command.script
def side_effect(old_cmd, new_cmd, settings):
"""Log directory creation for future reference."""
parts = new_cmd.split('&&')[0].strip().split()
if len(parts) > 2:
created_dir = parts[2]
log_file = Path.home() / '.thefuck' / 'created_dirs.log'
with log_file.open('a') as f:
f.write(f"{created_dir}\n")
enabled_by_default = True
priority = 1200Rules that handle multiple related applications.
def match(command, settings):
package_managers = ['apt-get', 'yum', 'dnf', 'pacman']
return (any(command.script.startswith(pm) for pm in package_managers) and
'permission denied' in command.stderr.lower())
def get_new_command(command, settings):
return f'sudo {command.script}'
enabled_by_default = True
priority = 150Rules that use environment context for better corrections.
import os
from thefuck.utils import which
def match(command, settings):
return (command.script.startswith('python ') and
'command not found' in command.stderr)
def get_new_command(command, settings):
corrections = []
# Try python3 if available
if which('python3'):
corrections.append(command.script.replace('python ', 'python3 ', 1))
# Try py if on Windows
if os.name == 'nt' and which('py'):
corrections.append(command.script.replace('python ', 'py ', 1))
return corrections
enabled_by_default = True
priority = 800Rules that use command history for intelligent corrections.
from thefuck.utils import get_valid_history_without_current
def match(command, settings):
return 'command not found' in command.stderr
def get_new_command(command, settings):
history = get_valid_history_without_current(command)
# Look for similar commands in history
invalid_cmd = command.script.split()[0]
for hist_cmd in history[:20]: # Check recent history
if hist_cmd.startswith(invalid_cmd[:-1]): # Similar prefix
return hist_cmd
return []
enabled_by_default = False # Experimental
priority = 1800Custom rules should be placed in the user's rules directory:
~/.thefuck/rules/my_custom_rule.py# Template for testing custom rules
from thefuck.types import Command
def test_rule():
"""Test the custom rule functionality."""
# Test command that should match
test_command = Command(
script="my_command --flag",
stdout="",
stderr="my_command: error message"
)
# Test match function
assert match(test_command, {}) == True
# Test correction generation
correction = get_new_command(test_command, {})
assert correction == "expected_correction"
print("Rule tests passed!")
if __name__ == "__main__":
test_rule()from thefuck import logs
def match(command, settings):
logs.debug(f"Testing rule against: {command.script}", settings)
result = # ... rule logic ...
logs.debug(f"Rule match result: {result}", settings)
return result
def get_new_command(command, settings):
logs.debug(f"Generating correction for: {command.script}", settings)
correction = # ... correction logic ...
logs.debug(f"Generated correction: {correction}", settings)
return correctiondef match(command, settings):
try:
# Rule logic that might fail
return analyze_command(command)
except Exception as e:
# Log error but don't crash
logs.exception(f"Rule match error: {e}", settings)
return False
def get_new_command(command, settings):
try:
# Correction logic
return generate_correction(command)
except Exception as e:
logs.exception(f"Rule correction error: {e}", settings)
return [] # Return empty list if correction fails# Use existing utilities for common operations
from thefuck.utils import replace_argument, get_closest, for_app
from thefuck.specific.sudo import sudo_support
# Combine decorators for powerful rule behavior
@sudo_support
@for_app('git')
def match(command, settings):
return 'permission denied' in command.stderr
@sudo_support # This will automatically handle sudo addition/removal
def get_new_command(command, settings):
return command.script # sudo_support handles the restThis comprehensive rule development system allows users to extend thefuck's capabilities to handle any command-line tool or error pattern, making it a truly extensible correction system.
Install with Tessl CLI
npx tessl i tessl/pypi-thefuck