Pie / PieUI
One ecosystem, two CLIs:
pie — Python/FastAPI backend. Source of truth for the command surface.
pieui — TypeScript/Next.js frontend. Mirrors pie where a frontend analog exists.
┌──────────────────────────────────────────────┐
│ Platform (pieui.swarm.ing) │
│ username/my-project/MyCard │
│ ├── python/ ← pie card remote push
│ └── typescript/ ← pieui card remote push
└──────────────────────────────────────────────┘
↑ push/pull ↑ push/pull
┌──────────────────┐ ┌─────────────────────────┐
│ Backend │ │ Frontend │
│ Python / FastAPI │ │ TypeScript / Next.js │
│ `pie` │ │ `pieui` │
│ pages/ │ │ piecomponents/ │
│ pages/components/│ │ app/ │
└──────────────────┘ └─────────────────────────┘
Read this first, then load one reference
This file is the map. Load the reference that matches the task — don't read them all.
| Reference |
Load it for |
references/pie-commands.md |
exact pie syntax: any backend command, flags, db, cloudflare, taskrun |
references/pieui-commands.md |
exact pieui syntax: any frontend command, flags, registry |
references/preview-and-mcp.md |
rendering one card: the harness, card show, show-mcp tools, launching/registering the MCP server, preview providers |
references/runtime-contracts.md |
ajax endpoints & deps derivation, the stored prop, pieui 3.0.0 migration, the Express backend, Centrifuge recovery |
references/workflows-and-edge-cases.md |
envelope policy, end-to-end recipes, known edge cases, monorepo layout, reading check-sync output |
references/style-isolation.md |
a card looks wrong and you're tempted to edit globals.css |
Command surface
pie |
pieui |
Notes |
create |
create / create-pie-app / create-pieui |
scaffold a project |
init |
init |
set up in an existing dir; links the other side |
login |
login |
writes .pie/config.json |
self-upgrade |
self-upgrade |
pie --pm uv|poetry|pip, pieui --pm bun|npm|pnpm|yarn |
card … |
card … |
largest surface |
page add|view|ajax |
page add|view|ajax |
|
web <mod:app> [verify|build] |
— |
backend only |
taskrun local|remote |
— |
backend only |
db … |
— |
divergence: Beanie/MongoDB |
cloudflare init|dev|deploy |
— |
divergence: Pyodide Worker |
card channels / card emit |
— |
divergence: Centrifuge introspection |
card show* |
— |
divergence: preview orchestration |
| — |
postbuild |
frontend only (manifest) |
| — |
registry dev|build |
frontend only (preview harness) |
| — |
card add-story / generate-preview / remove |
frontend only |
pie web takes a module:attribute, not a verb: pie web web:app verify.
There is no top-level pie card push — pushing is pie card remote push.
Cross-repo rule: any user-facing change in pie/__main__.py or pie/code/* must
be mirrored in pieui (src/code/{args,types}.ts, src/cli.ts,
src/code/commands/*). A PostToolUse hook nudges about this; it is safely ignored
for the divergences marked above.
Card types
pie |
pieui |
Shape |
simple |
simple |
only a data prop |
complex |
complex |
data + children |
container |
simple-container |
data + a single content slot |
complex-container |
complex-container |
data + content[] array |
The type is required on pie (omitting it is an argparse error). Only
pieui card add defaults to complex-container. Note the different spelling of the
single-slot container.
Flags on both: --io (realtime), --ajax (AJAX), --input (typed stored prop),
--from <ref> (port from the other side). All combinable.
Porting: --from reads the counterpart's metadata — a source file, a components
dir, a PieMetadata .json, or a bare card name resolved via the configured
frontendComponentsDir / backendComponentsDir. Omit --from and it auto-resolves
by name. Prefer this over hand-writing the other side.
Hard rules — never do manually
These must go through the CLI; doing them by hand breaks the registry or platform metadata.
| ❌ Never manually |
✅ Use instead |
Create a file in piecomponents/ |
pieui card add … |
Create app/<path>/page.tsx |
pieui page add <path> |
Edit piecomponents/registry.ts |
updated automatically by pieui card add/remove |
Create pages/components/*.py |
pie card add … |
Create pages/*.py |
pie page add … |
Add a methods/event key to <PieCard methods={…}> |
pieui card add-event <Card> <event> |
| Delete a piecomponent directory |
pieui card remove <Name> |
Plus:
"use client" at the top of every PIE card TSX file.
<button type="button"> always — without it the browser submits to /api/process/.
is_typed=False in every AsyncPage subclass that does not override get_content.
- Python
snake_case → camelCase on the frontend (send_label → sendLabel).
- After editing
web.py (new route) — restart the backend process.
- Never commit
.env, .pie/, .claude/, node_modules/, .next/, __pycache__/, .venv/.
pie card add-event is not implemented for Python sources — don't promise it.
Keeping the two sides in sync
pie card check-sync [NAME] # every card when NAME is omitted
pieui card check-sync <Name> # frontend still requires a name
pie card dump-metadata <NAME> # {"python": …} envelope — the raw input
pieui card dump-metadata <Name> # {"typescript": …} envelope
pie card check-sync exits 0 when aligned, 1 on any finding or build failure. It
shells out to the frontend CLI (bun $PIE_CHECK_SYNC_PIEUI_CLI → bunx pieui →
npx pieui) and needs the {"typescript": …} envelope back, so a stale pieui fails
every card. Not every finding is a bug — see references/workflows-and-edge-cases.md.
Environment & defaults
pie: uv run python -m pie … inside the pie repo; the installed pie entrypoint
elsewhere. Run from the target project root — config is read relative to CWD.
pieui: prefer bunx pieui …; bun src/cli.ts … inside the pieui repo.
- Generated paths: backend
pages/components/ (PIE_COMPONENTS_DIR), frontend
piecomponents/ + app/<path>/page.tsx, preview harness <project>/.pie/registry/.
- Env:
PIE_USER_ID / PIE_API_KEY (from login), PIE_PROJECT (or
PIE_PROJECT_SLUG, defaults to the cwd name), PIE_COMPONENTS_DIR. .env
auto-loads via python-dotenv.
- API keys are per project — a key for project A cannot read project B, even for
the same user. Log in per project.
- Before committing in the pie repo:
task fmt (CI enforces ruff format --check,
which task lint does not cover), then task lint, task typecheck.
Workflow
- Decide which side owns the change — the backend (
pie) is authoritative.
- Run the narrowest command with explicit args. Prefer
card view /
dump-metadata / the MCP list_cards over reading source to learn a card's shape.
- After a backend card change, run
pie card check-sync and port with
card add --from rather than hand-writing the counterpart.
- After any user-facing
pie CLI change, mirror it into pieui unless it falls
under a documented divergence.
- Report changed files and the follow-up command.
Safety rules
- Destructive or outward-facing — require explicit user intent:
card remote remove,
card remote public / private, db model remove, db import, db rollback,
cloudflare deploy.
db migrate / rollback --distance 0 means all migrations.
card emit / show-emit publish real events — confirm the channel first.
card show / show-mcp spawn a frontend next dev; if the user runs their own dev
server on that port, confirm before replacing it.
- Don't run
pieui postbuild --append unless the user wants built-in components merged.
- Don't commit machine-specific
.pie/config.json paths unless the team wants that.
When something breaks
- Anything in the preview → run the MCP
doctor tool first; every failing check
carries its own fix. Details in references/preview-and-mcp.md.
- Stale CSS / build errors in the preview after the source is fixed → the harness
has its own cache:
rm -rf <project>/.pie/registry/.next. Clearing the main app
.next does nothing.
- A card looks wrong → fix the card, not
globals.css (references/style-isolation.md).
- Contract mismatches →
references/workflows-and-edge-cases.md.
1---2name: pie-pieui3description: Use for any task in the Pie ecosystem — the Python `pie` CLI and FastAPI backend, the TypeScript `pieui` CLI and Next.js/React frontend, or both together. Covers project scaffolding, cards and pages, ajax handlers and the `stored` prop, backend↔frontend metadata sync, the card preview harness and pie-show MCP server, Centrifuge realtime, MongoDB/Beanie, Cloudflare Workers, the Express backend, remote component storage, and PieUI style isolation.4---56# Pie / PieUI78One ecosystem, two CLIs:910- **`pie`** — Python/FastAPI backend. **Source of truth** for the command surface.11- **`pieui`** — TypeScript/Next.js frontend. Mirrors `pie` where a frontend analog exists.1213```14┌──────────────────────────────────────────────┐15│ Platform (pieui.swarm.ing) │16│ username/my-project/MyCard │17│ ├── python/ ← pie card remote push18│ └── typescript/ ← pieui card remote push19└──────────────────────────────────────────────┘20 ↑ push/pull ↑ push/pull21┌──────────────────┐ ┌─────────────────────────┐22│ Backend │ │ Frontend │23│ Python / FastAPI │ │ TypeScript / Next.js │24│ `pie` │ │ `pieui` │25│ pages/ │ │ piecomponents/ │26│ pages/components/│ │ app/ │27└──────────────────┘ └─────────────────────────┘28```2930## Read this first, then load one reference3132This file is the map. Load the reference that matches the task — don't read them all.3334| Reference | Load it for |35|---|---|36| `references/pie-commands.md` | exact `pie` syntax: any backend command, flags, db, cloudflare, taskrun |37| `references/pieui-commands.md` | exact `pieui` syntax: any frontend command, flags, registry |38| `references/preview-and-mcp.md` | rendering one card: the harness, `card show`, show-mcp tools, launching/registering the MCP server, preview providers |39| `references/runtime-contracts.md` | ajax endpoints & deps derivation, the `stored` prop, pieui 3.0.0 migration, the Express backend, Centrifuge recovery |40| `references/workflows-and-edge-cases.md` | envelope policy, end-to-end recipes, known edge cases, monorepo layout, reading check-sync output |41| `references/style-isolation.md` | a card looks wrong and you're tempted to edit `globals.css` |4243## Command surface4445| `pie` | `pieui` | Notes |46|---|---|---|47| `create` | `create` / `create-pie-app` / `create-pieui` | scaffold a project |48| `init` | `init` | set up in an existing dir; links the other side |49| `login` | `login` | writes `.pie/config.json` |50| `self-upgrade` | `self-upgrade` | `pie --pm uv\|poetry\|pip`, `pieui --pm bun\|npm\|pnpm\|yarn` |51| `card …` | `card …` | largest surface |52| `page add\|view\|ajax` | `page add\|view\|ajax` | |53| `web <mod:app> [verify\|build]` | — | backend only |54| `taskrun local\|remote` | — | backend only |55| `db …` | — | **divergence**: Beanie/MongoDB |56| `cloudflare init\|dev\|deploy` | — | **divergence**: Pyodide Worker |57| `card channels` / `card emit` | — | **divergence**: Centrifuge introspection |58| `card show*` | — | **divergence**: preview orchestration |59| — | `postbuild` | frontend only (manifest) |60| — | `registry dev\|build` | frontend only (preview harness) |61| — | `card add-story` / `generate-preview` / `remove` | frontend only |6263`pie web` takes a **`module:attribute`**, not a verb: `pie web web:app verify`.64There is no top-level `pie card push` — pushing is `pie card remote push`.6566**Cross-repo rule:** any user-facing change in `pie/__main__.py` or `pie/code/*` must67be mirrored in `pieui` (`src/code/{args,types}.ts`, `src/cli.ts`,68`src/code/commands/*`). A PostToolUse hook nudges about this; it is safely ignored69for the divergences marked above.7071## Card types7273| `pie` | `pieui` | Shape |74|---|---|---|75| `simple` | `simple` | only a `data` prop |76| `complex` | `complex` | `data` + `children` |77| `container` | `simple-container` | `data` + a single `content` slot |78| `complex-container` | `complex-container` | `data` + `content[]` array |7980**The type is required on `pie`** (omitting it is an argparse error). Only81`pieui card add` defaults to `complex-container`. Note the different spelling of the82single-slot container.8384Flags on both: `--io` (realtime), `--ajax` (AJAX), `--input` (typed `stored` prop),85`--from <ref>` (port from the other side). All combinable.8687**Porting:** `--from` reads the counterpart's metadata — a source file, a components88dir, a PieMetadata `.json`, or a bare card name resolved via the configured89`frontendComponentsDir` / `backendComponentsDir`. Omit `--from` and it auto-resolves90by name. Prefer this over hand-writing the other side.9192## Hard rules — never do manually9394These must go through the CLI; doing them by hand breaks the registry or platform metadata.9596| ❌ Never manually | ✅ Use instead |97|---|---|98| Create a file in `piecomponents/` | `pieui card add …` |99| Create `app/<path>/page.tsx` | `pieui page add <path>` |100| Edit `piecomponents/registry.ts` | updated automatically by `pieui card add/remove` |101| Create `pages/components/*.py` | `pie card add …` |102| Create `pages/*.py` | `pie page add …` |103| Add a methods/event key to `<PieCard methods={…}>` | `pieui card add-event <Card> <event>` |104| Delete a piecomponent directory | `pieui card remove <Name>` |105106Plus:107108- `"use client"` at the top of **every** PIE card TSX file.109- `<button type="button">` always — without it the browser submits to `/api/process/`.110- `is_typed=False` in every `AsyncPage` subclass that does not override `get_content`.111- Python `snake_case` → **camelCase** on the frontend (`send_label` → `sendLabel`).112- After editing `web.py` (new route) — **restart the backend process**.113- Never commit `.env`, `.pie/`, `.claude/`, `node_modules/`, `.next/`, `__pycache__/`, `.venv/`.114- `pie card add-event` is **not implemented** for Python sources — don't promise it.115116## Keeping the two sides in sync117118```bash119pie card check-sync [NAME] # every card when NAME is omitted120pieui card check-sync <Name> # frontend still requires a name121pie card dump-metadata <NAME> # {"python": …} envelope — the raw input122pieui card dump-metadata <Name> # {"typescript": …} envelope123```124125`pie card check-sync` exits `0` when aligned, `1` on any finding or build failure. It126shells out to the frontend CLI (`bun $PIE_CHECK_SYNC_PIEUI_CLI` → `bunx pieui` →127`npx pieui`) and needs the `{"typescript": …}` envelope back, so a stale pieui fails128every card. Not every finding is a bug — see `references/workflows-and-edge-cases.md`.129130## Environment & defaults131132- **`pie`**: `uv run python -m pie …` inside the pie repo; the installed `pie` entrypoint133 elsewhere. Run from the target project root — config is read relative to CWD.134- **`pieui`**: prefer `bunx pieui …`; `bun src/cli.ts …` inside the pieui repo.135- Generated paths: backend `pages/components/` (`PIE_COMPONENTS_DIR`), frontend136 `piecomponents/` + `app/<path>/page.tsx`, preview harness `<project>/.pie/registry/`.137- Env: `PIE_USER_ID` / `PIE_API_KEY` (from `login`), `PIE_PROJECT` (or138 `PIE_PROJECT_SLUG`, defaults to the cwd name), `PIE_COMPONENTS_DIR`. `.env`139 auto-loads via python-dotenv.140- **API keys are per project** — a key for project A cannot read project B, even for141 the same user. Log in per project.142- Before committing in the pie repo: `task fmt` (CI enforces `ruff format --check`,143 which `task lint` does **not** cover), then `task lint`, `task typecheck`.144145## Workflow1461471. Decide which side owns the change — the backend (`pie`) is authoritative.1482. Run the narrowest command with explicit args. Prefer `card view` /149 `dump-metadata` / the MCP `list_cards` over reading source to learn a card's shape.1503. After a backend card change, run `pie card check-sync` and port with151 `card add --from` rather than hand-writing the counterpart.1524. After any user-facing `pie` CLI change, mirror it into `pieui` unless it falls153 under a documented divergence.1545. Report changed files and the follow-up command.155156## Safety rules157158- Destructive or outward-facing — require explicit user intent: `card remote remove`,159 `card remote public` / `private`, `db model remove`, `db import`, `db rollback`,160 `cloudflare deploy`.161- `db migrate` / `rollback --distance 0` means **all** migrations.162- `card emit` / `show-emit` publish real events — confirm the channel first.163- `card show` / `show-mcp` spawn a frontend `next dev`; if the user runs their own dev164 server on that port, confirm before replacing it.165- Don't run `pieui postbuild --append` unless the user wants built-in components merged.166- Don't commit machine-specific `.pie/config.json` paths unless the team wants that.167168## When something breaks169170- **Anything in the preview** → run the MCP `doctor` tool first; every failing check171 carries its own `fix`. Details in `references/preview-and-mcp.md`.172- **Stale CSS / build errors in the preview after the source is fixed** → the harness173 has its **own** cache: `rm -rf <project>/.pie/registry/.next`. Clearing the main app174 `.next` does nothing.175- **A card looks wrong** → fix the card, not `globals.css` (`references/style-isolation.md`).176- **Contract mismatches** → `references/workflows-and-edge-cases.md`.