thedivergentai/godot-gdscript-mastery
Expert GDScript landmine guidance: static typing opcodes, signal-up/call-down, %UniqueName/@onready lifecycle, Callable bind/unbind, await sequences, typed collections, and safe Dictionary iteration. Use for code review, refactoring hot paths, or project standards. Trigger keywords: static_typing, signal_architecture, unique_nodes, @onready, class_name, signal_up_call_down, Callable.bind, typed_collections, await_sequence.
npx skills add https://github.com/thedivergentai/GD-Agentic-Skills --skill godot-gdscript-mastery
Expert guidance for writing performant, maintainable GDScript — Godot-landmine decision trees, not a style-guide reprint.
type_checker, performance_analyzer, signal_architecture_validator) as runtime gameplay code.@onready and @export on the same variable — Initialization order will cause @onready to overwrite the Inspector value.dict.keys().duplicate() or iterate a clone to safely erase elements.connect("signal", ...) — Always use the Signal object syntax (button.pressed.connect(...)) for compile-time safety.queue_free() or get_class() is unsupported and will be ignored by engine callbacks.get_node() or $ inside _process() — Fetching paths every frame stalls the CPU. Cache and use @onready.Parent.method() calls — Violates "Signal Up, Call Down". Use signals to communicate with parents.is followed by a hard cast — If the type check passes but the object changes, it crashes. Use as and check for null.print() for production debugging — Use push_error(), push_warning(), or breakpoints._ready() — Use ResourceLoader.load_threaded_request() for async loading.static var is sufficient — Static variables offer better encapsulation.return (add return null if needed).| Landmine | Decision |
|---|---|
| Hot path still Variant? | Annotate vars/returns; prefer typed collections |
| Generic math in _process? | Use typed helpers (absf, ceili, clampf) |
| Green safe-lines missing? | Fix inference with := or explicit types |
> MANDATORY: typed_collections_mastery.gd, array_preallocation_perf.gd, type_checker.gd (EditorScript audit).
| Landmine | Decision |
|---|---|
| Child needs parent reaction? | Emit signal up — never call parent methods |
| Cross-script payload unsafe? | Typed signal name(arg: Type) |
| Connect visibility? | Prefer _ready() connects over invisible editor-only wiring |
> MANDATORY: typed_signal_definitions.gd, signal_architecture_validator.gd.
| Landmine | Decision |
|---|---|
| Need child nodes? | @onready / %UniqueName — never in _init() |
| Scene-instanced node with ctor args? | Use @export injection — _init(args) breaks PackedScene.instantiate() |
| Path lookup every frame? | Cache once; never $ / get_node in _process |
> MANDATORY: safe_type_casting.gd.
| Landmine | Decision |
|---|---|
| Extra context on callback? | Callable.bind(...) |
| Discard unused signal args? | Callable.unbind(n) |
| One-off timeout logic? | Inline lambda OK; keep refs if create_callback-style longevity matters |
> MANDATORY: callable_binding_context.gd, unbind_signal_args.gd, advanced_lambdas.gd, functional_lambda_logic.gd.
| Landmine | Decision |
|---|---|
| Sequence timers without threads? | await chains — see await manager |
| Global state without Autoload bloat? | static var (+ nullify large statics when done) |
| Erase while iterating Dictionary? | Clone keys first |
> MANDATORY: await_sequence_manager.gd, static_var_singleton_alt.gd, dictionary_safe_iteration.gd, performance_analyzer.gd (EditorScript).
| Script | When to open |
|---|---|
| typed_collections_mastery.gd | Typed Array/Dictionary opcodes |
| functional_lambda_logic.gd | reduce / all / any |
| advanced_lambdas.gd | Higher-order Callables |
| safe_type_casting.gd | as + null checks |
| typed_signal_definitions.gd | Typed signal boundaries |
| callable_binding_context.gd | bind() context injection |
| unbind_signal_args.gd | unbind() arity trim |
| await_sequence_manager.gd | Non-blocking await flows |
| array_preallocation_perf.gd | resize() pre-alloc |
| static_var_singleton_alt.gd | Lightweight global state |
| dictionary_safe_iteration.gd | Safe erase-while-iterate |
| type_checker.gd | EditorScript typing audit |
| performance_analyzer.gd | EditorScript hot-path scan |
| signal_architecture_validator.gd | EditorScript signal-up checks |
dict.get("key", default) over dict["key"] when presence is uncertain.%Name for critical UI/nodes.extends → class_name → signals/enums/consts → exports/onready → lifecycle → public → _private.> LLM-ignorance rule: If a general agent would not know it before reading, load the reference — never delete expert deltas.
> Progressive disclosure: open Official Documentation links only when researching a specific API; load Related Skills when routing to a peer domain — do not preload the whole lattice.
signal declarations, await, and first-class Callables this skill standardizes.extends → class_name → signals → exports → lifecycle → methods) used in reviews and refactoring.bind() / unbind() APIs for injecting or discarding callback arguments without wrapper nodes.resize(), and functional methods (filter/map/reduce/all/any) used in the scripts..get() defaults and why size must not change while iterating keys.@onready / %UniqueName instead of get_node/$ inside _process loops.static var singleton alternatives shown here.@onready, unique names, and await sequences must stay valid across scene swaps and loaders.watch_signals / spies reliable in unit tests.Take thedivergentai/godot-gdscript-mastery from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.