|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## What this is |
| 6 | + |
| 7 | +Text prompt → Beyond All Reason (BAR) minigame config (JSON) generator. A natural-language |
| 8 | +query is matched against a database of existing game scenarios, then a final scenario config is |
| 9 | +synthesized and visualized in the browser. The README is in Korean and is the canonical design doc. |
| 10 | + |
| 11 | +## Commands |
| 12 | + |
| 13 | +Backend (FastAPI, Python 3.11): |
| 14 | +```bash |
| 15 | +cd backend |
| 16 | +pip install -r requirements.txt |
| 17 | +cp .env.example .env # set OPENAI_API_KEY (BASE_URL optional) |
| 18 | +uvicorn app:app --reload # http://localhost:8000 |
| 19 | +python pipeline.py "<prompt>" # run the full pipeline from the CLI, no server |
| 20 | +python db_call.py # smoke-test DB folder/file LLM routing |
| 21 | +``` |
| 22 | + |
| 23 | +Frontend (React + Vite, requires Node 20): |
| 24 | +```bash |
| 25 | +cd frontend |
| 26 | +npm install |
| 27 | +npm run dev # http://localhost:5173, proxies /api -> :8000 |
| 28 | +npm run build # production build (this is what CI runs) |
| 29 | +``` |
| 30 | + |
| 31 | +There is no test suite. CI (`.github/workflows/ci.yml`, on push/PR to `main`/`dev`) only does |
| 32 | +`npm run build` for the frontend and, for the backend, `python -m compileall` + `import app` |
| 33 | +with a dummy `OPENAI_API_KEY`. **Module import must stay side-effect-free** (no LLM client built |
| 34 | +at import time) or CI breaks — clients are constructed lazily inside functions/constructors. |
| 35 | + |
| 36 | +## Architecture |
| 37 | + |
| 38 | +The core design decision: **do not generate a GDD or new rules from scratch.** Find the most |
| 39 | +similar existing scenario in `backend/db/`, reuse its validated rules, and only synthesize the |
| 40 | +final script. An earlier multi-agent build with an analyst/verify loop was deliberately stripped |
| 41 | +to remove the `game_simulation` (BAR engine) and `psutil` runtime dependencies — do not |
| 42 | +reintroduce them. |
| 43 | + |
| 44 | +### Backend pipeline (`backend/pipeline.py`, `generate()`) — 3 steps |
| 45 | + |
| 46 | +1. **`find_scenario(query)`** — match the prompt to a scenario in `db/scenario/meta.json`. |
| 47 | + Resilient by design: LLM match (`DBCall`) → keyword overlap fallback (`_keyword_fallback`) → |
| 48 | + default to the first scenario. Returns `None` only if the scenario DB is unreadable. |
| 49 | +2. **`load_existing_mode(name)`** — build a `gdd` dict from the scenario's `specification` and its |
| 50 | + referenced rules. Every rule is loaded as `action: existing, validated: True` (never generated). |
| 51 | + Rule `.lua` files are copied into the per-session `log/<timestamp>/.../rules_temp/`. |
| 52 | +3. **`ScriptDeveloperAgent.run()`** (`script_builder.py`) — a linear LangGraph: |
| 53 | + `select_map → place_units → generate_rule_config → get_condition → assemble_draft`. Each node |
| 54 | + is one LLM call (prompts in `developer_prompt.py`); `select_map`/`place_units` loop, issuing |
| 55 | + `call_db` actions to fetch more context, until the LLM returns `finish`. |
| 56 | + |
| 57 | +`generate()` returns `{scenario, config, raw}` where `config` is the `"normal"` difficulty entry of |
| 58 | +`final_json`. The agent always produces exactly the `["normal"]` difficulty. |
| 59 | + |
| 60 | +### DBCall (`backend/db_call.py`) — two-stage LLM retrieval |
| 61 | + |
| 62 | +`db/meta.json` lists folders (`scenario`, `map`, `unit`, `rule`, `decision`, `verify`, `rubric`); |
| 63 | +each folder's `meta.json` has a `details` map of `{name: {description, file}}`. `DBCall.call(query)` |
| 64 | +asks the LLM to pick a folder, then which files within it — returning file paths relative to `db/`. |
| 65 | +Pass `folder=` to skip folder selection. `call_with_names()` also returns the matched DB key names |
| 66 | +(used by `find_scenario`). |
| 67 | + |
| 68 | +### API (`backend/app.py`) |
| 69 | + |
| 70 | +- `POST /generate {query, seed?}` → pipeline result. `seed` is threaded down to every `ChatOpenAI` |
| 71 | + for reproducibility. |
| 72 | +- `GET /catalog` → scenarios (from `db/scenario/meta.json`) + maps (from `info/map.json`). |
| 73 | +- `GET /health`. CORS is wide open (`*`). |
| 74 | +- The top-level `try/except` returns `{error, config: None}` rather than raising. |
| 75 | + |
| 76 | +### Frontend (`frontend/src/`) |
| 77 | + |
| 78 | +`App.jsx` loads `/catalog` on mount and calls `/generate` via `api.js`. Components: `PromptInput` |
| 79 | +(prompt + example chips, ⌘/Ctrl+Enter to submit), `ConfigSummary`, `MiniMap` (2D canvas of |
| 80 | +`unit_placement`), `SimPlayback`, `JsonView`. No state library, no router. |
| 81 | + |
| 82 | +## Key conventions |
| 83 | + |
| 84 | +- **LLM model** is hardcoded to `gpt-5.2` in `common.get_client()`. Reads `OPENAI_API_KEY` and |
| 85 | + optional `BASE_URL` from env. All token usage flows through the shared `TokenTracker` callback. |
| 86 | +- **Coordinate system:** maps are sized in tiles where **1 tile = 512px**. A 16×16 map → 8192×8192 |
| 87 | + pixels. Units are placed in pixel coordinates. Prompts and `MiniMap` both rely on this. |
| 88 | +- **Config shape** (`assemble_draft` output): `{information, end_condition, unit_placement, customize}`. |
| 89 | + `unit_placement` is keyed by team (`"1"`, `"2"`) → list of `[unit_code, [x, y]]`. `customize` holds |
| 90 | + rule/gadget config (e.g. `enemy_wave_spawner`). |
| 91 | +- **`backend/db/`** is the source of truth (scenarios, validated rules + their `.lua` codes, maps, |
| 92 | + units, decisions). **`backend/info/`** holds flat unit/map/gadget summaries fed into prompts |
| 93 | + (`units_info.json`, `map.json`, etc.) — distinct from `db/`. |
| 94 | +- **Logs:** every pipeline run writes to `backend/log/<timestamp>/` (gitignored). Each agent node |
| 95 | + dumps its raw result via `common.log_node_result`. |
| 96 | +- **Name matching is fuzzy on purpose:** `pipeline._resolve_db_name` / `_normalize_name` reconcile |
| 97 | + LLM-returned names against DB keys (case, spaces, parentheses, hyphens). Keep this when LLM output |
| 98 | + may not match DB keys exactly. |
| 99 | + |
| 100 | +## Git workflow |
| 101 | + |
| 102 | +`main → dev → feature/*`. Branch `feature/*` off `dev`; PR `feature/* → dev`; release via |
| 103 | +`dev → main`. PRs close issues with `Closes #<n>`. Project docs live in the GitHub Wiki (`wiki/`). |
0 commit comments