9.9 KiB
name, description
| name | description |
|---|---|
| stanza-cli | Use the npm-distributed Stanza CLI non-interactively to scaffold or modify modular full-stack TypeScript monorepos. Use when a user asks an agent to run `stanza`, `create-stanza`, `npm create stanza`, add/remove/list/search Stanza modules, generate a Stanza project in CI, or work with a Stanza-based TypeScript monorepo. |
Stanza CLI
Use only the published CLI surface. Do not assume the Stanza source repo, local registry/modules, maintainer scripts, or workspace packages are available unless the user explicitly provides them.
For depth this skill doesn't cover (the concept/region model, the full registry roadmap, and module authoring) fetch the complete docs at https://stanza.tools/docs/llms-full.txt when you have network access. For the current set of installable modules, stanza search is authoritative; the roadmap doc lags it.
Runtime Contract
- The CLI package is
stanza-cli; the binary isstanza. - The create package is
create-stanza; package-manager create commands forward tostanza init. - Prefer direct npm execution in automation because it avoids package-manager argument-forwarding ambiguity:
npx -y stanza-cli@latest --help
npx -y stanza-cli@latest init my-app --yes --framework=next
- If the user wants create-style commands, use the correct separator for their package manager:
pnpm create stanza my-app --yes --framework=next
npm init stanza -- my-app --yes --framework=next
bun create stanza my-app --yes --framework=next
Non-Interactive Workflow
- Check the CLI surface before making assumptions:
npx -y stanza-cli@latest --help
npx -y stanza-cli@latest search
-
Discover current module IDs with
stanza search [query]. Registry contents evolve, so do not hardcode a module unless the user specified it orsearchconfirms it. -
Scaffold with
init --yes, passing every category you want explicitly:
npx -y stanza-cli@latest init my-app --yes \
--framework=next \
--ui=tailwind \
--db=postgres \
--orm=drizzle \
--auth=better-auth \
--testing=vitest,playwright \
--pm=pnpm
- Install dependencies using the generated manifest's package manager, then run the app's normal scripts:
cd my-app
pnpm install
pnpm dev
Commands
stanza init [name] --yes ...scaffolds a new project in a child directory of the current working directory. Today everyinitproduces a single web app (apps/web, idweb); multi-app init is planned but not yet exposed.stanza add <category> [[@<ns>/]<module>] [--app=<id>]adds one module to an existing Stanza project. Always pass an explicitmoduleid in automation: omitting it (or passing an unknown id) errors non-interactively with the list of available ids for that category.--app=<id>picks which app receives an app-scoped module (framework/testing) or a package-scoped module's shims (e.g. Better Auth's route handler); single-app projects auto-target, and a multi-app project errors without--appunlesscwdis inside an app'sdir. Prefix the id with@<ns>/to install from a third-party registry declared underregistriesinstanza.json.stanza remove <category> [[@<ns>/]<id>] [--app=<id>]removes a module. Theidis optional for single-choice categories. For multi-choice categories, omit it on a TTY to pick from installed records, but it is required in non-interactive mode (where omitting it errors with the installed list).--appscopes removal in multi-app projects. The@<ns>/prefix is accepted but optional —removematches against the storedid.stanza listprints installed modules grouped by category from the neareststanza.json.stanza search [query]lists registry modules and theircategory/idpairs.stanza doctorchecksstanza.jsonagainst the filesystem for drift (claimed files/deps/scripts/env vars still present, internal packages wired) and reports issues. Read-only; exits non-zero when drift is found.
Run add, remove, list, and doctor from the project root or any child directory containing a parent stanza.json.
Categories
Every module belongs to exactly one category, and each category is either single-choice or multi-choice:
- Single-choice categories:
framework,api,ui,db,orm,auth,payments,email,ai,tooling,monorepo. Adding a module to a filled single-choice category fails until the existing one is removed. - Multi-choice categories:
testing,deploy. Multiple modules coexist in one category.
For init --yes, pass each category's module ids as a comma-separated value; single-choice categories take exactly one, multi-choice take several — for example --framework=next and --testing=vitest,playwright.
--yes never chooses defaults for omitted categories. Missing selections are skipped.
Dependency Versions
init/addbump each^/~dep range to the latest npm version satisfying it (keeping the modifier); other ranges andworkspace:*are written as-is. Falls back to the declared range when offline.STANZA_NO_NPM_LOOKUP=1skips lookups (verbatim ranges);STANZA_NPM_REGISTRY=<url>overrides the npm registry (must behttps://; remotehttp://is refused — see Registries).
Safety Flags
- Use
--dry-runbefore a mutating command when the user wants a preview. It writes nothing. Foraddit prints a grouped plan of every file it would create, modify, or skip (skips show the reason, e.g. a dependency the user already pins higher), including the source files its codemods would edit. Because it rehearses codemods against your source, a dry run can surface blockers (like a missing root layout) before any real apply. A realaddprints the same created/modified/skipped tally as a one-line summary. - Mutating commands refuse to run in a dirty git worktree. Ask the user before using
--dangerously-allow-dirty; it intentionally allows Stanza edits to mix with existing changes. - A failed
addrolls back automatically — if any step throws (including a codemod), Stanza restores the worktree to its pre-addstate.
Registries
Stanza ships modules from the first-party @stanza namespace by default. To install modules from another publisher, declare the registry in stanza.json#registries and address its modules with @<scope>/<id> syntax.
Point a registry at the full URL of its main JSON file — the index, which lists every module and the relative path to its full JSON. The string form is that URL:
{
"registries": {
"@acme": "https://reg.acme.example/index.json"
}
}
stanza add testing @acme/cosmos
Editing stanza.json#registries is an expected user-driven edit (the rest of the manifest stays runner-managed). For auth or query params, use the object form:
{
"registries": {
"@acme": {
"url": "https://api.acme.example/r/registry.json",
"headers": { "Authorization": "Bearer ${ACME_TOKEN}" },
"params": { "channel": "stable" }
}
}
}
url is the full URL to the main JSON file (any filename — module paths in it are resolved relative to it; there is no {category}/{id} templating or directory convention). ${ENV_VAR} expansion runs against process.env: unset vars in headers silently drop the header; unset vars in params are a hard error. Unknown namespaces fail fast — no implicit fallback to @stanza. @stanza itself is reserved and rejected if redeclared under registries.
STANZA_REGISTRY=<url-or-path> overrides the @stanza namespace's source only — set it to the full URL or filesystem path to a registry's main JSON file (not a directory), for a self-hosted mirror, air-gapped install, or CI fixture. It does NOT enable third-party modules.
Cleartext is refused. Registry and npm payloads carry no integrity check beyond TLS, so a remote http:// endpoint — the @stanza default, STANZA_REGISTRY, any third-party registries[*].url, or STANZA_NPM_REGISTRY — is rejected. https://, file://, bare filesystem paths, and loopback (localhost/127.0.0.1) are always allowed. To knowingly use a trusted internal http:// mirror, set STANZA_ALLOW_INSECURE_REGISTRY=1; the CLI then prints a one-time stderr warning. A third-party http:// registry is skipped (with a warning) while the rest of the CLI keeps working; an http:// value for STANZA_REGISTRY or STANZA_NPM_REGISTRY is a hard error.
Telemetry
- The CLI sends anonymous usage events (command run, modules installed/removed — no PII, no identifier persisted). Disable per-invocation with
--no-telemetry, or persistently withSTANZA_TELEMETRY=0orDO_NOT_TRACK=1. Telemetry is auto-skipped in CI.
Error Handling
Module not found: runstanza searchand use the displayed module id, not the label.missing-peer,incompatible-peer, orno-adapter: the selected module does not fit the current stack. Search for alternatives or add the required peer category first.No stanza.json found: run from a generated Stanza project, not the parent directory.- Dirty worktree refusal: commit/stash user changes or ask before using
--dangerously-allow-dirty. - After adding package-scoped modules such as db/auth/orm, run the selected package manager install command so workspace packages link correctly.
Agent Rules
- Treat generated files as user-owned project code after Stanza writes them.
- Don't synthesize
modules,regions, orreadmeChecksuminstanza.json— those are runner-managed.registries,apps, andpackageManagerare user config and may be edited. Each app'sdirmust be a repo-relative path inside the project (no..or absolute paths); Stanza rejects the manifest otherwise. - Prefer CLI commands over reconstructing Stanza's template output yourself.
- After mutating a project (or editing generated files), run
stanza doctorto confirmstanza.jsonstill matches the filesystem; it exits non-zero on drift. - When reporting results, include the exact command run, whether it wrote files, and any follow-up package-manager command needed.