A Claude Skill that scaffolds a Python
project with the modern toolchain — uv, ruff, mypy, pytest, pre-commit — all
configured in a single pyproject.toml.
Ask Claude "set up a new Python project called foo" and you get a repo that formats, lints, type-checks and tests itself, with no back-and-forth about which config keys go where.
Based on How I Set Up a Python Project in 2026.
This scaffolds the tooling layer for a single Python project — uv, ruff,
mypy, pytest, pre-commit, wired together in one pyproject.toml. It's
domain-agnostic: no framework, no dependencies beyond the five tools above,
just a clean src/<name>/ package that lints, type-checks and tests itself.
| Works well for | Out of scope |
|---|---|
| CLIs, services, libraries, ML/DL experiment code — anything that's one Python package | Installing or configuring a framework (FastAPI, Django, PyTorch, an agent SDK, a desktop GUI toolkit) — add it yourself with uv add after scaffolding |
A single pyproject.toml project |
Monorepos / uv workspace setups — the skill guards against scaffolding into a directory that already has a pyproject.toml, so it won't add a member to an existing workspace |
| Python code | Frontends — no JS/TS/React tooling of any kind |
Concretely: it'll happily set up a FastAPI backend or a PyTorch training repo
— you just add the framework yourself afterward, and the whole project shares
one profile (there's no way to be strict on an API layer
and ml-relaxed on training code within the same scaffold). For an
end-to-end system with a frontend, multiple services, or a monorepo, run this
once per Python package and wire the rest together by hand.
git clone https://github.com/akakritagya/py-init ~/.claude/skills/py-initgit clone leaves a nested .git/ behind, which git treats as an embedded
repo rather than files — commit it as-is and teammates get an empty
directory, not the skill. Pick one of these instead:
# Option A: clone, then drop the nested .git so the files commit normally
git clone https://github.com/akakritagya/py-init .claude/skills/py-init
rm -rf .claude/skills/py-init/.git# Option B: skip git entirely — download and unzip the packaged release
curl -LO https://github.com/akakritagya/py-init/releases/latest/download/py-init.skill
unzip py-init.skill -d .claude/skills/ && rm py-init.skillEither way, git add .claude/skills/py-init afterwards to actually commit it.
Restart Claude Code, then check it loaded by asking what skills are available,
or invoke it directly with /py-init.
This is a plain Agent Skill —
SKILL.md plus a few asset files, nothing Claude-Code-specific. claude.ai
supports the same format (Settings → Capabilities → Skills), so uploading this
folder there works unmodified, as it runs in a sandboxed environment with
bash and file access.
For claude.ai (or anything that takes a packaged upload rather than a git
clone), grab the py-init.skill zip from the
latest release
instead — it's just SKILL.md, assets/, and references/, with the repo's
README, LICENSE and git metadata stripped out.
For any other AI coding agent: it'll work if that agent (a) reads SKILL.md
the way Claude Code and claude.ai do, and (b) gives the model bash and
filesystem tools to run uv, ruff, mypy, etc. If it doesn't support
Skills at all, this won't plug in as-is.
uv on your PATH. Everything else the skill installs itself.
curl -LsSf https://astral.sh/uv/install.sh | sh # macOS / Linux
powershell -c "irm https://astral.sh/uv/install.ps1 | iex" # WindowsOn WSL, install uv inside WSL — a Windows-side uv is invisible to your Linux
shell. Keep projects in the Linux filesystem (~/projects) rather than
/mnt/c/; uv creates a .venv with thousands of small files, and cross-
filesystem I/O will make it feel broken.
The skill asks three questions — project name, profile, Python version — then runs a fixed sequence:
- Guards against scaffolding over an existing project
uv python install+uv init --python(app or lib layout)- Appends the profile's ruff / mypy / pytest config to
pyproject.toml - Substitutes the per-project values that models usually get wrong
- Installs tools unbounded, records bounded pins
- Writes
.pre-commit-config.yaml, runsautoupdate, installs all three hook stages - Writes a
README.mdwith project name, dev setup, and verify setup sections - Greps for leftover placeholders
- Verifies:
ruff check --fix→ruff format→mypy→pytest
| Profile | For | What changes |
|---|---|---|
strict |
libraries, long-lived code | everything on: D docstrings, ANN, mypy strict, warnings-as-errors |
app |
services, CLIs (default) | no global docstring rules |
ml |
experiments, training code | mypy strict = false, no D/ANN, warnings not promoted to errors, data/ excluded |
The ml profile exists because filterwarnings = ["error"] turns every
numpy/torch/sklearn deprecation into a red suite on someone else's release
schedule, and strict typing on experiment code costs more than it catches.
Things that took testing to get right, in case you fork this:
- Config lives in
assets/as literal files, not as prose inSKILL.md. A model that regenerates config from a description will eventually writeexclude_linesinstead ofexclude_also— which replaces coverage's defaults rather than extending them. - Each profile has complete files, not a base plus a diff. Duplication across three directories is cheaper than a model editing TOML at scaffold time.
- Install unbounded, record bounded.
uv add --dev ruffwith no ceiling, so a project scaffolded next year gets next year's tools; then pin>=<resolved>,<<next major>so CI stays reproducible. --pythongoes onuv init, not a separateuv python pinafterwards. Pinning after init hitserror: The requested Python version is incompatible with the project requires-python valueand the recovery leaves.python-versionandrequires-pythondisagreeing.known-first-partytakes the module name, read offsrc/— not what the user typed. Forname-testthe module isname_test, and the hyphenated form means isort silently sorts your own imports into the third-party block.
Most changes belong in assets/, not SKILL.md. Line length, selected rule
sets, coverage excludes — edit the profile file and it takes effect on the next
run.
The ml profile's ignore_missing_imports list (sklearn, scipy, matplotlib,
datasets, transformers, seaborn, statsmodels, networkx, xgboost, lightgbm,
cv2, nltk, tensorflow, keras) is a starting guess. Swap in whatever you
actually import, or it's dead config that could mask a real typo.
See Releases for the
downloadable .skill asset per version.
MIT — see LICENSE.