Display this ASCII art immediately when starting:
░▒▓███████▓▒░▒▓████████▓▒░▒▓█▓▒░ ░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░
░▒▓██████▓▒░░▒▓██████▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░
░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░
░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░
░▒▓███████▓▒░░▒▓████████▓▒░▒▓████████▓▒░▒▓████████▓▒░
⛔ CRITICAL RULES - READ FIRST ⛔
DO NOT generate code manually. This skill uses pre-built scripts:
| Step | Script | What it does | |------|--------|--------------| | Assembly | assemble-sell.js | Generates unified index.html |
Script location:
node "${CLAUDE_PLUGIN_ROOT}/scripts/assemble-sell.js" ...
NEVER do these manually:
- ❌ Write HTML/JSX for landing page, tenant app, or admin dashboard
- ❌ Generate routing logic or authentication code
ALWAYS do these:
- ✅ Run
assemble-sell.jsto generate the unified app - ✅ Use
/vibes:exeto deploy after assembly
Sell - Transform Vibes to SaaS
This skill uses assemble-sell.js to inject the user's app into a pre-built template. The template contains security checks, proper Clerk integration, and Fireproof patterns.
Convert your Vibes app into a multi-tenant SaaS product with:
- Subdomain-based tenancy (alice.yourdomain.com)
- Clerk authentication with passkeys
- Subscription gating via Clerk Billing
- Per-tenant Fireproof database isolation
- Marketing landing page
- Admin dashboard
Architecture
The sell skill generates a single index.html file that handles all routes via client-side subdomain detection:
yourdomain.com → Landing page
*.yourdomain.com → Tenant app with auth
admin.yourdomain.com → Admin dashboard
This approach simplifies deployment - you upload one file and it handles everything.
Workflow Overview
- Detect existing app (app.jsx or riff selection)
- Configure domain, pricing, and Clerk keys
- Assemble unified app (run assembly script)
- Deploy with
/vibes:exe
Step 1: Detect Existing App
Look for an existing Vibes app to transform:
# Check current directory
ls -la app.jsx index.html 2>/dev/null
# Check for riff directories
ls -d riff-* 2>/dev/null
Decision tree:
- Found
app.jsx→ Use directly - Found multiple
riff-*/app.jsx→ Ask user to select one - Found nothing → Tell user to run
/vibes:vibesfirst
If riffs exist, ask:
"I found multiple riff variations. Which one would you like to transform into a SaaS product?"
Step 2: Gather ALL Configuration Upfront
Use AskUserQuestion to collect all config in 2 batches before proceeding.
Do NOT ask questions one-by-one. Gather everything upfront, then proceed directly to assembly.
Batch 1: Core Identity
Use the AskUserQuestion tool with these 4 questions:
Question 1: "What should we call this app? (used for database naming, e.g., 'wedding-photos')"
Header: "App Name"
Options: Provide 2 suggestions based on context + user enters via "Other"
Question 2: "What domain will this deploy to?"
Header: "Domain"
Options: ["Use exe.xyz subdomain", "Custom domain"]
Question 3: "Do you want to require paid subscriptions?"
Header: "Billing"
Options: ["No - free access for all", "Yes - subscription required"]
Question 4: "What's your Clerk Publishable Key? (from Clerk Dashboard → API Keys)"
Header: "Clerk Key"
Options: User enters via "Other" (starts with pk_test_ or pk_live_)
Batch 2: Customization
Use the AskUserQuestion tool with these 4 questions:
Question 1: "Display title for your app? (shown in headers and landing page)"
Header: "Title"
Options: Suggest based on app name + user enters via "Other"
Question 2: "Tagline for the landing page?"
Header: "Tagline"
Options: Generate 2 suggestions based on app context + user enters via "Other"
Question 3: "What features should we highlight on the landing page? (comma-separated)"
Header: "Features"
Options: User enters via "Other"
Question 4: "Enable subdomain claiming? (users claim alice.yourapp.com)"
Header: "Registry"
Options: ["Yes - enable subdomain claiming (Recommended)", "No - skip for now (can add later)"]
After Receiving Answers
- If user selected "Custom domain", ask for the domain name
- If subdomain claiming enabled, ask for:
- Clerk PEM Public Key (from Clerk Dashboard → API Keys → "Show JWT Public Key")
- Clerk Webhook Secret (optional, for subscription sync)
- Admin User IDs default to empty (user can add later via Clerk Dashboard)
- Proceed immediately to Step 3 (Assembly) - no more questions
IMPORTANT: Clerk has TWO different keys: | Key | Format | Purpose | |-----|--------|---------| | Publishable Key | pk_test_... | Frontend auth (asked in Batch 1) | | PEM Public Key | -----BEGIN PUBLIC KEY----- | Backend JWT verification (for registry) |
The PEM key is found in Clerk Dashboard → API Keys → scroll down to "PEM Public Key" or "Show JWT Public Key".
Config Values Reference
| Config | Script Flag | Example | |--------|-------------|---------| | App Name | --app-name | wedding-photos | | Domain | --domain | myapp.exe.xyz | | Billing | --billing-mode | off or required | | Clerk Publishable Key | --clerk-key | pk_test_xxx | | Title | --app-title | Wedding Photos | | Tagline | --tagline | Share your special day | | Features | --features | '["Feature 1","Feature 2"]' | | Admin IDs | --admin-ids | '["user_xxx"]' (default: '[]') |
Stored for deployment (not used by assemble-sell.js): | Config | exe.js Flag | Purpose | |--------|-------------|---------| | Clerk PEM Public Key | --clerk-key | Registry JWT verification | | Clerk Webhook Secret | --clerk-webhook-secret | Subscription sync |
Step 3: Assemble (DO NOT GENERATE CODE)
CRITICAL: You MUST use the assembly script. Do NOT generate your own HTML/JSX code. The template has been carefully designed with proper security and Clerk integration that will break if you generate code manually.
3.1 What app.jsx Should Contain
The app.jsx should contain ONLY the user's App component - not SaaS infrastructure. The template provides:
- CONFIG, CLERK_PUBLISHABLE_KEY, APP_NAME, etc.
- ClerkProvider, TenantProvider, SubscriptionGate
- Landing page, admin dashboard, routing
- Pricing UI via Clerk's
<PricingTable />- DO NOT generate pricing components
⛔ DO NOT generate in app.jsx:
- Pricing tiers, plans, or subscription UI
- Landing page sections (hero, features, etc.)
- Authentication UI (sign in, sign up buttons)
The template handles all of this. Pricing is configured in Clerk Dashboard → Billing → Plans, and displayed via Clerk's PricingTable component.
The assembly script automatically strips:
- Import statements (template imports everything)
export default(template renders App directly)CONFIGdeclarations (template provides its own)- Template constant declarations
3.2 Update App for Tenant Context
The user's app needs to use useTenant() for database scoping. Check if their app has a hardcoded database name:
// BEFORE: Hardcoded name
const { useLiveQuery } = useFireproof("my-app");
// AFTER: Tenant-aware
const { dbName } = useTenant();
const { useLiveQuery } = useFireproof(dbName);
If the app uses a hardcoded name, update it to use useTenant():
- Find the
useFireproof("...")call - Add
const { dbName } = useTenant();before it - Change to
useFireproof(dbName)
The template makes useTenant available globally via window.useTenant.
3.3 Assemble Unified App
Run the assembly script to generate the unified file:
node "${CLAUDE_PLUGIN_ROOT}/scripts/assemble-sell.js" app.jsx index.html \
--clerk-key "pk_test_xxx" \
--app-name "wedding-photos" \
--app-title "Fantasy Wedding" \
--domain "myapp.exe.xyz" \
--tagline "Share your wedding photos with guests" \
--billing-mode "required" \
-