# 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

AdiuvAI 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, noteEdits, taskComments |
| `db/notes-backfill.ts` | Startup backfill: generates aiSummary for notes with null summary |
| `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<AppRouter>()` 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`, `noteEdits`, `taskComments`, `ai`

### Database

Schema in `src/main/db/schema.ts`, migrations in `src/main/db/migrations/`. DB created in Electron's `userData` as `adiuvai.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/<feature>/`, 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 | `list_notes` + `get_note` (aiSummary-based navigation) |
| 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`. `<tool_call>` 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`.

**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)

### Notes AI Navigation (aiSummary index)

Notes have `aiSummary` (≤250 char, nullable) and `aiSummaryUpdatedAt` columns. Generated by backend `POST /api/v1/agents/notes/summarize` (gpt-4o-mini, Langfuse `note_summary` prompt).

- `list_notes` tool output includes the summary per note so AI can navigate without reading full content.
- `notes-backfill.ts` generates missing summaries on startup (throttled 1 req/s, skipped when offline).
- Summary is regenerated fire-and-forget on note create/update and on HITL approve.

### Notes HITL (`noteEdits` table)

AI-proposed note edits go to `noteEdits` instead of directly modifying `notes.content`:
- `type: append | insert | replace` — append adds at end; insert after `anchorBefore` text; replace replaces `anchorText`.
- `status: pending | approved | rejected` — pending shows in UI with dashed border + Approve/Reject.
- On approve: content merged into `notes.content`; summary regenerated. If anchor not found (note edited since proposal), auto-rejects.
- `propose_note_edit` backend tool → drizzle-executor `propose_note_edit` case → inserts `noteEdits` row.
- `noteEditsRouter` in `router/index.ts`: `list`, `listPending`, `approve`, `reject`.

### 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

### Users
Freelancers and solo professionals managing client work (projects, tasks, notes, timelines). Single workspace, no enterprise overhead. AI as force multiplier. They open the app mid-workday — often stressed — so the interface must feel immediately grounding and in control.

### Brand Personality
**Calm. Intelligent. Warm.** A thoughtful companion, not a flashy tool. Confident and understated — never loud, gamified, or corporate. Fully original aesthetic (no external design system references; this look is intentional and owned).

### Emotional Goal
When a user opens AdiuvAI, the first impression should communicate **"everything is under control"** — calm clarity over urgency. The design should lower cognitive load, not raise it.

### Aesthetic Direction
- Light mode: pinkish-white canvas `#f4edf3`, golden yellow primary `#fbc881`, slate blue-gray secondary `#8a8ea9`, dusty lavender borders `#c8c3cd`
- Dark mode: near-black `#0c0c0c`, pure white primary, dark gray `#323232` surfaces
- Geist sans-serif, weights 400/500/600. Tight tracking (`-1px`) on headings. Body `text-sm`, metadata `text-xs`
- 10px border-radius (`rounded-lg`), `rounded-2xl` for chat/AI elements
- Glassmorphism on AI inputs (`backdrop-blur-xl`, transparency, gradient border via padding-box/border-box technique)
- Spring animations (stiffness 400, damping 30), scale-and-fade transitions
- No gamification (badges, streaks, confetti). Mature and professional
- Dashed borders + Sparkles icon = AI-pending state marker

### Accessibility
Best-effort — not formally audited. Maintain reasonable contrast and keyboard operability without targeting a specific WCAG level.

### Current Design Focus
**Polish and refinement.** The overall direction is solid; the priority is elevating specific areas that feel rough or inconsistent — tighter spacing, more intentional hierarchy, better empty/loading states, and smoother motion.

### Design Principles
1. **Clarity over cleverness** — Clear hierarchy, generous whitespace, comfortable density. Never sacrifice legibility for style.
2. **AI as quiet partner** — Deeply integrated but never intrusive. Dashed borders for pending AI items, Sparkles icon as the sole AI marker. Surface AI capabilities without making them the hero.
3. **Warmth in restraint** — The warm palette feels approachable without being playful. Dark mode trades warmth for focus. Neither mode should feel cold or aggressive.
4. **Motion with purpose** — Spring animations reinforce spatial relationships and acknowledge state changes. Never purely decorative. Respect reduced-motion preferences where possible.
5. **Polish over features** — Every surface should feel considered. Prefer refining what exists over introducing new complexity. The right amount of visual weight is the minimum needed.
