Some checks failed
Release Electron App / release-desktop (push) Failing after 8m13s
136 lines
8.7 KiB
Markdown
136 lines
8.7 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
# 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.ts` — `createTRPCReact<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. **Types** — `AppRouter` 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`) — 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 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. |