preen
Preen is the conformance-and-adoption CLI for the py-canon fleet standard. py-canon defines the standard — a copier template, reusable GitHub workflows, and shared Sphinx configuration. Preen is how repos enter the fleet and stay in it: it scaffolds new packages, retrofits existing ones, pulls template updates, checks conformance, and cuts tag-driven releases.
preen requires Python >=3.12, matching the floor the fleet standard sets for every repo it checks.
Install
uv tool install preen # or: pipx install preen
Use with Claude Code
/plugin marketplace add gojiplus/preen
/plugin install preen@gojiplus
The bundled skill teaches Claude Code to reach for the preen CLI — scaffolding, adoption, checks, and releases — instead of reimplementing its logic.
Commands
| Command | What it does |
|---|---|
preen new NAME |
Scaffold a new package from the py-canon copier template |
preen adopt [PATH] |
Retrofit an existing repo: mine answers from the repo, copy in the managed files, rewrite [tool.*] in pyproject.toml |
preen update [PATH] |
Pull the latest template changes into an adopted repo (copier update) |
preen check [PATH] |
Run conformance checks (detection only); --strict for CI |
preen fix [CHECK] |
Apply fixes for issues the checks found |
preen release [X.Y.Z] |
Guided release: run checks, confirm, git tag vX.Y.Z, push — the tag triggers the release workflow |
Adopting an existing repo
cd my-package
preen adopt
# review the ADOPTION REPORT, then:
uv lock && uv sync --all-groups
preen check
preen adopt mines the copier answers from the repo itself (name,
description, and authors from pyproject.toml; org from the git remote),
renders the template into a temp directory, and copies in only the managed
files: the CI/docs/release workflow shims, .pre-commit-config.yaml and
dependabot config (if absent), docs/conf.py (old one backed up),
.copier-answers.yml, py.typed, plus LICENSE and CITATION.cff if
missing. It rewrites the [tool.ruff] (preserving any repo-specific lint
ignores already present, and setting target-version from the repo's own
requires-python floor, falling back to py311), [tool.pyright], and
[tool.pydoclint] sections to the standard with tomlkit (comments elsewhere
survive) and deletes legacy [tool.black], [tool.isort], [tool.flake8],
and [tool.mypy] sections.
Pass --release-migration to also convert the build backend to the fleet's
current uv_build series. The minimum is the latest tested release and the upper
bound prevents an unreviewed backend-series upgrade. An existing project.version
is preserved; a legacy dynamic-version project takes its current version from its
latest v* tag.
Checks
preen check runs: template (copier adoption + drift against the latest
py-canon tag), workflows (the four canon workflows are callers of the
reusable workflows, not stale copies), ruff, tests, citation,
changelog (Keep a Changelog structure), deps (deptry), deptree
(circular imports), depgroups (PEP 735 dependency-groups usage),
dropped-args (a parameter a caller accepts but never forwards), audit
(pip-audit over the locked dependencies), ci-matrix (canon shim, or a
matrix covering the requires-python floor), structure, runtime-assets
(schema-bearing data formats, pinned Hugging Face revisions), files
(README and .gitignore exist), precommit (.pre-commit-config.yaml
exists and parses), pytest-config (pytest settings the standard requires), examples (documented examples still name symbols the package defines), python-floor (requires-python meets the floor the standard declares; off until the fleet migration lands), version (hardcoded version strings), license (PEP
639 license metadata), links, metadata (build backend, requires-python
upper bound, PEP 561 py.typed), pydoclint, pyright, and codespell.
Issues carry an impact level: critical blocks release, important can
be overridden with informed consent, info is advisory. preen release
walks that ladder interactively before tagging. Most checks are
detection-only; preen fix license migrates the deprecated
{ text = ... } license table form to an SPDX string where the mapping is
unambiguous, drops redundant License :: classifiers, and adds a missing
license-files entry.
Releasing
The fleet standard keeps one explicit version in pyproject.toml, set with
uv version X.Y.Z; the matching vX.Y.Z tag identifies the release. preen release
runs the checks, then refuses to proceed unless the version is committed in
pyproject.toml, is PEP 440-valid, the tag doesn't already exist, and
CHANGELOG.md has an entry for it (offering to rename [Unreleased] to the new
version and commit that rename if the Unreleased section has content).
It then asks for confirmation, tags vX.Y.Z, and pushes the tag; the repo's
release workflow does the rest (build, PEP 740 attestations, PyPI trusted
publishing, GitHub Release). Use --dry-run to see the plan without
acting.
Configuration
Preen reads an optional [tool.preen] section in pyproject.toml:
[tool.preen]
src_layout = true # expect src/ layout (default: true)
tests_at_root = true # expect tests/ at the repo root (default: true)
examples_at_root = true # expect examples/ at the repo root (default: true)
skip_checks = ["links"] # checks to skip by default
run_doctests = false # execute `>>>` examples too (default: false)
enforce_python_floor = false # gate on the standard's Python floor
# URLs the `links` check should not fetch. Some endpoints are real but do not
# answer a bare GET, such as an API base that needs a path. Skipping the whole
# `links` check to silence one of those would stop the README being checked,
# which is where a broken link actually costs a reader something.
link_ignore = ["https://api.example.com/*"]
# Advisory ids `audit` should report but not fail on, for the case where no
# fixed release exists yet. Still shown as info so the exception stays visible.
audit_ignore = ["GHSA-8mgp-746c-j5xp"]
Notes
pydoclintcovers docstring-signature consistency for now; ruff ships equivalentDOCrules ([tool.ruff.lint] external = ["DOC"]reserves the codes), but they're still preview-only, sopydoclintstays until ruff stabilizes them.codespellstays: ruff has no spelling-check rules.
License
MIT
Release files for preen 0.6.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| preen-0.6.1.tar.gz | 109.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| preen-0.6.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 244.1 kB
Release files / preen-0.6.1.tar.gz
| Download URL | preen-0.6.1.tar.gz |
|---|---|
| Size | 109.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
82dd0e4e096302f95b825cfc802b7179d80c526a7c273dedaa09fbffd9e2b3e9
|
|
BLAKE2b-256 checksum How to use checksums |
34c063a672fa6c206a9aac6d6b7a78046060a8b1c45219dba3d2c153675d7ab0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency logRelease files / preen-0.6.1-py3-none-any.whl
| Download URL | preen-0.6.1-py3-none-any.whl |
|---|---|
| Size | 134.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9ea6a5fa2aa5474089d4a7527952a9ae06ee6756c48227f53f20ac639f757cb9
|
|
BLAKE2b-256 checksum How to use checksums |
d27a77e2e4620d9a7839ca00ff04013400c0c4c592965af9884f66c55f203897
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency log