Files
Reactive-Resume/AGENTS.md
Amruth Pillai 62f8270b3e Squashed commit of the following:
commit b2b0470a1d9267d042ec0ac66523c6635bf5b199
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Tue May 19 13:13:38 2026 +0200

    chore: update .gitignore to include .vite-hooks and modify pnpm-lock.yaml for dependencies

commit d28fadb5cd8706c874e616102878b4a394ec84c1
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Tue May 19 13:08:04 2026 +0200

    fix: remove timestamp conflict guard

commit c6998d9dbab19d09d3c8054feef1d2e4117555eb
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Tue May 19 12:11:51 2026 +0200

    chore(release): v5.1.5

commit f33d168711804880e1f12e88d24290aae16cc258
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Tue May 19 11:58:35 2026 +0200

    revert: compose.yml

commit d961e6535811a10c335525fb33a08d03e737278d
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Tue May 19 11:58:08 2026 +0200

    refactor(agent): replace 'revert' terminology with 'restore' for clarity, resolves #3086

commit 17f351171be218e33f01c469d95e4164d4c8dc57
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Tue May 19 11:10:41 2026 +0200

    refactor(pdf): simplify sidebar section filtering and update summary feature logic

commit d55179b9d76879e3204de185e8b53fadd0a107ed
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Tue May 19 09:53:37 2026 +0200

    chore: update pnpm-lock.yaml and turbo.json

commit 7cade6980e1a04352536bd44ef773f338c4ef599
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Tue May 19 09:38:30 2026 +0200

    fix(polyfill): add tested polyfill for Map Upsert methods

commit 26d175bb9c53d93225d1e907678445252c13d660
Merge: 1cf33dc6c 5b1297fa2
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Tue May 19 09:23:29 2026 +0200

    Merge remote-tracking branch 'origin/main' into feat/explore-hono-orpc-migration

    # Conflicts:
    #	packages/api/src/services/agent-url.ts
    #	packages/runtime-externals/package.json

commit 1cf33dc6c9d81735730ad656e16dab6501c6d6a1
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Tue May 19 09:22:12 2026 +0200

    chore: preserve branch changes before main sync

commit b380a4b00fdbcdd81ff4f8ef72b330fd027ccda5
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Mon May 18 07:50:28 2026 +0200

    chore: lot of fixes for monorepo migration

commit 8fcf0ec64e1c29572ebaff494338368bfcf75760
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Fri May 15 13:57:17 2026 +0200

    chore: update knip version and refine web app routing with new SEO endpoints

commit 234e68086ff15610a93877354c98e2c020364533
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Fri May 15 12:10:06 2026 +0200

    refactor(auth): update OAuth routes to include API prefix and remove unused schema endpoint

commit 91c84b9a8496b0ce21d71cae9f8b2a027638c9ac
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Fri May 15 11:54:29 2026 +0200

    chore: update dependencies and enhance PWA metadata in web app

commit 150117d4a5a9dd6cd92c64891aad8cae90f6a7af
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Fri May 15 11:12:35 2026 +0200

    docs: revise manifest-only pwa testing scope

commit 6b939a55661aec9dd8122b184e4b60a5c7325fb5
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Fri May 15 11:11:33 2026 +0200

    docs: add manifest-only pwa design

commit 1422e1fc96c400948b273210a1067251087d15d4
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Fri May 15 11:05:04 2026 +0200

    chore(dev): simplify server proxy config

commit bc2ff5a9f6fda41e6c40333c8f163aa23a6c5e48
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Fri May 15 11:04:50 2026 +0200

    docs: add unsafe oauth redirect plan

commit 445359ebe9b96c1515bf1c4c3f73ba8a8448ec12
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Fri May 15 11:04:34 2026 +0200

    feat(auth): add unsafe oauth redirect flag

commit 73fffdd24598e56b2793f7657919bc794835892e
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Fri May 15 10:55:02 2026 +0200

    docs: design unsafe oauth redirect flag

commit c0066aa19c15fc8a4c8e5179ed49889c117519f4
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Fri May 15 10:22:04 2026 +0200

    chore: update translation source paths

commit 9033da082418d252aafd6c2eed72f71f014be3d9
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Fri May 15 10:09:25 2026 +0200

    refactor(arch): react spa + hono migration

commit 6f27936c11bda895977dc63ee550c3346d4ce24b
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Fri May 15 01:10:47 2026 +0200

    docs: add docker nightly tagging design

commit ecc1fd9a88a0ee1dca2f1977dfc17f74527fe1da
Author: Amruth Pillai <im.amruth@gmail.com>
Date:   Thu May 14 20:05:44 2026 +0200

    feat: migrate to hono spa server
2026-05-19 13:14:21 +02:00

12 KiB

AGENTS.md

Cursor Cloud specific instructions

Overview

Reactive Resume is a pnpm monorepo (Turborepo) with two deployable apps: apps/web (TanStack Start / React 19 / Vite) and apps/server (Hono / Node.js). The production Docker image runs a single Node.js process on port 3000, with apps/server mounting the API/auth/MCP/static routes and serving the built web app.

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). Use nvm install 24 && nvm use 24 if needed.
  • Docker is required to run PostgreSQL. Start it with sudo dockerd & if the daemon isn't running.
  • pnpm 11.1.2 is managed via corepack (corepack enable).

Codebase map

  • apps/web owns TanStack Start routes, Vite config, PWA setup, oRPC browser client wiring, web features, and the resume builder UI.
  • apps/server owns the production Hono app, route composition, auth/RPC/MCP/OpenAPI handlers, static uploads, schema JSON, web-dist fallback serving, and startup checks.
  • packages/api contains oRPC routers, DTOs, rate limiting, and feature-owned API modules under packages/api/src/features/*. The router export at @reactive-resume/api/routers aggregates those feature routers for /api/rpc.
  • packages/auth contains Better Auth config, auth helper functions, and exported auth types. The server auth adapter in apps/server/src/http/auth.ts delegates to auth.handler.
  • packages/db contains the Drizzle client and schema. Migration files live at the repo root in migrations/.
  • packages/env defines server environment validation and auto-loads the root .env for app/server code.
  • packages/schema contains Zod schemas and typed resume/page/template models.
  • packages/pdf contains the React PDF document, font registration, shared template primitives, template implementations, and browser/server PDF generation adapters. PDF.js viewer UI stays in apps/web.
  • packages/resume contains pure resume-domain behavior such as JSON Patch helpers and social-network icon mapping.
  • packages/docx contains DOCX export generation.
  • packages/mcp contains MCP tools, prompts, resources, server-card generation, and tool metadata.
  • packages/ui contains shared Base UI/shadcn-style components and hooks.
  • packages/fonts, packages/email, packages/import, packages/ai, packages/utils, and packages/config provide focused support surfaces. Prefer their existing exports over adding cross-package shortcuts.
  • Development-only scripts live in tooling/, not under packages/, so packages only contain code bundled by the app/runtime.

Web app conventions

  • Routes are file-based under apps/web/src/routes. Do not hand-edit apps/web/src/routeTree.gen.ts; it is generated by TanStack Router tooling.
  • Server-owned HTTP behavior lives in apps/server/src/{http,rpc,mcp,openapi,static,startup}. Keep API/RPC/auth/MCP/static route wiring in apps/server, not in web routes.
  • apps/web/src/router.tsx initializes router context with queryClient, orpc, theme, locale, session, and flags. 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 route apps/web/src/routes/$username/$slug.tsx uses ssr: "data-only".
  • Browser-only resume preview code lives under apps/web/src/features/resume/preview, and public resume PDF viewer code lives under apps/web/src/features/resume/public. Keep PDF.js/canvas/browser APIs out of SSR paths and out of packages/pdf.
  • 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/rpc with credentials included.

Package and feature boundaries

  • Workspace dependencies must go through package names and package export maps. Do not import another workspace's src tree through repository paths, @reactive-resume/*/src/*, or TypeScript path aliases.
  • turbo boundaries is the executable package-boundary check. Workspace-level turbo.json files declare coarse tags:
    • app:web for the TanStack Start app.
    • app:server and runtime:server for the Node/Hono process.
    • runtime:server for server-only packages such as API/auth/db/env/email/MCP.
    • runtime:browser for browser-only shared UI.
    • runtime:universal for environment-neutral domain packages.
    • role:domain, role:infra, role:adapter, role:api, role:rendering, and role:tooling for package intent.
  • Browser/server runtime-specific code should live behind explicit export subpaths such as @reactive-resume/pdf/browser, @reactive-resume/pdf/server, or @reactive-resume/env/server. Keep root exports environment-neutral unless the package is intentionally server-only.
  • Wildcard exports are allowed only for leaf libraries whose public surface is intentionally file-like, currently @reactive-resume/ui/components/*, @reactive-resume/ui/hooks/*, and schema resume model files. Prefer explicit exports for packages that own runtime behavior.
  • Add new API procedures and business logic inside the owning packages/api/src/features/* module. Keep route wiring, DTO usage, helpers, and services colocated by feature/capability, then expose only intentional public surfaces through packages/api/package.json. Prefer protectedProcedure from packages/api/src/context.ts for authenticated procedures.
  • Add database columns/tables in packages/db/src/schema/*, then generate root-level migrations with dotenvx run -f .env.local -- pnpm 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 under packages/pdf/src/templates/<name>/, and static previews under apps/web/public/templates/{jpg,pdf}.
  • Resume JSON Patch behavior belongs in @reactive-resume/resume/patch; do not put resume-domain helpers in @reactive-resume/utils.
  • DOCX export behavior belongs in @reactive-resume/docx; do not put DOCX builders in @reactive-resume/utils.
  • 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.ts owns React PDF font registration, standard PDF font handling, CJK fallback stacks, and global hyphenation behavior.
  • PDF generation helpers live behind @reactive-resume/pdf/browser and @reactive-resume/pdf/server; locale-specific section-title resolution stays in the caller.
  • MCP implementation belongs in @reactive-resume/mcp; app packages must not import MCP implementation from another app's source tree.
  • packages/utils has narrowly exported helpers. If another package needs a utility, add an explicit export path instead of importing private files.

Placement decision tree:

  1. If the change is a web route, route loader, or user-facing web workflow, start in apps/web/src/routes or apps/web/src/features.
  2. If the change is a server HTTP route/adapter, startup check, static handler, MCP transport, or OpenAPI/well-known handler, start in apps/server/src.
  3. If it is authenticated API behavior, put the contract and implementation in the owning packages/api/src/features/* module.
  4. If it is pure resume data behavior with no DB, HTTP, DOM, or PDF renderer dependency, put it in packages/resume.
  5. If it renders resume PDFs, put shared React PDF/template code in packages/pdf; put PDF.js viewer/canvas UI in apps/web/src/features/resume.
  6. If it creates DOCX exports, put it in packages/docx.
  7. If it exposes MCP tools/prompts/resources, put it in packages/mcp.
  8. If it is a generic UI primitive or hook, put it in packages/ui; if it is workflow-specific UI, keep it in the owning web feature.
  9. If it is a narrow cross-cutting helper, add an explicit packages/utils export only after checking that no domain package is a better owner.

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. Run migration commands through dotenvx, for example dotenvx run -f .env.local -- pnpm db:migrate, so DATABASE_URL is present in the process environment.

The production server runs migrations during startup before serving traffic. 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 (default http://localhost:3000)
  • DATABASE_URL (default postgresql://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.

When running dev servers or migration commands, prefix the command with dotenvx run -f .env.local --. For example: dotenvx run -f .env.local -- pnpm dev. Tests, typechecks, linters, boundary checks, and pnpm build do not need this prefix by default. If one of those commands fails because a specific environment variable is required, rerun it with the dotenvx run -f .env.local -- prefix.

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 dotenvx run -f .env.local -- pnpm db:generate
Run migrations dotenvx run -f .env.local -- pnpm db:migrate
Dev server dotenvx run -f .env.local -- pnpm dev (starts on port 3000)
Web dev server only dotenvx run -f .env.local -- pnpm dev:web
Lint/format pnpm check (Biome)
Boundary check pnpm exec turbo boundaries
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
pnpm exec turbo boundaries

Vitest test paths are package-relative when running through pnpm --filter <package> test -- <path>.

Gotchas

  • The server startup path auto-runs migrations before serving traffic, so pnpm db:migrate is mainly needed for first-time setup, migration debugging, or applying migrations without starting the app.
  • 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.yml pre-commit hook runs biome check on staged files. Run pnpm check before committing to avoid hook failures.
  • pnpm check is 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, and cn.
  • Most packages use tsgo --noEmit for typechecking and vitest run --passWithNoTests for tests.
  • There may be unrelated local edits in the worktree. Inspect git status --short first and avoid reverting files you did not touch.
  • New env vars require a turbo.json entry. Turborepo 2.x runs in strict env mode by default — it filters out env vars that are not listed in globalEnv (or task-level env/passThroughEnv). Any new environment variable added to packages/env/src/server.ts must also be added to the globalEnv array in turbo.json, or the variable will be undefined inside child processes at runtime even if it is correctly set in the OS/container environment.