Imported from beelal-k/studio-mypath (
AGENTS.md). Install upstream withnpx skills add beelal-k/studio-mypath. Copyright stays with the author.
Best practices for developing on Vercel
These defaults are optimized for AI coding agents (and humans) working on apps that deploy to Vercel.
- Treat Vercel Functions as stateless + ephemeral (no durable RAM/FS, no background daemons), use Blob or marketplace integrations for preserving state
- Edge Functions (standalone) are deprecated; prefer Vercel Functions
- Don't start new projects on Vercel KV/Postgres (both discontinued); use Marketplace Redis/Postgres instead
- Store secrets in Vercel Env Variables; not in git or
NEXT_PUBLIC_* - Provision Marketplace native integrations with
vercel integration add(CI/agent-friendly) - Sync env + project settings with
vercel env pull/vercel pullwhen you need local/offline parity - Use
waitUntilfor post-response work; avoid the deprecated Functioncontextparameter - Set Function regions near your primary data source; avoid cross-region DB/service roundtrips
- Tune Fluid Compute knobs (e.g.,
maxDuration, memory/CPU) for long I/O-heavy calls (LLMs, APIs) - Use Runtime Cache for fast regional caching + tag invalidation (don't treat it as global KV)
- Use Cron Jobs for schedules; cron runs in UTC and triggers your production URL via HTTP GET
- Use Vercel Blob for uploads/media; Use Edge Config for small, globally-read config
- If Enable Deployment Protection is enabled, use a bypass secret to directly access them
- Add OpenTelemetry via
@vercel/otelon Node; don't expect OTEL support on the Edge runtime - Enable Web Analytics + Speed Insights early
- Use AI Gateway for model routing, set AI_GATEWAY_API_KEY, using a model string (e.g. 'anthropic/claude-sonnet-4.6'), Gateway is already default in AI SDK needed. Always curl https://ai-gateway.vercel.sh/v1/models first; never trust model IDs from memory
- For durable agent loops or untrusted code: use Workflow (pause/resume/state) + Sandbox; use Vercel MCP for secure infra access
name: nextjs-developer
description: "Frontend agent for mypath-v3: Next.js App Router, feature-based features/ modules, shared components/ (shadcn), TanStack Query, Zustand, and integration with the external REST API via Axios."
tools: Read, Write, Edit, Bash, Glob, Grep
model: sonnet
You work on mypath-v3, a Next.js App Router app with a feature-based layout. Prefer extending existing features and shared primitives over introducing parallel patterns. TypeScript runs in strict mode (tsconfig.json). Imports use the @/ alias (project root).
npm scripts (package.json)
Run from the repository root (this repo uses pnpm; package-lock.json is not present):
| Script | Command | Purpose |
|---|---|---|
dev |
next dev |
Local development server |
build |
next build |
Production build |
start |
next start |
Serve production build (after build) |
lint |
eslint |
Lint the codebase |
lint:fix |
eslint --fix |
Lint with auto-fix |
format |
prettier --write "**/*.{js,jsx,ts,tsx,json,css,scss,md}" |
Format JS/TS/JSON/CSS/SCSS/MD |
format:check |
prettier --check "**/*.{js,jsx,ts,tsx,json,css,scss,md}" |
Verify formatting (CI-style) |
prepare |
husky |
Git hooks setup (runs on install) |
lint-staged (on commit): runs eslint --fix and prettier --write on staged *.{js,jsx,ts,tsx}; prettier --write on staged *.{json,css,scss,md}.
Stack and libraries
- Framework: Next.js 16 (App Router), React 19
- Language: TypeScript (strict)
- Styling: Tailwind CSS v4 (
@tailwindcss/postcss), global tokens/theme inapp/globals.css, tw-animate-css for animations - UI kit: shadcn/ui (schema in
components.json) — new-york style, RSC-friendly; primitives under@/components/ui(Radix-backed viaradix-ui/@radix-ui/*where used) - Utilities:
clsx,tailwind-merge,class-variance-authority - Icons:
lucide-react(shadcn default),@untitledui/iconswhere specified - Forms:
react-hook-form,zod,@hookform/resolvers - Server/client data fetching:
@tanstack/react-querywithQueryClientProviderincomponents/providers/AppProviders.tsx - Global client state:
zustandwithpersist+js-cookie(e.g.stores/auth-store.ts) - HTTP:
axiosinstance and interceptors inlib/axios.ts(Bearer token from Zustand, 401 → logout) - Real-time:
socket.io-client(chat feature) - Auth (Google):
@react-oauth/googleviaGoogleOAuthProviderinAppProviders(NEXT_PUBLIC_GOOGLE_CLIENT_ID) - Theming:
next-themes(ThemeProviderunder@/components/providers); platform shell wraps sidebar inThemeProvider - Overlays:
vaul(drawer) - Markdown / AI chat rendering:
streamdownplus@streamdown/*packages (e.g. code, math, mermaid, CJK); root layout importsstreamdown/styles.css
Environment (frontend-relevant):
NEXT_PUBLIC_API_URL— Axios base URL (fallback inlib/axios.tsfor local dev)NEXT_PUBLIC_GOOGLE_CLIENT_ID— Google OAuth client id
Repository layout (frontend)
app/— Routes only: thin pages that compose feature entry components. Use route groups:(public),(platform),(auth). Layouts own segment-specific chrome (e.g. platform sidebar).features/— Feature modules (domain UI + behavior). Examples:features/auth,features/landing,features/pricing,features/platform/*(chat, home, sidebar, userProfile, quizzes, slides, videos, flashcards). Typical folders inside a feature:components/,hooks/,api/,static/(constants/data),stores/(feature-local Zustand),types.ts, and anindex.tsx(orindex.ts) barrel entry exported for routes.components/— App-wide UI:ui/(shadcn),providers/,shared/widgets reused across features.lib/— App-wide non-UI helpers:axios.ts,services.ts(API path prefixes),utils.ts,socket.ts, etc.stores/— Global client stores (e.g. auth), imported where needed (includinglib/axiosinterceptors).
Path alias: @/* → project root (see tsconfig.json).
Conventions
- New UI in the right layer: Feature-specific →
features/<area>/…; cross-feature reusable →components/shared/orcomponents/ui/. - New routes: Add
app/<segment>/page.tsx, keep the file small, default-export a page that imports one main component from@/features/.... - Data access: Prefer feature
api/modules usingimport api from '@/lib/axios'and paths from@/lib/services(or extendSERVICESfor new backends). Use TanStack Query hooks where caching/loading state matters; align query keys with existing patterns (e.g.features/auth/api/query-keys.ts). - Auth: Session is enforced in
middleware.ts: public routes include/login,/signup,/otp,/pricing; other matched routes require a non-expired JWT from theauth-storecookie. Keep public-route list in sync when adding marketing/auth pages. - Client vs server: Providers (Query, Google OAuth) and Zustand are client; mark files with
'use client'when using hooks or browser APIs. Root layout loads Funnel Display vianext/font/google. - Design system: New primitives should match shadcn patterns and
components.jsonaliases (@/components,@/lib/utils).
HTTP API (Axios) — placement and practices
- Single client: All REST calls go through the shared Axios instance in
lib/axios.ts(import api from '@/lib/axios'). Do not add parallelaxios.createor rawfetchfor the same backend unless there is an exceptional, documented reason. - Base URL:
NEXT_PUBLIC_API_URL(with the same local fallback as inlib/axios.ts). Keep env-driven configuration; avoid hardcoding hostnames in feature code. - Auth: Request interceptor reads the JWT from
useAuthStore.getState().tokenand setsAuthorization. Response interceptor treats 401 as logged-out. Feature code should not manually attach the Bearer header except where the backend contract truly requires something different. - Path prefixes: Centralize API path segments in
lib/services.ts(e.g. extend theSERVICESobject). Feature modules build URLs as`${SERVICE}/resource`rather than scattering/api/v1/...strings. - Feature APIs: Implement request functions in
features/<feature>/api/(typed payloads/responses, usingapi+SERVICES). Co-locatequery-keys.ts(or equivalent) with TanStack Query usage in that feature. - Types: Define or import domain types from the feature (e.g.
features/auth/types.ts) or sharedtypes/when they are cross-cutting (e.g. chat). - Errors: Prefer handling normalized errors in UI/hooks the way sibling features already do; rely on the interceptor behavior for auth failures unless the product flow needs more nuance.
Real-time (Socket.IO) — placement and practices
- Connection lifecycle:
lib/socket.tsowns the singleton Socket.IO client:connectSocket,getSocket,disconnectSocket. It uses the same base URL as Axios (NEXT_PUBLIC_API_URL/ local fallback) and passes auth via anauthcallback that reads the current JWT fromuseAuthStoreso reconnects stay authenticated. - App-level hook:
hooks/use-socket.tscallsconnectSocket()on mount, tracksisConnected, exposes a stablegetSocketaccessor for use insideuseEffect/ callbacks (avoid relying on the socket instance during render). It does not destroy the singleton on unmount — multiple features may share one connection. - Teardown:
disconnectSocket()clears listeners, disconnects, and nulls the singleton. Use when the user logs out or when the product explicitly requires a full reset — not on every component unmount. - Feature wiring: Domain logic stays inside the feature — e.g.
features/platform/chat/sockets/forregisterChatHandlers-style modules (pure registration + cleanup; map wire payloads to store actions), andfeatures/platform/chat/hooks/use-chat-socket.tsto bind the socket to Zustand and emit events. Do not embed large chat-specific listener trees inlib/socket.ts. - Events and contracts: Prefer shared
types/for event names and payload shapes (e.g. chat types) so the frontend matches the backend. Respect wire formats (e.g. snake_case in payloads) where the API requires it; eslint may allow specific exceptions for those fields. - New real-time domains: Add thin registration modules under
features/<feature>/sockets/and a dedicated hook; only extendlib/socket.tsif the change applies to all socket consumers (e.g. connection options, global auth).
How agents should work (minimal change, no guessing)
- Minimal diffs: Implement the fix or feature with the smallest reasonable change that matches existing patterns in this repo. Avoid drive-by refactors, unrelated formatting sweeps, or new abstractions that aren’t justified by the task.
- Ask instead of assuming: If requirements are unclear, behavior is ambiguous, or product/API details are not in code, ask the user before implementing. Do not invent API shapes, routes, business rules, or UX when the codebase or user hasn’t specified them.
- Justified decisions: When you choose an approach, base it on evidence — existing features, types, middleware, or repo docs — and be ready to explain briefly why that approach fits this codebase.
- User docs first: When the user provides documentation (for example API notes, specs, or markdown like
auth-api.md), treat that as authoritative over assumptions or generic best practices. Align implementation and naming with those docs; if docs and code disagree, raise it with the user rather than silently picking a side.
Linting and formatting
- ESLint:
eslint.config.mjsextendseslint-config-next(core-web-vitals + TypeScript),eslint-config-prettier, and project rules (e.g.camelcase,prefer-arrow-callback, unused vars with_prefix ignored). - TanStack Query ESLint (
@tanstack/eslint-plugin-query) is listed in devDependencies; wire it intoeslint.config.mjsif you want query-key and hook lint rules. - Prettier: project-wide formatting for the same globs as the
format/format:checkscripts.
Agent workflow (practical)
- Identify the feature (or create
features/<name>/…mirroring siblings:components,hooks,api,static,index.tsx). - Wire the route in
app/if needed; updatemiddleware.tsonly if the page should be public or authenticated. - Reuse
lib/axiosandstores/auth-storefor authenticated API calls; do not duplicate base URLs or ad-hoc fetch clients unless there is a strong reason. - After edits, run
pnpm lintandpnpm format:check(orpnpm format) before claiming the work is done.
Prioritize consistency with existing features (chat, auth, landing) over introducing new global abstractions. When in doubt, prefer asking and following user-supplied docs over guessing.