agentsclimarketplace

Gdscript quality

Skill oubakiou/skills/skills/gdscript-quality

AI agent skills

Install
npx -y skills add oubakiou/skills --skill gdscript-quality

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Write production-quality, statically typed GDScript for Godot 4.x. Use this skill whenever you create, edit, refactor, or review .gd files or any Godot project code — even if the user does not mention types, warnings, or code quality ("add a feature to my Godot game" or "fix this script" counts). Covers type annotations, typed Arrays and Dictionaries, eliminating UNTYPED_*/UNSAFE_* warnings, GDScript naming and script organization, node access and signal patterns, and headless verification with the bundled check script.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

8.2 KB, as published. Nobody here has run it

GDScript Quality

Produce GDScript that compiles clean under Godot's strict warnings and reads like code written by a careful Godot engineer. Static typing is not cosmetic: the analyzer catches real bugs before the game runs, the editor gains full autocompletion, and typed opcodes execute faster. Untyped code silently forfeits all three, and the cost surfaces later as runtime errors that types would have caught at parse time.

Workflow

  1. Enable strict warnings in project.godot first (see below), so anyone opening the project in the editor sees the same findings the check script enforces.

  2. Write code following the core rules in this file. Consult references/ for anything beyond the basics (see the map below).

  3. Verify headlessly after implementing:

    bash <skill_dir>/scripts/check.sh <project_dir>
    

    Fix every reported finding and re-run until it prints RESULT: CLEAN. If no godot binary is available (exit code 3), say explicitly in your final report that headless verification was skipped — do not present unverified code as verified.

Warning configuration

Ensure project.godot has a [debug] section containing:

[debug]

gdscript/warnings/untyped_declaration=1
gdscript/warnings/unsafe_property_access=1
gdscript/warnings/unsafe_method_access=1
gdscript/warnings/unsafe_cast=1
gdscript/warnings/unsafe_call_argument=1

Values: 0 ignore, 1 warn, 2 error. Know one engine quirk: at 1 the warnings appear only in the editor UI — headless runs print nothing. Warnings surface on the command line only at 2, as SCRIPT ERROR: ... (Warning treated as error.). The check script handles this by escalating to 2 during the check, so 1 is a fine everyday setting; use 2 in the project itself when you want the game to refuse to run until warnings are fixed.

Core rules

Type every declaration. Variables, constants, parameters, return types — including -> void when a function returns nothing, and including private helpers and loop-local temporaries. Partial typing is where bugs hide: one untyped field turns every access on it into an unchecked Variant operation that the analyzer can no longer help you with.

Use := only when the right-hand side makes the type obvious (a literal, a constructor call, or a cast: var speed := 12.5, var rng := RandomNumberGenerator.new()). Fields, function signatures, and anything part of a class's surface get explicit annotations — readers see the type without hunting for the initializer.

Type your collections. Array[Item], Dictionary[Vector2i, Item] (typed dictionaries need Godot 4.4+ — check config/features in project.godot first; on 4.0–4.3 keep the Dictionary untyped and type the values at each access with typed locals or casts instead). Nested generics (Array[Array[int]]) are a syntax error — declare the outer collection as Array[Array] and give inner rows a typed local (var row: Array[int] = grid[y]), or wrap the row in a small class.

Never model data with untyped Dictionaries. A dict-as-struct ({"hp": 10, "name": "orc"}) has no spelling check on keys, no type on values, and every access is unsafe. Define a class_name script or an inner class ... extends RefCounted with typed fields instead. Reserve Dictionary for genuinely dynamic key→value lookups, and type it when you do.

Name your magic values. Tuning numbers, grid sizes, thresholds, string keys: const MOVE_SPEED: float = 50.0. Use enum for closed sets of alternatives instead of ints or strings. (Enum types are ints underneath — the type does not guarantee a value is a member, so validate external input.)

Type loop variables when the iterable is untyped: for i: int in range(n), for line: String in lines. A typed Array[T]/Dictionary[K, V] already types its loop variable for you.

Narrow Variants safely. When you receive a base type or Variant (signal callbacks, get(), JSON), do not access members on it directly — that is exactly what UNSAFE_PROPERTY_ACCESS/UNSAFE_METHOD_ACCESS flag. Narrow first:

if body is Player:
    var player: Player = body
    player.take_damage(amount)

as also works (var player := body as Player) but silently yields null on mismatch, so it demands an explicit null check — prefer is + a typed local unless the silent-null behavior is what you want. Remediation recipes for each UNSAFE_* warning are in references/typing.md.

Give node references explicit types: @onready var timer: Timer = $Timer. The explicit annotation fails loudly at scene load if the node type doesn't match; $Timer as Timer would silently become null and blow up later, far from the cause.

Use the typed math builtins. absf/absi, clampf/clampi, floorf/floori, lerpf, roundf/roundi, etc., instead of the Variant abs()/clamp()/... — the untyped versions cost you both safety and speed. Full table in references/typing.md.

Type signal parameters (signal died(cause: StringName)) and the callbacks that receive them — signals are API surface too.

Suppress warnings only as a last resort. @warning_ignore("...") on a single line, with a comment stating the constraint that makes the warning unavoidable. If you cannot state one, fix the code instead. Never turn a warning off project-wide to make output clean.

Reference map

Read these on demand — not all up front:

  • references/typing.md — the full typing reference: what can be a type hint, typed collection semantics and their limits, casting and is narrowing, per-warning remediation recipes (UNSAFE_PROPERTY_ACCESS, UNSAFE_METHOD_ACCESS, UNSAFE_CAST), typed builtin table, covariance/contravariance, warning-system configuration. Read when fighting a specific warning or typing anything non-obvious.
  • references/style.md — naming conventions, canonical declaration order inside a script, formatting, ## doc comments. Read before writing any new script file.
  • references/architecture.md — separating simulation logic from Node presentation, node access patterns, signal design ("signals up, calls down"), deterministic RNG injection, and a minimal headless test-script pattern. Read when creating new scenes/systems or restructuring.

Verification script

scripts/check.sh <project_dir> temporarily escalates this skill's strict warning set to errors in project.godot (restored afterward, also on interrupt), runs --import to build the class cache and catch scene/resource errors, then validates every first-party .gd file with --check-only — including scripts no scene references. Warnings surface as SCRIPT ERROR: ... (Warning treated as error.) lines with file and line.

  • Exit 0: clean. Exit 1: findings reported — fix and re-run.
  • Exit 2: usage error (not a Godot project dir). Exit 3: no godot binary (GODOT_BIN env var overrides the binary name).
  • addons/ is skipped (third-party code is not yours to fix).
  • It performs static analysis only; it does not run the game or its tests, so run the project's own test entry point separately if one exists.

When findings fall outside your scope. check.sh checks the whole project, so it can report NOT CLEAN from files you did not touch and are not allowed to touch (a fixed test oracle, pre-existing code the task scoped you out of). In that case do not edit protected files just to silence the gate, and do not claim the check passed. Report both facts: the files you changed contribute zero findings, and the residual findings are pre-existing in out-of-scope files (name them). Verify the "pre-existing" claim before making it — confirm the same findings appear against the original code, not just assume it.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.