Files
tekton/addons/godot_ai/handlers/filesystem_handler.gd
T

113 lines
3.3 KiB
GDScript

@tool
extends RefCounted
const ErrorCodes := preload("res://addons/godot_ai/utils/error_codes.gd")
## Handles file read/write operations and reimport within the Godot project.
func read_file(params: Dictionary) -> Dictionary:
var path: String = params.get("path", "")
var path_err = McpPathValidator.path_error(path, "path")
if path_err != null:
return path_err
if not FileAccess.file_exists(path):
return ErrorCodes.make(ErrorCodes.RESOURCE_NOT_FOUND, "File not found: %s" % path)
var file := FileAccess.open(path, FileAccess.READ)
if file == null:
return ErrorCodes.make(ErrorCodes.INTERNAL_ERROR, "Failed to open file: %s" % path)
var content := file.get_as_text()
file.close()
return {
"data": {
"path": path,
"content": content,
"size": content.length(),
"line_count": content.count("\n") + (1 if not content.is_empty() else 0),
}
}
func write_file(params: Dictionary) -> Dictionary:
var path: String = params.get("path", "")
var content: String = params.get("content", "")
var path_err = McpPathValidator.path_error(path, "path", true)
if path_err != null:
return path_err
# Ensure parent directory exists
var dir_path := path.get_base_dir()
if not DirAccess.dir_exists_absolute(dir_path):
var err := DirAccess.make_dir_recursive_absolute(dir_path)
if err != OK:
return ErrorCodes.make(ErrorCodes.INTERNAL_ERROR, "Failed to create directory: %s" % dir_path)
var existed_before := FileAccess.file_exists(path)
var file := FileAccess.open(path, FileAccess.WRITE)
if file == null:
return ErrorCodes.make(ErrorCodes.INTERNAL_ERROR, "Failed to open file for writing: %s" % path)
file.store_string(content)
file.close()
# Single-file register, not a full scan() — a scan() per write stacks
# filesystem WorkerThreadPool tasks under concurrent writes and can SIGABRT
# in the global-class update (see dsarno/godot#6 and create_script in
# script_handler.gd). update_file() is what reimport()/material/theme use.
var efs := EditorInterface.get_resource_filesystem()
if efs != null:
efs.update_file(path)
var data := {
"path": path,
"size": content.length(),
"undoable": false,
"reason": "File system operations cannot be undone via editor undo",
}
McpResourceIO.attach_cleanup_hint(data, existed_before, [path])
return {"data": data}
func reimport(params: Dictionary) -> Dictionary:
var paths: Array = params.get("paths", [])
if paths.is_empty():
return ErrorCodes.make(ErrorCodes.MISSING_REQUIRED_PARAM, "Missing required param: paths (non-empty array)")
var efs := EditorInterface.get_resource_filesystem()
if efs == null:
return ErrorCodes.make(ErrorCodes.EDITOR_NOT_READY, "EditorFileSystem not available")
var reimported: Array[String] = []
var not_found: Array[String] = []
for path_variant in paths:
var path: String = str(path_variant)
var path_err := McpPathValidator.validate_resource_path(path)
if not path_err.is_empty():
not_found.append("%s (%s)" % [path, path_err])
continue
if not FileAccess.file_exists(path):
not_found.append("%s (file does not exist)" % path)
continue
efs.update_file(path)
reimported.append(path)
return {
"data": {
"reimported": reimported,
"not_found": not_found,
"reimported_count": reimported.size(),
"not_found_count": not_found.size(),
"undoable": false,
"reason": "Reimport is a file system operation",
}
}