AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Python Precommit

skill-paldom-python-skills-python-precommit · by Paldom

Configures pre-commit for a Python repo — a .pre-commit-config.yaml with ruff, mypy, hygiene, commit-message and pre-push hooks, plus non-Python file formatting and validation (YAML, JSON, TOML, Markdown), kept in sync with CI. Use for 'set up pre-commit', 'add git hooks', 'format the YAML and Markdown', 'enforce conventional commits'. Not for Claude Code agent hooks or CI workflows.

No reviews yet
0 installs
24 views
0.0% view→install

Install

$ agentstack add skill-paldom-python-skills-python-precommit

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-paldom-python-skills-python-precommit)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Python Precommit? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

python-precommit

Set up and maintain the pre-commit framework in the user's Python repository. The value is not "write a YAML file" — a competent model can do that. The value is avoiding the specific traps that make hook setups silently useless: unpinned or drifted hook versions that disagree with CI, ruff hooks that auto-fix and let the commit through anyway, mypy hooks running in an empty environment, the archived Prettier mirror, and slow hooks that train everyone to type --no-verify.

When NOT to use

  • Claude Code / agent hooks (PostToolUse, settings.json, AGENTS.md rules,

which enforcement layer should run what) — the agent-guardrails skill, if installed, owns that.

  • CI workflow authoring beyond the single pre-commit mirror job below

(matrices, required checks, caching, action SHA pinning at scale) — the python-ci skill.

  • Choosing ruff rules or fixing lint/format errors — the python-lint skill.
  • Choosing or configuring the type checker itself — the python-typing skill.
  • Release automation that consumes conventional commits (version bumps,

changelogs, publishing) — the python-release skill. This skill only enforces the message format at commit time.

  • **Repo-level secret scanning / push protection / Dependabot for package

dependencies** — the python-supply-chain skill.

Operating principles

  1. The hook is convenience; CI is enforcement. git commit --no-verify

bypasses everything, silently, with no audit trail. Never present a hook as a guarantee. Every check in .pre-commit-config.yaml must also run in CI (switowski.com/blog/pre-commit-vs-ci).

  1. Commit stage stays fast — a few seconds. Slow hooks are the main driver

of --no-verify habits. Slow checks go to pre-push or CI.

  1. Pin everything. Every repo: gets a pinned rev:; an unpinned or

branch-pinned hook is both irreproducible and a supply-chain hole (installing hooks clones and executes arbitrary repos — pre-commit#942).

  1. Never enable a hook that fails on the existing tree. Run it over all

files, fix the fallout, commit, then it starts gating.

Workflow

1. Survey the repo

Before writing config, check:

ls .pre-commit-config.yaml .git/hooks/pre-commit 2>/dev/null  # existing setup?
grep -n 'ruff\|mypy' pyproject.toml          # versions to keep hooks in lockstep with
ls .github/workflows/ 2>/dev/null            # workflows -> add check-jsonschema; CI mirror target
ls package.json 2>/dev/null                  # Node already present? -> Prettier is cheap here

Record the ruff (and mypy) versions in the dev dependency group — hook revs must match them, or local and CI results will disagree (the "double accounting" problem, step 7).

2. Write the Python baseline

.pre-commit-config.yaml at the repo root. Pin every rev (tags shown; see step 7 for SHA freezing), then run pre-commit autoupdate once to resolve current releases and review the diff — the revs below are known-good examples, not the latest.

# .pre-commit-config.yaml
default_install_hook_types: [pre-commit, commit-msg, pre-push]

repos:
  # Hygiene — cheap, universal
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v6.0.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
        args: [--allow-multiple-documents]
      - id: check-toml
      - id: check-json
      - id: check-added-large-files
      - id: check-merge-conflict
      - id: detect-private-key

  # Python lint + format. Order matters: fixer first, formatter second.
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.15.10   # keep in lockstep with the ruff version in your dev deps
    hooks:
      - id: ruff-check          # named `ruff` on older revs
        args: [--fix, --exit-non-zero-on-fix]
      - id: ruff-format

Two load-bearing details:

  • --exit-non-zero-on-fix: without it, ruff silently fixes the file and the

hook passes — but the fix is left unstaged, so the commit ships the unfixed content. With it, the hook fails, the developer stages the fix and recommits. That fail → git add → recommit loop is expected pre-commit behavior; tell the user so once.

  • Excludes under pre-commit are special: pre-commit passes filenames

explicitly, which can bypass exclusions from a pyproject.toml in an excluded subdirectory (ruff#9585). Put excludes in the top-level ruff config, and for certainty add a hook-level exclude: ^(vendored|migrations)/ regex in the pre-commit config.

3. Non-Python files — format AND validate

Formatting is not validation: a malformed GitHub workflow or pyproject.toml formats fine. Add both layers. Default (Prettier via the maintained fork — the original pre-commit/mirrors-prettier is archived, broken since Prettier v3; older tutorials still point at it):

  - repo: https://github.com/rbubley/mirrors-prettier   # maintained fork
    rev: v3.8.2
    hooks:
      - id: prettier
        types_or: [yaml, markdown, json]   # never .py/.pyi — no fight with ruff
        args: [--prose-wrap=always]

  - repo: https://github.com/abravalheri/validate-pyproject
    rev: v0.25
    hooks:
      - id: validate-pyproject
        additional_dependencies: ["validate-pyproject-schema-store[all]"]

  - repo: https://github.com/python-jsonschema/check-jsonschema
    rev: 0.37.1
    hooks:
      - id: check-github-workflows
      - id: check-dependabot

If the team rejects the Node dependency ("Node tax"), use the Node-free stack instead: mdformat for Markdown, taplo for TOML, yamllint for YAML — more tools, zero npm. Recipes, trade-offs, and the yamllint-vs-Prettier conflict are in [references/config-recipes.md](references/config-recipes.md).

4. Commit-message stage — conventional commits

Only if the user wants commit-format enforcement (it feeds changelog/release automation, which the python-release skill owns):

  - repo: https://github.com/compilerla/conventional-pre-commit
    rev: v4.0.0
    hooks:
      - id: conventional-pre-commit
        stages: [commit-msg]

commit-msg hooks only run if that hook type is installed — default_install_hook_types (already in the baseline) handles this. Without it, plain pre-commit install installs only the pre-commit stage and the check silently never runs.

5. Pre-push stage — slow checks

mypy and tests are too slow for every commit. Run them once per push via repo: local hooks that use the project's own environment (this also sidesteps the mirrors-mypy isolated-env pitfall — see gotchas):

  - repo: local
    hooks:
      - id: mypy
        name: mypy (pre-push)
        entry: uv run mypy .
        language: system
        pass_filenames: false
        stages: [pre-push]
      - id: pytest
        name: pytest (pre-push)
        entry: uv run pytest -q
        language: system
        pass_filenames: false
        stages: [pre-push]

language: system means the tool must exist in each clone — fine here because uv run resolves the project's locked versions (pip fallback: entry: python -m mypy .). Budget: pre-push under ~30 s; anything slower is CI-only.

6. Install and first run

uv add --dev pre-commit          # pins pre-commit itself in uv.lock
uv run pre-commit install        # installs every stage in default_install_hook_types
uv run pre-commit run --all-files

(Without uv: python -m pip install pre-commit, then the same pre-commit … commands — noted once, applies to all commands in this skill.)

The first run --all-files will be slow (hook environments are built on demand) and will likely fail while fixing files — re-run until clean, review, and commit the formatting changes separately from the config. Hooks are per-clone and not tracked by git, so add the install step to the contributor docs or a make setup target; CI is the only backstop for clones that never ran it.

7. Mirror in CI and keep versions in sync

Local hooks and CI must run the same config at the same versions. The mirror job (this one job is in scope here; everything else CI belongs to the python-ci skill):

# .github/workflows/pre-commit.yml
name: pre-commit
on:
  pull_request:
  push: { branches: [main] }
jobs:
  pre-commit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4        # pin actions to full SHAs in real use
      - uses: actions/setup-python@v5    # (re-tagged releases are a real attack)
        with: { python-version: "3.12" }
      - run: python -m pip install pre-commit==4.3.0   # match your uv.lock version
      - run: pre-commit run --all-files --show-diff-on-failure

Because CI runs the identical .pre-commit-config.yaml, hook revs cannot drift between local and CI. What still drifts: the hook rev vs the same tool's version in your dev dependency group (ruff in both places is the classic). Keep them in lockstep; automate rev updates with one of — Dependabot's pre-commit ecosystem, pre-commit.ci's scheduled autoupdate, or a monthly pre-commit autoupdate PR (reviewed, never blind). For security-sensitive repos, pre-commit autoupdate --freeze pins revs to full commit SHAs, immune to re-tagging. Details, the pre-commit.ci autofix debate, and the maintenance-only status of pre-commit/action are in [references/sync-and-ci.md](references/sync-and-ci.md).

8. Verify

python3 "${CLAUDE_SKILL_DIR}/scripts/check_precommit_config.py" .pre-commit-config.yaml  # footgun lint, read-only
uv run pre-commit run --all-files        # exits 0 on a clean tree
git commit --allow-empty -m "bad msg"    # commit-msg hook must reject (if step 4 used)

The script (in this skill's folder) checks for unpinned revs, the archived Prettier mirror, missing --exit-non-zero-on-fix, mirrors-mypy without additional_dependencies, unscoped Prettier, wrong hook order, and stages that were configured but never installed. It never modifies anything.

Output spec — what done looks like

  • .pre-commit-config.yaml at the repo root; every remote repo has a

pinned rev (tag at minimum, SHA via --freeze where warranted).

  • default_install_hook_types covers every stage any hook uses.
  • pre-commit run --all-files exits 0 on the current tree.
  • A typical commit completes its hooks in a few seconds.
  • CI runs the identical config (pre-commit run --all-files job or

pre-commit.ci) — no separately-versioned ruff/mypy invocations duplicating the hooks.

  • A rev-update mechanism exists (Dependabot / pre-commit.ci / scheduled

autoupdate PR).

  • Contributor docs or Makefile mention pre-commit install.
  • scripts/check_precommit_config.py reports 0 errors.

Failure modes & gotchas

  • Ruff "fixed" the files but the commit went through with the old content

--exit-non-zero-on-fix missing from the ruff hook args (step 2).

  • Commit fails, developer confused, files "already fixed" → auto-fix hooks

leave fixes unstaged by design; git add -u and recommit. Say this once in contributor docs.

  • Passes locally, fails in CI (or vice versa) → version skew: hook rev

vs dev-dependency vs whatever CI installs. Fix by running the same config in CI and syncing revs (step 7). This "double accounting" is the top recurring pre-commit complaint.

  • Ruff lints files that are in its exclude list → pre-commit passes paths

explicitly; nested-config excludes may be bypassed (ruff#9585). Top-level excludes + hook-level exclude: regex.

  • mypy hook reports import errors / misses real errors

pre-commit/mirrors-mypy runs in an isolated env without your project's dependencies, and only sees staged files (no whole-program view). Either add additional_dependencies: [types-…, pydantic, …] and accept partial checking, or prefer the local uv run mypy . pre-push hook (step 5).

  • Prettier hook broken or frozenpre-commit/mirrors-prettier is

archived; use rbubley/mirrors-prettier.

  • Prettier and ruff fight over files → always scope Prettier with

types_or: [yaml, markdown, json]. Note the type identifier for TypeScript is ts, not typescript — the wrong name silently matches nothing.

  • yamllint fails files Prettier just formatted → their defaults genuinely

conflict (comment spacing, indentation); reconcile configs explicitly (see references/config-recipes.md) or drop one of them.

  • Hooks that fight each other, reformatting in a loop → order is fixer →

formatter (ruff-check --fix then ruff-format); formatters last.

  • commit-msg / pre-push hooks configured but never firing → that hook type

was never installed. default_install_hook_types or pre-commit install --hook-type commit-msg --hook-type pre-push.

  • New contributor has no hooks at all → per-clone install, by design.

Document it; rely on the CI mirror as backstop.

  • Everyone bypasses with --no-verify → the hooks are too slow or too

noisy; that is a design failure, not a discipline failure. Profile with pre-commit run --verbose, move slow hooks to pre-push/CI, and offer SKIP= git commit … as the surgical alternative. Do not promise any local mechanism can prevent bypass.

  • First run downloads the world → expected; environments are cached in

~/.cache/pre-commit afterward. Warn users once.

  • Coverage thresholds or full test suites in the pre-commit stage

community consensus is these belong in CI (or at most pre-push); commit-time coverage gates get bypassed and resented.

Unsettled debates — present options, don't assert winners

  • Autofix in CI — dominant stance: CI enforces, never mutates

(pre-commit run --all-files, fail on diff). pre-commit.ci's autofix (auto-commits formatting to PRs) is a deliberate exception some teams adopt; note it has documented gaps auto-fixing .github/workflows files.

uv-powered, drop-in reader of the same .pre-commit-config.yaml, faster installs/updates (community-reported) and native monorepo support; newer and less battle-tested. Reasonable to offer when speed or monorepos hurt; uv add --dev prek && uv run prek install.

  • "Pre-commit is dead under agents" — a minority position (Massdriver

talk, mid-2026) argues high-frequency agent edits make local hooks impractical and checks should consolidate in CI. Most current stacks keep pre-commit as the fast local layer; if the user's repo is agent-heavy, note the trade-off and let them choose. Which layer (agent hook vs git hook vs CI) runs which check is the agent-guardrails skill's territory.

Bundled material

  • [references/config-recipes.md](references/config-recipes.md) — complete

annotated configs: full baseline, Prettier vs Node-free stacks, validators, conventional-commit and pre-push recipes, uv lockfile hooks, optional secret scan, ruff-in-Markdown, monorepo/local-hook patterns.

  • [references/sync-and-ci.md](references/sync-and-ci.md) — the double

accounting problem, CI mirror recipes, pre-commit.ci, rev-update automation, SHA freezing and hook supply chain, bypass honesty, prek, stage-placement budgets.

  • [scripts/checkprecommitconfig.py](scripts/checkprecommitconfig.py) —

read-only footgun linter for an existing .pre-commit-config.yaml (python3 "${CLAUDE_SKILL_DIR}/scripts/check_precommit_config.py" [path] [--strict]; non-zero exit on findings).

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.