# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview GearBox is a single-user web app for managing gear collections (bikepacking, sim racing, etc.), tracking weight/price, and planning purchases through research threads. Full-stack TypeScript monolith running on Bun. ## Commands ```bash # Development bun run dev # Starts both Vite client (:5173) and Hono server (:3000) concurrently bun run dev:client # Vite dev server on :5173 (proxies /api to :3000) bun run dev:server # Hono server on :3000 with hot reload # Database bun run db:generate # Generate Drizzle migration from schema changes bun run db:push # Apply migrations to gearbox.db # Testing bun test # Run all unit/integration tests bun test tests/services/item.service.test.ts # Run single test file bun run test:e2e # Run Playwright E2E tests bun run test:e2e:ui # Playwright UI mode for debugging # Lint & Format bun run lint # Biome check (tabs, double quotes, organized imports) # Build bun run build # Vite build → dist/client/ ``` ## Architecture **Stack**: React 19 + Hono + Drizzle ORM + SQLite, all running on Bun. ### Client (`src/client/`) - **Routing**: TanStack Router with file-based routes in `src/client/routes/`. Route tree auto-generated to `routeTree.gen.ts` — never edit manually. - **Data fetching**: TanStack React Query via custom hooks in `src/client/hooks/` (e.g., `useItems`, `useThreads`, `useSetups`). Mutations invalidate related query keys. - **UI state**: Zustand store (`stores/uiStore.ts`) for panel/dialog state only — server data lives in React Query. - **API calls**: Thin fetch wrapper in `lib/api.ts` (`apiGet`, `apiPost`, `apiPut`, `apiDelete`, `apiUpload`). - **Styling**: Tailwind CSS v4. ### Server (`src/server/`) - **Routes** (`routes/`): Hono handlers with Zod validation via `@hono/zod-validator`. Delegate to services. - **Services** (`services/`): Pure business logic functions that take a db instance. No HTTP awareness — testable without mocking. - Route registration in `src/server/index.ts` via `app.route("/api/...", routes)`. ### Shared (`src/shared/`) - **`schemas.ts`**: Zod schemas for API request validation (source of truth for types). - **`types.ts`**: Types inferred from Zod schemas + Drizzle table definitions. No manual type duplication. ### Database (`src/db/`) - **Schema**: `schema.ts` — Drizzle table definitions for SQLite. - **Prices stored as cents** (`priceCents: integer`) to avoid float rounding. - **Timestamps**: stored as integers (unix epoch) with `{ mode: "timestamp" }`. - Tables: `categories`, `items`, `threads`, `threadCandidates`, `setups`, `setupItems`, `settings`, `users`, `sessions`, `apiKeys`. ### Testing (`tests/` and `e2e/`) - **Unit/integration**: Bun test runner (`bun test`). Tests at service level and route level. - `tests/helpers/db.ts`: `createTestDb()` creates in-memory SQLite via Drizzle migrations and seeds an "Uncategorized" category. - **E2E**: Playwright (`bun run test:e2e`). Tests in `e2e/` run against a seeded SQLite database with the server in production mode. Seed script: `e2e/seed.ts`. ## Releasing Releases are managed by a Gitea Actions workflow (`.gitea/workflows/release.yml`). **Never create tags or releases manually** — always trigger the pipeline. The workflow runs CI (lint, test, build), computes the next version from the latest tag, generates a changelog, creates the tag, builds and pushes a Docker image, and creates a Gitea release. Trigger via Gitea API: ```bash curl -s -X POST "https://gitea.jeanlucmakiola.de/api/v1/repos/makiolaj/GearBox/actions/workflows/release.yml/dispatches" \ -H "Authorization: token " \ -H "Content-Type: application/json" \ -d '{"ref": "Develop", "inputs": {"bump": "patch"}}' # patch | minor | major ``` ## Branching - **Develop** is the main branch. Keep it clean — don't commit large feature work directly. - For each new brainstorming/implementation session, create a feature branch off Develop (e.g., `feature/setup-impact-preview`, `fix/error-handling`). - Merge back to Develop via PR or fast-forward merge when the work is complete and verified. ## Path Alias `@/*` maps to `./src/*` (configured in tsconfig.json). ## Key Patterns - **Thread resolution**: Resolving a thread copies the winning candidate's data into a new item in the collection, sets `resolvedCandidateId`, and changes status to "resolved". - **Setup item sync**: `PUT /api/setups/:id/items` replaces all setup_items atomically (delete all, re-insert). - **Image uploads**: `POST /api/images` saves to `./uploads/` with UUID filename, returned as `imageFilename` on item/candidate records. - **Image URL fetching**: `POST /api/images/from-url` fetches an image from a URL, saves locally, returns `{ filename, sourceUrl }`. - **Aggregates** (weight/cost totals): Computed via SQL on read, not stored on records. - **Authentication**: Public-read, authenticated-write. Cookie sessions for web UI, API keys (`X-API-Key` header) for programmatic access. `POST /api/auth/setup` for first-time account creation. Auth middleware protects all POST/PUT/DELETE on `/api/*` except `/api/auth/*`. ## Authentication - **First run**: No users exist. Visit `/login` to create your admin account. - **Web UI**: Cookie-based sessions (`gearbox_session`), 30-day expiry, auto-refreshed. - **Programmatic access**: API keys created in Settings > API Keys. Pass via `X-API-Key` header. - **Public read**: All GET endpoints work without auth. POST/PUT/DELETE require auth. - **Auth routes**: `/api/auth/login`, `/api/auth/logout`, `/api/auth/setup`, `/api/auth/me`, `/api/auth/password`, `/api/auth/keys`. - **MCP OAuth**: OAuth 2.1 + PKCE for Claude mobile/web. Endpoints at `/oauth/*`. Uses existing GearBox credentials. ## MCP Server GearBox includes a built-in MCP server for integration with Claude Code and Claude Desktop. Enabled by default, disable with `GEARBOX_MCP=false`. Authenticated via API key or OAuth 2.1 Bearer token. ### Tools (19 total) | Tool | Description | |------|-------------| | `list_items` | List all items, optionally filter by category | | `get_item` | Get item details by ID | | `create_item` | Add item to collection (for decided items; use `create_thread` for research) | | `update_item` | Update item details | | `delete_item` | Remove item from collection | | `list_categories` | List all categories | | `create_category` | Create a new category | | `list_threads` | List research threads (recommended workflow for gear purchases) | | `get_thread` | Get thread with candidates | | `create_thread` | Start a research thread to compare gear options | | `resolve_thread` | Pick winning candidate, adds it to collection | | `add_candidate` | Add candidate to a research thread | | `update_candidate` | Update candidate details | | `remove_candidate` | Remove candidate from thread | | `list_setups` | List gear setups | | `get_setup` | Get setup with items and totals | | `create_setup` | Create a new setup | | `update_setup` | Update setup name or items | | `upload_image_from_url` | Fetch image from URL, save locally | ### Resources - `gearbox://collection/summary` — Overview of collection: totals, items per category, active threads. ### Configuration **Claude Code** (`.claude/settings.json`): ```json { "mcpServers": { "gearbox": { "type": "streamable-http", "url": "http://localhost:3000/mcp", "headers": { "X-API-Key": "" } } } } ``` **Claude Desktop** (`claude_desktop_config.json`): ```json { "mcpServers": { "gearbox": { "type": "streamable-http", "url": "http://localhost:3000/mcp", "headers": { "X-API-Key": "" } } } } ``` Generate an API key from Settings > API Keys after logging in. ### OAuth Authentication (Claude Mobile / claude.ai) GearBox supports OAuth 2.1 Authorization Code + PKCE for MCP connections from Claude mobile app and claude.ai. The OAuth flow is automatic — Claude handles discovery and token exchange. **Required environment variable for production:** ```bash GEARBOX_URL=https://your-gearbox-domain.com # Used as OAuth issuer URL ``` **OAuth endpoints:** - `GET /.well-known/oauth-authorization-server` — Discovery metadata - `POST /oauth/register` — Dynamic Client Registration - `GET/POST /oauth/authorize` — Authorization with login form - `POST /oauth/token` — Token exchange and refresh **Both auth methods work simultaneously:** - **API key** (`X-API-Key` header) — Claude Code, scripts, programmatic access - **OAuth Bearer token** (`Authorization: Bearer` header) — Claude mobile, claude.ai