Context
nitrostack-py init copies nitrostack/templates/{starter,pizzaz,flight-booking}/ into the new folder (nitrostack/cli/main.py init_project → shutil.copytree).
After init, that folder has no pyproject.toml. The only dependency file is requirements.txt, and it is a single unpinned line:
Same for all three templates:
nitrostack/templates/starter/requirements.txt
nitrostack/templates/pizzaz/requirements.txt
nitrostack/templates/flight-booking/requirements.txt
The TypeScript CLI ships a real package.json with @nitrostack/core / @nitrostack/cli (see typescript/packages/cli/templates/typescript-starter/package.json). Python apps are not a first-class installable project.
This blocks:
nitrostack-py install editable install (install.py uses -e . only if pyproject.toml exists; otherwise pip install -r requirements.txt)
nitrostack-py validate (looks for [project].dependencies or requirements)
nitrostack-py upgrade (prefers rewriting pyproject)
nitrostack-py pack (has to synthesize a pyproject from _PYPROJECT_TEMPLATE in nitrostack/cli/pack.py because the app never had one)
- uv / IDEs / GitHub that expect a PEP 621 project
A follow-up issue covers uv.lock, .python-version, and uv.toml. This issue is pyproject.toml only. Do not block on uv being installed.
SDK itself already has pyproject.toml (requires-python = ">=3.10", setuptools). Do not change the SDK package layout except tests/CLI substitution. Work from develop.
Goal
Every nitrostack-py init project is a PEP 621 project: pyproject.toml with name, version, description, requires-python, and dependencies = ["nitrostack"] (plus template-specific deps if any). Init must substitute the project name/description/author collected in the existing prompts.
Keep requirements.txt in sync or generate it from pyproject so validate does not report a spec mismatch (validators.py compares both when both exist). Simplest: pyproject is source of truth; requirements.txt is nitrostack with the same spec, or drop shipping requirements if validate/install still work with pyproject only (they do). Prefer keeping a one-line requirements.txt matching [project].dependencies so older docs/pack still see it.
Probe
rg -n "requirements.txt|pyproject.toml" nitrostack/templates nitrostack/cli/main.py nitrostack/cli/install.py nitrostack/cli/validators.py nitrostack/cli/upgrade.py nitrostack/cli/pack.py
ls nitrostack/templates/starter nitrostack/templates/pizzaz nitrostack/templates/flight-booking
cat nitrostack/templates/starter/requirements.txt
Read:
nitrostack/cli/main.py — init_project (copytree, .env substitution for SERVER_DESC / SERVER_AUTHOR; no pyproject rewrite today)
nitrostack/cli/install.py — pyproject vs requirements branch
nitrostack/cli/pack.py — _PYPROJECT_TEMPLATE (reuse fields: setuptools, requires-python = ">=3.10")
tests/test_cli.py — _mini_project still uses requirements-only; test_cli_install.py
Confirm init does not currently replace {name} in copied files other than .env.
Implementation
- Add
pyproject.toml to all three templates. Minimal shape:
[build-system]
requires = ["setuptools>=61.0.0"]
build-backend = "setuptools.build_meta"
[project]
name = "nitrostack-starter" # pizzaz / flight-booking: distinct default names
version = "0.1.0"
description = "..."
requires-python = ">=3.10"
dependencies = ["nitrostack"]
[tool.setuptools.packages.find]
where = ["."]
namespaces = true
exclude = ["tests*", ".venv*", "venv*", "node_modules*", "widgets*"]
Use a placeholder the init step rewrites, e.g. name = "PROJECT_NAME" or a small token __PROJECT_NAME__, or write the file in init_project after copytree (like .env). Init already has name, description, author. PEP 503/508: project name must be a valid distribution name (hyphenate, lowercase). If the folder is My App, normalize.
-
Do not add fake extra runtime deps. Flight-booking OAuth comes through nitrostack. If a template imports something not provided by nitrostack, declare it (grep ^import |^from in that template).
-
Keep requirements.txt aligned with [project].dependencies (same pin/range). Unpinned nitrostack is OK if pyproject is also unpinned; better: nitrostack>=<current SDK major> if you already have that pattern in upgrade tests (nitrostack>=2.0 in tests/test_cli.py). Match whatever upgrade writes today.
-
pack should use the project’s pyproject when present instead of always synthesizing _PYPROJECT_TEMPLATE (if that is a one-line change; otherwise leave pack as-is and only ensure init ships pyproject).
-
Do not require uv in this issue. Do not add uv.lock here (follow-up).
Test / verify
pytest tests/test_cli.py tests/test_cli_install.py -q
Add:
- After copying a template dir (or calling
init_project with mocked prompts), assert pyproject.toml exists and [project].dependencies contains nitrostack.
install_dependencies(cwd=that_dir) takes the pyproject path (-e .), not only -r requirements.txt. Mock pip like tests/test_cli_install.py.
validate on a fresh template tree does not say nitrostack is missing.
- Parametrize all three template names:
python-starter, python-pizzaz, python-oauth.
- Init substitution: generated
name = in pyproject matches the project directory name (normalized).
Manual: nitrostack-py init /tmp/ns-uv-probe --template python-starter (non-interactive if flags exist; otherwise unit-test the copy+rewrite function). Folder must contain pyproject.toml. python -m pip install -e /tmp/ns-uv-probe must see a name/version (can mock).
Review
- Templates stay copyable; no SDK publish change.
- Name normalization is documented (what happens to spaces/uppercase).
- requirements.txt and pyproject do not disagree (validate’s mismatch check).
Success
Related
Follow-up: uv.lock / .python-version / uv.toml (file separately). Independent of mcp-2026 issues #22–#27.
Context
nitrostack-py initcopiesnitrostack/templates/{starter,pizzaz,flight-booking}/into the new folder (nitrostack/cli/main.pyinit_project→shutil.copytree).After init, that folder has no
pyproject.toml. The only dependency file isrequirements.txt, and it is a single unpinned line:Same for all three templates:
nitrostack/templates/starter/requirements.txtnitrostack/templates/pizzaz/requirements.txtnitrostack/templates/flight-booking/requirements.txtThe TypeScript CLI ships a real
package.jsonwith@nitrostack/core/@nitrostack/cli(seetypescript/packages/cli/templates/typescript-starter/package.json). Python apps are not a first-class installable project.This blocks:
nitrostack-py installeditable install (install.pyuses-e .only ifpyproject.tomlexists; otherwisepip install -r requirements.txt)nitrostack-py validate(looks for[project].dependenciesor requirements)nitrostack-py upgrade(prefers rewriting pyproject)nitrostack-py pack(has to synthesize a pyproject from_PYPROJECT_TEMPLATEinnitrostack/cli/pack.pybecause the app never had one)A follow-up issue covers
uv.lock,.python-version, anduv.toml. This issue is pyproject.toml only. Do not block on uv being installed.SDK itself already has
pyproject.toml(requires-python = ">=3.10", setuptools). Do not change the SDK package layout except tests/CLI substitution. Work fromdevelop.Goal
Every
nitrostack-py initproject is a PEP 621 project:pyproject.tomlwithname,version,description,requires-python, anddependencies = ["nitrostack"](plus template-specific deps if any). Init must substitute the project name/description/author collected in the existing prompts.Keep
requirements.txtin sync or generate it from pyproject sovalidatedoes not report a spec mismatch (validators.pycompares both when both exist). Simplest: pyproject is source of truth;requirements.txtisnitrostackwith the same spec, or drop shipping requirements if validate/install still work with pyproject only (they do). Prefer keeping a one-linerequirements.txtmatching[project].dependenciesso older docs/packstill see it.Probe
rg -n "requirements.txt|pyproject.toml" nitrostack/templates nitrostack/cli/main.py nitrostack/cli/install.py nitrostack/cli/validators.py nitrostack/cli/upgrade.py nitrostack/cli/pack.py ls nitrostack/templates/starter nitrostack/templates/pizzaz nitrostack/templates/flight-booking cat nitrostack/templates/starter/requirements.txtRead:
nitrostack/cli/main.py—init_project(copytree,.envsubstitution forSERVER_DESC/SERVER_AUTHOR; no pyproject rewrite today)nitrostack/cli/install.py— pyproject vs requirements branchnitrostack/cli/pack.py—_PYPROJECT_TEMPLATE(reuse fields: setuptools,requires-python = ">=3.10")tests/test_cli.py—_mini_projectstill uses requirements-only;test_cli_install.pyConfirm init does not currently replace
{name}in copied files other than.env.Implementation
pyproject.tomlto all three templates. Minimal shape:Use a placeholder the init step rewrites, e.g.
name = "PROJECT_NAME"or a small token__PROJECT_NAME__, or write the file ininit_projectafter copytree (like.env). Init already hasname,description,author. PEP 503/508: projectnamemust be a valid distribution name (hyphenate, lowercase). If the folder isMy App, normalize.Do not add fake extra runtime deps. Flight-booking OAuth comes through
nitrostack. If a template imports something not provided by nitrostack, declare it (grep^import |^fromin that template).Keep
requirements.txtaligned with[project].dependencies(same pin/range). Unpinnednitrostackis OK if pyproject is also unpinned; better:nitrostack>=<current SDK major>if you already have that pattern in upgrade tests (nitrostack>=2.0intests/test_cli.py). Match whateverupgradewrites today.packshould use the project’s pyproject when present instead of always synthesizing_PYPROJECT_TEMPLATE(if that is a one-line change; otherwise leave pack as-is and only ensure init ships pyproject).Do not require
uvin this issue. Do not adduv.lockhere (follow-up).Test / verify
Add:
init_projectwith mocked prompts), assertpyproject.tomlexists and[project].dependenciescontainsnitrostack.install_dependencies(cwd=that_dir)takes the pyproject path (-e .), not only-r requirements.txt. Mock pip liketests/test_cli_install.py.validateon a fresh template tree does not say nitrostack is missing.python-starter,python-pizzaz,python-oauth.name =in pyproject matches the project directory name (normalized).Manual:
nitrostack-py init /tmp/ns-uv-probe --template python-starter(non-interactive if flags exist; otherwise unit-test the copy+rewrite function). Folder must containpyproject.toml.python -m pip install -e /tmp/ns-uv-probemust see a name/version (can mock).Review
Success
pyproject.tomlnitrostack-py installon a fresh init uses editable pyproject installvalidate/upgradework on the generated treeRelated
Follow-up: uv.lock /
.python-version/uv.toml(file separately). Independent of mcp-2026 issues #22–#27.