StartupKitstartupkit
Getting Started

Agent Handoff Prompt

The copy-paste prompt that hands your AI agent a complete task: scaffold a production SaaS, wire GitHub, deploy, and verify each step.

Paste this into your AI agent (OpenCode, Claude Code, Cursor, …) to have it scaffold a new SaaS project and take it through local setup, GitHub, and Cloudflare deployment. Replace <Product Name> and <yourdomain.com> first.

You are scaffolding a new SaaS app from the StartupKit template and taking it
through local setup, GitHub repo creation, and Cloudflare deployment. Follow
the steps in order and verify each before continuing.

PREREQUISITES — verify these first and stop if any fail:
- bun --version
- gh auth status (GitHub CLI, authenticated)
- bunx wrangler whoami (Cloudflare access, or ask me for CLOUDFLARE_API_TOKEN)

STEP 1 — SCAFFOLD
- Run: npx startupkit init "<Product Name>" && cd <slugified-name>
- This clones 01-studio/startupkit/templates/repo (private — needs my git
  credentials), inits a fresh git history, runs bun install, and installs the
  startupkit agent skill to .agents/skills/startupkit/.

STEP 2 — LOCAL ENV (apps/web/)
- cp .env.example .env.local && cp .dev.vars.example .dev.vars
- Generate once: openssl rand -base64 32
- Put the SAME value as BETTER_AUTH_SECRET in both files.
- Verify: bun run typecheck passes; bun run migrate applies 2 migrations;
  bun run test passes.

STEP 3 — CLOUDFLARE D1
- cd apps/web && bunx wrangler d1 create startupkit-db
- Put the returned database_id into BOTH wrangler.jsonc and
  wrangler.next-dev.jsonc (replace REPLACE_WITH_YOUR_D1_DATABASE_ID).
- Verify: bunx wrangler d1 list shows startupkit-db.

STEP 4 — RUN LOCALLY
- From repo root: bun run dev  (app :3000, email preview :3001)
- Verify: GET / returns 200; GET /dashboard returns 307 (auth gate);
  GET /kit returns 200 with an X-Robots-Tag: noindex, nofollow header;
  GET /robots.txt disallows /kit;
  POST /api/auth/email-otp/send-verification-otp with
  {"email":"[email protected]","type":"sign-in"} returns 200 — in dev the OTP
  prints to the server log instead of being emailed.

STEP 5 — OPTIONAL INTEGRATIONS
- Stripe: create a recurring "Pro" product (monthly + yearly prices); set
  STRIPE_SECRET_KEY, STRIPE_PRICE_PRO_MONTHLY, STRIPE_PRICE_PRO_YEARLY,
  STRIPE_WEBHOOK_SECRET in apps/web/.env.local; webhook endpoint
  https://<yourdomain.com>/api/stripe/webhook for checkout.session.completed
  and customer.subscription.*.
- Google OAuth (optional): GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET in
  apps/web/.env.local.
- Email: verify a sending domain in Cloudflare Email Service; set EMAIL_FROM
  in apps/web/lib/email.ts.

STEP 6 — RENAME THE PRODUCT
- Replace startupkit with the product name: package names in apps/web/package.json,
  root package.json script filters, worker name startupkit-web, db name startupkit-db.
- Replace yourdomain.com in apps/web/lib/urls.ts, apps/web/lib/email.ts,
  apps/web/wrangler.jsonc routes.
- Update apps/web/public/favicon.svg, apps/web/i18n/messages/en.json,
  apps/web/app/page.tsx, and the root README.md.
- Rebrand the listing kit (it ships describing StartupKit itself):
  1. apps/web/lib/site.ts — name, url, oneLiner, tagline (≤10 words),
     short (≤60 chars), category, model, platform, brandColor (from the new
     favicon), socials (only accounts that exist), surfaces to screenshot.
     This file also drives the site's <meta description> and OG tags.
  2. apps/web/app/kit/page.tsx — rewrite the five long-description tiers
     from the product's marketing copy (2–4 short paragraphs each, different
     opening sentence per tier, ending with the URL), the Tags: line, the
     Submit-next rows, and Still-needed list. Leave Confirmed live listings empty.
  3. bun run kit:assets --skip-screenshots (logos + OG + X/LinkedIn/Facebook/
     YouTube banners from the new favicon), then with the app running: KIT_BASE_URL=http://localhost:3000
     bun run kit:assets --only=screenshots. Commit apps/web/public/kit/.
- Verify: bun run test passes (site.ts constraints are tested); GET /kit
  shows no "StartupKit", "startupkit.com", or placeholder text; pasting the
  site URL into a link-preview tool shows the new OG image + description.

STEP 7 — GITHUB
- gh repo create <org>/<repo-name> --private --source . --push
- Enable dependabot security updates. Version updates already come from
  .github/dependabot.yml in the template, but security updates are per-repo
  settings and off by default for private repos:
  gh api -X PATCH repos/<org>/<repo-name> \
    -f security_and_analysis[dependabot_alerts]=enabled \
    -f security_and_analysis[dependabot_security_updates]=enabled
- Verify: gh api repos/<org>/<repo-name> --jq '.security_and_analysis'
  reports dependabot_alerts and dependabot_security_updates both "enabled".
- Verify: CI (lint + typecheck + test) is green on the first push.
- gh secret set CLOUDFLARE_API_TOKEN --repo <org>/<repo-name> --body "<API
  token with Workers + D1 edit permissions>"
- gh secret set CLOUDFLARE_ACCOUNT_ID --repo <org>/<repo-name> --body "<account
  id from bunx wrangler whoami>" (these power .github/workflows/deploy.yml,
  which auto-deploys main: D1 migrations first, then the worker version)

STEP 8 — DEPLOY
- cd apps/web
- bunx wrangler secret put BETTER_AUTH_SECRET   (same value as Step 2)
- bunx wrangler secret put BETTER_AUTH_URL      (https://<yourdomain.com>)
- bun run migrate:remote
- bun run deploy
- Verify: https://<yourdomain.com> returns 200; sign-up works; Stripe webhook
  shows 200 deliveries.
- From now on, pushes to main deploy automatically (migrations apply before
  the new code ships); the deploy/migrate:remote scripts stay for manual runs.

RULES
- Never commit .env.local or .dev.vars (gitignored).
- Do not push or deploy without showing me the plan first.
- If a verification fails, stop and report — do not improvise fixes.

Where this lives

  • Source of truth: docs/agent-create-new-project.md
  • The scaffolded project also ships its own README.md and the startupkit agent skill (.agents/skills/startupkit/), which agents pick up automatically inside the repo.

On this page