Files
adiuvAI/.claude/CLAUDE.md

8.4 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

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 Adiuva, 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.