This file provides guidance to Claude Code (claude.ai/code) and other AI agents when working with code in this repository.
- Build all:
pnpm build(uses Turbo for optimized builds) - Type checking:
pnpm typecheck(runs type checking across all workspaces via Turbo) orpnpm w:app typecheck(for main app only) - Format checking:
pnpm format:check(verifies code formatting - required by CI) - Never run database migrations
- NEVER run
pnpm format(auto-fix) - only usepnpm format:checkto verify - Almost always prefer suspense queries over a query with useEffect
- When using Suspense Queries, be careful to have proper error boundaries as well
This is a Turborepo monorepo using pnpm workspaces with a clear separation of concerns:
apps/app: Main Next.js 15 frontend (App Router, React 19, Tailwind, Zustand)apps/api: tRPC API server with OpenAPI documentation
@op/ui: React Aria Components library with Tailwind variants and Storybook@op/core: Core utilities, configuration, and environment handling@op/hooks: Reusable React hooks (auth, data fetching)@op/common: Shared business logic and service layer
@op/db: Drizzle ORM schema, migrations, and database client@op/api: tRPC routers, procedures, and middleware@op/supabase: Supabase client configuration (auth, storage)@op/emails: React Email templates with Tailwind styling@op/cache: Caching utilities and Redis integration
- UI components are in
@op/uiand exported viapackage.jsonexports field - Components use React Aria Components with Tailwind variants
- Import components like:
import { Button } from "@op/ui/Button"
Intent UI is a shadcn-compatible component library built on React Aria. To add a component:
- Browse components at https://intentui.com/docs/components
- Fetch the component JSON from
https://intentui.com/r/{component-name}.json(e.g.,https://intentui.com/r/table.json) - Copy the component code to
packages/ui/src/components/ui/{component}.tsx - Update imports to use local paths:
@/lib/primitive→@/lib/primitive(already exists)@/hooks/use-media-query→@/hooks/use-media-query(already exists)- Replace any icon imports with
react-icons/lu(e.g.,import { LuChevronDown } from 'react-icons/lu')
- Add export to
packages/ui/package.jsonexports field - Create Storybook story in
packages/ui/stories/ - Run
pnpm w:ui typecheckto verify
Key files:
packages/styles/intent-ui-theme.css- Theme mapping to OP brand tokenspackages/ui/src/lib/primitive.ts-cx()utility for React Aria render propspackages/ui/src/hooks/use-media-query.ts- Media query hook
- Database schema managed with Drizzle ORM in
services/db/schema/ - tRPC API provides type-safe client-server communication
- After schema changes: run
pnpm w:db generatethenpnpm w:db migrate
Use pnpm w:<workspace> shortcuts:
pnpm w:app- apps/apppnpm w:api- apps/apipnpm w:db- services/dbpnpm w:ui- packages/uipnpm w:emails- services/emailspnpm w:supabase- services/supabase
- Add dependencies:
pnpm add <package> --filter <workspace-name> - Clean unused deps:
pnpm deps:clean - Enforce version consistency:
pnpm deps:override
- Shared configs in
configs/typescript-config/ - Each workspace extends appropriate base config (nextjs.json, react-library.json, etc.)
- ALWAYS checkout a new branch when making changes if currently on the
devbranch - Branch naming convention:
- Bug fixes:
fix-login-validation - Features:
user-dashboard - Do not prefix your branch with your name or initials
- Bug fixes:
- NEVER commit, push, or pull - these actions are always manual
- NEVER search outside the current worktree - all searches should stay within the current working directory
- The working directory is the root of the monorepo; do not traverse to parent directories or other projects
- Run type checking with
pnpm typecheckorpnpm w:app typecheckafter making changes - Follow existing code conventions and patterns in the file being edited
- Test changes thoroughly before completion
- Using
anyto fix type errors shuold be avoided - Code quality is enforced via Prettier (formatting) and TypeScript (type checking) only
- If statements should never be all on one line, rather you should always use K&R style for if statements
- Translation files location:
apps/app/src/lib/i18n/dictionaries/ - Supported languages: All
.jsonfiles in the dictionaries folder (e.g.,en.json,es.json,pt.json, etc.) - Use
useTranslationshook:const t = useTranslations()thent('Key string') - Use
TranslatedTextcomponent for server components
CRITICAL: When adding or modifying user-facing strings:
- ALWAYS wrap strings with
t('...')- never hardcode user-facing text - ALWAYS add the translation key to ALL language files in the dictionaries folder, not just
en.json - BEFORE completing any task that touches UI text, verify the translation key exists in every language file
- For dynamic values, use interpolation:
t('Hello {name}', { name: userName }) - When modifying existing translation keys, update ALL language files
- Node.js 18+ required, use
corepack enablefor pnpm version management - UI components use React Aria for accessibility
- tRPC provides end-to-end type safety between frontend and backend
- Tailwind configuration is centralized in
@op/stylespackage (packages/styles/shared-styles.css) - Only use colors defined in
packages/styles/tokens.css(prefixed with--op-*)
-
If you need to check interactions in the browser, you can use the Playwright MCP server and open http://localhost:3100 to open the dev server
-
Authorization checks are achieved by our access-zones library. We usually get the orgUser and pass the user's roles to
assertAccess -
Always prefer defining the parameter types directly inline within the function signature over separate definitions of an interface unless the interface is used across multiple functions