gdharness 0.13.14 → 0.13.15

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
@@ -7573,6 +7573,24 @@ function declaresMember(source, missing) {
7573
7573
  const declaration = missing.kind === "method" ? new RegExp(String.raw`^\s*(?:static\s+)?func\s+${name}\s*\(`, "m") : new RegExp(String.raw`^\s*(?:static\s+)?(?:@export\s+)?(?:var|const)\s+${name}\b`, "m");
7574
7574
  return declaration.test(source);
7575
7575
  }
7576
+ function unknownTypeIn(message) {
7577
+ const found = /(?:Could not find (?:base class|type)|Cannot find class|Identifier) "?([A-Za-z_][A-Za-z0-9_]*)"?(?: not declared| in the current scope|\b)/.exec(message);
7578
+ return found?.[1] ?? null;
7579
+ }
7580
+ function unloadedTypes(messages, declared, cached) {
7581
+ const seen = new Set;
7582
+ const found = [];
7583
+ for (const message of messages) {
7584
+ const type = unknownTypeIn(message);
7585
+ const declaredIn = type === null ? undefined : declared.get(type);
7586
+ if (type === null || declaredIn === undefined || seen.has(type)) {
7587
+ continue;
7588
+ }
7589
+ seen.add(type);
7590
+ found.push({ type, declaredIn, inTheClassCache: cached?.has(type) === true });
7591
+ }
7592
+ return found;
7593
+ }
7576
7594
  function contradictedDiagnostics(messages, classes, sourceOf) {
7577
7595
  const read = new Map;
7578
7596
  const found = [];
@@ -15784,7 +15802,7 @@ var init_tool_definitions = __esm(() => {
15784
15802
  },
15785
15803
  {
15786
15804
  name: "script_diagnostics",
15787
- description: "Errors and warnings for a script from the editor's language server, and whether the script is clean. Needs the editor running. Any diagnostic saying a member is not present on an inferred type is checked against the file the class cache points at, and one the file contradicts is named under contradictedByTheFile with staleAnalysis saying what to do: the language server hands dependents the type it analysed at startup, so a method added to an existing class_name is reported missing at every caller until the editor restarts.",
15805
+ description: "Errors and warnings for a script from the editor's language server, and whether the script is clean. Needs the editor running. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares comes back under contradictedByTheFile, and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says which remedy applies, because they differ: a stale analysed type needs editor_launch restart, while a class missing from the cache needs project_import refresh_classes.",
15788
15806
  parameters: {
15789
15807
  projectPath: PROJECT_PATH,
15790
15808
  scriptPath: SCRIPT_PATH
@@ -38050,34 +38068,27 @@ class GodotServer {
38050
38068
  const severity = asParams(entry)["severity"];
38051
38069
  return severity === 1 || severity === "error" || severity === "ERROR";
38052
38070
  }).length;
38053
- const contradicted = this.contradictedByTheFile(diagnostics, args);
38071
+ const disagrees = this.whereTheFileDisagrees(diagnostics, args);
38054
38072
  return this.jsonTextResponse(markIfStale({
38055
38073
  scriptPath: readString(args, "scriptPath"),
38056
38074
  clean: errors === 0,
38057
38075
  errors,
38058
38076
  warnings: diagnostics.length - errors,
38059
38077
  diagnostics,
38060
- ...contradicted.length === 0 ? {} : {
38061
- contradictedByTheFile: contradicted,
38062
- staleAnalysis: `The editor is reporting against an older copy of ${contradicted.length === 1 ? "a type" : "some types"} named above. Each member listed is declared in the file the class cache points at, so those diagnostics are wrong however the code is written. editor_launch restart clears it; editor_rescan and project_import refresh_classes do not, because neither reaches the analysed types the language server built at startup.`
38063
- }
38078
+ ...disagrees
38064
38079
  }, this.godotBridge.getStatus().addonVersion, SERVER_VERSION));
38065
38080
  }
38066
- contradictedByTheFile(diagnostics, args) {
38081
+ whereTheFileDisagrees(diagnostics, args) {
38067
38082
  const messages = diagnostics.map((entry) => asParams(entry)["message"]).filter((message) => typeof message === "string");
38068
- if (!messages.some((message) => missingMemberIn(message) !== null)) {
38069
- return [];
38070
- }
38071
- const project = this.project(args);
38072
- if (!project.ok) {
38073
- return [];
38074
- }
38075
- const classes = cachedClasses(project.value.path);
38076
- if (classes === null) {
38077
- return [];
38083
+ const worthChecking = messages.some((message) => missingMemberIn(message) !== null || unknownTypeIn(message) !== null);
38084
+ const project = worthChecking ? this.project(args) : null;
38085
+ if (project === null || !project.ok) {
38086
+ return {};
38078
38087
  }
38079
- return contradictedDiagnostics(messages, classes, (resourcePath) => {
38080
- const contained = resolveWithinProject(project.value.path, resourcePath);
38088
+ const projectPath = project.value.path;
38089
+ const cached = cachedClasses(projectPath);
38090
+ const contradicted = cached === null ? [] : contradictedDiagnostics(messages, cached, (resourcePath) => {
38091
+ const contained = resolveWithinProject(projectPath, resourcePath);
38081
38092
  if (!contained.ok || !existsSync14(contained.absolutePath)) {
38082
38093
  return null;
38083
38094
  }
@@ -38087,6 +38098,24 @@ class GodotServer {
38087
38098
  return null;
38088
38099
  }
38089
38100
  });
38101
+ const unloaded = messages.some((message) => unknownTypeIn(message) !== null) ? unloadedTypes(messages, declaredClasses(projectPath), cached) : [];
38102
+ const notes = [];
38103
+ if (contradicted.length > 0) {
38104
+ notes.push(`The editor is reporting against an older copy of ${contradicted.length === 1 ? "a type" : "some types"} named under contradictedByTheFile. Each member listed is declared in the file the class cache points at, so those diagnostics are wrong however the code is written. editor_launch restart clears it; editor_rescan and project_import refresh_classes do not, because neither reaches the analysed types the language server built at startup.`);
38105
+ }
38106
+ const uncached = unloaded.filter((type) => !type.inTheClassCache);
38107
+ if (uncached.length > 0) {
38108
+ notes.push(`${uncached.map((type) => type.type).join(", ")} ${uncached.length === 1 ? "is declared" : "are declared"} in this project and missing from .godot/global_script_class_cache.cfg, so a game launched now would not resolve ${uncached.length === 1 ? "it" : "them"} either. project_import refresh_classes rewrites the cache from the declarations on disk.`);
38109
+ }
38110
+ const stale = unloaded.filter((type) => type.inTheClassCache);
38111
+ if (stale.length > 0) {
38112
+ notes.push(`${stale.map((type) => type.type).join(", ")} ${stale.length === 1 ? "is" : "are"} in the class cache and still unresolved here, which is the editor's loaded list being behind rather than anything on disk. A game launched now reads the cache and resolves ${stale.length === 1 ? "it" : "them"}, so the run and the diagnostics disagree. editor_launch restart clears it.`);
38113
+ }
38114
+ return {
38115
+ ...contradicted.length === 0 ? {} : { contradictedByTheFile: contradicted },
38116
+ ...unloaded.length === 0 ? {} : { typesTheEditorHasNotLoaded: unloaded },
38117
+ ...notes.length === 0 ? {} : { staleAnalysis: notes.join(" ") }
38118
+ };
38090
38119
  }
38091
38120
  async handleLSP(toolName, args) {
38092
38121
  const port = this.editorServes("lspPort", "GDHARNESS_LSP_PORT", DEFAULT_LSP_PORT);
package/build/index.js CHANGED
@@ -24833,6 +24833,24 @@ function declaresMember(source, missing) {
24833
24833
  const declaration = missing.kind === "method" ? new RegExp(String.raw`^\s*(?:static\s+)?func\s+${name}\s*\(`, "m") : new RegExp(String.raw`^\s*(?:static\s+)?(?:@export\s+)?(?:var|const)\s+${name}\b`, "m");
24834
24834
  return declaration.test(source);
24835
24835
  }
24836
+ function unknownTypeIn(message) {
24837
+ const found = /(?:Could not find (?:base class|type)|Cannot find class|Identifier) "?([A-Za-z_][A-Za-z0-9_]*)"?(?: not declared| in the current scope|\b)/.exec(message);
24838
+ return found?.[1] ?? null;
24839
+ }
24840
+ function unloadedTypes(messages, declared, cached) {
24841
+ const seen = new Set;
24842
+ const found = [];
24843
+ for (const message of messages) {
24844
+ const type = unknownTypeIn(message);
24845
+ const declaredIn = type === null ? undefined : declared.get(type);
24846
+ if (type === null || declaredIn === undefined || seen.has(type)) {
24847
+ continue;
24848
+ }
24849
+ seen.add(type);
24850
+ found.push({ type, declaredIn, inTheClassCache: cached?.has(type) === true });
24851
+ }
24852
+ return found;
24853
+ }
24836
24854
  function contradictedDiagnostics(messages, classes, sourceOf) {
24837
24855
  const read = new Map;
24838
24856
  const found = [];
@@ -28901,7 +28919,7 @@ var TOOL_SPECS = [
28901
28919
  },
28902
28920
  {
28903
28921
  name: "script_diagnostics",
28904
- description: "Errors and warnings for a script from the editor's language server, and whether the script is clean. Needs the editor running. Any diagnostic saying a member is not present on an inferred type is checked against the file the class cache points at, and one the file contradicts is named under contradictedByTheFile with staleAnalysis saying what to do: the language server hands dependents the type it analysed at startup, so a method added to an existing class_name is reported missing at every caller until the editor restarts.",
28922
+ description: "Errors and warnings for a script from the editor's language server, and whether the script is clean. Needs the editor running. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares comes back under contradictedByTheFile, and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says which remedy applies, because they differ: a stale analysed type needs editor_launch restart, while a class missing from the cache needs project_import refresh_classes.",
28905
28923
  parameters: {
28906
28924
  projectPath: PROJECT_PATH,
28907
28925
  scriptPath: SCRIPT_PATH
@@ -30802,34 +30820,27 @@ class GodotServer {
30802
30820
  const severity = asParams(entry)["severity"];
30803
30821
  return severity === 1 || severity === "error" || severity === "ERROR";
30804
30822
  }).length;
30805
- const contradicted = this.contradictedByTheFile(diagnostics, args);
30823
+ const disagrees = this.whereTheFileDisagrees(diagnostics, args);
30806
30824
  return this.jsonTextResponse(markIfStale({
30807
30825
  scriptPath: readString(args, "scriptPath"),
30808
30826
  clean: errors === 0,
30809
30827
  errors,
30810
30828
  warnings: diagnostics.length - errors,
30811
30829
  diagnostics,
30812
- ...contradicted.length === 0 ? {} : {
30813
- contradictedByTheFile: contradicted,
30814
- staleAnalysis: `The editor is reporting against an older copy of ${contradicted.length === 1 ? "a type" : "some types"} named above. Each member listed is declared in the file the class cache points at, so those diagnostics are wrong however the code is written. editor_launch restart clears it; editor_rescan and project_import refresh_classes do not, because neither reaches the analysed types the language server built at startup.`
30815
- }
30830
+ ...disagrees
30816
30831
  }, this.godotBridge.getStatus().addonVersion, SERVER_VERSION));
30817
30832
  }
30818
- contradictedByTheFile(diagnostics, args) {
30833
+ whereTheFileDisagrees(diagnostics, args) {
30819
30834
  const messages = diagnostics.map((entry) => asParams(entry)["message"]).filter((message) => typeof message === "string");
30820
- if (!messages.some((message) => missingMemberIn(message) !== null)) {
30821
- return [];
30822
- }
30823
- const project = this.project(args);
30824
- if (!project.ok) {
30825
- return [];
30826
- }
30827
- const classes = cachedClasses(project.value.path);
30828
- if (classes === null) {
30829
- return [];
30835
+ const worthChecking = messages.some((message) => missingMemberIn(message) !== null || unknownTypeIn(message) !== null);
30836
+ const project = worthChecking ? this.project(args) : null;
30837
+ if (project === null || !project.ok) {
30838
+ return {};
30830
30839
  }
30831
- return contradictedDiagnostics(messages, classes, (resourcePath) => {
30832
- const contained = resolveWithinProject(project.value.path, resourcePath);
30840
+ const projectPath = project.value.path;
30841
+ const cached = cachedClasses(projectPath);
30842
+ const contradicted = cached === null ? [] : contradictedDiagnostics(messages, cached, (resourcePath) => {
30843
+ const contained = resolveWithinProject(projectPath, resourcePath);
30833
30844
  if (!contained.ok || !existsSync9(contained.absolutePath)) {
30834
30845
  return null;
30835
30846
  }
@@ -30839,6 +30850,24 @@ class GodotServer {
30839
30850
  return null;
30840
30851
  }
30841
30852
  });
30853
+ const unloaded = messages.some((message) => unknownTypeIn(message) !== null) ? unloadedTypes(messages, declaredClasses(projectPath), cached) : [];
30854
+ const notes = [];
30855
+ if (contradicted.length > 0) {
30856
+ notes.push(`The editor is reporting against an older copy of ${contradicted.length === 1 ? "a type" : "some types"} named under contradictedByTheFile. Each member listed is declared in the file the class cache points at, so those diagnostics are wrong however the code is written. editor_launch restart clears it; editor_rescan and project_import refresh_classes do not, because neither reaches the analysed types the language server built at startup.`);
30857
+ }
30858
+ const uncached = unloaded.filter((type) => !type.inTheClassCache);
30859
+ if (uncached.length > 0) {
30860
+ notes.push(`${uncached.map((type) => type.type).join(", ")} ${uncached.length === 1 ? "is declared" : "are declared"} in this project and missing from .godot/global_script_class_cache.cfg, so a game launched now would not resolve ${uncached.length === 1 ? "it" : "them"} either. project_import refresh_classes rewrites the cache from the declarations on disk.`);
30861
+ }
30862
+ const stale = unloaded.filter((type) => type.inTheClassCache);
30863
+ if (stale.length > 0) {
30864
+ notes.push(`${stale.map((type) => type.type).join(", ")} ${stale.length === 1 ? "is" : "are"} in the class cache and still unresolved here, which is the editor's loaded list being behind rather than anything on disk. A game launched now reads the cache and resolves ${stale.length === 1 ? "it" : "them"}, so the run and the diagnostics disagree. editor_launch restart clears it.`);
30865
+ }
30866
+ return {
30867
+ ...contradicted.length === 0 ? {} : { contradictedByTheFile: contradicted },
30868
+ ...unloaded.length === 0 ? {} : { typesTheEditorHasNotLoaded: unloaded },
30869
+ ...notes.length === 0 ? {} : { staleAnalysis: notes.join(" ") }
30870
+ };
30842
30871
  }
30843
30872
  async handleLSP(toolName, args) {
30844
30873
  const port = this.editorServes("lspPort", "GDHARNESS_LSP_PORT", DEFAULT_LSP_PORT);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "0.13.14",
3
+ "version": "0.13.15",
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",