agentsclimarketplace

Ruff

Skill hyperb1iss/hyperskills/skills/ruff

Use this skill when linting, formatting, or fixing Python code with ruff. Activates on mentions of ruff, ruff check, ruff format, ruff fix, ruff server, ruff analyze, Python linting, Python formatting, flake8, isort, black replacement, noqa, pycodestyle, pyflakes, lint rules, ruff config, ruff.toml, autofix, per-file-ignores, language server for Python, or Python code quality.From its SKILL.md

Install
npx -y skills add hyperb1iss/hyperskills --skill ruff

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

SKILL.md

11.2 KB, as published. Nobody here has run it

ruff: Python Linter & Formatter

ruff is three tools in one Rust binary: linter (ruff check), formatter (ruff format), and dependency analyzer (ruff analyze graph). It replaces Flake8, Black, isort, pyupgrade, and dozens more.

Snapshot: v0.15.12, Apr 2026. The installed binary outranks this file. Check ruff --version and ruff check --help when a flag or rule code here doesn't land.

The built-in language server (ruff server) replaces the deprecated ruff-lsp package (archived Dec 2025).

Invocation

uv run ruff ...     # Project dependency (pinned version)
uvx ruff ...        # One-off (latest)
ruff ...            # Global install

Rule Selection: The Critical Decision

Default rules are minimal: only ["E4", "E7", "E9", "F"], catches syntax errors and undefined names but misses most quality rules. You almost certainly need to extend this.

select vs extend-select

CommandBehavior
select = ["E", "F", "B"]Replaces entire default set. Only these run.
extend-select = ["B"]Adds to whatever select provides (or defaults)

Config inheritance trap: When a child config specifies select, the parent's ignore list is discarded. This surprises people with monorepo setups.

Specificity wins: More specific prefixes override less specific ones. select = ["E"] + ignore = ["E501"] enables all E rules except E501.

Recommended Selection Strategy

New project, start broad:

[tool.ruff.lint]
select = [
    "E", "W",    # pycodestyle
    "F",         # Pyflakes
    "I",         # isort
    "N",         # pep8-naming
    "UP",        # pyupgrade
    "B",         # flake8-bugbear
    "SIM",       # flake8-simplify
    "TC",        # flake8-type-checking
    "RUF",       # Ruff-specific
]
ignore = ["E501"]  # Let formatter handle line length

Library / open source, maximum strictness:

[tool.ruff.lint]
select = ["ALL"]
ignore = [
    # Formatter conflicts (MUST disable)
    "W191", "E111", "E114", "E117",
    "D206", "D300",
    "Q000", "Q001", "Q002", "Q003", "Q004",
    "COM812", "COM819",
    # Pydocstyle conflicts
    "D203", "D213",
    # Overly strict
    "D100", "D104",
    "ANN101", "ANN102",
    "FBT", "ERA001",
    "E501",
]

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["S101", "D", "ANN", "ARG"]
"scripts/**" = ["T20", "INP001"]
"**/__init__.py" = ["F401", "D104"]

Legacy migration, incremental:

[tool.ruff.lint]
select = ["E4", "E7", "E9", "F"]
extend-select = [
    "I",      # Step 1: import sorting (safe, auto-fixable)
    "UP",     # Step 2: pyupgrade (mostly auto-fixable)
    # "B",    # Step 3: uncomment when ready
]

The ALL Selector

select = ["ALL"] enables every stable rule. Ruff auto-disables conflicting pairs (D203/D211, D212/D213), but being explicit is better practice. Preview rules require preview = true and are not included even with ALL.

Formatter Behavior

Configuration

[tool.ruff.format]
quote-style = "double"           # "double" | "single" | "preserve"
indent-style = "space"           # "space" | "tab"
skip-magic-trailing-comma = false
docstring-code-format = true     # Format code in docstrings
preview = false                  # Enable 2026 style guide

Rules That CONFLICT With the Formatter

When using ruff format, these lint rules should be avoided:

ignore = [
    "W191", "E111", "E114", "E117",  # Indentation
    "D206", "D300",                   # Docstring formatting
    "Q000", "Q001", "Q002", "Q003", "Q004",  # Quotes
    "COM812", "COM819",               # Commas
]

Also avoid ISC002 in Ruff's documented formatter-conflict case: ISC002 selected, ISC001 not selected, and flake8-implicit-str-concat.allow-multiline = false.

Known Deviations from Black

Ruff targets >99.9% parity with Black but has 23 intentional divergences. The most impactful:

DeviationRuffBlack
F-string interiorsFormats {expr} contents (stable since 0.9.0)Does not touch f-string interiors
Pragma comments (# noqa, # type:)Excluded from line widthCounted in line width
Implicit string concatMerges when fits on one lineSplits more aggressively
Blank lines at block startRemoves themPreserves them (Black 24+)
Trailing commentsExpands statement to keep comment closeCollapses, moves comment to end
Single-element tuplesAlways parenthesizesRemoves parens when safe

E501 and the Formatter

The formatter makes best-effort line wrapping, it cannot always succeed. Comments, long strings, and URLs may exceed line-length. Either ignore E501 or set lint.pycodestyle.max-line-length higher than line-length.

Fix Safety Model

ruff check --fix .                    # Safe fixes only
ruff check --fix --unsafe-fixes .     # Include unsafe (review first!)
ruff check --fix --diff .             # Preview changes before applying
SafetyMeaningExample
SafeCannot change runtime behaviorReordering imports
UnsafeMay change behaviorlist(x)[0] -> next(iter(x)) changes exception type

Override per-rule:

[tool.ruff.lint]
extend-safe-fixes = ["RUF015"]       # Promote to safe
extend-unsafe-fixes = ["F401"]        # Demote to unsafe (require --unsafe-fixes)

Suppression System

# Line-level
import os  # noqa: F401

# Block-level (new in 0.15.0)
# ruff: disable[E501]
LONG_VALUE = "..."
# ruff: enable[E501]

# File-level
# ruff: noqa: F401, E501
ruff check --select RUF100 --fix .    # Clean up unused noqa comments
ruff check --add-noqa .               # Auto-add noqa to all violations

Preview Mode

Preview is a staging area for new rules and formatter changes.

[tool.ruff.lint]
preview = true       # Expands defaults from 59 to 412 rules
explicit-preview-rules = true  # Require individual opt-in even with preview on

Preview rules are NOT activated by prefix selection or ALL, they require preview mode enabled. Use explicit-preview-rules = true to control which preview rules activate individually.

Dependency Graph Analysis

ruff analyze graph src/                          # File dependency graph (JSON)
ruff analyze graph --direction=dependents src/   # Reverse graph
ruff analyze graph --detect-string-imports src/  # Include dynamic imports

Use cases: selective test running, dead code detection, circular import detection.

Configuration

File precedence: .ruff.toml > ruff.toml > pyproject.toml (nearest wins, no merging across levels).

Falls back to ~/.config/ruff/ruff.toml when no project config exists.

[tool.ruff]
target-version = "py312"         # Inferred from requires-python if unset
line-length = 88
src = ["src", "tests"]           # First-party import classification
required-version = "==0.15.12"   # Pin version with a PEP 440 specifier
extend = "../pyproject.toml"     # Inherit parent config

[tool.ruff.lint.isort]
known-first-party = ["myproject"]
combine-as-imports = true

[tool.ruff.lint.pydocstyle]
convention = "google"            # "google" | "numpy" | "pep257"

[tool.ruff.lint.flake8-type-checking]
runtime-evaluated-base-classes = ["pydantic.BaseModel"]
runtime-evaluated-decorators = ["attrs.define"]

For the complete rule catalog snapshot, see references/rules.md. For full configuration reference, see references/configuration.md.

Debugging

ruff check --show-settings .     # Dump resolved config
ruff check --show-files .        # List files that would be checked
ruff check --statistics .        # Count violations per rule
ruff rule E501                   # Explain a specific rule
ruff linter                      # List all available linters

Non-Obvious Gotchas

GotchaExplanation
TCH -> TC renameTCH prefix is now legacy alias for TC. Use TC in new configs
No third-party pluginsRuff re-implements Flake8 plugins in Rust. Cannot install additional ones
isort differencesSome edge cases differ from real isort (aliased imports, inline comments)
Notebooks: per-cell scopeE402 checked per-cell, not per-file. Each cell is its own module scope
--fix can break codeEven "safe" fixes can break dynamic Python. Review diffs for F401, UP, B rules
ruff-lsp is deadUse ruff server (built into binary). The separate ruff-lsp package was archived Dec 2025
Range formattingruff format --range=10:1-20:1 formats only lines 10-20 (single file, not notebooks)

Anti-Patterns

Anti-PatternFix
Blanket # noqa on every lineFix the violations or use per-file-ignores
select = ["ALL"] with no ignoreAlways pair with formatter conflict rules and overly strict rules
Running ruff format before ruff check --fixLint fixes first (may reorder imports), then format
Using ruff-lspSwitch to ruff server (built-in, maintained)
Ignoring E501 without using formatterEither use ruff format OR enforce E501, not neither
select in child config without knowing it resetsUse extend-select to preserve parent's rule set

What This Skill is NOT

  • Not a replacement for ruff --help or ruff rule <CODE> for specific rule docs
  • Not for type checking (use ty)
  • Not for package management (use uv)
  • Not for third-party Flake8 plugins that ruff hasn't re-implemented

Keep looking

Skills are one crate of 628,971. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.