gdharness 0.5.2 → 0.5.4

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.
Files changed (3) hide show
  1. package/build/cli.js +228 -193
  2. package/build/index.js +53 -34
  3. package/package.json +1 -1
package/build/cli.js CHANGED
@@ -7139,14 +7139,32 @@ var init_errors = __esm(() => {
7139
7139
  });
7140
7140
 
7141
7141
  // src/runner.ts
7142
+ import { existsSync as existsSync3 } from "node:fs";
7143
+ import { dirname, join as join2 } from "node:path";
7144
+ import process2 from "node:process";
7142
7145
  function currentRunner() {
7143
- const versions = process.versions;
7146
+ const versions = process2.versions;
7144
7147
  return versions["bun"] === undefined ? "npx" : "bunx";
7145
7148
  }
7146
7149
  function runLine(runner, version, rest = "") {
7147
7150
  const flag = runner === "npx" ? "-y " : "";
7148
7151
  return `${runner} ${flag}gdharness@${version}${rest === "" ? "" : ` ${rest}`}`;
7149
7152
  }
7153
+ function spawnFor(runner, version, execPath = process2.execPath) {
7154
+ const spec = `gdharness@${version}`;
7155
+ if (runner !== currentRunner()) {
7156
+ return { command: runner, args: runner === "npx" ? ["-y", spec] : [spec] };
7157
+ }
7158
+ if (runner === "bunx") {
7159
+ return { command: execPath, args: ["x", spec] };
7160
+ }
7161
+ return { command: alongside(execPath, "npx") ?? "npx", args: ["-y", spec] };
7162
+ }
7163
+ function alongside(execPath, name) {
7164
+ const runner = join2(dirname(execPath), process2.platform === "win32" ? `${name}.cmd` : name);
7165
+ return existsSync3(runner) ? runner : undefined;
7166
+ }
7167
+ var init_runner = () => {};
7150
7168
 
7151
7169
  // src/dictionary.ts
7152
7170
  function emptyRecord() {
@@ -7242,9 +7260,9 @@ var init_game_log = __esm(() => {
7242
7260
  });
7243
7261
 
7244
7262
  // src/update-check.ts
7245
- import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "node:fs";
7263
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "node:fs";
7246
7264
  import { homedir as homedir3, tmpdir as tmpdir2 } from "node:os";
7247
- import { join as join4 } from "node:path";
7265
+ import { join as join5 } from "node:path";
7248
7266
  function cacheDirectory(environment) {
7249
7267
  const set = (name) => {
7250
7268
  const value = environment[name];
@@ -7252,25 +7270,25 @@ function cacheDirectory(environment) {
7252
7270
  };
7253
7271
  const home = set("HOME") ?? homedir3();
7254
7272
  if (process.platform === "win32") {
7255
- return join4(set("LOCALAPPDATA") ?? home, "gdharness");
7273
+ return join5(set("LOCALAPPDATA") ?? home, "gdharness");
7256
7274
  }
7257
7275
  if (process.platform === "darwin") {
7258
- return join4(home, "Library", "Caches", "gdharness");
7276
+ return join5(home, "Library", "Caches", "gdharness");
7259
7277
  }
7260
- return join4(set("XDG_CACHE_HOME") ?? join4(home, ".cache"), "gdharness");
7278
+ return join5(set("XDG_CACHE_HOME") ?? join5(home, ".cache"), "gdharness");
7261
7279
  }
7262
7280
  function cacheFile(environment = process.env) {
7263
7281
  try {
7264
7282
  const directory = cacheDirectory(environment);
7265
7283
  mkdirSync2(directory, { recursive: true, mode: 448 });
7266
- return join4(directory, "update-check.json");
7284
+ return join5(directory, "update-check.json");
7267
7285
  } catch {
7268
- return join4(tmpdir2(), "gdharness-update-check.json");
7286
+ return join5(tmpdir2(), "gdharness-update-check.json");
7269
7287
  }
7270
7288
  }
7271
7289
  function readCache(path) {
7272
7290
  try {
7273
- if (!existsSync4(path)) {
7291
+ if (!existsSync5(path)) {
7274
7292
  return null;
7275
7293
  }
7276
7294
  const parsed = JSON.parse(readFileSync2(path, "utf8"));
@@ -7403,6 +7421,7 @@ class UpdateCheck {
7403
7421
  }
7404
7422
  var REGISTRY = "https://registry.npmjs.org/gdharness/latest", RELEASES = "https://github.com/Aureliolo/gdharness/releases/tag", CACHE_MS, REQUEST_TIMEOUT_MS = 1e4, MAX_BODY_BYTES, FIRST_RETRY_MS = 30000, MAX_RETRY_MS, VERSION;
7405
7423
  var init_update_check = __esm(() => {
7424
+ init_runner();
7406
7425
  CACHE_MS = 4 * 60 * 60 * 1000;
7407
7426
  MAX_BODY_BYTES = 1 << 20;
7408
7427
  MAX_RETRY_MS = 60 * 60 * 1000;
@@ -7439,8 +7458,8 @@ var init_server_version = __esm(() => {
7439
7458
  });
7440
7459
 
7441
7460
  // src/class-cache.ts
7442
- import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync5 } from "node:fs";
7443
- import { join as join5 } from "node:path";
7461
+ import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync5 } from "node:fs";
7462
+ import { join as join6 } from "node:path";
7444
7463
  function declaredClasses(projectPath) {
7445
7464
  const declared = new Map;
7446
7465
  const visit = (directory, prefix) => {
@@ -7448,7 +7467,7 @@ function declaredClasses(projectPath) {
7448
7467
  if (entry.name.startsWith(".")) {
7449
7468
  continue;
7450
7469
  }
7451
- const path = join5(directory, entry.name);
7470
+ const path = join6(directory, entry.name);
7452
7471
  if (entry.isDirectory()) {
7453
7472
  visit(path, `${prefix}${entry.name}/`);
7454
7473
  } else if (entry.isFile() && entry.name.endsWith(".gd")) {
@@ -7463,8 +7482,8 @@ function declaredClasses(projectPath) {
7463
7482
  return declared;
7464
7483
  }
7465
7484
  function cachedClasses(projectPath) {
7466
- const cache = join5(projectPath, ".godot", "global_script_class_cache.cfg");
7467
- if (!existsSync5(cache)) {
7485
+ const cache = join6(projectPath, ".godot", "global_script_class_cache.cfg");
7486
+ if (!existsSync6(cache)) {
7468
7487
  return null;
7469
7488
  }
7470
7489
  const listed = new Map;
@@ -7487,7 +7506,7 @@ var init_class_cache = () => {};
7487
7506
  import { execFile as execFile3 } from "node:child_process";
7488
7507
  import { mkdtempSync as mkdtempSync2, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "node:fs";
7489
7508
  import { tmpdir as tmpdir3 } from "node:os";
7490
- import { join as join6 } from "node:path";
7509
+ import { join as join7 } from "node:path";
7491
7510
  import { promisify as promisify3 } from "node:util";
7492
7511
  function snakeCased2(params) {
7493
7512
  const result = emptyRecord();
@@ -7527,8 +7546,8 @@ function reason2(stdout, stderr) {
7527
7546
  return stdout.trim().split(/\r?\n/).at(-1) ?? "no output at all";
7528
7547
  }
7529
7548
  async function runOperation2(engine, operation, params, projectPath) {
7530
- const paramsDir = mkdtempSync2(join6(tmpdir3(), "gdharness-params-"));
7531
- const paramsFile = join6(paramsDir, `${operation}.json`);
7549
+ const paramsDir = mkdtempSync2(join7(tmpdir3(), "gdharness-params-"));
7550
+ const paramsFile = join7(paramsDir, `${operation}.json`);
7532
7551
  writeFileSync4(paramsFile, JSON.stringify(snakeCased2(params)), "utf8");
7533
7552
  const args = [
7534
7553
  "--headless",
@@ -30552,10 +30571,10 @@ var init_stdio = __esm(() => {
30552
30571
  });
30553
30572
 
30554
30573
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
30555
- import process5 from "node:process";
30574
+ import process6 from "node:process";
30556
30575
 
30557
30576
  class StdioServerTransport {
30558
- constructor(_stdin = process5.stdin, _stdout = process5.stdout, options) {
30577
+ constructor(_stdin = process6.stdin, _stdout = process6.stdout, options) {
30559
30578
  this._stdin = _stdin;
30560
30579
  this._stdout = _stdout;
30561
30580
  this._started = false;
@@ -34491,7 +34510,7 @@ var init_godot_bridge = __esm(() => {
34491
34510
 
34492
34511
  // src/godot-path.ts
34493
34512
  import { execFile as execFile4 } from "node:child_process";
34494
- import { existsSync as existsSync8 } from "node:fs";
34513
+ import { existsSync as existsSync9 } from "node:fs";
34495
34514
  import { normalize as normalize2 } from "node:path";
34496
34515
  import { promisify as promisify4 } from "node:util";
34497
34516
  var run4, GodotLocator2;
@@ -34533,7 +34552,7 @@ var init_godot_path = __esm(() => {
34533
34552
  return known;
34534
34553
  }
34535
34554
  let ok = false;
34536
- if (path === "godot" || existsSync8(path)) {
34555
+ if (path === "godot" || existsSync9(path)) {
34537
34556
  try {
34538
34557
  await run4(path, ["--version"]);
34539
34558
  ok = true;
@@ -34549,7 +34568,7 @@ var init_godot_path = __esm(() => {
34549
34568
 
34550
34569
  // src/issues.ts
34551
34570
  import { createHash as createHash2 } from "node:crypto";
34552
- import process6 from "node:process";
34571
+ import process7 from "node:process";
34553
34572
  function feedbackNotice() {
34554
34573
  return JSON.stringify({
34555
34574
  gdharness_feedback: {
@@ -34559,9 +34578,9 @@ function feedbackNotice() {
34559
34578
  }, null, 2);
34560
34579
  }
34561
34580
  function runtime2() {
34562
- const versions = process6.versions;
34581
+ const versions = process7.versions;
34563
34582
  const bun = versions["bun"];
34564
- return bun === undefined ? `node ${process6.versions.node}` : `bun ${bun}`;
34583
+ return bun === undefined ? `node ${process7.versions.node}` : `bun ${bun}`;
34565
34584
  }
34566
34585
  function defectSignature2(where, message) {
34567
34586
  const normalised = `${where}
@@ -34573,7 +34592,7 @@ function facts2(where, message, godotVersion) {
34573
34592
  ["Where", where],
34574
34593
  ["Error", message],
34575
34594
  ["Version", `gdharness ${SERVER_VERSION}`],
34576
- ["Runtime", `${runtime2()}, ${process6.platform} ${process6.arch}`],
34595
+ ["Runtime", `${runtime2()}, ${process7.platform} ${process7.arch}`],
34577
34596
  ["Godot", godotVersion ?? "not known at the point this failed"],
34578
34597
  ["Signature", defectSignature2(where, message)]
34579
34598
  ];
@@ -34583,7 +34602,7 @@ function filledTemplate2(where, message, godotVersion) {
34583
34602
  `**gdharness version**: ${SERVER_VERSION}`,
34584
34603
  `**Godot version**: ${godotVersion ?? ""}`,
34585
34604
  `**Bun version**: ${runtime2()}`,
34586
- `**OS**: ${process6.platform} ${process6.arch}`,
34605
+ `**OS**: ${process7.platform} ${process7.arch}`,
34587
34606
  "**MCP client**:",
34588
34607
  "",
34589
34608
  "## What you did",
@@ -34836,7 +34855,7 @@ var init_junit = __esm(() => {
34836
34855
  import { realpathSync } from "node:fs";
34837
34856
  import { readFile, realpath } from "node:fs/promises";
34838
34857
  import { createConnection as createConnection2 } from "node:net";
34839
- import { dirname as dirname5, resolve as resolve3 } from "node:path";
34858
+ import { dirname as dirname6, resolve as resolve3 } from "node:path";
34840
34859
  import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
34841
34860
  function diagnosticsKey(uri) {
34842
34861
  let decoded;
@@ -34962,7 +34981,7 @@ class GodotLSPClient {
34962
34981
  if (this.initialized) {
34963
34982
  return;
34964
34983
  }
34965
- const rootPath = this.rootPath ?? dirname5(resolve3(filePath));
34984
+ const rootPath = this.rootPath ?? dirname6(resolve3(filePath));
34966
34985
  await this.initialize(rootPath);
34967
34986
  }
34968
34987
  async sendRequest(method, params) {
@@ -35337,7 +35356,7 @@ var init_lsp_client = __esm(() => {
35337
35356
 
35338
35357
  // src/project-scan.ts
35339
35358
  import { readdirSync as readdirSync4, readFileSync as readFileSync9 } from "node:fs";
35340
- import { join as join10 } from "node:path";
35359
+ import { join as join11 } from "node:path";
35341
35360
  function projectStructure(projectPath) {
35342
35361
  const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
35343
35362
  const visit = (directory) => {
@@ -35346,7 +35365,7 @@ function projectStructure(projectPath) {
35346
35365
  continue;
35347
35366
  }
35348
35367
  if (entry.isDirectory()) {
35349
- visit(join10(directory, entry.name));
35368
+ visit(join11(directory, entry.name));
35350
35369
  } else if (entry.isFile()) {
35351
35370
  const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
35352
35371
  if (extension === "tscn") {
@@ -35388,7 +35407,7 @@ function searchProject(projectPath, options) {
35388
35407
  if (SKIPPED.has(entry.name)) {
35389
35408
  continue;
35390
35409
  }
35391
- const entryPath = join10(directory, entry.name);
35410
+ const entryPath = join11(directory, entry.name);
35392
35411
  if (entry.isDirectory()) {
35393
35412
  visit(entryPath);
35394
35413
  continue;
@@ -35427,23 +35446,23 @@ var init_project_scan = __esm(() => {
35427
35446
  });
35428
35447
 
35429
35448
  // src/runtime-client.ts
35430
- import { existsSync as existsSync9, readdirSync as readdirSync5, readFileSync as readFileSync10, unlinkSync } from "node:fs";
35449
+ import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync10, unlinkSync } from "node:fs";
35431
35450
  import { createConnection as createConnection3 } from "node:net";
35432
35451
  import { tmpdir as tmpdir4 } from "node:os";
35433
- import { join as join11, resolve as resolve4 } from "node:path";
35452
+ import { join as join12, resolve as resolve4 } from "node:path";
35434
35453
  function runtimeDirectory(variables = process.env) {
35435
35454
  const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
35436
35455
  if (explicit) {
35437
35456
  return explicit;
35438
35457
  }
35439
35458
  const perUser = envValue("XDG_RUNTIME_DIR", variables);
35440
- return join11(perUser ?? tmpdir4(), "gdharness");
35459
+ return join12(perUser ?? tmpdir4(), "gdharness");
35441
35460
  }
35442
35461
  function runtimeDirectories(variables = process.env) {
35443
35462
  const candidates = [runtimeDirectory(variables)];
35444
35463
  const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
35445
35464
  for (const base of fallbacks) {
35446
- candidates.push(join11(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
35465
+ candidates.push(join12(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
35447
35466
  }
35448
35467
  return [...new Set(candidates.map((path) => resolve4(path)))];
35449
35468
  }
@@ -35460,35 +35479,44 @@ function parseAnnouncement(file, pid) {
35460
35479
  try {
35461
35480
  fields = asParams(JSON.parse(readFileSync10(file, "utf8")));
35462
35481
  } catch {
35463
- return null;
35482
+ return { kind: "rubbish" };
35464
35483
  }
35465
35484
  const project = readParams(fields, "project");
35466
35485
  const port = readNumber(fields, "port");
35467
35486
  const address = readString(fields, "address");
35468
- if (readNumber(fields, "protocol") !== RUNTIME_PROTOCOL || readNumber(fields, "pid") !== pid || port === undefined || !Number.isInteger(port) || port < 1 || port > 65535 || address === undefined || project === undefined) {
35469
- return null;
35487
+ const protocol = readNumber(fields, "protocol");
35488
+ if (readNumber(fields, "pid") !== pid || port === undefined || !Number.isInteger(port) || port < 1 || port > 65535 || address === undefined || project === undefined) {
35489
+ return { kind: "rubbish" };
35470
35490
  }
35471
- return {
35472
- pid,
35473
- port,
35474
- address,
35475
- project: { name: readString(project, "name") ?? "", path: readString(project, "path") ?? "" },
35476
- file
35491
+ const named = {
35492
+ name: readString(project, "name") ?? "",
35493
+ path: readString(project, "path") ?? ""
35477
35494
  };
35495
+ if (protocol !== RUNTIME_PROTOCOL) {
35496
+ return { kind: "unspoken", unspoken: { pid, protocol: protocol ?? 0, project: named } };
35497
+ }
35498
+ return { kind: "runtime", endpoint: { pid, port, address, project: named, file } };
35478
35499
  }
35479
- function discoverRuntimes(directories = runtimeDirectories()) {
35480
- const found = new Map;
35500
+ function runtimesAnnounced(directories = runtimeDirectories()) {
35501
+ const running = new Map;
35502
+ const unspoken = new Map;
35481
35503
  for (const directory of directories) {
35482
- for (const endpoint of announcedIn(directory)) {
35483
- if (!found.has(endpoint.pid)) {
35484
- found.set(endpoint.pid, endpoint);
35504
+ for (const found of announcedIn(directory)) {
35505
+ if (found.kind === "runtime" && !running.has(found.endpoint.pid)) {
35506
+ running.set(found.endpoint.pid, found.endpoint);
35507
+ } else if (found.kind === "unspoken" && !unspoken.has(found.unspoken.pid)) {
35508
+ unspoken.set(found.unspoken.pid, found.unspoken);
35485
35509
  }
35486
35510
  }
35487
35511
  }
35488
- return [...found.values()].sort((a, b) => b.pid - a.pid);
35512
+ const newest = (a, b) => b.pid - a.pid;
35513
+ return { running: [...running.values()].sort(newest), unspoken: [...unspoken.values()].sort(newest) };
35514
+ }
35515
+ function discoverRuntimes(directories = runtimeDirectories()) {
35516
+ return runtimesAnnounced(directories).running;
35489
35517
  }
35490
35518
  function announcedIn(directory) {
35491
- if (!existsSync9(directory)) {
35519
+ if (!existsSync10(directory)) {
35492
35520
  return [];
35493
35521
  }
35494
35522
  const found = [];
@@ -35497,24 +35525,32 @@ function announcedIn(directory) {
35497
35525
  if (!match) {
35498
35526
  continue;
35499
35527
  }
35500
- const file = join11(directory, entry);
35528
+ const file = join12(directory, entry);
35501
35529
  const pid = Number.parseInt(match[1] ?? "", 10);
35502
- const endpoint = processAlive(pid) ? parseAnnouncement(file, pid) : null;
35503
- if (endpoint) {
35504
- found.push(endpoint);
35505
- } else {
35530
+ const announced = processAlive(pid) ? parseAnnouncement(file, pid) : { kind: "rubbish" };
35531
+ if (announced.kind === "rubbish") {
35506
35532
  try {
35507
35533
  unlinkSync(file);
35508
35534
  } catch {}
35535
+ continue;
35509
35536
  }
35537
+ found.push(announced);
35510
35538
  }
35511
35539
  return found;
35512
35540
  }
35513
35541
  function describe2(endpoint) {
35514
35542
  return `pid ${endpoint.pid} on ${endpoint.address}:${endpoint.port} (${endpoint.project.name || "unnamed"} at ${endpoint.project.path})`;
35515
35543
  }
35516
- function chooseRuntime(endpoints, projectPath) {
35544
+ function tooNew(unspoken) {
35545
+ const named = unspoken.map((game) => `pid ${game.pid} speaks ${game.protocol} (${game.project.name || "unnamed"} at ${game.project.path})`).join("; ");
35546
+ const half = unspoken.some((game) => game.protocol > RUNTIME_PROTOCOL) ? "this server is the older half: reconnect it so it spawns the installed version" : "the addon is the older half: reinstall it and restart the game";
35547
+ return `A game is running, in a protocol this server does not speak ${named}. This server speaks ${RUNTIME_PROTOCOL}, so ${half}.`;
35548
+ }
35549
+ function chooseRuntime(endpoints, projectPath, unspoken = []) {
35517
35550
  if (endpoints.length === 0) {
35551
+ if (unspoken.length > 0) {
35552
+ return { problem: tooNew(unspoken) };
35553
+ }
35518
35554
  return {
35519
35555
  problem: "No game with the runtime addon is running. Start one with editor_run, or play the project from the editor with the addon enabled."
35520
35556
  };
@@ -35635,9 +35671,9 @@ var init_runtime_client = __esm(() => {
35635
35671
 
35636
35672
  // src/server.ts
35637
35673
  import { execFile as execFile5, spawn } from "node:child_process";
35638
- import { existsSync as existsSync10, mkdtempSync as mkdtempSync3, readdirSync as readdirSync6, readFileSync as readFileSync11, realpathSync as realpathSync2, rmSync as rmSync6 } from "node:fs";
35674
+ import { existsSync as existsSync11, mkdtempSync as mkdtempSync3, readdirSync as readdirSync6, readFileSync as readFileSync11, realpathSync as realpathSync2, rmSync as rmSync6 } from "node:fs";
35639
35675
  import { tmpdir as tmpdir5 } from "node:os";
35640
- import { basename as basename2, dirname as dirname6, join as join12, normalize as normalize3 } from "node:path";
35676
+ import { basename as basename2, dirname as dirname7, join as join13, normalize as normalize3 } from "node:path";
35641
35677
  import { setTimeout as delay2 } from "node:timers/promises";
35642
35678
  import { fileURLToPath as fileURLToPath4 } from "node:url";
35643
35679
  import { promisify as promisify5 } from "node:util";
@@ -35683,7 +35719,7 @@ function camelCased(params) {
35683
35719
  class GodotServer {
35684
35720
  mcp;
35685
35721
  locator = new GodotLocator2;
35686
- operationsScript = join12(__dirname2, "godot", "operations", "godot_operations.gd");
35722
+ operationsScript = join13(__dirname2, "godot", "operations", "godot_operations.gd");
35687
35723
  godotBridge;
35688
35724
  tools = buildToolDefinitions();
35689
35725
  activeProcess = null;
@@ -36137,8 +36173,8 @@ class GodotServer {
36137
36173
  if (path === undefined) {
36138
36174
  return { ok: false, response: this.createErrorResponse("projectPath is required.") };
36139
36175
  }
36140
- const file = join12(path, "project.godot");
36141
- if (!existsSync10(file)) {
36176
+ const file = join13(path, "project.godot");
36177
+ if (!existsSync11(file)) {
36142
36178
  return {
36143
36179
  ok: false,
36144
36180
  response: this.createErrorResponse(`Not a Godot project: ${path}`, [
@@ -36372,7 +36408,7 @@ class GodotServer {
36372
36408
  log.finish();
36373
36409
  const problems = log.select({ severity: "warning", sinceLastCall: false, limit: 200 });
36374
36410
  const verdict = {
36375
- exported: exitCode === 0 && log.count("error") === 0 && existsSync10(output.absolutePath),
36411
+ exported: exitCode === 0 && log.count("error") === 0 && existsSync11(output.absolutePath),
36376
36412
  preset,
36377
36413
  outputPath: output.relativePath,
36378
36414
  debug,
@@ -36402,7 +36438,7 @@ class GodotServer {
36402
36438
  return contained.response;
36403
36439
  }
36404
36440
  const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
36405
- if (!existsSync10(join12(project.value.path, runner))) {
36441
+ if (!existsSync11(join13(project.value.path, runner))) {
36406
36442
  return this.createErrorResponse(`gdUnit4 is not installed in this project: no ${runner}.`, [
36407
36443
  "Install gdUnit4 under addons/gdUnit4, from https://github.com/godot-gdunit-labs/gdUnit4"
36408
36444
  ]);
@@ -36435,7 +36471,7 @@ class GodotServer {
36435
36471
  ];
36436
36472
  const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 600000;
36437
36473
  this.logDebug(`Running tests: ${engine.value} ${cmdArgs.join(" ")}`);
36438
- const userData = mkdtempSync3(join12(tmpdir5(), "gdharness-tests-"));
36474
+ const userData = mkdtempSync3(join13(tmpdir5(), "gdharness-tests-"));
36439
36475
  const run = this.spawnGame(engine.value, cmdArgs, userDataIn(userData));
36440
36476
  const hung = await new Promise((resolve) => {
36441
36477
  const timer = setTimeout(() => {
@@ -36451,14 +36487,14 @@ class GodotServer {
36451
36487
  resolve(false);
36452
36488
  });
36453
36489
  });
36454
- const reportsDir = join12(project.value.path, ".godot", "gdharness-reports");
36490
+ const reportsDir = join13(project.value.path, ".godot", "gdharness-reports");
36455
36491
  let report = null;
36456
36492
  let reportProblem = null;
36457
36493
  try {
36458
- const written = existsSync10(reportsDir) ? readdirSync6(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
36494
+ const written = existsSync11(reportsDir) ? readdirSync6(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
36459
36495
  const newest = written.at(-1);
36460
36496
  if (newest !== undefined) {
36461
- report = parseJUnit(readFileSync11(join12(reportsDir, newest, "results.xml"), "utf8"));
36497
+ report = parseJUnit(readFileSync11(join13(reportsDir, newest, "results.xml"), "utf8"));
36462
36498
  }
36463
36499
  } catch (error) {
36464
36500
  reportProblem = errorMessage(error);
@@ -36965,13 +37001,14 @@ class GodotServer {
36965
37001
  }
36966
37002
  async handleRuntimeCommand(command, args, timeoutMs = this.runtimeTimeoutMs()) {
36967
37003
  const { op: _op, projectPath, ...params } = asParams(args);
36968
- const choice = chooseRuntime(discoverRuntimes(), typeof projectPath === "string" ? projectPath : undefined);
37004
+ const announced = runtimesAnnounced();
37005
+ const choice = chooseRuntime(announced.running, typeof projectPath === "string" ? projectPath : undefined, announced.unspoken);
36969
37006
  if ("problem" in choice) {
36970
37007
  return this.createErrorResponse(choice.problem);
36971
37008
  }
36972
37009
  const expectsScreenshot = command === "capture_screenshot" || command === "capture_viewport";
36973
- const screenshotDir = expectsScreenshot ? mkdtempSync3(join12(tmpdir5(), "gdharness-runtime-screenshot-")) : null;
36974
- const screenshotPath = screenshotDir ? join12(screenshotDir, "capture.png") : null;
37010
+ const screenshotDir = expectsScreenshot ? mkdtempSync3(join13(tmpdir5(), "gdharness-runtime-screenshot-")) : null;
37011
+ const screenshotPath = screenshotDir ? join13(screenshotDir, "capture.png") : null;
36975
37012
  try {
36976
37013
  const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs);
36977
37014
  if (!reply.ok) {
@@ -37077,7 +37114,7 @@ var init_server3 = __esm(() => {
37077
37114
  init_tool_definitions();
37078
37115
  init_update_check();
37079
37116
  run5 = promisify5(execFile5);
37080
- __dirname2 = dirname6(fileURLToPath4(import.meta.url));
37117
+ __dirname2 = dirname7(fileURLToPath4(import.meta.url));
37081
37118
  PATH_SOLUTIONS = [
37082
37119
  'Give the path relative to the project, such as "scenes/main.tscn" or "res://scenes/main.tscn"',
37083
37120
  "Point projectPath at the project the file belongs to"
@@ -37134,8 +37171,8 @@ var init_server3 = __esm(() => {
37134
37171
  });
37135
37172
 
37136
37173
  // src/cli.ts
37137
- import { existsSync as existsSync11 } from "fs";
37138
- import { join as join13, resolve as resolve5 } from "path";
37174
+ import { existsSync as existsSync12 } from "fs";
37175
+ import { join as join14, resolve as resolve5 } from "path";
37139
37176
 
37140
37177
  // src/errors.ts
37141
37178
  class Refusal extends Error {
@@ -37198,10 +37235,10 @@ class GodotLocator {
37198
37235
  }
37199
37236
 
37200
37237
  // src/harnesses.ts
37201
- import { existsSync as existsSync3, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
37238
+ import { existsSync as existsSync4, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
37202
37239
  import { homedir as homedir2 } from "node:os";
37203
- import { dirname, join as join2 } from "node:path";
37204
- import process2 from "node:process";
37240
+ import { dirname as dirname2, join as join3 } from "node:path";
37241
+ import process3 from "node:process";
37205
37242
 
37206
37243
  // node_modules/yaml/dist/index.js
37207
37244
  var composer = require_composer();
@@ -37343,13 +37380,11 @@ function isEmptyCollection(node) {
37343
37380
 
37344
37381
  // src/harnesses.ts
37345
37382
  init_errors();
37383
+ init_runner();
37346
37384
  var SERVER_KEY = "gdharness";
37347
37385
  function launchFor(version, godotPath, runner = currentRunner()) {
37348
- return {
37349
- command: runner,
37350
- args: runner === "npx" ? ["-y", `gdharness@${version}`] : [`gdharness@${version}`],
37351
- env: { GODOT_PATH: godotPath }
37352
- };
37386
+ const spawn = spawnFor(runner, version);
37387
+ return { command: spawn.command, args: spawn.args, env: { GODOT_PATH: godotPath } };
37353
37388
  }
37354
37389
  function tomlArray(values) {
37355
37390
  return `[${values.map((value) => `"${value}"`).join(", ")}]`;
@@ -37371,7 +37406,7 @@ var HARNESSES = [
37371
37406
  shape: "plain",
37372
37407
  home: ".claude",
37373
37408
  marker: ".claude",
37374
- skills: { dir: join2(".claude", "skills"), shared: false }
37409
+ skills: { dir: join3(".claude", "skills"), shared: false }
37375
37410
  },
37376
37411
  {
37377
37412
  id: "copilot-cli",
@@ -37381,7 +37416,7 @@ var HARNESSES = [
37381
37416
  container: "mcpServers",
37382
37417
  shape: "plain",
37383
37418
  home: ".copilot",
37384
- skills: { dir: join2(".github", "skills"), shared: true },
37419
+ skills: { dir: join3(".github", "skills"), shared: true },
37385
37420
  manual: "Trust the folder when Copilot CLI first asks, or it reads no config here."
37386
37421
  },
37387
37422
  {
@@ -37407,22 +37442,22 @@ var HARNESSES = [
37407
37442
  id: "cursor",
37408
37443
  name: "Cursor",
37409
37444
  scope: "project",
37410
- file: join2(".cursor", "mcp.json"),
37445
+ file: join3(".cursor", "mcp.json"),
37411
37446
  container: "mcpServers",
37412
37447
  shape: "plain",
37413
37448
  home: ".cursor",
37414
- skills: { dir: join2(".cursor", "skills"), shared: true },
37449
+ skills: { dir: join3(".cursor", "skills"), shared: true },
37415
37450
  manual: "Cursor asks before each tool call by default, and the server can be toggled off."
37416
37451
  },
37417
37452
  {
37418
37453
  id: "vscode",
37419
37454
  name: "VS Code",
37420
37455
  scope: "project",
37421
- file: join2(".vscode", "mcp.json"),
37456
+ file: join3(".vscode", "mcp.json"),
37422
37457
  container: "servers",
37423
37458
  shape: "typed",
37424
37459
  home: ".vscode",
37425
- skills: { dir: join2(".github", "skills"), shared: true },
37460
+ skills: { dir: join3(".github", "skills"), shared: true },
37426
37461
  manual: "VS Code asks you to trust a server the first time it starts one."
37427
37462
  },
37428
37463
  {
@@ -37432,45 +37467,45 @@ var HARNESSES = [
37432
37467
  file: "opencode.json",
37433
37468
  container: "mcp",
37434
37469
  shape: "opencode",
37435
- home: join2(".config", "opencode"),
37470
+ home: join3(".config", "opencode"),
37436
37471
  marker: ".opencode",
37437
- skills: { dir: join2(".opencode", "skills"), shared: true }
37472
+ skills: { dir: join3(".opencode", "skills"), shared: true }
37438
37473
  },
37439
37474
  {
37440
37475
  id: "junie",
37441
37476
  name: "Junie",
37442
37477
  scope: "project",
37443
- file: join2(".junie", "mcp", "mcp.json"),
37478
+ file: join3(".junie", "mcp", "mcp.json"),
37444
37479
  container: "mcpServers",
37445
37480
  shape: "plain",
37446
37481
  home: ".junie",
37447
- skills: { dir: join2(".junie", "skills"), shared: true }
37482
+ skills: { dir: join3(".junie", "skills"), shared: true }
37448
37483
  },
37449
37484
  {
37450
37485
  id: "kiro",
37451
37486
  name: "Kiro",
37452
37487
  scope: "project",
37453
- file: join2(".kiro", "settings", "mcp.json"),
37488
+ file: join3(".kiro", "settings", "mcp.json"),
37454
37489
  container: "mcpServers",
37455
37490
  shape: "plain",
37456
37491
  home: ".kiro",
37457
- skills: { dir: join2(".kiro", "skills"), shared: false }
37492
+ skills: { dir: join3(".kiro", "skills"), shared: false }
37458
37493
  },
37459
37494
  {
37460
37495
  id: "gemini",
37461
37496
  name: "Gemini CLI",
37462
37497
  scope: "project",
37463
- file: join2(".gemini", "settings.json"),
37498
+ file: join3(".gemini", "settings.json"),
37464
37499
  container: "mcpServers",
37465
37500
  shape: "plain",
37466
37501
  home: ".gemini",
37467
- skills: { dir: join2(".gemini", "skills"), shared: true }
37502
+ skills: { dir: join3(".gemini", "skills"), shared: true }
37468
37503
  },
37469
37504
  {
37470
37505
  id: "roo",
37471
37506
  name: "Roo Code",
37472
37507
  scope: "project",
37473
- file: join2(".roo", "mcp.json"),
37508
+ file: join3(".roo", "mcp.json"),
37474
37509
  container: "mcpServers",
37475
37510
  shape: "plain"
37476
37511
  },
@@ -37478,7 +37513,7 @@ var HARNESSES = [
37478
37513
  id: "kilo",
37479
37514
  name: "Kilo Code",
37480
37515
  scope: "project",
37481
- file: join2(".kilocode", "mcp.json"),
37516
+ file: join3(".kilocode", "mcp.json"),
37482
37517
  container: "mcpServers",
37483
37518
  shape: "plain"
37484
37519
  },
@@ -37486,16 +37521,16 @@ var HARNESSES = [
37486
37521
  id: "amazon-q",
37487
37522
  name: "Amazon Q Developer CLI",
37488
37523
  scope: "project",
37489
- file: join2(".amazonq", "mcp.json"),
37524
+ file: join3(".amazonq", "mcp.json"),
37490
37525
  container: "mcpServers",
37491
37526
  shape: "plain",
37492
- home: join2(".aws", "amazonq")
37527
+ home: join3(".aws", "amazonq")
37493
37528
  },
37494
37529
  {
37495
37530
  id: "zed",
37496
37531
  name: "Zed",
37497
37532
  scope: "project",
37498
- file: join2(".zed", "settings.json"),
37533
+ file: join3(".zed", "settings.json"),
37499
37534
  container: "context_servers",
37500
37535
  shape: "plain"
37501
37536
  },
@@ -37503,16 +37538,16 @@ var HARNESSES = [
37503
37538
  id: "amp",
37504
37539
  name: "Amp",
37505
37540
  scope: "project",
37506
- file: join2(".amp", "settings.json"),
37541
+ file: join3(".amp", "settings.json"),
37507
37542
  container: "amp.mcpServers",
37508
37543
  shape: "plain",
37509
- home: join2(".config", "amp")
37544
+ home: join3(".config", "amp")
37510
37545
  },
37511
37546
  {
37512
37547
  id: "warp",
37513
37548
  name: "Warp",
37514
37549
  scope: "project",
37515
- file: join2(".warp", ".mcp.json"),
37550
+ file: join3(".warp", ".mcp.json"),
37516
37551
  container: "mcpServers",
37517
37552
  shape: "plain",
37518
37553
  home: ".warp",
@@ -37522,7 +37557,7 @@ var HARNESSES = [
37522
37557
  id: "trae",
37523
37558
  name: "Trae",
37524
37559
  scope: "project",
37525
- file: join2(".trae", "mcp.json"),
37560
+ file: join3(".trae", "mcp.json"),
37526
37561
  container: "mcpServers",
37527
37562
  shape: "plain",
37528
37563
  home: ".trae"
@@ -37531,7 +37566,7 @@ var HARNESSES = [
37531
37566
  id: "factory",
37532
37567
  name: "Factory Droid",
37533
37568
  scope: "project",
37534
- file: join2(".factory", "mcp.json"),
37569
+ file: join3(".factory", "mcp.json"),
37535
37570
  container: "mcpServers",
37536
37571
  shape: "typed",
37537
37572
  home: ".factory"
@@ -37540,7 +37575,7 @@ var HARNESSES = [
37540
37575
  id: "tabnine",
37541
37576
  name: "Tabnine",
37542
37577
  scope: "project",
37543
- file: join2(".tabnine", "mcp_servers.json"),
37578
+ file: join3(".tabnine", "mcp_servers.json"),
37544
37579
  container: "mcpServers",
37545
37580
  shape: "plain",
37546
37581
  home: ".tabnine"
@@ -37557,7 +37592,7 @@ var HARNESSES = [
37557
37592
  id: "pi",
37558
37593
  name: "pi",
37559
37594
  scope: "project",
37560
- file: join2(".pi", "mcp.json"),
37595
+ file: join3(".pi", "mcp.json"),
37561
37596
  container: "mcpServers",
37562
37597
  shape: "plain",
37563
37598
  home: ".pi"
@@ -37566,7 +37601,7 @@ var HARNESSES = [
37566
37601
  id: "mistral-vibe",
37567
37602
  name: "Mistral Vibe",
37568
37603
  scope: "project",
37569
- file: join2(".vibe", "config.toml"),
37604
+ file: join3(".vibe", "config.toml"),
37570
37605
  container: "mcp_servers",
37571
37606
  shape: "plain",
37572
37607
  toml: {
@@ -37622,7 +37657,7 @@ var HARNESSES = [
37622
37657
  id: "codex",
37623
37658
  name: "Codex CLI",
37624
37659
  scope: "home",
37625
- file: join2(".codex", "config.toml"),
37660
+ file: join3(".codex", "config.toml"),
37626
37661
  container: "mcp_servers",
37627
37662
  shape: "plain",
37628
37663
  manual: "Codex reads its config at startup, so restart it.",
@@ -37641,20 +37676,20 @@ var HARNESSES = [
37641
37676
  id: "cline",
37642
37677
  name: "Cline",
37643
37678
  scope: "home",
37644
- file: join2(".cline", "data", "settings", "cline_mcp_settings.json"),
37679
+ file: join3(".cline", "data", "settings", "cline_mcp_settings.json"),
37645
37680
  container: "mcpServers",
37646
37681
  shape: "plain",
37647
37682
  marker: ".cline",
37648
- skills: { dir: join2(".cline", "skills"), shared: false }
37683
+ skills: { dir: join3(".cline", "skills"), shared: false }
37649
37684
  },
37650
37685
  {
37651
37686
  id: "goose",
37652
37687
  name: "Goose",
37653
37688
  scope: "home",
37654
37689
  file: {
37655
- win32: { appData: join2("Block", "goose", "config", "config.yaml") },
37656
- darwin: join2(".config", "goose", "config.yaml"),
37657
- linux: join2(".config", "goose", "config.yaml")
37690
+ win32: { appData: join3("Block", "goose", "config", "config.yaml") },
37691
+ darwin: join3(".config", "goose", "config.yaml"),
37692
+ linux: join3(".config", "goose", "config.yaml")
37658
37693
  },
37659
37694
  container: "extensions",
37660
37695
  shape: "plain",
@@ -37675,19 +37710,19 @@ var HARNESSES = [
37675
37710
  id: "windsurf",
37676
37711
  name: "Windsurf",
37677
37712
  scope: "home",
37678
- file: join2(".codeium", "windsurf", "mcp_config.json"),
37713
+ file: join3(".codeium", "windsurf", "mcp_config.json"),
37679
37714
  container: "mcpServers",
37680
37715
  shape: "plain",
37681
- skills: { dir: join2(".windsurf", "skills"), shared: true }
37716
+ skills: { dir: join3(".windsurf", "skills"), shared: true }
37682
37717
  },
37683
37718
  {
37684
37719
  id: "hermes",
37685
37720
  name: "Hermes",
37686
37721
  scope: "home",
37687
- file: join2(".hermes", "config.yaml"),
37722
+ file: join3(".hermes", "config.yaml"),
37688
37723
  container: "mcp_servers",
37689
37724
  shape: "plain",
37690
- skills: { dir: join2(".hermes", "skills"), shared: true },
37725
+ skills: { dir: join3(".hermes", "skills"), shared: true },
37691
37726
  yaml: {
37692
37727
  path: ["mcp_servers", SERVER_KEY],
37693
37728
  entry: (launch) => ({
@@ -37702,7 +37737,7 @@ var HARNESSES = [
37702
37737
  id: "openclaw",
37703
37738
  name: "OpenClaw",
37704
37739
  scope: "home",
37705
- file: join2(".openclaw", "openclaw.json"),
37740
+ file: join3(".openclaw", "openclaw.json"),
37706
37741
  container: "mcpServers",
37707
37742
  shape: "plain"
37708
37743
  },
@@ -37711,9 +37746,9 @@ var HARNESSES = [
37711
37746
  name: "Claude Desktop",
37712
37747
  scope: "home",
37713
37748
  file: {
37714
- win32: { appData: join2("Claude", "claude_desktop_config.json") },
37715
- darwin: join2("Library", "Application Support", "Claude", "claude_desktop_config.json"),
37716
- linux: join2(".config", "Claude", "claude_desktop_config.json")
37749
+ win32: { appData: join3("Claude", "claude_desktop_config.json") },
37750
+ darwin: join3("Library", "Application Support", "Claude", "claude_desktop_config.json"),
37751
+ linux: join3(".config", "Claude", "claude_desktop_config.json")
37717
37752
  },
37718
37753
  container: "mcpServers",
37719
37754
  shape: "plain"
@@ -37722,7 +37757,7 @@ var HARNESSES = [
37722
37757
  id: "zeroclaw",
37723
37758
  name: "ZeroClaw",
37724
37759
  scope: "home",
37725
- file: join2(".zeroclaw", "config.toml"),
37760
+ file: join3(".zeroclaw", "config.toml"),
37726
37761
  container: "mcp.servers",
37727
37762
  shape: "plain",
37728
37763
  toml: {
@@ -37743,7 +37778,7 @@ var HARNESSES = [
37743
37778
  id: "deepcode",
37744
37779
  name: "Deep Code",
37745
37780
  scope: "home",
37746
- file: join2(".deepcode", "settings.json"),
37781
+ file: join3(".deepcode", "settings.json"),
37747
37782
  container: "mcpServers",
37748
37783
  shape: "plain"
37749
37784
  },
@@ -37751,7 +37786,7 @@ var HARNESSES = [
37751
37786
  id: "nanobot",
37752
37787
  name: "nanobot",
37753
37788
  scope: "home",
37754
- file: join2(".nanobot", "config.json"),
37789
+ file: join3(".nanobot", "config.json"),
37755
37790
  container: "mcpServers",
37756
37791
  shape: "plain",
37757
37792
  snippet: (launch) => jsonSnippet(launch, "mcpServers", "plain")
@@ -37760,19 +37795,19 @@ var HARNESSES = [
37760
37795
  id: "autohand",
37761
37796
  name: "Autohand",
37762
37797
  scope: "home",
37763
- file: join2(".autohand", "config.json"),
37798
+ file: join3(".autohand", "config.json"),
37764
37799
  container: "mcpServers",
37765
37800
  shape: "plain",
37766
37801
  snippet: (launch) => jsonSnippet(launch, "mcpServers", "plain")
37767
37802
  }
37768
37803
  ];
37769
37804
  function appDataRoot() {
37770
- return process2.env["APPDATA"] ?? join2(homedir2(), "AppData", "Roaming");
37805
+ return process3.env["APPDATA"] ?? join3(homedir2(), "AppData", "Roaming");
37771
37806
  }
37772
37807
  function homePath(path) {
37773
- return typeof path === "string" ? join2(homedir2(), path) : join2(appDataRoot(), path.appData);
37808
+ return typeof path === "string" ? join3(homedir2(), path) : join3(appDataRoot(), path.appData);
37774
37809
  }
37775
- function fileFor(harness, platform = process2.platform) {
37810
+ function fileFor(harness, platform = process3.platform) {
37776
37811
  if (typeof harness.file === "string") {
37777
37812
  return harness.file;
37778
37813
  }
@@ -37781,11 +37816,11 @@ function fileFor(harness, platform = process2.platform) {
37781
37816
  function configPath(harness, projectPath) {
37782
37817
  const file = fileFor(harness);
37783
37818
  if (harness.scope === "project") {
37784
- return join2(projectPath, file);
37819
+ return join3(projectPath, file);
37785
37820
  }
37786
37821
  return homePath(file);
37787
37822
  }
37788
- function displayPath(harness, platform = process2.platform) {
37823
+ function displayPath(harness, platform = process3.platform) {
37789
37824
  const file = fileFor(harness, platform);
37790
37825
  if (typeof file !== "string") {
37791
37826
  return `%APPDATA%/${file.appData.replaceAll("\\", "/")}`;
@@ -37821,15 +37856,15 @@ function harnessById(id) {
37821
37856
  }
37822
37857
  function present(harness, projectPath) {
37823
37858
  const path = configPath(harness, projectPath);
37824
- if (existsSync3(path)) {
37859
+ if (existsSync4(path)) {
37825
37860
  return true;
37826
37861
  }
37827
37862
  const root = harness.scope === "project" ? projectPath : homedir2();
37828
- const own = dirname(path);
37829
- if (own !== root && existsSync3(own)) {
37863
+ const own = dirname2(path);
37864
+ if (own !== root && existsSync4(own)) {
37830
37865
  return true;
37831
37866
  }
37832
- return harness.marker !== undefined && existsSync3(join2(root, harness.marker));
37867
+ return harness.marker !== undefined && existsSync4(join3(root, harness.marker));
37833
37868
  }
37834
37869
  function candidates(projectPath) {
37835
37870
  const found = [];
@@ -37842,7 +37877,7 @@ function candidates(projectPath) {
37842
37877
  }
37843
37878
  if (present(harness, projectPath)) {
37844
37879
  found.push({ harness, reason: "configured" });
37845
- } else if (harness.home !== undefined && existsSync3(join2(homedir2(), harness.home))) {
37880
+ } else if (harness.home !== undefined && existsSync4(join3(homedir2(), harness.home))) {
37846
37881
  found.push({ harness, reason: "installed" });
37847
37882
  }
37848
37883
  }
@@ -37864,7 +37899,7 @@ function groupByFile(harnesses, projectPath) {
37864
37899
  }
37865
37900
  function registered(harness, projectPath) {
37866
37901
  const path = configPath(harness, projectPath);
37867
- if (harness.snippet !== undefined || !existsSync3(path)) {
37902
+ if (harness.snippet !== undefined || !existsSync4(path)) {
37868
37903
  return false;
37869
37904
  }
37870
37905
  const text = readFileSync(path, "utf8");
@@ -37888,7 +37923,7 @@ function registered(harness, projectPath) {
37888
37923
  }
37889
37924
  function disconnect(harness, projectPath) {
37890
37925
  const path = configPath(harness, projectPath);
37891
- if (!existsSync3(path)) {
37926
+ if (!existsSync4(path)) {
37892
37927
  return { harness, path, action: "absent" };
37893
37928
  }
37894
37929
  if (harness.snippet !== undefined) {
@@ -37935,7 +37970,7 @@ function connect(harness, projectPath, launch) {
37935
37970
  if (harness.snippet !== undefined) {
37936
37971
  return { harness, path, action: "snippet", snippet: harness.snippet(launch) };
37937
37972
  }
37938
- const held = existsSync3(path) ? readFileSync(path, "utf8") : "";
37973
+ const held = existsSync4(path) ? readFileSync(path, "utf8") : "";
37939
37974
  if (harness.toml !== undefined) {
37940
37975
  const already = held.includes(harness.toml.identity ?? harness.toml.header);
37941
37976
  return put(harness, path, writeToml(held, harness.toml, launch), already);
@@ -37958,7 +37993,7 @@ function connect(harness, projectPath, launch) {
37958
37993
  `, already);
37959
37994
  }
37960
37995
  function put(harness, path, contents, already) {
37961
- mkdirSync(dirname(path), { recursive: true });
37996
+ mkdirSync(dirname2(path), { recursive: true });
37962
37997
  writeFileSync(path, contents, "utf8");
37963
37998
  return { harness, path, action: already ? "replaced" : "written" };
37964
37999
  }
@@ -37979,7 +38014,7 @@ init_game_log();
37979
38014
  import { execFile as execFile2 } from "node:child_process";
37980
38015
  import { mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
37981
38016
  import { tmpdir } from "node:os";
37982
- import { join as join3 } from "node:path";
38017
+ import { join as join4 } from "node:path";
37983
38018
  import { promisify as promisify2 } from "node:util";
37984
38019
  var run2 = promisify2(execFile2);
37985
38020
  function snakeCased(params) {
@@ -38020,8 +38055,8 @@ function reason(stdout, stderr) {
38020
38055
  return stdout.trim().split(/\r?\n/).at(-1) ?? "no output at all";
38021
38056
  }
38022
38057
  async function runOperation(engine, operation, params, projectPath) {
38023
- const paramsDir = mkdtempSync(join3(tmpdir(), "gdharness-params-"));
38024
- const paramsFile = join3(paramsDir, `${operation}.json`);
38058
+ const paramsDir = mkdtempSync(join4(tmpdir(), "gdharness-params-"));
38059
+ const paramsFile = join4(paramsDir, `${operation}.json`);
38025
38060
  writeFileSync2(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
38026
38061
  const args = [
38027
38062
  "--headless",
@@ -38069,13 +38104,13 @@ async function runOperation(engine, operation, params, projectPath) {
38069
38104
  init_errors();
38070
38105
  init_server_version();
38071
38106
  import { createHash } from "node:crypto";
38072
- import process3 from "node:process";
38107
+ import process4 from "node:process";
38073
38108
  var NEW_ISSUE = "https://github.com/Aureliolo/gdharness/issues/new";
38074
38109
  var ENHANCEMENT_URL = `${NEW_ISSUE}?template=feature_request.md`;
38075
38110
  function runtime() {
38076
- const versions = process3.versions;
38111
+ const versions = process4.versions;
38077
38112
  const bun = versions["bun"];
38078
- return bun === undefined ? `node ${process3.versions.node}` : `bun ${bun}`;
38113
+ return bun === undefined ? `node ${process4.versions.node}` : `bun ${bun}`;
38079
38114
  }
38080
38115
  function defectSignature(where, message) {
38081
38116
  const normalised = `${where}
@@ -38087,7 +38122,7 @@ function facts(where, message, godotVersion) {
38087
38122
  ["Where", where],
38088
38123
  ["Error", message],
38089
38124
  ["Version", `gdharness ${SERVER_VERSION}`],
38090
- ["Runtime", `${runtime()}, ${process3.platform} ${process3.arch}`],
38125
+ ["Runtime", `${runtime()}, ${process4.platform} ${process4.arch}`],
38091
38126
  ["Godot", godotVersion ?? "not known at the point this failed"],
38092
38127
  ["Signature", defectSignature(where, message)]
38093
38128
  ];
@@ -38097,7 +38132,7 @@ function filledTemplate(where, message, godotVersion) {
38097
38132
  `**gdharness version**: ${SERVER_VERSION}`,
38098
38133
  `**Godot version**: ${godotVersion ?? ""}`,
38099
38134
  `**Bun version**: ${runtime()}`,
38100
- `**OS**: ${process3.platform} ${process3.arch}`,
38135
+ `**OS**: ${process4.platform} ${process4.arch}`,
38101
38136
  "**MCP client**:",
38102
38137
  "",
38103
38138
  "## What you did",
@@ -38159,10 +38194,10 @@ function defectReport(where, error, godotVersion) {
38159
38194
  }
38160
38195
 
38161
38196
  // src/prompt.ts
38162
- import process4 from "node:process";
38197
+ import process5 from "node:process";
38163
38198
  import { createInterface } from "node:readline/promises";
38164
38199
  function interactive() {
38165
- return process4.stdin.isTTY && process4.stdout.isTTY;
38200
+ return process5.stdin.isTTY && process5.stdout.isTTY;
38166
38201
  }
38167
38202
 
38168
38203
  class Ask {
@@ -38171,7 +38206,7 @@ class Ask {
38171
38206
  if (!interactive()) {
38172
38207
  return fallback;
38173
38208
  }
38174
- this.line ??= createInterface({ input: process4.stdin, output: process4.stdout });
38209
+ this.line ??= createInterface({ input: process5.stdin, output: process5.stdout });
38175
38210
  const answer = (await this.line.question(`${question} ${fallback ? "[Y/n]" : "[y/N]"} `)).trim();
38176
38211
  return answer === "" ? fallback : answer.toLowerCase().startsWith("y");
38177
38212
  }
@@ -38200,8 +38235,8 @@ init_class_cache();
38200
38235
  init_headless();
38201
38236
  init_resources();
38202
38237
  init_server_version();
38203
- import { cpSync, existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync7, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "node:fs";
38204
- import { dirname as dirname2, join as join7 } from "node:path";
38238
+ import { cpSync, existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync7, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "node:fs";
38239
+ import { dirname as dirname3, join as join8 } from "node:path";
38205
38240
  import { fileURLToPath } from "node:url";
38206
38241
  var ADDONS = ["gdharness_editor", "gdharness_runtime", "auto_reload"];
38207
38242
  var EDITOR_PLUGINS = ["gdharness_editor", "auto_reload"];
@@ -38211,24 +38246,24 @@ var RUNTIME_AUTOLOAD = {
38211
38246
  };
38212
38247
  var VERSION_MARKER = ".gdharness-version";
38213
38248
  function shippedAddonsDirectory() {
38214
- return join7(dirname2(fileURLToPath(import.meta.url)), "godot", "addons");
38249
+ return join8(dirname3(fileURLToPath(import.meta.url)), "godot", "addons");
38215
38250
  }
38216
38251
  function shippedOperationsScript() {
38217
- return join7(dirname2(fileURLToPath(import.meta.url)), "godot", "operations", "godot_operations.gd");
38252
+ return join8(dirname3(fileURLToPath(import.meta.url)), "godot", "operations", "godot_operations.gd");
38218
38253
  }
38219
38254
  function installAddons(projectPath, from = shippedAddonsDirectory()) {
38220
38255
  const installed = [];
38221
38256
  for (const name of ADDONS) {
38222
- const source = join7(from, name);
38223
- if (!existsSync6(join7(source, name === "gdharness_runtime" ? "runtime_autoload.gd" : "plugin.cfg"))) {
38257
+ const source = join8(from, name);
38258
+ if (!existsSync7(join8(source, name === "gdharness_runtime" ? "runtime_autoload.gd" : "plugin.cfg"))) {
38224
38259
  throw new Error(`The package holds no ${name} addon at ${source}.`);
38225
38260
  }
38226
- const target = join7(projectPath, "addons", name);
38227
- const replaced = existsSync6(target);
38261
+ const target = join8(projectPath, "addons", name);
38262
+ const replaced = existsSync7(target);
38228
38263
  rmSync4(target, { recursive: true, force: true });
38229
- mkdirSync3(dirname2(target), { recursive: true });
38264
+ mkdirSync3(dirname3(target), { recursive: true });
38230
38265
  cpSync(source, target, { recursive: true });
38231
- writeFileSync5(join7(target, VERSION_MARKER), `${SERVER_VERSION}
38266
+ writeFileSync5(join8(target, VERSION_MARKER), `${SERVER_VERSION}
38232
38267
  `);
38233
38268
  installed.push({ name, path: target, replaced });
38234
38269
  }
@@ -38237,8 +38272,8 @@ function installAddons(projectPath, from = shippedAddonsDirectory()) {
38237
38272
  function removeAddons(projectPath) {
38238
38273
  const removed = [];
38239
38274
  for (const name of ADDONS) {
38240
- const target = join7(projectPath, "addons", name);
38241
- if (existsSync6(target)) {
38275
+ const target = join8(projectPath, "addons", name);
38276
+ if (existsSync7(target)) {
38242
38277
  rmSync4(target, { recursive: true, force: true });
38243
38278
  removed.push(target);
38244
38279
  }
@@ -38272,10 +38307,10 @@ function enabledPlugins(settings) {
38272
38307
  function inspectProject(projectPath) {
38273
38308
  const problems = [];
38274
38309
  const addons = ADDONS.map((name) => {
38275
- const directory = join7(projectPath, "addons", name);
38276
- const marker = join7(directory, VERSION_MARKER);
38277
- const installed = existsSync6(directory);
38278
- const version = existsSync6(marker) ? readFileSync7(marker, "utf8").trim() : null;
38310
+ const directory = join8(projectPath, "addons", name);
38311
+ const marker = join8(directory, VERSION_MARKER);
38312
+ const installed = existsSync7(directory);
38313
+ const version = existsSync7(marker) ? readFileSync7(marker, "utf8").trim() : null;
38279
38314
  const current = version === SERVER_VERSION;
38280
38315
  if (!installed) {
38281
38316
  problems.push(`addons/${name} is not installed; run gdharness setup`);
@@ -38284,7 +38319,7 @@ function inspectProject(projectPath) {
38284
38319
  }
38285
38320
  return { name, installed, version, current };
38286
38321
  });
38287
- const settings = parseProjectGodot(readFileSync7(join7(projectPath, "project.godot"), "utf8"));
38322
+ const settings = parseProjectGodot(readFileSync7(join8(projectPath, "project.godot"), "utf8"));
38288
38323
  const pluginsEnabled = enabledPlugins(settings);
38289
38324
  for (const name of EDITOR_PLUGINS) {
38290
38325
  if (!pluginsEnabled.includes(name)) {
@@ -38313,8 +38348,8 @@ function inspectProject(projectPath) {
38313
38348
 
38314
38349
  // src/skill.ts
38315
38350
  init_tool_definitions();
38316
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync as readdirSync3, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "node:fs";
38317
- import { basename, dirname as dirname3, join as join8 } from "node:path";
38351
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync3, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "node:fs";
38352
+ import { basename, dirname as dirname4, join as join9 } from "node:path";
38318
38353
 
38319
38354
  // src/tool-reference.ts
38320
38355
  init_tool_definitions();
@@ -38361,7 +38396,7 @@ function renderToolsMarkdown() {
38361
38396
 
38362
38397
  // src/skill.ts
38363
38398
  var SKILL_NAME = "gdharness";
38364
- var SHARED_SKILLS = join8(".agents", "skills");
38399
+ var SHARED_SKILLS = join9(".agents", "skills");
38365
38400
  function skillMarkdown(version) {
38366
38401
  return `---
38367
38402
  name: ${SKILL_NAME}
@@ -38443,27 +38478,27 @@ whatever the engine said on stderr comes back under \`engine_messages\`.
38443
38478
  function skillFiles(version) {
38444
38479
  return new Map([
38445
38480
  ["SKILL.md", skillMarkdown(version)],
38446
- [join8("references", "tools.md"), renderToolsMarkdown()]
38481
+ [join9("references", "tools.md"), renderToolsMarkdown()]
38447
38482
  ]);
38448
38483
  }
38449
38484
  function skillDirectories(harnesses, projectPath, exists) {
38450
- const directories = [join8(projectPath, SHARED_SKILLS, SKILL_NAME)];
38485
+ const directories = [join9(projectPath, SHARED_SKILLS, SKILL_NAME)];
38451
38486
  for (const harness of harnesses) {
38452
38487
  if (harness.skills === undefined) {
38453
38488
  continue;
38454
38489
  }
38455
- const own = join8(projectPath, harness.skills.dir);
38456
- if ((!harness.skills.shared || exists(own)) && !directories.includes(join8(own, SKILL_NAME))) {
38457
- directories.push(join8(own, SKILL_NAME));
38490
+ const own = join9(projectPath, harness.skills.dir);
38491
+ if ((!harness.skills.shared || exists(own)) && !directories.includes(join9(own, SKILL_NAME))) {
38492
+ directories.push(join9(own, SKILL_NAME));
38458
38493
  }
38459
38494
  }
38460
38495
  return directories;
38461
38496
  }
38462
38497
  function everySkillDirectory(projectPath, harnesses) {
38463
- const directories = new Set([join8(projectPath, SHARED_SKILLS, SKILL_NAME)]);
38498
+ const directories = new Set([join9(projectPath, SHARED_SKILLS, SKILL_NAME)]);
38464
38499
  for (const harness of harnesses) {
38465
38500
  if (harness.skills !== undefined) {
38466
- directories.add(join8(projectPath, harness.skills.dir, SKILL_NAME));
38501
+ directories.add(join9(projectPath, harness.skills.dir, SKILL_NAME));
38467
38502
  }
38468
38503
  }
38469
38504
  return [...directories];
@@ -38471,33 +38506,33 @@ function everySkillDirectory(projectPath, harnesses) {
38471
38506
  function removeSkill(directories, projectPath) {
38472
38507
  const removed = [];
38473
38508
  for (const directory of directories) {
38474
- if (existsSync7(directory)) {
38509
+ if (existsSync8(directory)) {
38475
38510
  rmSync5(directory, { recursive: true, force: true });
38476
- pruneEmpty(dirname3(directory), projectPath);
38511
+ pruneEmpty(dirname4(directory), projectPath);
38477
38512
  removed.push(directory);
38478
38513
  }
38479
38514
  }
38480
38515
  return removed;
38481
38516
  }
38482
38517
  function pruneEmpty(directory, projectPath) {
38483
- const shared = join8(projectPath, ".agents");
38518
+ const shared = join9(projectPath, ".agents");
38484
38519
  let path = directory;
38485
38520
  while (path !== projectPath && (basename(path) === "skills" || path === shared)) {
38486
38521
  if (readdirSync3(path).length > 0) {
38487
38522
  return;
38488
38523
  }
38489
38524
  rmSync5(path, { recursive: true, force: true });
38490
- path = dirname3(path);
38525
+ path = dirname4(path);
38491
38526
  }
38492
38527
  }
38493
38528
  function writeSkill(directories, version) {
38494
38529
  const files = skillFiles(version);
38495
38530
  return directories.map((directory) => {
38496
- const replaced = existsSync7(directory);
38531
+ const replaced = existsSync8(directory);
38497
38532
  rmSync5(directory, { recursive: true, force: true });
38498
38533
  for (const [name, contents] of files) {
38499
- const path = join8(directory, name);
38500
- mkdirSync4(dirname3(path), { recursive: true });
38534
+ const path = join9(directory, name);
38535
+ mkdirSync4(dirname4(path), { recursive: true });
38501
38536
  writeFileSync6(path, contents, "utf8");
38502
38537
  }
38503
38538
  return { path: directory, replaced };
@@ -38506,13 +38541,13 @@ function writeSkill(directories, version) {
38506
38541
 
38507
38542
  // src/version.ts
38508
38543
  import { readFileSync as readFileSync8 } from "node:fs";
38509
- import { dirname as dirname4, join as join9 } from "node:path";
38544
+ import { dirname as dirname5, join as join10 } from "node:path";
38510
38545
  import { fileURLToPath as fileURLToPath2 } from "node:url";
38511
38546
  function getLocalVersion() {
38512
- const currentDirectory = dirname4(fileURLToPath2(import.meta.url));
38547
+ const currentDirectory = dirname5(fileURLToPath2(import.meta.url));
38513
38548
  const packageCandidates = [
38514
- join9(currentDirectory, "..", "package.json"),
38515
- join9(currentDirectory, "..", "..", "package.json")
38549
+ join10(currentDirectory, "..", "package.json"),
38550
+ join10(currentDirectory, "..", "..", "package.json")
38516
38551
  ];
38517
38552
  for (const packagePath of packageCandidates) {
38518
38553
  try {
@@ -38534,7 +38569,7 @@ class UsageError extends Refusal {
38534
38569
  function projectArgument(at) {
38535
38570
  const named = args.slice(at).find((value) => !value.startsWith("--"));
38536
38571
  const projectPath = resolve5(named ?? ".");
38537
- if (!existsSync11(join13(projectPath, "project.godot"))) {
38572
+ if (!existsSync12(join14(projectPath, "project.godot"))) {
38538
38573
  throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
38539
38574
  }
38540
38575
  return projectPath;
@@ -38678,7 +38713,7 @@ async function setup() {
38678
38713
  console.log("no harness configured; name one yourself, and gdharness harnesses lists them all");
38679
38714
  }
38680
38715
  if (!args.includes("--no-skill")) {
38681
- const directories = skillDirectories(chosen.harnesses, projectPath, existsSync11);
38716
+ const directories = skillDirectories(chosen.harnesses, projectPath, existsSync12);
38682
38717
  for (const written of writeSkill(directories, getLocalVersion())) {
38683
38718
  console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
38684
38719
  }
@@ -38763,7 +38798,7 @@ async function upgrade() {
38763
38798
  if (already.length === 0) {
38764
38799
  console.log("no harness config names gdharness, so none was re-pinned");
38765
38800
  }
38766
- for (const written of writeSkill(skillDirectories(already, projectPath, existsSync11), version)) {
38801
+ for (const written of writeSkill(skillDirectories(already, projectPath, existsSync12), version)) {
38767
38802
  console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
38768
38803
  }
38769
38804
  console.log(`