Imported from pavle-doby/course-hub (
AGENTS.md). Install upstream withnpx skills add pavle-doby/course-hub. Copyright stays with the author.
Course Hub — Agent Instructions
Architecture map: ARCHITECTURE.md
Stack at a Glance
- Web: Next.js 16 (App Router), React 19, Tailwind 4, shadcn/Radix —
apps/web/ - Mobile: Expo 55, React Native 0.83, NativeWind, Expo Router —
apps/native/ - API: Express 5, Node ≥20, OpenAPI 3.0 —
apps/api/ - DB: PostgreSQL (Supabase), Drizzle ORM 0.44 —
packages/db-schema/→packages/db/ - Shared types:
@repo/contract(Zod + drizzle-zod) —packages/contract/ - API client: Orval-generated React Query + Axios —
packages/api-client/src/generated/(never edit manually)
Hard Rules
- Never edit
packages/api-client/src/generated/— runpnpm api-client:generateafter any route or schema change. - Schema source of truth is
@repo/db-schema— update Drizzle tables first, then derive Zod schemas in@repo/contract. - Cross-package imports use
@repo/<name>— never use relative paths between packages. - All protected API routes must apply
handleAuthmiddleware (apps/api/src/middleware/auth.ts). - Validated request data is in
res.locals, notreq.body— thevalidate()middleware writes tores.locals.body/.query/.params. - Prefer barrel imports and exports — import from the nearest
index.tsbarrel, not from deep file paths. Every folder with public exports must have anindex.tsthat re-exports them. Never reach past a barrel (e.g.import { x } from "@repo/shared/utils"notimport { x } from "@repo/shared/utils/zod/getZodLocale"). - Always use
{}braces onifstatements, even single-line bodies — neverif (x) return;. - Agents must never run database commands — do not run
pnpm db:*,drizzle-kit,psql, Supabase SQL, migration, push, reset, seed, or destructive database commands. Agents may update schema source and migration files only when explicitly requested; the user runs all database commands. - New OpenCode configuration must use
opencode.jsonc, notopencode.json. - When working in
apps/web/, use the shadcn MCP for additional component or design-system context when needed.
Common Commands
pnpm dev # All apps
pnpm web / pnpm api # Individual apps
pnpm api-client:generate # Regenerate OpenAPI + React Query hooks
pnpm db:generate # New Drizzle migration
pnpm db:push # Apply schema to DB (dev)
pnpm build && pnpm lint && pnpm typecheck
Non-Obvious Conventions
Auth endpoints are platform-split; everything else is shared.
POST /auth/login sets an HTTP-only cookie (web); POST /auth/login/native returns tokens in JSON (mobile). All non-auth endpoints serve both platforms from a single route.
OpenAPI is registered in code, not YAML.
Add registry.registerPath() calls in apps/api/src/modules/<feature>/openapi/<feature>Openapi.ts, then import it side-effect style in apps/api/src/openapi/spec.ts.
Error handling uses typed classes.
Throw BadRequestError, UnauthorizedError, NotFoundError, etc. from @repo/contract. For feature-specific codes, define an ErrorCodeXxx enum in packages/contract/src/<feature>/errors.ts.
Scoped Instructions
Additional AGENTS.md files apply to their directory and all descendants. Consult the closest applicable file when working in apps/ or packages/.
CodeGraph
In repositories indexed by CodeGraph (a .codegraph/ directory exists at the repo root), reach for it BEFORE grep/find or reading files when you need to understand or locate code:
- MCP tool (when available):
codegraph_exploreanswers most code questions in one call — the relevant symbols' verbatim source plus the call paths between them, including dynamic-dispatch hops grep can't follow. Name a file or symbol in the query to read its current line-numbered source. If it's listed but deferred, load it by name via tool search. - Shell (always works):
codegraph explore "<symbol names or question>"prints the same output.
If there is no .codegraph/ directory, skip CodeGraph entirely — indexing is the user's decision.