diff --git a/.cursorrules b/.cursorrules index 8b14d41ec4..11c498716e 100644 --- a/.cursorrules +++ b/.cursorrules @@ -1,6 +1,6 @@ # Project Rules -Read CLAUDE.md at the repo root and apps/api/CLAUDE.md for comprehensive project rules. +Read AGENTS.md at the repo root and apps/api/CLAUDE.md for comprehensive project rules. ## Quick Reference diff --git a/AGENTS.md b/AGENTS.md index be4d3e28bb..e20273fdaf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -130,6 +130,7 @@ Every customer-facing API endpoint MUST have: - **DS components that do NOT accept `className`**: `Text`, `Stack`, `HStack`, `Badge`, `Button` — wrap in `
` for custom styling - **Layout**: Use `PageLayout`, `PageHeader`, `Stack`, `HStack`, `Section`, `SettingGroup` - **Patterns**: Sheet (`Sheet > SheetContent > SheetHeader + SheetBody`), Drawer, Collapsible +- **Responsive (MANDATORY)**: every UI change must work on mobile (375px), tablet (768px), desktop (1280px), and large desktop (1920px) by default — no one has to ask. See the `responsive-ui` skill for breakpoints, repo patterns, and the checklist. - **After editing any frontend component**: Run the `audit-design-system` skill to catch `@trycompai/ui` or `lucide-react` imports that should be migrated ## Data Fetching diff --git a/CLAUDE.md b/CLAUDE.md index 454a1e2b9e..978f6d8c87 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,155 +1,2 @@ -# Project Rules - -## Tooling - -- **Package manager**: `bun` (never npm/yarn/pnpm) -- **Build**: `bun run build` (uses turbo). Filter: `bun run --filter '@trycompai/app' build` -- **Typecheck**: `bun run typecheck` or `npx turbo run typecheck --filter=@trycompai/api` -- **Tests (app)**: `cd apps/app && npx vitest run` -- **Tests (api)**: `cd apps/api && npx jest src/ --passWithNoTests` -- **Lint**: `bun run lint` - -## Code Style - -- **Max 300 lines per file.** Split into focused modules if exceeded. -- **No `as any` casts.** Ever. Use proper types, generics, or `unknown` with type guards. -- **No `@ts-ignore` or `@ts-expect-error`.** Fix the type instead. -- **Strict TypeScript**: Use zod for runtime validation, generics over `any`. -- **Early returns** to avoid nested conditionals. -- **Named parameters** for functions with 2+ arguments. -- **Event handlers**: prefix with `handle` (e.g., `handleSubmit`). - -## Monorepo Structure - -``` -apps/ - api/ # NestJS API (auth, RBAC, business logic) - app/ # Next.js frontend (compliance + security products) - portal/ # Employee portal -packages/ - auth/ # RBAC definitions (permissions.ts) — single source of truth - db/ # Prisma schema + client - ui/ # Legacy component library (being phased out) -``` - -## Authentication & Session - -- **Auth lives in `apps/api` (NestJS).** The API is the single source of truth for authentication via better-auth. All apps and packages that need to authenticate (app, portal, device-agent, etc.) MUST go through the API — never run a local better-auth instance or handle auth directly in a frontend app. -- **Session-based auth only.** No JWT tokens. Cross-subdomain cookies (`.trycomp.ai`) allow sessions to work across all apps. -- **HybridAuthGuard** supports 3 methods in order: API Key (`x-api-key`), Service Token (`x-service-token`), Session (cookies). `@Public()` skips auth. -- **Client-side auth**: `authClient` (better-auth client) with `baseURL` pointing to the API, NOT the current app. -- **Client-side data**: `apiClient` from `@/lib/api-client` (always sends cookies). -- **Server-side data**: `serverApi` from `@/lib/api-server.ts`. -- **Server-side session checks**: Proxy to the API's `/api/auth/get-session` endpoint — do NOT instantiate better-auth locally. -- **Raw `fetch()` to API**: MUST include `credentials: 'include'`, otherwise 401. - -## API Architecture - -We are migrating away from Next.js server actions toward calling the NestJS API directly. - -### Simple CRUD operations -Client components call the NestJS API via custom SWR hooks. No server action wrapper needed. - -### Multi-step orchestration -When an operation requires multiple API calls (e.g., S3 upload + PATCH), create a Next.js API route (`apps/app/src/app/api/...`) that orchestrates them. - -### What NOT to do -- Do NOT use server actions for new features -- Do NOT keep server actions as wrappers around API calls -- Do NOT add direct database (`@db`) access in the Next.js app for mutations — always go through the API -- Do NOT use `useAction` from `next-safe-action` for new code - -### API Client -- Server-side (Next.js API routes/pages): `serverApi` from `apps/app/src/lib/api-server.ts` -- Client-side (hooks): `apiClient` / `api` from `@/lib/api-client` - -### API Response Format -- **List endpoints**: `{ data: [...], count, authType, authenticatedUser }` → access via `response.data.data` -- **Single resource endpoints**: `{ ...entity, authType, authenticatedUser }` → access via `response.data` -- Both `apiClient` and `serverApi` wrap in `{ data, error, status }` - -## RBAC - -### Permissions Model -- Flat `resource:action` model (e.g., `pentest:read`, `control:update`) -- Single source of truth: `packages/auth/src/permissions.ts` -- Built-in roles: `owner`, `admin`, `auditor`, `employee`, `contractor` -- Custom roles: stored in `organization_role` table per organization -- Multiple roles per user (comma-separated in `member.role`) - -### Multi-Product Architecture -- **Products** (compliance, pen testing) are org-level subscription/feature flags — NOT RBAC -- **RBAC** controls user access within products -- `app:read` gates the compliance dashboard; `pentest:read` gates security product -- Portal-only resources (`policy`, `compliance`) do NOT grant app access - -### API Endpoint Requirements -Every customer-facing API endpoint MUST have: -```typescript -@UseGuards(HybridAuthGuard, PermissionGuard) // at controller or endpoint level -@RequirePermission('resource', 'action') // on every endpoint -``` -- Controller format: `@Controller({ path: 'name', version: '1' })`, NOT `@Controller('v1/name')` -- `@Public()` for unauthenticated endpoints (webhooks, etc.) -- The `AuditLogInterceptor` only logs when `@RequirePermission` metadata is present - -### Frontend Permission Gating -- **Nav items**: Gate with `canAccessRoute(permissions, 'routeSegment')` -- **Rail icons**: Gate product sections (Compliance, Security, Trust, Settings) by permission -- **Mutation buttons**: Gate with `hasPermission(permissions, 'resource', 'action')` -- **Page-level**: Every product layout uses `requireRoutePermission('segment', orgId)` server-side -- **Route permissions**: Defined in `ROUTE_PERMISSIONS` in `apps/app/src/lib/permissions.ts` -- No manual role string parsing (`role.includes('admin')`) — always use permission checks - -### Permission Resources -`organization`, `member`, `control`, `evidence`, `policy`, `risk`, `vendor`, `task`, `framework`, `audit`, `finding`, `questionnaire`, `integration`, `apiKey`, `trust`, `pentest`, `app`, `compliance` - -## Design System - -- **Always prefer `@trycompai/design-system`** over `@trycompai/ui`. Check DS exports first. -- `@trycompai/ui` is the legacy library being phased out — only use as last resort. -- **Icons**: `@trycompai/design-system/icons` (Carbon icons), NOT `lucide-react` -- **DS components that do NOT accept `className`**: `Text`, `Stack`, `HStack`, `Badge`, `Button` — wrap in `
` for custom styling -- **Layout**: Use `PageLayout`, `PageHeader`, `Stack`, `HStack`, `Section`, `SettingGroup` -- **Patterns**: Sheet (`Sheet > SheetContent > SheetHeader + SheetBody`), Drawer, Collapsible -- **Responsive (MANDATORY)**: every UI change must work on mobile (375px), tablet (768px), desktop (1280px), and large desktop (1920px) by default — no one has to ask. See the `responsive-ui` skill for breakpoints, repo patterns, and the checklist. -- **After editing any frontend component**: Run the `audit-design-system` skill to catch `@trycompai/ui` or `lucide-react` imports that should be migrated - -## Data Fetching - -- **Server components**: Fetch with `serverApi`, pass as `fallbackData` to client -- **Client components**: `useSWR` with `apiClient` or custom hooks (e.g., `usePolicy`, `useTask`) -- **SWR hooks**: Use `fallbackData` for SSR initial data, `revalidateOnMount: !initialData` -- **`mutate()` safety**: Guard against `undefined` in optimistic update functions -- **`Array.isArray()` checks**: When consuming SWR data that could be stale - -## Testing - -- **Every new feature MUST include tests.** No exceptions. -- **TDD preferred**: Write failing tests first, then make them pass. -- **App tests**: Vitest + @testing-library/react (jsdom environment) -- **API tests**: Jest with NestJS testing utilities -- **Permission tests**: Test admin (write) and read-only user scenarios -- **Run from package dir**: `cd apps/app && npx vitest run` or `cd apps/api && npx jest` - -## Database - -- **Schema**: `packages/db/prisma/schema/` (split into files per model) -- **IDs**: Always use prefixed CUIDs: `@default(dbgenerated("generate_prefixed_cuid('prefix'::text)"))` -- **Migrations**: `cd packages/db && bunx prisma migrate dev --name your_name` -- **Multi-tenancy**: Always scope queries by `organizationId` -- **Transactions**: Use for operations modifying multiple records - -## Git - -- **Conventional commits**: `(): ` (imperative, lowercase) -- **Never use `git stash`** unless explicitly asked -- **Never skip hooks** (`--no-verify`) -- **Never force push** to main/master - -## Forms - -- All forms use **React Hook Form + Zod** validation -- Define Zod schema first, infer type with `z.infer` -- Use `Controller` for complex components (Select, Combobox) -- Never use `useState` for form field values +@AGENTS.md + diff --git a/apps/api/CLAUDE.md b/apps/api/CLAUDE.md index f840a709b5..c37323a0b7 100644 --- a/apps/api/CLAUDE.md +++ b/apps/api/CLAUDE.md @@ -67,16 +67,16 @@ Every customer-facing endpoint MUST have: ```bash # Run tests for a specific module (from apps/api directory) -npx jest src/ --passWithNoTests +bunx jest src/ --passWithNoTests # Run tests for changed files only -npx jest --onlyChanged +bunx jest --onlyChanged # Run all API tests (from repo root) -npx turbo run test --filter=@trycompai/api +bunx turbo run test --filter=@trycompai/api # Type-check before committing -npx turbo run typecheck --filter=@trycompai/api +bunx turbo run typecheck --filter=@trycompai/api ``` ### Test Patterns @@ -142,22 +142,22 @@ const module = await Test.createTestingModule({ 1. **Before coding**: Read existing code patterns in the module 2. **During coding**: Follow established patterns, add types 3. **After coding**: - - Run `npx turbo run typecheck --filter=@trycompai/api` - - Write and run tests: `npx jest src/` + - Run `bunx turbo run typecheck --filter=@trycompai/api` + - Write and run tests: `bunx jest src/` - Commit with conventional commit message ## Common Commands ```bash # Start API in development -npx turbo run dev --filter=@trycompai/api +bunx turbo run dev --filter=@trycompai/api # Type-check -npx turbo run typecheck --filter=@trycompai/api +bunx turbo run typecheck --filter=@trycompai/api # Run specific test file -npx jest src/roles/roles.service.spec.ts +bunx jest src/roles/roles.service.spec.ts # Generate Prisma client after schema changes -cd packages/db && npx prisma generate +cd packages/db && bunx prisma generate ```