Files
adiuva/.claude/CLAUDE.md

7.2 KiB

CLAUDE.md

Commands

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.tsdrizzle-kit generatedrizzle-kit push (dev) or commit the migration.

Adding a Feature (end-to-end)

  1. Schemasrc/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.

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