Nano Banana Pro
Agent skill for nano-banana-pro
Claude documentation is available globally at: `~/.claude-docs/docs/`
Sign in to like and favorite skills
Claude documentation is available globally at:
~/.claude-docs/docs/
Environment variable: $CLAUDE_DOCS_PATH
claude-docs-update # Update docs from anywhere claude-docs # Navigate to docs directory claude-docs-read # Read specific doc
docs/pdf-architecture-and-styling.md for comprehensive PDF generation guidelinesYou can use these commands without asking:
gh - GitHub CLIvercel - Vercel CLIsupabase - Supabase CLI (CRITICAL - see Supabase CLI Guidelines below)stripe - Stripe CLI (installed and configured - see Stripe Integration section)brew - Homebrew package managernpm / npx - Node package managersgit - Version controlNEVER suggest manual database changes in Supabase Dashboard. ALWAYS use CLI commands.
IMPORTANT: Use the following approach for Supabase operations:
apply_migration functionsbp_10122b563ee9bd601c0b31dc799378486acf13d2vnuieavheezjxbkyfxeaExample using Management API:
curl -X POST \ "https://api.supabase.com/v1/projects/vnuieavheezjxbkyfxea/database/query" \ -H "Authorization: Bearer sbp_10122b563ee9bd601c0b31dc799378486acf13d2" \ -H "Content-Type: application/json" \ -d '{"query": "YOUR_SQL_HERE"}'
supabase init # Initialize Supabase project supabase link --project-ref <project-ref> # Link to existing project supabase db pull # Pull remote schema to local
# 1. Create a new migration with descriptive name supabase migration new fix_rls_infinite_recursion # GOOD ā # NOT: supabase migration new update # BAD ā # 2. Edit the migration file in supabase/migrations/ # 3. Test locally supabase db reset # Reset local database and apply all migrations supabase test db # Run database tests # 4. Generate TypeScript types supabase gen types typescript --local > lib/database.types.ts # 5. Deploy to production supabase db push
supabase db diff # Show differences between local and remote supabase db lint # Check for issues supabase db dump -f supabase/seed.sql # Create seed file supabase db reset --debug # Reset with debug info
supabase start # Start local Supabase supabase stop # Stop local Supabase supabase status # Check service status supabase db remote commit # Commit remote changes to migration
-- GOOD ā : Singular, lowercase, underscores CREATE TABLE project (...); CREATE TABLE project_member (...); -- BAD ā: Plural, camelCase, unclear CREATE TABLE Projects (...); CREATE TABLE projectMembers (...);
-- Migration: fix_rls_infinite_recursion -- Description: Fixes infinite recursion in RLS policies -- Author: Claude -- Date: 2024 -- 1. Disable RLS temporarily ALTER TABLE project DISABLE ROW LEVEL SECURITY; ALTER TABLE project_member DISABLE ROW LEVEL SECURITY; -- 2. Drop problematic policies DROP POLICY IF EXISTS "circular_policy" ON project; -- 3. Create simple, non-recursive policies CREATE POLICY "owner_access" ON project FOR ALL USING (owner_id = auth.uid()); -- 4. Re-enable RLS ALTER TABLE project ENABLE ROW LEVEL SECURITY; ALTER TABLE project_member ENABLE ROW LEVEL SECURITY; -- 5. Add comments COMMENT ON POLICY "owner_access" ON project IS 'Simple owner-only access, no recursion';
update.sql, fix.sqlsupabase db resetsupabase gen types after changesGPT-5 models have a KNOWN BUG with the Chat Completions API that causes empty responses!
When GPT-5 models (gpt-5, gpt-5-mini, gpt-5-nano) are used with
chat.completions.create():
reasoning_tokensGPT-5 models MUST use the
client.responses.create() API instead:
// ā WRONG - Returns empty responses with GPT-5 const response = await client.chat.completions.create({ model: 'gpt-5-mini', messages: [...], temperature: 1, max_completion_tokens: 200 }) // Result: response.choices[0].message.content = "" (empty!) // ā CORRECT - Works perfectly with GPT-5 const response = await client.responses.create({ model: 'gpt-5-mini', input: 'Your prompt here', text: { verbosity: 'high' }, reasoning: { effort: 'minimal' }, max_output_tokens: 200 }) // Result: response.output_text contains the actual response
GPT-5 models (gpt-5, gpt-5-mini, gpt-5-nano) are available exclusively through Vercel AI Gateway, not directly via OpenAI API.
// Via Vercel AI SDK v5 import { streamText } from 'ai' const result = streamText({ model: "openai/gpt-5-nano", // or openai/gpt-5-mini, openai/gpt-5 prompt: "Your prompt here" })
When using OpenAI's Structured Outputs feature for reliable JSON generation:
zodResponseFormat helper from openai/helpers/zod for proper schema enforcementstrict: true in the schema configuration for guaranteed adherenceadditionalProperties: false on ALL object schemasz.any()null for optional fieldsimport { z } from "zod"; import { zodResponseFormat } from "openai/helpers/zod"; // ā CORRECT - Proper structured output schema const DocumentSchema = z.object({ title: z.string(), sections: z.array(z.object({ heading: z.string(), content: z.string(), priority: z.enum(["high", "medium", "low"]) })), metadata: z.object({ author: z.string(), date: z.string(), version: z.number() }), optionalNotes: z.union([z.string(), z.null()]) // Proper optional field }); // Use with chat.completions.parse for structured output const response = await openai.chat.completions.parse({ model: "gpt-4o-2024-08-06", messages: messages, response_format: zodResponseFormat(DocumentSchema, "document") }); // For direct JSON schema with GPT-5 const gpt5Response = await client.responses.create({ model: "gpt-5-mini", input: combinedInput, text: { format: { type: "json_schema", name: "document", strict: true, // CRITICAL schema: { type: "object", properties: { /* all properties */ }, required: [/* ALL fields */], additionalProperties: false // CRITICAL } } } });
// ā WRONG - Using z.any() defeats structured outputs const BadSchema = z.object({ data: z.any(), items: z.array(z.any()) }); // ā WRONG - Missing critical configuration const BadConfig = { type: "json_schema", schema: { /* schema */ } // Missing: strict: true // Missing: additionalProperties: false };
zodResponseFormatWhy: GPT-4o models support
chat.completions.parse with zodResponseFormat, guaranteeing valid structured outputs without truncation issues.
Why: GPT-5 excels at creative, analytical content with better reasoning capabilities but requires
responses.create API which returns plain text.
| Model | Input | Output | Best For |
|---|---|---|---|
| gpt-4o-nano | $0.50 | $2.00 | Testing, simple structured data |
| gpt-4o-mini | $1.50 | $6.00 | Production structured documents |
| gpt-5-nano | $0.05 | $0.40 | Testing, short narratives |
| gpt-5-mini | $0.25 | $2.00 | Production narrative documents |
Recommendation: Use nano variants during development/testing, upgrade to mini for production.
| Document Type | Recommended Model | Reasoning |
|---|---|---|
| PID | gpt-4o-nano/mini | Complex nested schema, needs zodResponseFormat |
| Business Case | gpt-4o-nano/mini | Structured financial data, strict validation |
| Risk Register | gpt-5-nano/mini | Narrative risk descriptions and analysis |
| Project Plan | gpt-5-nano/mini | Strategic planning and timeline narrative |
| Communication Plan | gpt-5-nano/mini | Stakeholder analysis and engagement strategies |
| Quality Management | gpt-5-nano/mini | Process descriptions and standards |
| Technical Landscape | gpt-5-nano/mini | Technical analysis and architecture description |
| Comparable Projects | gpt-5-nano/mini | Case studies and comparative analysis |
Symptom: JSON parsing errors, truncated responses,
[object Object] in output
Cause: GPT-5's responses.create API returns plain text that gets truncated for large schemas
Solution: Use GPT-4o models with zodResponseFormat for these structured documents
Symptom: All tokens allocated to reasoning_tokens, empty content Cause: Known bug with GPT-5 models using chat.completions.create Solution: Always use
responses.create API for GPT-5 models
We use Resend.com for transactional emails.
npm install resend npm install react-email @react-email/components # For email templates
Add to
.env.local:
RESEND_API_KEY=your_api_key_here [email protected] [email protected]
// lib/email.ts import { Resend } from 'resend'; const resend = new Resend(process.env.RESEND_API_KEY); export async function sendEmail({ to, subject, react, text, }: { to: string | string[]; subject: string; react?: React.ReactElement; text?: string; }) { const { data, error } = await resend.emails.send({ from: process.env.RESEND_FROM_EMAIL || '[email protected]', to, subject, react, text, }); if (error) { throw new Error(error.message); } return data; }
// emails/welcome.tsx import { Body, Button, Container, Head, Html, Preview, Section, Text, } from '@react-email/components'; export default function WelcomeEmail({ name }: { name: string }) { return ( <Html> <Head /> <Preview>Welcome to our platform!</Preview> <Body style={main}> <Container style={container}> <Text style={paragraph}>Hi {name},</Text> <Text style={paragraph}> Welcome to our platform! We're excited to have you on board. </Text> <Section style={btnContainer}> <Button style={button} href="https://yourdomain.com"> Get Started </Button> </Section> </Container> </Body> </Html> ); } // Styles const main = { backgroundColor: '#ffffff' }; const container = { margin: '0 auto', padding: '20px 0 48px' }; const paragraph = { fontSize: '16px', lineHeight: '26px' }; const btnContainer = { textAlign: 'center' as const }; const button = { backgroundColor: '#5F51E8', borderRadius: '3px', color: '#fff', fontSize: '16px', textDecoration: 'none', textAlign: 'center' as const, display: 'block', padding: '12px', };
# Core testing npm install -D vitest @vitest/ui @vitest/coverage-v8 npm install -D @testing-library/react @testing-library/user-event @testing-library/react-hooks # API Mocking npm install -D msw # Testing utilities npm install -D @testing-library/jest-dom npm install -D eslint-plugin-testing-library
npm run test # Run tests npm run test:ui # Open Vitest UI npm run test:coverage # Generate coverage report
Use HSL-based CSS variables for light/dark mode support:
/* globals.css - Default color scheme */ @layer base { :root { --background: 0 0% 100%; --foreground: 222.2 84% 4.9%; --primary: 222.2 47.4% 11.2%; --primary-foreground: 210 40% 98%; --secondary: 210 40% 96.1%; --muted: 210 40% 96.1%; --accent: 210 40% 96.1%; --destructive: 0 84.2% 60.2%; --border: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%; --ring: 222.2 84% 4.9%; --radius: 0.5rem; } .dark { --background: 222.2 84% 4.9%; --foreground: 210 40% 98%; --primary: 210 40% 98%; --primary-foreground: 222.2 47.4% 11.2%; /* ... dark mode colors */ } }
/* Blob animations for backgrounds */ @keyframes blob { 0% { transform: translate(0px, 0px) scale(1); } 33% { transform: translate(30px, -50px) scale(1.1); } 66% { transform: translate(-20px, 20px) scale(0.9); } 100% { transform: translate(0px, 0px) scale(1); } } .animate-blob { animation: blob 10s infinite; } .animate-blob-slow { animation: blob-slow 15s infinite; } .animate-pulse-slow { animation: pulse-slow 8s infinite; }
Two animated canvas-based backgrounds (no external libraries):
AnimatedBackground - Vibrant waves for hero sections
AnimatedBackgroundSubtle - Subtle version for content pages
// lib/utils.ts import { clsx, type ClassValue } from "clsx" import { twMerge } from "tailwind-merge" export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) }
// tailwind.config.ts const config = { darkMode: ["class"], theme: { extend: { colors: { // Use CSS variables for theming background: "hsl(var(--background))", foreground: "hsl(var(--foreground))", primary: { DEFAULT: "hsl(var(--primary))", foreground: "hsl(var(--primary-foreground))", }, // ... etc }, borderRadius: { lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)", }, }, }, }
# Core SEO npm install next-seo npm install next-sitemap # Schema & Structured Data npm install schema-dts # Analytics & Monitoring npm install @vercel/analytics @vercel/speed-insights npm install @next/third-parties # For Google Analytics # SEO Testing npm install -D lighthouse
next-seo.config.js - Global SEO settingsnext-sitemap.config.js - Sitemap generationrobots.txt - Search engine directivesproject/ āāā app/ # Next.js App Router ā āāā (auth)/ # Auth group routes ā āāā (marketing)/ # Public pages ā āāā (dashboard)/ # Protected pages ā āāā api/ # API routes ā āāā layout.tsx # Root layout ā āāā page.tsx # Home page āāā components/ ā āāā ui/ # shadcn/ui components ā āāā forms/ # Form components ā āāā layouts/ # Layout components āāā lib/ ā āāā supabase/ # Supabase client & types ā āāā utils/ # Utility functions ā āāā hooks/ # Custom React hooks āāā tests/ ā āāā unit/ # Unit tests ā āāā integration/ # Integration tests ā āāā e2e/ # End-to-end tests āāā public/ # Static assets āāā styles/ ā āāā globals.css # Global styles with Tailwind āāā development-progress-implementation-log.md # Implementation tracking
ALL projects MUST maintain a
file in the project root.development-progress-implementation-log.md
This file tracks all significant implementations with:
### v1.0 - Initial Setup **Date: 2025-01-25** **Timestamp: 10:00 GMT** #### Features Implemented: - Project initialization - Core functionality - Database schema ### v2.0 - Feature Enhancement **Date: 2025-01-25** **Timestamp: 15:30 GMT** #### Features Implemented: - New feature description - Files modified - Performance improvements
When to Update:
Auto-create this file when starting work on any project that doesn't have one.
any){ "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint", "test": "vitest", "test:ui": "vitest --ui", "test:coverage": "vitest --coverage", "test:e2e": "playwright test", "type-check": "tsc --noEmit", "format": "prettier --write .", "analyze": "ANALYZE=true next build", "lighthouse": "lighthouse http://localhost:3000 --view" } }
{ "compilerOptions": { "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true } }
Available at
~/.claude-docs/docs/:
npm run dev # Start dev server npm run build # Build for production npm run lint # Run ESLint npm run type-check # Check TypeScript
# Development workflow supabase start # Start local Supabase supabase migration new descriptive_change_name # Create migration supabase db reset # Test migration locally supabase gen types typescript --local > lib/database.types.ts # Generate types supabase db push # Deploy to production # Debugging supabase db diff # Check local vs remote supabase db lint # Check for issues
vercel # Deploy to Vercel vercel --prod # Deploy to production gh repo create # Create GitHub repo gh pr create # Create pull request
npm test # Run tests npm run test:coverage # With coverage npm run lighthouse # SEO/Performance audit
When starting a new project, run
claude-init in an empty directory. It will:
gh repo create)vercel)mkdir my-project && cd my-project claude-init # Creates entire project automatically cp .env.local.example .env.local # Then add your API keys npm run dev
Stripe is fully integrated with products and pricing configured via CLI.
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_51RzYXh2dFhd680hG... STRIPE_SECRET_KEY=sk_test_51RzYXh2dFhd680hG... STRIPE_WEBHOOK_SECRET=whsec_dcf456ce20a7fd39c813b7ae4d4cfa436... # Price IDs (Already Created) NEXT_PUBLIC_STRIPE_PRICE_ID_BASIC_MONTHLY=price_1RzZ1t2dFhd680hGONR7esBs NEXT_PUBLIC_STRIPE_PRICE_ID_BASIC_ANNUAL=price_1RzZ232dFhd680hGZsmm1VJM NEXT_PUBLIC_STRIPE_PRICE_ID_PREMIUM_MONTHLY=price_1RzZ2M2dFhd680hGSWQOd2cQ NEXT_PUBLIC_STRIPE_PRICE_ID_PREMIUM_ANNUAL=price_1RzZ2V2dFhd680hGnSHYUZeu
# Authentication stripe login # Authenticate with Stripe account # Product Management stripe products list # List all products stripe prices list # List all prices stripe customers list # List customers # Webhook Testing stripe listen --forward-to localhost:3000/api/stripe/webhook # Create checkout session (example) stripe checkout sessions create \ --success-url="http://localhost:3000/success" \ --cancel-url="http://localhost:3000/cancel" \ --line-items="price=price_1RzZ1t2dFhd680hGONR7esBs,quantity=1" \ --mode=subscription # View logs stripe logs tail # Stream API logs
/lib/stripe/client.ts - Stripe client initialization/lib/hooks/use-stripe-checkout.ts - Checkout session hook/app/api/stripe/checkout/route.ts - Checkout API endpoint/app/api/stripe/webhook/route.ts - Webhook handler/STRIPE_QUICKSTART.md - Quick setup guide/docs/stripe-llm-docs.txt - Complete Stripe LLM documentation (cached locally)4242 4242 4242 4242 # Success 4000 0000 0000 0002 # Decline 4000 0025 0000 3155 # Requires authentication
// Create checkout session (already implemented) const { createCheckoutSession } = useStripeCheckout() await createCheckoutSession(priceId, 'monthly') // Open customer portal (already implemented) const { openCustomerPortal } = useStripeCheckout() await openCustomerPortal()
/docs/stripe-llm-docs.txt/STRIPE_QUICKSTART.md