# CLAUDE.md ## Project Overview Officer is an AI-assisted personal backend and frontend for life management. ## Monorepo Structure ``` src/ ├── apps/ │ ├── dashboard/ # Main UI (React 19) │ └── editor/ # Visual editor ├── servers/ │ ├── api/ # REST API (Hono, port 5000) │ └── tracking/ # Event collection (port 5001) ├── databases/ │ ├── officer_db/ # Main app data │ └── ephemeral_db/ # Cache & temporary data └── workspaces/ # Shared: components, hooks, helpers, types, config, etc. ``` **Path aliases**: `@/` → dashboard, `@@/` → servers ## Tech Stack - **Runtime**: Bun - **Language**: TypeScript (strict), some raw SQL - **Frontend**: React 19, React Router, React Query + Context - **UI**: shadcn/ui base + custom components, Tailwind CSS - **Backend**: Hono framework - **Database**: PostgreSQL, Drizzle ORM + raw SQL ## Code Style - **Paradigm**: Functional - pure functions, immutability, composition - **TypeScript**: Strict - no `any`, proper types everywhere - **Comments**: Minimal - code should be self-documenting - **Async**: Always async/await - **Errors**: Try/catch (detailed patterns in section-specific docs) ## TypeScript Patterns ### Imports Order imports as: types → external → workspace → relative ```ts import type { Experiment, User } from 'types'; import { useState, useEffect } from 'react'; import { useQuery } from '@tanstack/react-query'; import { useClient } from 'hooks/useClient'; import { formatDate } from 'helpers/formatters'; import { useWebsites } from '../useWebsites'; ``` **Type-only imports**: With `verbatimModuleSyntax` enabled, types must use type-only imports: ```ts // ✅ Good - inline type import import { ActionModals, type ActionModalsTypes } from './ActionModals'; // ✅ Good - separate type import import type { FormEvent } from 'react'; import { useState } from 'react'; // ❌ Bad - will error with verbatimModuleSyntax import { ActionModals, ActionModalsTypes } from './ActionModals'; ``` ### Types - Prefer `type` over `interface` - Shared types go in `workspaces/types` - re-exports from database types - Database types live in their respective `db/types.ts` files - Component props: colocated as named type ```ts type ExperimentCardProps = { experiment: Experiment; onSelect: (id: number) => void; }; ``` ### Database Types Use Drizzle-inferred types, never manual type definitions in schema files: ```ts // In officerdb/types.ts import * as Schema from './schema'; // Simple table - just Select/Insert export type Screenshot = typeof Schema.Screenshots.$inferSelect; export type ScreenshotInsert = typeof Schema.Screenshots.$inferInsert; // Table with relations - Select + extended type for API responses export type UserSelect = typeof Schema.Users.$inferSelect; export type UserInsert = typeof Schema.Users.$inferInsert; export type User = UserSelect & { company: Company; passkeys: Passkey[]; }; ``` **In app code**, import types from `'types'` (the workspace re-exports everything): ```ts // ✅ Good - in apps/dashboard, apps/editor, etc. import type { User, Experiment, VariantStat } from 'types'; // ❌ Bad - don't import directly from database packages in app code import type { User } from 'officerdb/types'; import type { VariantStat } from 'statisticsdb/types'; ``` **In database/server code**, import from the specific package: ```ts // ✅ Good - in servers/, databases/ import type { User } from 'officerdb/types'; ``` ### Functions - Arrow functions for simple/one-liners - Regular functions for complex logic - Named exports only (no default exports) - **No multiline parameter definitions** - extract params type when signature gets long ```ts // Simple utility - arrow export const formatDate = (value: number) => new Date(value).toLocaleDateString(); // Complex logic - regular function export function calculateStatistics(data: DataPoint[]) { // multi-line logic } // ❌ Bad - multiline params export function processData( body: Record | undefined, query: Record, ): Result { ... } // ✅ Good - extract params type type ProcessDataParams = { body: Record | undefined; query: Record; }; export function processData({ body, query }: ProcessDataParams): Result { ... } ``` ### Null Handling - Early returns for guards - Optional chaining for property access - Use `?? undefined` to convert `null` to `undefined` for props expecting `string | undefined` - Use `?? 0` or `?? ''` for fallback values with state setters ```ts function processExperiment(exp: Experiment | null) { if (!exp) return null; // guard const websiteName = exp.website?.name ?? 'Unknown'; // access // ... } // Converting null to undefined for props // avatar is string | null, src expects string | undefined // Fallback for state setters expecting non-null setWebsite(website?.id ?? 0); // id might be undefined, setter expects number // Array access with known valid index - use non-null assertion const items = ['a', 'b', 'c']; if (items.length > 0) { const first = items[0]!; // We know index 0 exists after length check } ``` ## React Patterns See `CONVENTIONS.md` for detailed patterns (feature folders, manager pattern, state management, React 19 rules). ### Components Arrow function with named props type: ```tsx type ExperimentCardProps = { experiment: Experiment; onSelect: (id: number) => void; }; export const ExperimentCard = ({ experiment, onSelect }: ExperimentCardProps) => { return (/* ... */); }; ``` ### Hooks Return objects for complex hooks, tuples for simple state: ```tsx // Complex - return object export const useExperiment = (id: number) => { return { experiment, isLoading, update, delete: deleteExp }; }; // Simple state - return tuple (like useState) export const useGlobal = (key: string, initial: T) => { return [value, setValue, refresh, reset] as const; }; ``` ## Directory Structure ### Naming Conventions - **Components**: `PascalCase.tsx` - **Everything else**: `kebab-case.ts` - **Hooks**: `use-*.ts` or `useFeatureName.ts` ## Formatting Prettier config in `.prettierrc`: - Semicolons: always - Quotes: single (JS), double (JSX) - Trailing commas: all - Indent: 2 spaces - Line width: 120 ```bash bun format # Format all files bun format:check # Check without writing ``` ## Development ```bash bun dev # Runs dashboard + API server bun dev:tracking # Runs tracking server separately if needed bunx tsgo # TypeScript check (not npx tsc) ``` **New features**: Usually database-first (schema → API → UI) **Environment variables**: - Backend: `process.env.X` directly - Frontend: Use existing config patterns **Logging**: `console.log` for debugging (Signoz integration coming) ## Git Simple lowercase commit messages, no prefixes. ## Working With Me - **Ask first**: Confirm approach before significant changes - **Explore thoroughly**: Read related files for full context - **Keep it simple**: No over-engineering or premature abstractions - **Be explicit**: No magic or implicit behavior - **Stay focused**: Note unrelated issues but don't fix them - **Detailed output**: Provide full breakdown of changes when completing tasks ## Section-Specific Docs Detailed patterns for each area live in their respective directories: **Frontend:** - `src/apps/CLAUDE.md` - Shared frontend patterns (components, hooks, state) - `src/apps/officer-web/Screens/Dashboard/Automation/CLAUDE.md` - Automation screen specifics **Backend:** - `src/databases/CLAUDE.md` - Database schemas and patterns **Project-wide:** - `CONVENTIONS.md` - Detailed code patterns with rationale (component organization, state management, React patterns) - `TODO.md` - Current direction and deferred work. Takes precedence over this file where they disagree.