Skip to main content

manage-precommit

A Claude Code skill that builds a repository's .pre-commit-config.yaml from a small curated catalog — and merges into an existing config without clobbering what is already there.

Two invariants:

  • Latest, pinned. Every repo it adds gets the newest release tag, fetched live at run time. Nothing is hardcoded.
  • Never clobber. An existing config keeps its comments, formatting, top-level keys, repo revs, and any hooks outside the catalog. Only missing pieces are added, and nothing is duplicated.

Catalog

Key Adds Files written into the repo
hygiene pre-commit-hooks: trailing whitespace, end-of-file, check-yaml, check-json, large files, merge conflicts, mixed line endings
yamllint yamllint .yamllint.yaml
markdownlint markdownlint-cli2 .markdownlint.yaml
mermaid local hook validating fenced mermaid blocks scripts/lint-mermaid.mjs
gitleaks gitleaks secret scan

Mermaid ships no offline linter — its real parser only runs in a browser — so the bundled hook renders each diagram with mermaid-cli and fails the commit on a parse error.

Platforms

Linux and macOS. Not Windows.

The installer itself is cross-platform and CI proves it — manage-precommit install is exercised on Windows and macOS runners every push. The skill is the part that is not: its procedure runs mktemp, rm -f and command -v, and drives pre-commit, which is a POSIX arrangement throughout. Half of it working is not the half that matters, so the package does not claim the platform.

Requirements

  • pre-commit 4.0+
  • Python 3.10+ and git. No third-party Python packages — the skill is installed by symlink and its scripts run under your system python3, so anything it needed would have to be installed by hand on every machine.
  • For the mermaid hook only: Node.js, plus a Chromium/Chrome the hook's mermaid-cli can drive (it downloads one on first use if none is reusable). In CI, a container, or on a distro that restricts unprivileged user namespaces, Chromium's sandbox cannot start — set MERMAID_LINT_NO_SANDBOX=1 to run it without one. Opt-in, because that removes a real boundary around a browser rendering text out of the repository. The hook says so itself when it hits that failure, and reports it as an environment problem rather than an invalid diagram.

Install

Works under Claude Code, Codex and GitHub Copilot CLI. Both routes install a symlink, never a copy — so there is only ever one set of files, and nothing can drift out of sync.

manage-precommit install detects which of the three are on this machine and links the skill where each one looks:

Agent Skills directory
Claude Code ~/.claude/skills/
Codex ~/.agents/skills/
GitHub Copilot CLI ~/.agents/skills/

Codex and Copilot read the same directory, so installing for both writes one link, not two of the same name. --agent NAME (repeatable), --all and --dest DIR overrule the detection; --dry-run prints what would happen, refusals included.

As a package

pipx install manage-precommit
manage-precommit install

The package is an installer and nothing else; all the work lives in the skill files it links. --dry-run prints what would happen, refusals included; --dest DIR overrules the default location; --force acts on something that is not ours. manage-precommit uninstall removes the links and leaves the package alone -- and it sweeps every directory it could have written to, not just the detected ones: a link outlives the product that read it, which is exactly when leaving it behind is worst.

It refuses to touch anything it did not create: a real directory there may be a hand-written skill, and a link pointing elsewhere is not its to remove.

From a checkout

git clone git@github.com:grammy-jiang/manage-precommit.git
cd manage-precommit
make install     # ~/.claude/skills/manage-precommit -> ./src/manage_precommit/skill
make uninstall

This links the working tree, so an edit is live on the next Claude Code restart with no rebuild.

Usage

Invoke the skill and answer the prompts:

/manage-precommit

It scans the repo, proposes hooks (always-on plus ones matching what the repo actually contains), merges the selection, installs the git hook, runs the suite, shows the diff, and — only with confirmation — commits and pushes just the pre-commit setup files.

The engine also runs standalone (S=src/manage_precommit/skill/scripts):

python3 $S/precommit.py --catalog                      # list catalog keys
python3 $S/precommit.py --dir /path/to/repo --detect   # inspect existing config
python3 $S/precommit.py --dir /path/to/repo --recommend  # what the repo calls for, and why
python3 $S/precommit.py --dir /path/to/repo --force \
    --templates-file keys.txt --facts-out /tmp/facts.json

How a run flows

flowchart TD
  A[Scan repo] --> B[Propose hooks]
  B --> C{User selects}
  C --> D[Merge templates<br/>pin latest versions]
  D --> E[Install + run hooks]
  E --> F{All pass?}
  F -->|no| G[Report, stop]
  F -->|yes| H[Review diff]
  H --> I{Commit?}
  I -->|yes| J[Commit setup files only]
  J --> K[Push, with force gated<br/>behind a compare]
  I -->|no| L[Leave in working tree]

Layout

src/manage_precommit/skill/SKILL.md       the procedure Claude Code reads
                    skill/scripts/precommit.py  catalog, detect, recommend, merge, verify
                    skill/scripts/gitwork.py    status, commit, push-plan, push, facts
                    skill/scripts/config.py     the config scanner and additive writer
                    skill/scripts/summary.py    the end-of-run summary
                    skill/scripts/shared.py     sanitiser, no-follow reader, JSON contract
                    skill/templates/            one YAML fragment per catalog entry
                    skill/assets/               files copied into the target repo
                    skill/references/           on-demand detail (force-push, worked example)
tests/                                     pytest suite

The checkout and the installed tree are the same paths: nothing is remapped, so a path in a traceback traces back here by relative position.

How the merge keeps its promise

Each catalog entry is a YAML fragment with a __REV__ or __NPM__ placeholder. The engine substitutes the latest upstream version and inserts the fragment as text — it never re-emits the file. Every byte outside an inserted block is carried across untouched, and the write is rejected unless the original can be reconstructed from the result by deleting exactly the blocks that were added.

Reading is a strict line scanner rather than a YAML library. It refuses anything it cannot prove it understands — anchors, aliases, merge keys, flow sequences where a block is expected, more than one document, tabs — and says which line. A refusal is an exit code; a guess would be a wrong answer that looks right.

Dogfooding

This repo uses its own hooks. scripts/lint-mermaid.mjs is a symlink to src/manage_precommit/skill/assets/lint-mermaid.mjs, so the copy this repo runs and the payload it ships to other repos cannot drift apart.

Development

pip install -e '.[dev]'
python3 -m pytest        # 119 tests; no test touches the network
python3 -m ruff check . && python3 -m ruff format --check .
python3 -m mypy

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

manage_precommit-0.2.1.tar.gz (175.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

manage_precommit-0.2.1-py3-none-any.whl (103.0 kB view details)

Uploaded Python 3

File details

Details for the file manage_precommit-0.2.1.tar.gz.

File metadata

  • Download URL: manage_precommit-0.2.1.tar.gz
  • Upload date:
  • Size: 175.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for manage_precommit-0.2.1.tar.gz
Algorithm Hash digest
SHA256 d214a414fa841a5bde8be3ca3b357a59ec3b134eaaac7b0dd2c3bca64b5ed4a7
MD5 a7fe66e41a8a5872b165a1720e50dca8
BLAKE2b-256 92de02e4327a5655ff47d71df8234e8a6f1c67599f800591511c09964ed38d96

See more details on using hashes here.

Provenance

The following attestation bundles were made for manage_precommit-0.2.1.tar.gz:

Publisher: release.yml on grammy-jiang/manage-precommit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file manage_precommit-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for manage_precommit-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d4d3b6616f901089c11f32a9e2b14f8e0919ff80ec0406c277eccb6095962407
MD5 3f0b91f17d59401fd390d826910234ba
BLAKE2b-256 858e961d4ef7580b7fd56a738a43263803a80d0b7d60e03819b123d9d12a88f0

See more details on using hashes here.

Provenance

The following attestation bundles were made for manage_precommit-0.2.1-py3-none-any.whl:

Publisher: release.yml on grammy-jiang/manage-precommit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page