6.3 KiB
6.3 KiB
DnD Campaign Hub - Agent Guidelines
Project Overview
Monorepo with a Node.js/Express API + Discord bot and React/Vite frontend.
Stack:
- Server: Node.js + TypeScript + Express + SQLite + Discord.js
- Web: React 19 + TypeScript + Vite + React Router
- Testing: Vitest
- Build: npm workspaces
Commands
Root (Monorepo)
npm install # Install all dependencies
npm run dev # Run both server and web in dev mode
npm run build # Build server and web
npm run test # Run all tests
Server (apps/server)
npm run dev -w @dnd-hub/server # Dev mode with hot reload (tsx)
npm run build -w @dnd-hub/server # Compile TypeScript
npm run start -w @dnd-hub/server # Run compiled server
npm run test -w @dnd-hub/server # Run server tests
npm run migrate -w @dnd-hub/server # Run DB migrations
Web (apps/web)
npm run dev -w @dnd-hub/web # Vite dev server (port 5173)
npm run build -w @dnd-hub/web # Type check + Vite build
npm run preview -w @dnd-hub/web # Preview production build
npm run test -w @dnd-hub/web # Run web tests
Running a Single Test
# Run specific test file
npm run test -w @dnd-hub/server -- test/sessionService.test.ts
npm run test -w @dnd-hub/web -- src/smoke.test.ts
# Run with filter pattern
npm run test -w @dnd-hub/server -- --run --reporter=verbose
Code Style
TypeScript
- Strict mode enabled in both apps (
strict: true) - Target ES2022, use ESNext modules
- Server:
module: NodeNext,moduleResolution: NodeNext - Web:
module: ESNext,moduleResolution: Bundler - Always use
.ts/.tsxextensions (no.jsin src)
Imports
- Use ES modules (
import/export) - Include
.jsextension for relative imports in server code - Group imports: React/hooks first, then libraries, then local imports
- Example:
import { useEffect, useState } from "react"; import { Link, Route, Routes } from "react-router-dom"; import { api } from "./api/client";
Naming Conventions
- Files: camelCase for components/services (e.g.,
CharacterSheetDrawer.tsx,sessionService.ts) - Components: PascalCase (e.g.,
CharacterPage,ToastProvider) - Functions/variables: camelCase
- Constants: UPPER_SNAKE_CASE for config values
- Types/Interfaces: PascalCase (e.g.,
Me,Campaign) - Test files:
*.test.tsor*.test.tsxalongside or near tested code
Formatting
- 2-space indentation
- Semicolons required
- Double quotes for strings (single in JSX when needed)
- Trailing commas in multi-line objects/arrays
- Max line length: ~100 chars (flexible)
React Conventions
- Functional components with hooks only (no class components)
- Custom hooks prefixed with
use(e.g.,useToast,useDebugMode) - Context providers for global state (Toast, Debug, CharacterSheet)
- Arrow functions for event handlers
- TypeScript interfaces for props
Error Handling
- Server: Use try/catch with proper HTTP status codes
- Validate inputs with Zod schemas
- Web: Use Toast context for user-facing errors
- Log errors server-side, show friendly messages client-side
- Never expose stack traces to clients
API Design
- RESTful endpoints under
/routes/ - Use Express middleware for auth/validation
- CORS configured per environment
- Health check at
GET /health
Database
- SQLite via better-sqlite3 (synchronous)
- Migrations in
src/db/migrate.ts - Path:
./data/dnd_hub.db
Environment Variables
- Load via dotenv from monorepo root
.env - Use
config.tswrapper with defaults for dev - Required vars throw on startup if missing
- Never commit
.env(use.env.example)
Testing
- Framework: Vitest
- Pattern:
describe/it/expect - Import from
vitest - Tests should be isolated and deterministic
- Smoke tests for basic functionality
Project Structure
dnd-hub/
├── apps/
│ ├── server/
│ │ ├── src/
│ │ │ ├── routes/ # Express route handlers
│ │ │ ├── services/ # Business logic
│ │ │ ├── db/ # Database schema/migrations
│ │ │ ├── discord/ # Discord bot commands
│ │ │ ├── jobs/ # Scheduled cron jobs
│ │ │ └── main.ts # Entry point
│ │ └── test/ # Test files
│ └── web/
│ ├── src/
│ │ ├── components/ # React components
│ │ ├── contexts/ # React context providers
│ │ ├── pages/ # Route page components
│ │ ├── api/ # API client
│ │ └── App.tsx # Root component
│ └── vite.config.ts
├── infra/ # Docker, nginx config
├── scripts/ # Utility scripts
├── .env.example # Environment template
└── package.json # Workspace root
Key Features
Transcript Sessions
- Discord commands:
/session start,/session stop,/session status - API endpoints:
POST /sessions/*,GET /sessions/:id/transcript - Cloud sync via
CLOUD_API_BASE_URLwith HMAC signatures
Discord Integration
- OAuth2 authentication flow
- Role-based permissions (admin/dm/player)
- Game night notifications via node-cron
Recap System
- Configurable providers: ollama (local) or claude (cloud)
- Set via
RECAP_MODEorRECAP_PROVIDERSenv vars
Common Tasks
Adding a New Route
- Create handler in
apps/server/src/routes/ - Register in
app.ts - Add TypeScript types if needed
Adding a React Component
- Create in
apps/web/src/components/ - Use TypeScript with typed props
- Follow existing component patterns
Database Changes
- Update schema in
src/db/migrate.ts - Run
npm run migrate -w @dnd-hub/server
Debug Mode
- Web app has debug toggle to simulate player role
- Controlled via DebugContext
Gotchas
.envis at monorepo root, loaded relative to workspace- Server uses
.jsextensions in imports (NodeNext module resolution) - Web uses JSX transform (
jsx: react-jsx) - Both apps share Vitest but run independently
- Clean up both
.jsand.tsfiles in web/src (legacy JS exists)