gda 0.1.24__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gda-0.1.24/LICENSE +21 -0
- gda-0.1.24/PKG-INFO +643 -0
- gda-0.1.24/README.md +616 -0
- gda-0.1.24/pyproject.toml +66 -0
- gda-0.1.24/src/gda/__init__.py +1 -0
- gda-0.1.24/src/gda/__main__.py +14 -0
- gda-0.1.24/src/gda/binary.py +35 -0
- gda-0.1.24/src/gda/cli.py +1922 -0
- gda-0.1.24/src/gda/error_codes.py +405 -0
- gda-0.1.24/src/gda/errors.py +491 -0
- gda-0.1.24/src/gda/exit_codes.py +27 -0
- gda-0.1.24/src/gda/export_run.py +165 -0
- gda-0.1.24/src/gda/export_runner.py +97 -0
- gda-0.1.24/src/gda/headless.py +353 -0
- gda-0.1.24/src/gda/mcp/__init__.py +48 -0
- gda-0.1.24/src/gda/mcp/project_context.py +58 -0
- gda-0.1.24/src/gda/mcp/runner.py +132 -0
- gda-0.1.24/src/gda/mcp/server.py +303 -0
- gda-0.1.24/src/gda/models.py +2349 -0
- gda-0.1.24/src/gda/ops/operations.gd +3733 -0
- gda-0.1.24/src/gda/parser.py +38 -0
- gda-0.1.24/src/gda/project.py +71 -0
- gda-0.1.24/src/gda/render.py +574 -0
- gda-0.1.24/src/gda/runner.py +179 -0
- gda-0.1.24/src/gda/surface.py +94 -0
gda-0.1.24/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 aigengame
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
gda-0.1.24/PKG-INFO
ADDED
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gda
|
|
3
|
+
Version: 0.1.24
|
|
4
|
+
Summary: An agent-facing Godot CLI with structured output
|
|
5
|
+
Keywords: godot,cli,agent,mcp,game-development
|
|
6
|
+
Author: haihong.qin
|
|
7
|
+
Author-email: haihong.qin <haihongqin@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Games/Entertainment
|
|
16
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
17
|
+
Requires-Dist: pydantic>=2.13.4
|
|
18
|
+
Requires-Dist: typer>=0.26.7
|
|
19
|
+
Requires-Dist: mcp>=1.12,<2 ; extra == 'mcp'
|
|
20
|
+
Requires-Python: >=3.13
|
|
21
|
+
Project-URL: Homepage, https://github.com/aigengame/godot-agent
|
|
22
|
+
Project-URL: Repository, https://github.com/aigengame/godot-agent
|
|
23
|
+
Project-URL: Issues, https://github.com/aigengame/godot-agent/issues
|
|
24
|
+
Project-URL: Changelog, https://github.com/aigengame/godot-agent/blob/main/CHANGELOG.md
|
|
25
|
+
Provides-Extra: mcp
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# godot-agent (`gda`)
|
|
29
|
+
|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
> An agent-first **CLI and MCP server** that lets AI agents drive the [Godot Engine](https://godotengine.org) to build games — with **structured output** built for programmatic consumption.
|
|
33
|
+
|
|
34
|
+
[](#project-status)
|
|
35
|
+
[](https://github.com/aigengame/godot-agent/actions/workflows/ci.yml?query=branch%3Amain+event%3Apush)
|
|
36
|
+
[](https://www.python.org/)
|
|
37
|
+
[-478CBF)](https://godotengine.org)
|
|
38
|
+
[](https://github.com/astral-sh/uv)
|
|
39
|
+
[](LICENSE)
|
|
40
|
+
|
|
41
|
+
`godot-agent` lets AI agents drive the Godot engine through **structured, machine-readable
|
|
42
|
+
operations** rather than raw logs: an agent issues an operation and gets back a single clean
|
|
43
|
+
result it can act on, not prose it has to scrape.
|
|
44
|
+
|
|
45
|
+
It is designed as three layers: **`gda`**, the agent-facing CLI that exposes Godot operations
|
|
46
|
+
with structured `--json` output and self-describing schemas; **`gda-mcp`**, a thin
|
|
47
|
+
[Model Context Protocol](https://modelcontextprotocol.io) server that turns those same
|
|
48
|
+
capabilities into MCP tools, derived mechanically from `gda`'s schemas; and **`gda-daemon`**, a
|
|
49
|
+
long-lived process holding a persistent connection to a running engine to serve *live operations*
|
|
50
|
+
that a one-shot headless process cannot. `gda` lands first as a standalone headless CLI (Phase 1);
|
|
51
|
+
live operations follow through `gda-daemon` (Phase 2). See [Architecture](#architecture-at-a-glance)
|
|
52
|
+
for the full picture.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Why `gda`?
|
|
57
|
+
|
|
58
|
+
- **🤖 Agent-first, structured output.** Every command supports `--json` and emits exactly one
|
|
59
|
+
result object on stdout. Engine noise and diagnostics are routed to stderr, so an agent never
|
|
60
|
+
has to scrape prose. See the [structured-output contract](#the-structured-output-contract).
|
|
61
|
+
- **📐 Model-driven & self-describing.** Each command's input and output are defined as typed
|
|
62
|
+
models that back both `--json` and a machine-readable `--schema` (a JSON Schema contract). Carrying
|
|
63
|
+
a valid `--schema` is a hard, no-exception merge gate on every command — so an MCP adapter can
|
|
64
|
+
generate tool definitions mechanically instead of hand-maintaining them.
|
|
65
|
+
*(See [ADR-0004](docs/adr/0004-schema-flag-self-description.md).)*
|
|
66
|
+
- **🧩 Godot-native command surface.** Commands are grouped by Godot domain object
|
|
67
|
+
(`gda scene create`, `gda node add`, …) with a small, orthogonal verb vocabulary — zero learning
|
|
68
|
+
cost if you already know Godot. *(See [ADR-0005](docs/adr/0005-cli-command-taxonomy.md).)*
|
|
69
|
+
- **📦 Standalone, no service required.** The first delivery fulfils *headless operations* by
|
|
70
|
+
spawning one-shot `godot --headless` processes — nothing to install in the editor, no daemon to
|
|
71
|
+
run. Live, stateful operations arrive later behind the same CLI. *(See [ADR-0001](docs/adr/0001-godot-integration-mechanism.md).)*
|
|
72
|
+
- **🛡️ Fails loudly, not silently.** A missing or hung engine is bounded by a timeout and mapped
|
|
73
|
+
to a non-zero exit with a clear diagnostic — never an indefinite hang or a raw traceback.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Project status
|
|
78
|
+
|
|
79
|
+
> **`gda`'s Phase-1 headless command surface is feature-complete.** The architecture and contracts
|
|
80
|
+
> are settled (see [`CONTEXT.md`](CONTEXT.md) and [`docs/adr/`](docs/adr/)), and every headless
|
|
81
|
+
> domain command group designed in
|
|
82
|
+
> [PRD #17](https://github.com/aigengame/godot-agent/issues/17) now ships end-to-end against a real
|
|
83
|
+
> engine. `gda` is still pre-1.0 and not yet published to a package index
|
|
84
|
+
> ([#207](https://github.com/aigengame/godot-agent/issues/207)); **`gda-mcp` now ships** alongside
|
|
85
|
+
> the CLI, and the next milestone is Phase 2 live operations.
|
|
86
|
+
|
|
87
|
+
**Working today — the full headless command surface**
|
|
88
|
+
|
|
89
|
+
- ✅ **Eight command groups, fulfilled headlessly:** `scene`, `node`, `script`, `project`,
|
|
90
|
+
`resource`, `export`, `shader`, and `theme`, plus the `info` meta command — and the `project`
|
|
91
|
+
static-analysis reads (`find-references`, `dependencies`, `find-unused-resources`, `statistics`).
|
|
92
|
+
See the [command reference](#command-reference) for every command
|
|
93
|
+
([ADR-0005](docs/adr/0005-cli-command-taxonomy.md)).
|
|
94
|
+
- ✅ **The contract every command carries:** structured `--json` output, model-derived `--schema`
|
|
95
|
+
self-description as a hard merge gate
|
|
96
|
+
([ADR-0004](docs/adr/0004-schema-flag-self-description.md)), and structured
|
|
97
|
+
`{"error": {category, code, …}}` failures with category-distinguishing
|
|
98
|
+
[exit codes](#exit-codes-the-cli-abi).
|
|
99
|
+
- ✅ **The engine plumbing underneath:** Godot binary resolution (flag / env var / default), the
|
|
100
|
+
bounded one-shot `godot --headless` runner with its sentinel output contract
|
|
101
|
+
([ADR-0002](docs/adr/0002-headless-structured-output-contract.md)), command-agnostic failure
|
|
102
|
+
classification, and project-context / `res://` path resolution
|
|
103
|
+
([ADR-0006](docs/adr/0006-project-context-and-path-resolution.md)).
|
|
104
|
+
|
|
105
|
+
**Also working today — the MCP adapter**
|
|
106
|
+
|
|
107
|
+
- ✅ `gda-mcp`, a thin [Model Context Protocol](https://modelcontextprotocol.io) stdio server that
|
|
108
|
+
exposes the whole `gda` surface as MCP tools, generated mechanically from `--schema`. Register it
|
|
109
|
+
with your agent using the [registration recipes](docs/gda-mcp-registration.md).
|
|
110
|
+
|
|
111
|
+
**On the roadmap** (designed, not yet implemented)
|
|
112
|
+
|
|
113
|
+
- 🔜 `gda-daemon` for *live operations* against a running engine (Phase 2): live scene tree,
|
|
114
|
+
runtime inspection, input simulation, `scene play`/`stop`, screenshots.
|
|
115
|
+
|
|
116
|
+
**Out of scope for now**
|
|
117
|
+
|
|
118
|
+
- **C# / .NET scripts.** The `script` group operates on GDScript (`.gd`) only; whether and how to
|
|
119
|
+
support the Godot .NET build and `.cs` scripts is deferred
|
|
120
|
+
([#124](https://github.com/aigengame/godot-agent/issues/124)).
|
|
121
|
+
- **The deferred catalog groups** — animation, tilemap, physics, audio, particles, 3D scene,
|
|
122
|
+
navigation, Android deploy — each needs its own slice-level design before becoming a commitment.
|
|
123
|
+
|
|
124
|
+
The active backlog and per-command status live in the
|
|
125
|
+
[issue tracker](https://github.com/aigengame/godot-agent/issues) — the headless increment is
|
|
126
|
+
grouped under the [**Phase 1 — headless operations** milestone](https://github.com/aigengame/godot-agent/milestone/1).
|
|
127
|
+
The [command catalog](docs/command-catalog.md) maps the whole command surface (a non-binding
|
|
128
|
+
*feature map*, not a status tracker); this section deliberately duplicates neither. See also the
|
|
129
|
+
[roadmap](#roadmap).
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Architecture at a glance
|
|
134
|
+
|
|
135
|
+
`gda` is delivered bottom-up as three components and in two capability phases:
|
|
136
|
+
|
|
137
|
+
| Component | Role | Phase |
|
|
138
|
+
| ------------- | -------------------------------------------------------------------------------------- | ----- |
|
|
139
|
+
| **`gda`** | The agent-facing Godot CLI — the bottom layer that exposes Godot with structured output | 1 |
|
|
140
|
+
| **`gda-mcp`** | A thin MCP adapter that exposes `gda`'s capabilities as MCP tools, derived from `--schema` | 1+ |
|
|
141
|
+
| **`gda-daemon`** | A long-lived process holding a persistent connection to a running engine for *live operations* | 2 |
|
|
142
|
+
|
|
143
|
+
- **Headless operations** need no pre-existing engine state and are fulfilled by a one-shot
|
|
144
|
+
`godot --headless` process (e.g. report version, create a scene, export). This is the basis of
|
|
145
|
+
**Phase 1**.
|
|
146
|
+
- **Live operations** require an already-running engine (live scene tree, runtime inspection,
|
|
147
|
+
UndoRedo, input simulation) and are served by `gda-daemon` in **Phase 2**.
|
|
148
|
+
|
|
149
|
+
The vocabulary above is defined precisely in [`CONTEXT.md`](CONTEXT.md); the decisions behind it live
|
|
150
|
+
in [`docs/adr/`](docs/adr/).
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Requirements
|
|
155
|
+
|
|
156
|
+
- **[uv](https://github.com/astral-sh/uv)** — the Python toolchain/package manager used by this project.
|
|
157
|
+
- **Python 3.13+** (uv can provision this for you).
|
|
158
|
+
- **Godot 4.4+** — `gda` targets Godot 4.x with a minimum of 4.4; 4.6 is the tested baseline.
|
|
159
|
+
3.x is not supported. *(See [ADR-0003](docs/adr/0003-target-godot-version.md).)*
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Installation
|
|
164
|
+
|
|
165
|
+
`gda` is not yet published to a package index; install it from source with `uv`.
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
git clone https://github.com/aigengame/godot-agent.git
|
|
169
|
+
cd godot-agent
|
|
170
|
+
uv sync # create the environment and install dependencies
|
|
171
|
+
uv run gda --help
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
To install `gda` as a standalone CLI on your `PATH`:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
uv tool install .
|
|
178
|
+
gda --help
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### MCP server (`gda-mcp`)
|
|
182
|
+
|
|
183
|
+
`gda` ships a stdio [MCP](https://modelcontextprotocol.io) server behind an optional `[mcp]` extra,
|
|
184
|
+
so any MCP-speaking agent can drive Godot through it. Try it with no install:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
uvx --from "gda[mcp] @ git+https://github.com/aigengame/godot-agent" gda-mcp
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Register it by dropping the matching config in place. `gda-mcp` picks your Godot project from
|
|
191
|
+
`GDA_PROJECT` if set, otherwise the client's workspace `roots`, otherwise the working directory
|
|
192
|
+
(ADR-0014). An explicitly set `GDA_PROJECT` that is not a valid project is reported as an error, not
|
|
193
|
+
silently replaced.
|
|
194
|
+
|
|
195
|
+
**Claude Code** — project scope, `.mcp.json` at the repo root (auto-detects the project via `roots`):
|
|
196
|
+
|
|
197
|
+
```json
|
|
198
|
+
{
|
|
199
|
+
"mcpServers": {
|
|
200
|
+
"gda-mcp": {
|
|
201
|
+
"command": "uvx",
|
|
202
|
+
"args": ["--from", "gda[mcp] @ git+https://github.com/aigengame/godot-agent", "gda-mcp"]
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
User scope (every project) — the CLI, which writes `~/.claude.json`:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
claude mcp add --scope user gda-mcp -- \
|
|
212
|
+
uvx --from "gda[mcp] @ git+https://github.com/aigengame/godot-agent" gda-mcp
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Codex** — project scope, `.codex/config.toml` at the repo root (the project must be trusted):
|
|
216
|
+
|
|
217
|
+
```toml
|
|
218
|
+
[mcp_servers.gda-mcp]
|
|
219
|
+
command = "uvx"
|
|
220
|
+
args = ["--from", "gda[mcp] @ git+https://github.com/aigengame/godot-agent", "gda-mcp"]
|
|
221
|
+
|
|
222
|
+
[mcp_servers.gda-mcp.env]
|
|
223
|
+
GDA_PROJECT = "/absolute/path/to/your/godot/project"
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
User scope (every project) — the same table in `~/.codex/config.toml`, or add it with the CLI
|
|
227
|
+
(Codex has no workspace variable, so `GDA_PROJECT` stays an absolute path):
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
codex mcp add gda-mcp --env GDA_PROJECT=/absolute/path/to/your/godot/project -- \
|
|
231
|
+
uvx --from "gda[mcp] @ git+https://github.com/aigengame/godot-agent" gda-mcp
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Cursor** — project scope, `.cursor/mcp.json` at the repo root (`${workspaceFolder}` tracks the open
|
|
235
|
+
project):
|
|
236
|
+
|
|
237
|
+
```json
|
|
238
|
+
{
|
|
239
|
+
"mcpServers": {
|
|
240
|
+
"gda-mcp": {
|
|
241
|
+
"type": "stdio",
|
|
242
|
+
"command": "uvx",
|
|
243
|
+
"args": ["--from", "gda[mcp] @ git+https://github.com/aigengame/godot-agent", "gda-mcp"],
|
|
244
|
+
"env": {
|
|
245
|
+
"GDA_PROJECT": "${workspaceFolder}",
|
|
246
|
+
"PATH": "/opt/homebrew/bin:/usr/local/bin:${userHome}/.local/bin:${env:PATH}"
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
User scope (every project) — the same config in `~/.cursor/mcp.json`, but set `GDA_PROJECT` to an
|
|
254
|
+
absolute path (`${workspaceFolder}` is only reliable in the project-level file). Cursor has no
|
|
255
|
+
`mcp add` shell command — register via the JSON above or the Settings → MCP UI.
|
|
256
|
+
|
|
257
|
+
> Cursor and Claude Desktop are GUI-launched with a minimal `PATH`, so a bare `uvx` may not resolve —
|
|
258
|
+
> the Cursor snippet above repairs it in `env`; if `uvx` is still not found (or for Claude Desktop),
|
|
259
|
+
> use an absolute path (`which uvx`) for `command`. Full recipes — user vs project scope, Claude
|
|
260
|
+
> Desktop, and per-agent project pinning — are in the
|
|
261
|
+
> [registration recipes](docs/gda-mcp-registration.md). Once `gda` is on PyPI
|
|
262
|
+
> ([#207](https://github.com/aigengame/godot-agent/issues/207)), the `@ git+…` part drops to just
|
|
263
|
+
> `"gda[mcp]"`.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Quick start
|
|
268
|
+
|
|
269
|
+
Point `gda` at your Godot binary and ask for the engine version:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
# Use the GDA_GODOT environment variable (or the --godot flag, or the default path)
|
|
273
|
+
export GDA_GODOT="/path/to/Godot"
|
|
274
|
+
|
|
275
|
+
gda info --json
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
```json
|
|
279
|
+
{"major":4,"minor":6,"patch":3,"hex":263683,"status":"stable","build":"official","hash":"7d41c59c457bd5a245092b4e7eb2d833e3b3f8c3","string":"4.6.3-stable (official)","timestamp":0}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Without `--json`, `gda info` prints the human-readable version string (`4.6.3-stable (official)`).
|
|
283
|
+
All engine and script diagnostics go to **stderr**, so stdout is always clean JSON you can pipe:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
gda info --json | jq .major # → 4
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Create a scene headlessly and read its structured tree back:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
gda scene create game/main.tscn --root-type Node2D --json
|
|
293
|
+
# {"path":"game/main.tscn","root_name":"main","root_type":"Node2D"}
|
|
294
|
+
|
|
295
|
+
gda scene get game/main.tscn --json
|
|
296
|
+
# {"path":"game/main.tscn","root":{"name":"main","type":"Node2D","children":[]}}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Add a node into that scene and verify it landed — nodes are addressed by their path relative to
|
|
300
|
+
the scene root (`.` is the root itself):
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
gda node add game/main.tscn --type Sprite2D --name Hero --json
|
|
304
|
+
# {"scene_path":"game/main.tscn","path":"Hero","name":"Hero","type":"Sprite2D","script_class":null}
|
|
305
|
+
|
|
306
|
+
gda node list game/main.tscn --json
|
|
307
|
+
# {"scene_path":"game/main.tscn","root":{"name":"main","type":"Node2D","path":".","children":[{"name":"Hero","type":"Sprite2D","path":"Hero","children":[]}]}}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Read a node's properties as typed JSON, set one (the CLI value is coerced to the property's declared
|
|
311
|
+
Godot type), and verify the change round-trips via `get`:
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
gda node set game/main.tscn --node Hero --property position --value 10,20 --json
|
|
315
|
+
# {"scene_path":"game/main.tscn","path":"Hero","property":"position","type":"Vector2","value":[10.0,20.0]}
|
|
316
|
+
|
|
317
|
+
gda node get game/main.tscn --node Hero --json
|
|
318
|
+
# {"scene_path":"game/main.tscn","path":"Hero","name":"Hero","type":"Sprite2D","properties":[…,{"name":"position","type":"Vector2","value":[10.0,20.0]},…]}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Enumerate a project's scenes, then delete one — `scene list` walks the project's `res://` tree, so it
|
|
322
|
+
needs a project context (`--project`, `$GDA_PROJECT`, or a cwd that is a project):
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
gda scene list --project game --json
|
|
326
|
+
# {"scenes":[{"path":"res://main.tscn","root_name":"main","root_type":"Node2D"}]}
|
|
327
|
+
|
|
328
|
+
gda scene delete res://main.tscn --project game --json
|
|
329
|
+
# {"path":"res://main.tscn","root_name":"main","root_type":"Node2D"}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Usage
|
|
335
|
+
|
|
336
|
+
### Command surface
|
|
337
|
+
|
|
338
|
+
`gda` commands are **grouped by Godot domain object** and use a small, consistent verb vocabulary,
|
|
339
|
+
so the same verb means the same thing in every group:
|
|
340
|
+
|
|
341
|
+
```
|
|
342
|
+
gda <group> <command> [options] # domain commands, e.g. gda scene create
|
|
343
|
+
gda <meta-command> [options] # meta commands about gda/the engine, e.g. gda info
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
| Verb | Meaning |
|
|
347
|
+
| ------------------------ | ---------------------------------------------------------------- |
|
|
348
|
+
| `create` / `delete` | Make / remove a **standalone** entity (scene, script, resource) |
|
|
349
|
+
| `add` / `remove` | Add / remove a **sub-entity** within a container (node → scene) |
|
|
350
|
+
| `get` / `list` | Read one entity / enumerate many |
|
|
351
|
+
| `set` | Mutate a property |
|
|
352
|
+
| domain verbs | `play`, `run`, `export`, `import`, … kept with their natural meaning |
|
|
353
|
+
|
|
354
|
+
> The taxonomy and naming rules are specified in
|
|
355
|
+
> [ADR-0005](docs/adr/0005-cli-command-taxonomy.md). `gda --help` (and `gda <group> --help`) is the
|
|
356
|
+
> authoritative list of what is installed; the table below mirrors the shipped Phase-1 surface.
|
|
357
|
+
|
|
358
|
+
### Command reference
|
|
359
|
+
|
|
360
|
+
Every command supports `--json` and `--schema`; commands that read or mutate a `res://` path resolve
|
|
361
|
+
a [project context](#project-context), and mutating commands run that project's code
|
|
362
|
+
([project code execution](#project-code-execution)). Run `gda <group> <command> --help` for full
|
|
363
|
+
flags.
|
|
364
|
+
|
|
365
|
+
**Meta** — about `gda` / the engine itself
|
|
366
|
+
|
|
367
|
+
| Command | What it does |
|
|
368
|
+
| ------- | ------------ |
|
|
369
|
+
| `gda info` | Report the Godot engine version info. |
|
|
370
|
+
|
|
371
|
+
**`scene`** — scene files (`.tscn`)
|
|
372
|
+
|
|
373
|
+
| Command | What it does |
|
|
374
|
+
| ------- | ------------ |
|
|
375
|
+
| `scene create` | Create a new `.tscn` with the given root node type. |
|
|
376
|
+
| `scene get` | Read a scene and report its structured node tree. |
|
|
377
|
+
| `scene list` | Enumerate the `.tscn` scenes in the resolved project. |
|
|
378
|
+
| `scene get-exports` | List the `@export` properties a scene's nodes' scripts declare. |
|
|
379
|
+
| `scene delete` | Delete a scene file and report what was removed. |
|
|
380
|
+
|
|
381
|
+
**`node`** — nodes within a scene file
|
|
382
|
+
|
|
383
|
+
| Command | What it does |
|
|
384
|
+
| ------- | ------------ |
|
|
385
|
+
| `node add` | Add a node under a parent (built-in type or `class_name` script). |
|
|
386
|
+
| `node get` | Read a node's properties (by node path) as typed JSON. |
|
|
387
|
+
| `node list` | List a scene's node tree with each node's path relative to the root. |
|
|
388
|
+
| `node set` | Set a node property, coercing the value to its declared Godot type. |
|
|
389
|
+
| `node remove` | Remove a node (and its subtree) by node path. |
|
|
390
|
+
| `node duplicate` | Duplicate a node (and its subtree) under its parent. |
|
|
391
|
+
| `node move` | Reparent a node (and its subtree) under a new parent. |
|
|
392
|
+
| `node connect-signal` | Wire a source node's signal to a target node's method. |
|
|
393
|
+
| `node disconnect-signal` | Unwire an existing signal→method connection. |
|
|
394
|
+
|
|
395
|
+
**`script`** — GDScript files (`.gd`)
|
|
396
|
+
|
|
397
|
+
| Command | What it does |
|
|
398
|
+
| ------- | ------------ |
|
|
399
|
+
| `script create` | Create a new `.gd` script from a template or verbatim `--content`. |
|
|
400
|
+
| `script get` | Read a script's source plus its `class_name` / `extends` metadata. |
|
|
401
|
+
| `script list` | Enumerate the `.gd` scripts in the resolved project. |
|
|
402
|
+
| `script set` | Edit a script via search-replace, line-range, or full overwrite. |
|
|
403
|
+
| `script delete` | Delete a script file and report what was removed. |
|
|
404
|
+
| `script attach` | Attach a `.gd` script to a node (by node path) in a scene. |
|
|
405
|
+
| `script validate` | Syntax/compile-check a `.gd` script. |
|
|
406
|
+
|
|
407
|
+
**`project`** — the project as a whole (settings, autoloads, static analysis)
|
|
408
|
+
|
|
409
|
+
| Command | What it does |
|
|
410
|
+
| ------- | ------------ |
|
|
411
|
+
| `project info` | Report project metadata (name, main scene, viewport, engine version). |
|
|
412
|
+
| `project get` | Read a single project setting by section/key as typed JSON. |
|
|
413
|
+
| `project set` | Set a project setting, coercing the value to its declared type. |
|
|
414
|
+
| `project add-autoload` | Register an autoload singleton (name → script/scene). |
|
|
415
|
+
| `project remove-autoload` | Unregister an autoload singleton by name. |
|
|
416
|
+
| `project find-references` | Find every project file that references a given resource. |
|
|
417
|
+
| `project dependencies` | Map each scene/resource to the resources it depends on. |
|
|
418
|
+
| `project find-unused-resources` | Find resource files that nothing references. |
|
|
419
|
+
| `project statistics` | Report the project's file/line counts, autoloads, and more. |
|
|
420
|
+
|
|
421
|
+
**`resource`** — resource files (`.tres`)
|
|
422
|
+
|
|
423
|
+
| Command | What it does |
|
|
424
|
+
| ------- | ------------ |
|
|
425
|
+
| `resource create` | Create a new `.tres` resource of the given type. |
|
|
426
|
+
| `resource get` | Read a `.tres` resource's properties as typed JSON. |
|
|
427
|
+
| `resource set` | Set a `.tres` property, coercing the value to its declared type. |
|
|
428
|
+
| `resource delete` | Delete a `.tres` resource file and report what was removed. |
|
|
429
|
+
| `resource uid` | Resolve a resource UID ↔ its `res://` path in both directions. |
|
|
430
|
+
|
|
431
|
+
**`export`** — export presets and artifacts
|
|
432
|
+
|
|
433
|
+
| Command | What it does |
|
|
434
|
+
| ------- | ------------ |
|
|
435
|
+
| `export list` | Enumerate the project's export presets (name, platform, …). |
|
|
436
|
+
| `export get` | Report one preset's details plus export-template install status. |
|
|
437
|
+
| `export run` | Export a named preset (`release` / `debug` / `pack`) to a destination. |
|
|
438
|
+
|
|
439
|
+
**`shader`** — shader files (`.gdshader`)
|
|
440
|
+
|
|
441
|
+
| Command | What it does |
|
|
442
|
+
| ------- | ------------ |
|
|
443
|
+
| `shader create` | Create a new `.gdshader` from a template or verbatim `--content`. |
|
|
444
|
+
| `shader get` | Read a shader's source plus its `shader_type`. |
|
|
445
|
+
| `shader set` | Edit a `.gdshader` via search-replace, line-range, or full overwrite. |
|
|
446
|
+
|
|
447
|
+
**`theme`** — theme resources (`.tres`)
|
|
448
|
+
|
|
449
|
+
| Command | What it does |
|
|
450
|
+
| ------- | ------------ |
|
|
451
|
+
| `theme create` | Create a new, loadable `.tres` Theme resource (no-clobber). |
|
|
452
|
+
|
|
453
|
+
### Global flags
|
|
454
|
+
|
|
455
|
+
| Flag | Description |
|
|
456
|
+
| ---------- | ------------------------------------------------------------------- |
|
|
457
|
+
| `--json` | Emit the result as a single JSON object on stdout. Without it, commands print a concise human-readable rendering. |
|
|
458
|
+
| `--schema` | Emit the command's input/output JSON Schema contract (no Godot spawned). |
|
|
459
|
+
| `--godot` | Path to the Godot binary (overrides `$GDA_GODOT` and the default). |
|
|
460
|
+
| `--project` | Godot project directory for `res://` resolution (overrides `$GDA_PROJECT`; defaults to the current directory if it is a project). Domain commands only. Resolving a project runs that project's code — see [Project code execution](#project-code-execution). |
|
|
461
|
+
| `--help` | Show usage for `gda` or any command. |
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Configuration
|
|
466
|
+
|
|
467
|
+
`gda` resolves the Godot binary in this order (highest precedence first):
|
|
468
|
+
|
|
469
|
+
1. The **`--godot <path>`** flag.
|
|
470
|
+
2. The **`GDA_GODOT`** environment variable.
|
|
471
|
+
3. A **default development path** — `~/Applications/Godot.app/Contents/MacOS/Godot` (macOS).
|
|
472
|
+
On other platforms, set `GDA_GODOT` or pass `--godot`.
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
gda info --godot "/Applications/Godot.app/Contents/MacOS/Godot" --json
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Project context
|
|
479
|
+
|
|
480
|
+
Domain commands resolve a **Godot project** so that `res://` paths and a scene's
|
|
481
|
+
inter-resource references resolve deterministically, in this order (highest precedence first):
|
|
482
|
+
|
|
483
|
+
1. The **`--project <dir>`** flag.
|
|
484
|
+
2. The **`GDA_PROJECT`** environment variable.
|
|
485
|
+
3. The **current directory**, when it is a Godot project (contains `project.godot`).
|
|
486
|
+
|
|
487
|
+
A named directory must be a project, or `gda` reports it as an error. When none resolves, `gda`
|
|
488
|
+
runs **projectless** — only filesystem paths (absolute or cwd-relative) resolve, not `res://`.
|
|
489
|
+
Path normalization happens once at the CLI layer: `res://` / `user://` / `uid://` pass through to
|
|
490
|
+
the engine; filesystem paths get `~` expanded. See
|
|
491
|
+
[ADR-0006](docs/adr/0006-project-context-and-path-resolution.md).
|
|
492
|
+
|
|
493
|
+
```bash
|
|
494
|
+
gda scene get res://main.tscn --project ~/game --json
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### Project code execution
|
|
498
|
+
|
|
499
|
+
Resolving a project so `res://` paths work runs Godot against that project, and Godot runs
|
|
500
|
+
some of the project's own code as part of that. Concretely:
|
|
501
|
+
|
|
502
|
+
- **Autoloads run on every `--project` operation.** When a project is resolved, the engine
|
|
503
|
+
constructs the project's autoload singletons at startup — before the command's own work runs —
|
|
504
|
+
so their `_init` (and `_ready`) execute on **every** operation, including read-only ones like
|
|
505
|
+
`scene get` and `node list`. Without a resolved project, no autoloads are registered, so they do
|
|
506
|
+
not run.
|
|
507
|
+
- **Commands that instantiate a scene execute that scene's attached scripts' constructors.** A
|
|
508
|
+
command that needs a live node tree — every mutating command (`node add`, `node set`,
|
|
509
|
+
`node remove`, …), and `node get` (which reports runtime property defaults the stored data does
|
|
510
|
+
not carry) — loads and instantiates the scene, which constructs each node and runs the `_init` of
|
|
511
|
+
any script attached to a node in it. Commands that only read the stored scene data
|
|
512
|
+
(`scene get`, `scene list`, `node list`) walk it without instantiating, so they do not run those
|
|
513
|
+
scripts.
|
|
514
|
+
|
|
515
|
+
In short: pointing `gda` at a project executes that project's autoload code on every command, and
|
|
516
|
+
any command that instantiates a scene additionally executes that scene's attached scripts' `_init`.
|
|
517
|
+
The tracked decisions on this are [#61](https://github.com/aigengame/godot-agent/issues/61)
|
|
518
|
+
(autoloads) and [#62](https://github.com/aigengame/godot-agent/issues/62) (scene scripts on
|
|
519
|
+
instantiating operations); see also
|
|
520
|
+
[ADR-0009](docs/adr/0009-trust-boundary-trusted-project.md).
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## The structured-output contract
|
|
525
|
+
|
|
526
|
+
Headless Godot interleaves its banner, warnings, and `print()` output into stdout. `gda` solves this
|
|
527
|
+
with a sentinel contract ([ADR-0002](docs/adr/0002-headless-structured-output-contract.md)):
|
|
528
|
+
|
|
529
|
+
- The GDScript payload emits **exactly one** result, wrapped in unique sentinels on stdout:
|
|
530
|
+
|
|
531
|
+
```
|
|
532
|
+
<<<GDA:RESULT>>>{ ...json... }<<<GDA:END>>>
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
- It routes **all** of its own diagnostics to stderr; stdout carries nothing but the contract.
|
|
536
|
+
- `gda` extracts and parses only the bytes between the sentinels, ignoring the surrounding engine
|
|
537
|
+
noise, and surfaces stderr for inspection.
|
|
538
|
+
|
|
539
|
+
This is what makes `gda`'s output safe to consume programmatically, and it generalizes to the
|
|
540
|
+
per-message protocol the daemon will use in Phase 2.
|
|
541
|
+
|
|
542
|
+
### Exit codes (the CLI ABI)
|
|
543
|
+
|
|
544
|
+
A failed `gda` run exits with a small, stable code so a shell or agent can branch on the failure
|
|
545
|
+
**category without parsing the JSON error**:
|
|
546
|
+
|
|
547
|
+
| Exit code | Category | When |
|
|
548
|
+
| --------- | ------------- | --------------------------------------------------------------------- |
|
|
549
|
+
| `0` | — | Success. |
|
|
550
|
+
| `127` | `environment` | The Godot binary could not be launched (shell convention: not found). |
|
|
551
|
+
| `124` | `environment` | Godot launched but did not return before the runner timeout (shell convention: timed out). |
|
|
552
|
+
| `3` | `version` | The detected Godot version is below the supported minimum. |
|
|
553
|
+
| `4` | `operation` | The engine ran but the operation failed — a registered operation error, an engine crash, or an unstructured non-zero exit. |
|
|
554
|
+
| `5` | `parse` | The process claimed success but violated the structured-output contract. |
|
|
555
|
+
|
|
556
|
+
These values are the public ABI; their authoritative source is
|
|
557
|
+
[`src/gda/exit_codes.py`](src/gda/exit_codes.py) — that registry, not this table, defines them.
|
|
558
|
+
The `{"error": {category, code, …}}` envelope carries a **finer `code`** within each category
|
|
559
|
+
(e.g. `path_not_found`, `already_exists`, and `node_not_found` all sit under `operation` / exit `4`).
|
|
560
|
+
The full code → category → exit-code registry, kept in sync with the code by tests, lives in
|
|
561
|
+
[ADR-0002’s `GdaError.code` registry table](docs/adr/0002-headless-structured-output-contract.md#gdaerrorcode-registry).
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
## Development
|
|
566
|
+
|
|
567
|
+
```bash
|
|
568
|
+
uv sync # set up the environment
|
|
569
|
+
|
|
570
|
+
uv run pytest # run the full suite (includes e2e tests against a real Godot)
|
|
571
|
+
uv run pytest -m "not e2e" # unit tests only (no Godot binary required)
|
|
572
|
+
uv run pytest -m e2e # only the end-to-end tests (needs Godot 4.4+ on this machine)
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
The `e2e` tier runs by default with `uv run pytest`, and **fails loudly** — naming the
|
|
576
|
+
resolved path and how to fix it — if no Godot binary is found there, rather than skipping.
|
|
577
|
+
Deselect the whole tier with `-m "not e2e"` (CI's per-PR job uses exactly this) when you
|
|
578
|
+
have no engine; use `-m e2e` to run only the e2e tests.
|
|
579
|
+
|
|
580
|
+
### Project layout
|
|
581
|
+
|
|
582
|
+
```
|
|
583
|
+
src/gda/
|
|
584
|
+
cli.py # CLI entrypoint (Typer); command groups, --json/--schema, binary override
|
|
585
|
+
binary.py # Godot binary resolution (flag > $GDA_GODOT > default)
|
|
586
|
+
runner.py # GodotRunner seam (Protocol) + SubprocessGodotRunner (one-shot headless)
|
|
587
|
+
parser.py # sentinel-contract result parser (ADR-0002)
|
|
588
|
+
models.py # typed I/O models (Pydantic) backing --json and --schema (ADR-0004)
|
|
589
|
+
errors.py # failure classification: shared classify_run + per-command layers
|
|
590
|
+
error_codes.py # the registry of public GdaError.code values (ADR-0002 companion)
|
|
591
|
+
exit_codes.py # the single registry of process exit codes (the CLI ABI)
|
|
592
|
+
ops/operations.gd # GDScript payload, dispatched by operation name
|
|
593
|
+
tests/ # unit tests + e2e tests against a real engine (shared fixtures in conftest.py)
|
|
594
|
+
docs/adr/ # architecture decision records
|
|
595
|
+
CONTEXT.md # the project's shared domain language
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
The only external boundary — spawning a Godot process — sits behind the `GodotRunner` Protocol
|
|
599
|
+
(`runner.py`). Fast unit and CLI tests inject a canned result through that boundary; the e2e suite
|
|
600
|
+
drives a real engine. Everything between the CLI and the runner runs as real code in both tiers.
|
|
601
|
+
|
|
602
|
+
---
|
|
603
|
+
|
|
604
|
+
## Roadmap
|
|
605
|
+
|
|
606
|
+
| Phase / component | Delivers | Status |
|
|
607
|
+
| ----------------- | ------------------------------------------------------------------------- | ------ |
|
|
608
|
+
| **Phase 1** | `gda` serving *headless operations* standalone: `info`, structured errors, `--schema`, and the domain command groups `scene`, `node`, `script`, `project` (incl. static-analysis), `resource`, `export`, `shader`, `theme`. | ✅ Surface complete |
|
|
609
|
+
| **`gda-mcp`** | A thin MCP adapter generated mechanically from `--schema` — first on top of Phase 1, following `gda` forward automatically. | ✅ Shipped |
|
|
610
|
+
| **Phase 2** | `gda` also serving *live operations* through `gda-daemon`'s persistent engine connection. | 🗓 Planned |
|
|
611
|
+
|
|
612
|
+
Track progress and proposals on the [issue tracker](https://github.com/aigengame/godot-agent/issues).
|
|
613
|
+
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
## Contributing
|
|
617
|
+
|
|
618
|
+
Contributions are welcome. Before starting:
|
|
619
|
+
|
|
620
|
+
- Read [`CONTEXT.md`](CONTEXT.md) to align with the project's shared language, and review the
|
|
621
|
+
relevant [ADRs](docs/adr/) for the area you're touching.
|
|
622
|
+
- Issues and PRDs live as GitHub issues in [`aigengame/godot-agent`](https://github.com/aigengame/godot-agent/issues).
|
|
623
|
+
|
|
624
|
+
Commits follow the [Conventional Commits](https://www.conventionalcommits.org/) specification.
|
|
625
|
+
|
|
626
|
+
> **Working with an AI coding agent?** This project is built to be agent-navigable.
|
|
627
|
+
> [`AGENTS.md`](AGENTS.md) is the entry point for coding agents — it wires in the project's
|
|
628
|
+
> rules, domain docs, and skills.
|
|
629
|
+
|
|
630
|
+
---
|
|
631
|
+
|
|
632
|
+
## Acknowledgements
|
|
633
|
+
|
|
634
|
+
`gda`'s design draws on two reference implementations from the Godot + agent ecosystem: the
|
|
635
|
+
one-shot-headless approach of `godot-mcp` and the persistent editor-plugin approach of
|
|
636
|
+
`godot-mcp-pro`. `gda` deliberately adopts a **hybrid, phased** strategy that combines their
|
|
637
|
+
strengths (see [ADR-0001](docs/adr/0001-godot-integration-mechanism.md)).
|
|
638
|
+
|
|
639
|
+
---
|
|
640
|
+
|
|
641
|
+
## License
|
|
642
|
+
|
|
643
|
+
Released under the [MIT License](LICENSE). Copyright (c) 2026 aigengame.
|