Skip to content

Latest commit

 

History

History
332 lines (253 loc) · 13.1 KB

File metadata and controls

332 lines (253 loc) · 13.1 KB

CLAUDE.md

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

Development Commands

pnpm install                # Install dependencies
pnpm dev                   # Run app in development mode
pnpm build                 # Production build (includes typecheck)
pnpm typecheck             # Type checking only
pnpm lint                  # ESLint checking
pnpm lint:fix              # Auto-fix ESLint issues
pnpm test                  # Run Vitest unit tests
pnpm test:ui               # Run Vitest with UI
pnpm test:e2e              # Run Playwright E2E tests
pnpm package               # Create installers per OS platform

Architecture Overview

Levante is an Electron-based desktop AI chat application with multi-provider support. The architecture follows Hexagonal Architecture principles with clear separation between layers:

Core Structure

  • Main Process (src/main/): Node.js backend with services and IPC handlers
  • Preload (src/preload/): Secure bridge between main and renderer processes
  • Renderer (src/renderer/): React frontend with TypeScript

Key Technologies

  • Platform: Electron with secure IPC using levante/* namespace
  • Frontend: React + TypeScript with shadcn/ui components and Tailwind CSS
  • State Management: Zustand for global state (chat, models, preferences)
  • AI Integration: Vercel AI SDK with multi-provider support
  • Database: SQLite with schema migrations
  • Storage: electron-store for encrypted preferences at ~/levante/

Multi-Provider AI System

The application supports multiple AI providers through a unified architecture:

Supported Providers:

  • OpenRouter: Public model listing (API key optional), supports 500+ models
  • Vercel AI Gateway: Custom gateway with model filtering
  • Local: Ollama-compatible endpoints
  • Cloud: Direct provider APIs (OpenAI, Anthropic, Google)

Provider Architecture:

ModelStore (Zustand) → ModelService → ModelFetchService → IPC → Main Process → External APIs

Endpoint Handling:

  • OpenRouter: /api/v1/models for listing, /api/v1 for inference
  • Vercel Gateway: /v1/models for listing, /v1/ai for inference
  • API keys stored securely in electron-store with encryption

Database Schema

SQLite database with migrations in database/migrations/:

  • chat_sessions: Session management with model tracking
  • messages: Message storage with streaming support
  • Schema version tracking for migrations

Database Migration Rules

Migrations are defined in src/main/services/databaseService.tsgetMigrations(). The SQL files in database/migrations/ are documentation only — they are NOT executed at runtime.

Versioning:

  • Each migration has a sequential version number tracked in schema_migrations table
  • Never reuse or reorder version numbers — the runner skips any version ≤ current max
  • If migrations were added and later removed from the codebase, the DB may have a higher version than the code. Always check SELECT MAX(version) FROM schema_migrations before adding a new migration and use a version number higher than what the DB could have

Writing safe migrations:

  • Use CREATE TABLE IF NOT EXISTS — never bare CREATE TABLE
  • Use CREATE INDEX IF NOT EXISTS — never bare CREATE INDEX
  • Do NOT use bare ALTER TABLE ADD COLUMN — if the column already exists it throws. The migration runner tolerates already exists / duplicate column name errors, but be explicit about intent
  • Avoid DROP TABLE or DROP COLUMN unless strictly necessary — these cause data loss and break repair migrations
  • If you must drop something, add a subsequent repair migration in the same PR in case the drop needs to be undone

Runner behavior (runMigrations):

  • Executes each DDL query individually (not in a batch) so errors are isolated
  • Tolerates "already exists" / "duplicate column name" errors and continues
  • Records the version in schema_migrations only after all queries in the migration succeed
  • Errors are logged but swallowed in initialization.ts — the app continues in degraded mode. Always test that migrations apply cleanly on a real DB before shipping

OAuth System

Levante implements OAuth 2.1 with PKCE for MCP server authentication:

Key Features:

  • Dynamic Client Registration (RFC 7591)
  • Client secret expiration handling
  • Token auto-refresh
  • Token revocation on disconnect

Client Credentials:

  • Stored encrypted in ui-preferences.json
  • Automatic validation of client_secret_expires_at
  • Re-registration attempt if secret expires
  • IPC notification on credentials expiration

Files:

  • src/main/services/oauth/ - Core OAuth services
  • src/main/ipc/oauthHandlers.ts - IPC handlers
  • src/renderer/stores/oauthStore.ts - UI state

For detailed architecture: See OAuth Architecture

Configuration Storage

All configuration is stored in ~/levante/ directory with selective encryption:

Files:

  • ui-preferences.json - UI settings, providers (with encrypted API keys), theme, language
  • user-profile.json - User profile, wizard status, personalization
  • .config-version - Migration version tracker

Key Features:

  • Selective Encryption: Only sensitive values (API keys) are encrypted using safeStorage API
  • Migrations: Automatic schema migrations with semantic versioning
  • Type-safe: Full TypeScript support with JSON schema validation
  • Services: PreferencesService and UserProfileService manage access

Storage Architecture:

~/levante/
├── ui-preferences.json      → theme, language, providers (API keys encrypted), ai config
├── user-profile.json        → wizard, personalization (no encryption)
└── .config-version          → "1.1.0"

Encryption Details:

  • Only providers[].apiKey values are encrypted
  • Uses Electron's safeStorage (Keychain/DPAPI/libsecret)
  • Encrypted values prefixed with ENCRYPTED: marker
  • Files remain readable JSON with selective field encryption

Migration System:

  • Version-controlled schema updates in ConfigMigrationService
  • Runs automatically on app start before service initialization
  • Example: Migration 1.0 → 1.1 moved theme/language between files

For detailed information: See Configuration Storage Guide

IPC Communication

All IPC uses the levante/* namespace with structured responses:

Chat:

  • levante/chat/stream → Streaming chat responses
  • levante/chat/send → Single message requests

Models:

  • levante/models/openrouter → Fetch OpenRouter models
  • levante/models/gateway → Fetch Gateway models
  • levante/models/local → Discover local models

Preferences:

  • levante/preferences/get|set|getAll → Settings management

Logging:

  • levante/logger/log → Send log messages from renderer to main
  • levante/logger/isEnabled → Check if category/level is enabled
  • levante/logger/configure → Update logger configuration

State Management with Zustand

ChatStore (src/renderer/stores/chatStore.ts):

  • Current chat state, streaming messages, session management
  • Database message handling with pagination

ModelStore (src/renderer/stores/modelStore.ts):

  • Provider configuration, model synchronization
  • Bulk and individual model selection
  • Real-time updates across UI components

Component Architecture

Pages:

  • ChatPage: Main chat interface with model selection
  • ModelPage: Provider configuration and model management
  • SettingsPage: Application preferences

AI Components (src/renderer/components/ai-elements/):

  • prompt-input: Chat input with model selector
  • message: Message display with streaming support
  • code-block: Syntax highlighted code blocks

Security & CSP

Content Security Policy configured in src/renderer/index.html:

script-src 'self' 'unsafe-inline' blob:; worker-src 'self' blob:;

This allows Vite workers in development while maintaining security.

Model Management System

Configuration Storage:

  • Preferences saved to ui-preferences.json with encryption
  • Model lists cached locally for offline access
  • API keys stored securely per provider

Dynamic vs User-Defined Models:

  • Dynamic: Fetched from provider APIs (OpenRouter, Gateway)
  • User-Defined: Manually configured (Local, Cloud providers)

Model Selection Flow:

  1. Select Active Provider
  2. Configure API keys/endpoints
  3. Sync models from provider
  4. Select which models appear in chat
  5. Models available across all chat sessions

MCP Configuration System

Levante uses mcp-use as the default MCP (Model Context Protocol) client with support for Code Mode, which enables 40-60% token reduction for complex multi-tool workflows.

Settings > MCP Section:

  • SDK Selection: Choose between mcp-use (default, recommended) or official-sdk (legacy)
  • Code Mode: Enable AI agents to write JavaScript to orchestrate multiple MCP tools
  • Executor Options:
    • VM (Local): Local execution with basic sandboxing (default)
    • E2B (Cloud): Cloud-based sandbox with full isolation (requires API key)
  • Advanced Settings: Timeout, memory limits, E2B API key configuration

Architecture:

IMCPService ← MCPServiceFactory → MCPUseService (default)
                                 → MCPLegacyService (fallback)

Configuration Storage:

  • Saved in ui-preferences.json under mcp key
  • Preferences include SDK choice, Code Mode defaults, executor settings
  • E2B API key encrypted using safeStorage

Key Files:

  • src/main/services/mcp/mcpServiceFactory.ts - Runtime SDK selection
  • src/main/services/mcp/mcpUseService.ts - mcp-use implementation
  • src/main/services/mcp/mcpLegacyService.ts - Official SDK implementation
  • src/renderer/components/settings/MCPSection.tsx - UI configuration
  • src/renderer/hooks/useMCPConfig.ts - Settings state management

Usage in Settings:

  1. Navigate to Settings > MCP Configuration
  2. Select SDK (mcp-use recommended for Code Mode)
  3. Toggle Code Mode and configure executor
  4. Adjust timeout/memory for VM or add E2B API key
  5. Save configuration - changes apply to new server connections

Development Patterns

Renderer Alias: Use @ alias for imports within src/renderer:

import { modelService } from '@/services/modelService';
import { Button } from '@/components/ui/button';

Error Handling:

  • IPC responses use { success: boolean; data?: T; error?: string } pattern
  • Zustand stores handle async operations with loading/error states
  • UI displays errors using shadcn/ui Alert components

State Updates:

  • Use Zustand actions for state modifications
  • Avoid direct state mutation
  • UI components subscribe to specific store slices for performance

Environment Configuration

Environment variables loaded from:

  1. .env.local (highest priority, git-ignored)
  2. .env (committed defaults)

API Keys:

  • OPENAI_API_KEY
  • ANTHROPIC_API_KEY
  • GOOGLE_GENERATIVE_AI_API_KEY

Logging Configuration:

  • DEBUG_ENABLED → Master switch for all debug logging
  • DEBUG_AI_SDK → AI service operations and streaming
  • DEBUG_MCP → MCP server management and tools
  • DEBUG_DATABASE → Database operations and migrations
  • DEBUG_IPC → Inter-process communication
  • DEBUG_PREFERENCES → Settings and configuration
  • DEBUG_CORE → Application lifecycle and errors
  • DEBUG_OAUTH → OAuth flow, authorization, and tokens
  • LOG_LEVEL → Minimum log level (debug|info|warn|error)

Testing Strategy

  • Unit/Integration: Vitest for services and utilities
  • E2E: Playwright for complete user flows
  • Manual Testing: Development mode with DevTools enabled

Build System

  • Bundler: electron-vite with Vite for renderer, esbuild for main/preload
  • TypeScript: Strict mode with separate configs for main and preload processes
  • Assets: Icons and resources in resources/ directory
  • Output: Built files in out/ directory for development, dist-electron/ for distribution
  • no realices comprobaciones con pnpm dev, se realizaran manualmente

Logging System

Levante utiliza Winston para logging centralizado con sistema de categorías:

  • Categorías: ai-sdk, mcp, database, ipc, preferences, models, core, analytics, oauth
  • Niveles: debug, info, warn, error
  • Producción: Logs JSON estructurados con archivo de errores separado
  • Desarrollo: Salida coloreada legible en consola
  • Zero Overhead: Categorías deshabilitadas no ejecutan código de log
  • Rotación: Automática con winston-daily-rotate-file

Configuración por Entorno:

  • NODE_ENV=development: Verbose, consola, rotación pequeña
  • NODE_ENV=production: Minimal, solo archivo, rotación grande

Ver docs/LOGGING.md para documentación completa.

  • No utilices pnpm dev

Developer Documentation

For additional development guides and best practices: