Claude-skill-registry godot-modernize-gdscript
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/godot-modernize-gdscript" ~/.claude/skills/majiayu000-claude-skill-registry-godot-modernize-gdscript && rm -rf "$T"
skills/data/godot-modernize-gdscript/SKILL.mdGodot GDScript 2.0 Modernizer
Converts GDScript 1.0 (Godot 3.x) patterns to GDScript 2.0 (Godot 4.x) syntax.
Core Conversions
This skill performs five key modernizations:
| GDScript 1.0 | GDScript 2.0 | Pattern |
|---|---|---|
| | Async operations |
| | Node references |
| | Inspector variables |
| Property syntax | Getters/setters |
| | Static typing |
UPON INVOCATION - START HERE
When this skill is invoked, IMMEDIATELY execute:
1. Verify Godot Project (5 seconds)
ls project.godot 2>/dev/null && echo "✓ Godot project detected" || echo "✗ Not a Godot project"
If NOT a Godot project:
- Inform user this skill only works on Godot projects
- STOP here
If IS a Godot project:
- Proceed to step 2
2. Detect GDScript Version (10 seconds)
# Check for Godot 3.x patterns echo "=== Detecting GDScript 1.0 Patterns ===" echo "yield statements:" grep -rn "yield(" --include="*.gd" . | wc -l echo "onready declarations:" grep -rn "^onready var" --include="*.gd" . | wc -l echo "export declarations:" grep -rn "^export var\|^export(int)\|^export(float)" --include="*.gd" . | wc -l echo "setget properties:" grep -rn "setget" --include="*.gd" . | wc -l echo "untyped variables:" grep -rn "^var [a-z]" --include="*.gd" . | grep -v ":" | wc -l
3. Present Findings
Show the user:
=== GDScript 2.0 Modernization Analysis === Project: [project name] Current: GDScript 1.0 (Godot 3.x style) Patterns to modernize: - yield statements: X - onready variables: X - export variables: X - setget properties: X - untyped variables: X Total: X modernizations needed Modernization includes: ✓ yield → await conversion ✓ onready → @onready ✓ export → @export ✓ setget → property syntax ✓ Optional static typing ✓ Git commit per file ✓ Backup before changes Would you like me to: 1. Modernize all files (recommended) 2. Show detailed breakdown first 3. Select specific conversions 4. Cancel
4. Wait for User Choice
- If 1 (Proceed): Start Phase 2 immediately
- If 2 (Details): Show file-by-file breakdown, then offer to proceed
- If 3 (Selective): Ask which conversions to apply
- If 4 (Cancel): Exit skill
Phase 1: Analysis & Inventory
1.1 Create File Inventory
# Find all .gd files find . -name "*.gd" -type f | sort > /tmp/gdscript_files.txt wc -l /tmp/gdscript_files.txt echo "files found"
1.2 Analyze Each File
For each .gd file, detect patterns:
# Analyze patterns per file for file in $(cat /tmp/gdscript_files.txt); do echo "=== $file ===" grep -c "yield(" "$file" 2>/dev/null || echo 0 grep -c "^onready var" "$file" 2>/dev/null || echo 0 grep -c "^export" "$file" 2>/dev/null || echo 0 grep -c "setget" "$file" 2>/dev/null || echo 0 done
1.3 Create Modernization Plan
Modernization Plan: =================== 1. yield → await conversions (X files) 2. onready → @onready (X files) 3. export → @export (X files) 4. setget → properties (X files) 5. Static typing (X files) Total files to modify: X Estimated time: Auto (user doesn't wait) Backup created: YES (git tag) Rollback available: YES
Phase 2: Modernization Operations
Conversion A: yield → await
Detection:
grep -rn "yield(" --include="*.gd" .
Common Patterns:
| Old Pattern | New Pattern |
|---|---|
| |
| |
| |
| |
Transformation Process:
-
Extract yield statement
# Before yield(get_tree().create_timer(2.0), "timeout") -
Convert to await
# After await get_tree().create_timer(2.0).timeout -
Handle variable assignment
# Before var result = yield(async_function(), "completed") # After var result = await async_function()
Implementation:
# Pseudo-code for replacement def convert_yield_to_await(line): # Pattern: yield(object, "signal_name") # Convert to: await object.signal_name import re pattern = r'yield\(([^,]+),\s*"([^"]+)"\)' replacement = r'await \1.\2' return re.sub(pattern, replacement, line)
Conversion B: onready → @onready
Detection:
grep -rn "^onready var" --include="*.gd" .
Transformation:
# Before onready var player = $Player onready var health_bar = $UI/HealthBar # After @onready var player: Node = $Player @onready var health_bar: ProgressBar = $UI/HealthBar
Process:
- Replace
withonready@onready - Add type hints where detectable
- Keep variable name and initialization
Type Inference (Optional):
# If $Player is CharacterBody2D in scene @onready var player: CharacterBody2D = $Player # If $Timer is Timer node @onready var _timer: Timer = $Timer
Conversion C: export → @export
Detection:
grep -rn "^export" --include="*.gd" .
Transformation:
# Before export var speed = 200 export(int) var max_health = 100 export(float) var jump_force = 500.0 export(String) var character_name = "Player" export(NodePath) var target_path # After @export var speed: float = 200.0 @export var max_health: int = 100 @export var jump_force: float = 500.0 @export var character_name: String = "Player" @export var target_path: NodePath
Export Types Mapping:
| Old Export | New Export | Type |
|---|---|---|
| | Integer |
| | Float |
| | String |
| | Boolean |
| | Color |
| | Vector2 |
| | Vector3 |
| | NodePath |
| | Inferred |
Conversion D: setget → Property Syntax
Detection:
grep -rn "setget" --include="*.gd" .
Transformation:
# Before (GDScript 1.0) var health = 100 setget set_health, get_health func set_health(value): health = clamp(value, 0, max_health) health_changed.emit(health) func get_health(): return health
# After (GDScript 2.0) var health: int = 100: set(value): health = clamp(value, 0, max_health) health_changed.emit(health) get: return health
Process:
- Remove
from variable declarationsetget - Add colon after type
- Inline setter and getter
- Use
andset(value):
syntaxget:
Complex Example:
# Before var score = 0 setget set_score func set_score(value): score = max(0, value) update_ui() # After var score: int = 0: set(value): score = max(0, value) update_ui()
Conversion E: Static Typing (Optional)
Detection:
grep -rn "^var [a-z_]* = " --include="*.gd" . | grep -v ":"
Type Inference:
| Value | Inferred Type |
|---|---|
| |
| |
| |
| |
| |
| Node type from scene |
Transformation:
# Before var health = 100 var speed = 200.5 var name = "Player" var active = true var position = Vector2.ZERO # After var health: int = 100 var speed: float = 200.5 var name: String = "Player" var active: bool = true var position: Vector2 = Vector2.ZERO
Phase 3: Execution & Safety
3.1 Create Git Baseline
# Create backup tag git tag gdscript1-baseline-$(date +%Y%m%d-%H%M%S) # Stage any current changes git add . git commit -m "Baseline: Pre-GDScript 2.0 modernization" || echo "No changes to commit"
3.2 Process Files Sequentially
For each .gd file:
# Process file python3 << 'EOF' import re def modernize_gdscript(content): # Conversion 1: yield → await content = re.sub( r'yield\(([^,]+),\s*"([^"]+)"\)', r'await \1.\2', content ) # Conversion 2: onready → @onready content = re.sub( r'^onready var', '@onready var', content, flags=re.MULTILINE ) # Conversion 3: export → @export (simple) content = re.sub( r'^export var', '@export var', content, flags=re.MULTILINE ) return content # Read, process, write with open("script.gd", "r") as f: content = f.read() new_content = modernize_gdscript(content) with open("script.gd", "w") as f: f.write(new_content) EOF
3.3 Commit Per File
# After each file modification git add "$file" git commit -m "Modernize: $file to GDScript 2.0 - Converted yield to await - Updated onready to @onready - Migrated export to @export - Applied static typing"
3.4 Validation After Each File
# Validate syntax godot --headless --quit-after 2 project.godot 2>&1 | grep -i "error" && { echo "Syntax error detected in $file" git reset --hard HEAD~1 echo "Reverted changes to $file" }
Phase 4: Verification & Testing
4.1 Syntax Validation
# Check all modified files for syntax errors for file in $(git diff --name-only HEAD~10..HEAD | grep "\.gd$"); do echo "Checking $file..." # Basic syntax check gdscript_tool --check "$file" 2>/dev/null || echo "Manual review needed: $file" done
4.2 Pattern Verification
# Verify no old patterns remain echo "Checking for remaining GDScript 1.0 patterns..." echo "yield statements remaining:" grep -rn "yield(" --include="*.gd" . | wc -l echo "onready remaining:" grep -rn "^onready var" --include="*.gd" . | wc -l echo "export remaining:" grep -rn "^export var\|^export(" --include="*.gd" . | wc -l
4.3 Godot Project Test
# Open project in Godot to verify godot --editor project.godot & sleep 5 # Check for errors # (User should visually verify no red errors in Output panel)
Examples
Example 1: Complete Script Modernization
Before (GDScript 1.0):
extends CharacterBody2D export var speed = 200 export(int) var health = 100 onready var sprite = $Sprite onready var animation = $AnimationPlayer var damage = 10 setget set_damage func set_damage(value): damage = clamp(value, 0, 100) func _ready(): yield(get_tree().create_timer(1.0), "timeout") start_game() func take_damage(amount): health -= amount if health <= 0: yield(play_death_animation(), "completed") queue_free() func play_death_animation(): animation.play("death") yield(animation, "animation_finished")
After (GDScript 2.0):
extends CharacterBody2D @export var speed: float = 200.0 @export var health: int = 100 @onready var sprite: Sprite2D = $Sprite @onready var animation: AnimationPlayer = $AnimationPlayer var damage: int = 10: set(value): damage = clamp(value, 0, 100) func _ready(): await get_tree().create_timer(1.0).timeout start_game() func take_damage(amount: int) -> void: health -= amount if health <= 0: await play_death_animation() queue_free() func play_death_animation() -> void: animation.play("death") await animation.animation_finished
Example 2: Signal Connection Modernization
Before:
func _ready(): button.connect("pressed", self, "_on_button_pressed") timer.connect("timeout", self, "_on_timeout") func _on_button_pressed(): pass func _on_timeout(): pass
After:
func _ready(): button.pressed.connect(_on_button_pressed) timer.timeout.connect(_on_timeout) func _on_button_pressed() -> void: pass func _on_timeout() -> void: pass
Success Criteria
Modernization complete when:
- ✓ Zero
statements remain (converted to await)yield( - ✓ Zero
remain (converted to @onready)onready var - ✓ Zero
remain (converted to @export)export var - ✓ Zero
remain (converted to property syntax)setget - ✓ All scripts compile without errors
- ✓ No functional changes (behavior identical)
- ✓ Git history shows clear modernization commits
Common Issues & Solutions
Issue 1: yield with function calls
Problem:
var result = yield(load_data(), "completed")
Solution:
var result = await load_data()
Issue 2: Coroutines with state
Problem:
func process(): yield(do_step_1(), "completed") yield(do_step_2(), "completed")
Solution:
func process() -> void: await do_step_1() await do_step_2()
Issue 3: setget with only getter or setter
Problem:
var score = 0 setget , get_score # Only getter
Solution:
var score: int = 0: get: return score
Rollback Procedure
If modernization causes issues:
# Find baseline tag git tag | grep "gdscript1-baseline" # Reset to pre-modernization state git reset --hard <baseline-tag> # Or revert specific commits git revert <modernization-commit-hash>
Integration with Other Skills
Use before:
- Modernize syntax first, then refactor architecturegodot-refactor
- Update code before TileMap migrationgodot-migrate-tilemap
Use after:
- Project conversion from Godot 3.x to 4.x
- Organize files firstgodot-organize-project
This skill automates the tedious parts of GDScript 1.0 → 2.0 migration while preserving exact functionality.