Develop real-world judgment through high-stakes AI scenarios.
# 1. Install dependencies
npm install
# 2. Set up environment
cp .env.example .env
# Edit .env and add your ANTHROPIC_API_KEY
# 3. Build the frontend
npm run build
# 4. Run the server
ANTHROPIC_API_KEY=your_key node server.jsThen open http://localhost:3001
Run two terminals:
# Terminal 1: Backend
node server.js
# Terminal 2: Frontend (Vite dev server with proxy)
npm run devFrontend runs on http://localhost:5173 and proxies /api calls to the Express server.
To demo without a real API key:
DEMO_MODE=true node server.jsOr in your .env:
DEMO_MODE=true
Demo mode returns realistic hardcoded responses that feel pressure-filled and real.
All AI calls are isolated in src/lib/ai.js. See the comment block at the top of that file for instructions on switching to OpenAI or Groq — only server.js needs to change on the backend.
forge/
├── server.js # Express backend (two routes: /api/chat, /api/debrief)
├── src/
│ ├── App.jsx # Root component, screen routing
│ ├── main.jsx # React entry point
│ ├── data/
│ │ └── scenarios.js # All scenario content (add new scenarios here)
│ ├── hooks/
│ │ └── useForge.js # Central state + API orchestration
│ ├── lib/
│ │ └── ai.js # All AI/API calls (change this to switch providers)
│ └── screens/
│ ├── HomeScreen.jsx # Scenario selection
│ ├── ScenarioScreen.jsx # Live chat interface
│ └── DebriefScreen.jsx # Growth Report visualization
├── index.html
├── vite.config.js
└── package.json
Edit src/data/scenarios.js and add a new object to the scenarios array. No other files need to change.
Each scenario needs:
id— unique stringtitle,subtitle,tags,durationMin,intensitysetup— brief situation description shown to usercharacters[]— array of{id, name, role, initial, color}systemPrompt— the full system prompt sent to ClaudeopeningMessage— the first AI message the user sees
- Vite for frontend build — The spec said "no router needed" so this is a single SPA with state-based screen switching.
- SSE streaming —
/api/chatstreams Server-Sent Events. The frontend reads them with the Fetch Streams API (supported in all modern browsers). claude-sonnet-4-5— Used for both chat and debrief. The spec saidclaude-sonnet-4-6which doesn't exist yet;claude-sonnet-4-5is the latest Sonnet.- Opening message baked into scenario data — Rather than having the backend "start" the conversation, the first character message is defined in
scenarios.jsand rendered immediately without an API call. This keeps the scenario feeling instant. - No auth/sessions — This is a single-user demo app with no persistence. Conversation state lives in React state only.