- Replace Bun runtime with Node.js + `tsx` for the CLI dev path (`#!/usr/bin/env node` shebang, `tsx watch ./src/bin.ts` for `pnpm dev`) and `tsdown` for publishing — emits `dist/bin.mjs` as plain ESM; `create-stanza` gets the same treatment with its own `tsdown.config.ts` - Add `.github/workflows/ci.yml` running lint, fmt-check, registry build, web build, typecheck, vitest, CLI bundle build, and a smoke-test (`node dist/bin.mjs --version` / `--help`) on push/PR with concurrency cancel-in-progress - Add `apps/cli/src/commands/commands.test.ts` (196 lines, vitest) with unit tests for `add`, `init`, `list`, `remove`, and `search` command handlers; add `apps/cli/vitest.config.ts` - Replace `apps/web/scripts/copy-registry.ts` with a pure-bash `copy-registry.sh` — no Node/tsx prerequisite on the deploy target; drop the TS version and its `predev` auto-build hook (`pnpm registry:build` must be run before the first web build) - Add root `pnpm test` and `pnpm check-types` scripts that fan out to all workspaces via turbo; CI uses these as canonical entry points - Update `tsconfig.json` files for `packages/registry`, `packages/codemods`, and `packages/create-stanza` to stay source-only (`noEmit: true`); add `publishConfig` pointing at `dist/` in publishable `package.json` files so workspace dev resolution uses `./src/` while published tarballs resolve `./dist/` - Update CLAUDE.md toolchain invariants: document Node-only runtime, tsdown build pipeline, and per-workspace `dist/` paths; remove stale `bun apps/cli/src/bin.ts` dev command and all "Bun runs the CLI directly" references
6.2 KiB
First-party module registry
This is the canonical roadmap for the first-party modules stanza ships. Each
entry maps to a registry/modules/<slot>-<id>/ directory. Update this file
when a module lands, gets renamed, or is dropped.
Legend: [x] added · [ ] planned
Categories: slots vs add-ons
Stanza's module taxonomy splits into two categories. Slots are single-choice and constrain other modules' adapter dispatch (picking Next.js influences which auth adapters are available). Add-ons can coexist freely and don't influence anyone else's adapters (vitest doesn't constrain anything).
| Category | Type | Examples |
|---|---|---|
| Slots (constraint-bearing, one choice each) | framework, styling, db, orm, auth, api, ai, ui, payments |
next vs tanstack-start; drizzle vs prisma |
| Add-ons (no constraints, many allowed) | testing, tooling, deploy, email, monorepo |
vitest + playwright together; eslint or biome |
Today's CLI/manifest only models slots. The add-on schema (likely a
manifest.addons[] array + kind discriminator on Module) is
intentionally deferred — it should be designed around real add-on modules,
not in a vacuum. The first add-on (probably testing-vitest) will force the
schema decisions.
framework
- tanstack-start — TanStack Start on Vite (no Vinxi). Provides
web,react,ssr,node - next — Next.js 16 (App Router). Provides
web,react,ssr,rsc,node,edge - nuxt — Vue. Will require relaxing the React-implicit assumption in many peer modules (capability tag
vue) - svelte — SvelteKit. Capability
svelte - solid — SolidStart. Capability
solid
api
New slot (single-choice, constraint-bearing). Optional layer between the framework and the database/services.
- trpc — tRPC v11; per-framework adapters (next, tanstack-start, nuxt, svelte, solid)
- orpc — oRPC
ai
New slot (single-choice, constraint-bearing).
- vercel-ai-sdk —
aipackage + provider sub-recipes - tanstack-ai — TanStack AI
auth
- better-auth — headless, peers
orm: [drizzle, prisma],framework: [next, tanstack-start] - clerk — hosted UI, peers
framework: [next](TanStack Start adapter planned) - workos — WorkOS AuthKit
orm
- drizzle — Drizzle ORM 0.45, peers
db: [postgres, sqlite] - prisma — Prisma 7, peers
db: [postgres, sqlite]
db
- postgres —
postgresdriver 3.4.x - sqlite —
better-sqlite312.x
styling
- tailwind — Tailwind v4, adapters per framework
ui
New slot (single-choice, constraint-bearing). Layered on top of styling; provides component primitives.
- shadcn-radix — classic shadcn/ui (Radix primitives)
- shadcn-base — shadcn on react-base-ui
payments
New slot (single-choice, constraint-bearing).
- stripe — Checkout Sessions + webhooks
- polar — Polar SDK
New add-on (multi-allowed, no constraints).
- resend — Resend SDK + React Email templates
tooling
New add-on. Lint/format toolchain. Conventionally one per project but doesn't constrain others.
- eslint-prettier — ESLint + Prettier
- biome — Biome (lint + format)
- oxlint-oxfmt — Oxlint + oxfmt
testing
New add-on. Vitest and Playwright are independent and routinely coexist.
- vitest — unit + integration
- playwright — e2e
deploy
New add-on.
- vercel —
vercel.json+ framework-specific output - cloudflare — Workers / Pages adapter per framework
- railway —
railway.toml+ Dockerfile - docker — generic
Dockerfile+ compose for self-host
monorepo
Currently hardcoded in bootstrapShell as Turborepo. Promotes to an add-on when a second option (Nx, Moonrepo) lands.
- turborepo — Turbo 2.x (current default; not yet a configurable choice)
packageManager
Not a slot — a top-level field in stanza.json (packageManager: "pnpm" | "bun" | "npm"). Wizard prompts; codemods only touch package.json, never lockfiles.
- pnpm (default)
- bun
- npm
- yarn — needs lockfile/workspace handling that differs from the others
Slot-package extraction
Generated projects place each slot's output in one of two homes:
- App-scoped (
framework,styling) — files land inmanifest.appDir(e.g.apps/web/). These slots wire the app shell itself, so there's no useful extraction boundary. - Package-scoped (
auth,db,orm) — files land inpackages/<dir>/, named@<manifest.name>/<dir>, and the app gets aworkspace:*dep.dbandormshare a singlepackages/db/package so the ORM client sits next to the schema it queries.
The mapping lives in the canonical SLOTS array as the packageDir field — SLOT_PACKAGE_DIR is just a Record<SlotId, ...> view derived from it. When you add a new slot, decide upfront whether it extracts (data layer, observability, payments) or wires the shell (router, UI primitives that mount in <html>) and set packageDir accordingly.
Slot taxonomy changes required
Adding a new slot is now a two-line edit: append the id to KNOWN_SLOTS (the as const tuple Zod needs) and append a Slot entry to SLOTS with { id, label, description, packageDir }. Order is topological — earlier slots become peer candidates for later ones. Existing stanza.json files don't break: new slots are optional, so adding them is additive.
Adding an add-on category is a larger lift because the manifest schema doesn't model add-ons yet. When the first add-on lands, expect to introduce manifest.addons[] and a discriminator on Module. See the category table at the top of this file for which planned modules are add-ons vs slots.