Skip to content

Scaffolding an app

Detailed reference for the app-platform apps init step of the app-creator skill (Step 1).


The command

bash
mkdir -p <parent-dir>
bunx app-platform apps init <app-name> --template <template> --output-dir <parent-dir> --include-skill --blank
cd <parent-dir>/<app-name> && bun install
  • <app-name> — short kebab-case name derived from the user's prompt (e.g. "a voting app" → voting-app).
  • <parent-dir> — directory the new app folder will be created inside. The CLI errors out if <parent-dir>/<app-name> already exists.
  • --template <template> — one of react, preact, solidjs, vanilla-js. Prefer vanilla-js or preact for simplicity; only use react or solidjs if the prompt specifically needs them.
  • --include-skill — copies the SDK's bundled skills (app-creator, synced-store, poe-app) into <app-dir>/.claude/skills/ so Claude Code inside the new checkout has them available locally.
  • --blank — replaces the todo-list example with empty stubs. Always pass this (see below).

Why --blank is mandatory

Without --blank, the scaffold writes a working todo-list app you'd have to delete and rewrite. With --blank you get the same boilerplate (package.json, vite config, tsconfig.json, biome, index.html, styles.css) but these files are empty stubs:

  • synced-store/schema.tsappSchema with tables: {} / mutators: {}
  • synced-store/mutators.tsappMutators: InferMutatorHandlers<AppSchema> = {}
  • synced-store/client-config.ts and backend-config.ts — wire the empty schema/mutators
  • client.ts — re-exports appSchema / AppSchema / AppStoreClient / appClientConfig / appMutators
  • ui/App.{tsx,ts} — hello-world stub
  • synced-store/mutators.test.ts, ui/App.test.happydom.tsxtest.todo() placeholders (colocated with their source)
  • tests/e2e.test.playwright.ts — single page-loads smoke

Day-zero bun run type-check / test / build / test:playwright all pass on the empty skeleton — no half-converted broken state while you write your real code.

Do not pass --committed

--committed is a flag for use inside the Poe app-platform monorepo. It rewrites poe-apps-sdk to workspace:* (which won't resolve outside the monorepo), pins zod to a version your project may not have, and emits a tsconfig.json that extends a parent which doesn't exist. Outside the monorepo, leave it off.

Reference app — scaffold a sibling, read it, then delete it

The blank stubs use generic names (appSchema, appMutators, AppStoreClient, appClientConfig, appBackendConfig). To see a worked example, re-scaffold a sibling directory without --blank, read it, then delete it:

bash
bunx app-platform apps init reference-todo --template <same-template> --output-dir /tmp
# read /tmp/reference-todo/synced-store/, /tmp/reference-todo/ui/, /tmp/reference-todo/tests/

Note the patterns; don't copy code blindly — your schema is yours.

Templates

Four templates are available: react, preact, solidjs, vanilla-js. Running app-platform apps init without --template prompts interactively. All templates use the poeApp() vite plugin and ship with the same boilerplate (package.json, vite, tsconfig, biome, index.html, styles.css). Without --blank, the scaffold writes a working todo-list example (synced-store schema, mutator tests, playwright E2E tests). With --blank, that example is replaced by stubs as listed above. The --blank and non-blank scaffolds are identical apart from the contents of the stubbed files.

Generated files (after scaffolding)

After bunx app-platform apps init ... && bun install finishes, the new directory contains:

<app-name>/
├── app/                       # entry point + backend wiring
│   └── src/
│       ├── entry.tsx          # calls Poe.setupStore(clientConfig); renders UI
│       └── backend.ts         # re-exports appBackendConfig as default
├── synced-store/              # store contract
│   ├── schema.ts
│   ├── mutators.ts
│   ├── mutators.test.ts       # colocated unit tests
│   ├── client-config.ts
│   └── backend-config.ts
├── ui/                        # store-agnostic components (receive `store` via props)
│   ├── App.{tsx,ts}
│   └── App.test.happydom.tsx  # colocated UI tests
├── tests/                     # e2e (Playwright) + setup-dom helper
├── scripts/
│   └── doctor.sh              # toolchain health check (used by Step 0)
├── client.ts                  # client-safe re-exports
├── package.json
├── tsconfig.json
├── vite.config.ts
└── playwright.config.ts

The scripts/doctor.sh script is what the agent runs in Step 0 of the parent skill before scaffolding anything new — once the app is scaffolded, the same script is available inside the app for re-running on demand.