# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview Bibdle is a daily Bible verse guessing game built with SvelteKit 5. Players read a verse and try to guess which book of the Bible it comes from. The game provides feedback hints (Testament match, Section match, Adjacent book, etc.) similar to Wordle-style games. Progress is stored locally in the browser and a new verse is generated daily. You are able to use the Svelte MCP server, where you have access to comprehensive Svelte 5 and SvelteKit documentation. Here's how to use the available tools effectively: (Make sure you use the Svelte agent to execute these commands) ## Available MCP Tools: ### 1. list-sections Use this FIRST to discover all available documentation sections. Returns a structured list with titles, use_cases, and paths. When asked about Svelte or SvelteKit topics, ALWAYS use this tool at the start of the chat to find relevant sections. ### 2. get-documentation Retrieves full documentation content for specific sections. Accepts single or multiple sections. After calling the list-sections tool, you MUST analyze the returned documentation sections (especially the use_cases field) and then use the get-documentation tool to fetch ALL documentation sections that are relevant for the user's task. ### 3. svelte-autofixer Analyzes Svelte code and returns issues and suggestions. You MUST use this tool whenever writing Svelte code before sending it to the user. Keep calling it until no issues or suggestions are returned. ## Tech Stack - **Framework**: SvelteKit 5 with Svelte 5 (uses runes: `$state`, `$derived`, `$effect`, `$props`) - **Styling**: Tailwind CSS 4 - **Database**: SQLite with Drizzle ORM - **Auth**: Session-based authentication using Bun's built-in cryptographically secure functions - **Deployment**: Node.js adapter for production builds - **ML**: `@xenova/transformers` for verse embeddings (initialized in server hook) (currently disabled, was a test for a cancelled project) ## Development Commands ```bash # Start development server bun run dev # Type checking bun run check bun run check:watch # Run tests bun test bun test --watch bun test tests/timezone-handling.test.ts # Run a single test file # Build for production bun run build # Preview production build bun run preview # Database operations bun run db:push # Push schema changes directly (avoid in prod) bun run db:generate # Generate migrations bun run db:migrate # Run migrations bun run db:studio # Open Drizzle Studio GUI ``` ## Critical: Date/Time Handling **Bibdle is played by users across many timezones worldwide. The verse shown to a player must always be the verse for the calendar date at *their* location — not the server's timezone, not UTC. A user in Tokyo on Wednesday must see Wednesday's verse, even if the server (or a user in New York) is still on Tuesday.** **NEVER use server time or UTC time for user-facing date calculations.** - Get today's date client-side: `new Date().toLocaleDateString("en-CA")` → `YYYY-MM-DD` - Pass the date to the server as a query param or POST body (`localDate`) - Server-side date arithmetic must use UTC methods on the client-provided date string: `new Date(dateStr + 'T00:00:00Z')` + `setUTCDate`/`getUTCDate` - `src/routes/+page.ts` has `ssr = false` so the load runs client-side with the true local date - Never set the user-facing URL to include their date as a parameter. It should always be passed to an API route behind the scenes if needed. ### Streak Calculation A streak counts consecutive calendar days (in the user's local timezone) on which the user completed the puzzle. The rules: - The client passes its local date (`localDate`) to the streak API. The server never uses its own clock. - A streak is **active** if the user has completed today's puzzle *or* yesterday's puzzle (they still have time to play today). - Walk backwards from `localDate` through the `dailyCompletions` records, counting each day that has a completion. Stop as soon as a day is missing. - A streak of 1 (completed only today or only yesterday, with no prior consecutive days) is **not displayed** — the minimum shown streak is 2. - "Yesterday" and all date arithmetic on the server must use UTC methods on the client-provided date string to avoid timezone drift: `new Date(localDate + 'T00:00:00Z')`, then `setUTCDate`/`getUTCDate`. ## Architecture ### Database Schema (`src/lib/server/db/schema.ts`) - **user**: `id`, `firstName`, `lastName`, `email` (unique), `passwordHash`, `appleId` (unique), `isPrivate` - **session**: `id` (SHA-256 hash of token), `userId` (FK), `expiresAt` - **daily_verses**: Cached daily verses with book ID, verse text, reference, and date - **dailyCompletions**: Game results per user/date with guess count, grade, book; unique on `(userId, date)` Sessions expire after 30 days and auto-renew when < 15 days remain. ### Bible Data (`src/lib/types/bible.ts`) The `bibleBooks` array contains all 66 Bible books with metadata: - Testament (old/new), Section (Law, History, Wisdom, Prophets, Gospels, Epistles, Apocalyptic) - Order (1-66, used for adjacency detection) ### Daily Verse System (`src/routes/+page.server.ts`) `getTodayVerse()` checks the database for today's date, fetches a verse if missing, caches permanently, and returns verse with book metadata. ### Game Logic (`src/routes/+page.svelte`) **State Management:** - `guesses` array stored in localStorage keyed by date: `bibdle-guesses-${date}` - Each guess tracks: book, testamentMatch, sectionMatch, adjacent - `isWon` derived from whether any guess matches the correct book **Hint System, for share grid:** - ✅ Exact match | 🟩 Section match | 🟧 Testament match | ‼️ Adjacent book | 🟥 No match ### Authentication System (`src/lib/server/auth.ts`) - Token generation: base64-encoded random bytes; stored as SHA-256 hash in DB - Cookie name: `auth-session` - Anonymous users: identified by a client-generated ID; stats migrate on sign-up via `migrateAnonymousStats()` - Apple Sign-In supported via `appleId` field ### Stats & Streak (`src/routes/stats/`) - Stats page requires auth; returns `requiresAuth: true` if unauthenticated - Streak calculated client-side by calling `GET /api/streak?userId=X&localDate=Y` - Streak walk-back: counts consecutive days backwards from `localDate` through completed dates - Minimum displayed streak is 2 (single-day streaks suppressed) ## API Endpoints - `POST /api/daily-verse` — Fetch verse for a specific date - `POST /api/submit-completion` — Submit game result with stats - `GET /api/streak?userId=X&localDate=Y` — Current streak for user - `GET /api/streak-percentile` — Streak percentile ranking ## Key Files - `src/routes/+page.svelte` — Main game UI and client-side logic - `src/routes/+page.server.ts` / `+page.ts` — Server load (verse) + client load (`ssr: false`) - `src/routes/stats/+page.svelte` / `+page.server.ts` — Stats UI and server calculations - `src/lib/server/auth.ts` — Session management, password hashing, anonymous migration - `src/lib/server/bible-api.ts` — Random verse fetching from local XML Bible - `src/lib/server/bible.ts` — Bible book utility functions - `src/lib/types/bible.ts` — Bible books data and TypeScript types - `src/lib/server/db/schema.ts` — Drizzle ORM schema - `src/hooks.server.ts` — Session validation hook; initializes ML embeddings - `tests/` — Bun test suites (timezone, game, bible, stats, share, auth migration) ## Environment Variables Required in `.env`: - `DATABASE_URL` — Path to SQLite database file (e.g., `./local.db`) ## Deployment Uses `@sveltejs/adapter-node`. See `bibdle.service` systemd configuration. ## A Note The main developer of this project is still learning a lot about developing full-stack applications. If they ask you to do something, make sure they understand how it will be implemented before proceeding.