7.8 KiB
AGENTS.md
Cursor Cloud specific instructions
Overview
Reactive Resume is a pnpm monorepo (Turborepo) with a single full-stack web app at apps/web (TanStack Start / React 19 / Vite) and ~15 internal packages under packages/. It runs as a single Node.js process on port 3000.
Internal packages are source-consumed through package.json export maps that point at src files. Do not assume package-local dist output exists unless a package explicitly adds it.
Prerequisites
- Node.js 24 (matches Dockerfile
ARG NODE_VERSION=24). Usenvm install 24 && nvm use 24if needed. - Docker is required to run PostgreSQL. Start it with
sudo dockerd &if the daemon isn't running. - pnpm 11.0.9 is managed via corepack (
corepack enable).
Codebase map
apps/webis the only app. It owns TanStack Start routes, Vite/Nitro config, PWA setup, oRPC client wiring, route-level server handlers, and the resume builder UI.packages/apicontains oRPC routers, services, DTOs, storage, resume access policy, statistics, AI services, and rate limiting. The web routeapps/web/src/routes/api/rpc.$.tsexposes these routers at/api/rpc.packages/authcontains Better Auth config, auth helper functions, and exported auth types. The web routeapps/web/src/routes/api/auth.$.tsdelegates toauth.handler.packages/dbcontains the Drizzle client and schema. Migration files live at the repo root inmigrations/.packages/envdefines server environment validation and auto-loads the root.envfor app/server code.packages/schemacontains Zod schemas and typed resume/page/template models.packages/pdfcontains the React PDF document, font registration, shared template primitives, and template implementations.packages/uicontains shared Base UI/shadcn-style components and hooks.packages/fonts,packages/email,packages/import,packages/ai,packages/utils,packages/scripts,packages/config, andpackages/runtime-externalsprovide focused support surfaces. Prefer their existing exports over adding cross-package shortcuts.
Web app conventions
- Routes are file-based under
apps/web/src/routes. Do not hand-editapps/web/src/routeTree.gen.ts; it is generated by TanStack Router tooling. - Server-only route handlers use route
server.handlersblocks, for exampleapi/rpc.$.ts,api/auth.$.ts,api/health.ts, uploads, schema, OpenAPI, MCP, and.well-knownroutes. apps/web/src/router.tsxinitializes router context withqueryClient,orpc,theme,locale,session, andflags. Reuse route context where possible instead of refetching these concerns ad hoc.- The builder shell lives under
apps/web/src/routes/builder/$resumeId. The nested preview route is client-only (ssr: false), while the public resume routeapps/web/src/routes/$username/$slug.tsxusesssr: "data-only". - Browser-only resume preview code is split across
apps/web/src/components/resume/preview.tsx,preview.browser.tsx,pdf-canvas.tsx, and shared helpers. Keep PDF.js/canvas/browser APIs out of SSR paths. - The isomorphic oRPC client is in
apps/web/src/libs/orpc/client.ts; server calls use an in-process router client and browser calls use/api/rpcwith credentials included.
Package and feature boundaries
- Add new API procedures in
packages/api/src/routers/*and keep business logic inpackages/api/src/services/*or helpers. PreferprotectedProcedurefrompackages/api/src/context.tsfor authenticated procedures. - Add database columns/tables in
packages/db/src/schema/*, then generate root-level migrations withpnpm db:generate. - Add or change resume data shape in
packages/schema/src/resume/*first, then update API DTOs, importers, PDF rendering, and web forms that consume that shape. - Add or rename templates in all relevant places:
packages/schema/src/templates.ts,packages/pdf/src/templates/index.ts, template source underpackages/pdf/src/templates/<name>/, and static previews underapps/web/public/templates/{jpg,pdf}. - Shared PDF section filtering lives in
packages/pdf/src/templates/shared/filtering.ts. Keep template-specific visual exceptions in the owning template directory unless multiple templates need the same behavior. packages/pdf/src/hooks/use-register-fonts.tsowns React PDF font registration, standard PDF font handling, CJK fallback stacks, and global hyphenation behavior.packages/utilshas narrowly exported helpers. If another package needs a utility, add an explicit export path instead of importing private files.
Database
PostgreSQL runs via Docker Compose:
sudo docker compose -f compose.dev.yml up -d postgres
The dev default connection string is postgresql://postgres:postgres@localhost:5432/postgres.
Important: drizzle-kit (used by pnpm db:migrate) reads DATABASE_URL from process.env directly — it does not auto-load the .env file. You must export DATABASE_URL=... before running migration commands, or set it in your shell profile.
The web app also runs migrations during Nitro startup via apps/web/plugins/1.migrate.ts. Manual pnpm db:migrate is mainly for first setup, migration debugging, or applying migrations without starting the app.
Environment
Copy .env.example to .env. The three required variables are:
APP_URL(defaulthttp://localhost:3000)DATABASE_URL(defaultpostgresql://postgres:postgres@localhost:5432/postgres)AUTH_SECRET(any non-empty string)
S3/SeaweedFS is optional. If S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, and S3_BUCKET are all set, the app uses S3-compatible storage. The checked-in .env.example sets SeaweedFS defaults, so either start the seaweedfs compose service too or comment out those S3 vars to use local filesystem storage under <workspace>/data. LOCAL_STORAGE_PATH must be absolute when set.
Common commands
| Task | Command |
|---|---|
| Install deps | pnpm install |
| Start Postgres only | sudo docker compose -f compose.dev.yml up -d postgres |
| Start Postgres + SeaweedFS | sudo docker compose -f compose.dev.yml up -d postgres seaweedfs seaweedfs_create_bucket |
| Generate migrations | DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" pnpm db:generate |
| Run migrations | DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" pnpm db:migrate |
| Dev server | pnpm dev (starts on port 3000) |
| Web dev server only | pnpm dev:web |
| Lint/format | pnpm check (Biome) |
| Tests | pnpm test (Vitest) |
| Build | pnpm build |
| Typecheck | pnpm typecheck |
For focused validation, prefer package filters before repo-wide commands, for example:
pnpm --filter web typecheck
pnpm --filter @reactive-resume/pdf test
pnpm --filter @reactive-resume/api test
Vitest test paths are package-relative when running through pnpm --filter <package> test -- <path>.
Gotchas
- The dev server (
pnpm dev) auto-runs migrations on startup via Nitro, sopnpm db:migrateis only strictly needed for first-time setup or after pulling new migration files. - Email sending requires SMTP config; without it, emails are logged to console. This is fine for dev — the app still functions, but email verification links appear in server logs.
- The
lefthook.ymlpre-commit hook runsbiome checkon staged files. Runpnpm checkbefore committing to avoid hook failures. pnpm checkis write-capable (biome check --write --unsafe .). Call that out when using it, and use narrower Biome commands if you need a non-mutating inspection.- Biome uses tabs, double quotes, line width 120, organized import groups, and sorted Tailwind classes for
clsx,cva, andcn. - Most packages use
tsgo --noEmitfor typechecking andvitest run --passWithNoTestsfor tests. - There may be unrelated local edits in the worktree. Inspect
git status --shortfirst and avoid reverting files you did not touch.