Build a scene
Every command here takes a viewport path such as /root/Main/Player, so you name nodes the way you think about them rather than by line number.
Create the scene
godot-cli scene new --output scenes/main.tscn --root-name Main --root-type Node2D --project-root .
That writes a gd_scene header and a single root node. To start from something bigger, copy a built-in template:
godot-cli scene template list --json
godot-cli scene template copy 2d/top_down_player --output scenes/player.tscn --project-root .
The templates are 2d/character_body, 2d/top_down_player, 2d/camera_rig, 3d/static_body, and ui/control_root, each with its collision shape or mesh already in place. scene template show <id> prints the node tree before you copy, and --rename-node Player:Hero renames as it copies.
Add nodes and set properties
godot-cli scene node add scenes/main.tscn --parent /root/Main --name Player \
--type CharacterBody2D --project-root .
godot-cli scene set-property scenes/main.tscn --node-name Player \
--property position --value "Vector2(320, 180)" --project-root .
Property values go through the Variant parser, so a float lands as 16.0 on a property and as 2 inside Vector2(2, 1.5), which is what the editor writes for each. Pass --raw-value when you have already formatted the text yourself.
scene node add also takes --property and --value directly, repeated as many times as needed, so a Control's full set of anchors goes on in the same command that creates it.
Sub-resources
A collision shape, a gradient, or a material stored inside the scene is a sub_resource. Create it, then reference its id from a node:
godot-cli scene sub add scenes/main.tscn --type CapsuleShape2D \
--property radius --value 16.0 --project-root . --json
{ "id": "CapsuleShape2D_f8o8q", "type": "CapsuleShape2D", "summary": "added sub_resource CapsuleShape2D_f8o8q" }
godot-cli scene node add scenes/main.tscn --parent /root/Main/Player --name Collision \
--type CollisionShape2D --property shape --value 'SubResource("CapsuleShape2D_f8o8q")' --project-root .
Ids are generated the way Godot generates them. To get a predictable one, use a patch with id_hint, which produces CapsuleShape2D_player instead of a random suffix.
External files
Scripts, textures, and other scenes are ext_resource entries. scene ext add registers one, but the usual case is registering and assigning in a single step, which the assign_ext patch op does:
{
"ops": [
{ "op": "assign_ext", "path": "/root/Main/Player", "property": "script",
"type": "Script", "res_path": "res://scripts/player.gd" },
{ "op": "assign_ext", "path": "/root/Main/Player/Sprite", "property": "texture",
"type": "Texture2D", "res_path": "res://art/player.png" }
]
}
godot-cli scene apply scenes/main.tscn --patch attach.json --project-root . --json
When the path is already registered, assign_ext reuses the existing id instead of adding a second entry, so several sprites can share one texture safely.
To see what a scene depends on and whether those files exist:
godot-cli scene refs scenes/main.tscn --project-root . --json
Restructure without breaking the tree
Godot stores a node's parent as a path relative to the scene root, so renaming a node means rewriting the parent attribute of everything under it. This is the edit that goes wrong by hand.
godot-cli scene node rename scenes/main.tscn /root/Main/Player --name Hero --project-root .
godot-cli scene node reparent scenes/main.tscn /root/Main/Hero --parent /root/Main/Playfield --project-root .
After both commands the descendants have followed:
[node name="Playfield" type="Node2D" parent="." unique_id=248520651]
[node name="Hero" type="CharacterBody2D" parent="Playfield" unique_id=1278869255]
[node name="Sprite" type="Sprite2D" parent="Playfield/Hero" unique_id=1745944107]
scene node remove <path> deletes a node, and takes --recursive when it has children.
Move or rename a file
A file that moves by hand leaves every res:// reference to it stale. project move does the move and the repointing together: the file and its .uid sidecar, every scene and resource that referenced it, catalog manifests, and settings in project.godot such as the main scene.
godot-cli project move --project-root . --from scripts/player.gd --to scripts/hero.gd
godot-cli project move --project-root . --from res://scenes/level_2.tscn --to res://levels/two.tscn
--dry-run lists what would change.
Check the result
godot-cli scene node list scenes/main.tscn --json
godot-cli scene inspect scenes/main.tscn --json
godot-cli scene validate scenes/main.tscn --project-root . --json
node list gives the tree with paths and types. inspect gives sections with parsed property values, including the Variant type of each. validate exits 1 on duplicate ids, out-of-order sections, missing res:// targets, or stale UIDs.
Next
Longer edits are better described once and applied in one write. See batch edits with intents and patches.