This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
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 platformLevante is an Electron-based desktop AI chat application with multi-provider support. The architecture follows Hexagonal Architecture principles with clear separation between layers:
- 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
- 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/
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/modelsfor listing,/api/v1for inference - Vercel Gateway:
/v1/modelsfor listing,/v1/aifor inference - API keys stored securely in electron-store with encryption
SQLite database with migrations in database/migrations/:
chat_sessions: Session management with model trackingmessages: Message storage with streaming support- Schema version tracking for migrations
Migrations are defined in src/main/services/databaseService.ts → getMigrations().
The SQL files in database/migrations/ are documentation only — they are NOT executed at runtime.
Versioning:
- Each migration has a sequential
versionnumber tracked inschema_migrationstable - 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_migrationsbefore 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 bareCREATE TABLE - Use
CREATE INDEX IF NOT EXISTS— never bareCREATE INDEX - Do NOT use bare
ALTER TABLE ADD COLUMN— if the column already exists it throws. The migration runner toleratesalready exists/duplicate column nameerrors, but be explicit about intent - Avoid
DROP TABLEorDROP COLUMNunless 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_migrationsonly 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
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 servicessrc/main/ipc/oauthHandlers.ts- IPC handlerssrc/renderer/stores/oauthStore.ts- UI state
For detailed architecture: See OAuth Architecture
All configuration is stored in ~/levante/ directory with selective encryption:
Files:
ui-preferences.json- UI settings, providers (with encrypted API keys), theme, languageuser-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:
PreferencesServiceandUserProfileServicemanage 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[].apiKeyvalues 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
All IPC uses the levante/* namespace with structured responses:
Chat:
levante/chat/stream→ Streaming chat responseslevante/chat/send→ Single message requests
Models:
levante/models/openrouter→ Fetch OpenRouter modelslevante/models/gateway→ Fetch Gateway modelslevante/models/local→ Discover local models
Preferences:
levante/preferences/get|set|getAll→ Settings management
Logging:
levante/logger/log→ Send log messages from renderer to mainlevante/logger/isEnabled→ Check if category/level is enabledlevante/logger/configure→ Update logger configuration
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
Pages:
ChatPage: Main chat interface with model selectionModelPage: Provider configuration and model managementSettingsPage: Application preferences
AI Components (src/renderer/components/ai-elements/):
prompt-input: Chat input with model selectormessage: Message display with streaming supportcode-block: Syntax highlighted code blocks
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.
Configuration Storage:
- Preferences saved to
ui-preferences.jsonwith 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:
- Select Active Provider
- Configure API keys/endpoints
- Sync models from provider
- Select which models appear in chat
- Models available across all chat sessions
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) orofficial-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.jsonundermcpkey - 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 selectionsrc/main/services/mcp/mcpUseService.ts- mcp-use implementationsrc/main/services/mcp/mcpLegacyService.ts- Official SDK implementationsrc/renderer/components/settings/MCPSection.tsx- UI configurationsrc/renderer/hooks/useMCPConfig.ts- Settings state management
Usage in Settings:
- Navigate to Settings > MCP Configuration
- Select SDK (mcp-use recommended for Code Mode)
- Toggle Code Mode and configure executor
- Adjust timeout/memory for VM or add E2B API key
- Save configuration - changes apply to new server connections
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 variables loaded from:
.env.local(highest priority, git-ignored).env(committed defaults)
API Keys:
OPENAI_API_KEYANTHROPIC_API_KEYGOOGLE_GENERATIVE_AI_API_KEY
Logging Configuration:
DEBUG_ENABLED→ Master switch for all debug loggingDEBUG_AI_SDK→ AI service operations and streamingDEBUG_MCP→ MCP server management and toolsDEBUG_DATABASE→ Database operations and migrationsDEBUG_IPC→ Inter-process communicationDEBUG_PREFERENCES→ Settings and configurationDEBUG_CORE→ Application lifecycle and errorsDEBUG_OAUTH→ OAuth flow, authorization, and tokensLOG_LEVEL→ Minimum log level (debug|info|warn|error)
- Unit/Integration: Vitest for services and utilities
- E2E: Playwright for complete user flows
- Manual Testing: Development mode with DevTools enabled
- 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
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ñaNODE_ENV=production: Minimal, solo archivo, rotación grande
Ver docs/LOGGING.md para documentación completa.
- No utilices pnpm dev
For additional development guides and best practices:
- Local MCP Development - Complete guide for developing and testing MCP servers locally with uvx, uv run, and python
- Developer Docs Index - All developer-focused documentation