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 +110 -95
- package/build/index.js +25 -1
- package/package.json +1 -1
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
|
|
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
|
|
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.
|
|
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",
|