dnd-hub/AGENTS.md
2026-03-16 22:15:15 -04:00

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/.tsx extensions (no .js in src)

Imports

  • Use ES modules (import/export)
  • Include .js extension 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.ts or *.test.tsx alongside 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.ts wrapper 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_URL with 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_MODE or RECAP_PROVIDERS env vars

Common Tasks

Adding a New Route

  1. Create handler in apps/server/src/routes/
  2. Register in app.ts
  3. Add TypeScript types if needed

Adding a React Component

  1. Create in apps/web/src/components/
  2. Use TypeScript with typed props
  3. Follow existing component patterns

Database Changes

  1. Update schema in src/db/migrate.ts
  2. Run npm run migrate -w @dnd-hub/server

Debug Mode

  • Web app has debug toggle to simulate player role
  • Controlled via DebugContext

Gotchas

  • .env is at monorepo root, loaded relative to workspace
  • Server uses .js extensions in imports (NodeNext module resolution)
  • Web uses JSX transform (jsx: react-jsx)
  • Both apps share Vitest but run independently
  • Clean up both .js and .ts files in web/src (legacy JS exists)