From code-quality
Sets up Python code quality toolchain with ruff linting/formatting, mypy type checking, complexity gating, dead code detection, pre-commit hooks, pyproject.toml config, and Makefile targets.
How this skill is triggered — by the user, by Claude, or both
Slash command
/code-quality:code-qualityThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill sets up and enforces comprehensive Python code quality using a battle-tested
This skill sets up and enforces comprehensive Python code quality using a battle-tested
toolchain. Based on real production Makefiles using uv for fast dependency management.
uv run and uvx for instant tool execution — no global installs| Tool | Purpose | Config Location |
|---|---|---|
| ruff | Linting + formatting (replaces flake8, isort, black) | pyproject.toml |
| mypy | Static type checking | pyproject.toml |
| xenon | Cyclomatic complexity gating | CLI flags |
| vulture | Dead code detection | CLI flags |
| pre-commit | Git hook automation | .pre-commit-config.yaml |
When setting up code quality for a Python project, add these sections to pyproject.toml:
# =============================================================================
# Ruff — Linting & Formatting
# =============================================================================
[tool.ruff]
target-version = "py312" # Adjust to project's minimum Python version
line-length = 120
src = ["src", "tests"]
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort (import sorting)
"N", # pep8-naming
"UP", # pyupgrade
"B", # flake8-bugbear
"SIM", # flake8-simplify
"S", # flake8-bandit (security)
"A", # flake8-builtins
"C4", # flake8-comprehensions
"DTZ", # flake8-datetimez
"T20", # flake8-print
"PT", # flake8-pytest-style
"RET", # flake8-return
"PTH", # flake8-use-pathlib
"ERA", # eradicate (commented-out code)
"PL", # pylint subset
"RUF", # ruff-specific rules
]
ignore = [
"S101", # assert usage (fine in tests)
"PLR0913", # too many arguments (relax for data-heavy functions)
]
[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["S101", "PLR2004", "T20"] # Allow asserts, magic values, prints in tests
[tool.ruff.lint.isort]
known-first-party = ["PROJECT_NAME"] # Replace with actual package name
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "lf"
# =============================================================================
# Mypy — Type Checking
# =============================================================================
[tool.mypy]
python_version = "3.12" # Adjust to project's minimum Python version
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
strict_equality = true
warn_redundant_casts = true
warn_unused_ignores = true
no_implicit_reexport = true
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false # Relax for test functions
# =============================================================================
# Pytest
# =============================================================================
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v --strict-markers"
markers = [
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
]
select not extend-select: Explicit about exactly which rules are activeS rules included: Ruff's built-in bandit subset catches common security issues during lintingdisallow_untyped_defs forces type annotations — relax per-module if neededAdd to pyproject.toml under dev dependencies:
[project.optional-dependencies]
dev = [
"ruff>=0.8",
"mypy>=1.13",
"pytest>=8.0",
"pytest-cov>=6.0",
"pre-commit>=4.0",
]
Or with uv groups:
[dependency-groups]
dev = [
"ruff>=0.8",
"mypy>=1.13",
"pytest>=8.0",
"pytest-cov>=6.0",
"pre-commit>=4.0",
]
Tools that run via uvx (no install needed): xenon, vulture.
Add these targets to the project Makefile:
# =============================================================================
# Code Quality
# =============================================================================
lint:
uv run ruff check src tests
lint-fix:
uv run ruff check --fix src tests
format:
uv run ruff format src tests
format-check:
uv run ruff format --check src tests
typecheck:
uv run mypy src/PROJECT_NAME
# File length gate (max 500 lines per .py file)
MAX_LINES := 500
file-length:
@FAILED=0; \
for f in $$(find src/ -name '*.py'); do \
count=$$(wc -l < "$$f"); \
if [ "$$count" -gt $(MAX_LINES) ]; then \
echo "ERROR: $$f has $$count lines (max $(MAX_LINES))"; \
FAILED=1; \
fi; \
done; \
if [ "$$FAILED" -eq 1 ]; then \
echo ""; \
echo "Files exceeding $(MAX_LINES) lines must be split into smaller modules."; \
exit 1; \
fi; \
echo "All files under $(MAX_LINES) lines."
# Cyclomatic complexity gate (Xenon grade C)
complexity:
cd /tmp && uvx xenon --max-absolute C --max-modules D --max-average C $(CURDIR)/src/
# Dead code detection
dead-code:
uvx vulture src/ --min-confidence 90
# Ensure dev dependencies are installed
ensure-dev:
@uv sync --all-extras --quiet
# Run all checks (same as CI)
check: ensure-dev lint format-check typecheck file-length complexity test
@echo "All checks passed!"
# Full CI-equivalent pipeline (locally)
ci: ensure-dev lint format-check typecheck file-length complexity dead-code test
@echo "Full CI pipeline passed!"
Create .pre-commit-config.yaml:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.6 # Pin to a specific version
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.13.0
hooks:
- id: mypy
additional_dependencies: [] # Add stubs your project needs
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
args: ['--maxkb=500']
- id: check-merge-conflict
- id: debug-statements
Install hooks:
uv run pre-commit install
make lint format-check typecheck
make check
This runs: lint → format-check → typecheck → file-length → complexity → test
make ci
Adds: dead-code to the check pipeline.
When asked to review or improve code quality in an existing project, follow this order:
# Check if quality tools are configured
cat pyproject.toml | grep -A5 'tool.ruff'
cat pyproject.toml | grep -A5 'tool.mypy'
ls .pre-commit-config.yaml
ls Makefile
make lint 2>/dev/null || uv run ruff check src/
make typecheck 2>/dev/null || uv run mypy src/
Structure findings as:
make lint-fixmake format| Metric | Tool | Threshold | Action |
|---|---|---|---|
| Cyclomatic complexity | xenon | Grade C (max per function) | Refactor functions with complexity > 10 |
| Module complexity | xenon | Grade D (max per module) | Split modules that are too complex |
| Average complexity | xenon | Grade C (project average) | Overall project health indicator |
| File length | wc -l | 500 lines | Split into submodules |
| Dead code confidence | vulture | 90% | Investigate and remove confirmed dead code |
| Code | Name | What It Catches |
|---|---|---|
| E/W | pycodestyle | Basic style violations |
| F | pyflakes | Unused imports, undefined names |
| I | isort | Import ordering |
| N | pep8-naming | Naming convention violations |
| UP | pyupgrade | Python version upgrade opportunities |
| B | flake8-bugbear | Common bugs and design problems |
| SIM | flake8-simplify | Code that can be simplified |
| S | flake8-bandit | Security issues |
| C4 | flake8-comprehensions | Unnecessary list/dict/set calls |
| PTH | flake8-use-pathlib | os.path → pathlib suggestions |
| ERA | eradicate | Commented-out code |
| PL | pylint | Subset of pylint checks |
| RUF | ruff-specific | Ruff's own rules |
--ignore-missing-imports everywhere: Fix the imports, add stubsuv run and uvx — no system pollutionWhen a project already has some quality tooling:
disallow_untyped_defs = false, then tighten# type: ignore[specific-error] for known issues, never blanket ignores--min-confidence 90 for vulture to reduce false positives in new projectsnpx claudepluginhub sam-dumont/claude-skills --plugin code-qualityConfigures Ruff for linting and formatting, mypy or pyright for type checking, pre-commit hooks, lint rule sets, and modern Python typing patterns like PEP 695.
Enforces advanced Python code quality gates beyond basic linting with tools for cognitive complexity, code duplication, architectural boundaries, mutation testing, docstring coverage, type coverage, and AI-generated code detection.
Reviews and improves Python library code quality using ruff linting, mypy type checking, Pythonic refactoring idioms, and type hint patterns.