Whirlwind AI SaaS Template Skill
Version: 2.0.0
Overview
Whirlwind is a production-ready Next.js template for building AI SaaS products. It provides complete infrastructure so developers can focus on their unique AI features.
Included Infrastructure:
- ✅ Supabase authentication (email, OAuth, magic links)
- ✅ Stripe payments (subscriptions, one-time, webhooks)
- ✅ Email system (Resend/Mailgun)
- ✅ Multi-model AI client (Claude, GPT-4, Gemini)
- ✅ Landing page components
- ✅ Dashboard shell
- ✅ Database with RLS
ClawdBot's Role:
Customize the template for each user's specific AI product by generating custom components, API routes, and database tables.
File Structure
✅ Infrastructure (DON'T MODIFY)
These files are core infrastructure. Never modify:
lib/
supabaseClient.js - Database client setup
stripe.js - Stripe checkout/portal functions
api.js - Axios wrapper with auth
seo.js - SEO metadata helpers
ai-client.js - Multi-model AI client
components/
ButtonCheckout.js - Stripe checkout button
ButtonSignin.js - Authentication button
ButtonAccount.js - Account dropdown
CheckoutModal.js - Checkout modal
LayoutClient.js - Client-side layout wrapper
SubscribeForm.js - Email subscription
ai/ - Pre-built AI components
providers/ - Auth/context providers
app/api/
auth/ - Supabase auth routes
stripe/ - Stripe webhook handlers
webhook/ - Other webhooks
🎨 Customizable (UPDATE CONTENT)
Update these files with product-specific content:
components/
Hero.js - Update hero title/subtitle
Features.js - Update features list
Pricing.js - Update pricing copy
FAQ.js - Update FAQ items
CTA.js - Update call-to-action
Testimonials.js - Add testimonials
Header.js - Update navigation (partial)
Footer.js - Update footer links (partial)
config.js - App configuration (Stripe plans, etc.)
🤖 Create New (CLAWDBOT GENERATES)
Generate new files in these locations:
components/features/ - Custom AI feature components
app/api/custom/ - Custom API endpoints
lib/supabase/migrations/ - Custom tables (002+)
config/site.js - Product-specific configuration (NEW)
AI Client Usage
Whirlwind provides lib/ai-client.js for all AI operations. Always use this instead of creating ad-hoc clients.
Basic Usage
import AIClient from "@/lib/ai-client";
// Choose model: 'claude', 'openai', or 'gemini'
const ai = new AIClient('claude');
// Simple chat
const response = await ai.chat([
{ role: "user", content: "Your prompt here" }
]);
// With options
const response = await ai.chat(messages, {
maxTokens: 2000
});
// Streaming
await ai.stream(messages, (chunk) => {
console.log(chunk); // Handle each chunk
});
Available Models
claude - claude-sonnet-4-20250514 (recommended)
openai - gpt-4
gemini - gemini-pro (if configured)
Database Operations
Always use the Supabase client with proper error handling:
import { supabase } from "@/lib/supabaseClient";
// Insert
const { data, error } = await supabase
.from('table_name')
.insert({ column: value });
// Select with auth
const { data, error } = await supabase
.from('table_name')
.select('*')
.eq('user_id', userId);
// Always check errors
if (error) {
console.error(error);
throw new Error(error.message);
}
Creating an AI Product - Step by Step
When a user says: "Create [product name] - [description]"
Step 1: Create config/site.js
This centralizes all product-specific content:
/**
* 🎨 CLAWDBOT CUSTOMIZES
* Product-specific configuration
*/
const siteConfig = {
productName: "[Product Name]",
productDescription: "[Brief description]",
hero: {
title: "[Compelling title]",
subtitle: "[Subtitle explaining value]",
},
features: [
{
icon: "[emoji]",
name: "[Feature name]",
description: "[Feature description]"
},
// Add 3-6 features
]
};
export default siteConfig;
Step 2: Create Feature Component
Location: components/features/[ComponentName].jsx
Use this template structure:
/**
* 🤖 CLAWDBOT CREATED
* [Component description]
*/
"use client";
import { useState } from "react";
import apiClient from "@/lib/api";
export default function [ComponentName]() {
const [input, setInput] = useState("");
const [result, setResult] = useState("");
const [loading, setLoading] = useState(false);
const handleGenerate = async () => {
setLoading(true);
try {
const response = await apiClient.post("/custom/[endpoint]", {
input
});
setResult(response.result);
} catch (error) {
console.error("Error:", error);
}
setLoading(false);
};
return (
<div className="max-w-4xl mx-auto p-6">
<h2 className="font-bebas text-4xl uppercase tracking-wide mb-6">
[Component Title]
</h2>
{/* Input UI */}
<div className="space-y-4">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
className="w-full px-4 py-2 border border-gray-200 dark:border-white/10 rounded"
placeholder="[Placeholder]"
/>
<button
onClick={handleGenerate}
disabled={loading || !input}
className="px-6 py-3 bg-primary hover:bg-primary-focus text-black font-bebas text-lg uppercase tracking-wide transition-colors disabled:opacity-50"
>
{loading ? "Processing..." : "[Action Text]"}
</button>
</div>
{/* Output UI */}
{result && (
<div className="mt-6 p-4 bg-white dark:bg-white/5 border border-gray-200 dark:border-white/10 rounded">
<h3 className="font-bebas text-xl uppercase mb-2">Result:</h3>
<div className="font-ibm text-sm">{result}</div>
</div>
)}
</div>
);
}
Step 3: Create API Route
Location: app/api/custom/[endpoint]/route.js
/**
* 🤖 CLAWDBOT CREATED
* [Endpoint description]
*/
import { NextResponse } from "next/server";
import AIClient from "@/lib/ai-client";
import { supabase } from "@/lib/supabaseClient";
export async function POST(req) {
try {
// Parse input
const { input } = await req.json();
// Validate
if (!input) {
return NextResponse.json(
{ error: "Input is required" },
{ status: 400 }
);
}
// Create AI prompt
const prompt = `[Your specific prompt template]: ${input}`;
// Call AI
const ai = new AIClient('claude');
const result = await ai.chat([
{ role: "user", content: prompt }
]);
// Optional: Save to database
// const { data: session } = await supabase.auth.getSession();
// if (session?.user) {
// await supabase.from('your_table').insert({
// user_id: session.user.id,
// input,
// result
// });
// }
return NextResponse.json({
success: true,
result
});
} catch (error) {
console.error("API error:", error);
return NextResponse.json(
{ error: error.message || "Processing failed" },
{ status: 500 }
);
}
}
Step 4: Create Database Migration
Location: lib/supabase/migrations/00X_[table_name].sql
-- 🤖 CLAWDBOT CREATED
-- [Table description]
create table if not exists [table_name] (
id uuid default uuid_generate_v4() primary key,
user_id uuid references auth.users on delete cascade not null,
[your_columns] text not null,
created_at timestamp with time zone default timezone('utc'::text, now()) not null
);
-- Indexes
create index [table_name]_user_id_idx on [table_name](user_id);
create index [table_name]_created_at_idx on [table_name](created_at desc);
-- Row Level Security
alter table [table_name] enable row level security;
-- Policies
create policy "Users can view own records"
on [table_name] for select
using (auth.uid() = user_id);
create policy "Users can insert own records"
on [table_name] for insert
with check (auth.uid() = user_id);
create policy "Users can delete own records"
on [table_name] for delete
using (auth.uid() = user_id);
Step 5: Update Dashboard
Add component to app/dashboard/page.js:
import [ComponentName] from "@/components/features/[ComponentName]";
export default function Dashboard() {
return (
<div>
<h1 className="font-bebas text-5xl">Dashboard</h1>
<[ComponentName] />
</div>
);
}
Code Style Guidelines
Typography
- Headers:
font-bebas text-4xl uppercase tracking-wide
- Body:
font-ibm text-sm
- Always include dark mode classes
Colors
- Primary action:
bg-primary hover:bg-primary-focus
- Text:
text-gray-900 dark:text-white
- Borders:
border-gray-200 dark:border-white/10
- Backgrounds:
bg-white dark:bg-white/5
Components
- Always use
"use client" for interactive components
- Include loading states
- Handle errors gracefully
- Validate inputs
- Add disabled states for buttons
Rules and Constraints
NEVER:
- Modify infrastructure files (lib/supabaseClient.js, lib/stripe.js, etc.)
- Remove existing functionality
- Create duplicate AI clients (use AIClient)
- Forget Row Level Security in migrations
- Skip error handling
ALWAYS:
- Add ClawdBot marker comments (
🤖 CLAWDBOT CREATED)
- Use AIClient for AI operations
- Include proper error handling
- Validate user inputs
- Use Next.js 14 patterns
- Include TypeScript types if using TS
- Test database queries
- Use proper HTTP status codes
- Include loading/disabled states
Common Patterns
Content Generation
const prompt = `Generate ${contentType} about: ${topic}
Tone: ${tone}
Length: ${length} words
Include: ${requirements}`;
const ai = new AIClient('claude');
const content = await ai.chat([
{ role: "user", content: prompt }
]);
Image Analysis
// For image analysis, include base64 image in prompt
const prompt = `Analyze this image and ${task}`;
// Note: Image handling requires additional setup
Document Processing
const prompt = `Extract ${dataPoints} from this document:
${documentText}
Return as JSON.`;
const ai = new AIClient('claude');
const result = await ai.chat([
{ role: "user", content: prompt }
]);
Chat/Conversation
// Maintain conversation history
const conversationHistory = [
{ role: "user", content: "Previous message" },
{ role: "assistant", content: "Previous response" },
{ role: "user", content: "Current message" }
];
const ai = new AIClient('claude');
const response = await ai.chat(conversationHistory);
Example Workflows
See /examples folder for complete implementations:
content-generator.md - WriteFlow content platform
image-analyzer.md - VisionAI image analysis
chatbot.md - TalkAI chatbot platform
document-processor.md - DocuMind document intelligence
Templates
See /templates folder for code templates to copy:
component.jsx - Feature component template
api-route.js - API endpoint template
migration.sql - Database migration template
Environment Variables
Remind users to add these to .env:
# Required
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
# AI (at least one required)
ANTHROPIC_API_KEY=
OPENAI_API_KEY=
# Optional
RESEND_API_KEY=
GOOGLE_AI_API_KEY=
Testing
After generating code, suggest testing:
- Check component renders:
npm run dev
- Test API endpoint with curl or Postman
- Run database migration in Supabase dashboard
- Test full workflow end-to-end
Support
For issues or questions: