Overview
Add a btst init command to the @btst/codegen package that automatically scaffolds all the boilerplate required to integrate BTST into an existing Next.js (App Router), React Router v7, or TanStack Start project.
The install docs currently require ~8 steps of manual file creation and surgical edits to existing files. This command automates the entire flow end-to-end — detecting the framework, picking the right adapter, installing dependencies, generating files, and patching the root layout and CSS.
Think shadcn/ui's npx shadcn init — a single command that gets a project from zero to working without consulting documentation.
npx @btst/codegen init
# or if @btst/codegen is already installed:
codegen init
Core Features
Framework Detection & Selection
Adapter Selection
Dependency Installation
Plugin Selection & Basic Config
File Generation
Surgical File Patching
Prerequisite Validation
Package Structure
packages/cli/
├── src/
│ ├── index.ts # commander entry — registers init + generate commands
│ ├── commands/
│ │ ├── init.ts # btst init — full project bootstrapping
│ │ └── generate.ts # btst generate — existing DB schema codegen
│ ├── templates/
│ │ ├── shared/
│ │ │ └── lib/query-client.ts # no templating needed; identical across frameworks
│ │ ├── nextjs/
│ │ │ ├── lib/stack.ts.hbs # Handlebars template, one variant per adapter
│ │ │ ├── lib/stack-client.tsx.hbs
│ │ │ ├── app/api/data/[[...all]]/route.ts.hbs
│ │ │ ├── app/pages/[[...all]]/page.tsx.hbs
│ │ │ └── app/pages/[[...all]]/layout.tsx.hbs
│ │ ├── react-router/
│ │ │ ├── lib/stack.ts.hbs
│ │ │ ├── lib/stack-client.tsx.hbs
│ │ │ ├── app/routes/api/data/route.ts.hbs
│ │ │ ├── app/routes/pages/index.tsx.hbs
│ │ │ └── app/routes/pages/_layout.tsx.hbs
│ │ └── tanstack/
│ │ ├── lib/stack.ts.hbs
│ │ ├── lib/stack-client.tsx.hbs
│ │ ├── src/routes/api/data/$.ts.hbs
│ │ ├── src/routes/pages/$.tsx.hbs
│ │ └── src/routes/pages/route.tsx.hbs
│ └── utils/
│ ├── detect-framework.ts # sniff package.json + folder layout
│ ├── detect-package-manager.ts # lockfile detection
│ ├── detect-alias.ts # read tsconfig.json paths
│ ├── detect-css-file.ts # find globals.css / app.css
│ ├── file-writer.ts # write + skip/overwrite/diff conflict UX
│ ├── package-installer.ts # run pnpm/npm/yarn add
│ ├── css-patcher.ts # inject @import line safely
│ └── layout-patcher.ts # ts-morph AST wrap for QueryClientProvider
├── package.json
├── tsconfig.json
└── build.config.ts
Command Interface
btst init [options]
Options:
--framework <name> Skip detection prompt (nextjs | react-router | tanstack)
--adapter <name> Skip adapter prompt (memory | prisma | drizzle | kysely | mongodb)
--skip-install Skip dependency installation
--cwd <path> Run in a different directory (default: process.cwd())
--yes Accept all defaults / overwrite all conflicts without prompting
-h, --help Show help
Interactive Prompt Flow
◆ Which framework are you using?
● Next.js (App Router)
○ React Router v7
○ TanStack Start
◆ Which database adapter?
● Memory (development / testing)
○ Prisma
○ Drizzle
○ Kysely
○ MongoDB
◆ Where is your global CSS file?
app/globals.css
Installing dependencies with pnpm...
+ @btst/stack
+ @tanstack/react-query
+ @btst/adapter-memory
Writing files...
✔ lib/query-client.ts
✔ lib/stack.ts
✔ lib/stack-client.tsx
✔ app/api/data/[[...all]]/route.ts
✔ app/pages/[[...all]]/page.tsx
✔ app/pages/[[...all]]/layout.tsx
Patching files...
✔ app/globals.css added @import \"@btst/stack/style.css\"
✔ app/layout.tsx wrapped children with QueryClientProvider
◆ All done!
Next steps:
→ Add plugins to lib/stack.ts and lib/stack-client.tsx
→ Generate database schema: npx @btst/codegen generate --config=lib/stack.ts --orm=memory
→ Visit /pages to see plugin routes
Generated File Examples
lib/stack.ts (memory adapter, Next.js)
import { stack } from \"@btst/stack\"
import { createMemoryAdapter } from \"@btst/adapter-memory\"
let _stack: ReturnType<typeof stack> | undefined
function getStack() {
if (!_stack) {
_stack = stack({
basePath: \"/api/data\",
plugins: {
// Add your backend plugins here
},
adapter: (db) => createMemoryAdapter(db)({}),
})
}
return _stack
}
export const myStack = {
get handler() { return getStack().handler },
get api() { return getStack().api },
get adapter() { return getStack().adapter },
}
lib/stack-client.tsx
import { createStackClient } from \"@btst/stack/client\"
import type { QueryClient } from \"@tanstack/react-query\"
export const getStackClient = (queryClient: QueryClient) =>
createStackClient({
plugins: {
// Add your client plugins here
},
})
Detection Heuristics
| Signal |
What it determines |
package.json dep: next |
Framework = Next.js |
package.json dep: react-router |
Framework = React Router |
package.json dep: @tanstack/react-router |
Framework = TanStack |
pnpm-lock.yaml present |
Package manager = pnpm |
yarn.lock present |
Package manager = yarn |
tsconfig.json paths @/* |
Alias = @/ |
tsconfig.json paths ~/* |
Alias = ~/ |
components.json absent |
Warn: shadcn not installed |
Layout Patching Strategy
The root layout patch (QueryClientProvider wrap) uses ts-morph to parse the TSX AST and find the JSX return value. If patching succeeds silently, great. If the file is non-standard (class component, unusual structure), the patcher prints a clear diff block with manual instructions rather than failing:
⚠ Could not automatically patch app/layout.tsx.
Add the following manually:
import { QueryClientProvider } from \"@tanstack/react-query\"
import { getOrCreateQueryClient } from \"@/lib/query-client\"
// Wrap your root {children} with:
<QueryClientProvider client={getOrCreateQueryClient()}>
{children}
</QueryClientProvider>
Non-Goals (v1)
- Scaffolding a brand-new framework project (wrapping
create-next-app etc.) — init targets existing projects only
- Multi-monorepo-workspace awareness
- Adding plugins via
btst init (that is a separate btst add command concern)
- Windows-specific path handling beyond basic normalisation
- Non-TypeScript projects
Implementation Notes
- Use
@clack/prompts for the interactive prompt UX (same as shadcn CLI)
- Use
commander for argument parsing
- Use
ts-morph for AST-based layout patching
- Use
handlebars for file templates (adapter × framework matrix)
- Use
execa for running package manager commands
- Build with
tsup or unbuild; ship as CJS + ESM with a bin entry
Package Configuration
{
\"name\": \"@btst/codegen\",
\"bin\": { \"btst\": \"./dist/index.cjs\" },
\"scripts\": {
\"build\": \"unbuild --clean\",
\"dev\": \"tsx src/index.ts\"
}
}
Documentation
Add docs/content/docs/cli.mdx (or extend the existing CLI page) covering:
@btst/codegen init — full walkthrough
- Supported frameworks — Next.js, React Router, TanStack Start
- Supported adapters — all five, with links to adapter docs
--yes flag — CI/CD usage
- Manual patching fallback — what to do when auto-patch fails
Overview
Add a
btst initcommand to the@btst/codegenpackage that automatically scaffolds all the boilerplate required to integrate BTST into an existing Next.js (App Router), React Router v7, or TanStack Start project.The install docs currently require ~8 steps of manual file creation and surgical edits to existing files. This command automates the entire flow end-to-end — detecting the framework, picking the right adapter, installing dependencies, generating files, and patching the root layout and CSS.
Think shadcn/ui's
npx shadcn init— a single command that gets a project from zero to working without consulting documentation.npx @btst/codegen init # or if @btst/codegen is already installed: codegen initCore Features
Framework Detection & Selection
package.jsondeps (next,react-router,@tanstack/react-router)tsconfig.jsonpaths (@/,~/, etc.)pnpm-lock.yaml,yarn.lock,package-lock.json)Adapter Selection
memory,prisma,drizzle,kysely,mongodb@btst/adapter-*package automaticallyDependency Installation
@btst/stackand@tanstack/react-queryvia detected package manager@btst/adapter-*packagePlugin Selection & Basic Config
init(e.g.blog,comments, etc.)lib/stack.tsandlib/stack-client.tsxFile Generation
lib/query-client.ts— shared QueryClient utility (identical across frameworks)lib/stack.ts— backend instance with selected adapter boilerplatelib/stack-client.tsx— client instance stub (createStackClient)app/api/data/[[...all]]/route.tsapp/routes/api/data/route.tssrc/routes/api/data/$.tsapp/pages/[[...all]]/page.tsxapp/routes/pages/index.tsxsrc/routes/pages/$.tsxStackProviderlayout:app/pages/[[...all]]/layout.tsxapp/routes/pages/_layout.tsxsrc/routes/pages/route.tsxSurgical File Patching
@import \"@btst/stack/...\"into the project's global CSS file (auto-detected or prompted)<QueryClientProvider>— attempted via AST transform (ts-morph), with printed fallback instructions if the file is non-standardPrerequisite Validation
components.json(shadcn) is absent<Toaster />(Sonner) is not presentPackage Structure
Command Interface
Interactive Prompt Flow
Generated File Examples
lib/stack.ts(memory adapter, Next.js)lib/stack-client.tsxDetection Heuristics
package.jsondep:nextpackage.jsondep:react-routerpackage.jsondep:@tanstack/react-routerpnpm-lock.yamlpresentyarn.lockpresenttsconfig.jsonpaths@/*@/tsconfig.jsonpaths~/*~/components.jsonabsentLayout Patching Strategy
The root layout patch (
QueryClientProviderwrap) usests-morphto parse the TSX AST and find the JSX return value. If patching succeeds silently, great. If the file is non-standard (class component, unusual structure), the patcher prints a clear diff block with manual instructions rather than failing:Non-Goals (v1)
create-next-appetc.) — init targets existing projects onlybtst init(that is a separatebtst addcommand concern)Implementation Notes
@clack/promptsfor the interactive prompt UX (same as shadcn CLI)commanderfor argument parsingts-morphfor AST-based layout patchinghandlebarsfor file templates (adapter × framework matrix)execafor running package manager commandstsuporunbuild; ship as CJS + ESM with abinentryPackage Configuration
{ \"name\": \"@btst/codegen\", \"bin\": { \"btst\": \"./dist/index.cjs\" }, \"scripts\": { \"build\": \"unbuild --clean\", \"dev\": \"tsx src/index.ts\" } }Documentation
Add
docs/content/docs/cli.mdx(or extend the existing CLI page) covering:@btst/codegen init— full walkthrough--yesflag — CI/CD usage