Demos are specs, not templates
The obvious way to ship demo apps is to freeze three finished apps and copy the right one at scaffold time. We did not do that, and the reason is the whole architecture: a frozen template can only be copied, but a spec can be transformed. Each demo in geenius-mvp is a declarative JSON-shaped spec in tools/lib/demos.mjs, and a codegen module turns it into real code — schema blocks, CRUD backends, select-option constants, EntityConfig-driven pages, MCP tools — inside a freshly scaffolded app, for either database variant.
What a spec expresses
A demo is a title plus one or more entities. Each entity declares a table, singular and plural labels, an icon, a field list drawn from nine types (text, longtext, number, boolean, select, tags, date, image, position), role keys, and optional seed rows. Multi-entity works — the CRM generates contacts and an activities log from one spec, each with its own backend module and feature page. Seed rows become an idempotent seed mutation plus a Load sample data button that appears only when the workspace is empty. And the role keys carry the view logic: statusKey unlocks the board, dateKey the calendar and timeline, imageKey the gallery, amountKey the stat totals. Remove a role field with --fields and the codegen drops the key, so the dependent views auto-hide. The data model is the configuration; there is no second configuration to drift.
The transformations
Because the input is data, the CLI can operate on it before generating. --fields removes fields or adds and replaces them (with a type and an optional label), and both backend and frontend follow, down to seed rows dropping removed columns. --table and --labels rename the primary entity — the demo-eject move that makes the output yours. And --fields works without any demo at all: the generic records baseline is itself an entity spec, so pnpm new my-app --fields "+client:text,-position" reshapes it into your domain through the exact same pipeline. Demos are not a separate feature bolted on; they are the same machinery with richer inputs.
Bring your own spec
The spec format is public surface, not internal detail. Point --demo at a JSON file and the loader validates it and runs the same codegen:
pnpm new my-app --demo ./habit-tracker.json
A minimal valid spec is small enough to write by hand:
{
"title": "Habit Tracker",
"entities": [
{
"table": "habits",
"labels": ["Habit", "Habits"],
"titleKey": "name",
"statusKey": "cadence",
"fields": [
{ "key": "name", "label": "Name", "type": "text", "required": true },
{ "key": "cadence", "label": "Cadence", "type": "select", "options": [
{ "value": "daily", "label": "Daily", "tone": "accent" },
{ "value": "weekly", "label": "Weekly" }
] },
{ "key": "streak", "label": "Streak", "type": "number" }
],
"seed": [
{ "name": "Morning run", "cadence": "daily", "streak": 12 }
]
}
]
}
Validation is strict and errors name the rule broken: labels must be a singular-plural pair, selects need options, titleKey must name a field. Adding a built-in demo is one entry in demos.mjs — the codegen, the CLI catalog, and the self-test shapes all pick it up from there.
The manifest closes the loop
Generation ends by writing a .demo.json manifest into the app — feature ids, tables, routes, title keys, whether seeds and MCP are present. The smoke test reads it and exercises exactly what was generated, so a demo scaffold is verified by the same gate as everything else. When the mcp feature is on, the codegen also derives per-entity tools (list, create, and a set-status tool when there is a statusKey) and wires them into the app's MCP server, for both backends.
Why this is the AI-agent shape
For an agent, the difference between a template and a spec is the difference between reading code and writing data. The full spec format appears in pnpm new --list --json under demos, so one call teaches an agent the entire surface. From there, synthesizing a custom domain — invoices, applicants, bookings — is emitting one small JSON manifest and running one command. No view code, no backend boilerplate, no chance to hallucinate an API: the codegen writes code that is correct by construction, and the manifest plus smoke verify it. Short, declarative, hard to get wrong — that is the output shape agents are best at.
A template can only be copied. A spec can be reshaped, renamed, replaced — and written by anyone, including a model.
Three demos ship today. The interesting number is how many specs never ship with the repo at all.