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 itself requires Python >=3.12, stricter than the >=3.11 floor of the fleet standard it enforces on other repos — the tool can hold itself to a higher bar than the standard it applies.
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 hatchling +
uv-dynamic-versioning, so the git tag becomes the version.
Checks
preen check runs: template (copier adoption + drift against the latest
py-canon tag), ruff, tests, citation, changelog (Keep a Changelog
structure), deps (deptry), deptree (circular imports), depgroups (PEP
735 dependency-groups usage), audit (pip-audit over the locked
dependencies), ci-matrix (canon shim, or a matrix covering the
requires-python floor), structure, version (hardcoded version strings),
license (PEP 639 license metadata), links, metadata (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 derives versions from git tags (uv-dynamic-versioning) —
no bump commits. preen release runs the checks, then refuses to proceed
unless the version 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)
skip_checks = ["links"] # checks to skip by default
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
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file preen-0.2.0.tar.gz.
File metadata
- Download URL: preen-0.2.0.tar.gz
- Upload date:
- Size: 133.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
62f0caffb70be1be07003038e848752e8ff4d041e48a20b311dea5352678413d
|
|
| MD5 |
b8da2abc8faca05726121f744b588427
|
|
| BLAKE2b-256 |
bdbc8dfe58937b42a8a7f565326a419d9baace65a189dfb0e79bf679f9a0521a
|
Provenance
The following attestation bundles were made for preen-0.2.0.tar.gz:
Publisher:
release.yml on gojiplus/preen
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
preen-0.2.0.tar.gz -
Subject digest:
62f0caffb70be1be07003038e848752e8ff4d041e48a20b311dea5352678413d - Sigstore transparency entry: 2238751468
- Sigstore integration time:
-
Permalink:
gojiplus/preen@a01bdb897e118744a3e6958882ed4fa55467b76e -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/gojiplus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a01bdb897e118744a3e6958882ed4fa55467b76e -
Trigger Event:
push
-
Statement type:
File details
Details for the file preen-0.2.0-py3-none-any.whl.
File metadata
- Download URL: preen-0.2.0-py3-none-any.whl
- Upload date:
- Size: 66.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5913ae2b32e68b4d41cdc64fd1aa7298f3797f9538977b18c6a45c93647affb3
|
|
| MD5 |
5f0858a1fac3c54eb248f543d73b8288
|
|
| BLAKE2b-256 |
27f66c5e2f042063a07836f7db03d23428dbb8f1524950157effe66aaff98e0b
|
Provenance
The following attestation bundles were made for preen-0.2.0-py3-none-any.whl:
Publisher:
release.yml on gojiplus/preen
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
preen-0.2.0-py3-none-any.whl -
Subject digest:
5913ae2b32e68b4d41cdc64fd1aa7298f3797f9538977b18c6a45c93647affb3 - Sigstore transparency entry: 2238751914
- Sigstore integration time:
-
Permalink:
gojiplus/preen@a01bdb897e118744a3e6958882ed4fa55467b76e -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/gojiplus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a01bdb897e118744a3e6958882ed4fa55467b76e -
Trigger Event:
push
-
Statement type: