gdharness 1.1.16 → 1.1.17

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
@@ -8706,6 +8706,18 @@ function classRemedy(entries) {
8706
8706
  const reload = declaring.length === 1 ? `editor_rescan with reloadScript set to ${declaring[0]}` : `editor_rescan with reloadScript, a call each for ${declaring.join(" and ")} since it takes one script,`;
8707
8707
  return `For ${entries.map(written).join(", ")}, run ${reload} first. In the one project that has ` + "reproduced a stale enum member, twice, the reload cleared it at once both times, and the one " + "plain editor_rescan tried left the diagnostic standing.";
8708
8708
  }
8709
+ function failedReloadNote(failed) {
8710
+ const missing = (before, after) => before.filter((name) => !after.includes(name));
8711
+ const lost = [
8712
+ ...missing(failed.heldBefore, failed.heldAfter),
8713
+ ...missing(failed.constantsBefore, failed.constantsAfter)
8714
+ ];
8715
+ const unchanged = lost.length === 0 && missing(failed.heldAfter, failed.heldBefore).length === 0 && missing(failed.constantsAfter, failed.constantsBefore).length === 0;
8716
+ const answered = failed.codeName === "" ? `error ${failed.code}` : `error ${failed.code}, ${failed.codeName}`;
8717
+ const copy = unchanged ? "The copy the editor holds is as it was, with the members under heldBeforeReload and heldConstantsBeforeReload." : `The copy the editor holds changed even so${lost.length === 0 ? "" : `, and has lost ${lost.join(", ")}`}: anything diagnosed against it now is answered from what is left, and reloading it once it compiles rebuilds it.`;
8718
+ const order = failed.names.length === 0 ? "" : ` If they deny a member of ${failed.names.join(" or ")} that its file declares, the editor's copy of that class is behind: reload that class first with reloadScript and then this script, the order that cleared it in the one project that has met this.`;
8719
+ return `${failed.script} did not compile, so nothing was reloaded: Godot answered ${answered}. ${copy} ` + `script_diagnostics on it gives the errors.${order}`;
8720
+ }
8709
8721
  function uncachedClassNote(types) {
8710
8722
  if (types.length === 0) {
8711
8723
  return "";
@@ -17341,7 +17353,7 @@ var init_tool_definitions = __esm(() => {
17341
17353
  },
17342
17354
  reloadScript: {
17343
17355
  type: "string",
17344
- description: "A script to recompile in the editor after the scan, for the half a scan does not reach: a script the editor has loaded keeps the copy it built, that copy is refreshed when one of its dependencies changes rather than when it changes itself, and a scan leaves it alone. Measured here, a built copy comes back without a method added to it while script_diagnostics on the same script reads clean in the same window, so the copy the editor built and whatever the analyser resolves types against are not known to be one thing: reload for the copy, and read the diagnostics as a separate answer rather than as this one confirmed. This recompiles into the same object, so the holders that kept the stale copy alive see the new one. The methods it has afterwards come back under reloadedMethods, because a reload that compiled nothing and answered OK is the failure worth catching, and what it held before under heldBeforeReload. Its constants come back the same way, under heldConstantsBeforeReload and reloadedConstants, with each enum value as Kind.SHORT: an enum member is held as a constant and never shows among the methods."
17356
+ description: "A script to recompile in the editor after the scan, for the half a scan does not reach: a script the editor has loaded keeps the copy it built, that copy is refreshed when one of its dependencies changes rather than when it changes itself, and a scan leaves it alone. Measured here, a built copy comes back without a method added to it while script_diagnostics on the same script reads clean in the same window, so the copy the editor built and whatever the analyser resolves types against are not known to be one thing: reload for the copy, and read the diagnostics as a separate answer rather than as this one confirmed. This recompiles into the same object, so the holders that kept the stale copy alive see the new one. The methods it has afterwards come back under reloadedMethods, because a reload that compiled nothing and answered OK is the failure worth catching, and what it held before under heldBeforeReload. Its constants come back the same way, under heldConstantsBeforeReload and reloadedConstants, with each enum value as Kind.SHORT: an enum member is held as a constant and never shows among the methods. A script that does not compile is not reloaded: reloadProblem gives Godot's error, says whether the copy the editor holds survived, and names the classes the file uses, whose held copies being behind is what failed one before."
17345
17357
  }
17346
17358
  },
17347
17359
  requires: ["projectPath"]
@@ -41297,6 +41309,11 @@ class GodotServer {
41297
41309
  shortListEditorPid: this.godotBridge.getStatus().editorPid ?? null
41298
41310
  });
41299
41311
  }
41312
+ classesAScriptNames(projectPath, scriptPath) {
41313
+ const source = this.sourceOfClass(projectPath, scriptPath);
41314
+ const known = cachedClasses(projectPath) ?? declaredClasses(projectPath);
41315
+ return source === null ? [] : classesNamedIn(source, known).filter((name) => known.get(name) !== scriptPath);
41316
+ }
41300
41317
  sourceOfClass(projectPath, resourcePath) {
41301
41318
  const contained = resolveWithinProject(projectPath, resourcePath);
41302
41319
  if (!contained.ok || !existsSync15(contained.absolutePath)) {
@@ -43027,12 +43044,30 @@ class GodotServer {
43027
43044
  const found = readArray(answer, field);
43028
43045
  return found ? { [field]: found.map(String) } : {};
43029
43046
  };
43030
- reloaded = methods ? {
43031
- methods: methods.map(String),
43032
- ...listed("heldBefore"),
43033
- ...listed("constants"),
43034
- ...listed("heldConstantsBefore")
43035
- } : { problem: `the editor would not say what ${reloading} has after reloading it` };
43047
+ const failed = answer["failed"];
43048
+ if (typeof failed === "number" && failed !== 0) {
43049
+ reloaded = {
43050
+ ...listed("heldBefore"),
43051
+ ...listed("heldConstantsBefore"),
43052
+ problem: failedReloadNote({
43053
+ script: reloading,
43054
+ code: failed,
43055
+ codeName: readString(answer, "failedAs") ?? "",
43056
+ heldBefore: listed("heldBefore")["heldBefore"] ?? [],
43057
+ heldAfter: methods?.map(String) ?? [],
43058
+ constantsBefore: listed("heldConstantsBefore")["heldConstantsBefore"] ?? [],
43059
+ constantsAfter: listed("constants")["constants"] ?? [],
43060
+ names: projectPath === "" ? [] : this.classesAScriptNames(projectPath, reloading)
43061
+ })
43062
+ };
43063
+ } else {
43064
+ reloaded = methods ? {
43065
+ methods: methods.map(String),
43066
+ ...listed("heldBefore"),
43067
+ ...listed("constants"),
43068
+ ...listed("heldConstantsBefore")
43069
+ } : { problem: `the editor would not say what ${reloading} has after reloading it` };
43070
+ }
43036
43071
  } catch (error) {
43037
43072
  reloaded = { problem: `${reloading} could not be reloaded: ${errorMessage(error)}` };
43038
43073
  }
@@ -43095,7 +43130,13 @@ class GodotServer {
43095
43130
  }
43096
43131
  try {
43097
43132
  const answer = asParams(await this.godotBridge.invokeTool("reload_script", { scriptPath }));
43098
- if (readArray(answer, "methods") === undefined) {
43133
+ const failed = answer["failed"];
43134
+ if (typeof failed === "number" && failed !== 0) {
43135
+ notReloaded.push({
43136
+ scriptPath,
43137
+ problem: `it did not compile: Godot answered error ${failed}${readString(answer, "failedAs") ? `, ${readString(answer, "failedAs")}` : ""}`
43138
+ });
43139
+ } else if (readArray(answer, "methods") === undefined) {
43099
43140
  notReloaded.push({
43100
43141
  scriptPath,
43101
43142
  problem: "the editor would not say what it has after reloading it"
@@ -74,16 +74,22 @@ func reload_script(args: Dictionary) -> Dictionary:
74
74
  var reader: FileAccess = FileAccess.open(path, FileAccess.READ)
75
75
  if reader == null:
76
76
  return {"ok": false, "error": "Could not read " + path + ": " + str(FileAccess.get_open_error())}
77
+ var previous: String = script.source_code
77
78
  script.source_code = reader.get_as_text()
78
79
  reader.close()
79
80
 
81
+ # A reload that fails is still answered as a call that ran, because the readings are what a
82
+ # caller needs then: a failure travels back as its message alone, and a project met one and
83
+ # could not tell whether the copy had been damaged by it or before it. The old text goes back
84
+ # into the object, so what it holds and the source it says it was built from still agree.
80
85
  var failed: Error = script.reload(true)
81
86
  if failed != OK:
82
- return {"ok": false, "error": "Reloading " + path + " answered error " + str(failed)}
83
-
87
+ script.source_code = previous
84
88
  return {
85
89
  "ok": true,
86
90
  "script": path,
91
+ "failed": failed,
92
+ "failedAs": error_string(failed) if failed != OK else "",
87
93
  "heldBefore": held,
88
94
  "methods": _method_names(script),
89
95
  "heldConstantsBefore": held_constants,
package/build/index.js CHANGED
@@ -26276,6 +26276,18 @@ function classRemedy(entries) {
26276
26276
  const reload = declaring.length === 1 ? `editor_rescan with reloadScript set to ${declaring[0]}` : `editor_rescan with reloadScript, a call each for ${declaring.join(" and ")} since it takes one script,`;
26277
26277
  return `For ${entries.map(written).join(", ")}, run ${reload} first. In the one project that has ` + "reproduced a stale enum member, twice, the reload cleared it at once both times, and the one " + "plain editor_rescan tried left the diagnostic standing.";
26278
26278
  }
26279
+ function failedReloadNote(failed) {
26280
+ const missing = (before, after) => before.filter((name) => !after.includes(name));
26281
+ const lost = [
26282
+ ...missing(failed.heldBefore, failed.heldAfter),
26283
+ ...missing(failed.constantsBefore, failed.constantsAfter)
26284
+ ];
26285
+ const unchanged = lost.length === 0 && missing(failed.heldAfter, failed.heldBefore).length === 0 && missing(failed.constantsAfter, failed.constantsBefore).length === 0;
26286
+ const answered = failed.codeName === "" ? `error ${failed.code}` : `error ${failed.code}, ${failed.codeName}`;
26287
+ const copy = unchanged ? "The copy the editor holds is as it was, with the members under heldBeforeReload and heldConstantsBeforeReload." : `The copy the editor holds changed even so${lost.length === 0 ? "" : `, and has lost ${lost.join(", ")}`}: anything diagnosed against it now is answered from what is left, and reloading it once it compiles rebuilds it.`;
26288
+ const order = failed.names.length === 0 ? "" : ` If they deny a member of ${failed.names.join(" or ")} that its file declares, the editor's copy of that class is behind: reload that class first with reloadScript and then this script, the order that cleared it in the one project that has met this.`;
26289
+ return `${failed.script} did not compile, so nothing was reloaded: Godot answered ${answered}. ${copy} ` + `script_diagnostics on it gives the errors.${order}`;
26290
+ }
26279
26291
  function uncachedClassNote(types) {
26280
26292
  if (types.length === 0) {
26281
26293
  return "";
@@ -30998,7 +31010,7 @@ var TOOL_SPECS = [
30998
31010
  },
30999
31011
  reloadScript: {
31000
31012
  type: "string",
31001
- description: "A script to recompile in the editor after the scan, for the half a scan does not reach: a script the editor has loaded keeps the copy it built, that copy is refreshed when one of its dependencies changes rather than when it changes itself, and a scan leaves it alone. Measured here, a built copy comes back without a method added to it while script_diagnostics on the same script reads clean in the same window, so the copy the editor built and whatever the analyser resolves types against are not known to be one thing: reload for the copy, and read the diagnostics as a separate answer rather than as this one confirmed. This recompiles into the same object, so the holders that kept the stale copy alive see the new one. The methods it has afterwards come back under reloadedMethods, because a reload that compiled nothing and answered OK is the failure worth catching, and what it held before under heldBeforeReload. Its constants come back the same way, under heldConstantsBeforeReload and reloadedConstants, with each enum value as Kind.SHORT: an enum member is held as a constant and never shows among the methods."
31013
+ description: "A script to recompile in the editor after the scan, for the half a scan does not reach: a script the editor has loaded keeps the copy it built, that copy is refreshed when one of its dependencies changes rather than when it changes itself, and a scan leaves it alone. Measured here, a built copy comes back without a method added to it while script_diagnostics on the same script reads clean in the same window, so the copy the editor built and whatever the analyser resolves types against are not known to be one thing: reload for the copy, and read the diagnostics as a separate answer rather than as this one confirmed. This recompiles into the same object, so the holders that kept the stale copy alive see the new one. The methods it has afterwards come back under reloadedMethods, because a reload that compiled nothing and answered OK is the failure worth catching, and what it held before under heldBeforeReload. Its constants come back the same way, under heldConstantsBeforeReload and reloadedConstants, with each enum value as Kind.SHORT: an enum member is held as a constant and never shows among the methods. A script that does not compile is not reloaded: reloadProblem gives Godot's error, says whether the copy the editor holds survived, and names the classes the file uses, whose held copies being behind is what failed one before."
31002
31014
  }
31003
31015
  },
31004
31016
  requires: ["projectPath"]
@@ -33757,6 +33769,11 @@ class GodotServer {
33757
33769
  shortListEditorPid: this.godotBridge.getStatus().editorPid ?? null
33758
33770
  });
33759
33771
  }
33772
+ classesAScriptNames(projectPath, scriptPath) {
33773
+ const source = this.sourceOfClass(projectPath, scriptPath);
33774
+ const known = cachedClasses(projectPath) ?? declaredClasses(projectPath);
33775
+ return source === null ? [] : classesNamedIn(source, known).filter((name) => known.get(name) !== scriptPath);
33776
+ }
33760
33777
  sourceOfClass(projectPath, resourcePath) {
33761
33778
  const contained = resolveWithinProject(projectPath, resourcePath);
33762
33779
  if (!contained.ok || !existsSync12(contained.absolutePath)) {
@@ -35487,12 +35504,30 @@ class GodotServer {
35487
35504
  const found = readArray(answer, field);
35488
35505
  return found ? { [field]: found.map(String) } : {};
35489
35506
  };
35490
- reloaded = methods ? {
35491
- methods: methods.map(String),
35492
- ...listed("heldBefore"),
35493
- ...listed("constants"),
35494
- ...listed("heldConstantsBefore")
35495
- } : { problem: `the editor would not say what ${reloading} has after reloading it` };
35507
+ const failed = answer["failed"];
35508
+ if (typeof failed === "number" && failed !== 0) {
35509
+ reloaded = {
35510
+ ...listed("heldBefore"),
35511
+ ...listed("heldConstantsBefore"),
35512
+ problem: failedReloadNote({
35513
+ script: reloading,
35514
+ code: failed,
35515
+ codeName: readString(answer, "failedAs") ?? "",
35516
+ heldBefore: listed("heldBefore")["heldBefore"] ?? [],
35517
+ heldAfter: methods?.map(String) ?? [],
35518
+ constantsBefore: listed("heldConstantsBefore")["heldConstantsBefore"] ?? [],
35519
+ constantsAfter: listed("constants")["constants"] ?? [],
35520
+ names: projectPath === "" ? [] : this.classesAScriptNames(projectPath, reloading)
35521
+ })
35522
+ };
35523
+ } else {
35524
+ reloaded = methods ? {
35525
+ methods: methods.map(String),
35526
+ ...listed("heldBefore"),
35527
+ ...listed("constants"),
35528
+ ...listed("heldConstantsBefore")
35529
+ } : { problem: `the editor would not say what ${reloading} has after reloading it` };
35530
+ }
35496
35531
  } catch (error) {
35497
35532
  reloaded = { problem: `${reloading} could not be reloaded: ${errorMessage(error)}` };
35498
35533
  }
@@ -35555,7 +35590,13 @@ class GodotServer {
35555
35590
  }
35556
35591
  try {
35557
35592
  const answer = asParams(await this.godotBridge.invokeTool("reload_script", { scriptPath }));
35558
- if (readArray(answer, "methods") === undefined) {
35593
+ const failed = answer["failed"];
35594
+ if (typeof failed === "number" && failed !== 0) {
35595
+ notReloaded.push({
35596
+ scriptPath,
35597
+ problem: `it did not compile: Godot answered error ${failed}${readString(answer, "failedAs") ? `, ${readString(answer, "failedAs")}` : ""}`
35598
+ });
35599
+ } else if (readArray(answer, "methods") === undefined) {
35559
35600
  notReloaded.push({
35560
35601
  scriptPath,
35561
35602
  problem: "the editor would not say what it has after reloading it"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "1.1.16",
3
+ "version": "1.1.17",
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",