gdharness 1.1.39 → 1.1.41

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
@@ -17399,7 +17399,7 @@ var init_tool_definitions = __esm(() => {
17399
17399
  },
17400
17400
  {
17401
17401
  name: "project_settings",
17402
- 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 from the file (named under editorAdopted), 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.",
17402
+ 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.",
17403
17403
  parameters: {
17404
17404
  projectPath: PROJECT_PATH,
17405
17405
  setting: { type: "string", description: 'Setting path, such as "display/window/size/viewport_width".' },
@@ -17769,7 +17769,7 @@ var init_tool_definitions = __esm(() => {
17769
17769
  },
17770
17770
  {
17771
17771
  name: "script_edit",
17772
- description: "Creates a GDScript file, or adds functions, variables and signals to one. Every declaration written carries a type. Both load what they wrote under the project's own warning settings and answer with parses; the engine's reasons for a refusal come back under engine_messages. create answers with the extends and class_name the engine read, and a script declaring a class_name is put in the project's class list, with registered saying whether it is there. modify makes every addition or none, and answers with the line each one is on in the file written. rename renames a class or a member wherever the engine uses it and writes every file or none. A new name already taken is refused with nothing written, a member's included when a class inheriting it already uses the name in code, as a local or a parameter. changed lists each file with its changed lines as they now read, and leftAlone lists every other whole-word occurrence of the old name with what the tokenizer makes of it (code, string, comment, doc, nodePath, scene, text, or ignored for a file under a .gdignore directory, which is never changed) and, where it is not plain, why: a string that is the old name whole, such as find_children's type or a call's method name, which nothing renames or compiles and which is listed first; a node named like the class; a call on a value whose type the analyser could not tell. A member rename answers under languageServer how many requests it made and how long they took. A comment, documentation line, string or text file line with nothing more to say is given under leftAloneInProse by file, kind and line instead, and code that is certainly another symbol of the same name, a bare name in a file where nothing inherits the member or one the language server resolved to another declaration, under leftAloneOtherSymbols as each why with the files and lines it covers. summary.leftAlone counts each of the three lists beside the total. When those two grouped lists run past 300 lines, they are written with each line's text to a file under the project's .godot directory instead, and leftAloneDetail names the file and counts what is in it. Read both before calling the rename done; nothing in them was changed. Afterwards an open editor scans the changed files and reloads every changed script, which is also the compile check, and its answer comes back under editor; with no editor the class cache is rebuilt and the changed scripts are compiled in a fresh engine, under classCache and compiled. written is true once the files are written, and ok is false when what came after the write failed: the files are left changed, and note says so.",
17772
+ description: "Creates a GDScript file, or adds functions, variables and signals to one. Every declaration written carries a type. Both load what they wrote under the project's own warning settings and answer with parses; the engine's reasons for a refusal come back under engine_messages. create answers with the extends and class_name the engine read, and a script declaring a class_name is put in the project's class list, with registered saying whether it is there. modify makes every addition or none, and answers with the line each one is on in the file written. rename renames a class or a member wherever the engine uses it and writes every file or none. A new name already taken is refused with nothing written, a member's included when a class inheriting it already uses the name in code, as a local or a parameter. changed lists each file with its changed lines as they now read, and leftAlone lists every other whole-word occurrence of the old name with what the tokenizer makes of it (code, string, comment, doc, nodePath, scene, text, or ignored for a file under a .gdignore directory, which is never changed) and, where it is not plain, why: a string that is the old name whole, such as find_children's type or a call's method name, which nothing renames or compiles and which is listed first; a node named like the class; a call on a value whose type the analyser could not tell. A member rename answers under languageServer how many requests it made and how long they took. A comment, documentation line, string or text file line with nothing more to say is given under leftAloneInProse by file, kind and line instead, and code that is certainly another symbol of the same name, a bare name in a file where nothing inherits the member or one the language server resolved to another declaration, under leftAloneOtherSymbols as each why with the files and lines it covers. summary.leftAlone counts each of the three lists beside the total. When those two grouped lists run past 300 lines, they are written with each line's text to a file under the project's .godot directory instead, of which the newest ten are kept, and leftAloneDetail names the file and counts what is in it. Read both before calling the rename done; nothing in them was changed. Afterwards an open editor scans the changed files and reloads every changed script, which is also the compile check, and its answer comes back under editor; with no editor the class cache is rebuilt and the changed scripts are compiled in a fresh engine, under classCache and compiled. written is true once the files are written, and ok is false when what came after the write failed: the files are left changed, and note says so.",
17773
17773
  parameters: {
17774
17774
  projectPath: PROJECT_PATH,
17775
17775
  scriptPath: SCRIPT_PATH,
@@ -40178,7 +40178,7 @@ function planMemberRename(args) {
40178
40178
  } else if (occurrence.kind === "doc" && docLinkAt(script.text, one.offset) !== null) {
40179
40179
  plan.edit(one.file, one.offset, "doc link");
40180
40180
  } else {
40181
- plan.mention(one.file, script.lines, one.offset, occurrence.kind, `the language server resolved this to the ${kind === "func" ? "method" : "member"}, and it is inside ${occurrence.kind === "string" ? "a string" : occurrence.kind === "nodePath" ? "a node path" : "a comment"}, so it is left for you`);
40181
+ plan.mention(one.file, script.lines, one.offset, occurrence.kind, `the language server resolved this word to the ${kind === "func" ? "method" : "member"}, but it is inside ${occurrence.kind === "string" ? "a string" : occurrence.kind === "nodePath" ? "a node path" : "a comment"}, which no rename changes: read it, and change the word by hand if it names the ${kind === "func" ? "method" : "member"}`);
40182
40182
  }
40183
40183
  }
40184
40184
  for (const script of scripts.values()) {
@@ -41977,6 +41977,25 @@ function changedSettings(before, after) {
41977
41977
  const names = new Set([...before.keys(), ...after.keys()]);
41978
41978
  return [...names].filter((name) => JSON.stringify(before.get(name)) !== JSON.stringify(after.get(name))).sort();
41979
41979
  }
41980
+ function settingsNamedBy(op, args) {
41981
+ const text = (key) => readString(args, key) ?? "";
41982
+ switch (op) {
41983
+ case "set":
41984
+ return text("setting") === "" ? [] : [text("setting")];
41985
+ case "add_autoload":
41986
+ case "remove_autoload":
41987
+ return text("name") === "" ? [] : [`autoload/${text("name")}`];
41988
+ case "set_main_scene":
41989
+ return ["application/run/main_scene"];
41990
+ case "add_input_action":
41991
+ return text("actionName") === "" ? [] : [`input/${text("actionName")}`];
41992
+ case "enable_plugin":
41993
+ case "disable_plugin":
41994
+ return ["editor_plugins/enabled"];
41995
+ default:
41996
+ return [];
41997
+ }
41998
+ }
41980
41999
  function scriptErrorsNote(errors) {
41981
42000
  const causes = new Map;
41982
42001
  for (const error of errors) {
@@ -43199,7 +43218,8 @@ class GodotServer {
43199
43218
  if (AUDIO_BUS_OPS.has(op)) {
43200
43219
  return this.jsonTextResponse({ ...answer, ...await this.editorTakes("adopt_audio_bus_layout", {}) });
43201
43220
  }
43202
- const changed = changedSettings(before, this.settingKeysOf(projectPath));
43221
+ const diffed = changedSettings(before, this.settingKeysOf(projectPath));
43222
+ const changed = diffed === null ? null : [...new Set([...diffed, ...settingsNamedBy(op, args)])].sort();
43203
43223
  if (changed === null) {
43204
43224
  return this.jsonTextResponse({
43205
43225
  ...answer,
@@ -46172,7 +46192,7 @@ class GodotServer {
46172
46192
  const folded = asParams(outcome.report["leftAloneDetail"]);
46173
46193
  outcome.report["leftAloneDetail"] = {
46174
46194
  file,
46175
- note: `Too many lines were left alone to list here, and none of them is a use: every comment, documentation line, string and text file line that only shares the word, and every other symbol of the same name, is in ${file} with its text.`,
46195
+ note: `Too many lines were left alone to list here, and none of them is a use: every comment, documentation line, string and text file line that only shares the word, and every other symbol of the same name, is in ${file} with its text. The newest ten of these files are kept and older ones removed as new ones are written.`,
46176
46196
  ...folded
46177
46197
  };
46178
46198
  }
@@ -288,6 +288,10 @@ static func _areas_of(drawn: Array[Node]) -> Array[Rect2]:
288
288
  ## the click scrolls it into view first: what is drawn over its place now is not what it will land
289
289
  ## under. Judged not covered at all instead, a button below the fold of a roster that a full-screen
290
290
  ## page covered was picked over the ones in view, scrolled up under the page and pressed there.
291
+ ## Whatever else the scroll holds moves with it and is judged where it goes. Judged where it sat, the
292
+ ## card at the top of a tray scrolled down was drawn over a button above the view, because a later
293
+ ## card is drawn over an earlier one, and a button below the view was passed by the same card only
294
+ ## because the cards beside the bottom edge come before it.
291
295
  ##
292
296
  ## [param areas] holds the box around each of [param drawn], so everything whose box misses the
293
297
  ## point is passed over before anything costlier is asked of it. A window's box is in the space of
@@ -295,7 +299,8 @@ static func _areas_of(drawn: Array[Node]) -> Array[Rect2]:
295
299
  ## so a window is judged by [method _window_over] instead.
296
300
  static func _cover_of(target: Control, drawn: Array[Node], areas: Array[Rect2]) -> Node:
297
301
  var viewport: Viewport = target.get_viewport()
298
- var point: Vector2 = _where_it_is_clicked(target)
302
+ var moves: Dictionary[ScrollContainer, Vector2] = _scrolls_for(target)
303
+ var point: Vector2 = _centre_of(target) + _moved_by(target, moves)
299
304
  var layer: int = _layer_of(target)
300
305
  for at: int in drawn.size():
301
306
  var other: Node = drawn[at]
@@ -304,9 +309,11 @@ static func _cover_of(target: Control, drawn: Array[Node], areas: Array[Rect2])
304
309
  if _window_over(target, point, window):
305
310
  return window
306
311
  continue
307
- if not areas[at].has_point(point):
308
- continue
309
312
  var control: Control = other
313
+ # The point where this control is now, which is where the scroll will carry it to the point.
314
+ var before: Vector2 = point - _moved_by(control, moves)
315
+ if not areas[at].has_point(before):
316
+ continue
310
317
  if control == target or control.is_ancestor_of(target) or target.is_ancestor_of(control):
311
318
  continue
312
319
  if control.get_viewport() != viewport:
@@ -314,18 +321,34 @@ static func _cover_of(target: Control, drawn: Array[Node], areas: Array[Rect2])
314
321
  var its_layer: int = _layer_of(control)
315
322
  if its_layer < layer or (its_layer == layer and not control.is_greater_than(target)):
316
323
  continue
317
- if _holds(control, point) and not _clipped_away(control, point):
324
+ if _holds(control, before) and not _clipped_away(control, point, moves):
318
325
  return control
319
326
  return null
320
327
 
321
328
 
322
- ## Where a click at [param target] lands in its viewport: its centre, or where scrolling will bring
323
- ## the centre when a ScrollContainer holding it clips it away, as the click scrolls it first.
324
- static func _where_it_is_clicked(target: Control) -> Vector2:
325
- var point: Vector2 = target.get_global_transform_with_canvas() * (target.size * 0.5)
326
- if _clipped_away(target, point):
327
- point = _where_it_lands(target, point)
328
- return point
329
+ ## [param target]'s centre in its viewport, where it is now.
330
+ static func _centre_of(target: Control) -> Vector2:
331
+ return target.get_global_transform_with_canvas() * (target.size * 0.5)
332
+
333
+
334
+ ## How far the click moves what each ScrollContainer holding [param target] holds, to bring it into
335
+ ## view: empty when no container clips it away, as nothing is scrolled then.
336
+ static func _scrolls_for(target: Control) -> Dictionary[ScrollContainer, Vector2]:
337
+ var none: Dictionary[ScrollContainer, Vector2] = {}
338
+ var centre: Vector2 = _centre_of(target)
339
+ if not _clipped_away(target, centre, none):
340
+ return none
341
+ return _where_it_lands(target, centre)
342
+
343
+
344
+ ## How far the scrolls in [param moves] carry [param node]: the sum over the containers holding it,
345
+ ## and nothing for a container itself, which stays where it is while what it holds moves.
346
+ static func _moved_by(node: Node, moves: Dictionary[ScrollContainer, Vector2]) -> Vector2:
347
+ var moved: Vector2 = Vector2.ZERO
348
+ for holder: ScrollContainer in moves:
349
+ if holder.is_ancestor_of(node):
350
+ moved += moves[holder]
351
+ return moved
329
352
 
330
353
 
331
354
  ## Whether the embedded [param window] is drawn over [param point], [param target]'s centre in its
@@ -366,14 +389,15 @@ static func _holds(control: Control, point: Vector2) -> bool:
366
389
  return Rect2(Vector2.ZERO, control.size).has_point(placed.affine_inverse() * point)
367
390
 
368
391
 
369
- ## Where [param target]'s centre, now at [param point], will be once the click has scrolled it into
370
- ## view: moved as little as puts the whole of it inside each ScrollContainer holding it, innermost
371
- ## first, which is how one brings a control into view. A target larger than a container is centred
372
- ## in it. Only a ScrollContainer, because that is all a click scrolls: another container clipping
373
- ## what it holds, a collapsed section for one, leaves the target where it is.
374
- static func _where_it_lands(target: Control, point: Vector2) -> Vector2:
392
+ ## How far each ScrollContainer holding [param target] moves what it holds once the click has
393
+ ## scrolled [param target], centred at [param point], into view: as little as puts the whole of it
394
+ ## inside each, innermost first, which is how one brings a control into view. A target larger than a
395
+ ## container is centred in it. Only a ScrollContainer, because that is all a click scrolls: another
396
+ ## container clipping what it holds, a collapsed section for one, leaves the target where it is.
397
+ static func _where_it_lands(target: Control, point: Vector2) -> Dictionary[ScrollContainer, Vector2]:
375
398
  var drawn: Transform2D = target.get_global_transform_with_canvas()
376
399
  var half: Vector2 = (drawn * Rect2(Vector2.ZERO, target.size)).size * 0.5
400
+ var moves: Dictionary[ScrollContainer, Vector2] = {}
377
401
  var landed: Vector2 = point
378
402
  var walk: Node = target.get_parent()
379
403
  while walk != null and not (walk is Viewport):
@@ -382,12 +406,15 @@ static func _where_it_lands(target: Control, point: Vector2) -> Vector2:
382
406
  var shown: Rect2 = holder.get_global_transform_with_canvas() * Rect2(Vector2.ZERO, holder.size)
383
407
  var low: Vector2 = shown.position + half
384
408
  var high: Vector2 = shown.end - half
385
- landed = Vector2(
409
+ var next: Vector2 = Vector2(
386
410
  shown.get_center().x if low.x > high.x else clampf(landed.x, low.x, high.x),
387
411
  shown.get_center().y if low.y > high.y else clampf(landed.y, low.y, high.y)
388
412
  )
413
+ if next != landed:
414
+ moves[holder] = next - landed
415
+ landed = next
389
416
  walk = walk.get_parent()
390
- return landed
417
+ return moves
391
418
 
392
419
 
393
420
  ## Whether a click could not reach [param target] however it scrolled: its centre, where the click
@@ -395,8 +422,9 @@ static func _where_it_lands(target: Control, point: Vector2) -> Vector2:
395
422
  ## container the click does not scroll. A drawer parked off the side of the screen, or anything
396
423
  ## outside the 64 by 64 viewport of a game with no window, was listed among the controls on screen.
397
424
  static func _off_screen(target: Control) -> bool:
398
- var point: Vector2 = _where_it_is_clicked(target)
399
- if _clipped_away(target, point) or not target.get_viewport().get_visible_rect().has_point(point):
425
+ var moves: Dictionary[ScrollContainer, Vector2] = _scrolls_for(target)
426
+ var point: Vector2 = _centre_of(target) + _moved_by(target, moves)
427
+ if _clipped_away(target, point, moves) or not target.get_viewport().get_visible_rect().has_point(point):
400
428
  return true
401
429
  var reached: Dictionary = Screen.reach(target.get_viewport(), point)
402
430
  var viewport: Viewport = reached["viewport"]
@@ -404,16 +432,19 @@ static func _off_screen(target: Control) -> bool:
404
432
  return not viewport.get_visible_rect().has_point(there)
405
433
 
406
434
 
407
- ## Whether a container clipping what it holds cuts [param point] off from [param control].
435
+ ## Whether a container clipping what it holds cuts [param point] off from [param control], once the
436
+ ## scrolls in [param moves] have carried each container wherever they carry it.
408
437
  ##
409
438
  ## Only up to the viewport [param control] is in. A dialog is a window, and the containers the game
410
439
  ## added it under are in another space: judged against a clipping one of those, every button of a
411
440
  ## confirmation was refused as off the screen while it sat in plain view.
412
- static func _clipped_away(control: Control, point: Vector2) -> bool:
441
+ static func _clipped_away(
442
+ control: Control, point: Vector2, moves: Dictionary[ScrollContainer, Vector2]
443
+ ) -> bool:
413
444
  var walk: Node = control.get_parent()
414
445
  while walk != null and not (walk is Viewport):
415
446
  var holder: Control = walk as Control
416
- if holder != null and holder.clip_contents and not _holds(holder, point):
447
+ if holder != null and holder.clip_contents and not _holds(holder, point - _moved_by(holder, moves)):
417
448
  return true
418
449
  walk = walk.get_parent()
419
450
  return false
package/build/index.js CHANGED
@@ -30900,7 +30900,7 @@ function planMemberRename(args) {
30900
30900
  } else if (occurrence.kind === "doc" && docLinkAt(script.text, one.offset) !== null) {
30901
30901
  plan.edit(one.file, one.offset, "doc link");
30902
30902
  } else {
30903
- plan.mention(one.file, script.lines, one.offset, occurrence.kind, `the language server resolved this to the ${kind === "func" ? "method" : "member"}, and it is inside ${occurrence.kind === "string" ? "a string" : occurrence.kind === "nodePath" ? "a node path" : "a comment"}, so it is left for you`);
30903
+ plan.mention(one.file, script.lines, one.offset, occurrence.kind, `the language server resolved this word to the ${kind === "func" ? "method" : "member"}, but it is inside ${occurrence.kind === "string" ? "a string" : occurrence.kind === "nodePath" ? "a node path" : "a comment"}, which no rename changes: read it, and change the word by hand if it names the ${kind === "func" ? "method" : "member"}`);
30904
30904
  }
30905
30905
  }
30906
30906
  for (const script of scripts.values()) {
@@ -32820,7 +32820,7 @@ var TOOL_SPECS = [
32820
32820
  },
32821
32821
  {
32822
32822
  name: "project_settings",
32823
- 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 from the file (named under editorAdopted), 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.",
32823
+ 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.",
32824
32824
  parameters: {
32825
32825
  projectPath: PROJECT_PATH,
32826
32826
  setting: { type: "string", description: 'Setting path, such as "display/window/size/viewport_width".' },
@@ -33190,7 +33190,7 @@ var TOOL_SPECS = [
33190
33190
  },
33191
33191
  {
33192
33192
  name: "script_edit",
33193
- description: "Creates a GDScript file, or adds functions, variables and signals to one. Every declaration written carries a type. Both load what they wrote under the project's own warning settings and answer with parses; the engine's reasons for a refusal come back under engine_messages. create answers with the extends and class_name the engine read, and a script declaring a class_name is put in the project's class list, with registered saying whether it is there. modify makes every addition or none, and answers with the line each one is on in the file written. rename renames a class or a member wherever the engine uses it and writes every file or none. A new name already taken is refused with nothing written, a member's included when a class inheriting it already uses the name in code, as a local or a parameter. changed lists each file with its changed lines as they now read, and leftAlone lists every other whole-word occurrence of the old name with what the tokenizer makes of it (code, string, comment, doc, nodePath, scene, text, or ignored for a file under a .gdignore directory, which is never changed) and, where it is not plain, why: a string that is the old name whole, such as find_children's type or a call's method name, which nothing renames or compiles and which is listed first; a node named like the class; a call on a value whose type the analyser could not tell. A member rename answers under languageServer how many requests it made and how long they took. A comment, documentation line, string or text file line with nothing more to say is given under leftAloneInProse by file, kind and line instead, and code that is certainly another symbol of the same name, a bare name in a file where nothing inherits the member or one the language server resolved to another declaration, under leftAloneOtherSymbols as each why with the files and lines it covers. summary.leftAlone counts each of the three lists beside the total. When those two grouped lists run past 300 lines, they are written with each line's text to a file under the project's .godot directory instead, and leftAloneDetail names the file and counts what is in it. Read both before calling the rename done; nothing in them was changed. Afterwards an open editor scans the changed files and reloads every changed script, which is also the compile check, and its answer comes back under editor; with no editor the class cache is rebuilt and the changed scripts are compiled in a fresh engine, under classCache and compiled. written is true once the files are written, and ok is false when what came after the write failed: the files are left changed, and note says so.",
33193
+ description: "Creates a GDScript file, or adds functions, variables and signals to one. Every declaration written carries a type. Both load what they wrote under the project's own warning settings and answer with parses; the engine's reasons for a refusal come back under engine_messages. create answers with the extends and class_name the engine read, and a script declaring a class_name is put in the project's class list, with registered saying whether it is there. modify makes every addition or none, and answers with the line each one is on in the file written. rename renames a class or a member wherever the engine uses it and writes every file or none. A new name already taken is refused with nothing written, a member's included when a class inheriting it already uses the name in code, as a local or a parameter. changed lists each file with its changed lines as they now read, and leftAlone lists every other whole-word occurrence of the old name with what the tokenizer makes of it (code, string, comment, doc, nodePath, scene, text, or ignored for a file under a .gdignore directory, which is never changed) and, where it is not plain, why: a string that is the old name whole, such as find_children's type or a call's method name, which nothing renames or compiles and which is listed first; a node named like the class; a call on a value whose type the analyser could not tell. A member rename answers under languageServer how many requests it made and how long they took. A comment, documentation line, string or text file line with nothing more to say is given under leftAloneInProse by file, kind and line instead, and code that is certainly another symbol of the same name, a bare name in a file where nothing inherits the member or one the language server resolved to another declaration, under leftAloneOtherSymbols as each why with the files and lines it covers. summary.leftAlone counts each of the three lists beside the total. When those two grouped lists run past 300 lines, they are written with each line's text to a file under the project's .godot directory instead, of which the newest ten are kept, and leftAloneDetail names the file and counts what is in it. Read both before calling the rename done; nothing in them was changed. Afterwards an open editor scans the changed files and reloads every changed script, which is also the compile check, and its answer comes back under editor; with no editor the class cache is rebuilt and the changed scripts are compiled in a fresh engine, under classCache and compiled. written is true once the files are written, and ok is false when what came after the write failed: the files are left changed, and note says so.",
33194
33194
  parameters: {
33195
33195
  projectPath: PROJECT_PATH,
33196
33196
  scriptPath: SCRIPT_PATH,
@@ -34517,6 +34517,25 @@ function changedSettings(before, after) {
34517
34517
  const names = new Set([...before.keys(), ...after.keys()]);
34518
34518
  return [...names].filter((name) => JSON.stringify(before.get(name)) !== JSON.stringify(after.get(name))).sort();
34519
34519
  }
34520
+ function settingsNamedBy(op, args) {
34521
+ const text = (key) => readString(args, key) ?? "";
34522
+ switch (op) {
34523
+ case "set":
34524
+ return text("setting") === "" ? [] : [text("setting")];
34525
+ case "add_autoload":
34526
+ case "remove_autoload":
34527
+ return text("name") === "" ? [] : [`autoload/${text("name")}`];
34528
+ case "set_main_scene":
34529
+ return ["application/run/main_scene"];
34530
+ case "add_input_action":
34531
+ return text("actionName") === "" ? [] : [`input/${text("actionName")}`];
34532
+ case "enable_plugin":
34533
+ case "disable_plugin":
34534
+ return ["editor_plugins/enabled"];
34535
+ default:
34536
+ return [];
34537
+ }
34538
+ }
34520
34539
  var STALLED_AFTER_MS = 30000;
34521
34540
  var DEPENDED_FAILED = "Failed to compile depended scripts";
34522
34541
  function scriptErrorsNote(errors) {
@@ -35773,7 +35792,8 @@ class GodotServer {
35773
35792
  if (AUDIO_BUS_OPS.has(op)) {
35774
35793
  return this.jsonTextResponse({ ...answer, ...await this.editorTakes("adopt_audio_bus_layout", {}) });
35775
35794
  }
35776
- const changed = changedSettings(before, this.settingKeysOf(projectPath));
35795
+ const diffed = changedSettings(before, this.settingKeysOf(projectPath));
35796
+ const changed = diffed === null ? null : [...new Set([...diffed, ...settingsNamedBy(op, args)])].sort();
35777
35797
  if (changed === null) {
35778
35798
  return this.jsonTextResponse({
35779
35799
  ...answer,
@@ -38746,7 +38766,7 @@ class GodotServer {
38746
38766
  const folded = asParams(outcome.report["leftAloneDetail"]);
38747
38767
  outcome.report["leftAloneDetail"] = {
38748
38768
  file,
38749
- note: `Too many lines were left alone to list here, and none of them is a use: every comment, documentation line, string and text file line that only shares the word, and every other symbol of the same name, is in ${file} with its text.`,
38769
+ note: `Too many lines were left alone to list here, and none of them is a use: every comment, documentation line, string and text file line that only shares the word, and every other symbol of the same name, is in ${file} with its text. The newest ten of these files are kept and older ones removed as new ones are written.`,
38750
38770
  ...folded
38751
38771
  };
38752
38772
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "1.1.39",
3
+ "version": "1.1.41",
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",