# CLAUDE.md ## Commands ```bash source ~/.nvm/nvm.sh && npm start # Dev with hot-reload source ~/.nvm/nvm.sh && npm run make # Build distributable packages source ~/.nvm/nvm.sh && npm run package # Package without installers source ~/.nvm/nvm.sh && npm run lint # ESLint (.ts/.tsx) source ~/.nvm/nvm.sh && npx drizzle-kit generate # Generate migration from schema source ~/.nvm/nvm.sh && npx drizzle-kit push # Push schema directly (dev only) ``` No test suite currently. ## Architecture Adiuva is a local-first Electron desktop app. The three processes communicate via a custom tRPC v11 ↔ IPC bridge (the public `electron-trpc` package is incompatible with tRPC v11). ``` Renderer (React 19) ──ipcLink──► Preload (contextBridge) ──IPC──► Main (tRPC router + SQLite) ``` ### Main Process (`src/main/`) Owns the database and all business logic. | File | Purpose | |---|---| | `index.ts` | Window creation, app lifecycle | | `ipc.ts` | Bridges `ipcMain` to tRPC procedures | | `router/index.ts` | All tRPC sub-routers merged into `appRouter` | | `db/index.ts` | Drizzle + better-sqlite3, WAL mode, singleton `getDb()` | | `db/schema.ts` | Table definitions: clients, projects, tasks, checkpoints, notes, taskComments | | `db/vectordb.ts` | LanceDB vector store for note embeddings | | `store.ts` | electron-store for persistent UI settings | ### Preload (`src/preload/trpc.ts`) Exposes `window.electronTRPC` with `sendMessage()` / `onMessage()`. ### Renderer (`src/renderer/`) React 19 — never accesses Node APIs directly. All data through `trpc.*.useQuery()` / `trpc.*.useMutation()`. | File | Purpose | |---|---| | `lib/ipcLink.ts` | Custom TRPCLink routing through `window.electronTRPC` | | `lib/trpc.ts` | `createTRPCReact()` typed client | | `index.tsx` | QueryClient + tRPC + Router providers | ### Routing File-based via TanStack Router (`tsr.config.json` at root). Route tree auto-generated at `routeTree.gen.ts`. Routes: `__root.tsx` (AppShell layout), `index`, `tasks`, `timeline`, `projects`, `notes.$noteId` ### tRPC Routers `health`, `settings`, `clients`, `projects`, `tasks`, `checkpoints`, `notes`, `taskComments`, `ai` ### Database Schema in `src/main/db/schema.ts`, migrations in `src/main/db/migrations/`. DB created in Electron's `userData` as `adiuva.db`. On startup, `initDb()` runs non-destructive migrations. To add a table/column: edit `schema.ts` → `drizzle-kit generate` → `drizzle-kit push` (dev) or commit the migration. ### Adding a Feature (end-to-end) 1. **Schema** — `src/main/db/schema.ts` 2. **Router** — Add sub-router in `src/main/router/index.ts`, merge into `appRouter` 3. **Types** — Flow automatically via `AppRouter` export 4. **UI** — Components in `src/renderer/components//`, data via `trpc.*.useQuery()` ## AI Subsystem (`src/main/ai/`) LangGraph-based agentic system with pluggable LLM providers. ### Orchestrator (`orchestrator.ts`) Classifies user intent → routes to a specialist agent: | Agent | Scope | Tools | |---|---|---| | Project | Project-scoped Q&A | `read_project_notes`, `add_task`, `get_summary`, `suggest_checkpoints`, `suggest_tasks` | | Knowledge | Cross-project search | `vector_search_all` | | General | Workspace-wide | `add_task` | All providers use LangChain `bindTools()` + ToolMessage loop (max 5 iterations). Also exports `dailyBrief()` for AI-generated daily summaries (`ai.dailyBrief` tRPC mutation). ### Streaming `sendStreamChunk(sender, token, done)` over IPC `'ai:stream'`. Renderer subscribes via `window.electronAI.onStreamChunk()` in `AIChatPanel.tsx`. `` blocks are filtered before display. ### Providers (`llm.ts`) | Provider | Model | Notes | |---|---|---| | OpenAI | `gpt-4o-mini` | Via LangChain | | Anthropic | `claude-sonnet-4-20250514` | Via LangChain | | Copilot | `ChatCopilot` wrapper | `copilot.ts` / `chat-copilot.ts` | All use `temperature: 0.3`, streaming enabled. Provider management in `provider.ts`. ### Vector Embeddings (`db/vectordb.ts`) **Provider factory** (`llm.ts`): `gpt-4o-mini` (OpenAI), `claude-sonnet-4-20250514` (Anthropic), or ChatCopilot wrapper — all with `temperature: 0.3` and streaming enabled. **Token storage** (`token.ts`) — two-tier fallback: 1. electron-store + `safeStorage` — encrypted at rest (preferred) 2. Plain electron-store — last resort (e.g. WSL with no keyring) **AI approval pattern**: Tasks and checkpoints have `isAiSuggested` (bool) and `isApproved` (bool) columns. AI-suggested items appear in the UI pending user approval before being treated as real records. ### Vector Embeddings (`src/main/db/vectordb.ts`) LanceDB in `{userData}/vectors/`. Schema: `{ id, projectId, content, vector }` (1536-dim, `text-embedding-3-small` via `embeddings.ts`). Embedding priority: Copilot CLI token → OpenAI token. - `upsertNoteEmbedding()` on note create/update (fire-and-forget) - `migrateNotesIfNeeded()` backfills on first startup - `searchNotes(query, limit=5)` used by Knowledge agent ### AI Approval Pattern Tasks and checkpoints have `isAiSuggested` + `isApproved` columns. AI suggestions appear pending user approval (dashed borders in UI). ## Config Notes - Vite configs use `.mts` (not `.ts`) — avoids ESM/CJS conflicts with electron-forge - `@/*` path alias → `src/renderer/*` (TypeScript + Vite + shadcn/ui) - **shadcn/ui**: new-york style, neutral base color - **Icons**: lucide-react only — do not introduce other icon libraries - **Tailwind 4** — CSS variable theming in `globals.css`, no `tailwind.config.js` - **Notes editor**: Milkdown (`@milkdown/crepe`) at `src/renderer/components/notes/MilkdownEditor.tsx` ## Design Context ### Target User Freelancers and solo professionals managing client work (projects, tasks, notes, timelines). Single workspace, no enterprise overhead. AI as force multiplier. ### Brand **Calm, intelligent, warm.** Thoughtful companion, not flashy tool. Confident and understated, never loud or gamified. ### Palette | | Canvas | Primary | Secondary | Borders | |---|---|---|---|---| | **Light** | Pinkish-white `#f4edf3` | Golden yellow `#fbc881` | Slate blue-gray `#8a8ea9` | Dusty lavender `#c8c3cd` | | **Dark** | Near-black `#0c0c0c` | Pure white | — | Dark gray `#323232` | ### Typography Geist sans-serif, weights 400/500/600. Tight tracking (`-1px`) on headings. Body `text-sm`, metadata `text-xs`. ### Visual Language - 10px border-radius, `rounded-2xl` for chat elements - Glassmorphism on AI inputs (`backdrop-blur-xl`, transparency) - Spring animations (stiffness 400, damping 30), scale-and-fade transitions - No gamification (badges, streaks, confetti). Mature and professional ### Design Principles 1. **Clarity over cleverness** — Clear hierarchy, generous whitespace, comfortable density 2. **AI as quiet partner** — Deeply integrated but never intrusive. Dashed borders for pending AI items, Sparkles icon as AI marker 3. **Warmth in restraint** — Warm palette feels approachable without being playful. Dark mode trades warmth for focus 4. **Motion with purpose** — Animations reinforce spatial relationships, never decorative 5. **Confidence through consistency** — CSS variable tokens, shadcn/ui primitives, Geist font. Predictable, keyboard-first