Files
GearBox/CLAUDE.md
2026-04-07 15:28:34 +02:00

9.3 KiB

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

# 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:

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).

Reusable Components

Always use existing components instead of rebuilding with plain HTML. Check src/client/components/ before creating new form elements or UI patterns.

Need Use Not
Category selection CategoryPicker (icons, search, inline create) Plain <select>
Icon selection IconPicker (119 curated Lucide icons, grouped) Manual icon input
Image upload ImageUpload (preview, click-to-upload) Raw file input
Lucide icon rendering LucideIcon from lib/iconData Direct SVG or lucide-react imports
Weight/price formatting useFormatters() hook Manual formatting

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 S3-compatible storage (Garage/R2), returned as imageFilename on item/candidate records.
  • Image URL fetching: POST /api/images/from-url fetches an image from a URL, saves to S3, 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):

{
  "mcpServers": {
    "gearbox": {
      "type": "streamable-http",
      "url": "http://localhost:3000/mcp",
      "headers": {
        "X-API-Key": "<your-api-key>"
      }
    }
  }
}

Claude Desktop (claude_desktop_config.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:

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