Scaffold flows β verified generator reference
Last verified: 2026-07-10. Every adapter change must re-verify its section against upstream and update this date, the facts, and the sources. This document is the ground truth groot's adapters are written against.
Drift is watched automatically:
e2e.ymlre-runs the real generators weekly (behavior), anddrift.ymlre-checks every pinned series and this document's age (pins & docs) β see the maintainer runbook.
Version snapshot at verification: create-turbo 2.10.4 Β· Next.js 16.2.10 Β· sv 0.16.2 (SvelteKit 2.63 / Svelte 5.56 / Vite 8) Β· create-expo-app 4.0.0 (Expo SDK 57) Β· elysia 1.4.29 Β· create-hono 0.19.4 (hono 4.12.28) Β· convex 1.42.1.
Summary matrix
| Scaffold | groot invocation (pinned major) | Git-inits? | Auto-installs? | Non-empty dir behavior |
|---|---|---|---|---|
| Turborepo (trunk) | bunx create-turbo@2 <dir> -m bun --skip-install --no-git | yes β suppressed | yes β suppressed | assume fresh dir required |
| Next.js | bunx create-next-app@16 β¦ --disable-git --skip-install | skips inside existing repo; forced off anyway | yes β suppressed | refuses ("could conflict") |
| SvelteKit | bunx sv@0.16 create β¦ --no-install --no-dir-check | never | only with --install | refuses unless --no-dir-check |
| TanStack Start | bunx @tanstack/cli@0.69 create <name> --framework React --package-manager bun --no-git --no-install --no-examples --no-toolchain --no-intent --yes | --no-git | --no-install | refuses unless --force |
| Astro | bunx create-astro@5 <name> --template minimal --no-install --no-git --no-ai --skip-houston --yes | --no-git | --no-install | β οΈ silently redirects to a random dir under --yes (see Β§11) |
| React Router | bunx create-react-router@8 <name> --package-manager bun --no-git-init --no-install --no-agent-skills --yes | --no-git-init | --no-install | hard-fails on collisions without --overwrite |
| Nuxt | bunx create-nuxt@3 <name> --template minimal --packageManager bun --no-gitInit --no-install --no-modules | --no-gitInit | --no-install | --force to override |
| Vite | bunx create-vite@9 <name> --template react-ts --no-interactive --no-immediate | never | --no-immediate | --overwrite to clear |
| Expo | bunx create-expo-app@4 β¦ --no-install | no flag exists (see caveats) | yes β suppressed | (unverified) |
| React Native (bare) | bunx @react-native-community/cli@20 init <name> --pm bun --skip-install --install-pods false --skip-git-init --replace-directory false | --skip-git-init | --skip-install | prompts unless --replace-directory (groot targets fresh dirs, staged) |
| Tauri | bunx create-tauri-app@4 <name> --template react-ts --manager bun --identifier <id> --yes | never | never | refuses unless --force |
| Electron | bunx @quick-start/create-electron@1 <name> --template react-ts --skip | never | never | overwrite prompt nulled by --skip |
| Elysia | (none β groot writes files directly) | β | β | β |
| Hono | bunx create-hono@0.19 β¦ --template bun | never | only with -i | interactive confirm, no bypass β must target fresh dir |
| Fastify | bunx fastify-cli@8 generate <name> --lang=ts --esm | never | never (but shells out to npm init -y) | refuses ("directory already exists") |
| Convex | (files written directly, incl. vendored _generated stubs) | β | β | β |
| Supabase | bunx supabase@2 init (postCommand inside groot's package shell) | never | never | errors on existing config (exit 1; --force exists, groot targets fresh dirs) |
1. Turborepo trunk β create-turbo
bunx create-turbo@2 my-repo -m bun --skip-install --no-git
- Flags (from CLI source):
-m/--package-manager <npm|yarn|pnpm|bun>,--skip-install,--no-git,--skip-transforms,--turbo-version <v>,-e/--example <name|github-url>,-p/--example-path. - β οΈ
--skip-transformsconflicts with-m(transforms are what convert the pnpm-flavored example to bun). Never combine. - Generates (basic example):
apps/web+apps/docs(Next.js),packages/ui,packages/eslint-config,packages/typescript-config, rootturbo.json(v2"tasks"key), rootpackage.json. The bun transform replacespnpm-workspace.yamlwith a rootworkspacesarray +packageManager: "bun@β¦". - Git init runs last (after transforms/install) so suppression is clean.
- groot post-patch: remove the example
apps/web,apps/docs,packages/ui,packages/eslint-config(groot's selected scaffolds replace them;packages/typescript-configis kept and extended), reset root README, writegroot.json. - Sources: https://github.com/vercel/turborepo/blob/main/packages/create-turbo/src/cli.ts, https://github.com/vercel/turborepo/tree/main/examples/basic, https://turborepo.com/docs/reference/configuration.
2. Web: Next.js β create-next-app
bunx create-next-app@16 apps/web \
--ts --tailwind --eslint --app --src-dir --turbopack \
--import-alias "@/*" --use-bun --skip-install --disable-git --yes
- Current flag set (v16.2):
--ts/--js,--tailwind(negate--no-tailwind), linter trio--eslint|--biome|--no-linter(the old--no-eslintworld is gone),--react-compiler,--app,--api(route-handlers-only),--src-dir/--no-src-dir,--turbopack(default on),--import-alias <alias>,--empty,--use-bun(et al.),-e/--example,--skip-install,--disable-git,--yes,--reset-preferences,--agents-md/--no-agents-md(AGENTS.md + CLAUDE.md, default on). - β οΈ
--yesreuses saved user preferences β CI/agents must pass every flag explicitly (groot does). - Refuses non-empty target dirs; skips git init when already inside a git repo (groot passes
--disable-gitanyway). - Generates
app/router + Tailwind v4 (postcss.config.mjs),next.config.ts,tsconfig.json, own.gitignore,AGENTS.md/CLAUDE.md. - groot post-patch: rewire
tsconfig.jsonto extend@repo/typescript-config/nextjs.json, keep port 3000, remove per-app lockfile if any, fold.gitignore. - Sources: https://nextjs.org/docs/app/api-reference/cli/create-next-app, https://github.com/vercel/next.js/blob/canary/packages/create-next-app/index.ts, https://github.com/vercel/next.js/blob/canary/packages/create-next-app/helpers/git.ts.
3. Web: SvelteKit β sv create
create-svelte is deprecated; sv is the official CLI (merged create-svelte + svelte-add).
bunx sv@0.16 create apps/web --template minimal --types ts --no-add-ons --no-install --no-dir-check
- Flags:
--template <minimal|demo|library>,--types <ts|jsdoc>(or--no-types, not recommended),--add <add-onsβ¦>/--no-add-ons,--install <npm|pnpm|yarn|bun|deno>/--no-install,--no-dir-check,--from-playground <url>. - Never git-inits. Writes its own
.gitignoreand.npmrc. - β οΈ The generated
preparescript (svelte-kit sync || echo '') fires during rootbun installβ harmless, but expect it in install output. - Generates:
src/{app.html,app.d.ts,lib/,routes/},vite.config.ts,tsconfig.json. Deps:@sveltejs/kit ^2.63,svelte ^5.56,vite ^8,@sveltejs/adapter-auto ^7. - β οΈ No
svelte.config.js(verified 2026-07-10 against sv 0.16.2 output): SvelteKit config now lives insidevite.config.tsassveltekit({ adapter, compilerOptions })plugin options β adapters and doctor checks must look there. - groot post-patch: add
check/check-typesscripts (mirroring Turborepo's with-svelte example), keep Vite port 5173, fold.gitignore/.npmrc. - Sources: https://svelte.dev/docs/cli/sv-create, https://www.npmjs.com/package/sv, https://www.npmjs.com/package/create-svelte (deprecation notice), https://github.com/vercel/turborepo/tree/main/examples/with-svelte.
4. Mobile: Expo β create-expo-app
bunx create-expo-app@4 apps/mobile --template default@sdk-57 --no-install --yes
- Flags:
--yes,--no-install,--template <name>[@<tag>],--example <name>,--no-agents-md,--version. Templates:default(Expo Router + TS β groot's choice),blank,blank-typescript,tabs,bare-minimum. - β οΈ SDK transition gotcha: during the SDK 57 transition, omitting
--templateyields an SDK 54 project. groot always pins the template tag explicitly (default@sdk-57). - β οΈ No git-suppression flag exists.
create-expo-appgit-inits its output whenever it doesn't detect an enclosing repo β and duringgroot initthe root repo doesn't exist yet (rootgit initruns in the verify stage, after generation), so a nestedapps/mobile/.gitappeared every time (fixed in v1.0.1). The engine now scrubs any generator-created.gitimmediately after each scaffold grows (architecture.md#3-generate); the doctor check remains as a tripwire for workspaces grown earlier. - Monorepos: Expo has first-class bun-workspaces support; since SDK 52 Metro auto-configures for monorepos β do not write legacy
metro.config.jsworkspace hacks (watchFolders,nodeModulesPathβ¦). Shared packages consumed as"<pkg>": "*". - Metro dev server port: 8081 (kept).
- Sources: https://docs.expo.dev/more/create-expo/, https://docs.expo.dev/guides/monorepos/, https://www.npmjs.com/package/expo.
5. API: Elysia β files written directly (no generator)
bun create elysia resolves to the npm package create-elysia, which is community-owned (not an elysiajs org package), interactive-prompt-based, and diverges from Elysia's own docs (its template lacks the documented dev script). groot therefore writes the four files from Elysia's official manual setup:
apps/api/
βββ src/index.ts # new Elysia().get("/", β¦).listen(3001) β port patched from default 3000
βββ package.json # dev: bun --watch src/index.ts Β· build: bun build β¦ Β· start Β· test: bun test
βββ tsconfig.json # strict: true
βββ README.md
Deps: elysia@^1.4 + @types/bun. Scripts per Elysia's docs: dev: bun --watch src/index.ts, build: bun build src/index.ts --target bun --outdir ./dist, start: NODE_ENV=production bun dist/index.js, test: bun test.
- Sources: https://elysiajs.com/quick-start.html, https://www.npmjs.com/package/create-elysia (ownership), https://bun.com/docs/runtime/templating/create (
bun create xβ‘bunx create-x).
6. API: Hono β create-hono
bunx create-hono@0.19 apps/api --template bun --pm bun
- Flags:
-t/--template <bun|nodejs|cloudflare-workers|β¦>,-i/--install(install is opt-in),-p/--pm <bun|β¦>,-o/--offline(giget cache). - β οΈ The install confirmation has no negative flag (verified 2026-07-10 on 0.19.4): without
-i, create-hono still prompts "Do you want to install project dependencies?", and an unanswered prompt (closed stdin) aborts the entire scaffold with "User force closed the prompt" β no files written. groot pre-answersnvia stdin. - Templates are fetched from
honojs/starterpinned to a tag matching create-hono's own major.minor β deterministic per CLI version. Setspackage.json#nameto the directory basename. - Never git-inits. β οΈ Non-empty target β interactive confirm with no bypass flag β groot must always target a freshly created directory.
- Generates:
src/index.ts(export default app),package.json(dev: bun run --hot src/index.ts,hono ^4.12),tsconfig.json. - groot post-patch: Bun serves default-export apps on port 3000 β rewrite to
export default { port: 3001, fetch: app.fetch }; addbuild/startscripts mirroring the Elysia layout. - Sources: https://github.com/honojs/create-hono, https://github.com/honojs/starter/tree/main/templates/bun.
7. Backend: Convex β files written directly, _generated stubs vendored
No fully-offline official generator exists (create-convex is a degit template copier oriented at full-stack presets). groot writes packages/backend directly, modeled on Convex's own reference monorepo (get-convex/turbo-expo-nextjs-clerk-convex-monorepo):
packages/backend/
βββ convex/
β βββ schema.ts # starter schema (messages table)
β βββ messages.ts # starter query + mutation matching the schema
β βββ tsconfig.json # vendored from get-convex/templates
β βββ _generated/ # vendored standard stubs β COMMITTED (official recommendation)
βββ package.json # @repo/backend Β· dev: convex dev Β· setup: convex dev --until-success Β· typecheck
βββ .env.example # CONVEX_DEPLOYMENT / CONVEX_URL placeholders
- β οΈ
convex codegenis NOT offline-usable in current releases (verified 2026-07-10 on convex 1.42.1):bunx convex codegen --initin a fresh package fails with "No CONVEX_DEPLOYMENT set, runnpx convex devto configure a Convex project". The older maintainer statements about credential-free codegen (get-convex/convex-js#81) no longer hold for new projects. - groot copies the official templates' strategy instead: get-convex/templates ships the standard
convex/_generatedstubs (api.d.ts,api.js,dataModel.d.ts,server.d.ts,server.js) checked in, so the package typechecks before any login. groot vendors those stubs (src/adapters/convex-generated-stubs.ts,api.d.tsadapted to the startermessages.tsmodule); the firstconvex devrun regenerates them against the live deployment. - β οΈ The vendored
convex/tsconfig.jsondeclares"types": ["node"]β the generated package.json must therefore carry@types/node(pin tracks the upstream template), orconvex dev's built-in typecheck fails with TS2688. Caught in a live v0.2.0 run; the E2E now typechecks scaffolded packages, not just installs them. - β οΈ Known upstream quirk (verified 2026-07-10, cosmetic):
convex dev's optional "Set up Convex AI files?" step installs agent skills vianpx, which trips npm'sdevEnginesguard inside the bun-declared workspace (EBADDEVENGINES: required { name: 'bun' }). The guard is working as intended; Convex prints a manual retry command and continues β deployment provisioning and.env.localare unaffected. - The one unavoidable interactive step β
bunx convex dev --until-success(login, deployment provisioning,.env.local) β is never run by groot; it's printed as the first "next step". - Consumption pattern: apps depend on
"@repo/backend": "workspace:*"and deep-import@repo/backend/convex/_generated/api(noexportsmap β deliberate, matching the reference repo). Frontends receive the Convex URL via.envplumbing, named per framework:NEXT_PUBLIC_CONVEX_URL(Next),PUBLIC_CONVEX_URL(SvelteKit, Astro),VITE_CONVEX_URL(TanStack Start, React Router, Vite),NUXT_PUBLIC_CONVEX_URL(Nuxt),EXPO_PUBLIC_CONVEX_URL(Expo),CONVEX_URL(bare React Native β no public-env mechanism, left to the user's env lib). - Sources: https://docs.convex.dev/cli/reference/codegen, https://docs.convex.dev/cli, https://github.com/get-convex/templates (stub strategy), https://github.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo, https://docs.convex.dev/production/project-configuration.
8. Desktop: Tauri β create-tauri-app
bunx create-tauri-app@4 desktop --template react-ts --manager bun --identifier com.<workspace>.desktop --yes
- Flags:
-t/--template(react-tspinned β TS + React frontend),-m/--manager(bunis a first-class option),--identifier(reverse-domain bundle id; the CLI warns on itscom.tauri.devdefault, so groot derives one from the workspace name),-y/--yes(skip remaining prompts),-f/--force,--tauri-version. Never installs, never git-inits. - β οΈ The positional is a NAME, not a path β the generator derives the app and crate names from it. groot therefore spawns from the scaffold's parent directory (
apps/) with the bare directory name, instead of passingapps/desktopfrom the workspace root. - Port 1420 is the template's own contract: the generated
vite.configpinsserver.port: 1420withstrictPort: true, andsrc-tauri/tauri.conf.jsonpointsbuild.devUrlat the same port. Unique in groot's matrix β kept, no rewrite. - Rust is a dev-time dependency, not a scaffold-time one: generation is pure npm;
bun installworks without cargo.tauri dev/tauri buildneed Rust (plus webkit2gtk on Linux) β groot prints a rustup next-step anddoctorwarns (never fails) when cargo is absent. - Wrap-the-existing-web-app mode (
bunx @tauri-apps/cli init --cipointed atapps/web) is a distinct, officially supported shape β deferred to the add-on engine (expansion.md, roadmap v1.4). - Sources: https://v2.tauri.app/start/create-project/, https://v2.tauri.app/reference/cli/, https://github.com/tauri-apps/create-tauri-app.
9. Desktop: Electron β @quick-start/create-electron (electron-vite)
bunx @quick-start/create-electron@1 desktop --template react-ts --skip
- Why this generator: electron-vite's scaffolder is the de-facto standard for Electron + Vite + TS. Electron Forge's own
create-electron-appforce-installs dependencies (npm/yarn only, no skip flag) and marks its Vite template experimental β unsuitable for groot's scaffold-dry model (expansion.md). - Flags:
-t/--template(react-tspinned; the-tssuffix nulls the TypeScript prompt) and--skip(nulls the remaining prompts: overwrite, auto-update, mirror). Never installs, never git-inits β prints next steps only. - β οΈ The positional is a NAME, not a path β same contract as create-tauri-app: groot spawns from the scaffold's parent directory (
apps/) with the bare directory name. - No declared dev port: the renderer dev server is plain Vite (non-strict) and electron-vite launches Electron with whatever URL it resolved β self-wiring, so groot neither promises nor manages a port (unlike Tauri's contractual strictPort 1420).
- β οΈ Bun lifecycle nuance (verified 2026-07-12, bun 1.3.14): the
electronpackage downloads its runtime binary in a postinstall script, and bun only runs dependency lifecycle scripts for trusted packages β electron is NOT on bun's default-trusted list (the flagship E2E's realbun installproved it: runtime missing without a grant). The stitch stage writestrustedDependencies: ["electron"]into the workspace root package.json before verify's install β empirically honored for workspace-member dependencies β and verify keeps abun pm trust electronfallback for degraded workspaces. Note bun treats an explicittrustedDependenciesas replacing its default allowlist; packages a user later adds that need postinstalls belong in the same array. - β οΈ Bun 1.3 isolated-layout nuance: packages live in the
node_modules/.bunstore with symlinks in the declaring workspace member β there is no top-levelnode_modules/electron. Anything checking electron's install state must resolve from the app directory (groot's doctor/verify use node resolution fromapps/desktop), not from a hardcoded root path. - Build output:
out/(main/preload/renderer) β added to turbo'sbuild.outputs; electron-builder's installers land indist/(not cached). - Sources: https://electron-vite.org/guide/, https://www.npmjs.com/package/@quick-start/create-electron, https://bun.com/docs/pm/lifecycle.
10. Web: TanStack Start β @tanstack/cli (tanstack create)
bunx @tanstack/cli@0.69 create web --framework React --package-manager bun --no-git --no-install --no-examples --no-toolchain --no-intent --yes
- Flags (verified 2026-07-12 against the published 0.69.5 source):
--framework <React|Solid>(display names from the framework definitions),--package-manager bun(first-class value),--no-git,--no-install,--no-examples(skip demo pages),--no-toolchain(skip the eslint/biome overlay β the workspace root owns linting),--no-intent(skip TanStack Intent agent files; groot's own agent artifacts arrive with roadmap v1.3),-y/--yes(accept remaining defaults), plus--target-dir,-f/--force,--json,--add-ons. The deprecated--tailwind/--no-tailwindare compatibility no-ops β Tailwind is always enabled. - β οΈ The positional is a NAME, not a path β same contract as create-tauri-app/create-electron: groot spawns from the scaffold's parent with the bare directory name.
- Port 3000 lives in the dev script (
"dev": "vite dev --port 3000"in the template's package.json), not in vite.config β same default as Next.js. Single-web workspaces are clean;groot add tanstack-start --path apps/<name>next to a Next app trips the standard port-collision warning, and doctor tracks the dev-script/manifest agreement. - Build is plain
vite buildβdist/β the 1.x template has no Nitro/Vinxi (verified: no such deps in the template package.json);dist/**is already in turbo's outputs. - Template ships TS-first (typescript ^6, vite ^8, react 19), Tailwind, file-based routing with the Start plugin; the scaffolded package.json
namefield is empty β stitch names it from the directory. - The CLI is 0.x and fast-moving β pinned to the minor (
@tanstack/cli@0.69), the sv@0.16 precedent; the drift watch tracks the series automatically. - Sources: https://tanstack.com/start/latest/docs/framework/react/quick-start, https://www.npmjs.com/package/@tanstack/cli (dist/cli.js option table), https://www.npmjs.com/package/@tanstack/create (react framework template).
11. Web: Astro β create-astro
bunx create-astro@5 web --template minimal --no-install --no-git --no-ai --skip-houston --yes
- Flags (verified 2026-07-12 against the published 5.2.2 source):
--template <name|gh-repo>,--ref,--install/--no-install,--git/--no-git,--no-ai(skip AI agent config files β groot's own agent artifacts arrive with roadmap v1.3),--skip-houston(skip the mascot animation),-y/--yes,-n/--no,--dry-run,--add,--fancy. Node >= 22.12 required by the package. - β οΈ Non-empty-target gotcha (verified in source): with
--yes, a non-empty target directory is silently ignored β create-astro scaffolds into a randomly generated project name instead (generateProjectName()). groot's generate stage guarantees a fresh destination, which makes this unreachable β but never invoke this generator outside that guarantee. (A dir containing only.git/.gitignore-class entries counts as empty per its safe list.) - The positional doubles as the project-name source β bare names pass
toValidNameuntouched; a path likeapps/webwould become the package nameapps-web. groot spawns from the scaffold's parent with the bare basename. - Templates are fetched via giget from the withastro/astro examples at scaffold time (network to GitHub, not npm);
minimalis pinned. TS-strict by default in 5.x β the old--typescriptflag no longer exists. - Port 4321 is
astro dev's built-in default (no config entry, no dev-script flag) β the only unique web port in the matrix; nothing to rewrite or drift-check. - Build:
astro buildβdist/(already in turbo's outputs). - Sources: https://docs.astro.build/en/install-and-setup/, https://www.npmjs.com/package/create-astro (dist/index.js flag table + project-name action), https://docs.astro.build/en/guides/environment-variables/ (PUBLIC_ prefix).
12. Web: React Router (framework mode) β create-react-router
bunx create-react-router@8 web --package-manager bun --no-git-init --no-install --no-agent-skills --yes
- Flags (verified 2026-07-12 against the published 8.2.0 source):
--yes,--git-init/--no-git-init,--install/--no-install,--package-manager <npm|pnpm|yarn|bun|deno>,--agent-skills/--no-agent-skills,--template <gh-url-or-shorthand>,--overwrite,--react-router-version/-v,--no-color,--no-motion,--show-install-output,--token,--debug. - β οΈ v8.2 added an agent-skills step: the CLI ships React Router's own SKILL.md + reference docs and offers to copy them into the project. groot passes
--no-agent-skillsβ the same policy as TanStack's--no-intentand Astro's--no-ai: groot's own agent-era artifacts arrive with roadmap v1.3 and per-scaffold agent files would collide. - The default template is fetched from GitHub at scaffold time (
remix-run/react-router-templates/tree/main/default): TS + Tailwind + SSR, plus a Dockerfile (kept as-is). In non-TTY runs the CLI auto-forces--yes; pre-existing file collisions hard-fail without--overwriteβ groot's fresh-destination guarantee makes that unreachable. - The positional doubles as the project-name source (
toValidProjectName, same shape as create-astro) β groot spawns from the scaffold's parent with the bare basename. - Port 5173 is the Vite default (no dev-script flag, no config entry) β shared with SvelteKit; same-slot alternatives share ports (the elysia/hono precedent), and
add --pathcoexistence rides the collision warning. - Build:
react-router buildβbuild/(client + server; the template's serve script points atbuild/server/index.js) β added to turbo's outputs. Typecheck:react-router typegen && tsc. - Sources: https://reactrouter.com/start/framework/installation, https://www.npmjs.com/package/create-react-router (dist/cli.js option table), https://github.com/remix-run/react-router-templates/tree/main/default.
13. Web: Nuxt β create-nuxt
bunx create-nuxt@3 web --template minimal --packageManager bun --no-gitInit --no-install --no-modules
- Flags (verified 2026-07-12 against the published 3.36.1 source β citty args): positional
dir,-t/--template,--packageManager <npm|pnpm|yarn|bun|deno|aube>,--gitInit(boolean β--no-gitInitvia citty negation),--install(default true β--no-install),-M/--modules(string,--no-modulesskips the prompt),-f/--force,--offline,--preferOffline,--shell,--nightly,--logLevel. - β οΈ Non-interactive completeness is ENFORCED (verified in source):
nonInteractiveRequiredArgs = [dir, template, packageManager, gitInit]β in a non-TTY shell, any of the four missing exits 2 with a usage report. groot passes all four explicitly; there is no--yes. - The CLI's default template is
minimal, fetched via giget from the nuxt/starter templates registry (raw.githubusercontent.com β network to GitHub at scaffold time). Package iscreate-nuxt3.x even though Nuxt itself is v4. - The template ships
postinstall: nuxt prepareβ it runs during the rootbun install(workspace-member scripts always run under bun; this is not a blocked dependency lifecycle script). - Port 3000 is
nuxt dev's built-in default β shared with Next/TanStack per the same-slot rule. - Build:
nuxt buildβ.output/(Nitro) β added to turbo's outputs. Convex env:NUXT_PUBLIC_CONVEX_URL(runtimeConfig.public viaNUXT_PUBLIC_*). - Sources: https://nuxt.com/docs/api/commands/init, https://www.npmjs.com/package/create-nuxt (dist/index.mjs citty args + nonInteractiveRequiredArgs), https://github.com/nuxt/starter.
14. Web: Vite β create-vite
bunx create-vite@9 web --template react-ts --no-interactive --no-immediate
- Flags (verified 2026-07-12 against the published 9.1.1 bundle):
-t/--template,--interactive/--no-interactive(force mode),-i/--immediate/--no-immediate(β οΈ 9.x's--immediateinstalls dependencies AND starts the dev server β groot passes--no-immediateexplicitly),--eslint/--no-eslint(React templates default to Oxlint in 9.x; groot keeps the default β the workspace root owns linting),--overwrite,-h/--help. - Templates:
vanilla-ts,vue-ts,react-ts(groot's pin),react-compiler-ts,solid-ts,svelte-ts,preact-ts,qwik-ts,lit-ts(+ JS variants). Watch renames across create-vite majors. - The cleanest generator in the matrix: never installs, never git-inits, writes no lockfile; ships its gitignore as
_gitignore, renamed on copy (verified in bundle). - Port 5173 is the Vite default β shared with SvelteKit/React Router per the same-slot rule.
- Build:
vite buildβdist/(already in turbo's outputs). Convex env:VITE_CONVEX_URL. - Sources: https://vite.dev/guide/, https://www.npmjs.com/package/create-vite (dist/index.js option table + templates).
15. API: Fastify β fastify-cli generate
bunx fastify-cli@8 generate api --lang=ts --esm
- Fully non-interactive (verified 2026-07-13 in the published 8.0.0 bundle β
generate.js,cli.js): no prompts, no install, no git init, no lockfile. The package ships a single bin namedfastify(β package name) β bunx's single-bin fallback covers it, the same case@tanstack/cliβtanstackalready proves in this matrix. - Refuses existing directories (
directory <dir> already exists, exit 1) and creates its own leaf dir β groot spawns from the scaffold's parent with the bare basename, the name-not-path pattern. - β οΈ Shells out to
npm init -yinside the target (upstream behavior β the only generator in the matrix with a hard npm dependency at generate time; GitHub runners and dev machines with Node have it). That's also where the package name comes from: the directory basename. The template's scripts/deps are then merged into the resulting package.json. - β οΈ That npm call cannot run inside a groot workspace (caught live in e2e scenario 4, 2026-07-13): npm resolves its project root by walking up from cwd, finds the bun-declared workspace root (create-turbo's
-m buntransform writesdevEngines), and hard-fails withEBADDEVENGINESβ the same guard scaffold-flows Β§7 documents for Convex's npx step, but fatal here. groot therefore runs this generator staged: generated in a disposable directory under the OS tempdir (neutral ancestry), then moved intoapps/apiβ the trunk's temp-sibling pattern, generalized asScaffoldAdapter.stagedGeneration. The same guard is why the stitched scripts must be bun-invoking: the template's ownnpm run β¦chains would trip it at runtime too. --lang=ts --esmpins the ESM TypeScript template (templates/app-ts-esm): an @fastify/autoload app plugin (src/app.ts,forceESM: true) withsrc/plugins/+src/routes/and a node:test suite. Ships__gitignore, renamed to.gitignoreon copy (generify's__β.convention, verified in generify 4.2.0). Its tsconfig setsoutDir: dist,allowImportingTsExtensions+rewriteRelativeImportExtensionsβ so groot's server entry can import./app.tsdirectly whiletscstill emits clean JS.- The template serves through the Node-centric
fastify startwrapper β groot instead overlayssrc/server.ts(modeled on fastify-cli's own eject template: register the app plugin, listen on :3001) and the stitch stage swaps the scripts wholesale for the bun set:dev: bun --watch src/server.ts,start,build: tsc(βdist/, turbo-cached),test: bun test,typecheck. The rewrite is marker-gated on the template's own dev script; hand-rolled scripts are never clobbered. Template devDependencies stay as-is (ts-node/c8/concurrently go unused under bun β harmless, and removing them would couple groot to template internals). - @fastify/autoload β₯ 6 detects bun natively (
'Bun' in globalThis,lib/runtime.js) and loads the.tsplugins/routes with no compile step or env var. Bun's node:test shim runs the template's test suite (test/t.after/node:assertverified locally on bun 1.3.14). - Port 3001, shared with elysia/hono per the same-slot rule; the port lives in groot's
src/server.ts(doctor watches theport: 3001 }marker there). - Sources: https://github.com/fastify/fastify-cli (generate.js, args.js, templates/), https://www.npmjs.com/package/fastify-cli (published 8.0.0 bundle), https://github.com/fastify/fastify-autoload (lib/runtime.js bun detection).
16. Mobile: React Native (bare) β @react-native-community/cli init
bunx @react-native-community/cli@20 init mobile --pm bun --skip-install --install-pods false --skip-git-init --replace-directory false
- Fully non-interactive with these flags on a fresh target (verified 2026-07-13 in the published 20.2.0 bundle β commands/init/*.js): the positional is validated (
/^[$A-Z_][0-9A-Z_$]*$/i, no java keywords, no "helloworld" βmobilepasses), the react-native version and matching@react-native-community/templateresolve via plain registry fetches, and with--pm bunthe template installs via bun (bun init -y+bun add --exact) into the CLI's own tempdir before being copied into the target. Single binrnc-cli(β package name) β bunx's single-bin fallback, same as@tanstack/cliandfastify-cli. - β οΈ The registry-URL lookup shells npm (
npm config get registry --workspaces=false --include-workspace-root, tools/npm.js): inside a groot workspace that call hard-fails withEBADDEVENGINES(create-turbo's-m bunwritesdevEngines; reproduced 2026-07-13 β exit 1). The CLI's try/catch swallows it and falls back to the hardcoded default registry, so in-tree init survives by courtesy β groot runs it staged (ScaffoldAdapter.stagedGeneration) instead, which both dodges the guard and lets the user's real registry config apply. - Placeholder pipeline (editTemplate.js):
HelloWorldβ project name (case-preserving + lowercase pass) across file contents and names;_gitignore/_watchmanconfig/_bundle/β¦ β dotfiles (UNDERSCORED_DOTFILES). The android applicationId / iOS target becomecom.mobile/mobileβ override with--package-nameis available but groot keeps the default. package.json name lands asmobile, so the app-rename stitch is a no-op. - Pods / SDKs stay out of generate: CocoaPods runs only on darwin AND only inside the (skipped) install step β
--install-pods falseis belt-and-braces; Xcode/CocoaPods/Android SDK are the user's build-time next steps, never groot's. - Git:
--skip-git-init; init also auto-skips when it detects an enclosing repo (git rev-parseon the project folder). - Metro is not monorepo-aware in the bare template (unlike Expo β₯ SDK 52): the shipped
metro.config.jsismergeConfig(getDefaultConfig(__dirname), {})with a literalconst config = {};β groot's stitch rewrites that marker intowatchFolders: [workspaceRoot]+resolver.nodeModulesPathsfor the app and the workspace root, and the doctor watches the wiring persistently. Scripts stay untouched β they're package-manager-neutral (react-native start,jest). - β οΈ
add --pathbasenames must satisfy the CLI's name rules (the veto runs at resolve time, before anything generates): JS-identifier shape βapps/rn-apphas a dash and would crash init β plus no java keywords,react/react-native, or anything containinghelloworld. - Port 8081 is the Metro default, shared with Expo per the same-slot rule;
add --pathcoexistence rides the port-collision warning (the issue's original "port shift" idea predates the same-slot precedent set by the web wave). - Sources: https://github.com/react-native-community/cli (build/commands/init/*.js, build/tools/npm.js, build/tools/packageManager.js in the published 20.2.0 bundle), https://www.npmjs.com/package/@react-native-community/template (template.config.js, template/metro.config.js, 0.86.0), https://reactnative.dev/docs/metro.
17. Backend: Supabase β package shell + supabase init
bunx supabase@2 init # postCommand, cwd = packages/backend (after groot's writeFiles)
- Non-interactive by default in 2.x (verified 2026-07-13 in the cli monorepo's Go sources AND by running the published 2.109.1 linux-x64 binary non-TTY): the old IDE prompts moved behind an opt-in
--interactiveflag that additionally requires a TTY (cmd/init.go), and init pinsWORKDIRto.specifically to avoid walking up to a parent project. No install, no git init, no lockfile β it's a Go binary that shells nothing, so the EBADDEVENGINES staging class does not apply. - Writes exactly
supabase/config.tomlinto its cwd (no directory argument exists β hence the postCommand-inside-the-shell layout), plussupabase/.gitignorewhen an enclosing git repo exists (contained inside the scaffold either way β empirically: fresh dir β config only; git repo β both). Re-running errors with exit 1 (--forceto overwrite); groot's fresh-dir planning never hits it. - The npm wrapper is a 3-file shim that exec's a platform binary shipped as
optionalDependencies(@supabase/cli-<platform>, 8 targets): no postinstall, so bun's lifecycle-script trust list (the electron lesson) is never involved. Single bin name matches the package name. project_iddefaults to the cwd basename β"backend"for every groot workspace, but the field exists to "distinguish different Supabase projects on the same host" (Docker containers are named after it). The stitch renames it to the workspace name, anchored on the exact default line, triple-state like every template patch.- groot's package shell (writeFiles, before init runs):
package.json(@repo/backend,dev: supabase startΒ·stopΒ·typegenΒ·typecheck; the CLI pinned as a devDependency), adatabase.types.tsplaceholder matchinggen typesoutput for an empty schema (the Convex vendored-stubs pattern β apps deep-importing@repo/backend/database.typestypecheck before Docker ever runs), tsconfig, README,.env.example(values left empty βsupabase startprints them; no keys are ever vendored). - Docker, login, and real codegen stay out of generate:
supabase start(local stack),supabase login/link(cloud), andbun run typegenare next-steps output β the Convex login pattern. - Env plumbing: two lines per frontend,
<prefix>SUPABASE_URL=+<prefix>SUPABASE_ANON_KEY=, prefixed per framework exactly like the Convex names (Β§7); bare React Native gets the unprefixed pair. - Sources: https://github.com/supabase/cli (apps/cli-go/cmd/init.go, apps/cli-go/internal/init/init.go), https://www.npmjs.com/package/supabase (published 2.109.1 shim + optionalDependencies), plus a live run of the 2.109.1 binary (fresh dir / git repo / re-run / --force).
Stitching reference
The canonical "what does stitched output look like" spec is the diff between raw generator output and the same app inside Turborepo's official examples (basic, with-svelte, kitchen-sink): package renames, @repo/* deps with workspace:*, shared typescript-config extends, per-app ports in dev scripts, no nested lockfiles. See architecture.md for the full operation list.
Prior-art notes (why groot works this way)
- create-better-t-stack: vendored Handlebars templates + near-daily releases to fight drift. groot copies its automation contract (
--yes,--dry-run, explicit dir-conflict policy, JSON plan output) but not its template strategy. - create-t3-turbo: a template repo consumed via
create-turbo -eβ already trailing upstream majors at verification time; demonstrates template rot. - Both confirm: generators' monorepo-hostile side effects (git init, auto-install, nested lockfiles, port collisions) are the real problem groot's stitch stage solves.