Balatro Mod Development
Create and debug Balatro mods with Steamodded, Lovely, and SMODS.
Quick Agent Selection
When researching, spawn the right agent:
| Need to find... | Use agent | Search boundary | Default Backend |
|---|
| Game function implementation | | only | claude |
| SMODS API usage/hooks | | only | claude |
| How other mods do X | | folder only | claude |
| Lovely patch syntax | | lovely files only | claude |
| Project architecture/exploration | | Current project only | codex |
| Run temp script for data | | N/A (execution) | codex |
| Plan implementation strategy | | Current project only | opus |
| Review code for correctness | | Current project only | opus |
| Synthesize research findings | | Current project only | opus |
Parallel: When researching DIFFERENT sources - spawn multiple agents at once
Sequential: When second query depends on first result
⚠️ MANDATORY: Sub-Agent Invocation
ALWAYS use to spawn sub-agents. This adapter resolves backend config from
and routes through codeagent.
DO NOT use built-in agent spawning, direct shell commands, or any other method.
bash
# CORRECT - always use this
./scripts/run_subagent.sh game-source-researcher <<'EOF'
[task content]
EOF
# WRONG - never do this
# spawn_agent(...), create_subagent(...), direct codeagent calls, etc.
See
for boundaries, workflow patterns, and creating new agents.
Repo Type Awareness
Auto-detection: Compare mod manifest
with git remote username.
bash
# Get git remote username
git_user=$(git remote get-url origin 2>/dev/null | sed -E 's|.*[:/]([^/]+)/[^/]+\.git$|\1|' | tr '[:upper:]' '[:lower:]')
# Get mod author from manifest (first author, lowercase)
mod_author=$(jq -r '.author[0] // .author // ""' *.json 2>/dev/null | head -1 | tr '[:upper:]' '[:lower:]')
# Compare: match = own, no match = fork
[[ "$git_user" == "$mod_author" ]] && echo "own" || echo "fork"
| Type | Detection | Implications |
|---|
| Empty repo (no files) | Full docs, Logger.lua, localization |
| Author matches git user | Full docs, standardize structure |
| Author differs from git user | Minimal changes, temp logs only |
See
templates/project-rules-template.md
for detailed rules per type.
File Naming Convention (Claude & Codex)
Both Claude and Codex use the same file structure:
| File | Purpose | Git |
|---|
| Project rules, constraints for AI agents | ignored |
| Mod structure, functions, dependencies, dev status (for handover) | ignored |
| File lists for sync/release scripts | ignored |
| Issues & lessons learned | ignored |
AGENT.md Purpose: Enable seamless handover between agents. Another agent should quickly understand mod structure, functions, dependencies, and current development status without losing context.
File Placement Rules
Only these
files belong in root:
ALL other files MUST go in
External References (No Symlinks Needed)
Access reference code directly via absolute paths. No setup required.
Source Locations (macOS)
| Resource | Path |
|---|
| Game Source (desktop) | ~/Development/GitWorkspace/Balatro_src/desktop/
|
| Game Source (mobile) | ~/Development/GitWorkspace/Balatro_src/ios_plus/
|
| Steamodded Source | ~/Development/GitWorkspace/smods/src/
|
| Steamodded Lovely | ~/Development/GitWorkspace/smods/lovely/
|
| Lovely Docs | ~/Development/GitWorkspace/lovely-injector/
|
| Installed Mods | ~/Library/Application Support/Balatro/Mods/
|
| Lovely Logs | ~/Library/Application Support/Balatro/Mods/lovely/log/
|
Source Locations (Windows)
| Resource | Path |
|---|
| Game Source | Varies by setup |
| Installed Mods | |
| Lovely Logs | %APPDATA%/Balatro/Mods/lovely/log/
|
Finding Patterns & Examples
When you need to find how something is implemented:
| What to Find | Where to Search | Command |
|---|
| Game functions | Balatro_src/desktop/ | grep -rn "function Game:start_run" ~/Development/GitWorkspace/Balatro_src/desktop/
|
| SMODS API usage | smods/src/ | grep -rn "SMODS.Joker" ~/Development/GitWorkspace/smods/src/
|
| Lovely patch examples | smods/lovely/ | grep -rn "patches.pattern" ~/Development/GitWorkspace/smods/lovely/
|
| Other mods' implementations | Installed Mods | grep -rn "pattern" ~/Library/Application\ Support/Balatro/Mods/
|
| Mobile differences | Balatro_src/ios_plus/ | Compare with desktop version |
Key Dependencies
| Dependency | Purpose |
|---|
| Steamodded | Core mod loader, SMODS API |
| Lovely | Lua injection framework |
| Malverk | Texture pack API (AltTexture, TexturePack) |
Pattern References
Read these files for specific topics:
| Topic | Reference File |
|---|
| Lovely.toml syntax | patterns/lovely-patches.md
|
| SMODS hooks, config, localization | |
| Desktop vs mobile differences | patterns/mobile-compat.md
|
| UIBox, CardArea, draw order | |
| Game source file map + search tips | |
| G.GAME, G.STATES, G.P_* globals | |
| Lua/LuaJIT pitfalls, common mod bugs | references/lua-gotchas.md
|
New Mod Setup (type: new)
Templates in folder:
| File | Purpose |
|---|
project-rules-template.md
| INIT.md template (rules) |
| AGENT.md template (repo docs) |
agent-texture-pack-template.md
| AGENT.md for Malverk texture packs |
| Script configuration |
| Standard .gitignore |
| Centralized logging utility |
Meta Files:
| File | Purpose |
|---|
| SMODS mod manifest ({ModName}.json) |
manifest-json-template.json
| Thunderstore manifest |
User Docs in :
| File | Purpose |
|---|
| Concise README for docs/ |
NEXUSMODS_DESCRIPTION-template.txt
| BBCode for NexusMods |
knowledge-base-template.md
| Issues & lessons learned |
Required User Docs (new repos):
Root:
├── README.md, README_zh.md # Main docs (EN/ZH)
├── CHANGELOG.md, CHANGELOG_zh.md # Version history (EN/ZH)
└── {ModName}.json, manifest.json # Meta files
docs/:
├── description.md # Concise README
├── NEXUSMODS_DESCRIPTION.txt # BBCode format
└── knowledge-base.md # Issues & lessons
Basic Mod Structure (new repos):
{ModName}/
├── main.lua # Entry point, mod registration
├── config.lua # Config defaults (optional)
├── lovely.toml # Lovely patches (if needed)
├── {ModName}.json # SMODS mod manifest
├── manifest.json # Thunderstore manifest
├── mod.config.json # Script configuration
├── Utils/
│ └── Logger.lua # Centralized logging
├── localization/
│ ├── en-us.lua # English (required)
│ └── zh_CN.lua # Chinese
├── assets/ # Sprites, shaders
├── scripts/ # Utility scripts
└── docs/ # Documentation
AI Agent Config Templates
| Folder | Contents |
|---|
| Claude hooks.json, init command |
| Codex-specific templates (if needed) |
Logging
For new/my repos:
Use
(from
templates/logger-template.lua
):
lua
local Logger = require("Utils.Logger")
local log = Logger.create("ModuleName")
log("info", "Initialized")
log("error", "Failed: " .. err)
For forks/others' repos:
Use temp logs only (remove before PR):
lua
pcall(print, "[Debug] checkpoint: " .. tostring(var))
Utility Scripts
| Script | Purpose |
|---|
scripts/sync_to_mods.template.sh
| Sync mod files to game's Mods folder |
scripts/create_release.template.sh
| Create release packages |
scripts/fix_transparent_pixels.py
| Fix grey borders on sprites |
scripts/mod-scripts-guide.md
| Detailed script usage |
Workflow: Init Any Existing Repo
For ALL non-empty repos (own or fork), ALWAYS do these first:
- Delete folder if exists (legacy symlink approach)
- Move extra files to - only keep in root: README*.md, CHANGELOG*.md, AGENT.md, INIT.md, LICENSE.md
- Add dev files (if missing): AGENT.md, INIT.md, mod.config.json, scripts/sync_to_mods.sh
- Add Claude config (if missing): , ,
- Add hookify rules (if missing):
.claude/hookify.no-opus-subagents.local.md
(Opus only for reasoning agents), .claude/hookify.subagent-routing.local.md
- Update .gitignore with agent folders
Then for OWN repos: Also check manifest, scripts version (2.0.1), add create_release.sh, Logger.lua
Then for FORK repos: Keep AGENT.md lightweight, use fork-mode INIT.md, don't add release scripts
Workflow: Debugging
- Check
references/lua-gotchas.md
for known pitfalls (FFI cdata, nil scoping, boolean normalization)
- Check platform (desktop vs mobile)
- Search game source for function
- Check other mods for implementations
- Add logs (Logger.lua for own, temp for fork)
- Check Lovely logs
- If fix fails 3+ times: Document in
Workflow: Update User Docs
When user says "update all user docs":
- Review ALL files: README(_zh).md, CHANGELOG(_zh).md
- Review docs/: description.md, NEXUSMODS_DESCRIPTION.txt
- Update version in {ModName}.json, manifest.json
- Ensure EN/ZH consistency
Workflow: Draft PR Message (fork repos)
Use
command. Style: 3-5 sentences, casual tone, what/why/done.
Sub-Agents for Research
Main agent handles code. Sub-agents handle information gathering via
→ codeagent routing.
Shared context: When invoking multiple sub-agents for a task, the main agent
must first create
as a shared brief. Sub-agents read it for context and write their artifacts (research.md, analysis.md, plan.md, review.md) to the same directory. See
→ "Shared Task Context" for the full protocol.
| Situation | Use | Default Backend |
|---|
| Research (game, SMODS, mods, lovely) | Research agent | |
| Running temp scripts for data | | |
| Planning, reviewing, synthesizing | Reasoning agent | |
| Writing/editing code | Main agent | — |
| User interaction needed | Main agent | — |
Backends and source paths are
configurable in
:
- / / — category defaults
agent_backends.overrides.{agent-name}
— per-agent override (string or )
- — where game source, SMODS, mods are located on this machine
Model restriction: Opus is allowed only for reasoning sub-agents (strategic-planner, code-reviewer, research-analyst). Research agents use Sonnet; execution agents use Haiku.
Hookify enforcement (requires hookify plugin on-site):
hookify.no-opus-subagents.local.md
— Blocks Opus for non-reasoning agents (allows strategic-planner, code-reviewer, research-analyst)
hookify.subagent-routing.local.md
— Blocks direct codeagent/route_subagent calls
These are backend
hints. Codeagent owns final invocation policy (
,
).
resolves config and routes through codeagent automatically — no direct
calls.
See
for full config resolution, invocation patterns, and parallel examples.
Available Commands
- - Get familiar with this mod (reads AGENT.md, INIT.md, maps architecture)
- - Initialize new mod
- - Start sync with watch mode (run once at start)
/bump-version [patch|minor|major]
- Increment version, update changelogs
- - Create release packages (auto-detects version from manifests)
/fix-sprites <directory> [--preview]
- Fix grey borders on sprites
- - Review code for redundancy, outdated fallbacks, modularization
- - Verify fix by checking Lovely logs (auto-detects mod key from repo)
- - Draft PR message (for forks)
- - Audit project health: scripts, hooks, commands, config, file placement, gitignore
- - Review all docs (user docs + AGENT.md + INIT.md) for accuracy, staleness, duplication, verbosity
/update-skill [file|instruction]
- Update skill based on new knowledge
- - Review session work, capture discoveries (project-scope → AGENT.md, general → skill)
Sub-agents available after setup:
- - Find game functions and injection points
- - Find SMODS API patterns and usage
- - Find how other mods implement features
- - Find Lovely patch syntax and examples
- - Extensive codebase exploration (uses codex for token efficiency)
- - Run temp scripts and return results
- - Plan implementation strategy (uses opus for deep reasoning)
- - Review code for correctness and edge cases (uses opus)
- - Synthesize multi-source research findings (uses opus)