Repository navigation
Verb reference
Introspection (pure parse):
| verb | what it answers |
|---|---|
scene <file> [--props] [--paths] |
a .tscn/.tres's node tree and resources; --props every [resource]/[sub_resource] property value, packed data elided, each id verbatim — the address the write verbs take |
scene-diff <file> [--git <ref>] · scene-diff <old> <new>
|
a structural diff — nodes, properties, resources, reparents — keyed the way the write verbs address them |
refs <symbol> [--tests] |
every reference to a symbol, grouped by kind — including what Godot wires: a scene [connection] naming it as signal= or method= prints under scene connections, and an autoload name is indexed like a class_name (its project.godot entry the definition, Name. / Name) / Name, a typed ref); a bare handler in is_connected(…, h) / disconnect(…, h) is a call site; signal hits on a receiver the index cannot type (x.sig.connect(, emit_signal(&"sig", connect("sig", is_connected("sig", has_signal("sig", has_user_signal("sig", Signal(obj, "sig") print under dynamic (untyped receiver), and (no references found) prints only when that bucket is empty too |
orphans [--tests] |
tracked files nothing references |
autoloads |
the project.godot autoload census, grouped by suffix, layout flagged |
stats [<dir>...] [--by dir] [--json] [--ratchet FILE] |
files, lines and code lines (non-blank, non-comment, docstrings are comment) per bucket: .gd by role (game, tests, tools, vendored, from [stats]), .gdshader, .tscn, .tres; one row per tests subdir with a func test_ count; --by dir splits game .gd by top-level dir; zero files scanned exits 1 |
tiles <file> [--layer NAME] [--cols] [--rows] [--at X,Y] [--region X0,Y0,X1,Y1] |
a TileMapLayer's grid: cell count, bounds, tile-kind histogram, per-column/row counts |
Scene surgery (edits only the lines it was asked to, or refuses and says why):
| verb | what it does |
|---|---|
scene set <file> <node-path> <prop> <value> |
replace or append one property on one node |
scene set <file> --resource <prop> <value> · --sub-resource <id> <prop> <value>
|
the same on a .tres's [resource] body or a [sub_resource] by the id scene --props prints |
scene rename <file> <node-path> <new-name> |
rename a node and every NodePath, parent= and animation track that references it — never prose, never an export name |
scene add <file> <parent-path> <name> <type> [--script res://x.gd] |
add a node; a --script ref is minted uid-in-refs from the sidecar, or refused |
scene add <file> <parent-path> <name> --instance res://x.tscn |
add an instance node; its ref is minted from the scene's own uid, or refused |
scene rm <file> <node-path> |
remove a node with its descendants, connections and editable markers; prunes an ext_resource nothing else uses |
scene reparent <file> <node-path> <new-parent> |
move a subtree and fix its NodePaths |
scene connect <file> <signal> <from> <to> <method> [--flags N] · scene disconnect …
|
author or remove one [connection]; ambiguous matches are refused, --flags names one |
scene canonicalize <file>... [--elide-defaults] [--respell] [--order] |
restore what PackedScene.pack() drops — uid-in-refs, the header uid, index= on instance children; --elide-defaults also removes assignments equal to the script's @export default; --respell re-spells floats in the saver's shortest form and wraps a bare list on an Array[T] export as Array[T]([...]); --order puts a scripted section's properties in declaration order — line edits only, anything unprovable named and left alone |
refs --retarget <old-res-path> <new-res-path> [--dry-run] |
after a git mv: rewrite every ext_resource path and exact preload/load literal naming the old path; anything unprovable is SKIPPED with a reason, and skips exit 1 |
refs --rename <old> <new> [--dry-run] |
rename a class_name, method, signal or autoload across .gd, .tscn, .tres and project.godot as one plan — only the identifier token at each typed hit, a [connection]'s signal=/method= value, an autoload's key; all or nothing: an <old> or <new> the engine also names (a method or signal on any class — play, pressed, _ready), any dynamic hit, string or unproven occurrence, a defined <new> or an undefined <old> refuses the whole rename with every site named (exit 1), and a repeat prints already renamed; a res:// path that matches is never rewritten, only listed as a PATH line; tests/ is always in scope |
autoloads add <Name> <res://path> [--dry-run] · autoloads rm <Name>
|
declare or remove one project.godot autoload as an enabled singleton (Name="*res://path"); a missing file, a non-canonical path, a bad name (not an identifier, an engine class or a project class_name), a name already pointing elsewhere or declared disabled is refused, and a repeat is a no-op |
tiles paint <file> --layer NAME --region X0,Y0,X1,Y1 --tile SRC/AX,AY[/ALT] · tiles erase …
|
fill or clear a rectangle of one TileMapLayer; only that property's base64 is regenerated |
Localization (pure parse; writes one file, or refuses):
| verb | what it does |
|---|---|
pot [--dry-run | --check] |
write the POT that the editor's POT Generation writes, byte for byte (Godot 4.7), from project.godot's internationalization/locale/translations_pot_files to [pot] output; prints files scanned and msgids written; --check exits 1 on drift with a MISSING/EXTRA line per msgid, or one DRIFT line when only references, comments or order moved; a non-.gd entry, an empty manifest, a parse error or a constant only the editor can fold is refused with exit 2 |
The installer: install-runners [--force] [--diff] — see Runners and install-runners.
Agent UI skill: install-skills [--runtime codex|claude] [--dest ROOT] [--check] [--diff] [--force] (3.9.0). Installs bundled godot-ui below a skills root: .agents/skills for Codex or .claude/skills for Claude by default. --dest selects another root, including user scope. --check is read-only and returns 1 for missing/outdated files; --diff previews without writing. Differing regular files require --force. Symlinks below the selected root are refused; an explicitly selected root may itself be a symlink. The skill defines component contracts, native layout ownership and rendered-state proof; static tooling does not certify visual fit.
The gates: check <gate> for one, check all for the eight, check <gate> --help for that
gate's contract, config and scope. check uid --fix applies the repairs the gate already computes.
See also: The eight gates, Configuration reference.
godot-devkit stats [dir ...] [--by dir] [--json] [--ratchet FILE] reports physical/code/comment/doc/inline/docstring/test counts from git-known files. The shared GDScript tokenizer distinguishes real comments from hashes and escaped quotes in strings. comments includes own-line # comments and standalone docstrings; docs is the ## subset and is not added twice. Inline comment and docstring counts are separately limited so prose cannot move to evade a cap.
Capture a baseline once with godot-devkit stats --json > tools/ci/stats-baseline.json. Run the same scope and role roots with godot-devkit stats --ratchet tools/ci/stats-baseline.json. Comment/doc/inline/docstring limits and test-code/case limits must not grow. Production code counts are context rather than a dilution ratio. Existing baseline limits are checked against origin/main, or HEAD in a standalone repository; scope changes, deleted caps and cap increases fail. Malformed input exits 2 and a zero-file scan fails.
Introduced in 3.8.0. Table readers must accept the new columns; JSON fields are additive. No engine process or runtime dependency is introduced.
Getting started
Reference
Guides
Upgrading
Maintaining