Plan mode: If you are planning work, this entire skill is ONE plan step: "Invoke /vibes:launch". Do not decompose the steps below into separate plan tasks.
Display this ASCII art immediately when starting:
░▒▓█▓▒░ ░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓███████▓▒░░▒▓██████▓▒ ░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓████████▓▒░
░▒▓█▓▒░ ░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░
░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░▒▓█▓▒░░▒▓█▓▒░
Notation
Ask [Header]: "question" means call AskUserQuestion with that header and question. Options listed as bullets. User can always type custom via "Other". When collecting a key/secret, put one option like "Paste key" — the user types the actual value via Other.
For architecture context, see
in this directory.
FIRST: Pre-Flight Decision Tree
Run all five checks before collecting any input:
| # | Check | Command | If True |
|---|
| 1 | .env has Clerk keys + Connect URLs | grep -qE '^VITE_CLERK_PUBLISHABLE_KEY=pk_' .env && grep -qE '^VITE_API_URL=' .env && grep -qE '^VITE_CLOUD_URL=' .env
| Set . Read .env for clerkPk. Skip T2, T3, infra spawn. |
| 2 | .env has admin user ID | grep CLERK_ADMIN_USER_ID .env
| Store value. Skip Phase 3. |
| 3 | app.jsx exists | | Ask [Reuse]: "app.jsx exists. Reuse it or regenerate?" If reuse: skip T1. |
| 4 | Wrangler authenticated | | If NOT authenticated: tell user to run and wait. |
| 5 | SSH key exists | ls ~/.ssh/id_ed25519 ~/.ssh/id_rsa ~/.ssh/id_ecdsa 2>/dev/null
| If missing AND not CONNECT_READY: warn about Connect deploy. |
Phase 0: Collect Inputs
0.1 App Prompt
Ask [App prompt]: "What do you want to build? Describe the app you have in mind."
- "Todo list" — A simple task manager with categories and due dates
- "Photo gallery" — A shareable photo gallery with albums and captions
- "Team dashboard" — A metrics and status dashboard for small teams
0.2 App Name + Domain
Ask [App name]: "What's the app name? (used for subdomain + database)" AND [Domain]: "Where will this be deployed?"
- App name: "Derive from prompt" or "Let me specify"
- Domain: "Cloudflare Workers (Recommended)" or "Custom domain"
If "Derive from prompt": generate URL-safe slug (lowercase, hyphens, max 30 chars). If "Custom domain": ask for domain name. Store as
.
Resolve Workers URL (if Cloudflare):
bash
node "{pluginRoot}/scripts/lib/resolve-workers-url.js" --name "{appName}"
Store output as
. If script fails, ask for their Cloudflare subdomain and construct
{appName}.{subdomain}.workers.dev
.
0.3 AI Features (conditional)
Scan
for AI keywords: "chatbot", "chat with AI", "summarize", "generate", "analyze", "AI-powered", "intelligent".
If detected: Ask [AI features]: "Does this app need AI features?"
- "Yes — I have an OpenRouter key" — I'll paste my API key
- "Yes — I need to get one" — I'll sign up at openrouter.ai
- "No AI needed" — Skip AI capabilities
If yes: check
grep OPENROUTER_API_KEY ~/.vibes/.env
. If found, offer reuse (mask key). Otherwise collect via Ask and offer to cache to
. Store as
(or null if no AI).
Phase 1: Spawn Team & Parallel Work
1.1 Setup
- Resolve plugin root:
echo "${CLAUDE_PLUGIN_ROOT}"
→ store as
- Create team:
TeamCreate("launch-{appName}", "Full SaaS pipeline for {appName}")
- Create all tasks per the table in LAUNCH-REFERENCE.md. If : mark T2+T3 completed immediately.
1.2 Spawn Builder (T1)
- Read
{pluginRoot}/skills/launch/prompts/builder.md
- Substitute: , ,
- Set : if is set, add rule about hook (see vibes SKILL.md "AI Features"). If null, leave empty.
- Spawn: Task tool,
team_name="launch-{appName}"
, , subagent_type="general-purpose"
1.3 Clerk Credentials (T2) — simultaneous with builder
Skip entirely if CONNECT_READY.
Ask [Clerk app]: "Do you have a Clerk app configured?"
- "I have one ready" — Already has passkeys and email auth
- "I need to create one" — Walk me through setup
If creating new: guide through clerk.com/dashboard — create app, enable Email + Passkey, configure email settings (require OFF, verify ON, link ON, code ON). Then set up JWT template and webhook:
Ask [Clerk config]: "Complete these two setup steps in Clerk Dashboard:\n\n1.
JWT Template: JWT Templates → New Template → name it
, paste this JSON as the custom claims (the
fallbacks are required — Fireproof Studio rejects null names):\n
json\n{\n \"params\": {\n \"email\": \"{{user.primary_email_address}}\",\n \"email_verified\": \"{{user.email_verified}}\",\n \"external_id\": \"{{user.external_id}}\",\n \"first\": \"{{user.first_name || ''}}\",\n \"last\": \"{{user.last_name || ''}}\",\n \"name\": \"{{user.full_name || ''}}\",\n \"image_url\": \"{{user.image_url}}\",\n \"public_meta\": \"{{user.public_metadata}}\"\n },\n \"role\": \"authenticated\",\n \"userId\": \"{{user.id}}\"\n}\n
\n2.
Webhook: Webhooks → Add Endpoint → URL
→ subscribe to
\n\nHave you completed both?"
- "Yes, both done" — JWT template 'with-email' with email/name claims + webhook endpoint created
- "I need help" — Walk me through it step by step
Collect four credentials via Ask (user types actual values via Other):
Ask [Clerk PK]: "Paste your Clerk Publishable Key (starts with pk_test_ or pk_live_)"
- "Paste key" — From Clerk dashboard > API Keys. Validate prefix.
Repeat pattern for:
- [Clerk SK]: Secret Key — starts with or
- [PEM Key]: JWKS PEM Public Key — from API Keys > Advanced > Public Key. Starts with
-----BEGIN PUBLIC KEY-----
- [Webhook Secret]: From Webhooks > endpoint > Signing Secret. Starts with
Save PEM to file:
bash
cat > clerk-jwks-key.pem << 'PEMEOF'
{pemKey}
PEMEOF
Mark T2 completed.
1.4 Spawn Infra (T3) — after T2 completes
Skip if CONNECT_READY.
- Read
{pluginRoot}/skills/launch/prompts/infra.md
- Substitute: , , ,
- Spawn: Task tool,
team_name="launch-{appName}"
, , subagent_type="general-purpose"
1.5 Sell Config (T4) — while infra deploys
Sell config is collected here but applied later by invoking (or its assembly script) as an atomic step. Do NOT hand-implement SaaS logic — the sell skill handles tenant routing, auth gating, billing, and admin setup.
Choose billing mode based on monetization intent:
- "off" (free) — all authenticated users get full access. Good for MVPs and internal tools.
- "required" (subscription) — users must subscribe. Requires Clerk Billing (Dev instances auto-connect to Stripe sandbox).
Always ask the user — do not assume a default.
Ask [Billing]: "What billing mode for your SaaS?" AND [Title]: "App display title?"
- Billing: "Free (no billing)" or "Subscription required"
- Title: "Derive from app name" or "Let me specify"
If billing is "Subscription required": Note that Clerk Billing must be configured in the Clerk Dashboard after deploy (plans, Stripe connection). Dev instances auto-connect to Stripe sandbox for testing.
Ask [Tagline]: "Describe your app's tagline (short punchy phrase)"
- "Generate one" — Create from app description
- "Let me write it" — I'll provide it
When billing is "required": These fields appear on a pricing section visible to potential customers before signup. Optimize for marketing copy quality — benefit-driven language, not technical descriptions. Tagline = sales headline. Subtitle = value proposition ("why should I pay?"). Features = compelling benefit statements (3-5 items).
Repeat pattern for subtitle and features list (3-5 bullet points).
Store:
("off"/"required"),
,
,
,
(JSON array). Mark T4 completed.
Phase 2: Assembly & Deploy
Blocked by T1 + T3 + T4. Check TaskList until all complete.
2.1 Verify Inputs
Confirm:
exists with valid JSX.
has
VITE_CLERK_PUBLISHABLE_KEY
,
,
. All sell config values collected.
Scan app.jsx for builder mistakes (see LAUNCH-REFERENCE.md "Common Builder Mistakes"). Fix any found before proceeding.
2.2 Deploy Cycle
This sequence runs twice: first here (with
), then in Phase 3 (with real admin ID). Steps:
Step A — Assemble:
bash
node "${CLAUDE_PLUGIN_ROOT}/scripts/assemble-sell.js" app.jsx index.html \
--clerk-key "{clerkPk}" \
--app-name "{appName}" \
--app-title "{appTitle}" \
--domain "{domain}" \
--billing-mode "{billingMode}" \
--tagline "{tagline}" \
--subtitle "{subtitle}" \
--features '{featuresJSON}' \
--admin-ids '{adminIds}'
Step B — Validate: grep -c '__VITE_\|__CLERK_\|__APP_' index.html
— must be 0.
Step C — Deploy:
bash
node "${CLAUDE_PLUGIN_ROOT}/scripts/deploy-cloudflare.js" \
--name "{appName}" \
--file index.html \
--clerk-key "{clerkPk}" \
--billing-mode "{billingMode}" \
--webhook-secret "{webhookSecret}" \
{aiKeyFlag}
Where
=
--ai-key "{openRouterKey}"
if set, omitted if null. The
flag controls whether the client enforces JWT-based plan checks. The
flag sets the Clerk webhook signing secret as a Wrangler secret.
Run the cycle now with
=
(or
if found in pre-flight). Mark T5, T6 completed.
Phase 3: Admin Setup
Skip if was found in pre-flight.
3.1 Guide Signup
Tell the user:
Your app is live! Create your admin account:
- Open:
https://{domain}?subdomain=test
- Sign up with your email
- Complete email verification
- Create a passkey when prompted
Ask [Signup]: "Have you completed signup on the app?"
- "Yes, signed up" — Completed verification + passkey
- "Skip admin setup" — I'll do this later
If skip: proceed to Phase 4.
3.2 Collect Admin ID
Tell user: Go to clerk.com/dashboard > your app > Users > click your user > copy User ID (starts with
).
Ask [User ID]: "Paste your Clerk User ID (starts with user_)"
- "I need help finding it" — Clerk Dashboard > Users > click name > ID at top
Validate starts with
. Save to
:
bash
echo "CLERK_ADMIN_USER_ID={userId}" >> .env
3.3 Re-run Deploy Cycle
Re-run Phase 2.2 steps A-D with
=
.
Tell user: Admin dashboard now works at
https://{domain}?subdomain=admin
Phase 4: Verify & Cleanup
4.1 Verify
Ask [Verify]: "Your app is live! Open each URL and verify:\n\n- Landing: https://{domain}\n- Tenant: https://{domain}?subdomain=test\n- Admin: https://{domain}?subdomain=admin\n\nDoes everything look right?"
- "All working" — Everything loads correctly
- "Something's broken" — Need to troubleshoot
If billingMode === "required"
: Also ask the user to verify billing:
"Check billing flow: Sign in at
https://{domain}?subdomain=test
— you should see a paywall with pricing. Use test card
(any future expiry, any CVC) to complete a test subscription. After subscribing, the tenant app should load."
Mark T7 completed. If broken, ask what's wrong and troubleshoot.
4.2 Shutdown
Send
to "builder" and "infra" (if spawned). Wait for responses. Clean up team.
4.3 Summary
## Launch Complete
**App**: {appTitle}
**URL**: https://{domain}
**Clerk**: {clerkPk}
**Connect**: {studioUrl}
**Billing**: {billingMode}
### What's deployed:
- Cloudflare Worker with KV registry
- Fireproof Connect studio for real-time sync
- Clerk authentication with passkeys
- Subdomain-based multi-tenancy
### Next steps:
- Configure a custom domain (see CLAUDE.md DNS section)
- Set up Clerk billing plans if using subscription mode
Error Handling
| Failure | Recovery |
|---|
| Builder generates invalid JSX | Read app.jsx, fix TS syntax / wrong hooks, re-save |
| Connect deploy fails | Infra reports via SendMessage. Present error + fix steps |
| Assembly has placeholders | Check .env for missing values, re-run assembly |
| Cloudflare deploy fails | Check . Guide if needed |
| Wrangler secret put fails | Retry. If persistent, have user run manually |
| Teammate silent 3+ min | SendMessage status check. If no response, take over task |
| Builder hardcodes DB name | Edit app.jsx: replace with pattern before assembly |