[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M76VC4F33HRCTGXJ1RDP4VT4":3,"skill-same:01M76VC4F33HRCTGXJ1RDP4VT4":70},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":24,"commitSha":25,"skillDirectory":26,"forksCount":27,"repositoryHtmlUrl":28,"repositoryHomepageUrl":6,"ownerHtmlUrl":29,"ownerBlogUrl":30,"ownerDescription":6,"updatedAt":31,"install":32,"resources":45},"01M76VC4F33HRCTGXJ1RDP4VT4","saas-scaffolder",null,"Generates complete, production-ready SaaS project boilerplate including authentication, database schemas, billing integration, API routes, and a working dashboard using Next.js 14+ App Router, TypeScript, Tailwind CSS, shadcn\u002Fui, Drizzle ORM, and Stripe. Use when the user wants to create a new SaaS app, start a subscription-based web project, scaffold a Next.js application, or mentions terms like starter template, boilerplate, new project, or wiring up auth and payments.","kruxshnx\u002Fclaude-skills-devin","kruxshnx","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F127938788?v=4","01M757YM3F96N4K1BSE7Z0NQK7","ARJANGI GOVINDA KRISHNA","01M757YM43H5T7MP2B3DC13GZ2",2,"MIT","https:\u002F\u002Fgithub.com\u002Fkruxshnx\u002Fclaude-skills-devin\u002Fblob\u002Fdevin\u002F1780503568-install-devin-skills\u002F.devin\u002Fskills\u002Fsaas-scaffolder\u002FSKILL.md",[],[],"2026-11-12 13:55:43",false,"---\nname: \"saas-scaffolder\"\ndescription: \"Generates complete, production-ready SaaS project boilerplate including authentication, database schemas, billing integration, API routes, and a working dashboard using Next.js 14+ App Router, TypeScript, Tailwind CSS, shadcn\u002Fui, Drizzle ORM, and Stripe. Use when the user wants to create a new SaaS app, start a subscription-based web project, scaffold a Next.js application, or mentions terms like starter template, boilerplate, new project, or wiring up auth and payments.\"\n---\n\n# SaaS Scaffolder\n\n**Tier:** POWERFUL  \n**Category:** Product Team  \n**Domain:** Full-Stack Development \u002F Project Bootstrapping\n\n---\n\n## Input Format\n\n```\nProduct: [name]\nDescription: [1-3 sentences]\nAuth: nextauth | clerk | supabase\nDatabase: neondb | supabase | planetscale\nPayments: stripe | lemonsqueezy | none\nFeatures: [comma-separated list]\n```\n\n---\n\n## File Tree Output\n\n```\nmy-saas\u002F\n├── app\u002F\n│   ├── (auth)\u002F\n│   │   ├── login\u002Fpage.tsx\n│   │   ├── register\u002Fpage.tsx\n│   │   └── layout.tsx\n│   ├── (dashboard)\u002F\n│   │   ├── dashboard\u002Fpage.tsx\n│   │   ├── settings\u002Fpage.tsx\n│   │   ├── billing\u002Fpage.tsx\n│   │   └── layout.tsx\n│   ├── (marketing)\u002F\n│   │   ├── page.tsx\n│   │   ├── pricing\u002Fpage.tsx\n│   │   └── layout.tsx\n│   ├── api\u002F\n│   │   ├── auth\u002F[...nextauth]\u002Froute.ts\n│   │   ├── webhooks\u002Fstripe\u002Froute.ts\n│   │   ├── billing\u002Fcheckout\u002Froute.ts\n│   │   └── billing\u002Fportal\u002Froute.ts\n│   └── layout.tsx\n├── components\u002F\n│   ├── ui\u002F\n│   ├── auth\u002F\n│   │   ├── login-form.tsx\n│   │   └── register-form.tsx\n│   ├── dashboard\u002F\n│   │   ├── sidebar.tsx\n│   │   ├── header.tsx\n│   │   └── stats-card.tsx\n│   ├── marketing\u002F\n│   │   ├── hero.tsx\n│   │   ├── features.tsx\n│   │   ├── pricing.tsx\n│   │   └── footer.tsx\n│   └── billing\u002F\n│       ├── plan-card.tsx\n│       └── usage-meter.tsx\n├── lib\u002F\n│   ├── auth.ts\n│   ├── db.ts\n│   ├── stripe.ts\n│   ├── validations.ts\n│   └── utils.ts\n├── db\u002F\n│   ├── schema.ts\n│   └── migrations\u002F\n├── hooks\u002F\n│   ├── use-subscription.ts\n│   └── use-user.ts\n├── types\u002Findex.ts\n├── middleware.ts\n├── .env.example\n├── drizzle.config.ts\n└── next.config.ts\n```\n\n---\n\n## Key Component Patterns\n\n### Auth Config (NextAuth)\n\n```typescript\n\u002F\u002F lib\u002Fauth.ts\nimport { NextAuthOptions } from \"next-auth\"\nimport GoogleProvider from \"next-auth\u002Fproviders\u002Fgoogle\"\nimport { DrizzleAdapter } from \"@auth\u002Fdrizzle-adapter\"\nimport { db } from \".\u002Fdb\"\n\nexport const authOptions: NextAuthOptions = {\n  adapter: DrizzleAdapter(db),\n  providers: [\n    GoogleProvider({\n      clientId: process.env.GOOGLE_CLIENT_ID!,\n      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n    }),\n  ],\n  callbacks: {\n    session: async ({ session, user }) => ({\n      ...session,\n      user: {\n        ...session.user,\n        id: user.id,\n        subscriptionStatus: user.subscriptionStatus,\n      },\n    }),\n  },\n  pages: { signIn: \"\u002Flogin\" },\n}\n```\n\n### Database Schema (Drizzle + NeonDB)\n\n```typescript\n\u002F\u002F db\u002Fschema.ts\nimport { pgTable, text, timestamp, integer } from \"drizzle-orm\u002Fpg-core\"\n\nexport const users = pgTable(\"users\", {\n  id: text(\"id\").primaryKey().$defaultFn(() => crypto.randomUUID()),\n  name: text(\"name\"),\n  email: text(\"email\").notNull().unique(),\n  emailVerified: timestamp(\"emailVerified\"),\n  image: text(\"image\"),\n  stripeCustomerId: text(\"stripe_customer_id\").unique(),\n  stripeSubscriptionId: text(\"stripe_subscription_id\"),\n  stripePriceId: text(\"stripe_price_id\"),\n  stripeCurrentPeriodEnd: timestamp(\"stripe_current_period_end\"),\n  createdAt: timestamp(\"created_at\").defaultNow().notNull(),\n})\n\nexport const accounts = pgTable(\"accounts\", {\n  userId: text(\"user_id\").notNull().references(() => users.id, { onDelete: \"cascade\" }),\n  type: text(\"type\").notNull(),\n  provider: text(\"provider\").notNull(),\n  providerAccountId: text(\"provider_account_id\").notNull(),\n  refresh_token: text(\"refresh_token\"),\n  access_token: text(\"access_token\"),\n  expires_at: integer(\"expires_at\"),\n})\n```\n\n### Stripe Checkout Route\n\n```typescript\n\u002F\u002F app\u002Fapi\u002Fbilling\u002Fcheckout\u002Froute.ts\nimport { NextResponse } from \"next\u002Fserver\"\nimport { getServerSession } from \"next-auth\"\nimport { authOptions } from \"@\u002Flib\u002Fauth\"\nimport { stripe } from \"@\u002Flib\u002Fstripe\"\nimport { db } from \"@\u002Flib\u002Fdb\"\nimport { users } from \"@\u002Fdb\u002Fschema\"\nimport { eq } from \"drizzle-orm\"\n\nexport async function POST(req: Request) {\n  const session = await getServerSession(authOptions)\n  if (!session?.user) return NextResponse.json({ error: \"Unauthorized\" }, { status: 401 })\n\n  const { priceId } = await req.json()\n  const [user] = await db.select().from(users).where(eq(users.id, session.user.id))\n\n  let customerId = user.stripeCustomerId\n  if (!customerId) {\n    const customer = await stripe.customers.create({ email: session.user.email! })\n    customerId = customer.id\n    await db.update(users).set({ stripeCustomerId: customerId }).where(eq(users.id, user.id))\n  }\n\n  const checkoutSession = await stripe.checkout.sessions.create({\n    customer: customerId,\n    mode: \"subscription\",\n    payment_method_types: [\"card\"],\n    line_items: [{ price: priceId, quantity: 1 }],\n    success_url: `${process.env.NEXT_PUBLIC_APP_URL}\u002Fdashboard?upgraded=true`,\n    cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}\u002Fpricing`,\n    subscription_data: { trial_period_days: 14 },\n  })\n\n  return NextResponse.json({ url: checkoutSession.url })\n}\n```\n\n### Middleware\n\n```typescript\n\u002F\u002F middleware.ts\nimport { withAuth } from \"next-auth\u002Fmiddleware\"\nimport { NextResponse } from \"next\u002Fserver\"\n\nexport default withAuth(\n  function middleware(req) {\n    const token = req.nextauth.token\n    if (req.nextUrl.pathname.startsWith(\"\u002Fdashboard\") && !token) {\n      return NextResponse.redirect(new URL(\"\u002Flogin\", req.url))\n    }\n  },\n  { callbacks: { authorized: ({ token }) => !!token } }\n)\n\nexport const config = {\n  matcher: [\"\u002Fdashboard\u002F:path*\", \"\u002Fsettings\u002F:path*\", \"\u002Fbilling\u002F:path*\"],\n}\n```\n\n### Environment Variables Template\n\n```bash\n# .env.example\nNEXT_PUBLIC_APP_URL=http:\u002F\u002Flocalhost:3000\nDATABASE_URL=postgresql:\u002F\u002Fuser:pass@ep-xxx.us-east-1.aws.neon.tech\u002Fneondb?sslmode=require\nNEXTAUTH_SECRET=generate-with-openssl-rand-base64-32\nNEXTAUTH_URL=http:\u002F\u002Flocalhost:3000\nGOOGLE_CLIENT_ID=\nGOOGLE_CLIENT_SECRET=\nSTRIPE_SECRET_KEY=sk_test_...\nSTRIPE_WEBHOOK_SECRET=whsec_...\nNEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...\nSTRIPE_PRO_PRICE_ID=price_...\n```\n\n---\n\n## Scaffold Checklist\n\nThe following phases must be completed in order. **Validate at the end of each phase before proceeding.**\n\n### Phase 1 — Foundation\n- [ ] 1. Next.js initialized with TypeScript and App Router\n- [ ] 2. Tailwind CSS configured with custom theme tokens\n- [ ] 3. shadcn\u002Fui installed and configured\n- [ ] 4. ESLint + Prettier configured\n- [ ] 5. `.env.example` created with all required variables\n\n✅ **Validate:** Run `npm run build` — no TypeScript or lint errors should appear.  \n🔧 **If build fails:** Check `tsconfig.json` paths and that all shadcn\u002Fui peer dependencies are installed.\n\n### Phase 2 — Database\n- [ ] 6. Drizzle ORM installed and configured\n- [ ] 7. Schema written (users, accounts, sessions, verification_tokens)\n- [ ] 8. Initial migration generated and applied\n- [ ] 9. DB client singleton exported from `lib\u002Fdb.ts`\n- [ ] 10. DB connection tested in local environment\n\n✅ **Validate:** Run a simple `db.select().from(users)` in a test script — it should return an empty array without throwing.  \n🔧 **If DB connection fails:** Verify `DATABASE_URL` format includes `?sslmode=require` for NeonDB\u002FSupabase. Check that the migration has been applied with `drizzle-kit push` (dev) or `drizzle-kit migrate` (prod).\n\n### Phase 3 — Authentication\n- [ ] 11. Auth provider installed (NextAuth \u002F Clerk \u002F Supabase)\n- [ ] 12. OAuth provider configured (Google \u002F GitHub)\n- [ ] 13. Auth API route created\n- [ ] 14. Session callback adds user ID and subscription status\n- [ ] 15. Middleware protects dashboard routes\n- [ ] 16. Login and register pages built with error states\n\n✅ **Validate:** Sign in via OAuth, confirm session user has `id` and `subscriptionStatus`. Attempt to access `\u002Fdashboard` without a session — you should be redirected to `\u002Flogin`.  \n🔧 **If sign-out loops occur in production:** Ensure `NEXTAUTH_SECRET` is set and consistent across deployments. Add `declare module \"next-auth\"` to extend session types if TypeScript errors appear.\n\n### Phase 4 — Payments\n- [ ] 17. Stripe client initialized with TypeScript types\n- [ ] 18. Checkout session route created\n- [ ] 19. Customer portal route created\n- [ ] 20. Stripe webhook handler with signature verification\n- [ ] 21. Webhook updates user subscription status in DB idempotently\n\n✅ **Validate:** Complete a Stripe test checkout using a `4242 4242 4242 4242` card. Confirm `stripeSubscriptionId` is written to the DB. Replay the `checkout.session.completed` webhook event and confirm idempotency (no duplicate DB writes).  \n🔧 **If webhook signature fails:** Use `stripe listen --forward-to localhost:3000\u002Fapi\u002Fwebhooks\u002Fstripe` locally — never hardcode the raw webhook secret. Verify `STRIPE_WEBHOOK_SECRET` matches the listener output.\n\n### Phase 5 — UI\n- [ ] 22. Landing page with hero, features, pricing sections\n- [ ] 23. Dashboard layout with sidebar and responsive header\n- [ ] 24. Billing page showing current plan and upgrade options\n- [ ] 25. Settings page with profile update form and success states\n\n✅ **Validate:** Run `npm run build` for a final production build check. Navigate all routes manually and confirm no broken layouts, missing session data, or hydration errors.\n\n---\n\n## Reference Files\n\nFor additional guidance, generate the following companion reference files alongside the scaffold:\n\n- **`CUSTOMIZATION.md`** — Auth providers, database options, ORM alternatives, payment providers, UI themes, and billing models (per-seat, flat-rate, usage-based).\n- **`PITFALLS.md`** — Common failure modes: missing `NEXTAUTH_SECRET`, webhook secret mismatches, Edge runtime conflicts with Drizzle, unextended session types, and migration strategy differences between dev and prod.\n- **`BEST_PRACTICES.md`** — Stripe singleton pattern, server actions for form mutations, idempotent webhook handlers, `Suspense` boundaries for async dashboard data, server-side feature gating via `stripeCurrentPeriodEnd`, and rate limiting on auth routes with Upstash Redis + `@upstash\u002Fratelimit`.\n","# SaaS Scaffolder\n\n**Tier:** POWERFUL  \n**Category:** Product Team  \n**Domain:** Full-Stack Development \u002F Project Bootstrapping\n\n---\n\n## Input Format\n\n```\nProduct: [name]\nDescription: [1-3 sentences]\nAuth: nextauth | clerk | supabase\nDatabase: neondb | supabase | planetscale\nPayments: stripe | lemonsqueezy | none\nFeatures: [comma-separated list]\n```\n\n---\n\n## File Tree Output\n\n```\nmy-saas\u002F\n├── app\u002F\n│   ├── (auth)\u002F\n│   │   ├── login\u002Fpage.tsx\n│   │   ├── register\u002Fpage.tsx\n│   │   └── layout.tsx\n│   ├── (dashboard)\u002F\n│   │   ├── dashboard\u002Fpage.tsx\n│   │   ├── settings\u002Fpage.tsx\n│   │   ├── billing\u002Fpage.tsx\n│   │   └── layout.tsx\n│   ├── (marketing)\u002F\n│   │   ├── page.tsx\n│   │   ├── pricing\u002Fpage.tsx\n│   │   └── layout.tsx\n│   ├── api\u002F\n│   │   ├── auth\u002F[...nextauth]\u002Froute.ts\n│   │   ├── webhooks\u002Fstripe\u002Froute.ts\n│   │   ├── billing\u002Fcheckout\u002Froute.ts\n│   │   └── billing\u002Fportal\u002Froute.ts\n│   └── layout.tsx\n├── components\u002F\n│   ├── ui\u002F\n│   ├── auth\u002F\n│   │   ├── login-form.tsx\n│   │   └── register-form.tsx\n│   ├── dashboard\u002F\n│   │   ├── sidebar.tsx\n│   │   ├── header.tsx\n│   │   └── stats-card.tsx\n│   ├── marketing\u002F\n│   │   ├── hero.tsx\n│   │   ├── features.tsx\n│   │   ├── pricing.tsx\n│   │   └── footer.tsx\n│   └── billing\u002F\n│       ├── plan-card.tsx\n│       └── usage-meter.tsx\n├── lib\u002F\n│   ├── auth.ts\n│   ├── db.ts\n│   ├── stripe.ts\n│   ├── validations.ts\n│   └── utils.ts\n├── db\u002F\n│   ├── schema.ts\n│   └── migrations\u002F\n├── hooks\u002F\n│   ├── use-subscription.ts\n│   └── use-user.ts\n├── types\u002Findex.ts\n├── middleware.ts\n├── .env.example\n├── drizzle.config.ts\n└── next.config.ts\n```\n\n---\n\n## Key Component Patterns\n\n### Auth Config (NextAuth)\n\n```typescript\n\u002F\u002F lib\u002Fauth.ts\nimport { NextAuthOptions } from \"next-auth\"\nimport GoogleProvider from \"next-auth\u002Fproviders\u002Fgoogle\"\nimport { DrizzleAdapter } from \"@auth\u002Fdrizzle-adapter\"\nimport { db } from \".\u002Fdb\"\n\nexport const authOptions: NextAuthOptions = {\n  adapter: DrizzleAdapter(db),\n  providers: [\n    GoogleProvider({\n      clientId: process.env.GOOGLE_CLIENT_ID!,\n      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n    }),\n  ],\n  callbacks: {\n    session: async ({ session, user }) => ({\n      ...session,\n      user: {\n        ...session.user,\n        id: user.id,\n        subscriptionStatus: user.subscriptionStatus,\n      },\n    }),\n  },\n  pages: { signIn: \"\u002Flogin\" },\n}\n```\n\n### Database Schema (Drizzle + NeonDB)\n\n```typescript\n\u002F\u002F db\u002Fschema.ts\nimport { pgTable, text, timestamp, integer } from \"drizzle-orm\u002Fpg-core\"\n\nexport const users = pgTable(\"users\", {\n  id: text(\"id\").primaryKey().$defaultFn(() => crypto.randomUUID()),\n  name: text(\"name\"),\n  email: text(\"email\").notNull().unique(),\n  emailVerified: timestamp(\"emailVerified\"),\n  image: text(\"image\"),\n  stripeCustomerId: text(\"stripe_customer_id\").unique(),\n  stripeSubscriptionId: text(\"stripe_subscription_id\"),\n  stripePriceId: text(\"stripe_price_id\"),\n  stripeCurrentPeriodEnd: timestamp(\"stripe_current_period_end\"),\n  createdAt: timestamp(\"created_at\").defaultNow().notNull(),\n})\n\nexport const accounts = pgTable(\"accounts\", {\n  userId: text(\"user_id\").notNull().references(() => users.id, { onDelete: \"cascade\" }),\n  type: text(\"type\").notNull(),\n  provider: text(\"provider\").notNull(),\n  providerAccountId: text(\"provider_account_id\").notNull(),\n  refresh_token: text(\"refresh_token\"),\n  access_token: text(\"access_token\"),\n  expires_at: integer(\"expires_at\"),\n})\n```\n\n### Stripe Checkout Route\n\n```typescript\n\u002F\u002F app\u002Fapi\u002Fbilling\u002Fcheckout\u002Froute.ts\nimport { NextResponse } from \"next\u002Fserver\"\nimport { getServerSession } from \"next-auth\"\nimport { authOptions } from \"@\u002Flib\u002Fauth\"\nimport { stripe } from \"@\u002Flib\u002Fstripe\"\nimport { db } from \"@\u002Flib\u002Fdb\"\nimport { users } from \"@\u002Fdb\u002Fschema\"\nimport { eq } from \"drizzle-orm\"\n\nexport async function POST(req: Request) {\n  const session = await getServerSession(authOptions)\n  if (!session?.user) return NextResponse.json({ error: \"Unauthorized\" }, { status: 401 })\n\n  const { priceId } = await req.json()\n  const [user] = await db.select().from(users).where(eq(users.id, session.user.id))\n\n  let customerId = user.stripeCustomerId\n  if (!customerId) {\n    const customer = await stripe.customers.create({ email: session.user.email! })\n    customerId = customer.id\n    await db.update(users).set({ stripeCustomerId: customerId }).where(eq(users.id, user.id))\n  }\n\n  const checkoutSession = await stripe.checkout.sessions.create({\n    customer: customerId,\n    mode: \"subscription\",\n    payment_method_types: [\"card\"],\n    line_items: [{ price: priceId, quantity: 1 }],\n    success_url: `${process.env.NEXT_PUBLIC_APP_URL}\u002Fdashboard?upgraded=true`,\n    cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}\u002Fpricing`,\n    subscription_data: { trial_period_days: 14 },\n  })\n\n  return NextResponse.json({ url: checkoutSession.url })\n}\n```\n\n### Middleware\n\n```typescript\n\u002F\u002F middleware.ts\nimport { withAuth } from \"next-auth\u002Fmiddleware\"\nimport { NextResponse } from \"next\u002Fserver\"\n\nexport default withAuth(\n  function middleware(req) {\n    const token = req.nextauth.token\n    if (req.nextUrl.pathname.startsWith(\"\u002Fdashboard\") && !token) {\n      return NextResponse.redirect(new URL(\"\u002Flogin\", req.url))\n    }\n  },\n  { callbacks: { authorized: ({ token }) => !!token } }\n)\n\nexport const config = {\n  matcher: [\"\u002Fdashboard\u002F:path*\", \"\u002Fsettings\u002F:path*\", \"\u002Fbilling\u002F:path*\"],\n}\n```\n\n### Environment Variables Template\n\n```bash\n# .env.example\nNEXT_PUBLIC_APP_URL=http:\u002F\u002Flocalhost:3000\nDATABASE_URL=postgresql:\u002F\u002Fuser:pass@ep-xxx.us-east-1.aws.neon.tech\u002Fneondb?sslmode=require\nNEXTAUTH_SECRET=generate-with-openssl-rand-base64-32\nNEXTAUTH_URL=http:\u002F\u002Flocalhost:3000\nGOOGLE_CLIENT_ID=\nGOOGLE_CLIENT_SECRET=\nSTRIPE_SECRET_KEY=sk_test_...\nSTRIPE_WEBHOOK_SECRET=whsec_...\nNEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...\nSTRIPE_PRO_PRICE_ID=price_...\n```\n\n---\n\n## Scaffold Checklist\n\nThe following phases must be completed in order. **Validate at the end of each phase before proceeding.**\n\n### Phase 1 — Foundation\n- [ ] 1. Next.js initialized with TypeScript and App Router\n- [ ] 2. Tailwind CSS configured with custom theme tokens\n- [ ] 3. shadcn\u002Fui installed and configured\n- [ ] 4. ESLint + Prettier configured\n- [ ] 5. `.env.example` created with all required variables\n\n✅ **Validate:** Run `npm run build` — no TypeScript or lint errors should appear.  \n🔧 **If build fails:** Check `tsconfig.json` paths and that all shadcn\u002Fui peer dependencies are installed.\n\n### Phase 2 — Database\n- [ ] 6. Drizzle ORM installed and configured\n- [ ] 7. Schema written (users, accounts, sessions, verification_tokens)\n- [ ] 8. Initial migration generated and applied\n- [ ] 9. DB client singleton exported from `lib\u002Fdb.ts`\n- [ ] 10. DB connection tested in local environment\n\n✅ **Validate:** Run a simple `db.select().from(users)` in a test script — it should return an empty array without throwing.  \n🔧 **If DB connection fails:** Verify `DATABASE_URL` format includes `?sslmode=require` for NeonDB\u002FSupabase. Check that the migration has been applied with `drizzle-kit push` (dev) or `drizzle-kit migrate` (prod).\n\n### Phase 3 — Authentication\n- [ ] 11. Auth provider installed (NextAuth \u002F Clerk \u002F Supabase)\n- [ ] 12. OAuth provider configured (Google \u002F GitHub)\n- [ ] 13. Auth API route created\n- [ ] 14. Session callback adds user ID and subscription status\n- [ ] 15. Middleware protects dashboard routes\n- [ ] 16. Login and register pages built with error states\n\n✅ **Validate:** Sign in via OAuth, confirm session user has `id` and `subscriptionStatus`. Attempt to access `\u002Fdashboard` without a session — you should be redirected to `\u002Flogin`.  \n🔧 **If sign-out loops occur in production:** Ensure `NEXTAUTH_SECRET` is set and consistent across deployments. Add `declare module \"next-auth\"` to extend session types if TypeScript errors appear.\n\n### Phase 4 — Payments\n- [ ] 17. Stripe client initialized with TypeScript types\n- [ ] 18. Checkout session route created\n- [ ] 19. Customer portal route created\n- [ ] 20. Stripe webhook handler with signature verification\n- [ ] 21. Webhook updates user subscription status in DB idempotently\n\n✅ **Validate:** Complete a Stripe test checkout using a `4242 4242 4242 4242` card. Confirm `stripeSubscriptionId` is written to the DB. Replay the `checkout.session.completed` webhook event and confirm idempotency (no duplicate DB writes).  \n🔧 **If webhook signature fails:** Use `stripe listen --forward-to localhost:3000\u002Fapi\u002Fwebhooks\u002Fstripe` locally — never hardcode the raw webhook secret. Verify `STRIPE_WEBHOOK_SECRET` matches the listener output.\n\n### Phase 5 — UI\n- [ ] 22. Landing page with hero, features, pricing sections\n- [ ] 23. Dashboard layout with sidebar and responsive header\n- [ ] 24. Billing page showing current plan and upgrade options\n- [ ] 25. Settings page with profile update form and success states\n\n✅ **Validate:** Run `npm run build` for a final production build check. Navigate all routes manually and confirm no broken layouts, missing session data, or hydration errors.\n\n---\n\n## Reference Files\n\nFor additional guidance, generate the following companion reference files alongside the scaffold:\n\n- **`CUSTOMIZATION.md`** — Auth providers, database options, ORM alternatives, payment providers, UI themes, and billing models (per-seat, flat-rate, usage-based).\n- **`PITFALLS.md`** — Common failure modes: missing `NEXTAUTH_SECRET`, webhook secret mismatches, Edge runtime conflicts with Drizzle, unextended session types, and migration strategy differences between dev and prod.\n- **`BEST_PRACTICES.md`** — Stripe singleton pattern, server actions for form mutations, idempotent webhook handlers, `Suspense` boundaries for async dashboard data, server-side feature gating via `stripeCurrentPeriodEnd`, and rate limiting on auth routes with Upstash Redis + `@upstash\u002Fratelimit`.\n",{"name":5,"description":7},"devin\u002F1780503568-install-devin-skills","c1c6a7ac191f1fb795ae7f57f2fbfa600d3ef56b",".devin\u002Fskills\u002Fsaas-scaffolder",0,"https:\u002F\u002Fgithub.com\u002Fkruxshnx\u002Fclaude-skills-devin","https:\u002F\u002Fgithub.com\u002Fkruxshnx","https:\u002F\u002Fgovinda-krishna.vercel.app\u002F","2026-06-04 00:21:41",{"source":8,"skillName":5,"commands":33,"prompt":43,"zipAvailable":44},[34,37,40],{"tool":35,"command":36},"npx","npx skills add kruxshnx\u002Fclaude-skills-devin --skill saas-scaffolder",{"tool":38,"command":39},"bunx","bunx skills add kruxshnx\u002Fclaude-skills-devin --skill saas-scaffolder",{"tool":41,"command":42},"pnpm","pnpm dlx skills add kruxshnx\u002Fclaude-skills-devin --skill saas-scaffolder","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fkruxshnx\u002Fclaude-skills-devin\u002Fblob\u002Fdevin\u002F1780503568-install-devin-skills\u002F.devin\u002Fskills\u002Fsaas-scaffolder\u002FSKILL.md",true,[46,52,56,60,64],{"path":47,"resourceType":48,"resourceTypeDesc":49,"sizeBytes":50,"sourceHtmlUrl":51},"references\u002Farchitecture-patterns.md","REFERENCE","参考文档",6727,"https:\u002F\u002Fgithub.com\u002Fkruxshnx\u002Fclaude-skills-devin\u002Fblob\u002Fdevin\u002F1780503568-install-devin-skills\u002F.devin\u002Fskills\u002Fsaas-scaffolder\u002Freferences\u002Farchitecture-patterns.md",{"path":53,"resourceType":48,"resourceTypeDesc":49,"sizeBytes":54,"sourceHtmlUrl":55},"references\u002Fauth-billing-guide.md",7143,"https:\u002F\u002Fgithub.com\u002Fkruxshnx\u002Fclaude-skills-devin\u002Fblob\u002Fdevin\u002F1780503568-install-devin-skills\u002F.devin\u002Fskills\u002Fsaas-scaffolder\u002Freferences\u002Fauth-billing-guide.md",{"path":57,"resourceType":48,"resourceTypeDesc":49,"sizeBytes":58,"sourceHtmlUrl":59},"references\u002Fsaas-architecture-patterns.md",2764,"https:\u002F\u002Fgithub.com\u002Fkruxshnx\u002Fclaude-skills-devin\u002Fblob\u002Fdevin\u002F1780503568-install-devin-skills\u002F.devin\u002Fskills\u002Fsaas-scaffolder\u002Freferences\u002Fsaas-architecture-patterns.md",{"path":61,"resourceType":48,"resourceTypeDesc":49,"sizeBytes":62,"sourceHtmlUrl":63},"references\u002Ftech-stack-comparison.md",9028,"https:\u002F\u002Fgithub.com\u002Fkruxshnx\u002Fclaude-skills-devin\u002Fblob\u002Fdevin\u002F1780503568-install-devin-skills\u002F.devin\u002Fskills\u002Fsaas-scaffolder\u002Freferences\u002Ftech-stack-comparison.md",{"path":65,"resourceType":66,"resourceTypeDesc":67,"sizeBytes":68,"sourceHtmlUrl":69},"scripts\u002Fproject_bootstrapper.py","SCRIPT","脚本",14190,"https:\u002F\u002Fgithub.com\u002Fkruxshnx\u002Fclaude-skills-devin\u002Fblob\u002Fdevin\u002F1780503568-install-devin-skills\u002F.devin\u002Fskills\u002Fsaas-scaffolder\u002Fscripts\u002Fproject_bootstrapper.py",[71,79,86,93,100,107],{"skillNo":72,"name":73,"nameZh":6,"description":74,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":75,"categories":76,"keywords":77,"publishedAt":78},"01M779G0P3W78CJF700K9AHJ6M","syllabus","Generates a curated supplementary reading list from any course syllabus using Consensus academic search. Grill-me intake (syllabus input format + course audience + year range) plus a grouping forcing-options checkpoint before any search runs — so the reading list matches the course's level and recency need. Parses the syllabus to extract topics and learning outcomes, searches Consensus for recent peer-reviewed papers per topic, and produces a professionally formatted .docx with clickable Consensus links, plain-language summaries calibrated to audience level, and Bloom-higher-order discussion questions tied to course learning goals. Triggers whenever a user uploads a syllabus, course outline, or curriculum document and wants supplementary readings. Also triggers on: 'syllabus reading list', 'find papers for my course', 'create a reading list from this syllabus', 'recent research for my class', 'supplementary readings', 'find journal articles for these topics', 'what recent papers cover this material', 'any new research on these course topics', 'update my syllabus with recent papers'. Even casual mentions when a syllabus is attached should trigger this skill.","https:\u002F\u002Fgithub.com\u002Fkruxshnx\u002Fclaude-skills-devin\u002Fblob\u002Fdevin\u002F1780503568-install-devin-skills\u002Fresearch\u002Fsyllabus\u002Fskills\u002Fsyllabus\u002FSKILL.md",[],[],"2026-11-15 14:19:17",{"skillNo":80,"name":81,"nameZh":6,"description":82,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":83,"categories":84,"keywords":85,"publishedAt":78},"01M779FBBF54JKVQAGBAYHWE0B","research","Default entry point for any research request — a hybrid router that classifies the question deterministically and either delegates to a specialist research skill (pulse for trends\u002Fsentiment, grants for NIH funding, litreview for academic literature, syllabus for course reading, patent for prior-art + IP landscape, dossier for entity research) or runs its own plan-decompose-multi-source-search-synthesize-cite fallback workflow when no specialist matches. Always surfaces the routing decision so users can override. Triggers — \"research [topic]\", \"look into [topic]\", \"what do we know about [topic]\", \"investigate [topic]\", \"find me information on [topic]\", \"do some research on [topic]\", \"I need to understand [topic]\", or any research request that doesn't obviously match a more-specific specialist skill. Output is a markdown briefing (default) or .docx document (on request) with full citations and an audit log.","https:\u002F\u002Fgithub.com\u002Fkruxshnx\u002Fclaude-skills-devin\u002Fblob\u002Fdevin\u002F1780503568-install-devin-skills\u002Fresearch\u002Fresearch\u002Fskills\u002Fresearch\u002FSKILL.md",[],[],{"skillNo":87,"name":88,"nameZh":6,"description":89,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":90,"categories":91,"keywords":92,"publishedAt":78},"01M779EQ39CGK5Q4V78KC0HNMV","pulse","Multi-source recency research skill that takes the pulse of any topic across Reddit, Hacker News, the open web, and optionally X\u002FTwitter within a configurable recent window (default 30 days). Forcing intake clarifies topic specificity, angle (trend\u002Fsentiment\u002Fproblems\u002Fopportunities\u002Fcomparison), time window, and platform scope before searching. Returns a synthesized briefing with citations, engagement metrics, and cross-platform pattern analysis. Triggers: 'pulse on [topic]', 'what's happening with [topic]', 'what are people saying about [topic]', 'current conversation about [topic]', 'take the pulse of [topic]', 'trending: [topic]', 'find me info on [topic]', or any variation requesting multi-source recency intelligence on a topic. Also use for competitor research, trend discovery, tool comparisons, and audience sentiment analysis.","https:\u002F\u002Fgithub.com\u002Fkruxshnx\u002Fclaude-skills-devin\u002Fblob\u002Fdevin\u002F1780503568-install-devin-skills\u002Fresearch\u002Fpulse\u002Fskills\u002Fpulse\u002FSKILL.md",[],[],{"skillNo":94,"name":95,"nameZh":6,"description":96,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":97,"categories":98,"keywords":99,"publishedAt":78},"01M779EDS05MYFXBF7PGTVSDMA","patent","Patent prior-art and landscape intelligence skill — not generic patent help. Commits to one of five sub-use-cases via forcing intake (novelty search \u002F freedom-to-operate \u002F competitive landscape \u002F acquisition diligence \u002F litigation prior-art) before any search runs. Searches Google Patents, Espacenet, USPTO, and optionally Lens.org for citation-graph signals. Output is an editable Word document (.docx) with verdict, ranked closest art (claim-text extracted), CPC-class-aware landscape, family-resolved hits, geographic coverage, FTO flags where applicable, strategy recommendations, and full audit log. Triggers: 'prior art search for [invention]', 'patent search on [topic]', 'freedom to operate analysis', 'FTO for [product]', 'patent landscape for [field]', 'is [invention] novel', 'patents on [topic]', 'competitive patent analysis', 'prior art for litigation', 'patent diligence on [company]'. Produces search signal, not legal advice — always recommends consulting a patent attorney before filing or licensing decisions. Trademark, copyright, and trade-secret questions are out of scope.","https:\u002F\u002Fgithub.com\u002Fkruxshnx\u002Fclaude-skills-devin\u002Fblob\u002Fdevin\u002F1780503568-install-devin-skills\u002Fresearch\u002Fpatent\u002Fskills\u002Fpatent\u002FSKILL.md",[],[],{"skillNo":101,"name":102,"nameZh":6,"description":103,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":104,"categories":105,"keywords":106,"publishedAt":78},"01M779DVA52T5F0YC5HSM9BW4Z","notebooklm","Browser automation skill for controlling Google's NotebookLM. Handles reading and querying notebooks, adding sources (URLs, text, files, YouTube links, synthesized content), generating Studio outputs (Audio Overview, infographics, slide decks, study guides, briefing docs, mind maps, timelines, FAQs), and creating new notebooks. Triggers on any phrase involving NotebookLM — 'open NotebookLM', 'check my [name] notebook', 'pull info from NotebookLM', 'ask my notebook about X', 'add [source] to NotebookLM', 'create an infographic in NotebookLM', 'use NotebookLM Studio', 'generate a slide deck from my notebook', or any variation where the goal involves NotebookLM. Requires browser automation environment — fails gracefully when unavailable.","https:\u002F\u002Fgithub.com\u002Fkruxshnx\u002Fclaude-skills-devin\u002Fblob\u002Fdevin\u002F1780503568-install-devin-skills\u002Fresearch\u002Fnotebooklm\u002Fskills\u002Fnotebooklm\u002FSKILL.md",[],[],{"skillNo":108,"name":109,"nameZh":6,"description":110,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":111,"categories":112,"keywords":113,"publishedAt":78},"01M779D3SGW8FM1N8G0HK860WT","litreview","Academic literature orientation skill that searches papers via Consensus, builds a strategic search plan using PICO (default) or SPIDER \u002F Decomposition \u002F hybrid as fallbacks, and synthesizes findings into a professionally formatted Word document (.docx) research guide. Grill-me intake (research question specificity + framework hint + tentative depth) before the recon search; a second forcing checkpoint after Phase 2 confirms framework + sub-areas + depth before searches consume budget. Configurable depth (5\u002F10\u002F20 queries) controls coverage vs. speed. Output is a 'launching pad' — not a finished review, but an orientation guide that lets a researcher dive in confidently. Triggers: 'litreview on [topic]', 'literature review on [topic]', 'I'm starting a literature review on X', 'I'm writing a paper on X', 'help me research X', 'I'm doing research on X', 'can you help me research X'. Do NOT trigger for single one-off paper searches where the user just wants a quick list — that's a plain Consensus search.","https:\u002F\u002Fgithub.com\u002Fkruxshnx\u002Fclaude-skills-devin\u002Fblob\u002Fdevin\u002F1780503568-install-devin-skills\u002Fresearch\u002Flitreview\u002Fskills\u002Flitreview\u002FSKILL.md",[],[]]