Skip to content

Latest commit

 

History

History
154 lines (107 loc) · 9.77 KB

File metadata and controls

154 lines (107 loc) · 9.77 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Session Memory Protocol

At the start of every session, read the files in /memory/ to restore context:

  • memory/user.md — who the user is, their background and working style
  • memory/preferences.md — coding and workflow preferences for this project
  • memory/decisions.md — past architectural and technical decisions
  • memory/people.md — collaborators and stakeholders

At the end of every session (when the user says goodbye, wraps up, or asks to end), update the relevant memory files with anything learned during the session: new decisions made, preferences expressed, feedback given, or new information about people involved.


Project Overview

Questarr is a video game management app inspired by the *Arr ecosystem (Sonarr, Radarr). Users discover, track, and download games via automated indexer search and download client integration. Dark-themed UI built around visual game covers.

Design Context

Users

Questarr is for self-hosting game collectors and power users who manage discovery, tracking, and downloads from a browser-based dashboard. On mobile, they are often checking status, searching for a title, reviewing availability, or triggering a quick action while away from their desk. The primary mobile job to be done is fast, confident control of the library from a phone in portrait orientation, with tablet support as a secondary context.

Brand Personality

Questarr should feel focused, capable, and trustworthy. The current product language already points to a privacy-conscious, dark-first, cover-led interface built for enthusiasts; on mobile that should translate into a fast and utility-first experience rather than a decorative or playful one. The emotional goal is confidence through clarity: users should feel that core actions are always within reach and never fragile on small screens.

Aesthetic Direction

Keep the dark-first, media-rich Questarr identity, but adapt it into a denser thumb-friendly control surface for phones. Preserve strong cover art, blue primary accents, and clear status color cues, while reducing visual chrome, avoiding cramped multi-column layouts, and prioritizing stacked flows, sticky context, and touchable surfaces. Mobile should feel like a deliberate field console for managing a game library, not a scaled-down desktop admin panel.

Design Principles

  1. Thumb-first navigation: primary actions and key navigation must stay reachable and comfortable with one-handed use.
  2. Speed over ornament: mobile layouts should surface the next useful action immediately and avoid decorative complexity that slows scanning.
  3. Preserve capability: core desktop functionality must remain available on mobile, but reorganized through progressive disclosure instead of compression.
  4. Cover-led clarity: game art and status signals should remain the visual anchor, with metadata condensed into scannable layers.
  5. Touch-safe density: interfaces can stay information-rich, but all controls need forgiving spacing and touch targets sized for real phones.

Commands

npm run dev              # Dev server with hot reload (port 5000)
npm run build            # Production build: Vite (client) + tsc (server/shared)
npm start                # Run production server from dist/
npm run check            # TypeScript type checking (no emit)

npm run lint             # ESLint
npm run lint:fix         # ESLint with auto-fix
npm run format           # Prettier format all files
npm run format:check     # Prettier check only

npm test                 # Vitest watch mode
npm run test:run         # Run all tests once
npm run test:coverage    # Coverage report (v8, HTML output)
npm run test:e2e         # Playwright E2E tests (requires dev:test running)
npm run dev:test         # Dev server with test DB on port 5100

# Run a single test file
npx vitest run server/__tests__/api_routes.test.ts

# Run tests matching a name pattern
npx vitest -t "pattern"

npm run db:generate      # Generate Drizzle migration from schema changes
npm run db:migrate       # Run pending migrations
npm run db:push          # Push schema directly (dev only)

Architecture

Three-layer TypeScript app with a single package.json (not a monorepo):

  • /client/src — React 18 SPA. Wouter routing, TanStack React Query for server state, shadcn/ui + Radix primitives, Tailwind CSS 4. Pages are code-split with React.lazy. Pages: library, discover, search, wishlist, calendar, downloads, indexers, downloaders, rss, xrel-releases, stats, settings.
  • /server — Express REST API + Socket.io WebSockets. JWT auth (bcryptjs), express-validator for input validation, Pino logging, SSRF-protected fetch.
  • /shared — Drizzle ORM schema (schema.ts), Zod validation schemas (derived from Drizzle), game title normalization (title-utils.ts), download categorization.

Key backend modules

File Purpose
routes.ts All API endpoints (~3360 lines, organized by domain)
storage.ts Database access layer (Drizzle queries)
downloaders.ts Multi-client download management (qBittorrent, Transmission, rTorrent, sabnzbd, nzbget)
igdb.ts IGDB API client with in-memory cache
search.ts Aggregated Torznab/Newznab indexer search
cron.ts Scheduled jobs (auto-search, download checks, xREL monitoring, game updates)
middleware.ts Rate limiters, validators, sanitizers
ssrf.ts SSRF URL validation (DNS rebinding, cloud metadata filtering)
nexusmods.ts NexusMods API client — mod search and trending mods per game
steam.ts Steam wishlist import and Steam App ID resolution
steam-routes.ts Express router for Steam endpoints
pcgamingwiki-router.ts PCGamingWiki URL lookup via Steam App ID (CargoQuery API, 24h cache)
config.ts System-wide configuration access layer

Data flow

  1. Frontend uses React Query to call Express REST endpoints
  2. Routes validate input (express-validator + Zod), call storage/service layers
  3. Storage layer uses Drizzle ORM against SQLite (better-sqlite3)
  4. Real-time updates pushed via Socket.io (download progress, notifications)

Database

SQLite with Drizzle ORM. Schema defined in shared/schema.ts. Migrations in /migrations/. Key tables: users, userSettings, systemConfig, games, indexers, downloaders, gameDownloads, notifications, rssFeeds, rssFeedItems, xrelNotifiedReleases, releaseBlacklist.

Notable game fields added: steamAppId, hidden (boolean), userRating (0.5–10 scale), source ("manual" | "steam" | "api"). User fields: steamId64. UserSettings fields: preferredReleaseGroups, steamSyncFailures.

Code Conventions

  • TypeScript strict mode, no any. Unused params prefixed with _.
  • Path aliases: @/*client/src/*, @shared/*shared/*
  • ES modules throughout ("type": "module" in package.json)
  • Prettier: 100 char width, 2 spaces, trailing comma ES5
  • Pre-commit hooks: Husky + lint-staged runs ESLint + Prettier on staged files
  • Commit messages: Start with a verb ("Add", "Fix", "Update"), reference issues when applicable
  • Frontend styling: Tailwind CSS utility classes, dark-first theme with CSS variables. Colors: primary blue #3B82F6, secondary emerald #10B981, background dark slate #1F2937.
  • Components: Functional, TypeScript interfaces for props, Radix UI for interactive primitives

React Performance

  • Wrap expensive computations in useMemo; wrap stable callbacks passed as props or used in dependency arrays in useCallback.
  • Prefer deriving state via useMemo over storing redundant state that can be computed from existing state/props.

Accessibility

  • All interactive elements must have an accessible label: use aria-label on icon-only buttons/controls, htmlFor + id pairs on form fields, and aria-labelledby when a visible heading already describes the region.
  • Use semantic HTML elements (<button>, <nav>, <main>, <section>) rather than <div> with click handlers.
  • Radix UI primitives handle most ARIA roles automatically — avoid duplicating role attributes they already set.

Testing

  • Unit tests: Vitest with @testing-library/react (client) and supertest (server). Tests use in-memory SQLite.
  • E2E tests: Playwright. Run against dev:test server on port 5100.
  • Test files: server/__tests__/ and client/__tests__/
  • Setup: tests/setup.ts provides ResizeObserver mocks and test env vars

Environment Variables

Key vars (see .env.example):

  • IGDB_CLIENT_ID, IGDB_CLIENT_SECRET — IGDB/Twitch API credentials
  • SQLITE_DB_PATH — Database file path (default: sqlite.db)
  • JWT_SECRET — JWT signing secret (auto-generated if unset)
  • PORT — Server port (default: 5000)
  • NODE_ENVdevelopment | production | test

Stack Overflow for Agents (SOFA)

Skill: .claude/sofa-skill.md. Credentials: .sofa/credentials.json (gitignored). Read the skill before interacting with SOFA.