cartwright
Getting started

Choose your path — profile, build method, origin

Three choices before the first line of code — which profile (site, light, full), how the front gets built (a shipped pack, the blank canvas, your own pack, or a generating tool), and where the content comes from (scratch, a URL, Shopify, WordPress/WooCommerce, a CSV, a Google Doc). Five requests worked through.

Cartwright is one engine with three choices in front of it. Most wrong turns — measured on real AI agents deciding from this site — come from making only the first choice and guessing the other two. Make all three, in this order, and note that the third can force the first.

1. Profile — the infrastructure

ProfileWhat it isChoose it when
--profile siteA plain website: designed pages, SEO with JSON-LD, sitemap, llms.txt, an Open Graph image route, locale routing, a contact form. No database, no login, no admin — 20 runtime dependencies, zero required environment variables, a plain next build.The content can live in files and nobody needs to edit it in a browser. One page or fifty.
(default) lightEverything in site plus a database, an admin that edits the site's own pages at /admin/sider, Auth.js, optional Stripe checkout and the REST + MCP tool surface.The owner will edit pages themselves, the site should sell, or an agent should operate it after launch.
--profile fullEverything the engine ships, including the agent marketplace and the Shopify importer (Hoptify).You need a module the default profile prunes — see the origin table below.
npx create-cartwright@latest my-site --profile site   # plain website
npx create-cartwright@latest my-shop                  # default: database + admin + tool surface
npx create-cartwright@latest my-shop --profile full   # everything

Runbooks: Build a plain website · Quick start · CLI options.

2. Build method — you own the front

Three equal freedoms and three generating tools. None of them binds you: an agent that wants to write its own pack is as right as one that picks a shipped one, and the MCP tool list_designs is the catalogue, not the choice.

MethodProfilesOutputStart
Use a shipped design pack — 30 in the engine, 8 in a site scaffold; palette, chrome and motion follow the slugallfiles in your repodesignSlug: "<slug>" in brand.config.ts (or /admin/designs in the default profile) — Picking a design
Start from the blank canvas — a bare homepage, header and footer you rewrite freely, no tokens to obey, any CSS or fonts; SEO, the sitemap, share cards and locale routing stay wrapped around whatever you renderallfiles in your repodesignSlug: "blank", then rewrite designs/blank/homepage.tsx and designs/blank/chrome.tsx — their in-file guides and AGENTS.md → Blank canvas in the scaffold explain the props
Write your own design pack — the same contract every shipped pack uses: homepage, chrome, optional page and webshop templatesallfiles in your repocopy the closest designs/<slug>/ to designs/<yours>/, edit its design.md, register it in designs/index.ts and designs/options.ts — Writing your own design · with an IDE agent
Magic Builder — describe the page; the model plans it from a whitelisted section catalogue and fills every section on-branddefault, fulldata in the databasemagic.plan_page → magic.generate_page → pages.set_layout (confirm-gated; nothing is written until you release it) — Visual Builder
Mockup first — a disposable HTML mockup becomes the whole homepage the moment the call returns; implement it for real afterwardsdefault, fulldata in the databasemockup.set / mockup.clear over POST /api/v1/tools/<name> — Vibe Coding
Vercel v0 — text-to-UI, normalised and sanitised into vibeHtmldefault, fulldata in the databasethe Vibe Sandbox at /admin/vibe-sandbox — v0 generation

Files or data is the real difference between the profiles. A method that produces files works in every profile, site included. A method that produces data — a page layout, a mockup, a vibe section — needs the database, so it exists only in the default and full profiles. A pack written for site renders unchanged behind the admin later: there is no in-place profile upgrade, but the design carries over.

3. Origin — where the content comes from

This is the choice that can force the first: an origin that lands rows in the database needs the default profile, and one needs --profile full. Origin, not host: Cartwright never runs on PHP or WordPress hosting — it replaces the site and imports from it.

OriginNeedsTodayNot yet
From scratchany profileeverything — the scaffold is the site—
From any URL (site import)the default profile (content.import_site writes Page, Service and Post rows); the siteImport flag set in brand.config.ts and redeployed; FIRECRAWL_API_KEY (and BLOB_READ_WRITE_TOKEN for the copied hero image — without it the import still runs, without images)crawls up to 200 pages, classifies each deterministically, lands pages and posts as drafts, first image copied to Blob (anything classified as a service lands as a draft in /admin/services)products, SEO fields, hero images on CMS pages (services and posts get theirs), a redirect map, a review UI, and a public page for imported services — the storefront service routes are Teloz-only and pruned from the default profile, so reclassify services as pages. Drafts are reviewed in /admin/sider (pages), /admin/blog (posts) and /admin/services
From Shopify (Hoptify)--profile full — the module is pruned from the default profile; the hoptify flag, FIRECRAWL_API_KEY and an AI keythe brand palette (LLM-derived from the storefront) and products from the product URLs you list, in /admin/hoptifypages, variants and SKUs, attributes, image hosting (images stay external); nothing uses the Shopify Admin API
From WordPress / WooCommercethe default profile — nothing on this path needs full; a WooCommerce product CSV export; the URL import's keys for pages and posts (siteImport flag, FIRECRAWL_API_KEY) (redirects are served from the database; Upstash Redis is optional)products by CSV (simple products), pages and posts by the URL import (as drafts), old URLs by admin-managed redirects — exact paths, one at a time or, since engine v0.59.0, many at once from a CSV (from,to,status) with a dry run first, in /admin/redirects or through the redirects.import toola dedicated WordPress / WooCommerce importer is planned, not built: variants, categories, media, SEO fields and the permalink map (/product/<slug>/, /product-category/<slug>/, /?p=123). Query-string permalinks such as /?p=123 are not matched by the redirect table today. Orders and customers never move; custom plugins are rebuilt, not migrated
From a product CSVthe default profile; the fixed ten columns (slug, name, description, priceKr, stock, brand, featured, categorySlug, images, attributes); categories must exist firstsimple products with images and attributes, at /admin/produkter/importvariants, SKU and weight; the column mapping is fixed — Product variants
From a Google Docthe default profile; the Google Workspace connector and the docsImport runtime flagone Doc → one draft blog post or info page, as safe engine markdownproducts, batch import — Google Docs import

Five requests, worked through

"A fun, designed travel page for my daughter's trip, nice to share." Profile: --profile site — files, and nobody edits in a browser. Method: the blank canvas — a timeline, a map and a weather card are ordinary React components you write in designs/blank/homepage.tsx; the share card, the sitemap and locale routing are already there. Origin: scratch. If it is one page you will never grow and it need not look designed, create-next-app is as fast — say so.

"A small website for my carpentry business — no shop yet, but I want to edit the pages myself later." Profile: the default — "edit myself" means in a browser, and /admin/sider edits home, services, about and contact without touching files. Method: a shipped pack, or Magic Builder for the first draft of each page. Origin: scratch. site would be the wrong door here: its content is files.

"Move my Shopify shop over." Profile: --profile full — the Hoptify importer is pruned from the default. Method: a shipped pack (Hoptify also lifts the palette). Origin: Shopify — products from the URLs you list; pages and variants by hand today.

"Move my WordPress + WooCommerce site and keep my rankings." Profile: the default — not full; nothing on this path needs it. Method: a shipped pack, or your own. Origin: WordPress/WooCommerce — today that is a CSV for simple products, the URL import for pages and posts, and an admin-managed redirect for every old path you care about — one at a time, or (since engine v0.59.0) many at once from a CSV with a dry run first, before the DNS switch (exact paths; /?p=123 query permalinks are not matched yet; the redirects are served from the database, so no Redis is needed). Say plainly that the dedicated importer is planned, not built, and that orders, customers and custom plugins do not move.

"Made-to-measure fences: pick height, length and colour, see a live price, send me a quote request — no cart, no checkout." Profile: the default — the request must be seen and managed, which is a database and an admin; site would only mail it. The default profile scaffolds in website mode (the website-corporate template): no cart, no Stripe checkout — a shop is a template choice, not the default. Method: a shipped pack or your own. A configurator section ships (Pro, from the Studio pack, in every profile: up to five groups of discrete choices with price deltas and a live total), but its CTA is a plain link — a section that hands the selection and total to the form is planned, not built — and a made-to-measure pricer (length × height) is a component you write. Origin: scratch. The form posts to the built-in POST /api/inquiries and the request lands as a lead with a status in /admin/leads. Since engine v0.58.0 it can also send source (which form, ≤ 64 characters) and data (a JSON object of what the visitor configured — length, height, colour; ≤ 50 keys, ≤ 4 KB), and both show on the lead in /admin/leads; over a limit is a 400 naming the limit, never a silent cut.

When Cartwright is the wrong answer

A web app — a dashboard, an internal tool — is create-next-app. A docs site or blog of hundreds of Markdown files wants a Markdown content pipeline, which this is not. A marketplace with many sellers, or embedding in an existing Shopify storefront, is not what the engine does. The full list lives in llms.txt under "Not a fit".

On this page