Feature catalogue¶
What each answer in the questionnaire configures: the project types and their layouts, the toolchain choices, the opt-in layers, the support tiers CI guarantees, and why the option set looks the way it does. The README is the short version of this page.
Recommended settings, per area¶
The template asks a few questions and generates a project tailored to your answers. Besides the essentials (project type, package name, author, ...), each customisable area asks a single "use the recommended settings?" question first (default: yes), with the recommendation spelled out in its help text. Answer yes and that area is configured from its recommended defaults without asking anything else; answer no and the detailed question(s) for that area are asked — the questionnaire reference lists every one.
| Area gate | Recommended default | Asked when |
|---|---|---|
use_recommended_agent |
yes — a plain library / CLI without agent tooling. | library / cli |
use_recommended_toolchain |
uv (package manager) + just (task runner). | not ros2 + ros2_package_manager = pixi |
use_recommended_data_science |
GPU workloads enabled (NVIDIA CUDA Dockerfile + devcontainer). | the data_science layer is present |
use_recommended_polish |
src/ layout (library/cli), no Japanese (multibyte) characters in comments/docstrings. | all |
use_recommended_docs |
zensical (Zensical, an MkDocs fork with mkdocstrings). | all |
use_recommended_quality |
basedpyright (primary) + pyrefly (additional static analysis), strictness "recommended" (ruff ALL rules, typos/vulture/deptry/pip-audit). | all |
use_recommended_license |
MIT license, no FAIR research-software metadata (CITATION.cff / REUSE). | all |
use_recommended_integrations |
no Docker container, no PyPI auto-publish, no cloud provider, no Sentry, no MCP support, GitHub Actions for CI, structlog for logging. | all |
use_recommended_web_api |
a FastAPI app in a top-level app/ package (no library |
the web_api layer is present |
use_recommended_security |
minimal CI permissions, GitHub Actions pinned to commit SHAs (renovate keeps them up to date), zizmor + actionlint checks, a SECURITY.md vulnerability-reporting policy, a test_qa.py that verifies dependency integrity and the public API at runtime, and a license-check task (pip-licenses --fail-on with the project's copyleft policy) that CI runs alongside type-check. | all |
Ask order¶
The branches below follow the order the questions are actually asked in
(copier.yml); each gate's Yes/No branches rejoin before the next gate:
flowchart TD
Start([Start]) --> PT[project_type]
PT -->|ros2| Q0["ask: pkg_language (python / cpp), ros_distro (humble / jazzy),<br/>ros2_package_manager (apt / pixi)"]
PT -->|micropython| Q1["ask: micropython_port (esp32 / esp8266 / rp2 / stm32 / samd / unix /<br/>windows / mimxrt)"]
PT -->|library / cli| G_agent{"use_recommended_agent?<br/>(library / cli)"}
PT -->|other| G_toolchain{"use_recommended_toolchain?<br/>(not ros2 + ros2_package_manager = pixi)"}
Q0 --> G_toolchain
Q1 --> G_toolchain
G_agent -->|Yes| G_agent_yes["a plain library / CLI without agent tooling."]
G_agent -->|No| G_agent_no["add a runnable pydantic-ai example: a prompts/ directory, a typed tools/ package…"]
G_agent_yes --> G_toolchain
G_agent_no --> G_toolchain
G_toolchain -->|Yes| G_toolchain_yes["uv (package manager) + just (task runner)."]
G_toolchain -->|No| G_toolchain_no["ask: package_manager, task_runner, task_runner_pixi"]
G_toolchain_yes --> D2
G_toolchain_no --> D2
D2{"online_judge?"}
D2 -->|Yes| Q7["ask: oj_category (data_science / competitive_coding / ctf), oj_kind<br/>(kaggle / atcoder / leetcode / yukicoder / aoj / codeforces /<br/>kattis / other / ctf), oj_allow_ai (atcoder / leetcode /<br/>codeforces / kattis / other)"]
Q7 --> INC
D2 -->|No| INC
INC["ask: include_data_science, include_web_api, include_ctf,<br/>include_scraping, include_bot<br/>(each only for the bases it combines with)"]
INC --> L3{"include_scraping?"}
L3 -->|Yes| G_scraping{"use_recommended_scraping?<br/>(a cli base answers Yes to include_scraping)"}
L3 -->|No| L4
G_scraping -->|Yes| G_scraping_yes["httpx — a polite stdlib-robots fetcher"]
G_scraping -->|No| G_scraping_no["ask: scraping_engine"]
G_scraping_yes --> L4
G_scraping_no --> L4
L4{"include_bot?"}
L4 -->|Yes| G_bot{"use_recommended_bot?<br/>(a cli / web_api base answers Yes to include_bot)"}
L4 -->|No| L5
G_bot -->|Yes| G_bot_yes["discord = discord.py — the de-facto Discord library"]
G_bot -->|No| G_bot_no["ask: bot_platform"]
G_bot_yes --> L5
G_bot_no --> L5
L5{"data science layer?"}
L5 -->|Yes| G_data_science{"use_recommended_data_science?<br/>(the data_science layer is present)"}
L5 -->|No| G_polish
G_data_science -->|Yes| G_data_science_yes["GPU workloads enabled (NVIDIA CUDA Dockerfile + devcontainer)."]
G_data_science -->|No| G_data_science_no["ask: use_gpu"]
G_data_science_yes --> G_polish
G_data_science_no --> G_polish
G_polish{"use_recommended_polish?"}
G_polish -->|Yes| G_polish_yes["src/ layout (library/cli), no Japanese"]
G_polish -->|No| G_polish_no["ask: layout, allow_japanese"]
G_polish_yes --> G_docs
G_polish_no --> G_docs
G_docs{"use_recommended_docs?"}
G_docs -->|Yes| G_docs_yes["zensical (Zensical, an MkDocs fork with mkdocstrings)."]
G_docs -->|No| G_docs_no["ask: docs_type"]
G_docs_yes --> G_quality
G_docs_no --> G_quality
G_quality{"use_recommended_quality?"}
G_quality -->|Yes| G_quality_yes["basedpyright (primary) + pyrefly"]
G_quality -->|No| G_quality_no["ask: type_checker, strictness"]
G_quality_yes --> G_license
G_quality_no --> G_license
G_license{"use_recommended_license?"}
G_license -->|Yes| G_license_yes["MIT license, no FAIR research-software metadata (CITATION.cff / REUSE)."]
G_license -->|No| G_license_no["ask: license, fair, author_orcid"]
G_license_yes --> G_integrations
G_license_no --> G_integrations
G_integrations{"use_recommended_integrations?"}
G_integrations -->|Yes| G_integrations_yes["no Docker container, no PyPI auto-publish, no cloud provider, no Sentry, no MCP…"]
G_integrations -->|No| G_integrations_no["ask: docker, pypi, cloud_provider, aws_services, include_sentry,<br/>include_mcp, ci_provider, log_library"]
G_integrations_yes --> L6
G_integrations_no --> L6
L6{"web api layer?"}
L6 -->|Yes| G_web_api{"use_recommended_web_api?<br/>(the web_api layer is present)"}
L6 -->|No| G_security
G_web_api -->|Yes| G_web_api_yes["a FastAPI app in a top-level app/ package"]
G_web_api -->|No| G_web_api_no["ask: prometheus, rate_limit, cors"]
G_web_api_yes --> G_security
G_web_api_no --> G_security
G_security{"use_recommended_security?"}
G_security -->|Yes| G_security_yes["minimal CI permissions, GitHub Actions pinned to commit SHAs"]
G_security -->|No| G_security_no["ask: license_check, security_policy, scorecard"]
G_security_yes --> PD
G_security_no --> PD
PD["Project details: package_name, description, git_platform, github_org,<br/>gitlab_group, repo_name, distribution_name, author_name,<br/>author_email, dependencies, src_dirs"]
PD --> End([Generate project])
Package management¶
package_manager picks the dependency manager:
- uv — fast, pure Python package manager (default)
- pixi — conda-based package manager with cross-language support
- poetry — dependency management and packaging with Poetry
Task runner¶
task_runner (and task_runner_pixi when package_manager is pixi) picks the
runner every recipe goes through:
- A task runner of your choice (Just (default) /
Task (go-task) / poethepoet /
Make / pyinvoke /
duty) driving lint / type-check / test / docs — one shared task
definition, invoked by CI too. With
package_manager= pixi, the choices are pixi (native tasks) (default) / Task (go-task) / Just / Make (poethepoet is not offered there).
Project type¶
project_type picks the base — the shape of the repository:
- library — a Python library/package
- web_api — a working FastAPI scaffold in a top-level
app/package (no library<pkg>; run withuvicorn app.main:app): async SQLAlchemy 2.0 + Alembic + Postgres, demo CRUD router, request-id logging,/health+/docs. Deliberately API-only — full-stack needs point to the upstream full-stack-fastapi-template. Shipscompose.local.yml(API + Postgres) and a CI test job backed by a Postgres service container. Optional: Prometheus/metrics, slowapi rate limiting, CORS (see the web-api how-to) - cli — a command-line tool
- data_science:
data/,models/,reports/,notebooks/and asrc/pipeline layout. GPU Dockerfile, a Quarto paper and the de-identification data-sharing gate are always included (see the data-science how-to and the quarto-paper how-to). Ships polars / duckdb / pyarrow as base deps and adata/queries/SQL workspace readable viaduckdb.sql(open("data/queries/example.sql").read()). - online_judge: a competitive-programming / Kaggle project. The follow-up
oj_kindquestion picks the judge: - kaggle: the competition layout
src/{configs,data,input,output,features,logs,models,notebook,scripts,utils}wheresrc/utilsis the installable package, plus the GPU Dockerfile - atcoder / leetcode / yukicoder / aoj / codeforces / kattis / other: a bare code-submission workspace
(stdlib only) — no package or
solutions/tree is generated, and the repo stays empty until a CLI tool creates the per-problem folders andtest/sample files (ojfor AtCoder / yukicoder / Codeforces,accfor AtCoder contests,aoj-clifor AOJ,submit.py+.kattisrcfor Kattis; LeetCode is solved in its own editor,otherusesojwhere the service allows) - script — a minimal script, flat package at the repo root
- ros2 — a ROS 2 package (
ament_pythonwith rclpy, orament_cmakewith C++), built with colcon + rosdep. Choose Humble (Ubuntu 22.04 / Python 3.10, recommended for its wide deployment) or Jazzy (Ubuntu 24.04 / Python 3.12), and provision the environment with apt (classicros-<distro>-*+ industrial_ci) or pixi (RoboStack conda-forge via therobostack-<distro>channel on prefix.dev). Generatespackage.xml,setup.py/CMakeLists.txt,resource/, ament linter tests,Dockerfile.ros2, and a ROS-aware devcontainer. CI runs industrial_ci (apt) or setup-pixi + colcon (pixi). See the ros2 how-to - micropython — MicroPython firmware for a microcontroller (ESP32 / RP2 /
STM32 / ...). Choose the target port (
micropython_port); the firmware lives infirmware/(boot.py,main.py,board_config.py+ a device-independentcore/), is deployed with mpremote, and is type-checked againstmicropython-<port>-stubs(installed into a git-ignoredtypings/folder). The CPython dev toolchain (uv/ruff/pytest/basedpyright) coexists to unit-testcore/. See the MicroPython how-to
Combining bases and layers¶
include_data_science / include_web_api (and the other opt-in layers) add an
element to a combinable base:
project_typepicks the base; two opt-in questions can layer another element on top:include_data_science(asked forlibrary/cli/web_api) adds the analysis layout (notebooks/,data/,models/,reports/, Quarto paper), andinclude_web_api(asked forlibrary/cli/data_scienceand Kaggle) adds the FastAPI scaffold (top-levelapp/). So a data_science base can ship an API, and an API base can ship analysis directories and MCP — in one repo.ros2/micropython/ code-submission judges /scriptstay single-type: their build or execution shape cannot be combined.
Layout¶
layout (asked for library / cli) picks where the package lives:
- src — package in a
src/directory (default; prevents accidental imports of an uninstalled package) - flat — package at the repository root
data_sciencealways usessrc/;online_judgewith thekagglekind usessrc/too (assrc/utils);scriptalways uses flat;web_apialways uses a top-levelapp/package (nosrc/flat question);micropythonandros2don't ask (firmware/ament layouts instead)
AI agent¶
use_recommended_agent (library / cli) adds an agent example or not:
- Recommended: no — a plain library / CLI without agent tooling.
- Answer no to scaffold a runnable pydantic-ai
example: a
prompts/agent.mdsystem prompt, a typedtools/package (tools/example.py) and a module-levelagentwired with@agent.tool.python -m <package>.agent "..."runs offline via pydantic-ai'sTestModel; pass--model openai:gpt-4o-mini(with the matching API key in the environment) for a real model.
Cloud / integrations¶
- Cloud provider (
cloud_provider):none(default) /aws(boto3 + service type stubs) /gcp(google-cloud-storage) /azure(azure-identity). Foraws,aws_servicespicks theboto3-stubsextra (essential/s3/dynamodb/sqs/lambda). - Sentry (
include_sentry): addssentry-sdkand initialises it fromSENTRY_DSNat CLI startup. - MCP (
include_mcp, cli / web_api / the API layer): adds themcp[cli]SDK and scaffolds anmcp_server.pywith typed example tools, aToolErrorsample and a resource, plus amcp-server-<name>console script and an in-process client test — the template's first long-running executable layer (see the layer model and the MCP how-to). The module lives inapp/mcp_server.pywhen the web_api layer is present, otherwise in<pkg>/mcp_server.py. Run it with stdio (an MCP host launchesuv run mcp-server-<name>) or streamable-http (--transport streamable-http), and debug it with the MCP Inspector (uv run mcp dev src/<package>/mcp_server.py). - Logging library (
log_library):structlog(default) /loguru/picologging/logging(standard library, no extra dependency).logging_setup.pyexposes the samelogger.bind(...)/logger.info(event, **fields)call shape regardless of which one is chosen, plus aLOG_FORMAT=jsonconsole/JSON switch. Not asked forros2packages (they use rclpy's own node logger) ormicropythonfirmware (logging runs on the device, not through CPython's logging stack).
Experimentation¶
[project.optional-dependencies] experiment / the pixi experiment feature:
- marimo notebooks, matplotlib / seaborn / plotly for debugging, plus LLM API deps
- Kept separate from the minimal runtime dependencies
License & changelog¶
- License (
license, asked when you opt out ofuse_recommended_license— the recommendation is MIT): the full choosealicense.com list (MIT, Apache-2.0, GPL/LGPL/AGPL, BSD variants, MPL-2.0, ISC, Unlicense, CC0, and more), plus aProprietary/ all-rights-reserved option. Sets theLICENSEfile text,pyproject.toml's PEP 639license/license-files, and the README badge. Regenerated from source viatools/generate_license_template.py. - Changelog: git-cliff generates
CHANGELOG.mdfrom Conventional Commits; commit messages are enforced in CI (a conventional-commits check in the hygiene workflow), and each GitHub Release's notes are generated by git-cliff from that tag's commits. - FAIR / research-software metadata (adapted from
fair-python-cookiecutter):
the
fairoption adds aCITATION.cff(validated in CI, optionalauthor_orcid); CI additionally coversREUSE.tomlwith SPDX annotations — only for open-source licenses, Proprietary projects skip it. It also adds afair-software.ymlGitHub Actions workflow running howfairis to measure compliance with the fair-software.eu recommendations on push tomain. - Data governance (
data_scienceprojects; independent offair): a one-page de-identification protocol (ISO/IEC 20889), a data-transfer-agreement template and a transfer log always ship indata/, so every non-public extract that leaves for another organisation can be traced back to an agreement and an approver. On top of that,data_reusableopts into a DUO (Data Use Ontology) data-use conditions sheet, anddata_ethicsopts into a CARE principles data-governance statement (with provenance & custody records) — both asked under the data-science gate.
Tooling¶
- setuptools + setuptools-scm packaging
- pytest, coverage, hypothesis
- ruff, vulture, deptry, typos
- basedpyright plus pyrefly or ty as the secondary checker
- CI-enforced repo hygiene (secret scanning via gitleaks, actionlint, YAML/EOF checks, conventional commits) in a dedicated workflow
- OpenSSF Scorecard workflow + a SECURITY.md vulnerability-reporting policy
- editorconfig (
.editorconfig) for consistent editor indentation and line endings - A
.env.examplewith the environment variables the project understands (.envis git-ignored and auto-loaded by direnv / the compose stack) - Author/GitHub-org questions have plain defaults (override at any prompt)
- zensical, sphinx or great-docs for docs
- README badge row: CI, coverage, license, a Python-version badge matching the actual CI test matrix, and each tool's own officially documented badge — Ruff and a "Made with Copier" badge (h/t reproML and pypackage-template). No unofficial/inferred tool badges (e.g. uv, pixi have none) — pawamoy/copier-uv, a well-known uv-based copier template, only badges CI/docs/chat for the same reason.
CI/CD¶
- CI provider (
ci_provider):github_actions(default) generates the full GitHub Actions workflow set;noneskips.github/workflows/ - GitHub Actions:
concurrencywithcancel-in-progress, minimalpermissions, and arequired-checks-passedgate for branch protection - Security gate (
use_recommended_security, default yes): GitHub Actions pinned to commit SHAs via renovate (helpers:pinGitHubActionDigests), a zizmor CI job auditing the workflows, a generatedtests/test_qa.py, and — when you opt out (GitHub projects) — the choice of aSECURITY.mdvulnerability policy and an OpenSSF Scorecard workflow (public repos only). GitLab projects keep the hardened.gitlab-ci.ymlbut skip the GitHub-only files (SECURITY.md / Scorecard) - PyPI publishing, Docker containers, docs deployment to GitHub Pages
Support tiers¶
Supported — executed end to end by CI:
| combination | tier |
|---|---|
project_type=library/gate=recommended |
full |
project_type=cli/gate=recommended |
full |
project_type=web_api/gate=recommended |
full |
project_type=data_science/gate=recommended |
full |
project_type=script/gate=recommended |
full |
project_type=micropython/gate=recommended |
full |
project_type=online_judge/gate=recommended/oj=competitive_coding/atcoder |
full |
project_type=cli/gate=off:use_recommended_agent |
full |
Best effort — rendered by CI, but never executed:
| combination | tier |
|---|---|
task_runner = make \| poe \| invoke \| duty |
best_effort |
package_manager = poetry |
best_effort |
log_library = loguru \| picologging |
best_effort |
type_checker = ty |
best_effort |
project_type=ros2 |
best_effort |
project_type=online_judge/.../oj=* except atcoder |
best_effort |
any leaf under an opt-in layer (include_ctf \| include_data_science \| include_scraping \| include_web_api \| include_bot) |
best_effort |
any detailed-question branch (gate=off:use_recommended_*) |
best_effort |
any domain-trait variant (domain_traits selects personal-data / face-recognition / medtech) |
best_effort |
any distribution variant (distribution answers commercial and/or oss) |
best_effort |
Full matrix and the evidence behind each tier: support.md.
Design decisions¶
The option set has been consolidated over time. The key moves:
typing_style→strictness: type-annotation strictness and the static-analysis toolchain are now one axis (none/basic/recommended/full) instead of two loosely-coupled ones.recommended(the default) is the full toolchain used by the author's~/dotfiles/template(private repo): ruff withALLrules, basedpyright + pyrefly, typos / vulture / deptry / pip-audit.is_ds/quarto_paper/use_gpu→project_type: project kind is now one axis (library/web_api/cli/data_science/online_judge/script). The GPU Dockerfile and Quarto paper are always part ofdata_sciencerather than separate toggles.competition(sub-option of data_science) →online_judge+oj_kind=kaggle: a Kaggle-style competition is a competition, not an analysis project — the AI-use rules and the GPU/submission layout differ from plain data science. It now lives under theonline_judgeproject type, sodata_scienceis purely the analysis layout andonline_judgecan also express code-submission judges (AtCoder / LeetCode / yukicoder / AOJ — a bare workspace the user drives withoj/acc/aoj-cli).detail_level→ one "use recommended settings?" gate per area: a single upfront simple/detailed toggle controlled ~20 questions at once, so going off the beaten path for one option (say, the license) meant opting into every other detailed question too. Each customisable area now asks its own yes/no gate, right where that area comes up, with the recommendation spelled out in its help text — see the previous section.- Long-running executables (bots, MCP servers) are a layer, not a
project_type: they run on the same CPython + uv environment aslibrary/cli/web_api, so adding adaemontype would violate the "project_type = fundamentally different execution environment" rule. Instead they are opt-in modules with their own entry point (the first instance isinclude_mcp→mcp_server.pyoncli/web_api, with amcp-server-<name>console script), started by an MCP host or viapython -m <package>.mcp_server— never through__main__.py, which the DockerENTRYPOINTand CLI tests own. The same base+layer idea now coversinclude_data_science/include_web_api: one generated repo can hold a base plus another element (data_science + web_api, web_api + MCP, ...). See the layer model. web_apiis now a working FastAPI scaffold, not a shell: it used to generate Docker/compose/Postgres wiring and tell you to "add fastapi + uvicorn yourself". It now ships the full recommended stack — async SQLAlchemy 2.0 + Alembic + Postgres, a demo CRUD router, request-id logging (asgi-correlation-id),BackgroundTasks, and/health+/docs— with tests that run against SQLite locally and Postgres in CI. The web-API detail gate (use_recommended_web_api) offers exactly three switches (Prometheus /metrics via prometheus-client, slowapi rate limiting, CORS), deliberately not a catalogue; auth, other ORMs, admin UIs and task queues are documented as "add later" (fastapi-users is in maintenance mode, which is why no auth is baked in).
flowchart LR
subgraph before["Before"]
A1[typing_style<br/>none / partial / full / hardline]
A2[is_ds]
A3[quarto_paper]
A4[use_gpu]
A5[competition<br/>sub-option of data_science]
A6[detail_level<br/>simple / detailed, asked once upfront]
end
subgraph after["After"]
B1[strictness<br/>none / basic / recommended / full]
B2[project_type<br/>library / web_api / cli / data_science /<br/>online_judge / script]
B3["online_judge + oj_kind<br/>kaggle (ex-competition) / atcoder /<br/>leetcode / yukicoder / aoj"]
B4["use_recommended_* gates<br/>one per area, asked in place"]
end
A1 --> B1
A2 --> B2
A3 --> B2
A4 --> B2
A5 --> B3
A6 --> B4