Files
workspace/.claude/CLAUDE.md
Roberto Musso 1f1ce7d40e first commit
2026-04-08 22:55:08 +02:00

186 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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` (20200 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
```