Tool-agnostic guide for any coding agent (Codex, Cursor, Claude Code, or another) working in this repo. AGENTS.md is the one standard: an agent either reads it or it does not — the repo carries no per-tool shim files (CLAUDE.md, .cursor/rules/, etc.). A tool that ignores AGENTS.md is a limitation of that tool, not something the repo works around.
Humans are the first developers. README.md outranks this file. It is the human-facing description of what this template provides and how to use it. This file holds only agent-specific operational hints: how to navigate, build, and run the repo.
A Python application template using the src layout, uv for dependency resolution, ruff for lint and format, and pyrefly for type checking. The sample code is a demonstration seam, not a feature set.
- Everything here is inherited wholesale by every project generated from it. A dependency added here is a dependency every generated project carries, so add one only when the template itself needs it.
- Application code lives under
src/. Nothing is imported from the repository root. pyproject.tomlis the single source of truth for dependencies and tool configuration. There is norequirements.txt, nosetup.py, and no per-tool dotfile.uv.lockis committed and authoritative. Change dependencies throughuv add/uv remove, never by hand-editing the lock file.
- The task runner is
mise(rootmise.toml); commands aremise run <task>.mise run setupinstalls the git hooks. - Tools are pinned and installed by
mise; a shell with mise inactive resolves a bare tool call (python,uv,ruff, …) fromPATH, at an unpinned version.mise run <task>activates the toolchain for that task's duration, a bare tool call does not. - Run anything Python through
uv run, which resolves the locked environment. A barepython script.pyuses whatever interpreterPATHoffers and a different dependency set. mise run actreplays the pull request workflow locally with act. It reads secrets from.env— copy.env.examplefirst.- Before finishing a change, run
mise run checkto format, lint, typecheck, and test;mise run format/lint/typecheck/testrun each individually.
ruffis the arbiter, and it runs withselect = ["ALL"]. Every rule is on unless pyproject.toml names it inignore, with a comment saying why.- Never silence a rule with a bare
# noqa. Fix the code, or annotate the single line with the specific rule code and a reason. A rule that is wrong for the whole project belongs in theignorelist, with its justification. - Type hints are mandatory on every function signature;
pyreflychecks them inmise run typecheck. - Docstrings follow the ruff
Drules — every module, class, and public function has one. - Tests use
pytestand live intests/, mirroring thesrc/layout.
- Conventional Commits, enforced.
cog verifyruns oncommit-msgandcog checkonpre-push, so a malformed message is rejected locally before CI sees it. - Commit messages are a title only — no body, no footer.
- Never push unless asked to.