name: gm-scaffold description: | Scaffold a new Godot project: project.godot + addons + base directories + e2e/conftest.py + initial git commit. Lifetime-once role — runs only on fresh projects. Explicit invocation only — use /gm-scaffold. disable-model-invocation: true
GodotMaker Scaffold
$ARGUMENTS
You are scaffolding a brand-new Godot project. This is the lifetime-once foundation step: project.godot + addons + base directory layout + e2e/conftest.py + initial git commit. No game design happens here — that comes in /gm-gdd.
Session Setup
FIRST ACTION — before anything else: Write scaffold to .godotmaker/current_role.
session_start.py deletes .godotmaker/current_role on every new session
(including /clear, restart, and resume) so a stale role from a previous
session can't grant unintended write permissions. That is why every gm-*
skill re-writes its role as the first action — if a session is interrupted
mid-scaffold and you don't re-issue /gm-scaffold, write hooks will start
denying file writes silently. Re-issuing the slash command re-establishes
the role.
Resume Check
Read .godotmaker/stage.jsonl (treat as empty if missing) — each line is {"role": X, "ts": Y}. Find the last event in the file.
Scaffold is lifetime-once — its event gets cleared after each tag's finalize (stage.jsonl is truncated). So determine "already scaffolded" from project artifacts on disk, not the event log:
- If
project.godotexists ANDaddons/gecs/exists ANDgit loghas at least one commit → STOP. Tell the user:"Project is already scaffolded. Recommended next: /gm-gdd. If you need to re-scaffold (rare — usually addon migrations are handled by
tools/publish.py), just tell me." - Otherwise → proceed.
Hard Rules
- Do NOT design the game here.
- Use fixed 2D defaults. Use the project-scaffold default viewport (
1280x720) and rendering method. - Do NOT create Component/System stubs.
- All addons MUST come from the active runtime's
config/addon_versions.json— do not guess versions. - Initial git commit is mandatory and must include import metadata. Run
<godot_path> --headless --importbefore the commit and include generated.uidfiles.
Scaffold Steps
1. Gather minimal inputs
Use AskUserQuestion to ask for:
- Game name (snake_case, used as project directory name)
Use 2D defaults. GodotMaker currently generates 2D games only; do not ask the user to choose a dimension during scaffold.
Other settings (genre, art style, mechanics) are deferred to /gm-gdd.
2a. Run project-scaffold skill
Invoke .claude/skills/project-scaffold/SKILL.md with only the gathered game
name. Use project-scaffold defaults for every other template variable. Run only
Steps 1, 2, and 3. Do not run genre adaptations. From Step 3's template table,
fill just these four templates:
project.godot.tmpl→project.godotmain_scene.tmpl→scenes/main.tscnworld_scene.tmpl→scenes/game_world.tscngitignore.tmpl→.gitignore
That gives you the directory tree, an empty Main entry-point scene, the
gameplay scene placeholder, and a sensible .gitignore.
The remaining items in project-scaffold belong to other parts of the pipeline:
| project-scaffold concern | Owner |
|---|---|
| Component / System / test files (with or without a Game Plan) | decomposer in /gm-gdd writes STRUCTURE.md first; workers in /gm-build create the actual .gd files |
Genre Adaptations — [physics] / [input] (Step 4) |
/gm-gdd |
| Post-Scaffold publish (Step 5) | already done by tools/publish.py before scaffold runs |
| Addon install | step 2b below |
2b. Install addon-only directories
Install required addons from the active runtime's config/addon_versions.json.
Follow project-scaffold/references/addons.md.
Required result:
addons/gecs/plugin.cfgaddons/gdUnit4/plugin.cfgaddons/gdUnit4/bin/GdUnitCmdTool.gdaddons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gdaddons/godot_e2e/plugin.cfgaddons/godot_e2e/automation_server.gd- no
addons/*/project.godot
Then enable plugin entries in project.godot and register
AutomationServer.
.godotmaker/config.yaml is created by tools/publish.py before this skill
runs — verify it exists and move on.
E2E conftest.py template
Write the following to e2e/conftest.py:
import os
import pytest
from godot_e2e import GodotE2E
GODOT_PROJECT = os.path.join(os.path.dirname(__file__), "..")
GODOT_CONFIG = os.path.join(GODOT_PROJECT, ".claude", "godotmaker.yaml")
def _read_godot_path():
try:
with open(GODOT_CONFIG, "r", encoding="utf-8") as f:
for line in f:
line = line.split("#", 1)[0].strip()
if line.startswith("godot_path:"):
value = line.split(":", 1)[1].strip().strip("\"'")
return value or None
except OSError:
return None
return None
GODOT_PATH = _read_godot_path()
@pytest.fixture(scope="module")
def _game_process():
with GodotE2E.launch(
GODOT_PROJECT,
godot_path=GODOT_PATH,
timeout=15.0,
) as game:
game.wait_for_node("/root/Main", timeout=10.0)
yield game
@pytest.fixture(scope="function")
def game(_game_process):
_game_process.reload_scene()
_game_process.wait_for_node("/root/Main", timeout=5.0)
yield _game_process
3. Initial commit
git init # if not already a git repo
<godot_path> --headless --import # generate .godot/ cache + *.uid sidecars
git add -A
git commit -m "Scaffold: initial Godot project with addons"
Required for isolation: "worktree" in worker dispatch — without HEAD, parallel workers fail with fatal: not a valid object name: 'HEAD'.
4. Verify
python tools/check_project.py <project_dir> --build
--build is the gm-scaffold readiness check — it covers all of:
project.godotexists with[application]addons/gecs/,addons/gdUnit4/,addons/godot_e2e/present- each required addon has
plugin.cfg - no required addon contains a nested
project.godot addons/gdUnit4/bin/GdUnitCmdTool.gdandaddons/gdUnit4/src/core/runners/GdUnitTestCIRunner.gdexistgodot-e2eplugin enabled in[editor_plugins]AutomationServerautoload registered forgodot-e2ee2e/conftest.pyimportsGodotE2E.git/resolvesHEAD(worker worktree isolation needs it)<godot_path> --headless --quitexits 0 with no blocking Godot diagnostics
The command must exit 0 with no [FAIL] entries for scaffold to be
considered done. Shutdown-note [WARN] entries do not block scaffold.
If .claude/godotmaker.yaml lacks godot_path, re-run tools/publish.py.
Available Skills & Tools
| Skill | Purpose |
|---|---|
| project-scaffold | Project structure + addon installation |
| godot-api | Godot API reference (sanity-check project.godot syntax) |
When Done
After verification passes:
- From the project root run
python tools/append_stage_event.py scaffoldto append a{"role": "scaffold", "ts": "<server-generated UTC>"}line to.godotmaker/stage.jsonl. Do NOT hand-write the JSON or the timestamp — the helper exists so the timestamp comes from the system clock, not your own output. git add -A && git commit -m "chore(scaffold): stage event"- Inform the user:
Scaffold complete. Recommended next: /gm-gdd