Skip to content

How sillo-start detects and drives uv, pip, and the frontend managers: the adapter interface, the preference order, and why an existing lockfile wins.

Sillo Start prefers uv. It never requires one.

Each manager is a small adapter that knows how to phrase add, remove, install and run for its own tool, so the calling code expresses intent (“install this project’s dependencies”) and never branches on which tool is in use.

Two are supported:

addsyncrun
uvuv add pkguv syncuv run <cmd>
pippip install pkgpip install -e .(none)
from sillo_start.utils.pkgmanagers import detect_python_manager
detect_python_manager() # UvManager if uv is on PATH, else PipManager

uv if it is on your PATH, pip otherwise. There is no configuration and no prompt. The preference is fixed, and the fallback always exists.

This is what --install uses, and what decides how the next steps are phrased.

uv add edits pyproject.toml and the lockfile itself. pip install does not touch either.

So when pip is the manager, whatever is adding a dependency has to write the manifest entry too, and when uv is, it must not, or the dependency is recorded twice. The adapters carry that distinction rather than leaving it to each caller to remember.

They work fine with a project Sillo Start created. It produces a standard PEP 621 pyproject.toml, which any of them can take over.

What is not supported is Sillo Start driving them. Two adapters is what it takes to cover “the fast one” and “the one that is always there”, and every further adapter is a code path that has to keep working without being the one anybody exercises.

To use Poetry with a created project:

Terminal window
sillo-start create-app myapp
cd myapp
rm uv.lock
poetry install
poetry run sillo db:migrate

The uv.lock goes because it pins a resolution Poetry did not make; the pyproject.toml stays exactly as it is.

Four are supported, for the Inertia starter and anything else with a package.json:

LockfileInstallAdd
bunbun.lockbbun installbun add [-d]
pnpmpnpm-lock.yamlpnpm installpnpm add [-D]
npmpackage-lock.jsonnpm installnpm install [--save-dev]
yarnyarn.lockyarn installyarn add [--dev]
from sillo_start.utils.pkgmanagers import detect_frontend_manager
detect_frontend_manager(project_dir)

If the directory has a lockfile, that manager is used, even if a preferred one is also installed. A project with pnpm-lock.yaml keeps using pnpm on a machine that happens to have bun.

That rule exists because the alternative is a second lockfile in the repository, which is how two developers end up with different dependency trees and one of them has a bug the other cannot reproduce.

With no lockfile, preference order applies: bun, pnpm, npm, yarn, first one installed. With none installed it returns npm, on the reasoning that npm ships with Node and the failure will be clear.

Both hierarchies are small and public, so a project can use them for its own tooling:

from pathlib import Path
from sillo_start.utils.pkgmanagers import python_manager, frontend_manager
uv = python_manager("uv")
uv.available() # is it installed?
uv.add_command(["httpx"], group="dev") # ['uv', 'add', 'httpx', '--group', 'dev']
uv.add(["httpx"], cwd=Path("."), group="dev") # build it and run it
bun = frontend_manager("bun")
bun.run_command("build") # ['bun', 'run', 'build']

Every adapter has both a *_command method that builds the argument list and a method that runs it. The split is what makes the whole thing testable without a subprocess: a test asserts on the command, and only integration tests actually execute one.

An unknown name raises ToolNotFoundError and lists the ones it knows:

Unknown Python package manager 'poetry'. Known: pip, uv.

When --install fails, the manager’s own output is printed and the exit code is 1:

✗ uv exited with code 1.
× No solution found when resolving dependencies:
╰─▶ Because myapp depends on sillo-framework>=9.0 …

The resolver’s message is the useful one. Reprinting it beats replacing it with “installation failed”, and the project is still on disk either way. Fix the cause and install by hand.