new-python-project
Creates <project-name> under the current directory and leaves it with a first commit that passes just check. The templates live beside this file under templates/.
--lib is a package other projects import. --app is a program with an entry point. If the argument names neither, ask which.
uv init --lib --build-backend uv <name> or uv init --app --build-backend uv <name>. The flag names the uv_build backend, and uv writes the version pin its docs recommend. If <name>/.git is missing afterwards, the current directory is inside a repository. Stop and report.
- In the new directory, append
templates/pyproject-tools.toml to pyproject.toml, replacing MODULE with the directory name uv created under src/.
uv add --dev ruff pytest. The exclude-newer appended in step 2 makes uv choose the newest versions that clear the 14-day cooldown.
- Copy
templates/justfile to justfile, templates/pre-commit-config.yaml to .pre-commit-config.yaml, and templates/bca.toml to bca.toml. Append templates/gitignore-tools to .gitignore: the tool caches uv leaves out.
- Write
tests/test_<module>.py from templates/test_smoke.py, with the same MODULE replacement as step 2. pytest fails with no tests, so this test is what lets the first commit pass. Delete it, and its D103 ignore in pyproject.toml, in the commit that adds the first real test: any test that imports the module then catches what it caught.
prek install.
just check. It passes on a fresh scaffold. If it fails, stop and report.
git add the files by name: pyproject.toml uv.lock justfile .pre-commit-config.yaml bca.toml .gitignore .python-version README.md src tests. Commit as "Scaffold ".
From here the project is ordinary johns-way work. The first feature is sized by the routing rules like any other request.
What the defaults do
- ruff. Line length 88,
E501 off because ruff format owns line length, mccabe cyclomatic complexity capped at 10 through C90. Public classes, methods, and functions need docstrings, except the stub uv writes and the smoke test. Tests may use assert, nested with, and temporary paths. Four extra rules flag tests that always pass: a broad raises without a match, a duplicated parametrize row, an assert on a string literal, and a value compared with itself.
- bca.
bca check gates every function in every file type bca reads, Python and anything else in the tree, against the thresholds in bca.toml: cognitive complexity 15, cyclomatic 15, ABC 40, five arguments, five exits, and file size. It honours .gitignore, so .venv is skipped. A vendored file, such as a minified script, goes under [check] exclude in bca.toml.
[tool.uv] exclude-newer. The cooldown for machines without the user-level uv setting, such as CI.
- prek. ruff runs through
uv run, so the hook uses the version in uv.lock. bca is the binary on PATH. The lint commands appear in both the justfile and the hook config. Change both together.
The commit hooks
Git holds one pre-commit hook, so these projects use prek's shim and never johns-way:install-pre-commit. The johns-way commit check still runs just check before every agent commit, and git then runs prek on the staged files. Lint runs twice per agent commit. The tools take a fraction of a second on a small project.
1---2name: new-python-project3description: Scaffold a new Python project with John's defaults. uv with the uv_build backend, ruff configured in pyproject.toml, bca thresholds in bca.toml, a justfile check recipe, and prek hooks that lint on every commit.4---56# new-python-project78Creates `<project-name>` under the current directory and leaves it with a first commit that passes `just check`. The templates live beside this file under `templates/`.910`--lib` is a package other projects import. `--app` is a program with an entry point. If the argument names neither, ask which.11121. `uv init --lib --build-backend uv <name>` or `uv init --app --build-backend uv <name>`. The flag names the uv_build backend, and uv writes the version pin its docs recommend. If `<name>/.git` is missing afterwards, the current directory is inside a repository. Stop and report.132. In the new directory, append `templates/pyproject-tools.toml` to `pyproject.toml`, replacing `MODULE` with the directory name uv created under `src/`.143. `uv add --dev ruff pytest`. The `exclude-newer` appended in step 2 makes uv choose the newest versions that clear the 14-day cooldown.154. Copy `templates/justfile` to `justfile`, `templates/pre-commit-config.yaml` to `.pre-commit-config.yaml`, and `templates/bca.toml` to `bca.toml`. Append `templates/gitignore-tools` to `.gitignore`: the tool caches uv leaves out.165. Write `tests/test_<module>.py` from `templates/test_smoke.py`, with the same `MODULE` replacement as step 2. pytest fails with no tests, so this test is what lets the first commit pass. Delete it, and its `D103` ignore in `pyproject.toml`, in the commit that adds the first real test: any test that imports the module then catches what it caught.176. `prek install`.187. `just check`. It passes on a fresh scaffold. If it fails, stop and report.198. `git add` the files by name: `pyproject.toml uv.lock justfile .pre-commit-config.yaml bca.toml .gitignore .python-version README.md src tests`. Commit as "Scaffold <name>".2021From here the project is ordinary johns-way work. The first feature is sized by the routing rules like any other request.2223## What the defaults do2425- **ruff.** Line length 88, `E501` off because `ruff format` owns line length, mccabe cyclomatic complexity capped at 10 through `C90`. Public classes, methods, and functions need docstrings, except the stub uv writes and the smoke test. Tests may use `assert`, nested `with`, and temporary paths. Four extra rules flag tests that always pass: a broad `raises` without a match, a duplicated parametrize row, an assert on a string literal, and a value compared with itself.26- **bca.** `bca check` gates every function in every file type bca reads, Python and anything else in the tree, against the thresholds in `bca.toml`: cognitive complexity 15, cyclomatic 15, ABC 40, five arguments, five exits, and file size. It honours `.gitignore`, so `.venv` is skipped. A vendored file, such as a minified script, goes under `[check] exclude` in `bca.toml`.27- **`[tool.uv] exclude-newer`.** The cooldown for machines without the user-level uv setting, such as CI.28- **prek.** ruff runs through `uv run`, so the hook uses the version in `uv.lock`. bca is the binary on PATH. The lint commands appear in both the justfile and the hook config. Change both together.2930## The commit hooks3132Git holds one pre-commit hook, so these projects use prek's shim and never `johns-way:install-pre-commit`. The johns-way commit check still runs `just check` before every agent commit, and git then runs prek on the staged files. Lint runs twice per agent commit. The tools take a fraction of a second on a small project.