186 lines
7.6 KiB
Markdown
186 lines
7.6 KiB
Markdown
# CLAUDE.md
|
||
|
||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||
|
||
## Keeping This File Up to Date
|
||
|
||
Update this file whenever a lesson is learned during development. Specifically, update CLAUDE.md when:
|
||
|
||
- A non-obvious architectural decision is made or discovered
|
||
- A gotcha, footgun, or surprising behavior is encountered (and the fix/workaround)
|
||
- A new command, workflow, or tool is added to the project
|
||
- A convention is established that isn't obvious from reading the code
|
||
- An integration detail is clarified (e.g., how the WebSocket protocol actually behaves, edge cases in the agent tool call cycle)
|
||
|
||
Do **not** add things already derivable from reading the code, generic best practices, or ephemeral task notes — only durable, reusable knowledge.
|
||
|
||
## Repository Layout
|
||
|
||
This repo contains two independent projects:
|
||
|
||
- **`adiuva/`** — Electron desktop app (TypeScript/React)
|
||
- **`adiuva-api/`** — FastAPI backend (Python)
|
||
|
||
---
|
||
|
||
## adiuva (Electron App)
|
||
|
||
### Commands
|
||
|
||
```bash
|
||
npm run start # Start dev server (Electron + Vite)
|
||
npm run lint # ESLint
|
||
npm run knip # Dead code analysis
|
||
npm run make # Build installers (Windows/Linux/macOS)
|
||
npm run package # Package without creating installers
|
||
```
|
||
|
||
Database schema changes require running Drizzle Kit — check `package.json` for db commands.
|
||
|
||
### Architecture
|
||
|
||
```
|
||
Renderer (React 19 + TanStack Router)
|
||
↓ tRPC over contextBridge
|
||
Main Process (Electron)
|
||
├── SQLite (better-sqlite3 + Drizzle ORM) — local app data
|
||
├── LanceDB — local vector embeddings
|
||
└── WebSocket client → adiuva-api backend
|
||
```
|
||
|
||
**IPC model**: The renderer calls tRPC procedures defined in `src/main/router/`. The preload script (`src/preload/`) bridges them with `contextIsolation: true`.
|
||
|
||
**Backend integration**: The Electron main process connects to the FastAPI backend via WebSocket. The backend sends tool calls (e.g., `insert`, `vector_search`) which the main process executes against local SQLite via Drizzle and returns results. All AI intelligence lives on the backend — the app is a smart terminal.
|
||
|
||
**Key source directories**:
|
||
- `src/main/agents/` — Agent scheduler
|
||
- `src/main/ai/` — Orchestrator, token management
|
||
- `src/main/db/` — SQLite schema (Drizzle) + LanceDB
|
||
- `src/main/router/` — tRPC router (all IPC procedures)
|
||
- `src/renderer/components/` — UI components (tasks, notes, projects, timeline, auth)
|
||
- `src/renderer/routes/` — TanStack Router pages
|
||
- `src/shared/` — Zod schemas shared between main/renderer (WebSocket frame types, casing utils)
|
||
|
||
**Path aliases** (tsconfig): `@/*` → `src/renderer/`, `@shared/*` → `src/shared/`
|
||
|
||
**WebSocket frame types** are defined in `src/shared/api-types.ts` using Zod. Client sends: `chat_request`, `floating_request`, `tool_result`. Server sends: `text_chunk`, `tool_call`, `final`, `ping`.
|
||
|
||
---
|
||
|
||
## adiuva-api (FastAPI Backend)
|
||
|
||
### Commands
|
||
|
||
```bash
|
||
# Development
|
||
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
||
|
||
# Production
|
||
gunicorn app.main:app -k uvicorn.workers.UvicornWorker -w 4 --timeout 120
|
||
|
||
# Database migrations
|
||
alembic upgrade head
|
||
|
||
# Testing
|
||
pytest
|
||
pytest -v
|
||
pytest tests/test_agents.py # single test file
|
||
|
||
# Linting/formatting
|
||
ruff check .
|
||
ruff format .
|
||
|
||
# Docker (full stack)
|
||
docker compose up --build
|
||
```
|
||
|
||
### Architecture
|
||
|
||
```
|
||
FastAPI app (app/main.py)
|
||
├── Middleware: RateLimiter → Sanitizer → CORS
|
||
├── HTTP Routes (app/api/routes/)
|
||
│ ├── auth.py — register, login, token refresh
|
||
│ ├── chat.py — POST /chat, POST /chat/embed, WS /chat/stream
|
||
│ └── billing.py — Stripe subscriptions
|
||
├── Agent System (app/agents/)
|
||
│ ├── task_agent.py — 8 tools
|
||
│ ├── project_agent.py — 6 tools
|
||
│ ├── timeline_agent.py — 4 tools
|
||
│ └── note_agent.py — 5 tools
|
||
└── Orchestration (app/core/)
|
||
├── agent_registry.py
|
||
├── agent_runner.py
|
||
├── llm.py — LiteLLM factory (100+ providers)
|
||
└── memory_middleware.py
|
||
```
|
||
|
||
**LLM routing**: GPT-4o-mini classifies incoming intent → routes to appropriate domain agent → agent uses GPT-4o with its tool set → sends tool calls back to Electron client for local execution.
|
||
|
||
**Zero-trust data model**: The backend never decrypts user data. PostgreSQL stores only auth, billing, and metadata. All user content stays local on the Electron client.
|
||
|
||
**Tier system**: Free / Pro / Power / Team — enforced in `app/api/middleware/rate_limit.py` (20–200 req/min sliding window) and `app/billing/tier_manager.py`.
|
||
|
||
**Key config**: `app/config/settings.py` — all env vars via Pydantic Settings. Copy `.env.example` to `.env` for local dev.
|
||
|
||
**Database**: PostgreSQL with async SQLAlchemy 2.0 + asyncpg. Migrations in `alembic/versions/`. Models in `app/models.py`, Pydantic schemas in `app/schemas.py`.
|
||
|
||
**Testing**: pytest with pytest-asyncio. Fixtures in `tests/conftest.py`. Use in-memory SQLite for DB tests.
|
||
|
||
---
|
||
|
||
## Microservices Migration (In Progress)
|
||
|
||
The monolith (`adiuva-api/app/`) is being split into independent services under `adiuva-api/services/`. Architectural decisions are tracked in repo memory (`/memories/repo/microservices-architecture.md`).
|
||
|
||
### Target Services (MVP)
|
||
|
||
| Service | Owns | Scaling |
|
||
|---------|------|---------|
|
||
| **Auth** | JWT RS256 issuance, users, refresh_tokens, subscriptions | Stateless |
|
||
| **WS Gateway** | WebSocket connections, Redis frame routing, device registry | Sticky (user_id) |
|
||
| **Chat** | deep_agent, memory, domain agents (task/note/project/timeline), LLM | Stateless |
|
||
| **Batch Agent** | agent_runner, journey builder, filesystem_agent, integrations (+ Langfuse tracing TODO) | Stateless |
|
||
| **Billing** | Stripe, tier_manager | Stateless |
|
||
|
||
**API Gateway**: Traefik with ForwardAuth → Auth `/verify`. Injects `X-User-Id`, `X-User-Email`, `X-User-Tier` headers. Downstream services trust these headers.
|
||
|
||
### Monorepo Structure
|
||
|
||
```
|
||
adiuva-api/
|
||
├── shared/ ← SQLAlchemy models, Pydantic schemas, config, redis utils
|
||
├── services/
|
||
│ ├── auth/
|
||
│ ├── ws-gateway/
|
||
│ ├── chat/
|
||
│ ├── batch-agent/
|
||
│ └── billing/
|
||
├── alembic/ ← Centralized migrations (shared DB)
|
||
├── docker-compose.yml
|
||
└── traefik/
|
||
```
|
||
|
||
### Key Conventions
|
||
|
||
- **shared/ module**: Imported by all services. Contains models, schemas, config, DB session factory, Redis client. Changes here affect all services — be careful.
|
||
- **Redis is the glue**: WS Gateway ↔ Chat/Batch communication is entirely via Redis pub/sub and lists. See `/memories/repo/microservices-architecture.md` for channel naming.
|
||
- **No JWT validation in downstream services**: Only Auth Service has the private key. Other services receive pre-validated identity via Traefik headers.
|
||
- **Tool call round-trip**: Chat/Batch → publish `tool_call` to `ws:out:{user_id}` → WS Gateway forwards to Electron → Electron replies `tool_result` → WS Gateway LPUSH to `tool:result:{call_id}` → Chat/Batch BRPOP with 30s timeout.
|
||
- **Migration strategy**: Strangler fig. Extract one service at a time. The monolith `app/` continues to work until all services are extracted. Don't delete monolith code until the service replacement is tested.
|
||
- **Storage & Plugin services**: Removed from codebase. Will be re-evaluated in future feature planning.
|
||
|
||
### Commands (Microservices)
|
||
|
||
```bash
|
||
# Full stack
|
||
docker compose up --build
|
||
|
||
# Single service dev (example: chat)
|
||
docker compose up redis postgres auth
|
||
cd services/chat && uvicorn app.main:app --reload --port 8002
|
||
|
||
# Migrations (still centralized)
|
||
alembic upgrade head
|
||
```
|