Your first session
Open the repository in Claude Code, Cursor or Codex, ask for the database, sign-in and Stripe, and follow the agent through each skill until every check passes.
Outcome: in one conversation your agent connects the database, sign-in and Stripe. Each part is done when its check script prints all checks passed, for example Database: all checks passed. A step without that line is not done, whatever the agent says.
1. Open the repository in your agent
| Agent | How |
|---|---|
| Claude Code | in a terminal: cd my-saas, then claude. It loads CLAUDE.md and finds the skills in .claude/skills/. |
| Cursor | File → Open Folder → my-saas, then open the chat in Agent mode. It reads AGENTS.md. |
| Codex | in a terminal: cd my-saas, then codex. It reads AGENTS.md. |
The agent will ask before it runs commands. Allow it to run pnpm, node, git and openssl in this folder: every skill is a sequence of those commands, and the check scripts are how it proves each step.
2. Say what you want
Plain words are enough. The agent matches them to a skill through the table in AGENTS.md. You do not have to know the options in advance: whenever a decision is yours, the agent shows them as a numbered list with its recommendation first, and you answer with a number. "Set everything up" is a fine first sentence; the agent then offers the skills in order.
- "Set up the database, then auth, then Stripe."
- "Set up email with Resend."
- "Connect R2 for uploads."
- "I want to generate images with fal; set up AI."
- "Deploy to Cloudflare."
You can ask for several at once. The agent still runs them one by one, in the order they depend on each other: setup-database always comes first, because sign-in and payments store their data there.
3. What a session looks like
Below is a shortened first session. Your agent's wording will differ; the shape is the same: it reads the skill, runs a step, runs the check, and stops at every ⚠ needs you. Keys are shown as ••••. The agent never prints a key back.
Database
You: Set up the database, then auth, then Stripe.
Agent: I read AGENTS.md and skills/README.md. setup-database runs first; auth and
Stripe both need it. Starting skills/setup-database.
Where should the database run?
1. Neon — recommended. Free tier, works locally and on Cloudflare or Vercel.
2. Supabase — if you already use it. Only its Postgres is used.
3. A Postgres you already have — paste its connection string.
Press Enter for 1.
You: 1
Agent: $ pnpm tsx scripts/check-database.ts
✗ DATABASE_URL is not set in .env.local
⚠ needs you: create the Neon project and paste its connection string.
console.neon.tech → New project (any name, the region closest to
your users) → Create → Connect → keep Connection pooling on → copy the
connection string. Its host contains "-pooler". Paste it here.
You: postgres://neondb_owner:••••@ep-quiet-sun-a1b2c3-pooler.us-east-2.aws.neon.tech/neondb?sslmode=require
Agent: Written to .env.local. The database is new, so I apply the migrations.
$ pnpm db:migrate
$ pnpm tsx scripts/check-database.ts
✓ connected to ep-quiet-sun-a1b2c3-pooler.us-east-2.aws.neon.tech — PostgreSQL 17.5
✓ all 18 tables of lib/db/schema.ts exist with every column
✓ migrations applied up to 0001_nappy_charles_xavier (2 in lib/db/migrations)
✓ write + read inside a transaction works (rolled back, nothing kept)
✗ no superadmin in the user table
⚠ needs you: tell me the email you will sign in with as the site owner; then I
run: pnpm create-superadmin <email>
1 check(s) failed
You: ana@myproduct.com
Agent: $ pnpm create-superadmin ana@myproduct.com
$ pnpm tsx scripts/check-database.ts
…
✓ superadmin: ana@myproduct.com
Database: all checks passed
Sign-in
Agent: Starting skills/setup-auth. BETTER_AUTH_SECRET was empty: I generated one with
openssl rand -base64 32 and wrote it into .env.local.
$ pnpm tsx scripts/check-auth.ts --env-only
✓ BETTER_AUTH_SECRET set (44 characters)
✓ NEXT_PUBLIC_SITE_URL = http://localhost:3000
email login (magic link, OTP) off: DEFAULT_ADMIN_EMAIL, RESEND_API_KEY not set — skills/setup-email
✗ no sign-in method is configured: nobody, the superadmin included, can sign in
Which sign-in methods should the site offer? One or more numbers.
1. Google — recommended; one OAuth client, I show you each click.
2. Email link / code — no password; needs email set up first.
3. GitHub — for developer audiences.
4. Bot protection on the email forms (Turnstile) — only with 2.
You: 1
Agent: ⚠ needs you: create a Google OAuth client.
Google Cloud Console → pick or create a project → APIs & Services →
OAuth consent screen: app name, support email, audience External →
Clients → Create client → type Web application →
Authorized JavaScript origins: http://localhost:3000
Authorized redirect URIs: http://localhost:3000/api/auth/callback/google
→ Create. Paste the Client ID first.
You: ••••.apps.googleusercontent.com
Agent: Now the Client secret.
You: GOCSPX-••••
Agent: Both written. Starting the app with pnpm dev, then the full check.
$ pnpm tsx scripts/check-auth.ts
✓ BETTER_AUTH_SECRET set (44 characters)
✓ NEXT_PUBLIC_SITE_URL = http://localhost:3000
✓ Google login configured (NEXT_PUBLIC_GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET)
✓ GET http://localhost:3000/api/auth/ok → 200 {"ok":true}
✓ GET /api/auth/get-session without a cookie → 200 null
✓ Google: the app redirects to accounts.google.com with this client_id and
redirect_uri http://localhost:3000/api/auth/callback/google
✓ session round trip: a session signed with this BETTER_AUTH_SECRET in this
database → get-session returns its user
Auth: all checks passed
⚠ needs you: open http://localhost:3000, sign in with Google as
ana@myproduct.com, and tell me when /dashboard shows the admin menu.
You: done, I see the admin menu
Stripe
Agent: Starting skills/setup-stripe.
⚠ needs you: Stripe Dashboard → Developers → API keys → Secret key. Use the
sk_test_… key until the whole flow passes. Paste it here.
You: sk_test_••••
Agent: ✓ STRIPE_SECRET_KEY valid (test mode, account acct_1Q••••)
config/pricing.ts still holds the two sample plans. What does your site sell?
1. One-time purchase — a license, or a credit pack that never expires.
2. Subscription — monthly and/or yearly, credits reset each period.
3. Both — a subscription plus a top-up pack.
4. Keep the sample plans for now and change prices later.
You: 3
Agent: The pack first. Name, price and credits? For example "Starter, $9.90, 500 credits".
You: Starter pack: 500 credits for $9.90.
Agent: And the subscription: name, monthly price, credits per month?
You: Pro: $19 a month, 2,000 credits a month.
Agent: Replaced the plans with pack-starter and pro-monthly, card copy in all 8
languages. pnpm lint passes.
$ pnpm stripe:bootstrap (dry run: shows what it would create)
$ pnpm stripe:bootstrap --write (created 2 products and 2 prices, test ids written)
$ pnpm tsx scripts/check-stripe.ts
✓ STRIPE_SECRET_KEY valid (test mode, account acct_1Q••••)
✓ pack-starter: price_1Q•••• = USD 9.9 one-time
✓ pro-monthly: price_1Q•••• = USD 19 / month
✗ STRIPE_WEBHOOK_SECRET is not set
local dev: run `stripe listen --forward-to localhost:3000/api/stripe/webhook`, it prints a whsec_… — put it in .env.local
NEXT_PUBLIC_SITE_URL is local (http://localhost:3000): Stripe cannot call it, endpoint check skipped
1 check(s) failed
Installing the Stripe CLI and running stripe login.
⚠ needs you: a browser tab opened. Confirm the pairing code there, then say done.
You: done
Agent: stripe listen is running in a second terminal; its whsec_… is in .env.local and
I restarted pnpm dev. Your user id comes from the user table (you signed in
as ana@myproduct.com).
$ pnpm tsx scripts/check-stripe.ts --fire 9b1e••••
✓ STRIPE_SECRET_KEY valid (test mode, account acct_1Q••••)
✓ pack-starter: price_1Q•••• = USD 9.9 one-time
✓ pro-monthly: price_1Q•••• = USD 19 / month
✓ STRIPE_WEBHOOK_SECRET is set
NEXT_PUBLIC_SITE_URL is local (http://localhost:3000): Stripe cannot call it, endpoint check skipped
✓ signed checkout.session.completed accepted by http://localhost:3000/api/stripe/webhook
✓ order row written: pack-starter, 990 cents, 500 credits
test order deleted again (--keep leaves it)
Stripe: all checks passed
⚠ needs you: one real test payment. Open http://localhost:3000/#pricing, buy the
starter pack with card 4242 4242 4242 4242, any future date, any CVC. The
success page should name the plan and /dashboard/my-orders should list it.
Cards are on by default; other payment methods are a click each under
Stripe → Settings → Payment methods, which the API cannot change.
Left for launch: switch to the sk_live_… key, run the bootstrap again to fill
the live price ids, register the production webhook, repeat the test with a
real card.
How to read a check
Every check script prints one line per check, in four shapes:
| Line | Meaning |
|---|---|
✓ … |
this check passed |
✗ … |
this check failed; the script will exit with code 1 |
⚠ needs you: … |
the next step is yours; the line says what to do |
| indented line | a hint: the command to run next, or why a check was skipped |
The last line is either <Area>: all checks passed or <n> check(s) failed. The exit code follows it: 0 only when nothing failed. That is what keeps an agent from reporting done early. The scripts and their options are described in Skills.
What "done" means
- A setup skill is done when its check prints
all checks passedand the one real test the skill asks of you worked (a Google sign-in, a test payment, a mail in your inbox). - A deploy skill is done when
/api/healthon the live address answers200with"status":"ok". - If the agent says "done" without showing that line, ask it to run the check command from the skill and paste the result.
When it fails
- The agent loops on the same error. Say: "read the When a check fails table at the bottom of the skill". Every skill ends with one, keyed by the exact output line.
- A dashboard does not match the click path. Menus move. Tell the agent what you see; the value it needs (a key, a connection string) is still somewhere in that dashboard.
- A check passes but the app still fails. The app reads
.env.localat startup. Ask the agent to restartpnpm dev. redirect_uri_mismatchon Google. The redirect URI in Google Cloud differs from the one the agent printed:httpvshttps,www, the port, a trailing slash. Copy the printed one exactly.- You would rather not paste keys into the chat. Put the value into
.env.localyourself, under the name the agent asked for, and say "done". The agent then reads it from the file.
Next
What you do lists every step that is yours, for all skills, in one place.