Skill: bullkis1/rapid-prototyper (version 1.0.0)

--- name: rapid-prototyper description: >- Ultra-fast proof-of-concept and MVP development. Use when building new web apps, prototypes, or MVPs from scratch where speed matters over perfection. Specializes in the canonical fast-stack: Next.js 14 + Supabase + Clerk + shadcn/ui + Prisma. Triggers when user asks to "build", "prototype", "create a quick app", "spin up an MVP", or wants a working thing fast. NOT for small one-liner fixes or edits to existing codebases. --- # Rapid Prototyper Build working MVPs in days, not weeks. Speed-first. No over-engineering. ## Mindset - Ship working code over elegant architecture - Use pre-built components and BaaS over custom infra - Validate the idea first — optimize later - Include analytics and feedback hooks from day one ## The Stack **Always default to this unless there's a specific reason not to:** | Layer | Tool | Why | |---|---|---| | Framework | Next.js 14 (App Router) | Full-stack, fast setup | | Database | Supabase (PostgreSQL) | Instant DB + realtime + storage | | Auth | Clerk | Drop-in auth in minutes | | UI | shadcn/ui + Tailwind | Beautiful components, no design needed | | ORM | Prisma | Type-safe DB access | | State | Zustand | Simple, no boilerplate | | Forms | React Hook Form + Zod | Validation out of the box | | Animation | Framer Motion | Polish when needed | See `references/stack-setup.md` for full setup commands and boilerplate. ## Workflow ### Phase 1 — Define the core (5 min) 1. What is the ONE thing users do in this app? 2. What does success look like? (define metric now, not later) 3. What's the minimum to test the hypothesis? ### Phase 2 — Scaffold (15–30 min) ```bash npx create-next-app@latest my-app --typescript --tailwind --eslint --app cd my-app npx shadcn@latest init ``` Then: add Prisma, Supabase client, Clerk provider. See `references/stack-setup.md`. ### Phase 3 — Build core user flow only - One happy path first - Skip edge cases, error states, loading states until core works - Use shadcn components — don't style from scratch ### Phase 4 — Add feedback collection - Always add a simple feedback mechanism (textarea + submit) - Add Vercel Analytics or PostHog from day one - Log key actions ### Phase 5 — Deploy ```bash npx vercel --prod # or: push to GitHub → auto-deploy on Vercel ``` ## Critical Rules - **Never** spend more than 1 hour on auth — use Clerk - **Never** design a custom DB schema without checking if Supabase can handle it with defaults - **Always** deploy to a URL (even dev preview) before calling it done - **Always** ask: "Is this feature necessary to test the core hypothesis?" ## Success Metrics - Working app deployed to URL: ✅ done - Core user flow completable end-to-end: ✅ done - At least one real user can test it: ✅ done - Feedback collection in place: ✅ done ## References - `references/stack-setup.md` — Full setup commands, env vars, boilerplate code - `references/patterns.md` — Common patterns: auth routes, DB queries, API routes, forms

path: references/patterns.md
content: # Common Patterns

## API route with Prisma

```ts
// app/api/items/route.ts
import { auth } from '@clerk/nextjs/server'
import { prisma } from '@/lib/prisma'
import { NextResponse } from 'next/server'

export async function GET() {
  const { userId } = auth()
  if (!userId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })

  const items = await prisma.item.findMany({ where: { userId } })
  return NextResponse.json(items)
}

export async function POST(req: Request) {
  const { userId } = auth()
  if (!userId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })

  const body = await req.json()
  const item = await prisma.item.create({ data: { ...body, userId } })
  return NextResponse.json(item, { status: 201 })
}
```

## Form with Zod validation

```tsx
'use client'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'

const schema = z.object({
  title: z.string().min(1, 'Required').max(100),
  description: z.string().optional(),
})

type FormData = z.infer<typeof schema>

export function CreateItemForm({ onSuccess }: { onSuccess: () => void }) {
  const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<FormData>({
    resolver: zodResolver(schema),
  })

  const onSubmit = async (data: FormData) => {
    await fetch('/api/items', { method: 'POST', body: JSON.stringify(data) })
    onSuccess()
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
      <Input {...register('title')} placeholder="Title" />
      {errors.title && <p className="text-red-500 text-sm">{errors.title.message}</p>}
      <Button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Creating...' : 'Create'}
      </Button>
    </form>
  )
}
```

## Zustand store

```ts
// stores/useAppStore.ts
import { create } from 'zustand'

interface AppState {
  items: Item[]
  setItems: (items: Item[]) => void
  addItem: (item: Item) => void
}

export const useAppStore = create<AppState>((set) => ({
  items: [],
  setItems: (items) => set({ items }),
  addItem: (item) => set((state) => ({ items: [...state.items, item] })),
}))
```

## Server component with data fetch

```tsx
// app/dashboard/page.tsx
import { auth } from '@clerk/nextjs/server'
import { redirect } from 'next/navigation'
import { prisma } from '@/lib/prisma'

export default async function DashboardPage() {
  const { userId } = auth()
  if (!userId) redirect('/sign-in')

  const items = await prisma.item.findMany({
    where: { userId },
    orderBy: { createdAt: 'desc' },
  })

  return (
    <div className="container mx-auto py-8">
      <h1 className="text-2xl font-bold mb-6">Dashboard</h1>
      {items.map(item => (
        <div key={item.id} className="p-4 border rounded mb-2">{item.title}</div>
      ))}
    </div>
  )
}
```

## Feedback widget (always include)

```tsx
// components/FeedbackWidget.tsx
'use client'
import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Textarea } from '@/components/ui/textarea'

export function FeedbackWidget() {
  const [open, setOpen] = useState(false)
  const [text, setText] = useState('')
  const [sent, setSent] = useState(false)

  const submit = async () => {
    await fetch('/api/feedback', {
      method: 'POST',
      body: JSON.stringify({ feedback: text, url: window.location.href }),
    })
    setSent(true)
  }

  if (!open) return (
    <button onClick={() => setOpen(true)}
      className="fixed bottom-4 right-4 bg-black text-white px-3 py-2 rounded-full text-sm">
      Feedback
    </button>
  )

  return (
    <div className="fixed bottom-4 right-4 bg-white border rounded-lg p-4 shadow-lg w-72">
      {sent ? <p className="text-green-600">Thanks! 🙏</p> : (
        <>
          <Textarea placeholder="What's on your mind?" value={text} onChange={e => setText(e.target.value)} />
          <div className="flex gap-2 mt-2">
            <Button size="sm" onClick={submit}>Send</Button>
            <Button size="sm" variant="ghost" onClick={() => setOpen(false)}>Cancel</Button>
          </div>
        </>
      )}
    </div>
  )
}
```


path: references/stack-setup.md
content: # Stack Setup Reference

## Full project bootstrap

```bash
# 1. Create Next.js app
npx create-next-app@latest my-app --typescript --tailwind --eslint --app
cd my-app

# 2. shadcn/ui
npx shadcn@latest init
npx shadcn@latest add button input label card dialog form toast

# 3. Prisma + Supabase
npm install prisma @prisma/client @supabase/supabase-js
npx prisma init

# 4. Clerk auth
npm install @clerk/nextjs

# 5. Zustand + forms
npm install zustand react-hook-form @hookform/resolvers zod

# 6. Optional: Framer Motion
npm install framer-motion
```

## Environment variables (.env.local)

```env
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key
DATABASE_URL=postgresql://postgres:[REDACTED_SECRET]:5432/postgres

# Clerk
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxx
CLERK_SECRET_KEY=sk_test_xxx
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/dashboard
```

## Prisma schema starter

```prisma
// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        String   @id @default(cuid())
  clerkId   String   @unique
  email     String   @unique
  name      String?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
```

```bash
npx prisma db push   # push schema to Supabase
npx prisma generate  # generate client
```

## Clerk root layout

```tsx
// app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <ClerkProvider>
      <html lang="en">
        <body>{children}</body>
      </html>
    </ClerkProvider>
  )
}
```

## Middleware (protect routes)

```ts
// middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'

const isPublicRoute = createRouteMatcher(['/', '/sign-in(.*)', '/sign-up(.*)'])

export default clerkMiddleware((auth, req) => {
  if (!isPublicRoute(req)) auth().protect()
})

export const config = {
  matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
}
```

## Supabase client

```ts
// lib/supabase.ts
import { createClient } from '@supabase/supabase-js'

export const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
```

## Prisma client singleton

```ts
// lib/prisma.ts
import { PrismaClient } from '@prisma/client'

const globalForPrisma = global as unknown as { prisma: PrismaClient }

export const prisma = globalForPrisma.prisma || new PrismaClient()

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
```

## Deploy to Vercel

```bash
npm install -g vercel
vercel          # preview deploy
vercel --prod   # production deploy
```

Add env vars in Vercel dashboard → Settings → Environment Variables.


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)