Greenfield scaffolding
Use this when you want a new project from scratch. Instead of letting the model invent a stack, the wizard stands up BoringStack, a proven full-stack TypeScript template, by driving BoringStack’s own setup scripts. tsforge holds no stack knowledge of its own: the entire config surface is declared in a manifest committed in the BoringStack repo, which tsforge reads after cloning.
For a Phaser 4 game, use the Phaser archetype instead — that clones a different repo and does not use this fullstack manifest.
Three archetypes
Section titled “Three archetypes”| Archetype | What you get | Gate |
|---|---|---|
boringstack | The full stack: Bun + Elysia + Drizzle API (apps/api) + Vite/React UI (apps/ui), Postgres + Valkey, and whatever observability/billing/auth services your toggles enable | cd apps/api && bun run validate · cd apps/ui && bun run validate · bun run check |
astro | The Astro Starlight static site (apps/docs); no API, no Docker, no .env | bun run build |
phaser | A Phaser 4 TypeScript game from Phaser-TypeScript-AI-First-Starter; no Docker, no .env | bun run check |
When it runs
Section titled “When it runs”Greenfield only. Editing an existing repo never scaffolds; tsforge auto-detects the gate and patches in place. The wizard is its own subcommand so its flags don’t collide with the harness flags:
tsforge scaffold --dest ./my-appInteractively (a TTY), it asks the archetype’s questions and shows a live preview of the consequence before anything is written: the container topology (the 5-vs-20 service difference your toggles make), the secrets you’ll need to supply, and any blocking cross-rule violation. Off a TTY, it uses flags directly.
What it does, in order
Section titled “What it does, in order”- Clone BoringStack at the manifest’s
defaultRef, resolving the exact commit SHA into<project>/.tsforge/scaffold.jsonfor replay. - Configure by driving BoringStack’s own scripts:
scripts/rename-project.sh, thensetup.sh(which bootstrapscompose/.envand generates a GlitchTip secret), then writing your toggle/provider answers into the right.envfile (infra toggles →infra/compose/compose/.env; app features →infra/compose/compose/api.<stack>.env). Prod-only secrets (JWT_SECRET,MFA_ENCRYPTION_KEY,VALKEY_PASSWORD) are generated; secret values are written to disk but never logged. - Boot the stack with
setup.sh --upand health-poll the API + UI (skip with--no-boot/STACK=smoke). Boot is a one-time scaffold-step, not part of the per-edit gate. - Hand off: prints the exact command to start building features against the
scaffolded project (its own
bun run validatebecomes the gate).
| Flag | Meaning |
|---|---|
--dest <dir> | Where to create the project (required) |
--archetype <boringstack|astro|phaser> | Default boringstack |
--stack <dev|prod|smoke> | Default dev |
--set KEY=VALUE | Set a toggle/provider answer (repeatable). For example: --set WITH_OBSERVABILITY=0 |
--multi KEY=a,b | Set a multi-select answer. For example: --multi OAUTH_PROVIDERS=google,github |
--ref <git-ref> | Override the manifest’s clone ref |
--no-boot | Clone + configure, but don’t start Docker |
After it finishes:
tsforge --dir ./my-app --accept '(cd apps/api && bun run validate) && (cd apps/ui && bun run validate) && bun run check' "add a deals resource end-to-end"The manifest (single source of truth)
Section titled “The manifest (single source of truth)”tsforge models none of BoringStack’s surface itself. .tsforge/scaffold-manifest.json
in the BoringStack repo declares every toggle, provider choice, secret, the
services each toggle spawns, cross-rules (OAuth ⇒ Valkey, EMAIL_PROVIDER=smtp ⇒
WITH_MAILPIT, OTel-vs-Sentry exclusion), and which .env file each value targets.
tsforge reads it post-clone and a completeness alarm fails the build if a watched
WITH_*/*_ENABLED toggle in BoringStack’s .env.example isn’t modelled. The
wizard can never silently drop a capability. Evolving BoringStack means editing that
manifest, not tsforge.
How to test it
Section titled “How to test it”Unit (fast, no clone/Docker): the planner, manifest parser, wizard flow, env apply, and completeness alarm are pure and fully covered:
bun test packages/core/tests/scaffold-*.test.tsThis includes a snapshot of BoringStack’s real .env.example files asserting the
bundled manifest has zero coverage gaps, plus an exhaustive on/off matrix for every
toggle and provider.
Real clone + configure (opt-in, needs a local BoringStack checkout): runs git +
BoringStack’s actual setup.sh, no Docker boot:
TSFORGE_SCAFFOLD_E2E=1 BORINGSTACK_REPO=/path/to/boringstack \ bun test packages/core/tests/scaffold-clone-configure.e2e.test.tsEnd-to-end by hand: drive the whole flow against a local checkout (the
BORINGSTACK_REPO override avoids hitting GitHub):
BORINGSTACK_REPO=/path/to/boringstack \ bun packages/core/scripts/headless-scaffold-build.ts \ --dest /tmp/acme --set WITH_OBSERVABILITY=0 --multi OAUTH_PROVIDERS=google --no-bootIt prints the cloned SHA, the configured .env (secrets redacted), the composed gate
command, and the next step. A full boot (omit --no-boot) additionally health-checks
the running stack.