These instructions are for AI assistants working in this project.
Always open @/openspec/AGENTS.md when the request:
- Mentions planning or proposals (words like proposal, spec, change, plan)
- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
- Sounds ambiguous and you need the authoritative spec before coding
Use @/openspec/AGENTS.md to learn:
- How to create and apply change proposals
- Spec format and conventions
- Project structure and guidelines
Keep this managed block so 'openspec update' can refresh the instructions.
This file provides guidance to Claude Code when working with this repository.
Model Faceoff is an Electron desktop application that lets you compare AI models from different providers side-by-side. It features streaming responses, markdown rendering, conversation history, and usage tracking via OpenRouter API.
- Electron - Desktop framework
- React 19 - UI framework
- TypeScript - Type safety
- Vite - Build tool
- Tailwind CSS - Styling
- better-sqlite3 - SQLite database
- electron-updater - Auto-updates
- electron-forge - Build/packaging
npm run dev # Start development server with hot reload
npm run start # Clean build and start dev server
npm run make # Build distributable packages for current platform
npm run publish # Build and publish to GitHub Releases
npm run lint # Run ESLint
npm run format # Format with Prettier
npm run test # Run tests-
Main Process (
src/main.ts)- Node.js environment
- Creates browser windows
- Handles system APIs (file dialogs, menus)
- Manages auto-updates
- Initializes database
-
Preload Script (
src/preload.ts)- Bridge between main and renderer
- Exposes
window.apiobject - Uses contextBridge for security
-
Renderer Process (
src/renderer/)- React application
- Isolated browser context
- Communicates via
window.api
src/database/- SQLite connection, config, and schemasrc/ipc/handlers.ts- IPC handlers for main processsrc/types/window.ts- Type definitions forwindow.apisrc/renderer/- React components and styles
Uses SQLite with better-sqlite3. WAL mode enabled for concurrent access.
Tables:
settings- Key-value store for app configuration
Schema defined in src/database/schema.ts.
All IPC handlers return IPCResponse<T>:
interface IPCResponse<T> {
success: boolean;
data?: T;
error?: { code: string; message: string };
}-
Add handler in
src/ipc/handlers.ts:ipcMain.handle('myFeature:action', async (_, arg) => { try { return { success: true, data: result }; } catch (error) { return { success: false, error: { code: 'ERROR', message: String(error) } }; } });
-
Add method in
src/preload.ts:const myFeatureAPI = { action: (arg: string) => ipcRenderer.invoke('myFeature:action', arg), };
-
Add types in
src/types/window.ts -
Add to
contextBridge.exposeInMainWorld()
The single source of truth for app configuration. This file is committed to the repo.
export const config = {
productName: 'Model Faceoff',
executableName: 'model-faceoff',
appBundleId: 'com.modelfaceoff.app',
appDataFolder: 'ModelFaceoff',
dbFilename: 'app.db',
github: {
owner: 'dotnetfactory',
repo: 'model-faceoff',
private: false,
},
autoUpdateEnabled: true,
maintainer: 'Emad Ibrahim <emad@emadibrahim.com>',
};The config is injected at build time via Vite's define and available as __APP_CONFIG__ globally.
.env is for secrets only (gitignored):
GH_TOKEN- GitHub PAT for private repo accessAPPLE_*- macOS signing credentials
forge.config.ts- Importsapp.config.tsfor build settingsvite.main.config.ts- Injects__APP_CONFIG__into main processvite.renderer.config.ts- Injects__APP_CONFIG__into renderervite.preload.config.ts- Preload script build config
app.config.ts- App configuration (single source of truth)src/main.ts- Main process entry, window creation, auto-updatersrc/preload.ts- API bridge exposed to renderersrc/ipc/handlers.ts- All IPC handlerssrc/database/schema.ts- Database table definitionssrc/renderer/App.tsx- Main React componentforge.config.ts- Build and packaging configuration
The web app is located at ../model-faceoff-web