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

193 lines
6.3 KiB
Markdown

# 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)
```bash
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`)
```bash
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`)
```bash
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
```bash
# 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:
```typescript
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)