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 pack30 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.tsWriting 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_pagemagic.generate_pagepages.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-sandboxv0 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); Upstash Redis (UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN) for the redirect table — without it no redirect firesproducts by CSV (simple products), pages and posts by the URL import (as drafts), old URLs by admin-managed redirects — exact paths, one at a timea 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 (exact paths; /?p=123 query permalinks are not matched yet; the redirect table lives in Upstash Redis — set UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN or nothing redirects). 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.

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