A plugin is a folder. You can add a battery, a design or a preset without touching a line of generator code. The whole review is a few commands you run on your own machine.
This page gets you oriented. The full documents live in the repo:
CONTRIBUTING.md: the full contract, three reference batteries to copy, and a step-by-step walkthrough.docs/architecture/CONTRACT.md: manifests, frontmatter formats, tokens, slots, merge rules, determinism.packages/core/src/types.ts: the pinned types. Import them. Never redefine them.
The folder
registry/<stacks|batteries|designs>/<id>/
manifest.yaml required
template/ copied into the generated repo
agent/
rules/*.md path-scoped conventions
skills/*.md slash commands
agents/*.md subagents
hooks/*.md hook definition plus its inline script
solutions/*.md long-form docs, published at /cookbook/<id>/<doc>
design.yaml designs only: the token set
DESIGN.md designs only
template/ paths are relative to the generated repo's root:
template/src/lib/email/postmark.ts lands at src/lib/email/postmark.ts.
Start by copying the reference battery closest to yours. There are three, and between them they cover every mechanism the registry has: Stripe (the common case), Better Auth (slot injection) and Neon (a hook).
The gate: no config, no battery
A battery isn't accepted until it ships:
- Rules: at least one, scoped to the paths your battery owns.
- At least one skill: a real slash command for work people repeat.
- At least five solution docs: the mistakes, in advance.
This isn't bureaucracy. People pick this generator over a paid kit because the
agent editing src/lib/billing/** already knows to verify the webhook
signature. A folder of files without that knowledge is what we compete against,
not what we build.
Write solution docs for a stranger. Every one is published as a public page in the cookbook. So no "as discussed above", no repo-internal references, no "we" about your team. Good ones answer what someone types into a search box: "idempotent Stripe webhooks", "Neon connection pooling on Vercel". Bad ones restate the vendor's quickstart.
The gate covers three of the six parts of the agentic layer. Agents, hooks and MCP servers are optional. Ship them when your battery needs them: Neon ships a hook and an agent, PostHog ships an agent, and 9 batteries ship an MCP server.
Two rules that trip people up
Never ship a merger-owned file in template/. Two plugins writing the same
path is a validation error. These eight are built by the mergers, and you add to
them through manifest.yaml instead (contributes.dependencies, .scripts,
.env, .mcp, .gitignore, .files):
package.json · tsconfig.json · .mcp.json · .claude/settings.json ·
CLAUDE.md · README.md · .env.example · .gitignore
Plug into the app through slots, never by editing its pages. A page in
the signed-in app gets its sidebar entry from the app-nav slot and its
dashboard card from dashboard-cards. A landing section goes in
landing-sections, and a vendor that receives user data adds itself to the
privacy policy through legal-processors. The full slot table is in
registry/README.md.
Never hard-code a package manager or a colour. Use {{pm}}, {{pmx}},
{{pmDlx}}, {{pmRun}} and {{pmAdd}} in every template file, script value
and markdown body. {{pmx}} runs a binary the repo installed. {{pmDlx}}
fetches a one-off tool. Use design tokens for anything visual. The template
lint fails on both. See the token contract.
The commands
There is no CI here and none in generated repos. That's a choice. These run on your machine, and they are the whole review. Please actually run them.
This repo pins Bun (packageManager: bun@1.3.5) and its scripts call bun
directly, so unlike a generated repo, these are Bun only:
shell
bun run validateManifest schema, path collisions, slots, the template lint and the battery gate. Fast enough to run after every edit.
shell
bun run combos --battery=<id>Generates your battery alone and in every pair it can join, in memory. Then it
checks the output: packages nothing installs, imports that point nowhere, tokens
and slots left unfilled, env vars nothing declares. Takes seconds. Add
--tier=coupled for the full product of the categories that share modules.
shell
bun run matrix --battery <id> --pairs --build --add-testedRuns your battery alone, in every pair it can join, and in any preset or
curated combo that has it. For each one: install, typecheck, lint, unit tests, next build with no env
set, then boot the app and load every static route. Pairs that pass go into
registry/tested.yaml. Leave out --pairs and no pair runs, so nothing new
gets certified.
If your battery touches sign-in, billing, the database or the admin pages, run the lanes too:
shell
bun run matrix --battery <id> --browserEach combo gets a fresh database on local Postgres, the repo's own migrations
and seeds, a build with that env, and then the repo's own Playwright specs:
sign-in, the app shell, checkout and the admin pages. --db alone stops before
the browser. See how the generator works.
Never hand-edit tested.yaml. A pair in that file claims the combination
was installed, checked, built and booted. The resolver believes it completely,
so a line added by hand is how a stranger ends up with a repo that doesn't boot.
shell
bun run snapshotRegenerates every preset and compares hashes. If a preset that doesn't include your battery changed, you touched something shared. Look hard at the diff before you explain it away.
bun run release-check runs validate, combos, snapshot and
matrix --build in order.
Designs and presets
A design ships design.yaml, a light and a dark palette, its own five core
components, three preview screens and a DESIGN.md written for an agent. Nirali reviews design pull requests. See
designs.
A preset is registry/presets/<id>.json, matching the Preset type.
Presets become pages, so description is two to three real paragraphs and
highlights four to six concrete bullets. The matrix runs every preset as a
whole.
Ownership
Every manifest has an owner: your GitHub handle. It says who decides whether a
change to the plugin is right, and who the maintainers go to first when the
vendor ships a breaking change. Handing a plugin over means changing owner in
the same pull request. A stale owner is worse than none.
Nothing pings the owner automatically yet. If you open an issue about a plugin, tag its owner.
Ravi reviews core, the CLI and agent-layer quality. Expect the review to be mostly about the six layers, because that is where the product is.