From aa12c2a0aaf7a59d23363518688a7379a811b20e Mon Sep 17 00:00:00 2001 From: S1M0N38 Date: Tue, 3 Feb 2026 13:22:13 +0100 Subject: [PATCH 1/6] docs(claude): add balatrobot SKILL --- .claude/skills/balatrobot/SKILL.md | 140 +++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 .claude/skills/balatrobot/SKILL.md diff --git a/.claude/skills/balatrobot/SKILL.md b/.claude/skills/balatrobot/SKILL.md new file mode 100644 index 00000000..1e077b45 --- /dev/null +++ b/.claude/skills/balatrobot/SKILL.md @@ -0,0 +1,140 @@ +--- +name: balatrobot +description: Run and debug BalatroBot locally. Use when you need to start Balatro with the BalatroBot Lua mod, manually test or reproduce issues via the JSON-RPC HTTP API, inspect the newest session logs under logs/, and capture screenshots into logs//artifacts/ using only the balatrobot CLI (no curl, no uvx). +disable-model-invocation: true +--- + +# BalatroBot debug/runbook + +## Ground rules + +- Run commands from the repo root. +- Use `balatrobot ...` only (no `uvx`, no `curl`). +- Run `balatrobot api ...` requests sequentially (avoid concurrent calls). +- Prefer minimal, targeted changes while debugging; avoid large refactors. + +## Start BalatroBot + +### Choose a port (recommended) + +Pick a random port in the range `20000-30000` to avoid the port staying busy for ~20-30s after restarting: + +```bash +PORT="$((20000 + RANDOM % 10001))" +echo "Using PORT=$PORT" +``` + +Pick a new random `PORT` every time you restart `balatrobot serve` (crash/restart/kill/start). + +### Default profile (most cases) + +Use headless mode unless explicitly asked to take screenshots: + +```bash +balatrobot serve --port "$PORT" --headless --fast --debug +``` + +### Screenshot profile (only when explicitly asked) + +Use render-on-API mode for deterministic screenshots: + +```bash +balatrobot serve --port "$PORT" --render-on-api --fast --debug +``` + +## Call the API (no curl) + +Run API calls in a second terminal while `balatrobot serve ...` is running. + +Reminder: wrap JSON params in single quotes so you don't need to escape JSON double quotes. + +```bash +# Health check +balatrobot api health --port "$PORT" + +# Get full game state +balatrobot api gamestate --port "$PORT" + +# Return to menu +balatrobot api menu --port "$PORT" + +# Start a new run +balatrobot api start '{"deck":"RED","stake":"WHITE"}' --port "$PORT" + +# Select blind +balatrobot api select --port "$PORT" + +# Play cards (0-based indices) +balatrobot api play '{"cards":[0,1,2,3,4]}' --port "$PORT" +``` + +Always pass the same `--port` to both `serve` and `api`. + +## Logs and sessions + +### Session layout + +Each `balatrobot serve ...` run creates: + +- `logs//.log` (game + mod stdout/stderr) + +`` is a timestamp folder like `2025-12-29T19-18-18` (lexicographically sortable). + +### Find the current session + +Pick the newest session directory (works because of the timestamp format): + +```bash +SESSION="$(ls -1 logs | sort | tail -n 1)" +``` + +Use it to open/tail the log: + +```bash +tail -f "logs/$SESSION/$PORT.log" +``` + +The log filename matches the port: `logs//.log`. + +## Screenshots (write to logs//artifacts/) + +Only do this when explicitly asked for a screenshot. + +1) Start the server with the screenshot profile: + +```bash +balatrobot serve --port "$PORT" --render-on-api --fast --debug +``` + +2) Create the artifacts directory under the newest session: + +```bash +SESSION="$(ls -1 logs | sort | tail -n 1)" +mkdir -p "logs/$SESSION/artifacts" +``` + +3) Call the screenshot endpoint with a path inside that folder: + +```bash +balatrobot api screenshot "{\"path\":\"logs/$SESSION/artifacts/screenshot.png\"}" --port "$PORT" +``` + +## Debug workflow (tight loop) + +1) Reproduce with the smallest possible sequence of `balatrobot api ...` calls. +2) Capture: + - the exact `balatrobot serve ...` command, + - host/port, + - the session folder name, + - relevant excerpts from `logs//.log`, + - the JSON outputs/errors from `balatrobot api ...`. +3) Read the most relevant code before changing anything. +4) If needed, add minimal logging close to the suspected behavior, then re-run. + +## Where to look in the repo + +- CLI entry points: `src/balatrobot/cli/serve.py`, `src/balatrobot/cli/api.py` +- Game process + log session creation: `src/balatrobot/manager.py` +- Lua HTTP server + dispatcher: `src/lua/core/server.lua`, `src/lua/core/dispatcher.lua` +- API reference/spec: `docs/api.md`, `src/lua/utils/openrpc.json` +- Endpoint implementations: `src/lua/endpoints/*.lua` From 3d04e519f9e69c8b87bee8532d68b5121d8dc5c0 Mon Sep 17 00:00:00 2001 From: S1M0N38 Date: Tue, 3 Feb 2026 13:23:26 +0100 Subject: [PATCH 2/6] docs(claude): use absolute paths in screenshot endpoint in SKILL.md --- .claude/skills/balatrobot/SKILL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.claude/skills/balatrobot/SKILL.md b/.claude/skills/balatrobot/SKILL.md index 1e077b45..74e094a9 100644 --- a/.claude/skills/balatrobot/SKILL.md +++ b/.claude/skills/balatrobot/SKILL.md @@ -113,10 +113,10 @@ SESSION="$(ls -1 logs | sort | tail -n 1)" mkdir -p "logs/$SESSION/artifacts" ``` -3) Call the screenshot endpoint with a path inside that folder: +3) Call the screenshot endpoint with an absolute path inside that folder: ```bash -balatrobot api screenshot "{\"path\":\"logs/$SESSION/artifacts/screenshot.png\"}" --port "$PORT" +balatrobot api screenshot "{\"path\":\"$(pwd)/logs/$SESSION/artifacts/screenshot.png\"}" --port "$PORT" ``` ## Debug workflow (tight loop) From 4f7c98e2826f90930140c19e026f6708fc2c588f Mon Sep 17 00:00:00 2001 From: S1M0N38 Date: Tue, 3 Feb 2026 13:24:32 +0100 Subject: [PATCH 3/6] chore(gitignore): add logs to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index f6b2be36..b291817f 100644 --- a/.gitignore +++ b/.gitignore @@ -315,6 +315,9 @@ balatro # balatrobot runs/*.jsonl +# logs +logs/ + ################################################################################ # Legacy files ################################################################################ From 716816a506442005951a4a4f5452140709c59d9a Mon Sep 17 00:00:00 2001 From: S1M0N38 Date: Tue, 3 Feb 2026 13:59:49 +0100 Subject: [PATCH 4/6] docs(claude): add allowed-tools and jq examples to balatrobot SKILL.md --- .claude/skills/balatrobot/SKILL.md | 31 +++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/.claude/skills/balatrobot/SKILL.md b/.claude/skills/balatrobot/SKILL.md index 74e094a9..34577a01 100644 --- a/.claude/skills/balatrobot/SKILL.md +++ b/.claude/skills/balatrobot/SKILL.md @@ -1,6 +1,7 @@ --- name: balatrobot description: Run and debug BalatroBot locally. Use when you need to start Balatro with the BalatroBot Lua mod, manually test or reproduce issues via the JSON-RPC HTTP API, inspect the newest session logs under logs/, and capture screenshots into logs//artifacts/ using only the balatrobot CLI (no curl, no uvx). +allowed-tools: Bash(balatrobot:*) Bash(mkdir:*) Bash(ls:*) Bash(tail:*) Bash(PORT=:*) Bash(echo:*) Bash(jq:*) Bash(sleep:*) Read Grep disable-model-invocation: true --- @@ -68,6 +69,34 @@ balatrobot api select --port "$PORT" balatrobot api play '{"cards":[0,1,2,3,4]}' --port "$PORT" ``` +### Filter responses with `jq` (optional) + +The `balatrobot api ...` CLI prints the JSON-RPC `result` object to stdout on success (see `docs/api.md`), so `jq` filters target top-level fields like `.state` (not `.result.state`). + +```bash +# Print the current state as a raw string (MENU / BLIND_SELECT / SELECTING_HAND / ...) +balatrobot api gamestate --port "$PORT" | jq -r '.state' + +# Quick summary (useful for bug reports) +balatrobot api gamestate --port "$PORT" | jq '{state, round_num, ante_num, money, deck, stake, won}' + +# Round counters (hands/discards/chips) +balatrobot api gamestate --port "$PORT" | jq '.round | {hands_left, discards_left, chips, reroll_cost}' + +# Cards currently in hand (ids + labels) +balatrobot api gamestate --port "$PORT" | jq '.hand.cards | map({id, key, label})' + +# Joker labels (one per line) +balatrobot api gamestate --port "$PORT" | jq -r '.jokers.cards[].label' +``` + +On failure, `balatrobot api ...` prints a human-readable error to stderr and exits non-zero. To extract fields from that error output, capture stderr and use `jq` raw mode: + +```bash +balatrobot api play '{"cards":[999]}' --port "$PORT" 2>&1 >/dev/null \ + | jq -R 'capture("^Error: (?.*?) - (?.*)$")' +``` + Always pass the same `--port` to both `serve` and `api`. ## Logs and sessions @@ -127,7 +156,7 @@ balatrobot api screenshot "{\"path\":\"$(pwd)/logs/$SESSION/artifacts/screenshot - host/port, - the session folder name, - relevant excerpts from `logs//.log`, - - the JSON outputs/errors from `balatrobot api ...`. + - the JSON outputs (stdout) and errors (stderr) from `balatrobot api ...`. 3) Read the most relevant code before changing anything. 4) If needed, add minimal logging close to the suspected behavior, then re-run. From 77fe5c7c8fa08ffc8af2915c21a1ee17264dcadf Mon Sep 17 00:00:00 2001 From: S1M0N38 Date: Tue, 3 Feb 2026 14:11:23 +0100 Subject: [PATCH 5/6] fix(make): add SKILL.md to mdformat --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 51ee27ea..bfc75eb1 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ format: ## Run formatters (ruff, mdformat, stylua) ruff check --select I --fix . ruff format . @$(PRINT) "$(YELLOW)Running mdformat formatter...$(RESET)" - mdformat ./docs README.md CLAUDE.md + mdformat ./docs README.md CLAUDE.md .claude/skills/balatrobot/SKILL.md @if command -v stylua >/dev/null 2>&1; then \ $(PRINT) "$(YELLOW)Running stylua formatter...$(RESET)"; \ stylua src/lua; \ From 59d6b025900218b868c1443b4a932600ead3681c Mon Sep 17 00:00:00 2001 From: S1M0N38 Date: Tue, 3 Feb 2026 14:11:35 +0100 Subject: [PATCH 6/6] style(claude): format SKILL.md --- .claude/skills/balatrobot/SKILL.md | 44 +++++++++++++++--------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/.claude/skills/balatrobot/SKILL.md b/.claude/skills/balatrobot/SKILL.md index 34577a01..2ff66237 100644 --- a/.claude/skills/balatrobot/SKILL.md +++ b/.claude/skills/balatrobot/SKILL.md @@ -129,36 +129,36 @@ The log filename matches the port: `logs//.log`. Only do this when explicitly asked for a screenshot. -1) Start the server with the screenshot profile: +1. Start the server with the screenshot profile: -```bash -balatrobot serve --port "$PORT" --render-on-api --fast --debug -``` + ```bash + balatrobot serve --port "$PORT" --render-on-api --fast --debug + ``` -2) Create the artifacts directory under the newest session: +2. Create the artifacts directory under the newest session: -```bash -SESSION="$(ls -1 logs | sort | tail -n 1)" -mkdir -p "logs/$SESSION/artifacts" -``` + ```bash + SESSION="$(ls -1 logs | sort | tail -n 1)" + mkdir -p "logs/$SESSION/artifacts" + ``` -3) Call the screenshot endpoint with an absolute path inside that folder: +3. Call the screenshot endpoint with an absolute path inside that folder: -```bash -balatrobot api screenshot "{\"path\":\"$(pwd)/logs/$SESSION/artifacts/screenshot.png\"}" --port "$PORT" -``` + ```bash + balatrobot api screenshot "{\"path\":\"$(pwd)/logs/$SESSION/artifacts/screenshot.png\"}" --port "$PORT" + ``` ## Debug workflow (tight loop) -1) Reproduce with the smallest possible sequence of `balatrobot api ...` calls. -2) Capture: - - the exact `balatrobot serve ...` command, - - host/port, - - the session folder name, - - relevant excerpts from `logs//.log`, - - the JSON outputs (stdout) and errors (stderr) from `balatrobot api ...`. -3) Read the most relevant code before changing anything. -4) If needed, add minimal logging close to the suspected behavior, then re-run. +1. Reproduce with the smallest possible sequence of `balatrobot api ...` calls. +2. Capture: + - the exact `balatrobot serve ...` command, + - host/port, + - the session folder name, + - relevant excerpts from `logs//.log`, + - the JSON outputs (stdout) and errors (stderr) from `balatrobot api ...`. +3. Read the most relevant code before changing anything. +4. If needed, add minimal logging close to the suspected behavior, then re-run. ## Where to look in the repo