gdharness 1.1.48 → 1.1.49

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
@@ -18589,6 +18589,106 @@ var init_headless_operations = __esm(() => {
18589
18589
  });
18590
18590
  });
18591
18591
 
18592
+ // src/tool-reference.ts
18593
+ function namedType(declared) {
18594
+ if (typeof declared === "string") {
18595
+ return declared;
18596
+ }
18597
+ if (Array.isArray(declared) && declared.every((kind) => typeof kind === "string")) {
18598
+ return declared.join(" or ");
18599
+ }
18600
+ return "any";
18601
+ }
18602
+ function argumentShape(schema) {
18603
+ const named = namedType(schema["type"]);
18604
+ const items = schema["items"];
18605
+ const held = typeof items === "object" && items !== null && !Array.isArray(items) ? namedType(items["type"]) : "any";
18606
+ return named.split(" or ").map((kind) => kind === "array" && held !== "any" ? `array of ${held}s` : kind).join(" or ");
18607
+ }
18608
+ function headlessSection() {
18609
+ const everyOp = new Map;
18610
+ for (const table of [HEADLESS_OPERATIONS, ENGINE_PASSES]) {
18611
+ for (const [tool, ops] of Object.entries(table)) {
18612
+ everyOp.set(tool, [...everyOp.get(tool) ?? [], ...Object.keys(ops)]);
18613
+ }
18614
+ }
18615
+ const byTool = [...everyOp].map(([tool, ops]) => `\`${tool}\` (${ops.join(", ")})`).sort();
18616
+ return [
18617
+ "## What a gate can call",
18618
+ "",
18619
+ "These answer from a short-lived headless engine: no editor window, no running game, and no",
18620
+ "process or port outliving the answer. They are the only calls an automatic check can make at",
18621
+ "all, because every other tool needs an editor somebody has open or a game somebody is running.",
18622
+ "",
18623
+ ...byTool.map((line) => `- ${line}`),
18624
+ "",
18625
+ "Needing nothing is not the same as changing nothing, and a gate wants both. Most of these",
18626
+ "write to the project because that is what they are for: `script_edit create` writes a file,",
18627
+ "`project_settings set` rewrites `project.godot`, `project_import reimport` and `refresh_classes`",
18628
+ "write under `.godot`, and `project_import refresh_uids` writes a `.uid` beside each script that",
18629
+ "had none. The reading ops are the ones to gate on, and which is which is in each",
18630
+ "op's own summary below rather than assumed from this list.",
18631
+ "",
18632
+ "Everything else needs the editor (`editor_*`, `debug_*`, `scene_*`, `resource_*`) or a running",
18633
+ "game (`runtime_*`).",
18634
+ "",
18635
+ 'One of these can be pointed the other way: `project_settings get` takes `from: "editor"` to ask',
18636
+ "the open editor instead of the file, which is a different reading and costs no engine start. A",
18637
+ "gate leaves it alone, because there is no editor on a build machine and the call is refused",
18638
+ "rather than answered from the file.",
18639
+ ""
18640
+ ];
18641
+ }
18642
+ function renderToolsMarkdown() {
18643
+ const lines = [
18644
+ "# Tools",
18645
+ "",
18646
+ `${TOOL_SPECS.length} tools, named \`domain_verb\`. A tool that does several related things takes an`,
18647
+ "`op`. An unknown op or argument is refused with the valid set listed. Generated from the server.",
18648
+ "",
18649
+ `${projectPathSentence()}: the \`runtime_*\` and \`debug_*\` ones pick between running games`,
18650
+ "instead, and the others answer about the editor on this server. The pick, when a call names",
18651
+ `neither \`projectPath\` nor \`pid\`: ${theGamePicked()}. An argument a tool does not`,
18652
+ "declare is refused rather than ignored. Answers are read from the engine after the change, not",
18653
+ "echoed from the request. Engine stderr comes back under `engine_messages`.",
18654
+ "",
18655
+ ...headlessSection()
18656
+ ];
18657
+ for (const tool of TOOL_SPECS) {
18658
+ lines.push(`## ${tool.name}`, "", tool.description, "");
18659
+ const operations = Object.entries(tool.operations ?? {});
18660
+ if (operations.length > 0) {
18661
+ for (const [op, spec] of operations) {
18662
+ const needs = [...tool.requires, ...spec.requires];
18663
+ const isDefault = tool.defaultOperation === op ? " (default)" : "";
18664
+ const wants = needs.length > 0 ? ` Needs: ${needs.map((name) => `\`${name}\``).join(", ")}.` : "";
18665
+ const spare = argumentsOf(tool, op).filter((name) => !needs.includes(name) && name !== "projectPath");
18666
+ const takes = spare.length > 0 ? ` Takes: ${spare.map((name) => `\`${name}\``).join(", ")}.` : "";
18667
+ lines.push(`- \`op: ${op}\`${isDefault}: ${spec.summary}.${wants}${takes}`);
18668
+ }
18669
+ lines.push("");
18670
+ } else if (tool.requires.length > 0) {
18671
+ lines.push(`Needs: ${tool.requires.map((name) => `\`${name}\``).join(", ")}.`, "");
18672
+ }
18673
+ const parameters = Object.entries(tool.parameters).filter(([name]) => name !== "projectPath");
18674
+ if (parameters.length > 0) {
18675
+ lines.push("Arguments:", "");
18676
+ for (const [name, schema] of parameters) {
18677
+ const type = argumentShape(schema);
18678
+ const note = typeof schema["description"] === "string" ? `: ${schema["description"]}` : "";
18679
+ lines.push(`- \`${name}\` (${type})${note}`);
18680
+ }
18681
+ lines.push("");
18682
+ }
18683
+ }
18684
+ return lines.join(`
18685
+ `);
18686
+ }
18687
+ var init_tool_reference = __esm(() => {
18688
+ init_headless_operations();
18689
+ init_tool_definitions();
18690
+ });
18691
+
18592
18692
  // node_modules/zod/v3/helpers/util.js
18593
18693
  var util, objectUtil, ZodParsedType, getParsedType = (data) => {
18594
18694
  const t = typeof data;
@@ -42945,7 +43045,14 @@ class GodotServer {
42945
43045
  const asked = readString(args, "op");
42946
43046
  const named = asked !== undefined && spec.operations?.[asked] !== undefined ? asked : null;
42947
43047
  const wanted = named === null ? [...known] : [...argumentsOf(spec, named), "op"];
42948
- const takes = wanted.length === 0 ? "It takes no arguments." : `It takes: ${wanted.join(", ")}.`;
43048
+ const shaped = wanted.map((name) => {
43049
+ const schema = spec.parameters[name];
43050
+ if (schema !== undefined) {
43051
+ return `${name} (${argumentShape(schema)})`;
43052
+ }
43053
+ return name === "op" && named === null && spec.operations !== undefined ? `op (${Object.keys(spec.operations).join(", ")})` : name;
43054
+ });
43055
+ const takes = wanted.length === 0 ? "It takes no arguments." : `It takes: ${shaped.join(", ")}.`;
42949
43056
  const where = named === null ? spec.name : `${spec.name} ${named}`;
42950
43057
  return {
42951
43058
  ok: false,
@@ -47177,6 +47284,7 @@ var init_server3 = __esm(() => {
47177
47284
  init_server_version();
47178
47285
  init_setup();
47179
47286
  init_tool_definitions();
47287
+ init_tool_reference();
47180
47288
  init_update_check();
47181
47289
  run4 = promisify5(execFile5);
47182
47290
  __dirname2 = dirname19(fileURLToPath5(import.meta.url));
@@ -48170,102 +48278,9 @@ init_setup();
48170
48278
  // src/skill.ts
48171
48279
  init_setup();
48172
48280
  init_tool_definitions();
48281
+ init_tool_reference();
48173
48282
  import { existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync5, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "node:fs";
48174
48283
  import { basename as basename4, dirname as dirname6, join as join11 } from "node:path";
48175
-
48176
- // src/tool-reference.ts
48177
- init_headless_operations();
48178
- init_tool_definitions();
48179
- function namedType(declared) {
48180
- if (typeof declared === "string") {
48181
- return declared;
48182
- }
48183
- if (Array.isArray(declared) && declared.every((kind) => typeof kind === "string")) {
48184
- return declared.join(" or ");
48185
- }
48186
- return "any";
48187
- }
48188
- function headlessSection() {
48189
- const everyOp = new Map;
48190
- for (const table of [HEADLESS_OPERATIONS, ENGINE_PASSES]) {
48191
- for (const [tool, ops] of Object.entries(table)) {
48192
- everyOp.set(tool, [...everyOp.get(tool) ?? [], ...Object.keys(ops)]);
48193
- }
48194
- }
48195
- const byTool = [...everyOp].map(([tool, ops]) => `\`${tool}\` (${ops.join(", ")})`).sort();
48196
- return [
48197
- "## What a gate can call",
48198
- "",
48199
- "These answer from a short-lived headless engine: no editor window, no running game, and no",
48200
- "process or port outliving the answer. They are the only calls an automatic check can make at",
48201
- "all, because every other tool needs an editor somebody has open or a game somebody is running.",
48202
- "",
48203
- ...byTool.map((line) => `- ${line}`),
48204
- "",
48205
- "Needing nothing is not the same as changing nothing, and a gate wants both. Most of these",
48206
- "write to the project because that is what they are for: `script_edit create` writes a file,",
48207
- "`project_settings set` rewrites `project.godot`, `project_import reimport` and `refresh_classes`",
48208
- "write under `.godot`, and `project_import refresh_uids` writes a `.uid` beside each script that",
48209
- "had none. The reading ops are the ones to gate on, and which is which is in each",
48210
- "op's own summary below rather than assumed from this list.",
48211
- "",
48212
- "Everything else needs the editor (`editor_*`, `debug_*`, `scene_*`, `resource_*`) or a running",
48213
- "game (`runtime_*`).",
48214
- "",
48215
- 'One of these can be pointed the other way: `project_settings get` takes `from: "editor"` to ask',
48216
- "the open editor instead of the file, which is a different reading and costs no engine start. A",
48217
- "gate leaves it alone, because there is no editor on a build machine and the call is refused",
48218
- "rather than answered from the file.",
48219
- ""
48220
- ];
48221
- }
48222
- function renderToolsMarkdown() {
48223
- const lines = [
48224
- "# Tools",
48225
- "",
48226
- `${TOOL_SPECS.length} tools, named \`domain_verb\`. A tool that does several related things takes an`,
48227
- "`op`. An unknown op or argument is refused with the valid set listed. Generated from the server.",
48228
- "",
48229
- `${projectPathSentence()}: the \`runtime_*\` and \`debug_*\` ones pick between running games`,
48230
- "instead, and the others answer about the editor on this server. The pick, when a call names",
48231
- `neither \`projectPath\` nor \`pid\`: ${theGamePicked()}. An argument a tool does not`,
48232
- "declare is refused rather than ignored. Answers are read from the engine after the change, not",
48233
- "echoed from the request. Engine stderr comes back under `engine_messages`.",
48234
- "",
48235
- ...headlessSection()
48236
- ];
48237
- for (const tool of TOOL_SPECS) {
48238
- lines.push(`## ${tool.name}`, "", tool.description, "");
48239
- const operations = Object.entries(tool.operations ?? {});
48240
- if (operations.length > 0) {
48241
- for (const [op, spec] of operations) {
48242
- const needs = [...tool.requires, ...spec.requires];
48243
- const isDefault = tool.defaultOperation === op ? " (default)" : "";
48244
- const wants = needs.length > 0 ? ` Needs: ${needs.map((name) => `\`${name}\``).join(", ")}.` : "";
48245
- const spare = argumentsOf(tool, op).filter((name) => !needs.includes(name) && name !== "projectPath");
48246
- const takes = spare.length > 0 ? ` Takes: ${spare.map((name) => `\`${name}\``).join(", ")}.` : "";
48247
- lines.push(`- \`op: ${op}\`${isDefault}: ${spec.summary}.${wants}${takes}`);
48248
- }
48249
- lines.push("");
48250
- } else if (tool.requires.length > 0) {
48251
- lines.push(`Needs: ${tool.requires.map((name) => `\`${name}\``).join(", ")}.`, "");
48252
- }
48253
- const parameters = Object.entries(tool.parameters).filter(([name]) => name !== "projectPath");
48254
- if (parameters.length > 0) {
48255
- lines.push("Arguments:", "");
48256
- for (const [name, schema] of parameters) {
48257
- const type = namedType(schema["type"]);
48258
- const note = typeof schema["description"] === "string" ? `: ${schema["description"]}` : "";
48259
- lines.push(`- \`${name}\` (${type})${note}`);
48260
- }
48261
- lines.push("");
48262
- }
48263
- }
48264
- return lines.join(`
48265
- `);
48266
- }
48267
-
48268
- // src/skill.ts
48269
48284
  var SKILL_NAME = "gdharness";
48270
48285
  var SHARED_SKILLS = join11(".agents", "skills");
48271
48286
  function skillMarkdown(version) {
package/build/index.js CHANGED
@@ -34268,6 +34268,23 @@ function buildToolDefinitions() {
34268
34268
  });
34269
34269
  }
34270
34270
 
34271
+ // src/tool-reference.ts
34272
+ function namedType(declared) {
34273
+ if (typeof declared === "string") {
34274
+ return declared;
34275
+ }
34276
+ if (Array.isArray(declared) && declared.every((kind) => typeof kind === "string")) {
34277
+ return declared.join(" or ");
34278
+ }
34279
+ return "any";
34280
+ }
34281
+ function argumentShape(schema) {
34282
+ const named = namedType(schema["type"]);
34283
+ const items = schema["items"];
34284
+ const held = typeof items === "object" && items !== null && !Array.isArray(items) ? namedType(items["type"]) : "any";
34285
+ return named.split(" or ").map((kind) => kind === "array" && held !== "any" ? `array of ${held}s` : kind).join(" or ");
34286
+ }
34287
+
34271
34288
  // src/server.ts
34272
34289
  var UPDATE_NOTICE_EVERY = 500;
34273
34290
  var FEEDBACK_NOTICE_EVERY = 250;
@@ -35514,7 +35531,14 @@ class GodotServer {
35514
35531
  const asked = readString(args, "op");
35515
35532
  const named = asked !== undefined && spec.operations?.[asked] !== undefined ? asked : null;
35516
35533
  const wanted = named === null ? [...known] : [...argumentsOf(spec, named), "op"];
35517
- const takes = wanted.length === 0 ? "It takes no arguments." : `It takes: ${wanted.join(", ")}.`;
35534
+ const shaped = wanted.map((name) => {
35535
+ const schema = spec.parameters[name];
35536
+ if (schema !== undefined) {
35537
+ return `${name} (${argumentShape(schema)})`;
35538
+ }
35539
+ return name === "op" && named === null && spec.operations !== undefined ? `op (${Object.keys(spec.operations).join(", ")})` : name;
35540
+ });
35541
+ const takes = wanted.length === 0 ? "It takes no arguments." : `It takes: ${shaped.join(", ")}.`;
35518
35542
  const where = named === null ? spec.name : `${spec.name} ${named}`;
35519
35543
  return {
35520
35544
  ok: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "1.1.48",
3
+ "version": "1.1.49",
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",