Files
GearBox/CLAUDE.md
2026-04-04 09:27:34 +02:00

190 lines
8.7 KiB
Markdown

# 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 <GITEA_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": "<your-api-key>"
}
}
}
}
```
**Claude Desktop** (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"gearbox": {
"type": "streamable-http",
"url": "http://localhost:3000/mcp",
"headers": {
"X-API-Key": "<your-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