This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
pnpm dev # Next.js dev server with Turbopack
pnpm build # Production build with Turbopack
pnpm start # Start production server
pnpm test # Jest unit tests (__tests__/**/*.test.{ts,tsx})
pnpm test:e2e # Playwright E2E tests (__tests__/e2e/**/*.spec.ts)
# Database (Prisma schema is at server/db/schema.prisma — always pass --schema)
pnpm prisma generate --schema=server/db/schema.prisma
pnpm prisma migrate dev --schema=server/db/schema.prisma
pnpm prisma studio --schema=server/db/schema.prisma
pnpm db:seed # Seed with sample data
pnpm db:reset # Reset DB and re-seedThe postinstall script auto-runs prisma generate, so the Prisma client should always be available after pnpm install.
app/ # Next.js App Router — route files only
(landing)/ # Public pages (/, /blogs, /admin/blogs-dashboard)
(dashboard)/app/ # Protected pages (/app/*)
(auth)/ # Sign-in/sign-up pages
api/ # API route handlers
components/
ui/ # Reusable UI primitives (shadcn/ui + custom)
shared/ # Shared app-level components (auth, error handling)
landing/ # Landing page sections
dashboard/ # Dashboard feature components
hooks/ # Shared React hooks
lib/ # Shared client-side utilities and configs (cn, query-client, pricing, stripe-client)
providers/ # React context providers
stores/ # Zustand stores
server/
api/ # Client-side Axios API wrappers (browser)
auth/ # NextAuth configuration
db/ # Prisma schema, client singleton, seed
integrations/ # Third-party services (Stripe, Cloudinary, Appwrite)
services/ # Core business logic (subscription, user, notes)
utils/ # Server-side utilities (rate-limit, api-protection, plan limits)
Route groups. The App Router uses three route groups:
(landing)— public pages (landing home, /blogs, /admin/blogs-dashboard). The/adminroute here is a public blog management dashboard, not the authenticated/apparea.(dashboard)— all routes under/appare protected by middleware and use theNotesLayoutwith sidebar. Includes/app/pricing,/app/subscription, and the main notes dashboard at/app.(auth)— sign-in and sign-up pages, redirected to/appif already authenticated.
Auth flow. server/auth/config.ts configures NextAuth v5 with Google OAuth (JWT strategy, no database sessions). The JWT callback does a dynamic import of getOrCreateUserByEmail — Prisma is kept out of the Edge middleware bundle. On first sign-in it creates/looks up the user and stores the DB user ID as token.sub. The session callback copies isSubscribed from the token.
proxy.ts wraps NextAuth's auth() middleware. It protects /app/*, redirects authenticated users away from /sign-in and /sign-up, and redirects authenticated users from the landing page and /admin to /app.
Database. PostgreSQL with Prisma. The Prisma client is a singleton (server/db/client.ts) marked server-only. Generated client output goes to the default node_modules/.prisma/client location. The User model denormalizes isSubscribed and noteCount for fast checks without joining through Subscription.
Subscription system. Stripe webhooks (app/api/stripe/webhook/route.ts) handle the full lifecycle: checkout.session.completed → create subscription + set isSubscribed=true; customer.subscription.updated / deleted → sync status; invoice.payment_succeeded / failed → renew or downgrade. The Subscription model tracks Stripe IDs, plan type, billing periods, and trial windows. Plan limits are defined in server/utils/subscription-check.ts with four tiers: free (no time limit), basic, pro, enterprise.
AI (LangChain). All AI calls go through LangChain for a unified provider interface. server/services/langchain.ts creates ChatGoogleGenerativeAI (Gemini 2.5 Flash) and ChatOpenAI (GPT-4o-mini) models. getChatModel() returns a model with Gemini as primary and OpenAI as automatic fallback via LangChain's .withFallbacks(). toLangChainMessages() converts internal message format to LangChain's HumanMessage/AIMessage/SystemMessage.
AI suggestions. app/api/ai-suggestion/route.ts provides two modes: improve (spelling/grammar fixes, temp=0.1) and expand (adds detail, temp=0.8). Uses getChatModel(…).invoke() for non-streaming responses. Output is cleaned by extractOutput() to strip intros/sign-offs/code fences. Usage is tracked per-user with monthly resets via aiSuggestionsCount / aiSuggestionsResetAt on the User model. Two layers of rate limiting: in-memory by IP+user ID, and plan-tier quota from the database.
AI chat (SnackAI). app/api/ai/chat/route.ts is a streaming SSE endpoint for in-note chat. server/api/ai-chat.ts builds a system prompt from the current note context and streams via getChatModel(…).stream(). The client hook hooks/use-lang-chat.ts handles SSE parsing, abort, and message state. The AiChatPanel component (components/dashboard/ai-chat-panel.tsx) renders the chat UI with a lightweight markdown renderer (components/dashboard/chat-markdown.tsx). Usage is counted against the same per-plan AI quota as suggestions.
Client-server data flow. Client components use TanStack Query hooks that call /api/ routes. The server/api/ directory contains Axios-based API client functions (getNotes, createNote, etc.) consumed by React Query. API routes use auth() for session checks and the Prisma client directly. Server-side subscription checks go through server/services/subscription.ts which runs Prisma transactions to keep User.isSubscribed in sync.
State. Zustand store (stores/use-app-store.ts) persists theme, user info, and sidebar state to localStorage. TanStack Query (lib/query-client.ts) handles server state with a 1-minute staleTime and no retry on 4xx errors.
Stripe config. server/integrations/stripe/config.ts lazy-initializes the Stripe SDK behind a Proxy to avoid build-time errors when STRIPE_SECRET_KEY is missing.
Next.js config. PostHog analytics is reverse-proxied through /ingest/* rewrites. Security headers are applied globally. Heavy packages (Radix UI, GSAP, Stripe.js, etc.) are marked for optimizePackageImports.