gdharness 1.1.38 → 1.1.39
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 +125 -46
- package/build/index.js +123 -49
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -16541,6 +16541,16 @@ var init_types = __esm(() => {
|
|
|
16541
16541
|
};
|
|
16542
16542
|
});
|
|
16543
16543
|
|
|
16544
|
+
// src/answer-json.ts
|
|
16545
|
+
function answerJson(value) {
|
|
16546
|
+
return JSON.stringify(value, null, 2).replace(SCALAR_ARRAY, (_whole, items) => `[${items.split(/,\n\s+/).join(", ")}]`);
|
|
16547
|
+
}
|
|
16548
|
+
var SCALAR, SCALAR_ARRAY;
|
|
16549
|
+
var init_answer_json = __esm(() => {
|
|
16550
|
+
SCALAR = String.raw`(?:-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)`;
|
|
16551
|
+
SCALAR_ARRAY = new RegExp(String.raw`\[\n\s+(${SCALAR}(?:,\n\s+${SCALAR})*)\n\s*\]`, "g");
|
|
16552
|
+
});
|
|
16553
|
+
|
|
16544
16554
|
// src/resources.ts
|
|
16545
16555
|
import { readFileSync as readFileSync8, realpathSync as realpathSync3 } from "node:fs";
|
|
16546
16556
|
import { extname, resolve as resolve3 } from "node:path";
|
|
@@ -16727,7 +16737,7 @@ function readResourceText(uri, getProjectPath) {
|
|
|
16727
16737
|
const parsedProject = parseProjectGodot(rawProject);
|
|
16728
16738
|
return {
|
|
16729
16739
|
mimeType: "application/json",
|
|
16730
|
-
text:
|
|
16740
|
+
text: answerJson(parsedProject)
|
|
16731
16741
|
};
|
|
16732
16742
|
}
|
|
16733
16743
|
const filePath = resolveProjectFile(projectPath, parsedUri.resourcePath);
|
|
@@ -16770,6 +16780,7 @@ function setupResourceHandlers(mcp, getProjectPath) {
|
|
|
16770
16780
|
var STATIC_RESOURCES, RESOURCE_TEMPLATES, RESOURCE_COUNT;
|
|
16771
16781
|
var init_resources = __esm(() => {
|
|
16772
16782
|
init_types();
|
|
16783
|
+
init_answer_json();
|
|
16773
16784
|
init_errors();
|
|
16774
16785
|
init_paths();
|
|
16775
16786
|
STATIC_RESOURCES = [
|
|
@@ -17758,7 +17769,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17758
17769
|
},
|
|
17759
17770
|
{
|
|
17760
17771
|
name: "script_edit",
|
|
17761
|
-
description: "Creates a GDScript file, or adds functions, variables and signals to one. Every declaration written carries a type. Both load what they wrote under the project's own warning settings and answer with parses; the engine's reasons for a refusal come back under engine_messages. create answers with the extends and class_name the engine read, and a script declaring a class_name is put in the project's class list, with registered saying whether it is there. modify makes every addition or none, and answers with the line each one is on in the file written. rename renames a class or a member wherever the engine uses it and writes every file or none. A new name already taken is refused with nothing written, a member's included when a class inheriting it already uses the name in code, as a local or a parameter. changed lists each file with its changed lines as they now read, and leftAlone lists every other whole-word occurrence of the old name with what the tokenizer makes of it (code, string, comment, doc, nodePath, scene, text, or ignored for a file under a .gdignore directory, which is never changed) and, where it is not plain, why: a string that is the old name whole, such as find_children's type or a call's method name, which nothing renames or compiles and which is listed first; a node named like the class; a call on a value whose type the analyser could not tell. A member rename answers under languageServer how many requests it made and how long they took. A comment, documentation line, string or text file line with nothing more to say is given under leftAloneInProse by file, kind and line instead, and code that is certainly another symbol of the same name, a bare name in a file where nothing inherits the member or one the language server resolved to another declaration, under leftAloneOtherSymbols as each why with the files and lines it covers. summary.leftAlone counts each of the three lists beside the total. Read both before calling the rename done; nothing in them was changed. Afterwards an open editor scans the changed files and reloads every changed script, which is also the compile check, and its answer comes back under editor; with no editor the class cache is rebuilt and the changed scripts are compiled in a fresh engine, under classCache and compiled. written is true once the files are written, and ok is false when what came after the write failed: the files are left changed, and note says so.",
|
|
17772
|
+
description: "Creates a GDScript file, or adds functions, variables and signals to one. Every declaration written carries a type. Both load what they wrote under the project's own warning settings and answer with parses; the engine's reasons for a refusal come back under engine_messages. create answers with the extends and class_name the engine read, and a script declaring a class_name is put in the project's class list, with registered saying whether it is there. modify makes every addition or none, and answers with the line each one is on in the file written. rename renames a class or a member wherever the engine uses it and writes every file or none. A new name already taken is refused with nothing written, a member's included when a class inheriting it already uses the name in code, as a local or a parameter. changed lists each file with its changed lines as they now read, and leftAlone lists every other whole-word occurrence of the old name with what the tokenizer makes of it (code, string, comment, doc, nodePath, scene, text, or ignored for a file under a .gdignore directory, which is never changed) and, where it is not plain, why: a string that is the old name whole, such as find_children's type or a call's method name, which nothing renames or compiles and which is listed first; a node named like the class; a call on a value whose type the analyser could not tell. A member rename answers under languageServer how many requests it made and how long they took. A comment, documentation line, string or text file line with nothing more to say is given under leftAloneInProse by file, kind and line instead, and code that is certainly another symbol of the same name, a bare name in a file where nothing inherits the member or one the language server resolved to another declaration, under leftAloneOtherSymbols as each why with the files and lines it covers. summary.leftAlone counts each of the three lists beside the total. When those two grouped lists run past 300 lines, they are written with each line's text to a file under the project's .godot directory instead, and leftAloneDetail names the file and counts what is in it. Read both before calling the rename done; nothing in them was changed. Afterwards an open editor scans the changed files and reloads every changed script, which is also the compile check, and its answer comes back under editor; with no editor the class cache is rebuilt and the changed scripts are compiled in a fresh engine, under classCache and compiled. written is true once the files are written, and ok is false when what came after the write failed: the files are left changed, and note says so.",
|
|
17762
17773
|
parameters: {
|
|
17763
17774
|
projectPath: PROJECT_PATH,
|
|
17764
17775
|
scriptPath: SCRIPT_PATH,
|
|
@@ -34666,7 +34677,7 @@ async function handleDAPTool(client, toolName, args) {
|
|
|
34666
34677
|
}
|
|
34667
34678
|
const result = await client.setBreakpoint(safeArgs.scriptPath, safeArgs.line);
|
|
34668
34679
|
return {
|
|
34669
|
-
content: [{ type: "text", text:
|
|
34680
|
+
content: [{ type: "text", text: answerJson(result) }]
|
|
34670
34681
|
};
|
|
34671
34682
|
}
|
|
34672
34683
|
case "dap_remove_breakpoint": {
|
|
@@ -34675,7 +34686,7 @@ async function handleDAPTool(client, toolName, args) {
|
|
|
34675
34686
|
}
|
|
34676
34687
|
const result = await client.removeBreakpoint(safeArgs.scriptPath, safeArgs.line);
|
|
34677
34688
|
return {
|
|
34678
|
-
content: [{ type: "text", text:
|
|
34689
|
+
content: [{ type: "text", text: answerJson(result) }]
|
|
34679
34690
|
};
|
|
34680
34691
|
}
|
|
34681
34692
|
case "dap_continue": {
|
|
@@ -34690,12 +34701,12 @@ async function handleDAPTool(client, toolName, args) {
|
|
|
34690
34701
|
heldAgain,
|
|
34691
34702
|
note: `The game had not stopped again ${client.stepPatienceMs / 1000}s after the step: the step is still running, it went on without stopping anywhere, or the game was not held when it was asked. debug_state stack answers once it stops.`
|
|
34692
34703
|
};
|
|
34693
|
-
return { content: [{ type: "text", text:
|
|
34704
|
+
return { content: [{ type: "text", text: answerJson(answer) }] };
|
|
34694
34705
|
}
|
|
34695
34706
|
case "dap_get_stack_trace": {
|
|
34696
34707
|
const stack = await client.getStackTrace();
|
|
34697
34708
|
return {
|
|
34698
|
-
content: [{ type: "text", text:
|
|
34709
|
+
content: [{ type: "text", text: answerJson(stack) }]
|
|
34699
34710
|
};
|
|
34700
34711
|
}
|
|
34701
34712
|
case "dap_get_variables": {
|
|
@@ -34708,7 +34719,7 @@ async function handleDAPTool(client, toolName, args) {
|
|
|
34708
34719
|
note: `The values of ${missing.join(", ")} did not arrive from the game within ${client.scopesPatienceMs / 1000}s, so they are unknown rather than empty. Ask again.`
|
|
34709
34720
|
};
|
|
34710
34721
|
return {
|
|
34711
|
-
content: [{ type: "text", text:
|
|
34722
|
+
content: [{ type: "text", text: answerJson(answer) }]
|
|
34712
34723
|
};
|
|
34713
34724
|
}
|
|
34714
34725
|
default:
|
|
@@ -34727,6 +34738,7 @@ async function handleDAPTool(client, toolName, args) {
|
|
|
34727
34738
|
}
|
|
34728
34739
|
var DEFAULT_DAP_PORT = 6006, DAP_REQUEST_TIMEOUT_MS = 1e4, SCOPES_TIMEOUT_MS = 20000, STEP_LANDS_WITHIN_MS = 1e4, VARIABLES_POLL_TIMEOUT_MS = 2000, ReadRefusal;
|
|
34729
34740
|
var init_dap_client = __esm(() => {
|
|
34741
|
+
init_answer_json();
|
|
34730
34742
|
init_errors();
|
|
34731
34743
|
init_framing();
|
|
34732
34744
|
init_ports();
|
|
@@ -39341,7 +39353,7 @@ function asToolResponse(payload) {
|
|
|
39341
39353
|
content: [
|
|
39342
39354
|
{
|
|
39343
39355
|
type: "text",
|
|
39344
|
-
text:
|
|
39356
|
+
text: answerJson(payload)
|
|
39345
39357
|
}
|
|
39346
39358
|
]
|
|
39347
39359
|
};
|
|
@@ -39438,6 +39450,7 @@ async function handleLSPTool(client, toolName, args) {
|
|
|
39438
39450
|
}
|
|
39439
39451
|
var DEFAULT_LSP_PORT = 6005, DIAGNOSTICS_TIMEOUT_MS = 5000, REQUEST_TIMEOUT_MS2 = 1e4, REFERENCES_TIMEOUT_MS = 180000, LSPTimeout, ArgumentRefusal, AnotherProjectsServer;
|
|
39440
39452
|
var init_lsp_client = __esm(() => {
|
|
39453
|
+
init_answer_json();
|
|
39441
39454
|
init_errors();
|
|
39442
39455
|
init_framing();
|
|
39443
39456
|
init_paths();
|
|
@@ -40074,7 +40087,14 @@ class PlanBuilder {
|
|
|
40074
40087
|
}
|
|
40075
40088
|
function isWholeString(text, offset, name) {
|
|
40076
40089
|
const before = text[offset - 1];
|
|
40077
|
-
|
|
40090
|
+
if (before !== '"' && before !== "'" || text[offset + name.length] !== before) {
|
|
40091
|
+
return false;
|
|
40092
|
+
}
|
|
40093
|
+
let after = offset + name.length + 1;
|
|
40094
|
+
while (text[after] === " " || text[after] === "\t") {
|
|
40095
|
+
after += 1;
|
|
40096
|
+
}
|
|
40097
|
+
return text[after] !== ":";
|
|
40078
40098
|
}
|
|
40079
40099
|
function declaresItsOwn(script, name, except) {
|
|
40080
40100
|
return script.occurrences(name).some((occurrence) => occurrence.kind === "code" && occurrence.offset !== except && occurrence.declaredAs !== null && occurrence.declaredAs !== "class_name");
|
|
@@ -40536,7 +40556,7 @@ function applyRewrites(rewrites) {
|
|
|
40536
40556
|
throw error;
|
|
40537
40557
|
}
|
|
40538
40558
|
}
|
|
40539
|
-
var DOC_LINK_KINDS, DOC_KIND, WHOLE_CLASS_NAME = "the whole string is the class's name, as find_children's type, a comparison with get_global_name() or a list of accepted type names takes it, and it was not changed: if it names this class, it no longer matches anything after the rename, and no compile says so", WHOLE_MEMBER_NAME = "the whole string is the member's name, as call, connect, has_method, get or
|
|
40559
|
+
var DOC_LINK_KINDS, DOC_KIND, WHOLE_CLASS_NAME = "the whole string is the class's name, as find_children's type, a comparison with get_global_name() or a list of accepted type names takes it, and it was not changed: if it names this class, it no longer matches anything after the rename, and no compile says so", WHOLE_MEMBER_NAME = "the whole string is the member's name, as call, connect, has_method, get, set or a subscript on an object take it, and it was not changed: if it names this member, it no longer finds it after the rename, and no compile says so", NOT_INHERITED = "no class in this file inherits the member, so a bare name here is that class's own local, parameter or member, or a global", RESOLVED_ELSEWHERE = "the language server resolved this to another declaration of the name";
|
|
40540
40560
|
var init_rename = __esm(() => {
|
|
40541
40561
|
init_gdscript_source();
|
|
40542
40562
|
DOC_LINK_KINDS = new Set([
|
|
@@ -40560,7 +40580,7 @@ var init_rename = __esm(() => {
|
|
|
40560
40580
|
});
|
|
40561
40581
|
|
|
40562
40582
|
// src/rename-symbol.ts
|
|
40563
|
-
import { existsSync as existsSync18, realpathSync as realpathSync5 } from "node:fs";
|
|
40583
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync11, readdirSync as readdirSync8, realpathSync as realpathSync5, rmSync as rmSync9, writeFileSync as writeFileSync13 } from "node:fs";
|
|
40564
40584
|
import { basename as basename5, dirname as dirname15, join as join21, relative as relative4, resolve as resolve6 } from "node:path";
|
|
40565
40585
|
function resourcePathOf(projectPath, absolute) {
|
|
40566
40586
|
const inside = relative4(onDiskSpelling(projectPath), onDiskSpelling(absolute));
|
|
@@ -40818,6 +40838,7 @@ async function renameSymbol(request, services) {
|
|
|
40818
40838
|
moved: moveTo === null ? null : { from: declaringPath, to: moveTo },
|
|
40819
40839
|
scriptsInOrder,
|
|
40820
40840
|
rewrites,
|
|
40841
|
+
detail: left.detail,
|
|
40821
40842
|
report: {
|
|
40822
40843
|
renamed: { what, from: symbol, to: newName, declaredIn: declaringPath, resolvedBy },
|
|
40823
40844
|
...took === null ? {} : { languageServer: took },
|
|
@@ -40930,6 +40951,10 @@ function leftAloneReport(mentions) {
|
|
|
40930
40951
|
lines.push(line);
|
|
40931
40952
|
}
|
|
40932
40953
|
};
|
|
40954
|
+
const textOf = new Map;
|
|
40955
|
+
for (const mention of mentions) {
|
|
40956
|
+
textOf.set(`${mention.file}:${String(mention.line)}`, mention.text);
|
|
40957
|
+
}
|
|
40933
40958
|
for (const mention of mentions) {
|
|
40934
40959
|
if (PROSE_KINDS.has(mention.kind) && mention.why === undefined) {
|
|
40935
40960
|
const key = `${mention.file}
|
|
@@ -40951,20 +40976,64 @@ ${mention.kind}`;
|
|
|
40951
40976
|
}
|
|
40952
40977
|
}
|
|
40953
40978
|
const loudFirst = (mention) => mention.kind === "string" && mention.why !== undefined ? 0 : 1;
|
|
40979
|
+
const leftAlone = full.sort((a, b) => loudFirst(a) - loudFirst(b));
|
|
40980
|
+
const inProse = [...prose.values()];
|
|
40981
|
+
const otherSymbols = [...others.entries()].map(([why, files]) => ({
|
|
40982
|
+
why,
|
|
40983
|
+
files: [...files.entries()].map(([file, lines]) => ({ file, lines }))
|
|
40984
|
+
}));
|
|
40985
|
+
const grouped = inProse.reduce((sum, group) => sum + group.lines.length, 0) + otherSymbols.reduce((sum, reason) => sum + reason.files.reduce((n, one) => n + one.lines.length, 0), 0);
|
|
40986
|
+
if (grouped <= LEFT_ALONE_ANSWER_LINES) {
|
|
40987
|
+
return {
|
|
40988
|
+
lists: {
|
|
40989
|
+
leftAlone,
|
|
40990
|
+
...inProse.length > 0 ? { leftAloneInProse: inProse } : {},
|
|
40991
|
+
...otherSymbols.length > 0 ? { leftAloneOtherSymbols: otherSymbols } : {}
|
|
40992
|
+
},
|
|
40993
|
+
counts,
|
|
40994
|
+
detail: null
|
|
40995
|
+
};
|
|
40996
|
+
}
|
|
40997
|
+
const withText = (file, lines) => lines.map((line) => ({ line, text: textOf.get(`${file}:${String(line)}`) ?? "" }));
|
|
40954
40998
|
return {
|
|
40955
40999
|
lists: {
|
|
40956
|
-
leftAlone
|
|
40957
|
-
|
|
40958
|
-
|
|
40959
|
-
|
|
40960
|
-
why,
|
|
40961
|
-
files:
|
|
41000
|
+
leftAlone,
|
|
41001
|
+
leftAloneDetail: {
|
|
41002
|
+
inProse: { files: new Set(inProse.map((group) => group.file)).size, occurrences: counts.inProse },
|
|
41003
|
+
otherSymbols: otherSymbols.map((reason) => ({
|
|
41004
|
+
why: reason.why,
|
|
41005
|
+
files: reason.files.length,
|
|
41006
|
+
occurrences: reason.files.reduce((n, one) => n + one.lines.length, 0)
|
|
40962
41007
|
}))
|
|
40963
|
-
}
|
|
41008
|
+
}
|
|
40964
41009
|
},
|
|
40965
|
-
counts
|
|
41010
|
+
counts,
|
|
41011
|
+
detail: {
|
|
41012
|
+
leftAloneInProse: inProse.map((group) => ({
|
|
41013
|
+
file: group.file,
|
|
41014
|
+
kind: group.kind,
|
|
41015
|
+
lines: withText(group.file, group.lines)
|
|
41016
|
+
})),
|
|
41017
|
+
leftAloneOtherSymbols: otherSymbols.map((reason) => ({
|
|
41018
|
+
why: reason.why,
|
|
41019
|
+
files: reason.files.map((one) => ({ file: one.file, lines: withText(one.file, one.lines) }))
|
|
41020
|
+
}))
|
|
41021
|
+
}
|
|
40966
41022
|
};
|
|
40967
41023
|
}
|
|
41024
|
+
function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
41025
|
+
const directory = join21(projectPath, ".godot", "gdharness-renames");
|
|
41026
|
+
mkdirSync11(directory, { recursive: true });
|
|
41027
|
+
detailWrites += 1;
|
|
41028
|
+
const path = join21(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
41029
|
+
writeFileSync13(path, `${JSON.stringify(detail, null, 2)}
|
|
41030
|
+
`, "utf8");
|
|
41031
|
+
const kept = readdirSync8(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
41032
|
+
for (const old of kept.slice(10)) {
|
|
41033
|
+
rmSync9(join21(directory, old), { force: true });
|
|
41034
|
+
}
|
|
41035
|
+
return path;
|
|
41036
|
+
}
|
|
40968
41037
|
function summaryOf(edits, mentions, rewrites, left) {
|
|
40969
41038
|
const count = (values) => {
|
|
40970
41039
|
const counted = {};
|
|
@@ -40981,7 +41050,7 @@ function summaryOf(edits, mentions, rewrites, left) {
|
|
|
40981
41050
|
leftAloneByKind: count(mentions.map((mention) => mention.kind))
|
|
40982
41051
|
};
|
|
40983
41052
|
}
|
|
40984
|
-
var WHAT, PROSE_KINDS;
|
|
41053
|
+
var WHAT, LEFT_ALONE_ANSWER_LINES = 300, detailWrites = 0, PROSE_KINDS;
|
|
40985
41054
|
var init_rename_symbol = __esm(() => {
|
|
40986
41055
|
init_gdscript_source();
|
|
40987
41056
|
init_lsp_client();
|
|
@@ -41001,7 +41070,7 @@ var init_rename_symbol = __esm(() => {
|
|
|
41001
41070
|
});
|
|
41002
41071
|
|
|
41003
41072
|
// src/restart-note.ts
|
|
41004
|
-
import { mkdirSync as
|
|
41073
|
+
import { mkdirSync as mkdirSync12, readFileSync as readFileSync19, rmSync as rmSync10, writeFileSync as writeFileSync14 } from "node:fs";
|
|
41005
41074
|
import { dirname as dirname16, join as join22 } from "node:path";
|
|
41006
41075
|
function restartNotePath(projectPath) {
|
|
41007
41076
|
return join22(projectPath, ".godot", "gdharness-restart.json");
|
|
@@ -41009,8 +41078,8 @@ function restartNotePath(projectPath) {
|
|
|
41009
41078
|
function noteRestartBegun(note) {
|
|
41010
41079
|
const path = restartNotePath(note.projectPath);
|
|
41011
41080
|
try {
|
|
41012
|
-
|
|
41013
|
-
|
|
41081
|
+
mkdirSync12(dirname16(path), { recursive: true });
|
|
41082
|
+
writeFileSync14(path, `${JSON.stringify(note, null, 2)}
|
|
41014
41083
|
`, "utf8");
|
|
41015
41084
|
} catch (error) {
|
|
41016
41085
|
console.error(`[SERVER] Could not record the restart at ${path}: ${errorMessage(error)}`);
|
|
@@ -41033,7 +41102,7 @@ function restartOwed(projectPath) {
|
|
|
41033
41102
|
}
|
|
41034
41103
|
function restartSettled(projectPath) {
|
|
41035
41104
|
try {
|
|
41036
|
-
|
|
41105
|
+
rmSync10(restartNotePath(projectPath), { force: true });
|
|
41037
41106
|
} catch (error) {
|
|
41038
41107
|
console.error(`[SERVER] Could not clear the restart note: ${errorMessage(error)}`);
|
|
41039
41108
|
}
|
|
@@ -41046,14 +41115,14 @@ var init_restart_note = __esm(() => {
|
|
|
41046
41115
|
import { execFile as execFile4 } from "node:child_process";
|
|
41047
41116
|
import { createHash as createHash4 } from "node:crypto";
|
|
41048
41117
|
import {
|
|
41049
|
-
mkdirSync as
|
|
41118
|
+
mkdirSync as mkdirSync13,
|
|
41050
41119
|
openSync as openSync2,
|
|
41051
|
-
readdirSync as
|
|
41120
|
+
readdirSync as readdirSync9,
|
|
41052
41121
|
readFileSync as readFileSync20,
|
|
41053
41122
|
realpathSync as realpathSync6,
|
|
41054
|
-
rmSync as
|
|
41123
|
+
rmSync as rmSync11,
|
|
41055
41124
|
statSync as statSync8,
|
|
41056
|
-
writeFileSync as
|
|
41125
|
+
writeFileSync as writeFileSync15
|
|
41057
41126
|
} from "node:fs";
|
|
41058
41127
|
import { basename as basename6, dirname as dirname17, join as join23, resolve as resolve7 } from "node:path";
|
|
41059
41128
|
import { promisify as promisify4 } from "node:util";
|
|
@@ -41085,7 +41154,7 @@ function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
|
41085
41154
|
return join23(directory, "runs", `note-${projectKey(projectPath)}.json`);
|
|
41086
41155
|
}
|
|
41087
41156
|
function openTranscript(startedAt) {
|
|
41088
|
-
|
|
41157
|
+
mkdirSync13(runsDirectory(), { recursive: true });
|
|
41089
41158
|
const path = join23(runsDirectory(), `run-${startedAt}.log`);
|
|
41090
41159
|
return { path, fd: openSync2(path, "a") };
|
|
41091
41160
|
}
|
|
@@ -41096,8 +41165,8 @@ function editorNotePath(projectPath, directory = runtimeDirectory()) {
|
|
|
41096
41165
|
return join23(directory, "runs", `editor-${projectKey(projectPath)}.json`);
|
|
41097
41166
|
}
|
|
41098
41167
|
function writeEditorRunNote(note) {
|
|
41099
|
-
|
|
41100
|
-
|
|
41168
|
+
mkdirSync13(runsDirectory(), { recursive: true });
|
|
41169
|
+
writeFileSync15(editorNotePath(note.projectPath), JSON.stringify(note, null, 2), "utf8");
|
|
41101
41170
|
}
|
|
41102
41171
|
function readEditorRunNote(projectPath) {
|
|
41103
41172
|
for (const directory of runtimeDirectories()) {
|
|
@@ -41147,7 +41216,7 @@ function everyRunRecord() {
|
|
|
41147
41216
|
for (const directory of runtimeDirectories()) {
|
|
41148
41217
|
let entries;
|
|
41149
41218
|
try {
|
|
41150
|
-
entries =
|
|
41219
|
+
entries = readdirSync9(join23(directory, "runs"));
|
|
41151
41220
|
} catch {
|
|
41152
41221
|
continue;
|
|
41153
41222
|
}
|
|
@@ -41366,7 +41435,7 @@ function clearRunRecord(projectPath) {
|
|
|
41366
41435
|
const ours = sharedRecord !== null && sharedRecord.projectPath !== "" && isSameDirectory(sharedRecord.projectPath, projectPath);
|
|
41367
41436
|
for (const path of [runRecordPath(projectPath, directory), ...ours ? [shared] : []]) {
|
|
41368
41437
|
try {
|
|
41369
|
-
|
|
41438
|
+
rmSync11(path, { force: true });
|
|
41370
41439
|
} catch {}
|
|
41371
41440
|
}
|
|
41372
41441
|
}
|
|
@@ -41383,7 +41452,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
41383
41452
|
const kept = new Set(everyRunRecord().map((record) => record.transcript));
|
|
41384
41453
|
let entries;
|
|
41385
41454
|
try {
|
|
41386
|
-
entries =
|
|
41455
|
+
entries = readdirSync9(directory);
|
|
41387
41456
|
} catch {
|
|
41388
41457
|
return;
|
|
41389
41458
|
}
|
|
@@ -41396,7 +41465,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
41396
41465
|
continue;
|
|
41397
41466
|
}
|
|
41398
41467
|
try {
|
|
41399
|
-
|
|
41468
|
+
rmSync11(path);
|
|
41400
41469
|
} catch {}
|
|
41401
41470
|
}
|
|
41402
41471
|
}
|
|
@@ -41420,9 +41489,9 @@ import {
|
|
|
41420
41489
|
copyFileSync,
|
|
41421
41490
|
existsSync as existsSync19,
|
|
41422
41491
|
fstatSync,
|
|
41423
|
-
mkdirSync as
|
|
41492
|
+
mkdirSync as mkdirSync14,
|
|
41424
41493
|
openSync as openSync3,
|
|
41425
|
-
readdirSync as
|
|
41494
|
+
readdirSync as readdirSync10,
|
|
41426
41495
|
readFileSync as readFileSync21,
|
|
41427
41496
|
readSync as readSync2,
|
|
41428
41497
|
rmdirSync as rmdirSync2,
|
|
@@ -41876,7 +41945,7 @@ function exportAnswer(ending, asked, before, after) {
|
|
|
41876
41945
|
...problems.omitted === 0 ? {} : { entriesOmitted: problems.omitted }
|
|
41877
41946
|
};
|
|
41878
41947
|
if (verdict.exported) {
|
|
41879
|
-
return { content: [{ type: "text", text:
|
|
41948
|
+
return { content: [{ type: "text", text: answerJson(verdict) }] };
|
|
41880
41949
|
}
|
|
41881
41950
|
const stale = !written && after !== null ? ` The ${asked.outputPath} there is from before this export, which did not write it.` : "";
|
|
41882
41951
|
const why = ending.exitSignal === null ? stale : ` The engine was ${howItEnded(ending)}, so it has no exit code.${stale}`;
|
|
@@ -41886,7 +41955,7 @@ function exportAnswer(ending, asked, before, after) {
|
|
|
41886
41955
|
type: "text",
|
|
41887
41956
|
text: `Export with preset '${asked.preset}' did not produce ${asked.outputPath}.${why}`
|
|
41888
41957
|
},
|
|
41889
|
-
{ type: "text", text:
|
|
41958
|
+
{ type: "text", text: answerJson(verdict) }
|
|
41890
41959
|
],
|
|
41891
41960
|
isError: true
|
|
41892
41961
|
};
|
|
@@ -41990,7 +42059,7 @@ function elsewhereIn(projectPath, asked) {
|
|
|
41990
42059
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
41991
42060
|
let near = [];
|
|
41992
42061
|
try {
|
|
41993
|
-
near =
|
|
42062
|
+
near = readdirSync10(projectPath, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.toLowerCase() !== root).map((entry) => entry.name).filter((name) => name.toLowerCase().startsWith("test") || root.startsWith(name.toLowerCase()));
|
|
41994
42063
|
} catch {
|
|
41995
42064
|
return "";
|
|
41996
42065
|
}
|
|
@@ -42092,7 +42161,7 @@ class GodotServer {
|
|
|
42092
42161
|
return response;
|
|
42093
42162
|
}
|
|
42094
42163
|
jsonTextResponse(payload) {
|
|
42095
|
-
return { content: [{ type: "text", text:
|
|
42164
|
+
return { content: [{ type: "text", text: answerJson(payload) }] };
|
|
42096
42165
|
}
|
|
42097
42166
|
async run() {
|
|
42098
42167
|
const located = await this.locator.find();
|
|
@@ -42425,7 +42494,7 @@ class GodotServer {
|
|
|
42425
42494
|
saying(answer, block) {
|
|
42426
42495
|
return {
|
|
42427
42496
|
...answer,
|
|
42428
|
-
content: [...answer.content, { type: "text", text:
|
|
42497
|
+
content: [...answer.content, { type: "text", text: answerJson(block) }]
|
|
42429
42498
|
};
|
|
42430
42499
|
}
|
|
42431
42500
|
projectHasMovedOn() {
|
|
@@ -43398,7 +43467,7 @@ class GodotServer {
|
|
|
43398
43467
|
if (!outcome.ok) {
|
|
43399
43468
|
const response = this.createErrorResponse(outcome.message);
|
|
43400
43469
|
if (Object.keys(extras).length > 0) {
|
|
43401
|
-
response.content.push({ type: "text", text:
|
|
43470
|
+
response.content.push({ type: "text", text: answerJson(extras) });
|
|
43402
43471
|
}
|
|
43403
43472
|
return response;
|
|
43404
43473
|
}
|
|
@@ -43476,7 +43545,7 @@ class GodotServer {
|
|
|
43476
43545
|
}
|
|
43477
43546
|
const folder = dirname18(output.absolutePath);
|
|
43478
43547
|
try {
|
|
43479
|
-
|
|
43548
|
+
mkdirSync14(folder, { recursive: true });
|
|
43480
43549
|
} catch (error) {
|
|
43481
43550
|
return this.createErrorResponse(`Cannot create ${dirname18(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
43482
43551
|
}
|
|
@@ -43587,7 +43656,7 @@ class GodotServer {
|
|
|
43587
43656
|
let report = null;
|
|
43588
43657
|
let reportProblem = null;
|
|
43589
43658
|
try {
|
|
43590
|
-
const written = existsSync19(reportsDir) ?
|
|
43659
|
+
const written = existsSync19(reportsDir) ? readdirSync10(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
|
|
43591
43660
|
const newest = written.at(-1);
|
|
43592
43661
|
if (newest !== undefined) {
|
|
43593
43662
|
report = parseJUnit(readFileSync21(join24(reportsDir, newest, "results.xml"), "utf8"));
|
|
@@ -46098,6 +46167,15 @@ class GodotServer {
|
|
|
46098
46167
|
const said = outcome.conflicts === undefined ? "" : ` ${outcome.conflicts.join("; ")}.`;
|
|
46099
46168
|
return this.createErrorResponse(`${outcome.reason}${said}`, outcome.advice ?? []);
|
|
46100
46169
|
}
|
|
46170
|
+
if (outcome.detail !== null) {
|
|
46171
|
+
const file = writeLeftAloneDetail(projectPath, symbol, outcome.detail);
|
|
46172
|
+
const folded = asParams(outcome.report["leftAloneDetail"]);
|
|
46173
|
+
outcome.report["leftAloneDetail"] = {
|
|
46174
|
+
file,
|
|
46175
|
+
note: `Too many lines were left alone to list here, and none of them is a use: every comment, documentation line, string and text file line that only shares the word, and every other symbol of the same name, is in ${file} with its text.`,
|
|
46176
|
+
...folded
|
|
46177
|
+
};
|
|
46178
|
+
}
|
|
46101
46179
|
if (readBoolean(args, "preview") === true) {
|
|
46102
46180
|
return this.jsonTextResponse({ preview: true, ...outcome.report });
|
|
46103
46181
|
}
|
|
@@ -46561,6 +46639,7 @@ var init_server3 = __esm(() => {
|
|
|
46561
46639
|
init_stdio2();
|
|
46562
46640
|
init_types();
|
|
46563
46641
|
init_alive();
|
|
46642
|
+
init_answer_json();
|
|
46564
46643
|
init_boot_note();
|
|
46565
46644
|
init_breakpoint_note();
|
|
46566
46645
|
init_bridge_announce();
|
|
@@ -47831,7 +47910,7 @@ every other occurrence of the word for you to read: a string that is the name wh
|
|
|
47831
47910
|
\`find_children\` type, first under \`leftAlone\` since nothing compiles it, a file under a
|
|
47832
47911
|
\`.gdignore\` directory there too, a word in a comment, a doc line, a string or a text file by
|
|
47833
47912
|
line under \`leftAloneInProse\`, and a local or another class's member of the same name by line
|
|
47834
|
-
under \`leftAloneOtherSymbols
|
|
47913
|
+
under \`leftAloneOtherSymbols\`; a common name's lines go to the file \`leftAloneDetail\` names. Pass \`preview\` first on a large rename. If the answer has \`ok: false\`
|
|
47835
47914
|
beside \`written: true\`, the files were changed and a script did not compile afterwards.
|
|
47836
47915
|
|
|
47837
47916
|
Editing a file while a game is running does not change the running game. \`auto_reload\` is an
|
package/build/index.js
CHANGED
|
@@ -10238,9 +10238,9 @@ import {
|
|
|
10238
10238
|
copyFileSync,
|
|
10239
10239
|
existsSync as existsSync16,
|
|
10240
10240
|
fstatSync,
|
|
10241
|
-
mkdirSync as
|
|
10241
|
+
mkdirSync as mkdirSync12,
|
|
10242
10242
|
openSync as openSync3,
|
|
10243
|
-
readdirSync as
|
|
10243
|
+
readdirSync as readdirSync9,
|
|
10244
10244
|
readFileSync as readFileSync19,
|
|
10245
10245
|
readSync as readSync2,
|
|
10246
10246
|
rmdirSync as rmdirSync2,
|
|
@@ -24828,6 +24828,13 @@ function alive(pid) {
|
|
|
24828
24828
|
}
|
|
24829
24829
|
}
|
|
24830
24830
|
|
|
24831
|
+
// src/answer-json.ts
|
|
24832
|
+
var SCALAR = String.raw`(?:-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)`;
|
|
24833
|
+
var SCALAR_ARRAY = new RegExp(String.raw`\[\n\s+(${SCALAR}(?:,\n\s+${SCALAR})*)\n\s*\]`, "g");
|
|
24834
|
+
function answerJson(value) {
|
|
24835
|
+
return JSON.stringify(value, null, 2).replace(SCALAR_ARRAY, (_whole, items) => `[${items.split(/,\n\s+/).join(", ")}]`);
|
|
24836
|
+
}
|
|
24837
|
+
|
|
24831
24838
|
// src/boot-note.ts
|
|
24832
24839
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
|
|
24833
24840
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
@@ -27580,7 +27587,7 @@ async function handleDAPTool(client, toolName, args) {
|
|
|
27580
27587
|
}
|
|
27581
27588
|
const result = await client.setBreakpoint(safeArgs.scriptPath, safeArgs.line);
|
|
27582
27589
|
return {
|
|
27583
|
-
content: [{ type: "text", text:
|
|
27590
|
+
content: [{ type: "text", text: answerJson(result) }]
|
|
27584
27591
|
};
|
|
27585
27592
|
}
|
|
27586
27593
|
case "dap_remove_breakpoint": {
|
|
@@ -27589,7 +27596,7 @@ async function handleDAPTool(client, toolName, args) {
|
|
|
27589
27596
|
}
|
|
27590
27597
|
const result = await client.removeBreakpoint(safeArgs.scriptPath, safeArgs.line);
|
|
27591
27598
|
return {
|
|
27592
|
-
content: [{ type: "text", text:
|
|
27599
|
+
content: [{ type: "text", text: answerJson(result) }]
|
|
27593
27600
|
};
|
|
27594
27601
|
}
|
|
27595
27602
|
case "dap_continue": {
|
|
@@ -27604,12 +27611,12 @@ async function handleDAPTool(client, toolName, args) {
|
|
|
27604
27611
|
heldAgain,
|
|
27605
27612
|
note: `The game had not stopped again ${client.stepPatienceMs / 1000}s after the step: the step is still running, it went on without stopping anywhere, or the game was not held when it was asked. debug_state stack answers once it stops.`
|
|
27606
27613
|
};
|
|
27607
|
-
return { content: [{ type: "text", text:
|
|
27614
|
+
return { content: [{ type: "text", text: answerJson(answer) }] };
|
|
27608
27615
|
}
|
|
27609
27616
|
case "dap_get_stack_trace": {
|
|
27610
27617
|
const stack = await client.getStackTrace();
|
|
27611
27618
|
return {
|
|
27612
|
-
content: [{ type: "text", text:
|
|
27619
|
+
content: [{ type: "text", text: answerJson(stack) }]
|
|
27613
27620
|
};
|
|
27614
27621
|
}
|
|
27615
27622
|
case "dap_get_variables": {
|
|
@@ -27622,7 +27629,7 @@ async function handleDAPTool(client, toolName, args) {
|
|
|
27622
27629
|
note: `The values of ${missing.join(", ")} did not arrive from the game within ${client.scopesPatienceMs / 1000}s, so they are unknown rather than empty. Ask again.`
|
|
27623
27630
|
};
|
|
27624
27631
|
return {
|
|
27625
|
-
content: [{ type: "text", text:
|
|
27632
|
+
content: [{ type: "text", text: answerJson(answer) }]
|
|
27626
27633
|
};
|
|
27627
27634
|
}
|
|
27628
27635
|
default:
|
|
@@ -30082,7 +30089,7 @@ function asToolResponse(payload) {
|
|
|
30082
30089
|
content: [
|
|
30083
30090
|
{
|
|
30084
30091
|
type: "text",
|
|
30085
|
-
text:
|
|
30092
|
+
text: answerJson(payload)
|
|
30086
30093
|
}
|
|
30087
30094
|
]
|
|
30088
30095
|
};
|
|
@@ -30798,10 +30805,17 @@ class PlanBuilder {
|
|
|
30798
30805
|
}
|
|
30799
30806
|
function isWholeString(text, offset, name) {
|
|
30800
30807
|
const before = text[offset - 1];
|
|
30801
|
-
|
|
30808
|
+
if (before !== '"' && before !== "'" || text[offset + name.length] !== before) {
|
|
30809
|
+
return false;
|
|
30810
|
+
}
|
|
30811
|
+
let after = offset + name.length + 1;
|
|
30812
|
+
while (text[after] === " " || text[after] === "\t") {
|
|
30813
|
+
after += 1;
|
|
30814
|
+
}
|
|
30815
|
+
return text[after] !== ":";
|
|
30802
30816
|
}
|
|
30803
30817
|
var WHOLE_CLASS_NAME = "the whole string is the class's name, as find_children's type, a comparison with get_global_name() or a list of accepted type names takes it, and it was not changed: if it names this class, it no longer matches anything after the rename, and no compile says so";
|
|
30804
|
-
var WHOLE_MEMBER_NAME = "the whole string is the member's name, as call, connect, has_method, get or
|
|
30818
|
+
var WHOLE_MEMBER_NAME = "the whole string is the member's name, as call, connect, has_method, get, set or a subscript on an object take it, and it was not changed: if it names this member, it no longer finds it after the rename, and no compile says so";
|
|
30805
30819
|
function declaresItsOwn(script, name, except) {
|
|
30806
30820
|
return script.occurrences(name).some((occurrence) => occurrence.kind === "code" && occurrence.offset !== except && occurrence.declaredAs !== null && occurrence.declaredAs !== "class_name");
|
|
30807
30821
|
}
|
|
@@ -31266,7 +31280,7 @@ function applyRewrites(rewrites) {
|
|
|
31266
31280
|
}
|
|
31267
31281
|
|
|
31268
31282
|
// src/rename-symbol.ts
|
|
31269
|
-
import { existsSync as existsSync14, realpathSync as realpathSync5 } from "node:fs";
|
|
31283
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync8, readdirSync as readdirSync6, realpathSync as realpathSync5, rmSync as rmSync6, writeFileSync as writeFileSync10 } from "node:fs";
|
|
31270
31284
|
import { basename as basename3, dirname as dirname10, join as join16, relative as relative3, resolve as resolve5 } from "node:path";
|
|
31271
31285
|
|
|
31272
31286
|
// src/resources.ts
|
|
@@ -31484,7 +31498,7 @@ function readResourceText(uri, getProjectPath) {
|
|
|
31484
31498
|
const parsedProject = parseProjectGodot(rawProject);
|
|
31485
31499
|
return {
|
|
31486
31500
|
mimeType: "application/json",
|
|
31487
|
-
text:
|
|
31501
|
+
text: answerJson(parsedProject)
|
|
31488
31502
|
};
|
|
31489
31503
|
}
|
|
31490
31504
|
const filePath = resolveProjectFile(projectPath, parsedUri.resourcePath);
|
|
@@ -31791,6 +31805,7 @@ async function renameSymbol(request, services) {
|
|
|
31791
31805
|
moved: moveTo === null ? null : { from: declaringPath, to: moveTo },
|
|
31792
31806
|
scriptsInOrder,
|
|
31793
31807
|
rewrites,
|
|
31808
|
+
detail: left.detail,
|
|
31794
31809
|
report: {
|
|
31795
31810
|
renamed: { what, from: symbol, to: newName, declaredIn: declaringPath, resolvedBy },
|
|
31796
31811
|
...took === null ? {} : { languageServer: took },
|
|
@@ -31903,6 +31918,10 @@ function leftAloneReport(mentions) {
|
|
|
31903
31918
|
lines.push(line);
|
|
31904
31919
|
}
|
|
31905
31920
|
};
|
|
31921
|
+
const textOf = new Map;
|
|
31922
|
+
for (const mention of mentions) {
|
|
31923
|
+
textOf.set(`${mention.file}:${String(mention.line)}`, mention.text);
|
|
31924
|
+
}
|
|
31906
31925
|
for (const mention of mentions) {
|
|
31907
31926
|
if (PROSE_KINDS.has(mention.kind) && mention.why === undefined) {
|
|
31908
31927
|
const key = `${mention.file}
|
|
@@ -31924,20 +31943,66 @@ ${mention.kind}`;
|
|
|
31924
31943
|
}
|
|
31925
31944
|
}
|
|
31926
31945
|
const loudFirst = (mention) => mention.kind === "string" && mention.why !== undefined ? 0 : 1;
|
|
31946
|
+
const leftAlone = full.sort((a, b) => loudFirst(a) - loudFirst(b));
|
|
31947
|
+
const inProse = [...prose.values()];
|
|
31948
|
+
const otherSymbols = [...others.entries()].map(([why, files]) => ({
|
|
31949
|
+
why,
|
|
31950
|
+
files: [...files.entries()].map(([file, lines]) => ({ file, lines }))
|
|
31951
|
+
}));
|
|
31952
|
+
const grouped = inProse.reduce((sum, group) => sum + group.lines.length, 0) + otherSymbols.reduce((sum, reason) => sum + reason.files.reduce((n, one) => n + one.lines.length, 0), 0);
|
|
31953
|
+
if (grouped <= LEFT_ALONE_ANSWER_LINES) {
|
|
31954
|
+
return {
|
|
31955
|
+
lists: {
|
|
31956
|
+
leftAlone,
|
|
31957
|
+
...inProse.length > 0 ? { leftAloneInProse: inProse } : {},
|
|
31958
|
+
...otherSymbols.length > 0 ? { leftAloneOtherSymbols: otherSymbols } : {}
|
|
31959
|
+
},
|
|
31960
|
+
counts,
|
|
31961
|
+
detail: null
|
|
31962
|
+
};
|
|
31963
|
+
}
|
|
31964
|
+
const withText = (file, lines) => lines.map((line) => ({ line, text: textOf.get(`${file}:${String(line)}`) ?? "" }));
|
|
31927
31965
|
return {
|
|
31928
31966
|
lists: {
|
|
31929
|
-
leftAlone
|
|
31930
|
-
|
|
31931
|
-
|
|
31932
|
-
|
|
31933
|
-
why,
|
|
31934
|
-
files:
|
|
31967
|
+
leftAlone,
|
|
31968
|
+
leftAloneDetail: {
|
|
31969
|
+
inProse: { files: new Set(inProse.map((group) => group.file)).size, occurrences: counts.inProse },
|
|
31970
|
+
otherSymbols: otherSymbols.map((reason) => ({
|
|
31971
|
+
why: reason.why,
|
|
31972
|
+
files: reason.files.length,
|
|
31973
|
+
occurrences: reason.files.reduce((n, one) => n + one.lines.length, 0)
|
|
31935
31974
|
}))
|
|
31936
|
-
}
|
|
31975
|
+
}
|
|
31937
31976
|
},
|
|
31938
|
-
counts
|
|
31977
|
+
counts,
|
|
31978
|
+
detail: {
|
|
31979
|
+
leftAloneInProse: inProse.map((group) => ({
|
|
31980
|
+
file: group.file,
|
|
31981
|
+
kind: group.kind,
|
|
31982
|
+
lines: withText(group.file, group.lines)
|
|
31983
|
+
})),
|
|
31984
|
+
leftAloneOtherSymbols: otherSymbols.map((reason) => ({
|
|
31985
|
+
why: reason.why,
|
|
31986
|
+
files: reason.files.map((one) => ({ file: one.file, lines: withText(one.file, one.lines) }))
|
|
31987
|
+
}))
|
|
31988
|
+
}
|
|
31939
31989
|
};
|
|
31940
31990
|
}
|
|
31991
|
+
var LEFT_ALONE_ANSWER_LINES = 300;
|
|
31992
|
+
var detailWrites = 0;
|
|
31993
|
+
function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
31994
|
+
const directory = join16(projectPath, ".godot", "gdharness-renames");
|
|
31995
|
+
mkdirSync8(directory, { recursive: true });
|
|
31996
|
+
detailWrites += 1;
|
|
31997
|
+
const path = join16(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
31998
|
+
writeFileSync10(path, `${JSON.stringify(detail, null, 2)}
|
|
31999
|
+
`, "utf8");
|
|
32000
|
+
const kept = readdirSync6(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
32001
|
+
for (const old of kept.slice(10)) {
|
|
32002
|
+
rmSync6(join16(directory, old), { force: true });
|
|
32003
|
+
}
|
|
32004
|
+
return path;
|
|
32005
|
+
}
|
|
31941
32006
|
var PROSE_KINDS = new Set(["comment", "doc", "string", "text"]);
|
|
31942
32007
|
function summaryOf(edits, mentions, rewrites, left) {
|
|
31943
32008
|
const count = (values) => {
|
|
@@ -31957,7 +32022,7 @@ function summaryOf(edits, mentions, rewrites, left) {
|
|
|
31957
32022
|
}
|
|
31958
32023
|
|
|
31959
32024
|
// src/restart-note.ts
|
|
31960
|
-
import { mkdirSync as
|
|
32025
|
+
import { mkdirSync as mkdirSync9, readFileSync as readFileSync16, rmSync as rmSync7, writeFileSync as writeFileSync11 } from "node:fs";
|
|
31961
32026
|
import { dirname as dirname11, join as join17 } from "node:path";
|
|
31962
32027
|
function restartNotePath(projectPath) {
|
|
31963
32028
|
return join17(projectPath, ".godot", "gdharness-restart.json");
|
|
@@ -31965,8 +32030,8 @@ function restartNotePath(projectPath) {
|
|
|
31965
32030
|
function noteRestartBegun(note) {
|
|
31966
32031
|
const path = restartNotePath(note.projectPath);
|
|
31967
32032
|
try {
|
|
31968
|
-
|
|
31969
|
-
|
|
32033
|
+
mkdirSync9(dirname11(path), { recursive: true });
|
|
32034
|
+
writeFileSync11(path, `${JSON.stringify(note, null, 2)}
|
|
31970
32035
|
`, "utf8");
|
|
31971
32036
|
} catch (error) {
|
|
31972
32037
|
console.error(`[SERVER] Could not record the restart at ${path}: ${errorMessage(error)}`);
|
|
@@ -31989,7 +32054,7 @@ function restartOwed(projectPath) {
|
|
|
31989
32054
|
}
|
|
31990
32055
|
function restartSettled(projectPath) {
|
|
31991
32056
|
try {
|
|
31992
|
-
|
|
32057
|
+
rmSync7(restartNotePath(projectPath), { force: true });
|
|
31993
32058
|
} catch (error) {
|
|
31994
32059
|
console.error(`[SERVER] Could not clear the restart note: ${errorMessage(error)}`);
|
|
31995
32060
|
}
|
|
@@ -31999,14 +32064,14 @@ function restartSettled(projectPath) {
|
|
|
31999
32064
|
import { execFile as execFile4 } from "node:child_process";
|
|
32000
32065
|
import { createHash as createHash3 } from "node:crypto";
|
|
32001
32066
|
import {
|
|
32002
|
-
mkdirSync as
|
|
32067
|
+
mkdirSync as mkdirSync10,
|
|
32003
32068
|
openSync as openSync2,
|
|
32004
|
-
readdirSync as
|
|
32069
|
+
readdirSync as readdirSync7,
|
|
32005
32070
|
readFileSync as readFileSync17,
|
|
32006
32071
|
realpathSync as realpathSync6,
|
|
32007
|
-
rmSync as
|
|
32072
|
+
rmSync as rmSync8,
|
|
32008
32073
|
statSync as statSync6,
|
|
32009
|
-
writeFileSync as
|
|
32074
|
+
writeFileSync as writeFileSync12
|
|
32010
32075
|
} from "node:fs";
|
|
32011
32076
|
import { basename as basename4, dirname as dirname12, join as join18, resolve as resolve6 } from "node:path";
|
|
32012
32077
|
import { promisify as promisify4 } from "node:util";
|
|
@@ -32040,7 +32105,7 @@ function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
|
32040
32105
|
}
|
|
32041
32106
|
var SHARED_NOTE = "run.json";
|
|
32042
32107
|
function openTranscript(startedAt) {
|
|
32043
|
-
|
|
32108
|
+
mkdirSync10(runsDirectory(), { recursive: true });
|
|
32044
32109
|
const path = join18(runsDirectory(), `run-${startedAt}.log`);
|
|
32045
32110
|
return { path, fd: openSync2(path, "a") };
|
|
32046
32111
|
}
|
|
@@ -32051,8 +32116,8 @@ function editorNotePath(projectPath, directory = runtimeDirectory()) {
|
|
|
32051
32116
|
return join18(directory, "runs", `editor-${projectKey(projectPath)}.json`);
|
|
32052
32117
|
}
|
|
32053
32118
|
function writeEditorRunNote(note) {
|
|
32054
|
-
|
|
32055
|
-
|
|
32119
|
+
mkdirSync10(runsDirectory(), { recursive: true });
|
|
32120
|
+
writeFileSync12(editorNotePath(note.projectPath), JSON.stringify(note, null, 2), "utf8");
|
|
32056
32121
|
}
|
|
32057
32122
|
function readEditorRunNote(projectPath) {
|
|
32058
32123
|
for (const directory of runtimeDirectories()) {
|
|
@@ -32102,7 +32167,7 @@ function everyRunRecord() {
|
|
|
32102
32167
|
for (const directory of runtimeDirectories()) {
|
|
32103
32168
|
let entries;
|
|
32104
32169
|
try {
|
|
32105
|
-
entries =
|
|
32170
|
+
entries = readdirSync7(join18(directory, "runs"));
|
|
32106
32171
|
} catch {
|
|
32107
32172
|
continue;
|
|
32108
32173
|
}
|
|
@@ -32322,7 +32387,7 @@ function clearRunRecord(projectPath) {
|
|
|
32322
32387
|
const ours = sharedRecord !== null && sharedRecord.projectPath !== "" && isSameDirectory(sharedRecord.projectPath, projectPath);
|
|
32323
32388
|
for (const path of [runRecordPath(projectPath, directory), ...ours ? [shared] : []]) {
|
|
32324
32389
|
try {
|
|
32325
|
-
|
|
32390
|
+
rmSync8(path, { force: true });
|
|
32326
32391
|
} catch {}
|
|
32327
32392
|
}
|
|
32328
32393
|
}
|
|
@@ -32339,7 +32404,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
32339
32404
|
const kept = new Set(everyRunRecord().map((record) => record.transcript));
|
|
32340
32405
|
let entries;
|
|
32341
32406
|
try {
|
|
32342
|
-
entries =
|
|
32407
|
+
entries = readdirSync7(directory);
|
|
32343
32408
|
} catch {
|
|
32344
32409
|
return;
|
|
32345
32410
|
}
|
|
@@ -32352,7 +32417,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
32352
32417
|
continue;
|
|
32353
32418
|
}
|
|
32354
32419
|
try {
|
|
32355
|
-
|
|
32420
|
+
rmSync8(path);
|
|
32356
32421
|
} catch {}
|
|
32357
32422
|
}
|
|
32358
32423
|
}
|
|
@@ -32363,13 +32428,13 @@ import { createHash as createHash4 } from "node:crypto";
|
|
|
32363
32428
|
import {
|
|
32364
32429
|
cpSync,
|
|
32365
32430
|
existsSync as existsSync15,
|
|
32366
|
-
mkdirSync as
|
|
32367
|
-
readdirSync as
|
|
32431
|
+
mkdirSync as mkdirSync11,
|
|
32432
|
+
readdirSync as readdirSync8,
|
|
32368
32433
|
readFileSync as readFileSync18,
|
|
32369
32434
|
rmdirSync,
|
|
32370
|
-
rmSync as
|
|
32435
|
+
rmSync as rmSync9,
|
|
32371
32436
|
statSync as statSync7,
|
|
32372
|
-
writeFileSync as
|
|
32437
|
+
writeFileSync as writeFileSync13
|
|
32373
32438
|
} from "node:fs";
|
|
32374
32439
|
import { dirname as dirname13, join as join19 } from "node:path";
|
|
32375
32440
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
@@ -32391,7 +32456,7 @@ function runtimeAddonDigest(from = shippedAddonsDirectory()) {
|
|
|
32391
32456
|
function addonsDigest(names, from) {
|
|
32392
32457
|
const hash = createHash4("sha256");
|
|
32393
32458
|
const walk = (directory, relative) => {
|
|
32394
|
-
for (const name of
|
|
32459
|
+
for (const name of readdirSync8(directory).sort()) {
|
|
32395
32460
|
if (name === VERSION_MARKER || name === DIGEST_MARKER) {
|
|
32396
32461
|
continue;
|
|
32397
32462
|
}
|
|
@@ -33125,7 +33190,7 @@ var TOOL_SPECS = [
|
|
|
33125
33190
|
},
|
|
33126
33191
|
{
|
|
33127
33192
|
name: "script_edit",
|
|
33128
|
-
description: "Creates a GDScript file, or adds functions, variables and signals to one. Every declaration written carries a type. Both load what they wrote under the project's own warning settings and answer with parses; the engine's reasons for a refusal come back under engine_messages. create answers with the extends and class_name the engine read, and a script declaring a class_name is put in the project's class list, with registered saying whether it is there. modify makes every addition or none, and answers with the line each one is on in the file written. rename renames a class or a member wherever the engine uses it and writes every file or none. A new name already taken is refused with nothing written, a member's included when a class inheriting it already uses the name in code, as a local or a parameter. changed lists each file with its changed lines as they now read, and leftAlone lists every other whole-word occurrence of the old name with what the tokenizer makes of it (code, string, comment, doc, nodePath, scene, text, or ignored for a file under a .gdignore directory, which is never changed) and, where it is not plain, why: a string that is the old name whole, such as find_children's type or a call's method name, which nothing renames or compiles and which is listed first; a node named like the class; a call on a value whose type the analyser could not tell. A member rename answers under languageServer how many requests it made and how long they took. A comment, documentation line, string or text file line with nothing more to say is given under leftAloneInProse by file, kind and line instead, and code that is certainly another symbol of the same name, a bare name in a file where nothing inherits the member or one the language server resolved to another declaration, under leftAloneOtherSymbols as each why with the files and lines it covers. summary.leftAlone counts each of the three lists beside the total. Read both before calling the rename done; nothing in them was changed. Afterwards an open editor scans the changed files and reloads every changed script, which is also the compile check, and its answer comes back under editor; with no editor the class cache is rebuilt and the changed scripts are compiled in a fresh engine, under classCache and compiled. written is true once the files are written, and ok is false when what came after the write failed: the files are left changed, and note says so.",
|
|
33193
|
+
description: "Creates a GDScript file, or adds functions, variables and signals to one. Every declaration written carries a type. Both load what they wrote under the project's own warning settings and answer with parses; the engine's reasons for a refusal come back under engine_messages. create answers with the extends and class_name the engine read, and a script declaring a class_name is put in the project's class list, with registered saying whether it is there. modify makes every addition or none, and answers with the line each one is on in the file written. rename renames a class or a member wherever the engine uses it and writes every file or none. A new name already taken is refused with nothing written, a member's included when a class inheriting it already uses the name in code, as a local or a parameter. changed lists each file with its changed lines as they now read, and leftAlone lists every other whole-word occurrence of the old name with what the tokenizer makes of it (code, string, comment, doc, nodePath, scene, text, or ignored for a file under a .gdignore directory, which is never changed) and, where it is not plain, why: a string that is the old name whole, such as find_children's type or a call's method name, which nothing renames or compiles and which is listed first; a node named like the class; a call on a value whose type the analyser could not tell. A member rename answers under languageServer how many requests it made and how long they took. A comment, documentation line, string or text file line with nothing more to say is given under leftAloneInProse by file, kind and line instead, and code that is certainly another symbol of the same name, a bare name in a file where nothing inherits the member or one the language server resolved to another declaration, under leftAloneOtherSymbols as each why with the files and lines it covers. summary.leftAlone counts each of the three lists beside the total. When those two grouped lists run past 300 lines, they are written with each line's text to a file under the project's .godot directory instead, and leftAloneDetail names the file and counts what is in it. Read both before calling the rename done; nothing in them was changed. Afterwards an open editor scans the changed files and reloads every changed script, which is also the compile check, and its answer comes back under editor; with no editor the class cache is rebuilt and the changed scripts are compiled in a fresh engine, under classCache and compiled. written is true once the files are written, and ok is false when what came after the write failed: the files are left changed, and note says so.",
|
|
33129
33194
|
parameters: {
|
|
33130
33195
|
projectPath: PROJECT_PATH,
|
|
33131
33196
|
scriptPath: SCRIPT_PATH,
|
|
@@ -34411,7 +34476,7 @@ function exportAnswer(ending, asked, before, after) {
|
|
|
34411
34476
|
...problems.omitted === 0 ? {} : { entriesOmitted: problems.omitted }
|
|
34412
34477
|
};
|
|
34413
34478
|
if (verdict.exported) {
|
|
34414
|
-
return { content: [{ type: "text", text:
|
|
34479
|
+
return { content: [{ type: "text", text: answerJson(verdict) }] };
|
|
34415
34480
|
}
|
|
34416
34481
|
const stale = !written && after !== null ? ` The ${asked.outputPath} there is from before this export, which did not write it.` : "";
|
|
34417
34482
|
const why = ending.exitSignal === null ? stale : ` The engine was ${howItEnded(ending)}, so it has no exit code.${stale}`;
|
|
@@ -34421,7 +34486,7 @@ function exportAnswer(ending, asked, before, after) {
|
|
|
34421
34486
|
type: "text",
|
|
34422
34487
|
text: `Export with preset '${asked.preset}' did not produce ${asked.outputPath}.${why}`
|
|
34423
34488
|
},
|
|
34424
|
-
{ type: "text", text:
|
|
34489
|
+
{ type: "text", text: answerJson(verdict) }
|
|
34425
34490
|
],
|
|
34426
34491
|
isError: true
|
|
34427
34492
|
};
|
|
@@ -34568,7 +34633,7 @@ function elsewhereIn(projectPath, asked) {
|
|
|
34568
34633
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
34569
34634
|
let near = [];
|
|
34570
34635
|
try {
|
|
34571
|
-
near =
|
|
34636
|
+
near = readdirSync9(projectPath, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.toLowerCase() !== root).map((entry) => entry.name).filter((name) => name.toLowerCase().startsWith("test") || root.startsWith(name.toLowerCase()));
|
|
34572
34637
|
} catch {
|
|
34573
34638
|
return "";
|
|
34574
34639
|
}
|
|
@@ -34670,7 +34735,7 @@ class GodotServer {
|
|
|
34670
34735
|
return response;
|
|
34671
34736
|
}
|
|
34672
34737
|
jsonTextResponse(payload) {
|
|
34673
|
-
return { content: [{ type: "text", text:
|
|
34738
|
+
return { content: [{ type: "text", text: answerJson(payload) }] };
|
|
34674
34739
|
}
|
|
34675
34740
|
async run() {
|
|
34676
34741
|
const located = await this.locator.find();
|
|
@@ -35003,7 +35068,7 @@ class GodotServer {
|
|
|
35003
35068
|
saying(answer, block) {
|
|
35004
35069
|
return {
|
|
35005
35070
|
...answer,
|
|
35006
|
-
content: [...answer.content, { type: "text", text:
|
|
35071
|
+
content: [...answer.content, { type: "text", text: answerJson(block) }]
|
|
35007
35072
|
};
|
|
35008
35073
|
}
|
|
35009
35074
|
projectHasMovedOn() {
|
|
@@ -35976,7 +36041,7 @@ class GodotServer {
|
|
|
35976
36041
|
if (!outcome.ok) {
|
|
35977
36042
|
const response = this.createErrorResponse(outcome.message);
|
|
35978
36043
|
if (Object.keys(extras).length > 0) {
|
|
35979
|
-
response.content.push({ type: "text", text:
|
|
36044
|
+
response.content.push({ type: "text", text: answerJson(extras) });
|
|
35980
36045
|
}
|
|
35981
36046
|
return response;
|
|
35982
36047
|
}
|
|
@@ -36054,7 +36119,7 @@ class GodotServer {
|
|
|
36054
36119
|
}
|
|
36055
36120
|
const folder = dirname14(output.absolutePath);
|
|
36056
36121
|
try {
|
|
36057
|
-
|
|
36122
|
+
mkdirSync12(folder, { recursive: true });
|
|
36058
36123
|
} catch (error) {
|
|
36059
36124
|
return this.createErrorResponse(`Cannot create ${dirname14(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
36060
36125
|
}
|
|
@@ -36165,7 +36230,7 @@ class GodotServer {
|
|
|
36165
36230
|
let report = null;
|
|
36166
36231
|
let reportProblem = null;
|
|
36167
36232
|
try {
|
|
36168
|
-
const written = existsSync16(reportsDir) ?
|
|
36233
|
+
const written = existsSync16(reportsDir) ? readdirSync9(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
|
|
36169
36234
|
const newest = written.at(-1);
|
|
36170
36235
|
if (newest !== undefined) {
|
|
36171
36236
|
report = parseJUnit(readFileSync19(join20(reportsDir, newest, "results.xml"), "utf8"));
|
|
@@ -38676,6 +38741,15 @@ class GodotServer {
|
|
|
38676
38741
|
const said = outcome.conflicts === undefined ? "" : ` ${outcome.conflicts.join("; ")}.`;
|
|
38677
38742
|
return this.createErrorResponse(`${outcome.reason}${said}`, outcome.advice ?? []);
|
|
38678
38743
|
}
|
|
38744
|
+
if (outcome.detail !== null) {
|
|
38745
|
+
const file = writeLeftAloneDetail(projectPath, symbol, outcome.detail);
|
|
38746
|
+
const folded = asParams(outcome.report["leftAloneDetail"]);
|
|
38747
|
+
outcome.report["leftAloneDetail"] = {
|
|
38748
|
+
file,
|
|
38749
|
+
note: `Too many lines were left alone to list here, and none of them is a use: every comment, documentation line, string and text file line that only shares the word, and every other symbol of the same name, is in ${file} with its text.`,
|
|
38750
|
+
...folded
|
|
38751
|
+
};
|
|
38752
|
+
}
|
|
38679
38753
|
if (readBoolean(args, "preview") === true) {
|
|
38680
38754
|
return this.jsonTextResponse({ preview: true, ...outcome.report });
|
|
38681
38755
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.39",
|
|
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",
|