Files
adiuva/.claude/CLAUDE.md

8.9 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commands

# Development
source ~/.nvm/nvm.sh && npm start          # Start Electron app with hot-reload

# Build & Package
source ~/.nvm/nvm.sh && npm run make       # Build distributable packages
source ~/.nvm/nvm.sh && npm run package    # Package without making installers

# Lint
source ~/.nvm/nvm.sh && npm run lint       # ESLint over .ts/.tsx files

# Database migrations (Drizzle)
source ~/.nvm/nvm.sh && npx drizzle-kit generate   # Generate migration from schema changes
source ~/.nvm/nvm.sh && npx drizzle-kit push        # Push schema directly (dev only)

There is no test suite currently.

Architecture Overview

Adiuva is a local-first Electron desktop app. The three Electron processes communicate via a custom tRPC↔IPC bridge (the public electron-trpc package is incompatible with tRPC v11, so a custom implementation is used).

Process Boundaries

Renderer (React)  ──ipcLink──►  Preload (contextBridge)  ──IPC──►  Main (tRPC router + SQLite)
  1. Main process (src/main/) — Node.js, owns the database and all business logic

    • index.ts — Window creation, app lifecycle
    • ipc.ts — Custom handler that bridges ipcMain to tRPC procedures
    • router/index.ts — All tRPC routers (clients, projects, tasks, checkpoints, notes, settings, ai)
    • db/index.ts — Drizzle + better-sqlite3, WAL mode, singleton getDb()
    • db/schema.ts — All table definitions (clients, projects, tasks, checkpoints, notes)
    • store.ts — electron-store for persistent UI settings (e.g., sidebarCollapsed)
  2. Preload (src/preload/trpc.ts) — Exposes window.electronTRPC with sendMessage() / onMessage()

  3. Renderer (src/renderer/) — React 19, never accesses Node APIs directly

    • lib/ipcLink.ts — Custom TRPCLink that routes calls through window.electronTRPC
    • lib/trpc.tscreateTRPCReact<AppRouter>() typed client
    • index.tsx — QueryClient + tRPC + Router providers
    • All data access is through trpc.*.*useQuery() / trpc.*.*.useMutation()

Routing

File-based routing via TanStack Router. Add a file to src/renderer/routes/ and the route tree (src/renderer/routeTree.gen.ts) is auto-regenerated by the Vite plugin on next npm start. Routes:

  • __root.tsx — Root layout wrapping everything in AppShell
  • index.tsx, tasks.tsx, timeline.tsx, projects.tsx

Database

Schema lives in src/main/db/schema.ts. Migrations are in src/main/db/migrations/. The DB is created in Electron's userData directory as adiuva.db. On startup, initDb() runs non-destructive migrations (CREATE TABLE IF NOT EXISTS).

To add a new table or column: edit schema.ts, run drizzle-kit generate, then drizzle-kit push (dev) or commit the migration file.

Adding a New Feature (end-to-end pattern)

  1. Schema — Add table/columns to src/main/db/schema.ts
  2. Router — Add a tRPC sub-router in src/main/router/index.ts, merge it into appRouter
  3. TypesAppRouter is exported from src/main/router/index.ts and imported in src/renderer/lib/trpc.ts — types flow automatically
  4. UI — Create components under src/renderer/components/<feature>/, use trpc.*.*useQuery() for data

AI Subsystem (src/main/ai/)

LangGraph-based agentic system with pluggable LLM providers (OpenAI, Anthropic, GitHub Copilot).

Orchestrator (orchestrator.ts): Classifies user intent → routes to one of three specialist agents:

  • Project agent — project-scoped Q&A with tools: read_project_notes, add_task, get_summary, suggest_checkpoints, suggest_tasks
  • Knowledge agent — cross-project semantic search via vector_search_all
  • General agent — workspace-wide add_task

Tool-calling strategy differs by provider: OpenAI/Anthropic use LangChain bindTools() + ToolMessage loop (max 5 iterations); Copilot uses SDK-native tools (loop handled internally).

Streaming: Orchestrator calls sendStreamChunk(sender, token, done) over IPC channel 'ai:stream'. Renderer subscribes via window.electronAI.onStreamChunk() in AIChatPanel.tsx. <tool_call> blocks are filtered before sending to renderer.

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) — three-tier fallback:

  1. keytar (OS keychain) — preferred, encrypted per-user
  2. electron-store + safeStorage — encrypted at rest
  3. Plain electron-store — WSL fallback

Keytar service name is 'adiuva'. Once keytar fails, keytarFailed flag skips it for the session.

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 stored in {userData}/vectors/. Table schema: { id, projectId, content, vector }. Vectors are 1536-dimensional (text-embedding-3-small). Embeddings use a priority chain: Copilot CLI token → OpenAI token.

  • Note create/update fires upsertNoteEmbedding() (fire-and-forget, errors swallowed)
  • migrateNotesIfNeeded() backfills existing notes on first startup
  • searchNotes(query, limit=5) is called by the Knowledge agent tool

Key Config Notes

  • Vite configs use .mts extension (not .ts) to avoid ESM/CJS conflicts with electron-forge's externalize-deps plugin
  • @/* path alias resolves to src/renderer/* (TypeScript + Vite + shadcn/ui all share this alias)
  • shadcn/ui style: new-york, base color: neutral
  • Icons: lucide-react throughout — do not introduce other icon libraries
  • Tailwind 4 (not 3) — use CSS variable theming via globals.css, not tailwind.config.js
  • Notes use Milkdown (@milkdown/crepe) as the markdown editor (src/renderer/components/notes/MilkdownEditor.tsx)
  • Routes: index, tasks, timeline, projects, notes.$noteId (note ID is a URL param)

Design Context

Users

Freelancers and solo professionals managing their own client work — projects, tasks, notes, and timelines. They work alone and need a single workspace that keeps everything organized without the overhead of enterprise tools. The AI assistant is a force multiplier, helping them stay on top of their workload.

Brand Personality

Calm, intelligent, warm. Adiuva is a thoughtful companion, not a flashy tool. It should feel like a well-organized desk — everything in its place, nothing competing for attention. The tone is confident and understated, never loud or gamified.

Aesthetic Direction

  • Visual tone: Editorial, premium, content-first. Inspired by Notion's clean typography and warm neutrals, but with a distinct identity through the warm pinkish-white canvas and golden yellow accent
  • Light mode: Soft and warm — pinkish-white (#f4edf3) canvas, golden yellow (#fbc881) primary, slate blue-gray (#8a8ea9) secondary, dusty lavender borders (#c8c3cd)
  • Dark mode: Stark monochrome — near-black canvas (#0c0c0c), crisp white text, dark gray surfaces (#323232). No color accent; primary is pure white
  • Typography: Geist (geometric sans-serif) at 400/500/600. Tight tracking on large headings (-1px). Body at text-sm, metadata at text-xs
  • Corners: 10px base radius, consistently rounded. Chat elements use rounded-2xl
  • Signature effects: Glassmorphism on AI inputs/floating chat (backdrop-blur-xl, transparency). Spring physics animations (stiffness 400, damping 30). Subtle scale-and-fade transitions
  • Anti-references: No gamification (badges, streaks, confetti). No corporate/enterprise density. Keep it mature and professional

Design Principles

  1. Clarity over cleverness — Every element should communicate its purpose instantly. Prefer clear hierarchy and whitespace over decorative flourish. Information density should feel comfortable, not cramped.

  2. AI as quiet partner — The AI is deeply integrated (floating chat, suggestions) but never intrusive. AI-suggested items use dashed borders to signal "pending." The Sparkles icon is the consistent AI identity marker.

  3. Warmth in restraint — The palette is deliberately warm (pinkish whites, golden yellows) to feel approachable without being playful. Dark mode trades warmth for focus. Let the content breathe.

  4. Motion with purpose — Spring physics and glassmorphism create a sense of physicality and depth. Animations should feel natural and responsive, never decorative or slow. Every transition should reinforce spatial relationships.

  5. Confidence through consistency — Use the established token system (CSS variables, shadcn/ui primitives, Geist font). The user should feel in control — predictable patterns, keyboard-first interactions, no surprises.