pyreon

@pyreon/create-zero is the interactive project scaffolder for Pyreon Zero. One command walks you through template choice, rendering mode, deployment adapter, feature selection, backend integrations, AI tooling, and framework-migration compat — then writes a ready-to-run project. Every prompt also has a flag, so the whole flow is scriptable for CI, templates repos, and --yes one-liners.

@pyreon/create-zerostable

Installation

You don't install it — you invoke it through your package manager's create command. It ships under two bin aliases (create-pyreon-app is canonical, create-zero is the back-compat alias) but both run the same scaffolder.

npm create @pyreon/zero@latest my-app
bun create @pyreon/zero my-app
pnpm create @pyreon/zero my-app
yarn create @pyreon/zero my-app

Then start the dev server:

cd my-app
npm install
npm run dev
cd my-app
bun install
bun run dev
cd my-app
pnpm install
pnpm run dev
cd my-app
yarn
yarn dev

The scaffolder never installs dependencies itself — it prints cd, install, and dev as next steps and stops. (It accepts --install / --no-install as no-ops, since people pass them out of habit from create-vite / create-next-app.)

Why a scaffolder?

A Pyreon Zero project has several genuinely-computed pieces — the dependencies map depends on which features you pick, the vite.config.ts plugins array depends on rendering mode + adapter + compat shim, and the deploy artefacts depend on the target platform. Wiring all of that by hand means cross-referencing docs and keeping versions in sync. The scaffolder does it in one pass:

  • Feature → dependency resolution — pick query and it adds @pyreon/query + @tanstack/query-core; pick forms and it adds @pyreon/form + @pyreon/validation + zod. Every @pyreon/* dep is pinned to the same monorepo version as the scaffolder itself, so the set is always internally consistent.

  • Mode + adapter → vite.config.ts — the generated config imports the matching *Adapter() factory and sets zero({ mode, ssr }) for you.

  • Platform → deploy artefactsvercel.json, wrangler.toml, Dockerfile, etc. land alongside your code.

  • AI tooling — generates CLAUDE.md, .mcp.json, Cursor rules, and more from one shared "Pyreon principles" body so the guidance stays consistent across editors.

Quick Start

The fastest non-interactive paths, by template:

# Standard app (store + query + forms), SSR streaming, Vercel
npm create @pyreon/zero@latest my-app -- --preset standard --yes

# SSG markdown blog on Cloudflare Pages
npm create @pyreon/zero@latest my-blog -- --template blog --adapter cloudflare --yes

# SaaS-shape dashboard with Supabase + Resend
npm create @pyreon/zero@latest my-saas -- --template dashboard \
  --integrations supabase,email --yes

# Bun-workspaces monorepo
npm create @pyreon/zero@latest my-mono -- --template monorepo --preset standard --yes

The interactive flow

Run without --yes (or with any prompt's flag omitted) and you get an ordered series of prompts. Every prompt is skipped when its flag is supplied — flags and prompts are interchangeable.

  1. Project name — the positional argument, or a text prompt. The target directory must not already exist (it bails if it does). Required up front when using --yes.

  2. Templateapp / blog / dashboard / monorepo.

  3. Rendering modessr-stream / ssr-string / ssg / spa. Skipped for templates that force a mode (blog → SSG, dashboard → SSR streaming).

  4. Deployment target — filtered to the adapters the chosen template supports.

  5. Feature preset — pick a preset, or Custom to drop into a grouped multiselect of all 22 features (8 categories).

  6. Package imports@pyreon/meta single barrel, or individual packages.

  7. Backend integrations — Supabase, Email (Resend), or none.

  8. AI tooling — multi-select of rule files (MCP, CLAUDE.md, Cursor, Copilot, AGENTS.md).

  9. Compat mode — native Pyreon, or a React / Vue / Solid / Preact migration shim.

  10. Lint — include @pyreon/lint (the prompt advertises "59 Pyreon-specific rules").

Cancelling at any prompt (Ctrl-C) aborts cleanly.

Templates

Four curated starting points, selected by --template or interactively. Each declares its own default rendering mode, default feature set, supported adapters, and (for dashboard) preselected integrations.

TemplateDefault modeMode forced?Default featuresWhat you get
appSSR streamingnostore + query + formsFull-featured starter — counter, posts list + detail, an (admin) route group, _layout / _error / _loading, and api/ route handlers.
blogStatic (SSG)yes(none)Markdown-style TSX posts in src/content/posts/, an RSS feed at /api/rss, SEO-ready. Static-first — node / bun adapters are excluded.
dashboardSSR streamingyesstore + query + forms + tableSaaS shape — marketing landing → auth-gated app/* routes (dashboard, users, invoices, settings) → an invoice PDF/email export demo built on @pyreon/document-primitives. Server-required, so static is excluded. Preselects both integrations.
monorepoSSR streamingnostore + query + formsBun workspaces shell — apps/web/ (a full app-shaped project) + packages/ui/ + packages/types/.
npm create @pyreon/zero@latest my-app -- --template dashboard
npm create @pyreon/zero@latest my-blog -- --template blog

Generated structure (flat templates)

The app template scaffolds the following. blog and dashboard share the same shell (entry points, config, public/) with different src/routes/ content.

my-app/
├── package.json            # generated — deps computed from your selections
├── vite.config.ts          # generated — plugins computed from mode + adapter + compat
├── tsconfig.json           # strict, jsxImportSource: @pyreon/core, moduleResolution: bundler
├── index.html              # #app mount point + theme-flash-prevention script
├── env.d.ts                # ambient types for virtual:zero/* modules
├── .gitignore
├── README.md
├── public/
│   └── favicon.svg
└── src/
    ├── entry-client.ts     # startClient({ routes }) from @pyreon/zero/client
    ├── entry-server.ts     # createServer({ ... }) with the SSR mode baked in
    ├── global.css
    └── routes/             # file-based routing
        ├── index.tsx
        ├── about.tsx
        ├── counter.tsx
        ├── _layout.tsx
        ├── _error.tsx
        ├── _loading.tsx
        ├── (admin)/dashboard.tsx
        ├── api/health.ts
        ├── api/posts.ts
        └── posts/
            ├── index.tsx
            └── [id].tsx

There is no pyreon.config.ts — all configuration lives in vite.config.ts via the zero({ ... }) plugin and in tsconfig.json. Selecting the store feature adds src/stores/app.ts (and swaps in a store-aware _layout.tsx); selecting feature adds src/features/posts.ts + a posts/new.tsx route. Integration and AI selections add their own files (see below).

Monorepo layout

--template monorepo scaffolds the web app first (running the full flat pipeline against apps/web/ with the app shape), then wraps it in a Bun workspaces root:

my-mono/
├── package.json            # workspaces: ["apps/*", "packages/*"] + proxy scripts
├── tsconfig.json           # root TS settings
├── README.md
├── .gitignore
├── apps/
│   └── web/                # full app-shaped project
│       ├── package.json    # name: "web" + workspace deps on the shared packages
│       └── …               # everything the `app` template produces
└── packages/
    ├── ui/                 # @my-mono/ui — shared component stub
    │   ├── package.json    # deps: { "@my-mono/types": "workspace:^" }
    │   ├── tsconfig.json
    │   └── src/index.ts    # Button + ButtonProps stub
    └── types/              # @my-mono/types — framework-agnostic shared types
        ├── package.json
        ├── tsconfig.json
        └── src/index.ts    # ButtonVariant + User stubs

The @<projectName>/ scope is derived from the project name automatically — no extra prompt. The root package.json carries no dependencies; it's a dispatcher whose dev / build / preview scripts proxy to the web app via bun run --filter='web' …, and typecheck runs --filter='*'. All your feature / adapter / integration / AI / lint choices apply to the inner web app (which is always app-shaped today).

Rendering modes

Set with --mode (ignored when the template forces a mode). The mode maps directly to the generated vite.config.ts and entry-server.ts.

Modezero({ ... })When to use
ssr-streammode: 'ssr', ssr: { mode: 'stream' }Recommended. Progressive HTML with Suspense — best TTFB.
ssr-stringmode: 'ssr'Buffered HTML — simpler, slower first byte.
ssgmode: 'ssg'Pre-rendered at build time. No runtime server.
spamode: 'spa'Client-only — no server rendering.

At the server level the ssr.mode field only has stream / string; ssg and spa both fall back to string (SSG renders once at build time; SPA never SSRs). See Zero → Rendering modes for the full semantics.

Deployment adapters

Pick a target during the prompt or pass --adapter. The prompt is filtered to the adapters the chosen template supports. Each adapter writes its platform deploy artefacts and (where applicable) injects the matching *Adapter() factory into the generated vite.config.ts.

AdapterVite factoryFiles writtenEnv keys
vercelvercelAdapter()vercel.json
cloudflarecloudflareAdapter()wrangler.toml (the adapter writes the authoritative dist/_routes.json at build time)
netlifynetlifyAdapter()netlify.toml (render-mode-aware — SSR modes publish dist/publish + wire the ssr function from dist/netlify/functions; SSG/SPA publish dist)
nodenodeAdapter()Dockerfile, .dockerignorePORT
bunbunAdapter()Dockerfile (Bun-based), .dockerignorePORT
static(none)(none — dist/ after build is the whole site)
npm create @pyreon/zero@latest my-app -- --adapter cloudflare

Features

22 Pyreon fundamentals can be enabled per project. The interactive flow offers a preset shortcut first; choosing Custom drops you into a multiselect grouped into 8 categories for discoverability.

Presets

Four atomic shortcuts via --preset. Each is a (features, mode, adapter) triple:

PresetFeature setModeAdapter
minimal(none)spastatic
standardstore + query + formsssr-streamvercel
dashboardstandard + table + chartsssr-streamvercel
fullevery feature (22)ssr-streamvercel
npm create @pyreon/zero@latest my-app -- --preset standard --yes
npm create @pyreon/zero@latest my-app -- --preset full --yes

Feature categories

When you pick Custom, the multiselect groups features for browsing:

CategoryFeatures
State managementstore, state-tree, storage, url-state
Forms + validationforms, feature
Data fetching + transformsquery, rx
UI primitives + animationstyler, elements, animations, coolgrid
Collections + tablestable, virtual
Interactive widgetscharts, code, flow, toast
Internationalization + accessibilityi18n, hotkeys, permissions
Utility hookshooks

Each feature pulls its package(s) into dependencies. Some pull peers — query adds @tanstack/query-core, forms adds @pyreon/validation + zod, and the feature (CRUD) bundle pulls store + query + forms + their peers.

Atomic add / remove

--with-<feature> adds one feature; --no-<feature> removes one. They compose on top of whatever the base set is (preset, explicit --features, or template default under --yes):

# Standard preset, plus i18n, minus forms
npm create @pyreon/zero@latest my-app -- --preset standard --with-i18n --no-forms --yes

--no-X always wins over --with-X for the same feature (set-subtraction semantics). An unknown feature in --with- / --no- is a hard error that prints the known list.

Explicit feature list

--features <csv> is an explicit set that overrides --preset entirely (only --with- / --no- still compose on top):

npm create @pyreon/zero@latest my-app -- --features store,query,i18n --with-toast --yes

Resolution order

When several feature sources are combined, the highest-priority source wins as the base, then --with-X / --no-X overlay on top:

  1. --features <csv> — explicit list, ignores preset + template default

  2. --preset <id> — preset's feature set as the base (skips the feature prompts)

  3. --yes (no preset / features) — the chosen template's default features

  4. Interactive — preset prompt → Custom → grouped multiselect

Package imports

The --packages flag (prompt: "Package imports") chooses how features reach your code:

ValueBehaviour
metaAdds @pyreon/meta — a single barrel re-exporting everything, tree-shaken at build. Simpler imports.
individualInstalls only the packages your selected features need — smaller node_modules.
npm create @pyreon/zero@latest my-app -- --packages individual --yes

Either way, the specific feature packages are still added to dependencies (templates import them by their real package name, e.g. @pyreon/query); meta just adds the barrel on top for ergonomic imports.

Backend integrations

Two scaffolders that write plain files into your project — no Pyreon-side wrapper packages and no version coupling. You own the code and update it independently of Pyreon releases. Select with --integrations (csv) or the multiselect.

IntegrationFiles writtenDependenciesEnv keys
supabasesrc/lib/supabase.ts (+ src/lib/auth.ts, src/lib/db.ts on the dashboard template)@supabase/supabase-jsSUPABASE_URL, SUPABASE_ANON_KEY
emailsrc/lib/email.ts, src/emails/welcome.tsx, src/routes/api/email/welcome.tsresend, @pyreon/document-primitives, @pyreon/document, @pyreon/connector-documentRESEND_API_KEY, EMAIL_FROM

Each integration appends its env keys to a generated .env.example. On the dashboard template, Supabase's auth.ts / db.ts replace the template's in-memory stubs (same contract). The dashboard template preselects both integrations; on app / blog they're off by default but selectable.

npm create @pyreon/zero@latest my-saas -- --template dashboard --integrations supabase,email --yes

The email integration showcases the document-primitives angle: the same <DocDocument> / <DocSection> / <DocText> tree renders in the browser and exports to email HTML via @pyreon/document-primitives — one author surface for many output formats.

AI tooling

Generate per-tool rule files so AI assistants understand Pyreon's conventions. All share a canonical "Pyreon principles" body, so the guidance stays consistent across editors. Select with --ai (csv) or the multiselect.

OptionFile writtenDefault
mcp.mcp.json (also adds @pyreon/mcp as a dev dep)
claudeCLAUDE.md (includes a bun run doctor line)
cursor.cursor/rules/pyreon.md
copilot.github/copilot-instructions.md
agentsAGENTS.md
npm create @pyreon/zero@latest my-app -- --ai mcp,claude,cursor --yes

Compat mode

Migrating from another framework? --compat (or the "Migrating from another framework?" prompt) configures @pyreon/vite-plugin with the matching shim and adds the matching compat package.

ValuePackage addedLets you write
noneNative Pyreon (recommended)
react@pyreon/react-compatuseState, useEffect, …
vue@pyreon/vue-compatref, computed, watch, …
solid@pyreon/solid-compatcreateSignal, createEffect, …
preact@pyreon/preact-compatuseState, signals, …
npm create @pyreon/zero@latest my-app -- --compat react --yes

Lint

--lint / --no-lint (prompt: "Include @pyreon/lint?", default yes) toggles @pyreon/lint. When enabled, the scaffolder adds the dev dep, writes a .pyreonlintrc.json with the recommended preset, and adds a lint script (pyreon-lint .).

npm create @pyreon/zero@latest my-app -- --no-lint --yes

Generated package.json scripts

Every scaffolded project gets these scripts (lint adds one more when enabled):

ScriptCommandPurpose
devzero devDev server with HMR
buildzero buildProduction build
previewzero previewPreview the production build locally
doctorzero doctorProject health audit (React-pattern + anti-pattern detection)
doctor:fixzero doctor --fixAuto-fix what the audit can
doctor:cizero doctor --ciExit non-zero on findings (CI gate)
lintpyreon-lint .Only present when @pyreon/lint is enabled

The dev dependencies always include @pyreon/vite-plugin, @pyreon/zero-cli (the zero CLI), typescript, and vite. See Zero CLI for the zero command surface.

CLI flags

Every prompt maps to a flag, so the entire flow is scriptable. Run --help (or -h) for the built-in usage text.

FlagValues
[name]Positional project name (required with --yes)
--template <id>app / blog / dashboard / monorepo
--mode <id>ssr-stream / ssr-string / ssg / spa / isr
--typed-routes / --no-typed-routesTyped routes (<Link href> autocomplete + typo rejection); default on
--adapter <id>vercel / cloudflare / netlify / node / bun / static
--preset <id>minimal / standard / dashboard / full (composes with --with-X / --no-X)
--features <csv>e.g. store,query,forms — overrides --preset entirely
--with-<feature>atomic add, e.g. --with-store --with-i18n
--no-<feature>atomic remove, e.g. --no-forms
--integrations <csv>supabase,email
--ai <csv>mcp,claude,cursor,copilot,agents
--compat <id>none / react / vue / solid / preact
--packages <id>meta (single barrel) / individual (selected packages only)
--lint / --no-linttoggle @pyreon/lint
--yesskip every prompt that has a usable default
--install / --no-installaccepted no-ops (the scaffolder never installs)
--help, -hprint usage and exit

--packages also accepts the aliases --pm and --package-strategy. An unknown flag, an invalid enum value, or an extra positional argument fails fast with an actionable message and a non-zero exit code.

Worked examples

# Standard app, defaults for everything else
npm create @pyreon/zero@latest my-app -- --yes

# Dashboard + Supabase + Resend + full AI tooling, on Vercel
npm create @pyreon/zero@latest my-saas -- --template dashboard \
  --adapter vercel \
  --integrations supabase,email \
  --ai mcp,claude,cursor \
  --yes

# Standard preset, plus i18n, minus forms
npm create @pyreon/zero@latest my-app -- --preset standard --with-i18n --no-forms --yes

# Blog on Cloudflare Pages
npm create @pyreon/zero@latest my-blog -- --template blog --adapter cloudflare --yes

# Monorepo with the full preset, individual package imports
npm create @pyreon/zero@latest my-mono -- --template monorepo \
  --preset full --packages individual --yes

# React migration target, no lint
npm create @pyreon/zero@latest my-app -- --compat react --no-lint --yes

After scaffolding

cd my-app
bun install        # or npm install / pnpm install / yarn
bun run dev        # zero dev — HMR dev server

The generated project is a complete Pyreon Zero app: file-based routing, SSR/SSG/SPA per your mode, @pyreon/vite-plugin wired up, strict TypeScript with jsxImportSource: @pyreon/core, and your selected features, adapter, integrations, and AI rule files all in place. From here, see:

  • Zero — the meta-framework the scaffolder targets (routing, rendering modes, adapters, server actions).

  • Zero CLI — the zero command your scripts call (dev / build / preview / doctor).

  • SSG — static generation (the blog template's mode).

  • Deployment — per-platform deploy notes for each adapter.

  • Create Multiplatform — the scaffolder for iOS + Android + web targets, when you need native.

Reference

FlagDefault (interactive)Notes
--templateprompt (app under --yes)Drives mode/adapter/feature/integration defaults
--modetemplate defaultSkipped when the template forces a mode
--adaptertemplate defaultPrompt filtered to template-supported adapters
--presetprompt (Custom)minimal / standard / dashboard / full
--featurestemplate defaultcsv; overrides --preset; not validated
--with-<feature>composes on top of the base set; errors on unknown
--no-<feature>composes on top; wins over --with- for the same feature
--integrationstemplate default ([], both for dashboard)supabase / email
--aimcp,claudemcp / claude / cursor / copilot / agents
--compatnonereact / vue / solid / preact shim
--packagespromptmeta / individual (aliases: --pm, --package-strategy)
--linttrue--no-lint to opt out
--yesrequires a positional name
@pyreon/create-zero