CLI
The Stanza command-line verbs, flags, and environment variables.
Stanza ships six verbs against the category taxonomy. Run any verb with --help for its full flag list:
npx stanza-cli add --helpinit
Scaffold a new monorepo. Without flags it launches an interactive wizard; with --yes it takes every pick from flags.
npx stanza-cli init [name] --yes --framework=next --orm=drizzle --db=postgres --pm=pnpmname— project directory name (positional; prompted if omitted).--yes— non-interactive; take selections from category flags.--<category>=<ids>— pick modules for a category. There's one flag per category in the registry (--framework,--ui,--db,--orm,--auth,--ai,--payments,--email,--tooling,--testing,--deploy,--monorepo, …); single-choice categories take one id, multi-choice categories take a comma-separated list. Omitted categories are skipped —--yeschooses no defaults.--pm=<pnpm|bun|npm>— package manager recorded in the manifest.
init always scaffolds a single web app today — { id: "web", dir: "apps/web", kind: "web" } in stanza.json — and tags every app-home record with apps: ["web"]. Multi-app init (scaffolding apps/native alongside apps/web, for example) is planned; the schema and runtime already support it. See Concepts → Apps.
add
Add one module to an existing project.
npx stanza-cli add <category> <module> [--app=<id>]Resolves peers, selects the matching adapter, and writes the module's templates, deps, env, and scripts to the right home. If an apply step fails partway through — including inside a codemod — Stanza rolls the changes back, so a failed add leaves your tree as it was. Adding a module to a single-choice category that's already filled fails until you remove the existing one — and that limit is per app for home: "app" categories (so picking a framework for one app doesn't block a different framework for another).
--app=<id>— pick which app the install targets. Required for app-relevant modules when the project has multiple apps and you're not running inside one of them. Single-app projects auto-target; multi-app projects also auto-pick whencwdis inside one app's directory. Interactive runs (TTY, no--yes) fall back to a prompt; non-interactive runs error out asking for the flag.- The flag is meaningless for
home: "repo"modules liketooling.
remove
Remove a module and sweep the files (regions) it owns.
npx stanza-cli remove <category> [id] [--app=<id>]For single-choice categories the id is optional; for multi-choice categories (like testing) it's required. --app=<id> scopes removal in projects with multiple apps — without it, remove looks across every app for a matching record.
The package-dir sweep (when removing the last module under packages/<dir>/) strips the workspace:* dep from every app's package.json, not just the first one.
list
Print installed modules, grouped by category, from the nearest stanza.json.
npx stanza-cli listsearch
List registry modules and their category/id pairs. Pass a query to filter.
npx stanza-cli search [query]Use the printed id (not the display label) when passing a module to add.
doctor
Check stanza.json against the filesystem and report drift, without changing anything.
npx stanza-cli doctorWalks every region claim in the manifest and verifies it still holds on disk — claimed files exist, claimed dependencies/scripts/env vars are still present, and each internal package with claims is wired into its consuming apps. Read-only; exits non-zero when it finds drift, so it slots into CI or a pre-commit check. It doesn't repair anything — use add / remove for that.
Planned: swap (replace a module with another in the same category) and update (re-pull a
module at a newer version) are on the roadmap. The manifest already reserves the fields they need;
the verbs aren't implemented yet.
Global flags
These apply to the mutating verbs (init, add, remove):
--dry-run— print the actions that would be taken and write nothing.--dangerously-allow-dirty— allow a mutating command to run with a dirty git working tree. By default Stanza refuses, so its edits never mix with uncommitted changes. Commit or stash first when you can.--no-telemetry— disable anonymous usage events for this invocation.
Environment variables
STANZA_REGISTRY=<url-or-path>— override the@stanzadefault namespace's source: the full URL or filesystem path to a registry's main JSON file (not a directory). For per-namespace third-party registries, use theregistriesfield instanza.jsoninstead — see Third-party registries.STANZA_NO_NPM_LOOKUP=1— skip npm version lookups and write dependency ranges verbatim.STANZA_NPM_REGISTRY=<url>— override the npm registry used for version lookups.STANZA_TELEMETRY=0/DO_NOT_TRACK=1— disable telemetry persistently. Telemetry is also auto-skipped in CI.STANZA_TELEMETRY_URL=<url>— point telemetry at a self-hosted ingest endpoint instead ofhttps://stanza.tools/api/events.
Telemetry
Stanza captures a small set of anonymous events to help us see which modules people actually pick. The aggregates are surfaced publicly on the Stats page.
What's sent
- The command name (
init,add,remove,list,search,doctor), its duration in milliseconds, and whether it succeeded. - CLI version, Node version, OS, and architecture.
- For installs and removes: the module id, its category, and the namespace it came from (e.g.
@stanzaor@acme). - An ephemeral UUID generated per process so events from the same run can be grouped. It's regenerated next time and never persisted.
Third-party modules
Third-party module installs are counted in the aggregate totals at the top of the Stats page (so adoption of Stanza-as-a-platform shows up), but they're filtered out of the per-category leaderboards underneath. A private @acme/auth-internal doesn't outrank @stanza/better-auth in a public ranking.
What's never sent
- File paths, project names, the contents of templates or env files, or anything else that could identify a project or person.
- Your IP address — the CLI posts to a server-side proxy on
stanza.toolsthat hands off to PostHog without forwarding the request IP. - Anything from CI environments. The CLI auto-detects
CI,GITHUB_ACTIONS,GITLAB_CI,CIRCLECI, andBUILD_NUMBER.
Opting out
- One invocation:
--no-telemetry. - Persistently:
STANZA_TELEMETRY=0orDO_NOT_TRACK=1in your shell.
The implementation is a single file: apps/cli/src/lib/telemetry.ts.
Dependency versioning
On init and add, Stanza bumps each ^/~ dependency range to the latest npm version that satisfies it, keeping the modifier. Other ranges and workspace:* specifiers are written as-is. When offline, it falls back to the range declared in the module.