Instructions for AI coding agents operating in this repository.
Hive -- Electron desktop app for managing git projects/worktrees with integrated AI coding sessions. Stack: Electron 33, React 19, TypeScript 5.7, Tailwind CSS 4, Zustand 5, SQLite (better-sqlite3).
pnpm dev # Start dev with hot reload
pnpm build # Production build to out/
pnpm lint # ESLint check
pnpm lint:fix # ESLint auto-fix
pnpm format # Prettier format src/**/*.{ts,tsx,css}
pnpm test # Vitest run all tests
pnpm test:watch # Vitest watch mode
pnpm test:e2e # Playwright E2E tests
pnpm build:mac # Package for macOSRun a single test file:
pnpm vitest run test/path/to/file.test.tsAlways use pnpm -- never npm or yarn.
Three-process Electron model:
| Directory | Context | Alias |
|---|---|---|
src/main/ |
Node.js (main) | @main/ |
src/preload/ |
Isolated bridge | @preload/ |
src/renderer/ |
Browser (React) | @/ |
The renderer is sandboxed (sandbox: true, contextIsolation: true). All cross-process communication uses typed IPC through the preload layer.
Data flow: Component -> window.{namespace}.{method}() -> preload ipcRenderer.invoke -> main handler -> returns result -> store updates -> re-render.
- No semicolons
- Single quotes
- No trailing commas
- 100 char print width
- 2-space indent
- Strict mode via
@electron-toolkit/tsconfig - Prefix unused variables/params with
_(ESLint:argsIgnorePattern: '^_') - Use explicit types for function params; return types optional for components
- Error narrowing:
error instanceof Error ? error.message : String(error) - Catch blocks for non-critical failures may use bare
catch { }(no variable)
Renderer code: Use @/ alias exclusively.
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import { useProjectStore, useWorktreeStore } from '@/stores'Main process: Use relative imports or @main/ alias.
import { getDatabase } from '../db'
import { createLogger } from '../services/logger'Preload: Import directly from 'electron'.
| Entity | Convention | Example |
|---|---|---|
| Components | PascalCase | ProjectItem, AppLayout |
| Stores | useXxxStore |
useProjectStore |
| IPC handler files | kebab-case | project-handlers.ts |
| Service files | kebab-case | git-service.ts |
| Type interfaces | PascalCase | Project, ProjectCreate |
| IPC channels | colon-delim | db:project:create, git:stageFile |
- Functional components only, no class components
- Use
cn()from@/lib/utilsfor conditional classNames - UI primitives from shadcn/ui (new-york style, zinc base)
- Icons from
lucide-react - Toasts via
sonner(toast.success(),toast.error()) - Wrap major sections in
ErrorBoundary - Add
data-testidattributes for testable elements
Pattern: state + actions in a single interface, async actions call window.* APIs.
create<State>()(
persist(
(set, get) => ({
/* state + actions */
}),
{ name: 'hive-xxx', storage: createJSONStorage(() => localStorage) }
)
)All stores exported from src/renderer/src/stores/index.ts.
- Main process: try/catch with
log.error(), re-throw or return{ success: false, error }. UsewithErrorHandlerwrapper where available (seedatabase-handlers.ts). - Renderer stores: try/catch returning
{ success: boolean; error?: string }. - Components: ErrorBoundary wrapping, toast notifications for user-facing errors.
src/preload/index.d.ts is the single source of truth for shared types between processes. All entity types (Project, Worktree, Session, SessionMessage, Setting) and the full Window interface with all 12 API namespaces are defined there.
Main-process-only types live in src/main/db/types.ts with Create/Update variants.
- Add handler in
src/main/ipc/-- useipcMain.handle(channel, handler)pattern - Register in
src/main/index.tsif creating a new handler module - Expose in
src/preload/index.tsunder the appropriatewindow.*namespace - Add type declaration in
src/preload/index.d.ts - Call from renderer via
window.{namespace}.{method}()
SQLite via better-sqlite3. Singleton DatabaseService. WAL mode, foreign keys ON.
Schema version tracked in CURRENT_SCHEMA_VERSION in src/main/db/schema.ts.
To add a migration: append to the MIGRATIONS array, bump CURRENT_SCHEMA_VERSION.
- Framework: Vitest (jsdom environment for renderer tests, node for main)
- Libraries:
@testing-library/react,@testing-library/jest-dom,@testing-library/user-event - Setup:
test/setup.tsmockswindow.matchMedia,window.gitOps,window.fileTreeOps - Mock pattern: Use
Object.defineProperty(window, 'apiName', { value: { method: vi.fn() } }) - Tests live in:
test/directory, organized by phase/session subdirectories - Globals:
vitestglobals enabled -- no need to importdescribe,it,expect
- shadcn/ui: new-york style, zinc base, CSS variables. Add components:
pnpm dlx shadcn@latest add <component> - Tailwind CSS 4:
@tailwindcss/viteplugin,@import "tailwindcss"+@theme inlinepattern - Theming: 10 presets (6 dark, 4 light) via CSS variables in
src/renderer/src/lib/themes.ts
Main process uses createLogger({ component: 'Name' }) from src/main/services/logger.ts.
Logs to ~/.hive/logs/hive-YYYY-MM-DD.log. Max 5MB/file, 5 files retained.