ShipAgent

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 passed and 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/health on the live address answers 200 with "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.local at startup. Ask the agent to restart pnpm dev.
  • redirect_uri_mismatch on Google. The redirect URI in Google Cloud differs from the one the agent printed: http vs https, 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.local yourself, 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.