Files
adiuva/.claude/CLAUDE.md

165 lines
6.8 KiB
Markdown

# 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<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`, `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/<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 | `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`. `<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`)
Three-tier fallback (keytar service name: `'adiuva'`):
1. keytar (OS keychain) — preferred; `keytarFailed` flag skips after first failure
2. electron-store + `safeStorage` — encrypted at rest
3. Plain electron-store — WSL fallback
### Vector Embeddings (`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