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 torouteTree.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.tsviaapp.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 ine2e/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/itemsreplaces all setup_items atomically (delete all, re-insert). - Image uploads:
POST /api/imagessaves to S3-compatible storage (Garage/R2), returned asimageFilenameon item/candidate records. - Image URL fetching:
POST /api/images/from-urlfetches 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-Keyheader) for programmatic access.POST /api/auth/setupfor first-time account creation. Auth middleware protects all POST/PUT/DELETE on/api/*except/api/auth/*.
Authentication
- First run: No users exist. Visit
/loginto 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-Keyheader. - 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 metadataPOST /oauth/register— Dynamic Client RegistrationGET/POST /oauth/authorize— Authorization with login formPOST /oauth/token— Token exchange and refresh
Both auth methods work simultaneously:
- API key (
X-API-Keyheader) — Claude Code, scripts, programmatic access - OAuth Bearer token (
Authorization: Bearerheader) — Claude mobile, claude.ai