gdharness 1.2.13 → 1.2.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
@@ -11005,7 +11005,7 @@ var init_tool_definitions = __esm(() => {
11005
11005
  {
11006
11006
  name: "project_settings",
11007
11007
  title: "Project settings",
11008
- description: "Reads or writes project.godot: settings, autoloads, the main scene, input actions, plugins and audio buses. A write with an editor open on the project is handed to the editor, which takes the settings the write changed or names from the file (named under editorAdopted), including a setting the file already held, since the editor can hold another value after the file was put back, or reloads the bus layout, so it answers the new values and does not save the old ones back over them. editorNote is an editor that could not take the write, one on an addon from before this, which still holds the old values until editor_launch restart. An enabled plugin's code still loads on the editor's next start.",
11008
+ description: "Reads or writes project.godot: settings, autoloads, the main scene, input actions, plugins and audio buses. A write with an editor open on the project is handed to the editor, which takes the settings the write changed or names from the file (named under editorAdopted), including a setting the file already held, since the editor can hold another value after the file was put back, or reloads the bus layout, so it answers the new values and does not save the old ones back over them. editorNote is an editor that could not take the write, one on an addon from before this, which still holds the old values until editor_launch restart. An enabled plugin's code still loads on the editor's next start. set answers old_value and new_value, with was_new true when project.godot did not set the setting before, and was_default true when what it held was the engine's own default, which old_value then is.",
11009
11009
  parameters: {
11010
11010
  projectPath: PROJECT_PATH,
11011
11011
  setting: { type: "string", description: 'Setting path, such as "display/window/size/viewport_width".' },
@@ -11708,7 +11708,7 @@ var init_tool_definitions = __esm(() => {
11708
11708
  },
11709
11709
  check: {
11710
11710
  effect: "additive",
11711
- summary: "boot headless, quit after a few frames, and report the verdict",
11711
+ summary: "boot headless, quit after a few frames, and report the verdict. booted is that verdict rather than whether the game came up: true only for a run that quit on its own within timeoutMs, exited 0 and printed no error, so a game that reached _ready and then raised an error answers false. hung is a run killed at timeoutMs, and errors and warnings count what it printed, which comes back under entries with repeats folded together",
11712
11712
  requires: ["projectPath"]
11713
11713
  },
11714
11714
  wait: {
@@ -12889,6 +12889,18 @@ function prefixIssues(path, issues) {
12889
12889
  function unwrapMessage(message) {
12890
12890
  return typeof message === "string" ? message : message?.message;
12891
12891
  }
12892
+ function sizeCaps(defChecks) {
12893
+ const caps = [];
12894
+ for (const ch of defChecks ?? []) {
12895
+ const d = ch._zod.def;
12896
+ if (!/_(length|size)$|_equals$/.test(d.check))
12897
+ break;
12898
+ const max = d.maximum ?? d.length ?? d.size;
12899
+ if (max >= 0)
12900
+ caps.push([max, ch]);
12901
+ }
12902
+ return caps;
12903
+ }
12892
12904
  function attachSchema(issues, start, inst) {
12893
12905
  var _a;
12894
12906
  for (let i = start;i < issues.length; i++) {
@@ -13926,7 +13938,7 @@ var init_versions = __esm(() => {
13926
13938
  version = {
13927
13939
  major: 4,
13928
13940
  minor: 6,
13929
- patch: 5
13941
+ patch: 7
13930
13942
  };
13931
13943
  });
13932
13944
 
@@ -14052,6 +14064,28 @@ function isValidJWT(token, algorithm = null) {
14052
14064
  return false;
14053
14065
  }
14054
14066
  }
14067
+ function capSize(inst, size) {
14068
+ const caps = sizeCaps(inst._zod.def.checks);
14069
+ if (!caps.length)
14070
+ return;
14071
+ const cap = Math.min(...caps.map((c) => c[0]));
14072
+ const parse = inst._zod.parse;
14073
+ inst._zod.parse = (payload, ctx) => {
14074
+ const n = size(payload.value);
14075
+ if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
14076
+ for (const [max, ch] of sizeCaps(inst._zod.def.checks)) {
14077
+ if (n > max && ch._zod.def.when(payload)) {
14078
+ const start = payload.issues.length;
14079
+ ch._zod.check(payload);
14080
+ payload.issues[start].continue = false;
14081
+ attachSchema(payload.issues, start, inst);
14082
+ return payload;
14083
+ }
14084
+ }
14085
+ }
14086
+ return parse(payload, ctx);
14087
+ };
14088
+ }
14055
14089
  function handleArrayResult(result, final, index) {
14056
14090
  if (result.issues.length) {
14057
14091
  final.issues.push(...prefixIssues(index, result.issues));
@@ -14357,7 +14391,7 @@ function handleRefineResult(result, payload, input, inst) {
14357
14391
  payload.issues.push(issue(_iss));
14358
14392
  }
14359
14393
  }
14360
- var $ZodType, toStandardResult = (r, ctx) => r.issues.length ? { issues: r.issues.map((iss) => finalizeIssue(iss, ctx, config())) } : { value: r.value }, $ZodString, $ZodStringFormat, $ZodGUID, $ZodUUID, $ZodEmail, URL_BAD_FORMAT = 1, URL_UNPARSEABLE = 2, asciiTabOrNewline, $ZodURL, $ZodEmoji, $ZodNanoID, $ZodCUID, $ZodCUID2, $ZodULID, $ZodXID, $ZodKSUID, $ZodISODateTime, $ZodISODate, $ZodISOTime, $ZodISODuration, $ZodIPv4, ipv6Alphabet, $ZodIPv6, $ZodCIDRv4, $ZodCIDRv6, base64Charset, $ZodBase64, base64urlCharset, $ZodBase64URL, $ZodE164, $ZodJWT, $ZodNumber, $ZodNumberFormat, $ZodBoolean, $ZodNull, $ZodAny, $ZodUnknown, $ZodNever, $ZodArray, NO_SYMBOL_KEYS, $ZodObject, $ZodObjectJIT, $ZodUnion, $ZodDiscriminatedUnion, $ZodIntersection, $ZodRecord, $ZodEnum, $ZodLiteral, $ZodTransform, $ZodOptional, $ZodExactOptional, $ZodNullable, $ZodDefault, $ZodPrefault, $ZodNonOptional, $ZodCatch, $ZodPipe, $ZodPreprocess, $ZodReadonly, $ZodLazy, $ZodCustom;
14394
+ var $ZodType, toStandardResult = (r, ctx) => r.issues.length ? { issues: r.issues.map((iss) => finalizeIssue(iss, ctx, config())) } : { value: r.value }, $ZodString, $ZodStringFormat, $ZodGUID, $ZodUUID, $ZodEmail, URL_BAD_FORMAT = 1, URL_UNPARSEABLE = 2, asciiTabOrNewline, $ZodURL, $ZodEmoji, $ZodNanoID, $ZodCUID, $ZodCUID2, $ZodULID, $ZodXID, $ZodKSUID, $ZodISODateTime, $ZodISODate, $ZodISOTime, $ZodISODuration, $ZodIPv4, ipv6Alphabet, $ZodIPv6, $ZodCIDRv4, $ZodCIDRv6, base64Charset, $ZodBase64, base64urlCharset, $ZodBase64URL, $ZodE164, $ZodJWT, $ZodNumber, $ZodNumberFormat, $ZodBoolean, $ZodNull, $ZodAny, $ZodUnknown, $ZodNever, arraySize = (input) => Array.isArray(input) ? input.length : -1, $ZodArray, NO_SYMBOL_KEYS, $ZodObject, $ZodObjectJIT, $ZodUnion, $ZodDiscriminatedUnion, $ZodIntersection, $ZodRecord, $ZodEnum, $ZodLiteral, $ZodTransform, $ZodOptional, $ZodExactOptional, $ZodNullable, $ZodDefault, $ZodPrefault, $ZodNonOptional, $ZodCatch, $ZodPipe, $ZodPreprocess, $ZodReadonly, $ZodLazy, $ZodCustom;
14361
14395
  var init_schemas = __esm(() => {
14362
14396
  init_checks();
14363
14397
  init_core();
@@ -14847,6 +14881,7 @@ var init_schemas = __esm(() => {
14847
14881
  }
14848
14882
  return payload;
14849
14883
  };
14884
+ capSize(inst, arraySize);
14850
14885
  });
14851
14886
  NO_SYMBOL_KEYS = [];
14852
14887
  $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
@@ -40799,7 +40834,11 @@ var init_prompts = __esm(() => {
40799
40834
  }
40800
40835
  ],
40801
40836
  steps: [
40802
- { tool: "editor_status", passes: [], why: "see whether an editor is connected and its addon matches" },
40837
+ {
40838
+ tool: "editor_status",
40839
+ passes: [],
40840
+ why: "see whether an editor is connected and its addon matches; without one the run is a game of its own, not a fault"
40841
+ },
40803
40842
  { tool: "editor_run", op: "start", passes: ["projectPath", "scene"], why: "start the run" },
40804
40843
  {
40805
40844
  tool: "editor_run",
@@ -40817,7 +40856,11 @@ var init_prompts = __esm(() => {
40817
40856
  description: "Check the editor connection, the project health report, export validation and the import state.",
40818
40857
  arguments: [PROJECT_PATH2],
40819
40858
  steps: [
40820
- { tool: "editor_status", passes: [], why: "see whether an editor is connected and its addon matches" },
40859
+ {
40860
+ tool: "editor_status",
40861
+ passes: [],
40862
+ why: "see whether an editor is connected and, if one is, whether its addon matches; no editor is no fault in the project, so leave it out of what is wrong"
40863
+ },
40821
40864
  {
40822
40865
  tool: "project_info",
40823
40866
  passes: ["projectPath"],
@@ -283,6 +283,7 @@ func _methods_of(class_name_str: String, include_inherited: bool) -> Array[Dicti
283
283
  {
284
284
  "name": a.get("name", ""),
285
285
  "type": a.get("type", 0),
286
+ "type_name": _type_name(a, "Variant"),
286
287
  "class_name": a.get("class_name", ""),
287
288
  "hint_string": a.get("hint_string", "")
288
289
  }
@@ -293,7 +294,11 @@ func _methods_of(class_name_str: String, include_inherited: bool) -> Array[Dicti
293
294
  "name": m.get("name", ""),
294
295
  "args": args,
295
296
  "return":
296
- {"type": return_info.get("type", 0), "class_name": return_info.get("class_name", "")},
297
+ {
298
+ "type": return_info.get("type", 0),
299
+ "type_name": _type_name(return_info, "void"),
300
+ "class_name": return_info.get("class_name", "")
301
+ },
297
302
  "flags": m.get("flags", 0),
298
303
  "default_args": m.get("default_args", [])
299
304
  }
@@ -302,6 +307,20 @@ func _methods_of(class_name_str: String, include_inherited: bool) -> Array[Dicti
302
307
  return methods
303
308
 
304
309
 
310
+ # What [param info]'s type is called: its class for an object of one, and otherwise the engine's own
311
+ # name for the type, beside the number the engine reports, which reads as nothing without its enum.
312
+ # Nil is a value that can be anything when the usage says so, and [param nil_name] otherwise: void
313
+ # for what a method returns, and Variant for an argument or property with no type.
314
+ static func _type_name(info: Dictionary, nil_name: String) -> String:
315
+ var type: int = Read.as_int(info.get("type", 0))
316
+ var named: String = str(info.get("class_name", ""))
317
+ if type == TYPE_OBJECT and not named.is_empty():
318
+ return named
319
+ if type == TYPE_NIL:
320
+ return "Variant" if Read.as_int(info.get("usage", 0)) & PROPERTY_USAGE_NIL_IS_VARIANT else nil_name
321
+ return type_string(type)
322
+
323
+
305
324
  func _properties_of(class_name_str: String, include_inherited: bool) -> Array[Dictionary]:
306
325
  var properties: Array[Dictionary] = []
307
326
 
@@ -314,6 +333,7 @@ func _properties_of(class_name_str: String, include_inherited: bool) -> Array[Di
314
333
  {
315
334
  "name": p.get("name", ""),
316
335
  "type": p.get("type", 0),
336
+ "type_name": _type_name(p, "Variant"),
317
337
  "class_name": p.get("class_name", ""),
318
338
  "hint": p.get("hint", 0),
319
339
  "hint_string": p.get("hint_string", ""),
@@ -332,7 +352,12 @@ func _signals_of(class_name_str: String, include_inherited: bool) -> Array[Dicti
332
352
  var declared_args: Array = s.get("args", [])
333
353
  for a: Dictionary in declared_args:
334
354
  sig_args.append(
335
- {"name": a.get("name", ""), "type": a.get("type", 0), "class_name": a.get("class_name", "")}
355
+ {
356
+ "name": a.get("name", ""),
357
+ "type": a.get("type", 0),
358
+ "type_name": _type_name(a, "Variant"),
359
+ "class_name": a.get("class_name", "")
360
+ }
336
361
  )
337
362
  signals.append({"name": s.get("name", ""), "args": sig_args})
338
363
 
@@ -103,7 +103,16 @@ func get_import_status(params: Dictionary) -> Dictionary:
103
103
 
104
104
  _tally(summary, status)
105
105
 
106
- return {"resources": resources, "summary": summary}
106
+ var answer: Dictionary = {"resources": resources, "summary": summary}
107
+ # A total of none reads as a walk that found nothing when the project is full of scripts and
108
+ # scenes, which the engine loads as they are and never imports.
109
+ if resource_path.is_empty() and summary["total"] == 0:
110
+ answer["note"] = (
111
+ "Nothing in this project is imported: the engine imports images, audio, fonts, models and"
112
+ + " the like, found by extension or by a .import sidecar beside them, and loads scripts and"
113
+ + " scenes as they are."
114
+ )
115
+ return answer
107
116
 
108
117
 
109
118
  # Get import options for a resource
@@ -94,6 +94,11 @@ func set_project_setting(params: Dictionary) -> Dictionary:
94
94
  var had_value: bool = ProjectSettings.has_setting(setting_path)
95
95
  if had_value:
96
96
  old_value = ProjectSettings.get_setting(setting_path)
97
+ # A setting the engine registers is there with its default whether or not project.godot sets it,
98
+ # so has_setting alone called the description of a project that had never set one an old value.
99
+ # At its default, the file does not set it: the engine drops a key at its default when it saves.
100
+ var default_value: Variant = ProjectSettings.property_get_revert(setting_path)
101
+ var was_default: bool = had_value and default_value != null and old_value == default_value
97
102
 
98
103
  var final_value: Variant = _values.deserialize_value(value)
99
104
  var wanted: int = _wanted_type(setting_path, old_value if had_value else null)
@@ -116,7 +121,8 @@ func set_project_setting(params: Dictionary) -> Dictionary:
116
121
  "setting_path": setting_path,
117
122
  "old_value": _values.serialize_value(old_value) if had_value else null,
118
123
  "new_value": _values.serialize_value(final_value),
119
- "was_new": not had_value,
124
+ "was_new": not had_value or was_default,
125
+ "was_default": was_default,
120
126
  "saved": true,
121
127
  }
122
128
 
@@ -30,16 +30,9 @@ func get_project_health(params: Dictionary) -> Dictionary:
30
30
  if not FileAccess.file_exists("res://project.godot"):
31
31
  details.append("Missing project.godot file")
32
32
  deductions += 30
33
-
34
- var recommended_dirs: Array[String] = ["res://scenes", "res://scripts", "res://assets"]
35
- var missing_dirs: Array[String] = []
36
- for dir: String in recommended_dirs:
37
- if not DirAccess.dir_exists_absolute(ProjectSettings.globalize_path(dir)):
38
- missing_dirs.append(dir)
39
-
40
- if missing_dirs.size() > 0:
41
- details.append("Consider organizing with directories: " + ", ".join(missing_dirs))
42
- deductions += 2 * missing_dirs.size()
33
+ # No layout is asked for. A project without res://scenes, res://scripts and res://assets
34
+ # failed this check and lost points, while Godot's own guidance is to keep a scene's files
35
+ # together by feature, so the check failed projects for following the engine's advice.
43
36
 
44
37
  checks["structure"] = {"name": "Project Structure", "passed": details.is_empty(), "details": details}
45
38
 
@@ -74,7 +67,6 @@ func get_project_health(params: Dictionary) -> Dictionary:
74
67
 
75
68
  var script_files: Array[String] = _files.find_files("res://", ".gd")
76
69
  var todo_count: int = 0
77
- var empty_functions: int = 0
78
70
 
79
71
  for script_path: String in script_files:
80
72
  var file: FileAccess = FileAccess.open(script_path, FileAccess.READ)
@@ -84,16 +76,12 @@ func get_project_health(params: Dictionary) -> Dictionary:
84
76
 
85
77
  if "# TODO" in content or "# FIXME" in content:
86
78
  todo_count += 1
87
- if "pass # " in content or content.ends_with("pass\n"):
88
- empty_functions += 1
89
79
 
80
+ # Work noted for later, which no script fails on, so said rather than failed. A count of
81
+ # "empty functions" read off a script ending in `pass` went with it: a virtual left for an
82
+ # override to fill ends that way and is exactly right.
90
83
  if todo_count > 0:
91
- details.append(str(todo_count) + " scripts have TODO/FIXME comments")
92
- deductions += todo_count
93
-
94
- if empty_functions > 0:
95
- details.append(str(empty_functions) + " scripts may have empty functions")
96
- deductions += empty_functions
84
+ recommendations.append("%d scripts have TODO or FIXME comments to look at" % todo_count)
97
85
 
98
86
  checks["scripts"] = {
99
87
  "name": "Scripts",
@@ -136,10 +124,6 @@ func get_project_health(params: Dictionary) -> Dictionary:
136
124
  details.append("No project name set")
137
125
  deductions += 2
138
126
 
139
- if not FileAccess.file_exists("res://export_presets.cfg"):
140
- details.append("No export presets configured")
141
- deductions += 3
142
-
143
127
  checks["config"] = {"name": "Configuration", "passed": details.is_empty(), "details": details}
144
128
 
145
129
  var score: int = maxi(0, 100 - deductions)
@@ -156,11 +140,11 @@ func get_project_health(params: Dictionary) -> Dictionary:
156
140
  else:
157
141
  grade = "F"
158
142
 
159
- if deductions > 0:
160
- if score < 70:
161
- recommendations.append("Address critical issues to improve project stability")
162
- if not FileAccess.file_exists("res://export_presets.cfg"):
163
- recommendations.append("Configure export presets for your target platforms")
143
+ if score < 70:
144
+ recommendations.append("Address critical issues to improve project stability")
145
+ # A project still being made has no presets yet, which is no fault in it.
146
+ if "config" in check_categories and not FileAccess.file_exists("res://export_presets.cfg"):
147
+ recommendations.append("No export presets are configured: add them before exporting")
164
148
 
165
149
  # Every detail costs points, so each is an issue; `issues` was always empty beside them, and a
166
150
  # category said passed next to the failure it had just found.
package/build/index.js CHANGED
@@ -3807,6 +3807,18 @@ function prefixIssues(path, issues) {
3807
3807
  function unwrapMessage(message) {
3808
3808
  return typeof message === "string" ? message : message?.message;
3809
3809
  }
3810
+ function sizeCaps(defChecks) {
3811
+ const caps = [];
3812
+ for (const ch of defChecks ?? []) {
3813
+ const d = ch._zod.def;
3814
+ if (!/_(length|size)$|_equals$/.test(d.check))
3815
+ break;
3816
+ const max = d.maximum ?? d.length ?? d.size;
3817
+ if (max >= 0)
3818
+ caps.push([max, ch]);
3819
+ }
3820
+ return caps;
3821
+ }
3810
3822
  function attachSchema(issues, start, inst) {
3811
3823
  var _a;
3812
3824
  for (let i = start;i < issues.length; i++) {
@@ -4808,7 +4820,7 @@ ${content.join(`
4808
4820
  var version = {
4809
4821
  major: 4,
4810
4822
  minor: 6,
4811
- patch: 5
4823
+ patch: 7
4812
4824
  };
4813
4825
 
4814
4826
  // node_modules/zod/v4/core/schemas.js
@@ -5381,6 +5393,29 @@ var $ZodNever = /* @__PURE__ */ $constructor("$ZodNever", (inst, def) => {
5381
5393
  return payload;
5382
5394
  };
5383
5395
  });
5396
+ function capSize(inst, size) {
5397
+ const caps = sizeCaps(inst._zod.def.checks);
5398
+ if (!caps.length)
5399
+ return;
5400
+ const cap = Math.min(...caps.map((c) => c[0]));
5401
+ const parse = inst._zod.parse;
5402
+ inst._zod.parse = (payload, ctx) => {
5403
+ const n = size(payload.value);
5404
+ if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
5405
+ for (const [max, ch] of sizeCaps(inst._zod.def.checks)) {
5406
+ if (n > max && ch._zod.def.when(payload)) {
5407
+ const start = payload.issues.length;
5408
+ ch._zod.check(payload);
5409
+ payload.issues[start].continue = false;
5410
+ attachSchema(payload.issues, start, inst);
5411
+ return payload;
5412
+ }
5413
+ }
5414
+ }
5415
+ return parse(payload, ctx);
5416
+ };
5417
+ }
5418
+ var arraySize = (input) => Array.isArray(input) ? input.length : -1;
5384
5419
  function handleArrayResult(result, final, index) {
5385
5420
  if (result.issues.length) {
5386
5421
  final.issues.push(...prefixIssues(index, result.issues));
@@ -5424,6 +5459,7 @@ var $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
5424
5459
  }
5425
5460
  return payload;
5426
5461
  };
5462
+ capSize(inst, arraySize);
5427
5463
  });
5428
5464
  function handlePropertyResult(result, final, key, input, optin, optout) {
5429
5465
  const isPresent = key in input;
@@ -31348,7 +31384,11 @@ var PROMPT_SPECS = [
31348
31384
  }
31349
31385
  ],
31350
31386
  steps: [
31351
- { tool: "editor_status", passes: [], why: "see whether an editor is connected and its addon matches" },
31387
+ {
31388
+ tool: "editor_status",
31389
+ passes: [],
31390
+ why: "see whether an editor is connected and its addon matches; without one the run is a game of its own, not a fault"
31391
+ },
31352
31392
  { tool: "editor_run", op: "start", passes: ["projectPath", "scene"], why: "start the run" },
31353
31393
  {
31354
31394
  tool: "editor_run",
@@ -31366,7 +31406,11 @@ var PROMPT_SPECS = [
31366
31406
  description: "Check the editor connection, the project health report, export validation and the import state.",
31367
31407
  arguments: [PROJECT_PATH],
31368
31408
  steps: [
31369
- { tool: "editor_status", passes: [], why: "see whether an editor is connected and its addon matches" },
31409
+ {
31410
+ tool: "editor_status",
31411
+ passes: [],
31412
+ why: "see whether an editor is connected and, if one is, whether its addon matches; no editor is no fault in the project, so leave it out of what is wrong"
31413
+ },
31370
31414
  {
31371
31415
  tool: "project_info",
31372
31416
  passes: ["projectPath"],
@@ -33895,7 +33939,7 @@ var TOOL_SPECS = [
33895
33939
  {
33896
33940
  name: "project_settings",
33897
33941
  title: "Project settings",
33898
- description: "Reads or writes project.godot: settings, autoloads, the main scene, input actions, plugins and audio buses. A write with an editor open on the project is handed to the editor, which takes the settings the write changed or names from the file (named under editorAdopted), including a setting the file already held, since the editor can hold another value after the file was put back, or reloads the bus layout, so it answers the new values and does not save the old ones back over them. editorNote is an editor that could not take the write, one on an addon from before this, which still holds the old values until editor_launch restart. An enabled plugin's code still loads on the editor's next start.",
33942
+ description: "Reads or writes project.godot: settings, autoloads, the main scene, input actions, plugins and audio buses. A write with an editor open on the project is handed to the editor, which takes the settings the write changed or names from the file (named under editorAdopted), including a setting the file already held, since the editor can hold another value after the file was put back, or reloads the bus layout, so it answers the new values and does not save the old ones back over them. editorNote is an editor that could not take the write, one on an addon from before this, which still holds the old values until editor_launch restart. An enabled plugin's code still loads on the editor's next start. set answers old_value and new_value, with was_new true when project.godot did not set the setting before, and was_default true when what it held was the engine's own default, which old_value then is.",
33899
33943
  parameters: {
33900
33944
  projectPath: PROJECT_PATH2,
33901
33945
  setting: { type: "string", description: 'Setting path, such as "display/window/size/viewport_width".' },
@@ -34598,7 +34642,7 @@ var TOOL_SPECS = [
34598
34642
  },
34599
34643
  check: {
34600
34644
  effect: "additive",
34601
- summary: "boot headless, quit after a few frames, and report the verdict",
34645
+ summary: "boot headless, quit after a few frames, and report the verdict. booted is that verdict rather than whether the game came up: true only for a run that quit on its own within timeoutMs, exited 0 and printed no error, so a game that reached _ready and then raised an error answers false. hung is a run killed at timeoutMs, and errors and warnings count what it printed, which comes back under entries with repeats folded together",
34602
34646
  requires: ["projectPath"]
34603
34647
  },
34604
34648
  wait: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "1.2.13",
3
+ "version": "1.2.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",