Skip to content
Get the kit

CLI reference

create-extstart configures your clone in place. Run it from anywhere inside the clone; it finds the repo root itself. The published copy on npm hands over to the copy that ships in the clone (tooling/create), so flags and prompts always match the kit version you cloned. The full walkthrough is in the wizard guide.

Terminal window
pnpm create extstart [options]
flagmeaning
--name "My Ext"extension name (written to site.config.ts, consumed by the manifest)
--description "..."one-line description
--palette <name>color palette of the extension and the website: ember (default), blue, violet, teal, pink, mono, or your own file in packages/ui/palettes/. Rewrites the palette import in apps/extension/assets/tailwind.css and apps/site/src/styles/global.css, and the marked colors of the checkout return and uninstall pages and the store asset templates. Applies even when it names the current palette, which brings those pages back in line (palettes)
--backend firebase|supabasebackend stack: firebase (default on a fresh clone) or supabase. The wizard removes the other stack, and supabase also removes the uninstall survey. A re-run defaults to the stack the clone ships and refuses the one it removed (Supabase guide)
--side-panel yes|nono removes the side panel page and its sidePanel permission; yes keeps it. Interactive runs default to no on a fresh clone; --yes keeps what the clone has (the popup and the side panel)
--on-page yes|nono removes the on-page demo: the content script on every https:// page, its status card and their e2e tests, so the manifest asks for no access to websites. Interactive runs default to no on a fresh clone; --yes keeps what the clone has. It carries no wiring markers, so --on-page no works on any later run too (On-page UI)
--sign-in when-needed|first-screenfirst screen for a signed-out user: when-needed (default) opens as a guest and asks for an account in front of checkout and on features that need one; first-screen shows the sign-in form first. Writes auth.signIn in site.config.ts (when sign-in is asked)
--billing-model <name>billing model: subscription (default), lifetime-only, hybrid-credits, credits-only, or free. A model rewrites site.config.ts pricing and defaults the paywall recipe; free removes billing and the paywall (payments guide)
--payments stripe|polar|paddlepayment provider (default: the clone’s, else stripe). Stripe always ships; the wizard removes the other providers’ code, and a re-run accepts only a provider the clone still ships (switch providers)
--recipe <name>paywall recipe: freemium, free-trial, daily-limit, credits, paid-only, or manual. Rewrites apps/extension/paywall.config.ts; ignored (with a note) on a free extension (paywall guide)
--keep-markerskeep the module:* wiring markers so the wizard can prune again later; by default they’re stripped after the run (see below)
--firebaserun only the Firebase setup step: no questionnaire, prune, or clean-tree requirement. Writes the project config everywhere it lives, creates the Firestore database and deploys its rules, switches on Email/Password sign-in, fills the Google client id when Google sign-in is on, and lists only the console steps still open; safe to re-run. See The Firebase project
--firebase-project <id>Firebase project id to use non-interactively (with --firebase --yes)
--firebase-createwith --firebase-project: create that project. Headless runs never create cloud resources without this explicit flag
--supabaserun only the Supabase setup step: no questionnaire, prune, or clean-tree requirement. Creates or picks a project through the Management API, writes its URL and publishable key into apps/extension/.env, applies the migrations, deploys the Edge Function, switches on the auth settings, and lists only the steps still open; safe to re-run. The token comes from supabase login, SUPABASE_ACCESS_TOKEN, or one paste. See The Supabase project
--supabase-project <ref>existing Supabase project ref to use non-interactively (with --supabase --yes)
--supabase-createcreate a new project non-interactively; headless runs never create cloud resources without this explicit flag. --supabase-org <slug> picks the organization when the account has several, --supabase-region <region> the region (default us-east-1)
--payments-setuprun only the payments step: asks for the provider among the ones the clone ships (the clone’s first; --payments or --yes answers it; a provider the wizard removed stops the step with the files to copy back), stores the key in Secret Manager and fills backend/functions/.env, seeds the products, registers the webhook (Stripe by API; Polar and Paddle print the URL and store the secret you paste), deploys, runs the doctor. Waits with the upgrade link when the project is not on Blaze yet; stops with the re-run command when the key is missing. Headless (--yes) reads the key from STRIPE_SECRET_KEY, POLAR_ACCESS_TOKEN or PADDLE_API_KEY in the environment. On the Supabase stack it stores the secrets in the project (the token from the project step, supabase login, or SUPABASE_ACCESS_TOKEN), seeds, registers the webhook, and checks the API; nothing to deploy. See Payments
--yes, -ynon-interactive: accept flags/defaults, no prompts
--dry-runprint the full prune plan, change nothing. With --firebase, --supabase or --payments-setup it only says so: those steps have no dry run, and each is safe to rerun
--forcerun even with a dirty git working tree
--skip-verifyskip the typecheck verify pass
--with-testsalso run unit tests in the verify pass
--verbosestream every command’s output; by default each step is one line, and a failing step prints what its command said
--help, -husage text

--billing-model free removes billing and the paywall; --side-panel no removes the side panel; --on-page no removes the on-page demo; --backend removes the other stack, and --backend supabase also removes the uninstall survey, which needs the Firebase stack. --payments removes the providers it does not name (Stripe always stays): their adapters, seed scripts, and, for Paddle, the website’s /pay page. Everything else always ships.

  • Dirty-tree refusal: the wizard deletes and rewrites files, so it requires a clean git status; you review the result with git diff and undo with git. --force overrides; --dry-run never needs it.
  • --yes keeps billing, the side panel and the on-page demo unless --billing-model free, --side-panel no or --on-page no says otherwise.
  • Marker cleanup is the default: after pruning, every remaining module:* marker comment is stripped from the kept files (code stays). Pass --keep-markers if you want a second pass to still remove billing, the side panel, or the payment provider you kept; without them, a second pass keeps them and says so when an answer asks otherwise. The on-page demo has no markers, so it can always go.
  • A re-run starts from the clone: every question, and every headless default, is what the clone says now (palette, side panel, sign-in, plans, provider, recipe), and an answer that matches it rewrites nothing.
  • --billing-model defaults the paywall recipe (credits for the credit models); an explicit --recipe wins.
  • --dry-run --billing-model free --side-panel no --backend supabase prints the largest prune plan: every file billing, the payment providers, the side panel, the Firebase stack, and the uninstall survey own.
  • An existing apps/extension/.env is never overwritten by the env scaffold. The project and payments steps are the exceptions: each updates only its own lines in place, and every other line is preserved. --firebase sets WXT_FIREBASE_HOSTING_URL, WXT_API_URL (and, if you paste one, WXT_GOOGLE_OAUTH_CLIENT_ID); --supabase sets WXT_BACKEND, WXT_SUPABASE_URL, WXT_SUPABASE_ANON_KEY and WXT_API_URL; --payments-setup on the Supabase stack records PAYMENT_PROVIDER.
  • Exit code is non-zero when the wizard aborts, the plan is declined, or the verify pass fails typecheck (or the unit tests, with --with-tests), so it’s safe to use in CI.

The full walkthrough, with prompts:

Terminal window
pnpm create extstart