# AGENTS.md Guide for agentic coding assistants working in the Officer monorepo. ## Quick Start Commands ### Development ```bash bun dev # Dashboard + API server (port 5000) bun dev:tracking # Tracking server (port 5001) bun dev:experiments # Experiments server bun dev:emailer # Emailer workspace ``` ### Building & Database ```bash bun run prebuild # Run prebuild tasks bun run build:dashboard # Build dashboard bun run build:editor # Build editor (app + extension + runtime) bun run build:runtime # Build all runtime scripts bun run db:gen && db:push # Generate & push officer_db migrations bun run db:gen:stats && db:push:stats # Generate & push statistics_db ``` ### Code Quality ```bash bun format # Format all files (Prettier, required before commit) bun format:check # Check formatting without writing bunx tsgo # TypeScript type checking (comprehensive, slow) ``` **Note:** No automated tests configured yet. Always run `bun format` before committing. ## Code Style Guide ### Imports Order: **types → external → workspace → relative** ```ts import type { Experiment } from 'types'; import { useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { useExperiment } from 'hooks/use-experiment'; import { formatDate } from '../helpers'; ``` ### TypeScript - Strict mode always - no `any` - Prefer `type` over `interface` - Colocate prop types with components as named exports - Early returns for null/undefined guards - Use optional chaining and nullish coalescing: `obj?.prop ?? fallback` ### Functions - Arrow functions for simple/one-liners - Regular functions for complex multi-line logic - Named exports only (never default exports) ```ts export const formatDate = (ts: number) => new Date(ts).toLocaleDateString(); export function calculateStats(data: DataPoint[]) { /* ... */ } ``` ### React Components - Functional components with named prop types - Arrow function syntax - No useMemo/useCallback (React 19 compiler handles optimization) ```tsx type CardProps = { title: string; onClick: () => void }; export const Card = ({ title, onClick }: CardProps) =>
{title}
; ``` ### State Management - **Manager pattern** for complex hooks - return object with state + methods - **Colocation** - all feature state in one hook - **Derived state** - compute in hook, not in components ```ts export const useExperimentManager = (id: number) => { const [exp, setExp] = useState(null); const isActive = exp?.status === 'running'; return { exp, isActive, update: (data) => { /* ... */ }, }; }; ``` ### Naming Conventions - **Components**: `PascalCase.tsx` (e.g., `ExperimentCard.tsx`) - **Everything else**: `kebab-case.ts` (e.g., `use-experiment.ts`, `format-date.ts`) - **Hooks**: `use-*.ts` or `useFeatureName.ts` ### Formatting (Prettier) - Semicolons: always - Quotes: single (JS/TS), double (JSX) - Trailing commas: all - Indent: 2 spaces - Line width: 120 characters ### Error Handling - Use try/catch for async operations - Include context in error messages (IDs, resource names) - Log with `console.error` and re-throw appropriately - Handle database connection errors explicitly ### Async/Await - Always use async/await (never .then() chains) - Minimize nesting ## Project Structure - `src/apps/dashboard/` - Admin UI (React 19) - `src/apps/editor/` - Visual editor - `src/servers/api/` - REST API (Hono, port 5000) - `src/servers/tracking/` - Event collection (port 5001) - `src/databases/` - PostgreSQL schemas (3 DBs: officer_db, statistics_db, ephemeral_db) - `src/workspaces/` - Shared: components, hooks, helpers, types ## Path Aliases - `@/` → `src/apps/dashboard/` - `@@/` → `src/servers/` ## Tech Stack - **Runtime**: Bun - **Language**: TypeScript 5.9 (strict mode) - **Frontend**: React 19, React Router, React Query, Tailwind CSS, shadcn/ui - **Backend**: Hono, PostgreSQL, Drizzle ORM - **Build**: Vite ## General Guidelines - **Database-first** approach - schema → API → UI - **Self-documenting code** - clear naming, minimal comments - **Explicit over implicit** - no magic - **Workspace dependencies** - use `workspace:*` - **Environment variables** - use `.env`, access directly in code