gdharness 0.13.22 → 0.13.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/cli.js
CHANGED
|
@@ -15231,6 +15231,9 @@ function opTakes(spec, op, name) {
|
|
|
15231
15231
|
function argumentsOf(spec, op) {
|
|
15232
15232
|
return Object.keys(spec.parameters).filter((name) => opTakes(spec, op, name));
|
|
15233
15233
|
}
|
|
15234
|
+
function toolsWithoutProjectPath() {
|
|
15235
|
+
return TOOL_SPECS.filter((spec) => !Object.hasOwn(spec.parameters, "projectPath")).map((spec) => spec.name).sort();
|
|
15236
|
+
}
|
|
15234
15237
|
function toolSpec(name) {
|
|
15235
15238
|
return SPECS_BY_NAME[name];
|
|
15236
15239
|
}
|
|
@@ -15961,7 +15964,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
15961
15964
|
},
|
|
15962
15965
|
{
|
|
15963
15966
|
name: "editor_rescan",
|
|
15964
|
-
description: "Makes the running editor scan the project filesystem, so files written outside it are picked up. The scan is a change-detecting walk rather than an unconditional reparse, so a file another engine has already imported reads as settled and the walk does not look inside it: its class_name then stays out of the list the editor resolves against, however many times you scan. Any class in that state is named under unseenByEditor, which is what no check on disk can see, since the declaration and the cache are both correct there and only the editor disagrees. The cure
|
|
15967
|
+
description: "Makes the running editor scan the project filesystem, so files written outside it are picked up. The scan is a change-detecting walk rather than an unconditional reparse, so a file another engine has already imported reads as settled and the walk does not look inside it: its class_name then stays out of the list the editor resolves against, however many times you scan. Any class in that state is named under unseenByEditor, which is what no check on disk can see, since the declaration and the cache are both correct there and only the editor disagrees. The cure is this call on its own, with no change to the declaring script: measured against a real editor on three platforms, with idle waits of the same length ruled out so the scan is credited rather than the time it takes, and reproduced in a second project against its own reproduction in 203ms. editor_launch restart also does it and costs a window, and project_import refresh_classes does not: it rewrites the cache and does not touch what the editor is holding. A scan writes .godot/global_script_class_cache.cfg from the list the editor is holding, so a class the editor cannot resolve goes out of that file with it. That is not a reason to refuse the scan, because on a class the editor has merely not walked yet the scan is the cure; what the answer does instead is name the loss under cacheLost and rebuild the cache from the files, naming what came back under cacheRestored, so no fresh engine, CI run or clone inherits the short file. An editor that lost classes is still holding the short list, so the note says to restart it before scanning again. Needs the editor connected.",
|
|
15965
15968
|
parameters: {
|
|
15966
15969
|
projectPath: PROJECT_PATH,
|
|
15967
15970
|
timeoutMs: { type: "number", description: "How long to wait for the scan. Default 30000." },
|
|
@@ -39000,7 +39003,8 @@ class GodotServer {
|
|
|
39000
39003
|
try {
|
|
39001
39004
|
const answer = asParams(await this.godotBridge.invokeTool("reload_script", { ...args, scriptPath: reloading }));
|
|
39002
39005
|
const methods = readArray(answer, "methods");
|
|
39003
|
-
|
|
39006
|
+
const before = readArray(answer, "heldBefore");
|
|
39007
|
+
reloaded = methods ? { methods: methods.map(String), ...before ? { heldBefore: before.map(String) } : {} } : { problem: `the editor would not say what ${reloading} has after reloading it` };
|
|
39004
39008
|
} catch (error) {
|
|
39005
39009
|
reloaded = { problem: `${reloading} could not be reloaded: ${errorMessage(error)}` };
|
|
39006
39010
|
}
|
|
@@ -39012,13 +39016,14 @@ class GodotServer {
|
|
|
39012
39016
|
ok: !busy && unseen.length === 0 && stillGone.length === 0 && checked.unchecked === undefined,
|
|
39013
39017
|
stillWorking: busy,
|
|
39014
39018
|
waitedMs: Date.now() - started,
|
|
39019
|
+
heldBeforeReload: reloaded.heldBefore,
|
|
39015
39020
|
reloadedMethods: reloaded.methods,
|
|
39016
39021
|
reloadProblem: reloaded.problem,
|
|
39017
39022
|
unseenByEditor: unseen.length > 0 ? unseen : undefined,
|
|
39018
39023
|
cacheLost: lost.length > 0 ? lost : undefined,
|
|
39019
39024
|
cacheRestored: restored.length > 0 ? restored : undefined,
|
|
39020
39025
|
classesUnchecked: checked.unchecked,
|
|
39021
|
-
note: busy ? "The editor was still scanning or importing when the wait ran out, so new files may not be visible yet." : stillGone.length > 0 ? "The scan wrote the class cache from the list this editor is holding, that list is shorter than the file was, and rebuilding the cache from the files did not bring these back. Every engine that reads the cache next, including a test run, will report them as unknown identifiers. Restart the editor with editor_launch restart before scanning again." : lost.length > 0 ? "The scan wrote the class cache from the list this editor is holding, which is shorter than the file was, so the cache was rebuilt from the files and these classes are back in it. Nothing was lost, but this editor is still holding the short list: do not rescan again until it has been restarted with editor_launch restart, because it will write the same list over the file each time." : unseen.length > 0 ? "The scan finished and these classes are still not in the list the editor resolves against, so every use of them reads as an unknown identifier. Its walk skips a file another engine has already imported.
|
|
39026
|
+
note: busy ? "The editor was still scanning or importing when the wait ran out, so new files may not be visible yet." : stillGone.length > 0 ? "The scan wrote the class cache from the list this editor is holding, that list is shorter than the file was, and rebuilding the cache from the files did not bring these back. Every engine that reads the cache next, including a test run, will report them as unknown identifiers. Restart the editor with editor_launch restart before scanning again." : lost.length > 0 ? "The scan wrote the class cache from the list this editor is holding, which is shorter than the file was, so the cache was rebuilt from the files and these classes are back in it. Nothing was lost, but this editor is still holding the short list: do not rescan again until it has been restarted with editor_launch restart, because it will write the same list over the file each time." : unseen.length > 0 ? "The scan finished and these classes are still not in the list the editor resolves against, so every use of them reads as an unknown identifier. Its walk skips a file another engine has already imported. Rescan again on its own, which is the measured cure and needs no change to the declaring script, or restart the editor with editor_launch restart." : undefined
|
|
39022
39027
|
});
|
|
39023
39028
|
}
|
|
39024
39029
|
async rebuildCacheAfterLoss(projectPath, lost) {
|
|
@@ -40637,9 +40642,10 @@ function renderToolsMarkdown() {
|
|
|
40637
40642
|
`${TOOL_SPECS.length} tools, named \`domain_verb\`. A tool that does several related things takes an`,
|
|
40638
40643
|
"`op`. An unknown op or argument is refused with the valid set listed. Generated from the server.",
|
|
40639
40644
|
"",
|
|
40640
|
-
|
|
40641
|
-
"
|
|
40642
|
-
"
|
|
40645
|
+
`Every call takes \`projectPath\` except ${toolsWithoutProjectPath().map((name) => `\`${name}\``).join(", ")}, which take none: the \`runtime_*\` and \`debug_*\` ones pick between running games`,
|
|
40646
|
+
"instead, and the others answer about the editor on this server. An argument a tool does not",
|
|
40647
|
+
"declare is refused rather than ignored. Answers are read from the engine after the change, not",
|
|
40648
|
+
"echoed from the request. Engine stderr comes back under `engine_messages`.",
|
|
40643
40649
|
"",
|
|
40644
40650
|
...headlessSection()
|
|
40645
40651
|
];
|
|
@@ -40695,8 +40701,9 @@ that is running, and the project on disk. ${TOOL_SPECS.length} tools, named \`do
|
|
|
40695
40701
|
|
|
40696
40702
|
## Start here
|
|
40697
40703
|
|
|
40698
|
-
- Every call takes \`projectPath
|
|
40699
|
-
|
|
40704
|
+
- Every call takes \`projectPath\` except ${toolsWithoutProjectPath().map((name) => `\\\`${name}\\\``).join(", ")}, which take none. The \`runtime_*\` and \`debug_*\` ones pick between running
|
|
40705
|
+
games instead; the others answer about the editor this server is connected to. There is no
|
|
40706
|
+
ambient project, and an argument a tool does not declare is refused rather than ignored.
|
|
40700
40707
|
- \`editor_status\` says whether an editor is connected and whether its addon matches the server.
|
|
40701
40708
|
\`addonIsStale\` means they differ, and \`staleNote\` says which half is behind: an editor that
|
|
40702
40709
|
needs restarting, or a server that needs reconnecting in your harness. Every answer that came
|
|
@@ -60,6 +60,11 @@ func reload_script(args: Dictionary) -> Dictionary:
|
|
|
60
60
|
if script == null:
|
|
61
61
|
return {"ok": false, "error": path + " is not a GDScript"}
|
|
62
62
|
|
|
63
|
+
# What the copy had before anything touched it, which is the reading that shows the fault rather
|
|
64
|
+
# than the one that mends it. Without it a caller cannot tell a copy that was already current
|
|
65
|
+
# from one this call repaired, and those are different facts about their editor.
|
|
66
|
+
var held: Array[String] = _method_names(script)
|
|
67
|
+
|
|
63
68
|
# The source is read off disk and put back before reloading, which is the whole of the fix and
|
|
64
69
|
# is not obvious. `reload` recompiles from the object's own `source_code`, not from the file, so
|
|
65
70
|
# a script the editor loaded before the change recompiles the text it was holding and comes back
|
|
@@ -75,11 +80,15 @@ func reload_script(args: Dictionary) -> Dictionary:
|
|
|
75
80
|
if failed != OK:
|
|
76
81
|
return {"ok": false, "error": "Reloading " + path + " answered error " + str(failed)}
|
|
77
82
|
|
|
78
|
-
|
|
83
|
+
return {"ok": true, "script": path, "heldBefore": held, "methods": _method_names(script)}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
func _method_names(script: GDScript) -> Array[String]:
|
|
87
|
+
var names: Array[String] = []
|
|
79
88
|
for entry: Dictionary in script.get_script_method_list():
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return
|
|
89
|
+
names.append(str(entry.get("name", "")))
|
|
90
|
+
names.sort()
|
|
91
|
+
return names
|
|
83
92
|
|
|
84
93
|
|
|
85
94
|
func global_classes(_args: Dictionary) -> Dictionary:
|
package/build/index.js
CHANGED
|
@@ -29078,7 +29078,7 @@ var TOOL_SPECS = [
|
|
|
29078
29078
|
},
|
|
29079
29079
|
{
|
|
29080
29080
|
name: "editor_rescan",
|
|
29081
|
-
description: "Makes the running editor scan the project filesystem, so files written outside it are picked up. The scan is a change-detecting walk rather than an unconditional reparse, so a file another engine has already imported reads as settled and the walk does not look inside it: its class_name then stays out of the list the editor resolves against, however many times you scan. Any class in that state is named under unseenByEditor, which is what no check on disk can see, since the declaration and the cache are both correct there and only the editor disagrees. The cure
|
|
29081
|
+
description: "Makes the running editor scan the project filesystem, so files written outside it are picked up. The scan is a change-detecting walk rather than an unconditional reparse, so a file another engine has already imported reads as settled and the walk does not look inside it: its class_name then stays out of the list the editor resolves against, however many times you scan. Any class in that state is named under unseenByEditor, which is what no check on disk can see, since the declaration and the cache are both correct there and only the editor disagrees. The cure is this call on its own, with no change to the declaring script: measured against a real editor on three platforms, with idle waits of the same length ruled out so the scan is credited rather than the time it takes, and reproduced in a second project against its own reproduction in 203ms. editor_launch restart also does it and costs a window, and project_import refresh_classes does not: it rewrites the cache and does not touch what the editor is holding. A scan writes .godot/global_script_class_cache.cfg from the list the editor is holding, so a class the editor cannot resolve goes out of that file with it. That is not a reason to refuse the scan, because on a class the editor has merely not walked yet the scan is the cure; what the answer does instead is name the loss under cacheLost and rebuild the cache from the files, naming what came back under cacheRestored, so no fresh engine, CI run or clone inherits the short file. An editor that lost classes is still holding the short list, so the note says to restart it before scanning again. Needs the editor connected.",
|
|
29082
29082
|
parameters: {
|
|
29083
29083
|
projectPath: PROJECT_PATH,
|
|
29084
29084
|
timeoutMs: { type: "number", description: "How long to wait for the scan. Default 30000." },
|
|
@@ -31752,7 +31752,8 @@ class GodotServer {
|
|
|
31752
31752
|
try {
|
|
31753
31753
|
const answer = asParams(await this.godotBridge.invokeTool("reload_script", { ...args, scriptPath: reloading }));
|
|
31754
31754
|
const methods = readArray(answer, "methods");
|
|
31755
|
-
|
|
31755
|
+
const before = readArray(answer, "heldBefore");
|
|
31756
|
+
reloaded = methods ? { methods: methods.map(String), ...before ? { heldBefore: before.map(String) } : {} } : { problem: `the editor would not say what ${reloading} has after reloading it` };
|
|
31756
31757
|
} catch (error) {
|
|
31757
31758
|
reloaded = { problem: `${reloading} could not be reloaded: ${errorMessage(error)}` };
|
|
31758
31759
|
}
|
|
@@ -31764,13 +31765,14 @@ class GodotServer {
|
|
|
31764
31765
|
ok: !busy && unseen.length === 0 && stillGone.length === 0 && checked.unchecked === undefined,
|
|
31765
31766
|
stillWorking: busy,
|
|
31766
31767
|
waitedMs: Date.now() - started,
|
|
31768
|
+
heldBeforeReload: reloaded.heldBefore,
|
|
31767
31769
|
reloadedMethods: reloaded.methods,
|
|
31768
31770
|
reloadProblem: reloaded.problem,
|
|
31769
31771
|
unseenByEditor: unseen.length > 0 ? unseen : undefined,
|
|
31770
31772
|
cacheLost: lost.length > 0 ? lost : undefined,
|
|
31771
31773
|
cacheRestored: restored.length > 0 ? restored : undefined,
|
|
31772
31774
|
classesUnchecked: checked.unchecked,
|
|
31773
|
-
note: busy ? "The editor was still scanning or importing when the wait ran out, so new files may not be visible yet." : stillGone.length > 0 ? "The scan wrote the class cache from the list this editor is holding, that list is shorter than the file was, and rebuilding the cache from the files did not bring these back. Every engine that reads the cache next, including a test run, will report them as unknown identifiers. Restart the editor with editor_launch restart before scanning again." : lost.length > 0 ? "The scan wrote the class cache from the list this editor is holding, which is shorter than the file was, so the cache was rebuilt from the files and these classes are back in it. Nothing was lost, but this editor is still holding the short list: do not rescan again until it has been restarted with editor_launch restart, because it will write the same list over the file each time." : unseen.length > 0 ? "The scan finished and these classes are still not in the list the editor resolves against, so every use of them reads as an unknown identifier. Its walk skips a file another engine has already imported.
|
|
31775
|
+
note: busy ? "The editor was still scanning or importing when the wait ran out, so new files may not be visible yet." : stillGone.length > 0 ? "The scan wrote the class cache from the list this editor is holding, that list is shorter than the file was, and rebuilding the cache from the files did not bring these back. Every engine that reads the cache next, including a test run, will report them as unknown identifiers. Restart the editor with editor_launch restart before scanning again." : lost.length > 0 ? "The scan wrote the class cache from the list this editor is holding, which is shorter than the file was, so the cache was rebuilt from the files and these classes are back in it. Nothing was lost, but this editor is still holding the short list: do not rescan again until it has been restarted with editor_launch restart, because it will write the same list over the file each time." : unseen.length > 0 ? "The scan finished and these classes are still not in the list the editor resolves against, so every use of them reads as an unknown identifier. Its walk skips a file another engine has already imported. Rescan again on its own, which is the measured cure and needs no change to the declaring script, or restart the editor with editor_launch restart." : undefined
|
|
31774
31776
|
});
|
|
31775
31777
|
}
|
|
31776
31778
|
async rebuildCacheAfterLoss(projectPath, lost) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.24",
|
|
4
4
|
"mcpName": "io.github.Aureliolo/gdharness",
|
|
5
5
|
"description": "A harness for driving a Godot 4 project from an agent: editor addons, a runtime bridge into the running game, an MCP server in front of them, and a CLI that installs and diagnoses the Godot side.",
|
|
6
6
|
"type": "module",
|