gda is an agent-first CLI for the Godot engine: every operation is one command
with structured JSON output, so you build and inspect a game without opening the
editor. Two kinds of operation: headless (a one-shot godot --headless per
call — scenes, scripts, exports) and live (against a running game via the
gda-daemon).
Grammar
gda <group> <command> [options] --json
Exactly one JSON result is printed to stdout; all engine noise (warnings,
progress, the engine banner) goes to stderr. Read stdout, ignore stderr unless
debugging. info / schema / skill are top-level meta commands (no group).
Setup
- Engine — set
GDA_GODOTto your Godot binary (or pass--godot PATH). - Project — resolved by
--project DIR→$GDA_PROJECT→ the current directory; the directory must containproject.godot. Resolving a project runs that project's autoloads at engine startup. - Projectless — meta commands and file-path-only operations run with no
project; they resolve filesystem paths but not
res://.
Structured output & errors
Always pass --json. A success is the operation's result object. A failure is
{"error": {"category": "...", "code": "...", "message": "..."}}
Branch on the stable category/code and the exit code, never on prose:
| Exit | Meaning |
|---|---|
0 | success |
127 | Godot binary not found |
124 | engine timed out |
3 | engine version too old |
4 | operation-reported failure |
5 | could not parse the engine's output |
6 | live operation failed (e.g. daemon_not_running) |
Discovery
gda --help— every group.gda <group> --help— a group's commands.gda <group> <command> --schema— one command's input/output/error JSON Schema (no Godot spawned).gda schema— the whole surface as one JSON manifest.
Headless commands (Godot 4.4+, all platforms)
| Group | Commands |
|---|---|
scene | create, get, list, get-exports, delete (.tscn files) |
node | add, get, list, set, remove, duplicate, move, connect-signal, disconnect-signal (nodes within a scene) |
script | create, get, list, set, delete, attach, validate (.gd files) |
project | info, get, set, add-autoload, remove-autoload, find-references, dependencies, find-unused-resources, statistics |
resource | create, get, set, delete, uid (.tres files) |
export | list, get, run (export a preset by name; --mode release/debug/pack) |
shader | create, get, set (.gdshader files) |
theme | create (a loadable .tres Theme) |
Live operations (via the daemon; Godot 4.6+, macOS/Linux)
Prerequisites: run gda daemon start first; the engine session launches lazily on
the first live op. screen capture needs a windowed session
(gda daemon start --windowed).
| Group | Commands |
|---|---|
daemon | start, stop, status, uninstall (lifecycle; installs the in-game harness) |
game | tree, get, set (the running game's runtime scene graph) |
diag | errors, log (runtime errors / output log; survive a crash) |
perf | monitors, monitor (counters now / a property-or-signal timeline) |
input | key, mouse-click, mouse-move, action, sequence |
screen | capture, frames (viewport PNGs; needs --windowed) |
Worked example
Headless: build and export a scene.
export GDA_GODOT="/path/to/Godot"
gda scene create game/main.tscn --root-type Node2D --project game --json
gda node add game/main.tscn --type Sprite2D --name Hero --project game --json
gda node set game/main.tscn --node Hero --property position --value "100,50" --project game --json
gda export run --preset "Linux/X11" --output build/game.zip --project game --json # --preset: a name from 'gda export list'
Live: observe the running game, then tear down.
gda daemon start --project game --json # launches the session on the first live op
gda game tree --project game --json # the runtime scene tree, after _ready
gda daemon stop --project game --json
Tips
- Node paths are relative to the scene root;
.is the root itself. --valueis coerced to the property's declared Godot type — the same coercion fornode set,resource set,project set, and livegame set.- For large or scripted input, pass one JSON object with
--params-json '{...}'(or--params-json -to read it from stdin) instead of individual flags. - Live ops with no daemon report
daemon_not_running(exit6) and name the remedy — start the daemon and retry.