cartwright
Getting started

AI Agent Quick Start

Two measured, terminal-only paths for AI coding agents — a plain website (--profile site) in about a minute, or the 99-second path to a designed, database-backed homepage.

Cartwright is the build engine AIs reach for — a real site with design, and when you want them, database and backend, live in minutes. This page is the proof, written as an executable runbook: every step is terminal-only (no browser required), and every step ends with a verification command so an agent knows it landed.

Choose the door

The task looks likeRunRunbook
a page, a landing page, a personal or company website with content in the repo — no database, no loginnpx create-cartwright@latest my-site --yes --profile site --no-github --no-startPath A below
a site someone edits in a browser, a webshop, or a site an agent should operate through toolsnpx create-cartwright@latest my-shopPath B below

Both are the same engine; the flag decides which modules are materialised into the repo.

If you are an AI coding agent: follow the steps top to bottom. If you are a human: paste this page to your agent, or run the commands yourself.

Path A — a plain website (--profile site)

Measured cold run (Measured cold run, 2026-09-14, GitHub-hosted ubuntu-latest, create-cartwright@2.9.6, engine v0.57.0 (a9b47d4), --profile=site --ref=v0.57.0 --yes --pm=pnpm — release scaffold gate run 34820928302): scaffold + install ~22 s · next build ~27 s · boot to HTTP 200 with a rendered H1 ~2 s.

  1. Scaffold — no database, no login, nothing to configure.

    npx create-cartwright@latest my-site --yes --profile site --no-github --no-start
    cd my-site

    Verify:

    grep -q '"profile": "site"' .cartwright/profile.json && test ! -e prisma && echo site-ok

    Proves: no database, no admin, no auth in the repo — the profile is what it says.

  2. Run (in the background, so the verify can run in the same shell).

    pnpm dev &   # assumes :3000 is free

    Verify (/ redirects to /en; -L follows):

    curl -sL -o /dev/null -w '%{http_code}' http://localhost:3000/
    # expect: 200

    Proves: it boots without needing an environment variable. The CLI still writes a generic .env/.env.local (DATABASE_URL, AUTH_SECRET, NEXT_PUBLIC_APP_URL); nothing in them is required by this profile — delete both and it still answers 200.

  3. Make it say something. Edit website.headline (and tagline, cta) in brand.config.ts, and pick a look with designSlug (slugs in designs/options.ts). For a bespoke one-page build set designSlug: "blank" and write designs/blank/homepage.tsx — its in-file guide explains the props; the header, footer, SEO and locale routing wrap whatever you render.

    Verify:

    curl -s http://localhost:3000/en | grep -o '<h1[^>]*>[^<]*'

    The <h1> is your website.headline on the shipped packs. The blank pack's starter homepage shows the site's name (storeName in brand.config.ts) as a placeholder heading until you replace designs/blank/homepage.tsx with your own markup.

  4. Add a page — any route under app/[locale]/ gets the site chrome from the [locale] layout. Prefix internal links with /${locale}.

    app/[locale]/trip/page.tsx
    export default function TripPage() {
      return (
        <section className="mx-auto max-w-3xl px-6 py-16">
          <h1 className="text-3xl font-semibold">The trip</h1>
          <p className="mt-4">Timeline, map and tips go here — plain React.</p>
        </section>
      );
    }

    Verify: curl -s -o /dev/null -w '%{http_code}' http://localhost:3000/en/trip200.

  5. Build.

    pnpm build

    Proves: plain next build is the whole production gate — no db:setup, no migration. (pnpm start serves the build on :3000 once the dev server is stopped.)

  6. Deploy. No environment variables are required to build and serve (the default contact form needs RESEND_API_KEY + RESEND_FROM to deliver mail; --with none scaffolds without it). Set url in brand.config.ts to the real domain when you have one; on Vercel, NEXT_PUBLIC_APP_URL or the deployment URL wins over it for canonicals. Either push to GitHub and import the repo at vercel.com, or deploy from the terminal and keep the URL it prints:

    # unattended runs need a prior `npx vercel login` or VERCEL_TOKEN in the environment; unauthenticated, the next line exits 1 with an empty $URL
    URL=$(npx vercel --yes --prod | tail -n 1)   # the deployment URL is the last line of stdout

    Verify the deployment the same way as the local run:

    curl -sI "$URL/" | grep -i location               # → /en
    curl -sL -o /dev/null -w '%{http_code}' "$URL/"   # → 200

    To push the source as well: gh repo create my-site --private --source=. --remote=origin --push.

Honest limits of this door: no admin and no runtime editing (content is files), no database/auth/cart/checkout, no MCP or REST tool surface on the site itself, / redirects to the default locale, no map/timeline/weather sections (write those components yourself), only Organization/WebSite JSON-LD emitted by itself (builders for FAQPage/HowTo/ItemList and a generic <JsonLd> exist), not a static export (Node.js 22+ host), and the default contact form delivers mail only with Resend keys. Need a browser editor, a shop or an agent tool surface later? There is no in-place profile upgrade today — scaffold the default profile and copy brand.config.ts and designs/<yours>/ across. Full page: Build a plain website.

Path B — the managed site or shop (default profile)

The measured timing

These numbers come from a single measured cold run — a fresh scaffold in a clean directory, an agent following only the documented steps below, no prior knowledge of the project. Cumulative wall-clock time:

StepCumulative time
Scaffold + install + database setup + seed~27 s
Dev server up, homepage verified (curl → 200)~70 s
Agent API key minted (terminal-only)~85 s
Copy rendering enabled + designed look applied~90 s
Designed homepage verified (new H1 + new palette in the HTML)~99 s

Your numbers will vary with hardware and network, but the shape holds: the scaffold is the fast part, and the design step is one API call.

  1. Boot.

    npx create-cartwright@latest my-site
    cd my-site
    pnpm dev

    create-cartwright installs dependencies, creates the database, and seeds an admin + demo data — the admin login is printed and saved to .admin-credentials. (Manual clone instead? pnpm install && pnpm db:setup && pnpm dev.)

    Verify (use your brand.defaultLocaleen in a new scaffold):

    curl -s -o /dev/null -w '%{http_code}' http://localhost:3000/en
    # expect: 200

    Proves: the app boots, the database exists, and the seed ran.

  2. Mint an agent API key (one-time). This unlocks the whole tool surface over REST — POST /api/v1/tools/<name> — without ever opening the admin UI. Keys are normally created in /admin/api-keys; the no-browser bootstrap is a short script that writes the key row directly:

    scripts/agent-key.ts
    // Run once, then delete (or keep; it only ADDs keys).
    import { config as loadEnv } from "dotenv";
    loadEnv({ path: ".env" });
    loadEnv({ path: ".env.local", override: true });
    
    async function main() {
      const { generateApiKey } = await import("../lib/api-auth");
      const { prisma } = await import("../lib/db");
      const { SCOPES } = await import("../lib/scopes");
      const admin = await prisma.user.findFirst({ where: { role: "admin" } });
      if (!admin) throw new Error("No admin user — run pnpm db:setup first.");
      const { plaintext, hash } = generateApiKey();
      await prisma.apiKey.create({
        data: {
          userId: admin.id,
          name: "agent-bootstrap",
          keyHash: hash,
          scopes: JSON.stringify(SCOPES), // or a narrower list from lib/scopes.ts
        },
      });
      console.log(plaintext); // shown once — the DB stores only the hash
    }
    main().catch((e) => { console.error(e); process.exit(1); });
    KEY=$(pnpm exec tsx --conditions react-server scripts/agent-key.ts | tail -1)

    Two caveats that cost a cold agent real minutes when undocumented:

    • --conditions react-server is required — the project's lib/* modules guard with server-only.
    • Keep the script's imports to exactly this narrow set (lib/api-auth, lib/db, lib/scopes). Importing lib/tools/registry from a standalone script crashes on Next-only modules — call tools over REST instead.

    Proves: an admin exists and you now hold a scoped bearer token for the tool API.

  3. Turn on genome copy rendering. The designed looks in step 4 write their pre-written copy through the Resolvable Genome, and the storefront only renders genome copy when the genomeResolve flag is on — without it, only the palette and 3D scene change and the copy silently stays put:

    curl -s -X POST http://localhost:3000/api/v1/tools/features.set \
      -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
      -d '{"key":"genomeResolve","enabled":true,"confirm":true}'

    Proves: the tool surface works end to end (auth, scopes, confirm-token flow).

  4. Apply a designed look — one call, instant, no LLM involved:

    curl -s -X POST http://localhost:3000/api/v1/tools/magic.compose_look \
      -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
      -d '{"vertical":"cafe","confirm":true}'

    A Voice (in verticals/: cafe, carpenter, fable, kindergarten, salon) applies pre-written on-brand copy + a palette + a suggested design + a 3D scene in one step. To pick a specific Skin instead or as well, pass "design":"<slug>" (slugs in designs/options.ts) or call design.set_slug.

    Proves: the site is designed — copy, palette, layout and scene — without a browser, a designer, or a generation step.

  5. Verify the design landed:

    curl -s http://localhost:3000/en | grep -o '<h1[^>]*>[^<]*'

    The H1 and the --color-sol-* palette variables in the HTML should reflect the chosen look. That's the finish line: a real, designed, database-backed site serving HTML — at ~99 s on the measured cold run.

Prefer a browser?

The same things live in the admin UI: /admin/designs (skins), /admin/verticals (voices), /admin/mixer (combine skin + voice + chrome), /admin/api-keys, /admin/features. Sign in per Sign in for the first time.

Where to go next

On this page