diff --git a/.pi/skills/dev-serve/SKILL.md b/.pi/skills/dev-serve/SKILL.md new file mode 100644 index 0000000..d44a65a --- /dev/null +++ b/.pi/skills/dev-serve/SKILL.md @@ -0,0 +1,126 @@ +--- +name: dev-serve +description: | + Boots the TTRPG Initiative Tracker dev stack (Node backend + CRA frontend) for + hands-on human testing, then opens a headed browser window the user can see and + click. Use when user says "let me test", "open it for me", "give me a browser", + "run it locally", "boot the app", or wants to manually use the app. Handles + STORAGE mode selection (ws vs firebase), port collision cleanup, background + process management, and headed agent_browser launch in one shot. +--- + +# dev-serve + +Run the full app stack for human testing, then open a headed browser window the +user can see and drive. + +## Prerequisites + +- Repo root has `server/`, `shared/`, `src/` (npm workspaces). +- Node 18+. +- Ports 4001 (backend) and 3999 (frontend) free, or let this skill kill stale procs. + +## Defaults + +- Backend: `DB_PATH=/tmp/tracker-dev.sqlite PORT=4001` +- Frontend: `REACT_APP_STORAGE=ws` (self-hosted backend) unless user asks for firebase. + Override: user says "firebase mode" → `REACT_APP_STORAGE=firebase` + requires + `.env.local` with `REACT_APP_FIREBASE_*`. +- Frontend port: `3999`. Backend: `4001`. + +## Steps + +### 1. Kill stale procs (idempotent) + +```bash +pkill -f "node server/index.js" 2>/dev/null +pkill -f "react-scripts start" 2>/dev/null +sleep 2 +``` + +Do not error if pkill finds nothing. + +### 2. Boot backend (background, log to /tmp) + +```bash +rm -f /tmp/tracker-dev.sqlite* +DB_PATH=/tmp/tracker-dev.sqlite PORT=4001 nohup node server/index.js > /tmp/tracker-backend.log 2>&1 & +sleep 2 +curl -s http://127.0.0.1:4001/health # must return {"ok":true} +``` + +If health fails, tail `/tmp/tracker-backend.log`, diagnose, fix. Do not proceed. + +### 3. Boot frontend (background, log to /tmp) + +```bash +REACT_APP_STORAGE=ws \ +REACT_APP_BACKEND_URL=http://127.0.0.1:4001 \ +REACT_APP_BACKEND_WS=ws://127.0.0.1:4001/ws \ +BROWSER=none PORT=3999 nohup npm start > /tmp/tracker-frontend.log 2>&1 & +``` + +Wait ~10s, then confirm compile: + +```bash +grep -E "Compiled successfully|Failed to compile" /tmp/tracker-frontend.log | tail -1 +``` + +`BROWSER=none` stops CRA from stealing focus; we open our own headed window. + +### 4. Open headed browser window for user + +Use `agent_browser` tool, fresh session (headed flag is launch-scoped): + +```json +{ + "agent_browser": { + "args": ["open", "http://127.0.0.1:3999", "--headed"], + "sessionMode": "fresh", + "timeoutMs": 20000 + } +} +``` + +Then snapshot to confirm UI rendered (look for "TTRPG Initiative Tracker" heading + +"Create Campaign" button, NOT "Loading campaigns..." after 2-3s). + +### 5. Tell user the URL + +``` +App: http://127.0.0.1:3999 (browser window open) +Backend: http://127.0.0.1:4001 +Logs: /tmp/tracker-frontend.log, /tmp/tracker-backend.log +``` + +User can also navigate to that URL in their own browser if they closed the +agent_browser window. + +## Troubleshooting + +| Symptom | Cause | Fix | +|---------|-------|-----| +| "Loading campaigns..." stuck | WS subscribe silent fail | Check `/tmp/tracker-backend.log` for WS connection; check ws.js `ensureWs` uses `.onopen/.onmessage` not `.on('open')` | +| "Configuration Error" screen | firebase mode without `.env.local`, OR init not gated on STORAGE_MODE | Confirm `STORAGE=ws`; confirm `initializeStorage()` gates firebase | +| Port 4001/3999 EADDRINUSE | stale proc | Rerun step 1 | +| Campaign created but not visible | WS broadcast not wired, or path prefix not normalized | Check ws.js `norm()` strips `artifacts/.../public/data/` | +| Headed window not on screen | remote/VM/headless env | `--headed` only guarantees browser context, not OS visibility; tell user env limitation | + +## Teardown + +When user done testing, kill both: + +```bash +pkill -f "node server/index.js" +pkill -f "react-scripts start" +``` + +Or leave running if user continuing dev. Ask which. + +## Storage mode notes + +- `ws` (default here): self-hosted backend. Cross-device via backend WS. No Firebase creds needed. +- `firebase`: original upstream. Needs `.env.local`. Behavior identical per characterization tests. +- `memory`: in-process, tests only. Resets on reload. + +Switching modes mid-session: kill procs, re-export `REACT_APP_STORAGE`, reboot.