Install
$ agentstack add skill-aigengame-godot-agent-skill ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
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 [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 contain project.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 --help— a group's commands.gda --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, run (.gd files; run executes a res:// script one-shot and passes its exit_status/stdout/stderr through — a non-zero quit() is still success) | | project | info, get, set, list, add-autoload, remove-autoload, add-input-action, remove-input-action, 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 (optionally --scene to boot a specific scene instead of the project's main scene); 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, rect, set (the running game's runtime scene graph) | | diag | errors (structured runtime errors with callstacks; survive a crash) | | logger | tail (the running game's structured log stream; --raw for verbatim lines, --level to filter by severity, --limit N) | | 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) |
For gda input sequence, event frame offsets are the original harness/process-frame clock from the harness _process loop; they are not Godot's fixed physics frames. When input timing must map to physics simulation, use physics_frame offsets instead. To hold an action for N physics frames, press at {"type":"action","action":"move_right","physics_frame":0} and release at {"type":"action","action":"move_right","release":true,"physics_frame":N} in the same sequence. At Godot's default 60 Hz physics clock, N=30 is 0.5 seconds of physics simulation. Do not mix frame and physics_frame in one sequence.
Structured logging from game code
To emit a record gda logger tail reads back as a rich, field-carrying LogRecord, call the harness autoload from your GDScript — but gate it on the daemon-launched predicate, not on harness presence. The harness is present only where it was installed, and a supported gda export run artifact omits it entirely (ADR-0028), so resolve it by node path and null-check it — never the GdaHarness global, which fails to parse when the autoload is absent (a stripped export build, a project before gda daemon start, or after gda daemon uninstall), taking the whole script down with it. Even where it is present, it only captures logs when gda-daemon launched the session. Resolve the node, then gate on is_daemon_launched() (a pure read), falling back to print() when it is absent or dormant so no record is lost:
var harness := get_node_or_null("/root/GdaHarness")
if harness != null and harness.is_daemon_launched():
harness.gda_log("info", "player spawned", {"hp": 100})
else:
print("player spawned") # absent or dormant: gda_log() would be a silent no-op
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 "$PWD/game/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
Scene authoring
Wiring a functional scene means binding scripts, authoring Resources, and setting typed properties. Reach for the right command — the generic node set does not cover scripts, and Resource-typed fields take a res:// path, not a coerced literal.
Attach a script — script attach, never node set --property script. script attach is the one authoritative way to bind a .gd script to a node: it verifies the script compiles, checks its base type against the node, and reports any script it displaced. Setting the script property with node set is refused with an actionable use_script_attach error that points you back here. Create any assets a script preload("res://...") references before attaching that script; missing preload targets fail as missing_dependency and name the missing path.
gda script attach game/main.tscn --node Player --script res://player.gd --project game --json
Author and populate a Resource — resource create / resource set. Create a .tres (a built-in type, or a project-local class_name — resolved without opening the editor), then set its properties with the same --value coercion below:
gda resource create res://shapes/box.tres --type RectangleShape2D --project game --json
gda resource set res://shapes/box.tres --property size --value "32,64" --project game --json
Assign a Resource to an Object-typed property — --value res://….tres. For a property that expects a Resource (sub)class — e.g. a CollisionShape2D's shape — pass the .tres path as --value to node set (or resource set). The path is loaded, type-checked against the property's expected class, and stored as an external ext_resource (not inlined). Pass --project so res:// resolves:
gda node set game/main.tscn --node Col --property shape --value res://shapes/box.tres --project game --json
Its failures are distinct structured codes, never uncoercible_value: a non-res:// value → expected_resource_path; a path that is not a Resource → not_a_resource; a type mismatch → resource_type_mismatch; a class_name-typed target (not yet supported) → unsupported_property_type.
--value string forms
--value is a string coerced to the property's declared Godot type. The accepted forms — several of which are not obvious from --help:
bool—true/false(case-insensitive).int/float— a numeric literal (7,-3,1.5).String/StringName— the string, verbatim.Vector2/Vector2i— comma-separated components:--value "48,72"→
Vector2(48, 72). A JSON array ("[48,72]") or a constructor literal ("Vector2(48,72)") is rejected (uncoercible_value).
Color—#rrggbb/#rrggbbaa, or 3–4 comma-separated floats in 0..1:
--value "0.2,0.6,1,1".
Dictionary— a JSON object string:--value '{"wine":2}'. In Dictionary/Array
JSON values, JSON integer literals stay int and JSON float literals stay float; typed containers assign entries through their declared container type.
Array— a JSON array string:--value '["wine","key"]'. The same JSON
integer/float preservation rule applies to array elements.
- An Object-typed (Resource) property — a
res://….trespath, as above.
Whitespace is trimmed for the numeric forms — bool, int / float, the Vector2 / Vector2i components, and Color (hex or list) — but not for String / StringName (taken verbatim) or the res:// path (matched literally, so a leading space fails as expected_resource_path). The value-typed forms are shared by node set, resource set, project set, and live game set; the res:// Resource assignment is headless-only (node set / resource set). For live game get / game set, an explicitly named attached-script variable is addressable after storage properties are checked; unfiltered game get still lists only storage properties.
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
for node set, resource set, project set, and live game set.
- Create preloaded assets before attaching scripts that reference them; a missing
preload("res://...") target is reported as missing_dependency.
- 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.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: aigengame
- Source: aigengame/godot-agent
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.