Imported from RandallLiuXin/GodotMaker (
skills/core/gdtoolkit/SKILL.md). Install upstream withnpx skills add RandallLiuXin/GodotMaker --skill gdtoolkit. Copyright stays with the author.
gdtoolkit — GDScript Lint & Format
Currently disabled (v0.3.4+).
gm-verifyno longer invokesgdlint/gdformat, andgm-build/gm-fixgaphave removed this skill from their Available Skills tables. Reason: repeatedgdtoolkit/linter/class_checks.py:144 NotImplementedErrorcrashes on common ECS-style GDScript class shapes, plus low signal-to-noise vs the headless compile + reviewer pattern checks. Re-enabling tracked as ROADMAPR-112. The reference content below is preserved for ad-hoc use outside the pipeline and for the future re-enablement.
Wraps two CLI tools from the gdtoolkit Python package:
- gdlint — static analysis and style checking
- gdformat — deterministic auto-formatter
Prerequisites
pip install "gdtoolkit==4.*" # Godot 4 projects
pip install "gdtoolkit==3.*" # Godot 3 projects
Verify installation: gdlint --version && gdformat --version
If not installed, tell the user and offer to install. The version major must match
the project's Godot major version (read from project.godot config/features).
Lint workflow
Run gdlint
gdlint path/to/file.gd # single file
gdlint path/to/directory/ # all .gd files recursively
Interpret output
Each problem is one line on stderr:
path/to/file.gd:42: Error: Function name "MyFunc" is not valid (function-name)
Format: {file}:{line}: Error: {message} ({rule-id})
Exit codes:
- 0 — no problems (stdout:
Success: no problems found) - 1 — problems found (stderr:
Failure: N problem(s) found)
Fix lint issues
For each reported issue, either:
- Fix the code — rename to match convention, remove unused arg, reorder members
- Suppress inline — when the violation is intentional:
# gdlint:ignore = rule-id var _unusedButNeeded := 0 # this line + next line are suppressed - Suppress region — for larger blocks:
# gdlint: disable=function-name func ALLCAPS_required_by_engine(): pass # gdlint: enable=function-name
When suppressing, always add a brief comment explaining why.
Format workflow
Run gdformat
gdformat path/to/file.gd # format in-place
gdformat path/to/directory/ # format all .gd files
gdformat --check path/ # check only, don't modify (exit 1 if changes needed)
gdformat --diff path/ # show unified diff on stderr, don't modify
Exit codes:
- 0 — already formatted / formatting succeeded
- 1 — check mode found differences / parse error / safety check failed
Safety checks
gdformat runs three safety checks by default (disable with --fast):
- TreeInvariantViolation — parse tree changed after formatting
- FormattingStabilityViolation — formatting isn't idempotent
- CommentPersistenceViolation — comments were lost
If a safety check fails, report the error to the user — do NOT use --fast to bypass it.
This likely indicates a gdtoolkit bug; the file should be formatted manually or the
problematic section excluded.
Rule reference
Naming rules (regex-configurable)
| Rule ID | Default convention | Example |
|---|---|---|
function-name |
snake_case or _on_PascalCase_signal |
move_player, _on_Button_pressed |
class-name |
PascalCase | PlayerController |
sub-class-name |
_PascalCase (leading underscore) | _InternalHelper |
signal-name |
snake_case | health_changed |
class-variable-name |
snake_case or _private | speed, _cache |
function-variable-name |
snake_case | local_var |
function-argument-name |
snake_case or _unused | target_pos, _ignored |
loop-variable-name |
snake_case or _unused | item, _i |
constant-name |
UPPER_SNAKE_CASE | MAX_SPEED |
enum-name |
PascalCase | Direction |
enum-element-name |
UPPER_SNAKE_CASE | NORTH, SOUTH_EAST |
Code quality rules
| Rule ID | Default | What it checks |
|---|---|---|
max-returns |
6 | Too many return statements per function |
max-public-methods |
20 | Too many public methods per class |
function-arguments-number |
10 | Too many function arguments |
max-file-lines |
1000 | File too long |
max-line-length |
100 | Line too long |
Style rules
| Rule ID | What it checks |
|---|---|
unnecessary-pass |
pass in non-empty body |
duplicated-load |
Same resource loaded twice |
expression-not-assigned |
Standalone expression with no effect |
unused-argument |
Argument never used (fix: prefix with _) |
comparison-with-itself |
x == x |
private-method-call |
Calling _private_method() from outside |
class-definitions-order |
Members not in canonical order (see below) |
trailing-whitespace |
Trailing spaces |
mixed-tabs-and-spaces |
Mixed indentation |
no-elif-return |
Unnecessary elif after return |
no-else-return |
Unnecessary else after return |
Class member order (class-definitions-order)
gdlint expects this top-to-bottom order:
@toolclass_nameextends- Docstring
- Signals
- Enums
- Constants
- Static variables
@exportvariables- Public variables
- Private variables (
_prefixed) @onreadypublic variables@onreadyprivate variables- Remaining declarations
Configuration
gdlintrc
Create .gdlintrc (or gdlintrc) in the project root. YAML format.
gdlint searches upward from CWD, uses the first file found.
Generate defaults: gdlint -d > .gdlintrc
Example with customizations:
# Relax line length to match gdformat default
max-line-length: 120
# Allow _on_NodeName_signal pattern (already default)
function-name: '(_on_[A-Z][a-z0-9]*(_[a-z0-9]+)*|[a-z][a-z0-9]*(_[a-z0-9]+)*)'
# Disable rules that conflict with project style
disable:
- unnecessary-pass # we use pass as explicit "intentionally empty"
# Exclude generated/vendored code
excluded_directories: !!set
.git: null
addons: null
.godot: null
gdformatrc
Create gdformatrc in the project root. YAML format.
Generate defaults: gdformat --dump-default-config > gdformatrc
line_length: 120
# use_spaces: 4 # uncomment to use spaces instead of tabs
excluded_directories: !!set
.git: null
addons: null
.godot: null
Important: no pyproject.toml support
gdtoolkit does NOT read from pyproject.toml. Only its own YAML config files work.
Recommended: align line lengths
Set max-line-length in .gdlintrc and line_length in gdformatrc to the same
value. If they differ, gdformat may merge lines that then exceed gdlint's limit —
a known source of false positives.
Quirks and limitations
-
No
# gdformat: off/on— there is no way to skip formatting for a code region. If gdformat mangles a specific construct, the only workaround is to restructure the code. -
gdlint checks unused arguments, not unused variables —
var x = 1with no further use ofxwill NOT be flagged. Only function arguments triggerunused-argument. -
excluded_directoriesonly works when scanning directories — passing a file path directly (gdlint addons/plugin/main.gd) bypasses exclusion rules. -
gdformat safety check failures are real bugs — do not silence them with
--fast. Report to the user and format that section manually. -
gdformat may cause data loss — always ensure the file is under version control before formatting. Run
git diffafter formatting to verify changes are correct. -
Version must match Godot version — gdtoolkit 4.x parses Godot 4 syntax, 3.x parses Godot 3. Mismatched versions cause parse errors on valid code.
Fallback: upstream documentation
If this skill's instructions don't resolve your issue — unexpected output, unfamiliar rule IDs, config syntax errors, or parse failures — consult the upstream repo directly:
- Repository: https://github.com/Scony/godot-gdscript-toolkit
- Linter wiki: https://github.com/Scony/godot-gdscript-toolkit/wiki/3.-Linter
- Formatter wiki: https://github.com/Scony/godot-gdscript-toolkit/wiki/4.-Formatter
- Open issues: https://github.com/Scony/godot-gdscript-toolkit/issues
Use WebFetch to read the wiki pages or issue threads when you need details beyond
what this skill covers.