pi-readseek 0.9.8 → 0.9.10
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/README.md +5 -5
- package/dist/index.ts +1563 -1413
- package/package.json +5 -5
package/dist/index.ts
CHANGED
|
@@ -403,11 +403,6 @@ var TOOL_CONTRACTS = [
|
|
|
403
403
|
];
|
|
404
404
|
// ../readseek-api/src/policy.ts
|
|
405
405
|
var REPLACEABLE_TOOLS = [
|
|
406
|
-
{
|
|
407
|
-
tool: "digest",
|
|
408
|
-
builtIn: "read",
|
|
409
|
-
benefit: "it returns LINE:HASH anchors for safe edits."
|
|
410
|
-
},
|
|
411
406
|
{
|
|
412
407
|
tool: "edit",
|
|
413
408
|
builtIn: "edit",
|
|
@@ -661,6 +656,7 @@ function buildRenameArgs(input) {
|
|
|
661
656
|
return appendGitSelectionArgs(args, input);
|
|
662
657
|
}
|
|
663
658
|
// ../readseek-api/src/platform.ts
|
|
659
|
+
import { accessSync, constants as fsConstants } from "node:fs";
|
|
664
660
|
import { createRequire } from "node:module";
|
|
665
661
|
import path from "node:path";
|
|
666
662
|
var READSEEK_PLATFORM_PACKAGES = {
|
|
@@ -694,6 +690,19 @@ function resolveReadSeekBinaryFromPackages(resolver = resolveFromPackage, option
|
|
|
694
690
|
});
|
|
695
691
|
return path.join(path.dirname(packageJsonPath), "bin", readSeekBinaryName(platform));
|
|
696
692
|
}
|
|
693
|
+
function resolveReadSeekBinaryFromPath(pathEnv = process.env.PATH) {
|
|
694
|
+
const name = readSeekBinaryName();
|
|
695
|
+
for (const dir of (pathEnv ?? "").split(path.delimiter)) {
|
|
696
|
+
if (!dir)
|
|
697
|
+
continue;
|
|
698
|
+
const candidate = path.join(dir, name);
|
|
699
|
+
try {
|
|
700
|
+
accessSync(candidate, fsConstants.X_OK);
|
|
701
|
+
return candidate;
|
|
702
|
+
} catch {}
|
|
703
|
+
}
|
|
704
|
+
return null;
|
|
705
|
+
}
|
|
697
706
|
// ../readseek-api/src/json.ts
|
|
698
707
|
var READSEEK_USAGE_HINT = /\n\s*Run readseek(?: [\w-]+)* --help for more information\.?\s*$/;
|
|
699
708
|
function cleanReadSeekErrorText(stderr) {
|
|
@@ -854,1367 +863,1695 @@ async function runReadSeekNodeText(binaryPath, args, options = {}) {
|
|
|
854
863
|
async function runReadSeekNodeJson(binaryPath, args, options = {}) {
|
|
855
864
|
return parseSuccessfulJson(await runReadSeekNode(binaryPath, args, options));
|
|
856
865
|
}
|
|
857
|
-
// src/
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
const
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
866
|
+
// ../readseek-api/src/client.ts
|
|
867
|
+
function classifyReadSeekFailure(err) {
|
|
868
|
+
const failure = err;
|
|
869
|
+
const message = String(failure?.message ?? err);
|
|
870
|
+
const missing = failure?.code === "ENOENT" || /Cannot find package|Cannot find module|no such file/i.test(message);
|
|
871
|
+
if (missing) {
|
|
872
|
+
return { code: "readseek-not-installed", message, hint: "Run npm install to install @jarkkojs/readseek." };
|
|
873
|
+
}
|
|
874
|
+
return { code: "readseek-execution-error", message };
|
|
864
875
|
}
|
|
865
|
-
function
|
|
866
|
-
|
|
876
|
+
function requireNumber(value, field) {
|
|
877
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value))
|
|
878
|
+
throw new Error(`invalid readseek ${field}: expected safe integer`);
|
|
879
|
+
return value;
|
|
867
880
|
}
|
|
868
|
-
function
|
|
869
|
-
if (
|
|
870
|
-
|
|
871
|
-
return
|
|
881
|
+
function requireString(value, field) {
|
|
882
|
+
if (typeof value !== "string")
|
|
883
|
+
throw new Error(`invalid readseek ${field}`);
|
|
884
|
+
return value;
|
|
872
885
|
}
|
|
873
|
-
function
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
return [name, alias];
|
|
878
|
-
}));
|
|
886
|
+
function requireBoolean(value, field) {
|
|
887
|
+
if (typeof value !== "boolean")
|
|
888
|
+
throw new Error(`invalid readseek ${field}: expected boolean`);
|
|
889
|
+
return value;
|
|
879
890
|
}
|
|
880
|
-
function
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
891
|
+
function optionalString(value, field) {
|
|
892
|
+
if (value === undefined || value === null)
|
|
893
|
+
return;
|
|
894
|
+
return requireString(value, field);
|
|
895
|
+
}
|
|
896
|
+
function parseHashline(value, field) {
|
|
897
|
+
if (!value || typeof value !== "object")
|
|
898
|
+
throw new Error(`invalid readseek ${field}`);
|
|
899
|
+
const item = value;
|
|
888
900
|
return {
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
...preferenceGuideline ? [preferenceGuideline] : [],
|
|
893
|
-
...renderCompactGuidelines(toolName, aliases)
|
|
894
|
-
]
|
|
901
|
+
line: requireNumber(item.line, `${field}.line`),
|
|
902
|
+
hash: requireString(item.hash, `${field}.hash`),
|
|
903
|
+
text: requireString(item.text, `${field}.text`)
|
|
895
904
|
};
|
|
896
905
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
// src/hashline.ts
|
|
902
|
-
import xxhashWasm from "xxhash-wasm";
|
|
903
|
-
var HASH_LEN = 3;
|
|
904
|
-
var RADIX = 16;
|
|
905
|
-
var HASH_MOD = RADIX ** HASH_LEN;
|
|
906
|
-
var DICT = Array.from({ length: HASH_MOD }, (_, i) => i.toString(RADIX).padStart(HASH_LEN, "0"));
|
|
907
|
-
var HASHLINE_STATE_KEY = Symbol.for("pi-readseek.hashlineState.v1");
|
|
908
|
-
function getHashlineState() {
|
|
909
|
-
const globalObject = globalThis;
|
|
910
|
-
const state = globalObject[HASHLINE_STATE_KEY] ?? {
|
|
911
|
-
h32Fn: null,
|
|
912
|
-
initPromise: null
|
|
913
|
-
};
|
|
914
|
-
globalObject[HASHLINE_STATE_KEY] = state;
|
|
915
|
-
return state;
|
|
906
|
+
function parseSearchHashlines(value, field) {
|
|
907
|
+
if (!Array.isArray(value))
|
|
908
|
+
throw new Error(`invalid readseek ${field}`);
|
|
909
|
+
return value.map((line) => parseHashline(line, field));
|
|
916
910
|
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
911
|
+
function parseReadOutput(value) {
|
|
912
|
+
if (!value || typeof value !== "object")
|
|
913
|
+
throw new Error("invalid readseek read output");
|
|
914
|
+
const output = value;
|
|
915
|
+
const hashlines = output.hashlines;
|
|
916
|
+
if (!Array.isArray(hashlines))
|
|
917
|
+
throw new Error("invalid readseek hashlines");
|
|
918
|
+
return {
|
|
919
|
+
file: requireString(output.file, "file"),
|
|
920
|
+
language: requireString(output.language, "language"),
|
|
921
|
+
line_count: requireNumber(output.line_count, "line_count"),
|
|
922
|
+
file_hash: requireString(output.file_hash, "file_hash"),
|
|
923
|
+
start_line: requireNumber(output.start_line, "start_line"),
|
|
924
|
+
end_line: requireNumber(output.end_line, "end_line"),
|
|
925
|
+
hashlines: hashlines.map((line) => parseHashline(line, "hashline"))
|
|
926
|
+
};
|
|
927
927
|
}
|
|
928
|
-
function
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
928
|
+
function parseMapOutput(value) {
|
|
929
|
+
if (!value || typeof value !== "object")
|
|
930
|
+
throw new Error("invalid readseek map output");
|
|
931
|
+
const output = value;
|
|
932
|
+
const symbols = output.symbols;
|
|
933
|
+
if (!Array.isArray(symbols))
|
|
934
|
+
throw new Error("invalid readseek symbols");
|
|
935
|
+
return {
|
|
936
|
+
file: requireString(output.file, "file"),
|
|
937
|
+
language: requireString(output.language, "language"),
|
|
938
|
+
line_count: requireNumber(output.line_count, "line_count"),
|
|
939
|
+
file_hash: requireString(output.file_hash, "file_hash"),
|
|
940
|
+
symbols: symbols.map((symbol) => {
|
|
941
|
+
if (!symbol || typeof symbol !== "object")
|
|
942
|
+
throw new Error("invalid readseek symbol");
|
|
943
|
+
const item = symbol;
|
|
944
|
+
return {
|
|
945
|
+
kind: requireString(item.kind, "symbol.kind"),
|
|
946
|
+
name: requireString(item.name, "symbol.name"),
|
|
947
|
+
qualified_name: requireString(item.qualified_name, "symbol.qualified_name"),
|
|
948
|
+
start_line: requireNumber(item.start_line, "symbol.start_line"),
|
|
949
|
+
end_line: requireNumber(item.end_line, "symbol.end_line"),
|
|
950
|
+
start_hash: requireString(item.start_hash, "symbol.start_hash"),
|
|
951
|
+
end_hash: requireString(item.end_hash, "symbol.end_hash")
|
|
952
|
+
};
|
|
953
|
+
})
|
|
954
|
+
};
|
|
933
955
|
}
|
|
934
|
-
function
|
|
935
|
-
if (
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
956
|
+
function parseSearchOutput(value) {
|
|
957
|
+
if (!value || typeof value !== "object")
|
|
958
|
+
throw new Error("invalid readseek search output");
|
|
959
|
+
const output = value;
|
|
960
|
+
if (!Array.isArray(output.results))
|
|
961
|
+
throw new Error("invalid readseek search results");
|
|
962
|
+
return output.results.map((result) => {
|
|
963
|
+
if (!result || typeof result !== "object")
|
|
964
|
+
throw new Error("invalid readseek search result");
|
|
965
|
+
const file = result;
|
|
966
|
+
if (!Array.isArray(file.matches))
|
|
967
|
+
throw new Error("invalid readseek search matches");
|
|
968
|
+
return {
|
|
969
|
+
file: requireString(file.file, "search.file"),
|
|
970
|
+
language: requireString(file.language, "search.language"),
|
|
971
|
+
file_hash: requireString(file.file_hash, "search.file_hash"),
|
|
972
|
+
matches: file.matches.map((match) => {
|
|
973
|
+
if (!match || typeof match !== "object")
|
|
974
|
+
throw new Error("invalid readseek search match");
|
|
975
|
+
const item = match;
|
|
976
|
+
if (!Array.isArray(item.captures))
|
|
977
|
+
throw new Error("invalid readseek search captures");
|
|
978
|
+
return {
|
|
979
|
+
pattern_index: item.pattern_index === undefined ? 0 : requireNumber(item.pattern_index, "search.match.pattern_index"),
|
|
980
|
+
start_line: requireNumber(item.start_line, "search.match.start_line"),
|
|
981
|
+
end_line: requireNumber(item.end_line, "search.match.end_line"),
|
|
982
|
+
start_hash: requireString(item.start_hash, "search.match.start_hash"),
|
|
983
|
+
end_hash: requireString(item.end_hash, "search.match.end_hash"),
|
|
984
|
+
hashlines: parseSearchHashlines(item.hashlines, "search.match.hashlines"),
|
|
985
|
+
captures: item.captures.map((capture) => {
|
|
986
|
+
if (!capture || typeof capture !== "object")
|
|
987
|
+
throw new Error("invalid readseek search capture");
|
|
988
|
+
const captureItem = capture;
|
|
989
|
+
return {
|
|
990
|
+
name: requireString(captureItem.name, "search.capture.name"),
|
|
991
|
+
start_line: requireNumber(captureItem.start_line, "search.capture.start_line"),
|
|
992
|
+
end_line: requireNumber(captureItem.end_line, "search.capture.end_line"),
|
|
993
|
+
start_hash: requireString(captureItem.start_hash, "search.capture.start_hash"),
|
|
994
|
+
end_hash: requireString(captureItem.end_hash, "search.capture.end_hash"),
|
|
995
|
+
hashlines: parseSearchHashlines(captureItem.hashlines, "search.capture.hashlines")
|
|
996
|
+
};
|
|
997
|
+
})
|
|
998
|
+
};
|
|
999
|
+
})
|
|
1000
|
+
};
|
|
1001
|
+
});
|
|
939
1002
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1003
|
+
function parseRefsOutput(value) {
|
|
1004
|
+
if (!value || typeof value !== "object")
|
|
1005
|
+
throw new Error("invalid readseek refs output");
|
|
1006
|
+
const output = value;
|
|
1007
|
+
if (!Array.isArray(output.references))
|
|
1008
|
+
throw new Error("invalid readseek references");
|
|
1009
|
+
return output.references.map((reference) => {
|
|
1010
|
+
if (!reference || typeof reference !== "object")
|
|
1011
|
+
throw new Error("invalid readseek reference");
|
|
1012
|
+
const item = reference;
|
|
1013
|
+
const symbol = item.symbol;
|
|
1014
|
+
const enclosing = symbol && typeof symbol === "object" ? optionalString(symbol.qualified_name, "reference.symbol.qualified_name") : undefined;
|
|
1015
|
+
return {
|
|
1016
|
+
file: requireString(item.file, "reference.file"),
|
|
1017
|
+
line: requireNumber(item.line, "reference.line"),
|
|
1018
|
+
column: requireNumber(item.column, "reference.column"),
|
|
1019
|
+
line_hash: requireString(item.line_hash, "reference.line_hash"),
|
|
1020
|
+
text: requireString(item.text, "reference.text"),
|
|
1021
|
+
enclosingSymbol: enclosing
|
|
1022
|
+
};
|
|
944
1023
|
});
|
|
945
1024
|
}
|
|
946
|
-
function
|
|
947
|
-
|
|
1025
|
+
function parseDiagnosticKind(value) {
|
|
1026
|
+
if (value === "error" || value === "missing")
|
|
1027
|
+
return value;
|
|
1028
|
+
throw new Error("invalid readseek diagnostic.kind");
|
|
948
1029
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1030
|
+
function parseCheckOutput(value) {
|
|
1031
|
+
if (!value || typeof value !== "object")
|
|
1032
|
+
throw new Error("invalid readseek check output");
|
|
1033
|
+
const output = value;
|
|
1034
|
+
if (!Array.isArray(output.diagnostics))
|
|
1035
|
+
throw new Error("invalid readseek diagnostics");
|
|
1036
|
+
return {
|
|
1037
|
+
errorCount: requireNumber(output.error_count, "error_count"),
|
|
1038
|
+
missingCount: requireNumber(output.missing_count, "missing_count"),
|
|
1039
|
+
diagnostics: output.diagnostics.map((diagnostic) => {
|
|
1040
|
+
if (!diagnostic || typeof diagnostic !== "object")
|
|
1041
|
+
throw new Error("invalid readseek diagnostic");
|
|
1042
|
+
const item = diagnostic;
|
|
1043
|
+
return {
|
|
1044
|
+
kind: parseDiagnosticKind(item.kind),
|
|
1045
|
+
start_line: requireNumber(item.start_line, "diagnostic.start_line"),
|
|
1046
|
+
end_line: requireNumber(item.end_line, "diagnostic.end_line")
|
|
1047
|
+
};
|
|
1048
|
+
})
|
|
1049
|
+
};
|
|
955
1050
|
}
|
|
956
|
-
function
|
|
957
|
-
|
|
1051
|
+
function parseDetectedObjects(value) {
|
|
1052
|
+
if (value === undefined || value === null)
|
|
1053
|
+
return;
|
|
1054
|
+
if (!Array.isArray(value))
|
|
1055
|
+
throw new Error("invalid readseek detect objects");
|
|
1056
|
+
return value.map((object) => {
|
|
1057
|
+
if (!object || typeof object !== "object")
|
|
1058
|
+
throw new Error("invalid readseek detect object");
|
|
1059
|
+
const item = object;
|
|
1060
|
+
const bbox = item.bbox;
|
|
1061
|
+
if (!Array.isArray(bbox) || bbox.length !== 4)
|
|
1062
|
+
throw new Error("invalid readseek detect object.bbox");
|
|
1063
|
+
return {
|
|
1064
|
+
label: requireString(item.label, "object.label"),
|
|
1065
|
+
bbox: bbox.map((n, i) => requireNumber(n, `object.bbox[${i}]`))
|
|
1066
|
+
};
|
|
1067
|
+
});
|
|
958
1068
|
}
|
|
959
|
-
function
|
|
960
|
-
|
|
1069
|
+
function parseImageEncoding(value) {
|
|
1070
|
+
if (value === "base64")
|
|
1071
|
+
return value;
|
|
1072
|
+
throw new Error("invalid image encoding");
|
|
961
1073
|
}
|
|
962
|
-
function
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
const
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
const
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
if (firstChangedLine === undefined)
|
|
981
|
-
firstChangedLine = newLineNum;
|
|
982
|
-
for (const line of raw) {
|
|
983
|
-
if (part.added) {
|
|
984
|
-
output.push(`+${String(newLineNum).padStart(lineNumWidth, " ")} ${line}`);
|
|
985
|
-
newLineNum++;
|
|
986
|
-
} else {
|
|
987
|
-
output.push(`-${String(oldLineNum).padStart(lineNumWidth, " ")} ${line}`);
|
|
988
|
-
oldLineNum++;
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
lastWasChange = true;
|
|
992
|
-
continue;
|
|
993
|
-
}
|
|
994
|
-
const nextPartIsChange = i < parts.length - 1 && (parts[i + 1].added || parts[i + 1].removed);
|
|
995
|
-
if (lastWasChange || nextPartIsChange) {
|
|
996
|
-
let linesToShow = raw;
|
|
997
|
-
let skipStart = 0;
|
|
998
|
-
let skipEnd = 0;
|
|
999
|
-
if (!lastWasChange) {
|
|
1000
|
-
skipStart = Math.max(0, raw.length - contextLines);
|
|
1001
|
-
linesToShow = raw.slice(skipStart);
|
|
1002
|
-
}
|
|
1003
|
-
if (!nextPartIsChange && linesToShow.length > contextLines) {
|
|
1004
|
-
skipEnd = linesToShow.length - contextLines;
|
|
1005
|
-
linesToShow = linesToShow.slice(0, contextLines);
|
|
1006
|
-
}
|
|
1007
|
-
if (skipStart > 0) {
|
|
1008
|
-
output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
|
|
1009
|
-
oldLineNum += skipStart;
|
|
1010
|
-
newLineNum += skipStart;
|
|
1011
|
-
}
|
|
1012
|
-
for (const line of linesToShow) {
|
|
1013
|
-
output.push(` ${String(oldLineNum).padStart(lineNumWidth, " ")} ${line}`);
|
|
1014
|
-
oldLineNum++;
|
|
1015
|
-
newLineNum++;
|
|
1016
|
-
}
|
|
1017
|
-
if (skipEnd > 0) {
|
|
1018
|
-
output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
|
|
1019
|
-
oldLineNum += skipEnd;
|
|
1020
|
-
newLineNum += skipEnd;
|
|
1021
|
-
}
|
|
1022
|
-
} else {
|
|
1023
|
-
oldLineNum += raw.length;
|
|
1024
|
-
newLineNum += raw.length;
|
|
1025
|
-
}
|
|
1026
|
-
lastWasChange = false;
|
|
1074
|
+
function parseDetectOutput(value) {
|
|
1075
|
+
if (!value || typeof value !== "object")
|
|
1076
|
+
throw new Error("invalid readseek detect output");
|
|
1077
|
+
const output = value;
|
|
1078
|
+
const type = requireString(output.type, "type");
|
|
1079
|
+
const file = requireString(output.file, "file");
|
|
1080
|
+
const mime = optionalString(output.mime, "mime");
|
|
1081
|
+
if (type === "application/pdf") {
|
|
1082
|
+
if (output.format !== "pdf")
|
|
1083
|
+
throw new Error("invalid readseek PDF format");
|
|
1084
|
+
return {
|
|
1085
|
+
kind: "pdf",
|
|
1086
|
+
type,
|
|
1087
|
+
file,
|
|
1088
|
+
mime,
|
|
1089
|
+
format: output.format,
|
|
1090
|
+
pages: requireNumber(output.pages, "pages")
|
|
1091
|
+
};
|
|
1027
1092
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
changeCount++;
|
|
1045
|
-
if (changeCount > 1)
|
|
1046
|
-
break;
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
if (changeCount === 1 && changedIndex >= 0) {
|
|
1050
|
-
const lineNum = changedIndex + 1;
|
|
1051
|
-
const oldLine = oldLines[changedIndex] ?? "";
|
|
1052
|
-
const newLine = newLines[changedIndex] ?? "";
|
|
1053
|
-
const oldHash = computeLineHash(oldLine);
|
|
1054
|
-
const newHash = computeLineHash(newLine);
|
|
1055
|
-
return {
|
|
1056
|
-
diff: `${lineNum}:${oldHash}|${oldLine} → ${lineNum}:${newHash}|${newLine}`,
|
|
1057
|
-
firstChangedLine: lineNum
|
|
1058
|
-
};
|
|
1059
|
-
}
|
|
1093
|
+
if (output.width !== undefined || output.height !== undefined) {
|
|
1094
|
+
return {
|
|
1095
|
+
kind: "image",
|
|
1096
|
+
type,
|
|
1097
|
+
file,
|
|
1098
|
+
mime,
|
|
1099
|
+
format: requireString(output.format, "format"),
|
|
1100
|
+
width: requireNumber(output.width, "width"),
|
|
1101
|
+
height: requireNumber(output.height, "height"),
|
|
1102
|
+
animated: requireBoolean(output.animated, "animated"),
|
|
1103
|
+
encoding: output.encoding === undefined ? undefined : parseImageEncoding(output.encoding),
|
|
1104
|
+
data: output.data === undefined ? undefined : requireString(output.data, "data"),
|
|
1105
|
+
ocr: optionalString(output.ocr, "ocr"),
|
|
1106
|
+
caption: optionalString(output.caption, "caption"),
|
|
1107
|
+
objects: parseDetectedObjects(output.objects)
|
|
1108
|
+
};
|
|
1060
1109
|
}
|
|
1061
|
-
if (
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
continue;
|
|
1073
|
-
}
|
|
1074
|
-
failed = true;
|
|
1075
|
-
break;
|
|
1076
|
-
}
|
|
1077
|
-
if (!failed && deletedIndex !== -1 && j === newLines.length) {
|
|
1078
|
-
const lineNum = deletedIndex + 1;
|
|
1079
|
-
const oldLine = oldLines[deletedIndex] ?? "";
|
|
1080
|
-
const oldHash = computeLineHash(oldLine);
|
|
1081
|
-
return {
|
|
1082
|
-
diff: `${lineNum}:${oldHash}|${oldLine} → [deleted]`,
|
|
1083
|
-
firstChangedLine: lineNum
|
|
1084
|
-
};
|
|
1085
|
-
}
|
|
1110
|
+
if (output.language !== undefined) {
|
|
1111
|
+
return {
|
|
1112
|
+
kind: "source",
|
|
1113
|
+
type,
|
|
1114
|
+
file,
|
|
1115
|
+
language: requireString(output.language, "language"),
|
|
1116
|
+
engine: optionalString(output.engine, "engine"),
|
|
1117
|
+
supported: requireBoolean(output.supported, "supported"),
|
|
1118
|
+
mime,
|
|
1119
|
+
syntax: optionalString(output.syntax, "syntax")
|
|
1120
|
+
};
|
|
1086
1121
|
}
|
|
1087
|
-
return
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
// src/path-utils.ts
|
|
1091
|
-
import { homedir } from "node:os";
|
|
1092
|
-
import { isAbsolute, resolve as resolvePath } from "node:path";
|
|
1093
|
-
var UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
|
|
1094
|
-
function normalizeUnicodeSpaces(str) {
|
|
1095
|
-
return str.replace(UNICODE_SPACES, " ");
|
|
1122
|
+
return { kind: "text", type, file, mime };
|
|
1096
1123
|
}
|
|
1097
|
-
function
|
|
1098
|
-
|
|
1124
|
+
function parseDocumentCache(value) {
|
|
1125
|
+
if (!value || typeof value !== "object")
|
|
1126
|
+
throw new Error("invalid readseek document cache");
|
|
1127
|
+
const cache = value;
|
|
1128
|
+
const state = requireString(cache.state, "cache.state");
|
|
1129
|
+
if (state !== "built" && state !== "reused")
|
|
1130
|
+
throw new Error("invalid readseek cache.state");
|
|
1131
|
+
return {
|
|
1132
|
+
content_hash: requireString(cache.content_hash, "cache.content_hash"),
|
|
1133
|
+
state
|
|
1134
|
+
};
|
|
1099
1135
|
}
|
|
1100
|
-
function
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
return
|
|
1136
|
+
function parseIdentifyOutput(value) {
|
|
1137
|
+
if (!value || typeof value !== "object")
|
|
1138
|
+
throw new Error("invalid readseek identify output");
|
|
1139
|
+
const output = value;
|
|
1140
|
+
const identifier = output.identifier;
|
|
1141
|
+
const symbol = output.symbol;
|
|
1142
|
+
return {
|
|
1143
|
+
file: requireString(output.file, "file"),
|
|
1144
|
+
language: requireString(output.language, "language"),
|
|
1145
|
+
engine: optionalString(output.engine, "engine"),
|
|
1146
|
+
line_count: requireNumber(output.line_count, "line_count"),
|
|
1147
|
+
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1148
|
+
line: requireNumber(output.line, "line"),
|
|
1149
|
+
column: requireNumber(output.column, "column"),
|
|
1150
|
+
line_hash: requireString(output.line_hash, "line_hash"),
|
|
1151
|
+
identifier: identifier && typeof identifier === "object" ? {
|
|
1152
|
+
text: requireString(identifier.text, "identifier.text"),
|
|
1153
|
+
start_column: requireNumber(identifier.start_column, "identifier.start_column"),
|
|
1154
|
+
end_column: requireNumber(identifier.end_column, "identifier.end_column"),
|
|
1155
|
+
start_byte: requireNumber(identifier.start_byte, "identifier.start_byte"),
|
|
1156
|
+
end_byte: requireNumber(identifier.end_byte, "identifier.end_byte")
|
|
1157
|
+
} : undefined,
|
|
1158
|
+
symbol: symbol && typeof symbol === "object" ? {
|
|
1159
|
+
name: requireString(symbol.name, "symbol.name"),
|
|
1160
|
+
kind: requireString(symbol.kind, "symbol.kind"),
|
|
1161
|
+
qualified_name: requireString(symbol.qualified_name, "symbol.qualified_name"),
|
|
1162
|
+
start_line: requireNumber(symbol.start_line, "symbol.start_line"),
|
|
1163
|
+
end_line: requireNumber(symbol.end_line, "symbol.end_line")
|
|
1164
|
+
} : undefined
|
|
1165
|
+
};
|
|
1107
1166
|
}
|
|
1108
|
-
function
|
|
1109
|
-
const
|
|
1110
|
-
|
|
1167
|
+
function parseImageContent(value) {
|
|
1168
|
+
const detection = parseDetectOutput(value);
|
|
1169
|
+
if (detection.kind !== "image")
|
|
1170
|
+
throw new Error("invalid readseek image content");
|
|
1171
|
+
return detection;
|
|
1111
1172
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1173
|
+
function parseTextContent(value) {
|
|
1174
|
+
if (!value || typeof value !== "object")
|
|
1175
|
+
throw new Error("invalid readseek content");
|
|
1176
|
+
const output = value;
|
|
1177
|
+
if (output.hashlines !== undefined && output.start_line === undefined) {
|
|
1178
|
+
const hashlines = parseSearchHashlines(output.hashlines, "content.hashlines");
|
|
1179
|
+
const first = hashlines[0];
|
|
1180
|
+
const last = hashlines[hashlines.length - 1];
|
|
1181
|
+
return {
|
|
1182
|
+
file: requireString(output.file, "file"),
|
|
1183
|
+
language: requireString(output.language, "language"),
|
|
1184
|
+
line_count: requireNumber(output.line_count, "line_count"),
|
|
1185
|
+
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1186
|
+
start_line: first?.line ?? 1,
|
|
1187
|
+
end_line: last?.line ?? 0,
|
|
1188
|
+
hashlines
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
return parseReadOutput(value);
|
|
1117
1192
|
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1193
|
+
function parseDigestEnvelope(value) {
|
|
1194
|
+
if (!value || typeof value !== "object")
|
|
1195
|
+
throw new Error("invalid readseek digest output");
|
|
1196
|
+
const output = value;
|
|
1197
|
+
if (output.metadata === undefined)
|
|
1198
|
+
throw new Error("invalid readseek digest metadata");
|
|
1199
|
+
const result = {
|
|
1200
|
+
metadata: parseDetectOutput(output.metadata)
|
|
1201
|
+
};
|
|
1202
|
+
if (output.cache !== undefined)
|
|
1203
|
+
result.cache = parseDocumentCache(output.cache);
|
|
1204
|
+
if (output.content !== undefined)
|
|
1205
|
+
result.content = output.content;
|
|
1206
|
+
if (output.map !== undefined)
|
|
1207
|
+
result.map = parseMapOutput(output.map);
|
|
1208
|
+
if (output.diagnostics !== undefined)
|
|
1209
|
+
result.diagnostics = parseCheckOutput(output.diagnostics);
|
|
1210
|
+
if (output.identity !== undefined)
|
|
1211
|
+
result.identity = parseIdentifyOutput(output.identity);
|
|
1212
|
+
return result;
|
|
1124
1213
|
}
|
|
1125
|
-
function
|
|
1126
|
-
|
|
1214
|
+
function parseEditOutput(value) {
|
|
1215
|
+
if (!value || typeof value !== "object")
|
|
1216
|
+
throw new Error("invalid readseek edit output");
|
|
1217
|
+
const output = value;
|
|
1127
1218
|
return {
|
|
1128
|
-
|
|
1129
|
-
|
|
1219
|
+
file: requireString(output.file, "file"),
|
|
1220
|
+
before_hash: requireString(output.before_hash, "before_hash"),
|
|
1221
|
+
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1222
|
+
plan_hash: requireString(output.plan_hash, "plan_hash"),
|
|
1223
|
+
applied: requireBoolean(output.applied, "applied"),
|
|
1224
|
+
changed: requireBoolean(output.changed, "changed"),
|
|
1225
|
+
edit_count: requireNumber(output.edit_count, "edit_count"),
|
|
1226
|
+
before_content: optionalString(output.before_content, "before_content"),
|
|
1227
|
+
content: optionalString(output.content, "content")
|
|
1130
1228
|
};
|
|
1131
1229
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1230
|
+
function parseRenameConflicts(value, field) {
|
|
1231
|
+
if (value === undefined)
|
|
1232
|
+
return [];
|
|
1233
|
+
if (!Array.isArray(value))
|
|
1234
|
+
throw new Error(`invalid readseek ${field}`);
|
|
1235
|
+
return value.map((item) => {
|
|
1236
|
+
if (!item || typeof item !== "object")
|
|
1237
|
+
throw new Error(`invalid readseek ${field} entry`);
|
|
1238
|
+
const c = item;
|
|
1239
|
+
return {
|
|
1240
|
+
line: requireNumber(c.line, `${field}.line`),
|
|
1241
|
+
column: requireNumber(c.column, `${field}.column`),
|
|
1242
|
+
reason: requireString(c.reason, `${field}.reason`)
|
|
1243
|
+
};
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
function parseRenameEdits(value, field) {
|
|
1247
|
+
if (value === undefined)
|
|
1248
|
+
return [];
|
|
1249
|
+
if (!Array.isArray(value))
|
|
1250
|
+
throw new Error(`invalid readseek ${field} entry`);
|
|
1251
|
+
return value.map((item) => {
|
|
1252
|
+
if (!item || typeof item !== "object")
|
|
1253
|
+
throw new Error(`invalid readseek ${field} entry`);
|
|
1254
|
+
const e = item;
|
|
1255
|
+
return {
|
|
1256
|
+
line: requireNumber(e.line, `${field}.line`),
|
|
1257
|
+
start_column: requireNumber(e.start_column, `${field}.start_column`),
|
|
1258
|
+
end_column: requireNumber(e.end_column, `${field}.end_column`),
|
|
1259
|
+
start_byte: requireNumber(e.start_byte, `${field}.start_byte`),
|
|
1260
|
+
end_byte: requireNumber(e.end_byte, `${field}.end_byte`),
|
|
1261
|
+
occurrence: requireString(e.occurrence, `${field}.occurrence`),
|
|
1262
|
+
line_hash: requireString(e.line_hash, `${field}.line_hash`),
|
|
1263
|
+
text: requireString(e.text, `${field}.text`)
|
|
1264
|
+
};
|
|
1265
|
+
});
|
|
1266
|
+
}
|
|
1267
|
+
function parseRenameOutput(value) {
|
|
1268
|
+
if (!value || typeof value !== "object")
|
|
1269
|
+
throw new Error("invalid readseek rename output");
|
|
1270
|
+
const output = value;
|
|
1271
|
+
const others = output.others;
|
|
1272
|
+
if (others !== undefined && !Array.isArray(others))
|
|
1273
|
+
throw new Error("invalid readseek others");
|
|
1274
|
+
return {
|
|
1275
|
+
file: requireString(output.file, "file"),
|
|
1276
|
+
language: requireString(output.language, "language"),
|
|
1277
|
+
engine: optionalString(output.engine, "engine"),
|
|
1278
|
+
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1279
|
+
old_name: requireString(output.old_name, "old_name"),
|
|
1280
|
+
new_name: requireString(output.new_name, "new_name"),
|
|
1281
|
+
applied: requireBoolean(output.applied, "applied"),
|
|
1282
|
+
conflicts: parseRenameConflicts(output.conflicts, "conflicts"),
|
|
1283
|
+
edits: parseRenameEdits(output.edits, "edits"),
|
|
1284
|
+
others: others?.map((entry) => {
|
|
1285
|
+
if (!entry || typeof entry !== "object")
|
|
1286
|
+
throw new Error("invalid readseek other");
|
|
1287
|
+
const o = entry;
|
|
1288
|
+
return {
|
|
1289
|
+
file: requireString(o.file, "other.file"),
|
|
1290
|
+
language: requireString(o.language, "other.language"),
|
|
1291
|
+
engine: optionalString(o.engine, "other.engine"),
|
|
1292
|
+
file_hash: requireString(o.file_hash, "other.file_hash"),
|
|
1293
|
+
conflicts: parseRenameConflicts(o.conflicts, "other.conflicts"),
|
|
1294
|
+
edits: parseRenameEdits(o.edits, "other.edits")
|
|
1295
|
+
};
|
|
1296
|
+
}) ?? []
|
|
1142
1297
|
};
|
|
1143
|
-
if (!edits)
|
|
1144
|
-
return counts;
|
|
1145
|
-
for (const edit of edits) {
|
|
1146
|
-
counts.total++;
|
|
1147
|
-
if (edit && typeof edit === "object") {
|
|
1148
|
-
for (const key of VARIANT_KEYS) {
|
|
1149
|
-
if (key in edit) {
|
|
1150
|
-
counts[key]++;
|
|
1151
|
-
break;
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
return counts;
|
|
1157
1298
|
}
|
|
1158
|
-
function
|
|
1159
|
-
if (!
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1299
|
+
function parseDefCompact(value) {
|
|
1300
|
+
if (!value || typeof value !== "object")
|
|
1301
|
+
throw new Error("invalid readseek def output");
|
|
1302
|
+
const output = value;
|
|
1303
|
+
const locations = output.locations;
|
|
1304
|
+
if (!Array.isArray(locations))
|
|
1305
|
+
throw new Error("invalid readseek def locations");
|
|
1306
|
+
return locations.map((loc) => {
|
|
1307
|
+
if (!loc || typeof loc !== "object")
|
|
1308
|
+
throw new Error("invalid readseek def location");
|
|
1309
|
+
const item = loc;
|
|
1310
|
+
return {
|
|
1311
|
+
file: requireString(item.file, "location.file"),
|
|
1312
|
+
line: requireNumber(item.line, "location.line"),
|
|
1313
|
+
column: requireNumber(item.column, "location.column"),
|
|
1314
|
+
line_hash: requireString(item.line_hash, "location.line_hash"),
|
|
1315
|
+
text: requireString(item.text, "location.text"),
|
|
1316
|
+
kind: optionalString(item.kind, "location.kind"),
|
|
1317
|
+
name: optionalString(item.name, "location.name"),
|
|
1318
|
+
qualified_name: optionalString(item.qualified_name, "location.qualified_name")
|
|
1319
|
+
};
|
|
1320
|
+
});
|
|
1173
1321
|
}
|
|
1174
|
-
function
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1322
|
+
function createReadSeekClient(runner) {
|
|
1323
|
+
function digestArgs(target, options) {
|
|
1324
|
+
return buildDigestArgs({
|
|
1325
|
+
path: target,
|
|
1326
|
+
select: options.select ?? "content",
|
|
1327
|
+
at: options.at,
|
|
1328
|
+
end: options.end,
|
|
1329
|
+
limit: options.limit,
|
|
1330
|
+
language: options.language,
|
|
1331
|
+
visionMode: options.visionMode,
|
|
1332
|
+
visionLevel: options.visionLevel,
|
|
1333
|
+
stdinName: options.stdin !== undefined && target === "-" ? options.stdinName ?? target : undefined
|
|
1334
|
+
});
|
|
1182
1335
|
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1336
|
+
async function runDigestRaw(target, options = {}) {
|
|
1337
|
+
const args = digestArgs(target, options);
|
|
1338
|
+
const runOptions = {
|
|
1339
|
+
signal: options.signal,
|
|
1340
|
+
stdin: options.stdin,
|
|
1341
|
+
timeoutMs: options.timeoutMs,
|
|
1342
|
+
vision: options.vision
|
|
1343
|
+
};
|
|
1344
|
+
return runner.json(args, runOptions);
|
|
1345
|
+
}
|
|
1346
|
+
async function digest(target, options = {}) {
|
|
1347
|
+
return parseDigestEnvelope(await runDigestRaw(target, options));
|
|
1348
|
+
}
|
|
1349
|
+
function requireDigestContent(envelope, field) {
|
|
1350
|
+
if (envelope.content === undefined)
|
|
1351
|
+
throw new Error(`invalid readseek ${field}: missing content`);
|
|
1352
|
+
return envelope.content;
|
|
1353
|
+
}
|
|
1354
|
+
async function digestContent(filePath, options = {}) {
|
|
1355
|
+
const envelope = await digest(filePath, {
|
|
1356
|
+
select: "content",
|
|
1357
|
+
at: options.startLine !== undefined ? buildLineLocation(options.startLine) : undefined,
|
|
1358
|
+
end: options.endLine,
|
|
1359
|
+
visionMode: options.visionMode,
|
|
1360
|
+
signal: options.signal,
|
|
1361
|
+
vision: options.vision
|
|
1362
|
+
});
|
|
1363
|
+
if (envelope.metadata.kind === "pdf") {
|
|
1364
|
+
return { metadata: envelope.metadata, cache: envelope.cache };
|
|
1188
1365
|
}
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
return { path: rawPath, suffix };
|
|
1193
|
-
}
|
|
1194
|
-
function formatEditResultText(input) {
|
|
1195
|
-
const { isError, diff, warnings, noopEdits, errorText } = input;
|
|
1196
|
-
const isNoOp = isError && (Array.isArray(noopEdits) && noopEdits.length > 0 || errorText.includes("No changes made"));
|
|
1197
|
-
const stats = parseDiffStats(diff);
|
|
1198
|
-
const hasDiffStats = stats.added > 0 || stats.removed > 0;
|
|
1199
|
-
const diffStats = hasDiffStats ? `+${stats.added} / -${stats.removed}` : undefined;
|
|
1200
|
-
let warningsBadge;
|
|
1201
|
-
if (warnings.length === 1) {
|
|
1202
|
-
warningsBadge = "⚠ 1 warning";
|
|
1203
|
-
} else if (warnings.length > 1) {
|
|
1204
|
-
warningsBadge = `⚠ ${warnings.length} warnings`;
|
|
1205
|
-
}
|
|
1206
|
-
const showErrorText = isError ? errorText : undefined;
|
|
1207
|
-
let semanticBadge;
|
|
1208
|
-
if (!isError && !isNoOp && input.semanticClassification) {
|
|
1209
|
-
switch (input.semanticClassification) {
|
|
1210
|
-
case "whitespace-only":
|
|
1211
|
-
semanticBadge = "ws-only";
|
|
1212
|
-
break;
|
|
1213
|
-
case "semantic":
|
|
1214
|
-
semanticBadge = "✓ semantic";
|
|
1215
|
-
break;
|
|
1216
|
-
case "mixed":
|
|
1217
|
-
semanticBadge = "mixed";
|
|
1218
|
-
break;
|
|
1366
|
+
const content = requireDigestContent(envelope, "content");
|
|
1367
|
+
if (envelope.metadata.kind === "image") {
|
|
1368
|
+
return { metadata: envelope.metadata, image: parseImageContent(content) };
|
|
1219
1369
|
}
|
|
1370
|
+
return { metadata: envelope.metadata, text: parseTextContent(content) };
|
|
1220
1371
|
}
|
|
1221
1372
|
return {
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1373
|
+
async view(filePath, options = {}) {
|
|
1374
|
+
const args = buildViewArgs({
|
|
1375
|
+
path: filePath,
|
|
1376
|
+
node: options.node,
|
|
1377
|
+
page: options.page,
|
|
1378
|
+
kind: options.kind,
|
|
1379
|
+
depth: options.depth,
|
|
1380
|
+
outline: options.outline,
|
|
1381
|
+
visionMode: options.visionMode,
|
|
1382
|
+
visionLevel: options.visionLevel
|
|
1383
|
+
});
|
|
1384
|
+
return runner.text(args, {
|
|
1385
|
+
signal: options.signal,
|
|
1386
|
+
vision: options.visionMode !== undefined
|
|
1387
|
+
});
|
|
1388
|
+
},
|
|
1389
|
+
digestRaw(target, options = {}) {
|
|
1390
|
+
return runDigestRaw(target, options);
|
|
1391
|
+
},
|
|
1392
|
+
digest,
|
|
1393
|
+
digestContent,
|
|
1394
|
+
async image(filePath, modes, options = {}) {
|
|
1395
|
+
const requestedModes = modes.length > 1 ? ["all"] : modes;
|
|
1396
|
+
const results = await Promise.allSettled(requestedModes.map(async (mode) => {
|
|
1397
|
+
const digested = await digestContent(filePath, {
|
|
1398
|
+
visionMode: mode,
|
|
1399
|
+
signal: options.signal,
|
|
1400
|
+
vision: true
|
|
1401
|
+
});
|
|
1402
|
+
if (!digested.image)
|
|
1403
|
+
throw new Error(`readseek returned no image analysis for ${filePath}`);
|
|
1404
|
+
return digested.image;
|
|
1405
|
+
}));
|
|
1406
|
+
let merged;
|
|
1407
|
+
for (const result of results) {
|
|
1408
|
+
if (result.status !== "fulfilled")
|
|
1409
|
+
continue;
|
|
1410
|
+
const detection = result.value;
|
|
1411
|
+
merged = merged === undefined ? detection : {
|
|
1412
|
+
...merged,
|
|
1413
|
+
ocr: detection.ocr ?? merged.ocr,
|
|
1414
|
+
caption: detection.caption ?? merged.caption,
|
|
1415
|
+
objects: detection.objects ?? merged.objects
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
if (merged !== undefined)
|
|
1419
|
+
return merged;
|
|
1420
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
1421
|
+
if (failure?.status === "rejected")
|
|
1422
|
+
throw failure.reason;
|
|
1423
|
+
throw new Error(`readseek returned no image analysis for ${filePath}`);
|
|
1424
|
+
},
|
|
1425
|
+
async preparedImage(filePath, options = {}) {
|
|
1426
|
+
const digested = await digestContent(filePath, {
|
|
1427
|
+
visionMode: "none",
|
|
1428
|
+
signal: options.signal
|
|
1429
|
+
});
|
|
1430
|
+
const output = digested.image;
|
|
1431
|
+
if (!output || output.encoding !== "base64" || output.data === undefined || output.mime === undefined) {
|
|
1432
|
+
throw new Error(`readseek returned no prepared image for ${filePath}`);
|
|
1433
|
+
}
|
|
1434
|
+
return { mime: output.mime, encoding: output.encoding, data: output.data };
|
|
1435
|
+
},
|
|
1436
|
+
async search(target, pattern, options = {}) {
|
|
1437
|
+
const args = buildSearchArgs({
|
|
1438
|
+
path: target,
|
|
1439
|
+
pattern,
|
|
1440
|
+
language: options.language,
|
|
1441
|
+
cached: options.cached,
|
|
1442
|
+
others: options.others,
|
|
1443
|
+
ignored: options.ignored
|
|
1444
|
+
});
|
|
1445
|
+
return parseSearchOutput(await runner.json(args, { signal: options.signal }));
|
|
1446
|
+
},
|
|
1447
|
+
async refs(target, options = {}) {
|
|
1448
|
+
const args = buildRefsArgs({
|
|
1449
|
+
path: target,
|
|
1450
|
+
name: options.name,
|
|
1451
|
+
scope: options.scope,
|
|
1452
|
+
line: options.line,
|
|
1453
|
+
column: options.column,
|
|
1454
|
+
language: options.language,
|
|
1455
|
+
cached: options.cached,
|
|
1456
|
+
others: options.others,
|
|
1457
|
+
ignored: options.ignored
|
|
1458
|
+
});
|
|
1459
|
+
return parseRefsOutput(await runner.json(args, { signal: options.signal }));
|
|
1460
|
+
},
|
|
1461
|
+
async def(target, options) {
|
|
1462
|
+
const args = buildDefArgs({
|
|
1463
|
+
path: target,
|
|
1464
|
+
name: options.name,
|
|
1465
|
+
format: "plain",
|
|
1466
|
+
language: options.language,
|
|
1467
|
+
cached: options.cached,
|
|
1468
|
+
others: options.others,
|
|
1469
|
+
ignored: options.ignored
|
|
1470
|
+
});
|
|
1471
|
+
return parseDefCompact(await runner.json(args, { signal: options.signal }));
|
|
1472
|
+
},
|
|
1473
|
+
async check(filePath, content, options = {}) {
|
|
1474
|
+
const envelope = await digest("-", {
|
|
1475
|
+
select: "diagnostics",
|
|
1476
|
+
stdin: content,
|
|
1477
|
+
stdinName: filePath,
|
|
1478
|
+
signal: options.signal
|
|
1479
|
+
});
|
|
1480
|
+
if (!envelope.diagnostics)
|
|
1481
|
+
throw new Error("invalid readseek diagnostics: missing diagnostics facet");
|
|
1482
|
+
return envelope.diagnostics;
|
|
1483
|
+
},
|
|
1484
|
+
async identify(filePath, content, options = {}) {
|
|
1485
|
+
const envelope = await digest("-", {
|
|
1486
|
+
select: "identity",
|
|
1487
|
+
stdin: content,
|
|
1488
|
+
stdinName: filePath,
|
|
1489
|
+
at: options.line !== undefined ? buildLineLocation(options.line, options.column) : undefined,
|
|
1490
|
+
language: options.language,
|
|
1491
|
+
signal: options.signal
|
|
1492
|
+
});
|
|
1493
|
+
if (!envelope.identity)
|
|
1494
|
+
throw new Error("invalid readseek identity: missing identity facet");
|
|
1495
|
+
return envelope.identity;
|
|
1496
|
+
},
|
|
1497
|
+
async detect(filePath, options = {}) {
|
|
1498
|
+
const envelope = await digest(filePath, { select: "metadata", signal: options.signal });
|
|
1499
|
+
return envelope.metadata;
|
|
1500
|
+
},
|
|
1501
|
+
async edit(filePath, edits, options = {}) {
|
|
1502
|
+
const args = buildEditArgs({
|
|
1503
|
+
path: filePath,
|
|
1504
|
+
apply: options.apply,
|
|
1505
|
+
planHash: options.planHash,
|
|
1506
|
+
language: options.language
|
|
1507
|
+
});
|
|
1508
|
+
return parseEditOutput(await runner.json(args, {
|
|
1509
|
+
signal: options.signal,
|
|
1510
|
+
stdin: JSON.stringify({ edits }),
|
|
1511
|
+
timeoutMs: options.timeoutMs,
|
|
1512
|
+
cancelable: options.cancelable
|
|
1513
|
+
}));
|
|
1514
|
+
},
|
|
1515
|
+
async rename(filePath, options) {
|
|
1516
|
+
const args = buildRenameArgs({
|
|
1517
|
+
path: filePath,
|
|
1518
|
+
to: options.to,
|
|
1519
|
+
line: options.line,
|
|
1520
|
+
column: options.column,
|
|
1521
|
+
workspace: options.workspace,
|
|
1522
|
+
apply: options.apply,
|
|
1523
|
+
language: options.language,
|
|
1524
|
+
cached: options.cached,
|
|
1525
|
+
others: options.others,
|
|
1526
|
+
ignored: options.ignored
|
|
1527
|
+
});
|
|
1528
|
+
return parseRenameOutput(await runner.json(args, { signal: options.signal }));
|
|
1529
|
+
}
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
// src/tool-prompt-metadata.ts
|
|
1533
|
+
var REPLACEABLE_BY_TOOL = new Map(REPLACEABLE_TOOLS.map((entry) => [entry.tool, entry]));
|
|
1534
|
+
function toolNameFromPrompt(fileName) {
|
|
1535
|
+
const name = fileName.replace(/\.md$/, "");
|
|
1536
|
+
if (!TOOL_NAMES.includes(name))
|
|
1537
|
+
throw new Error(`unknown ReadSeek tool prompt: ${fileName}`);
|
|
1538
|
+
return name;
|
|
1539
|
+
}
|
|
1540
|
+
function promptFileName(promptUrl) {
|
|
1541
|
+
return promptUrl.pathname.split("/").pop() ?? "";
|
|
1542
|
+
}
|
|
1543
|
+
function rewriteRegisteredToolNames(value, toolAliases) {
|
|
1544
|
+
if (!toolAliases)
|
|
1545
|
+
return value;
|
|
1546
|
+
return Object.entries(toolAliases).reduce((rewritten, [canonicalName, registeredName]) => rewritten.replaceAll(canonicalName, registeredName), value);
|
|
1547
|
+
}
|
|
1548
|
+
function descriptionAliases(toolName, registeredName, toolAliases) {
|
|
1549
|
+
return Object.fromEntries(TOOL_NAMES.map((name) => {
|
|
1550
|
+
const readSeekName = `readSeek_${name}`;
|
|
1551
|
+
const alias = name === toolName && registeredName ? registeredName : toolAliases?.[readSeekName] ?? readSeekName;
|
|
1552
|
+
return [name, alias];
|
|
1553
|
+
}));
|
|
1554
|
+
}
|
|
1555
|
+
function defineToolPromptMetadata(options) {
|
|
1556
|
+
const fileName = promptFileName(options.promptUrl);
|
|
1557
|
+
const toolName = toolNameFromPrompt(fileName);
|
|
1558
|
+
const replaceable = REPLACEABLE_BY_TOOL.get(toolName);
|
|
1559
|
+
const defaultReadSeekName = `readSeek_${toolName}`;
|
|
1560
|
+
const registeredName = options.registeredName ?? (replaceable ? defaultReadSeekName : undefined);
|
|
1561
|
+
const preferenceGuideline = replaceable && registeredName ? registeredName === defaultReadSeekName ? `Prefer ${registeredName} over ${replaceable.builtIn} when both are available; ${replaceable.benefit}` : `Use ${registeredName}; ${replaceable.benefit}` : undefined;
|
|
1562
|
+
const aliases = descriptionAliases(toolName, registeredName, options.toolAliases);
|
|
1563
|
+
return {
|
|
1564
|
+
description: renderToolDescription(toolName, aliases),
|
|
1565
|
+
promptSnippet: rewriteRegisteredToolNames(options.promptSnippet, options.toolAliases),
|
|
1566
|
+
promptGuidelines: [
|
|
1567
|
+
...preferenceGuideline ? [preferenceGuideline] : [],
|
|
1568
|
+
...renderCompactGuidelines(toolName, aliases)
|
|
1569
|
+
]
|
|
1227
1570
|
};
|
|
1228
1571
|
}
|
|
1229
1572
|
|
|
1230
|
-
// src/
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1573
|
+
// src/edit-diff.ts
|
|
1574
|
+
import * as Diff from "diff";
|
|
1575
|
+
|
|
1576
|
+
// src/hashline.ts
|
|
1577
|
+
import xxhashWasm from "xxhash-wasm";
|
|
1578
|
+
var HASH_LEN = 3;
|
|
1579
|
+
var RADIX = 16;
|
|
1580
|
+
var HASH_MOD = RADIX ** HASH_LEN;
|
|
1581
|
+
var DICT = Array.from({ length: HASH_MOD }, (_, i) => i.toString(RADIX).padStart(HASH_LEN, "0"));
|
|
1582
|
+
var HASHLINE_STATE_KEY = Symbol.for("pi-readseek.hashlineState.v1");
|
|
1583
|
+
function getHashlineState() {
|
|
1584
|
+
const globalObject = globalThis;
|
|
1585
|
+
const state = globalObject[HASHLINE_STATE_KEY] ?? {
|
|
1586
|
+
h32Fn: null,
|
|
1587
|
+
initPromise: null
|
|
1238
1588
|
};
|
|
1589
|
+
globalObject[HASHLINE_STATE_KEY] = state;
|
|
1590
|
+
return state;
|
|
1239
1591
|
}
|
|
1240
|
-
function
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
blocks.push(`>>${line.anchor}|${line.display}${lineSuffix?.(line) ?? ""}`);
|
|
1249
|
-
}
|
|
1592
|
+
async function ensureHashInit() {
|
|
1593
|
+
const state = getHashlineState();
|
|
1594
|
+
if (state.h32Fn)
|
|
1595
|
+
return;
|
|
1596
|
+
if (!state.initPromise) {
|
|
1597
|
+
state.initPromise = xxhashWasm().then((hasher) => {
|
|
1598
|
+
state.h32Fn = hasher.h32;
|
|
1599
|
+
});
|
|
1250
1600
|
}
|
|
1251
|
-
|
|
1252
|
-
`);
|
|
1601
|
+
await state.initPromise;
|
|
1253
1602
|
}
|
|
1254
|
-
function
|
|
1255
|
-
|
|
1603
|
+
function xxh32(input) {
|
|
1604
|
+
const state = getHashlineState();
|
|
1605
|
+
if (!state.h32Fn)
|
|
1606
|
+
throw new Error("Hash not initialized — call ensureHashInit() first");
|
|
1607
|
+
return state.h32Fn(input, 0) >>> 0;
|
|
1256
1608
|
}
|
|
1257
|
-
function
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
...details !== undefined ? { details } : {}
|
|
1263
|
-
};
|
|
1609
|
+
function computeLineHash(line) {
|
|
1610
|
+
if (line.endsWith("\r"))
|
|
1611
|
+
line = line.slice(0, -1);
|
|
1612
|
+
line = line.replace(/\s+/g, "");
|
|
1613
|
+
return DICT[xxh32(line) % HASH_MOD];
|
|
1264
1614
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
readSeekValue: {
|
|
1271
|
-
tool,
|
|
1272
|
-
...opts.extra ?? {},
|
|
1273
|
-
ok: false,
|
|
1274
|
-
...opts.path !== undefined ? { path: opts.path } : {},
|
|
1275
|
-
error: buildReadSeekError(code, message, opts.hint, opts.details)
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
};
|
|
1615
|
+
var DISPLAY_CONTROL_CHAR_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f]/g;
|
|
1616
|
+
function escapeControlCharsForDisplay(text) {
|
|
1617
|
+
return text.replace(DISPLAY_CONTROL_CHAR_RE, (ch) => {
|
|
1618
|
+
return `\\u${ch.charCodeAt(0).toString(16).padStart(4, "0")}`;
|
|
1619
|
+
});
|
|
1279
1620
|
}
|
|
1280
|
-
function
|
|
1281
|
-
return {
|
|
1282
|
-
tool: "edit",
|
|
1283
|
-
ok: input.ok ?? true,
|
|
1284
|
-
path: input.path,
|
|
1285
|
-
summary: input.summary,
|
|
1286
|
-
diff: input.diff,
|
|
1287
|
-
...input.diffData ? { diffData: input.diffData } : {},
|
|
1288
|
-
firstChangedLine: input.firstChangedLine,
|
|
1289
|
-
warnings: [...input.warnings],
|
|
1290
|
-
noopEdits: [...input.noopEdits],
|
|
1291
|
-
...input.semanticSummary ? { semanticSummary: input.semanticSummary } : {}
|
|
1292
|
-
};
|
|
1621
|
+
function formatHashlineDisplay(lineNumber, content) {
|
|
1622
|
+
return `${lineNumber}:${computeLineHash(content)}|${escapeControlCharsForDisplay(content)}`;
|
|
1293
1623
|
}
|
|
1294
1624
|
|
|
1295
|
-
// src/edit-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1625
|
+
// src/edit-diff.ts
|
|
1626
|
+
function normalizeToLF(text) {
|
|
1627
|
+
return text.replace(/\r\n/g, `
|
|
1628
|
+
`).replace(/\r/g, `
|
|
1629
|
+
`);
|
|
1299
1630
|
}
|
|
1300
|
-
function
|
|
1301
|
-
|
|
1302
|
-
if (stats.added > 0 || stats.removed > 0)
|
|
1303
|
-
return stats;
|
|
1304
|
-
if (!diff.includes("→"))
|
|
1305
|
-
return stats;
|
|
1306
|
-
if (diff.includes("→ [deleted]"))
|
|
1307
|
-
return { added: 0, removed: 1 };
|
|
1308
|
-
return { added: 1, removed: 1 };
|
|
1631
|
+
function stripBom(content) {
|
|
1632
|
+
return content.startsWith("\uFEFF") ? { bom: "\uFEFF", text: content.slice(1) } : { bom: "", text: content };
|
|
1309
1633
|
}
|
|
1310
|
-
function
|
|
1311
|
-
|
|
1312
|
-
const counts = countEditTypes(edits);
|
|
1313
|
-
const editCount = counts.total || 1;
|
|
1314
|
-
if (counts.total > 0 && counts.insert_after === counts.total && stats.removed > 0 && stats.added === stats.removed + counts.insert_after) {
|
|
1315
|
-
stats = { added: counts.insert_after, removed: 0 };
|
|
1316
|
-
}
|
|
1317
|
-
const changeWord = editCount === 1 ? "change" : "changes";
|
|
1318
|
-
const changedLineCount = Math.max(stats.added, stats.removed);
|
|
1319
|
-
const lineWord = changedLineCount === 1 ? "line" : "lines";
|
|
1320
|
-
return `Edited ${displayPath} (${editCount} ${changeWord}, +${stats.added} -${stats.removed} ${lineWord})`;
|
|
1634
|
+
function hasBareCarriageReturn(content) {
|
|
1635
|
+
return content.replace(/\r\n/g, "").includes("\r");
|
|
1321
1636
|
}
|
|
1322
|
-
function
|
|
1323
|
-
const
|
|
1324
|
-
|
|
1325
|
-
|
|
1637
|
+
function generateDiffString(oldContent, newContent, contextLines = 4) {
|
|
1638
|
+
const parts = Diff.diffLines(oldContent, newContent);
|
|
1639
|
+
const output = [];
|
|
1640
|
+
const maxLineNum = Math.max(oldContent.split(`
|
|
1641
|
+
`).length, newContent.split(`
|
|
1642
|
+
`).length);
|
|
1643
|
+
const lineNumWidth = String(maxLineNum).length;
|
|
1644
|
+
let oldLineNum = 1;
|
|
1645
|
+
let newLineNum = 1;
|
|
1646
|
+
let lastWasChange = false;
|
|
1647
|
+
let firstChangedLine;
|
|
1648
|
+
for (let i = 0;i < parts.length; i++) {
|
|
1649
|
+
const part = parts[i];
|
|
1650
|
+
const raw = part.value.split(`
|
|
1651
|
+
`);
|
|
1652
|
+
if (raw[raw.length - 1] === "")
|
|
1653
|
+
raw.pop();
|
|
1654
|
+
if (part.added || part.removed) {
|
|
1655
|
+
if (firstChangedLine === undefined)
|
|
1656
|
+
firstChangedLine = newLineNum;
|
|
1657
|
+
for (const line of raw) {
|
|
1658
|
+
if (part.added) {
|
|
1659
|
+
output.push(`+${String(newLineNum).padStart(lineNumWidth, " ")} ${line}`);
|
|
1660
|
+
newLineNum++;
|
|
1661
|
+
} else {
|
|
1662
|
+
output.push(`-${String(oldLineNum).padStart(lineNumWidth, " ")} ${line}`);
|
|
1663
|
+
oldLineNum++;
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
lastWasChange = true;
|
|
1326
1667
|
continue;
|
|
1327
|
-
const operations = edit;
|
|
1328
|
-
for (const operationName of EDIT_OPERATION_NAMES) {
|
|
1329
|
-
const operation = operations[operationName];
|
|
1330
|
-
if (hasNewText(operation))
|
|
1331
|
-
values.push(operation.new_text);
|
|
1332
1668
|
}
|
|
1669
|
+
const nextPartIsChange = i < parts.length - 1 && (parts[i + 1].added || parts[i + 1].removed);
|
|
1670
|
+
if (lastWasChange || nextPartIsChange) {
|
|
1671
|
+
let linesToShow = raw;
|
|
1672
|
+
let skipStart = 0;
|
|
1673
|
+
let skipEnd = 0;
|
|
1674
|
+
if (!lastWasChange) {
|
|
1675
|
+
skipStart = Math.max(0, raw.length - contextLines);
|
|
1676
|
+
linesToShow = raw.slice(skipStart);
|
|
1677
|
+
}
|
|
1678
|
+
if (!nextPartIsChange && linesToShow.length > contextLines) {
|
|
1679
|
+
skipEnd = linesToShow.length - contextLines;
|
|
1680
|
+
linesToShow = linesToShow.slice(0, contextLines);
|
|
1681
|
+
}
|
|
1682
|
+
if (skipStart > 0) {
|
|
1683
|
+
output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
|
|
1684
|
+
oldLineNum += skipStart;
|
|
1685
|
+
newLineNum += skipStart;
|
|
1686
|
+
}
|
|
1687
|
+
for (const line of linesToShow) {
|
|
1688
|
+
output.push(` ${String(oldLineNum).padStart(lineNumWidth, " ")} ${line}`);
|
|
1689
|
+
oldLineNum++;
|
|
1690
|
+
newLineNum++;
|
|
1691
|
+
}
|
|
1692
|
+
if (skipEnd > 0) {
|
|
1693
|
+
output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
|
|
1694
|
+
oldLineNum += skipEnd;
|
|
1695
|
+
newLineNum += skipEnd;
|
|
1696
|
+
}
|
|
1697
|
+
} else {
|
|
1698
|
+
oldLineNum += raw.length;
|
|
1699
|
+
newLineNum += raw.length;
|
|
1700
|
+
}
|
|
1701
|
+
lastWasChange = false;
|
|
1333
1702
|
}
|
|
1334
|
-
return
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
return;
|
|
1341
|
-
return "⚠ Edit classified as whitespace-only — if you intended a behavior change, re-read to verify.";
|
|
1342
|
-
}
|
|
1343
|
-
function formatReplaceHint(edits, noopEdits) {
|
|
1344
|
-
if ((noopEdits ?? []).length > 0)
|
|
1345
|
-
return;
|
|
1346
|
-
const counts = countEditTypes(edits);
|
|
1347
|
-
if (counts.replace === 0)
|
|
1348
|
-
return;
|
|
1349
|
-
if (counts.replace !== counts.total)
|
|
1350
|
-
return;
|
|
1351
|
-
return "[info: this edit used replace (unverified). For safer future edits, prefer set_line/replace_lines with an anchor from readSeek_digest/readSeek_grep/readSeek_search.]";
|
|
1352
|
-
}
|
|
1353
|
-
function buildEditOutput(input) {
|
|
1354
|
-
const summary = `Updated ${input.displayPath}`;
|
|
1355
|
-
const visibleSummary = buildVisibleSummary(input.displayPath, input.diff, input.edits);
|
|
1356
|
-
const semanticWarning = formatWhitespaceOnlyWarning(input.semanticSummary, input.edits);
|
|
1357
|
-
const warningText = input.warnings.length ? `
|
|
1358
|
-
|
|
1359
|
-
Warnings:
|
|
1360
|
-
${input.warnings.join(`
|
|
1361
|
-
`)}` : "";
|
|
1362
|
-
const replaceHint = formatReplaceHint(input.edits, input.noopEdits);
|
|
1363
|
-
let text = visibleSummary;
|
|
1364
|
-
if (semanticWarning)
|
|
1365
|
-
text += `
|
|
1366
|
-
${semanticWarning}`;
|
|
1367
|
-
text += warningText;
|
|
1368
|
-
if (replaceHint)
|
|
1369
|
-
text += `
|
|
1370
|
-
${replaceHint}`;
|
|
1371
|
-
return {
|
|
1372
|
-
text,
|
|
1373
|
-
patch: input.patch ?? "",
|
|
1374
|
-
readSeekValue: buildReadSeekEditResult({
|
|
1375
|
-
path: input.path,
|
|
1376
|
-
summary,
|
|
1377
|
-
diff: input.diff,
|
|
1378
|
-
...input.diffData ? { diffData: input.diffData } : {},
|
|
1379
|
-
firstChangedLine: input.firstChangedLine,
|
|
1380
|
-
warnings: input.warnings,
|
|
1381
|
-
noopEdits: input.noopEdits,
|
|
1382
|
-
...input.semanticSummary ? { semanticSummary: input.semanticSummary } : {}
|
|
1383
|
-
})
|
|
1384
|
-
};
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
// src/edit-classify.ts
|
|
1388
|
-
import * as Diff2 from "diff";
|
|
1389
|
-
function classifyEdit(oldContent, newContent) {
|
|
1390
|
-
if (oldContent === newContent) {
|
|
1391
|
-
return { classification: "no-op" };
|
|
1392
|
-
}
|
|
1703
|
+
return { diff: output.join(`
|
|
1704
|
+
`), firstChangedLine };
|
|
1705
|
+
}
|
|
1706
|
+
function generateCompactOrFullDiff(oldContent, newContent, contextLines = 4) {
|
|
1707
|
+
if (oldContent === newContent)
|
|
1708
|
+
return { diff: "", firstChangedLine: undefined };
|
|
1393
1709
|
const oldLines = oldContent.split(`
|
|
1394
1710
|
`);
|
|
1395
1711
|
const newLines = newContent.split(`
|
|
1396
1712
|
`);
|
|
1397
1713
|
if (oldLines.length === newLines.length) {
|
|
1398
|
-
let
|
|
1399
|
-
let
|
|
1714
|
+
let changedIndex = -1;
|
|
1715
|
+
let changeCount = 0;
|
|
1400
1716
|
for (let i = 0;i < oldLines.length; i++) {
|
|
1401
|
-
if (oldLines[i]
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
hasSemanticChange2 = true;
|
|
1717
|
+
if (oldLines[i] !== newLines[i]) {
|
|
1718
|
+
changedIndex = i;
|
|
1719
|
+
changeCount++;
|
|
1720
|
+
if (changeCount > 1)
|
|
1721
|
+
break;
|
|
1407
1722
|
}
|
|
1408
1723
|
}
|
|
1409
|
-
if (
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1724
|
+
if (changeCount === 1 && changedIndex >= 0) {
|
|
1725
|
+
const lineNum = changedIndex + 1;
|
|
1726
|
+
const oldLine = oldLines[changedIndex] ?? "";
|
|
1727
|
+
const newLine = newLines[changedIndex] ?? "";
|
|
1728
|
+
const oldHash = computeLineHash(oldLine);
|
|
1729
|
+
const newHash = computeLineHash(newLine);
|
|
1730
|
+
return {
|
|
1731
|
+
diff: `${lineNum}:${oldHash}|${oldLine} → ${lineNum}:${newHash}|${newLine}`,
|
|
1732
|
+
firstChangedLine: lineNum
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1414
1735
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
hasWhitespaceChange = true;
|
|
1428
|
-
} else {
|
|
1429
|
-
hasSemanticChange = true;
|
|
1736
|
+
if (oldLines.length === newLines.length + 1) {
|
|
1737
|
+
let deletedIndex = -1;
|
|
1738
|
+
let j = 0;
|
|
1739
|
+
let failed = false;
|
|
1740
|
+
for (let i = 0;i < oldLines.length; i++) {
|
|
1741
|
+
if (j < newLines.length && oldLines[i] === newLines[j]) {
|
|
1742
|
+
j++;
|
|
1743
|
+
continue;
|
|
1744
|
+
}
|
|
1745
|
+
if (deletedIndex === -1) {
|
|
1746
|
+
deletedIndex = i;
|
|
1747
|
+
continue;
|
|
1430
1748
|
}
|
|
1749
|
+
failed = true;
|
|
1750
|
+
break;
|
|
1751
|
+
}
|
|
1752
|
+
if (!failed && deletedIndex !== -1 && j === newLines.length) {
|
|
1753
|
+
const lineNum = deletedIndex + 1;
|
|
1754
|
+
const oldLine = oldLines[deletedIndex] ?? "";
|
|
1755
|
+
const oldHash = computeLineHash(oldLine);
|
|
1756
|
+
return {
|
|
1757
|
+
diff: `${lineNum}:${oldHash}|${oldLine} → [deleted]`,
|
|
1758
|
+
firstChangedLine: lineNum
|
|
1759
|
+
};
|
|
1431
1760
|
}
|
|
1432
1761
|
}
|
|
1433
|
-
|
|
1434
|
-
return { classification: "mixed" };
|
|
1435
|
-
if (hasWhitespaceChange)
|
|
1436
|
-
return { classification: "whitespace-only" };
|
|
1437
|
-
return { classification: "semantic" };
|
|
1762
|
+
return generateDiffString(oldContent, newContent, contextLines);
|
|
1438
1763
|
}
|
|
1439
1764
|
|
|
1440
|
-
// src/
|
|
1441
|
-
import {
|
|
1442
|
-
import {
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
// src/readseek/enums.ts
|
|
1447
|
-
var SymbolKind;
|
|
1448
|
-
((SymbolKind2) => {
|
|
1449
|
-
SymbolKind2["Class"] = "class";
|
|
1450
|
-
SymbolKind2["Function"] = "function";
|
|
1451
|
-
SymbolKind2["Method"] = "method";
|
|
1452
|
-
SymbolKind2["Variable"] = "variable";
|
|
1453
|
-
SymbolKind2["Constant"] = "constant";
|
|
1454
|
-
SymbolKind2["Interface"] = "interface";
|
|
1455
|
-
SymbolKind2["Type"] = "type";
|
|
1456
|
-
SymbolKind2["Enum"] = "enum";
|
|
1457
|
-
SymbolKind2["Struct"] = "struct";
|
|
1458
|
-
SymbolKind2["Import"] = "import";
|
|
1459
|
-
SymbolKind2["Module"] = "module";
|
|
1460
|
-
SymbolKind2["Namespace"] = "namespace";
|
|
1461
|
-
SymbolKind2["Property"] = "property";
|
|
1462
|
-
SymbolKind2["Heading"] = "heading";
|
|
1463
|
-
SymbolKind2["Table"] = "table";
|
|
1464
|
-
SymbolKind2["View"] = "view";
|
|
1465
|
-
SymbolKind2["Procedure"] = "procedure";
|
|
1466
|
-
SymbolKind2["Trigger"] = "trigger";
|
|
1467
|
-
SymbolKind2["Index"] = "index";
|
|
1468
|
-
SymbolKind2["Schema"] = "schema";
|
|
1469
|
-
SymbolKind2["Signal"] = "signal";
|
|
1470
|
-
SymbolKind2["Unknown"] = "unknown";
|
|
1471
|
-
})(SymbolKind ||= {});
|
|
1472
|
-
|
|
1473
|
-
// src/readseek-settings.ts
|
|
1474
|
-
import { readFileSync, statSync } from "node:fs";
|
|
1475
|
-
import { homedir as homedir2 } from "node:os";
|
|
1476
|
-
import { join } from "node:path";
|
|
1477
|
-
var READSEEK_TOOL_REPLACEMENTS = {
|
|
1478
|
-
read: "readSeek_digest",
|
|
1479
|
-
edit: "readSeek_edit",
|
|
1480
|
-
grep: "readSeek_grep",
|
|
1481
|
-
write: "readSeek_write"
|
|
1482
|
-
};
|
|
1483
|
-
var READSEEK_KEYS = ["overrideTools", "imageMode", "syntaxValidation", "timeoutMs", "grep", "display"];
|
|
1484
|
-
var READSEEK_GREP_KEYS = ["maxLines", "maxBytes"];
|
|
1485
|
-
var READSEEK_DISPLAY_KEYS = ["read", "grep", "edit", "write"];
|
|
1486
|
-
var DEFAULT_DISPLAY_MODES = {
|
|
1487
|
-
read: "compact",
|
|
1488
|
-
grep: "compact",
|
|
1489
|
-
edit: "expanded",
|
|
1490
|
-
write: "expanded"
|
|
1491
|
-
};
|
|
1492
|
-
function globalSettingsPath() {
|
|
1493
|
-
return join(homedir2(), ".pi/agent/settings.json");
|
|
1494
|
-
}
|
|
1495
|
-
function projectSettingsPath() {
|
|
1496
|
-
return join(process.cwd(), ".pi/settings.json");
|
|
1765
|
+
// src/path-utils.ts
|
|
1766
|
+
import { homedir } from "node:os";
|
|
1767
|
+
import { isAbsolute, resolve as resolvePath } from "node:path";
|
|
1768
|
+
var UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
|
|
1769
|
+
function normalizeUnicodeSpaces(str) {
|
|
1770
|
+
return str.replace(UNICODE_SPACES, " ");
|
|
1497
1771
|
}
|
|
1498
|
-
function
|
|
1499
|
-
return
|
|
1772
|
+
function normalizeAtPrefix(filePath) {
|
|
1773
|
+
return filePath.startsWith("@") ? filePath.slice(1) : filePath;
|
|
1500
1774
|
}
|
|
1501
|
-
function
|
|
1502
|
-
|
|
1775
|
+
function expandPath(filePath) {
|
|
1776
|
+
const normalized = normalizeUnicodeSpaces(normalizeAtPrefix(filePath));
|
|
1777
|
+
if (normalized === "~")
|
|
1778
|
+
return homedir();
|
|
1779
|
+
if (normalized.startsWith("~/"))
|
|
1780
|
+
return homedir() + normalized.slice(1);
|
|
1781
|
+
return normalized;
|
|
1503
1782
|
}
|
|
1504
|
-
function
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
continue;
|
|
1508
|
-
const path2 = `${prefix}.${key}`;
|
|
1509
|
-
warnings.push({ source, path: path2, message: `Unknown readseek setting at ${path2}` });
|
|
1510
|
-
}
|
|
1783
|
+
function resolveToCwd(filePath, cwd) {
|
|
1784
|
+
const expanded = expandPath(filePath);
|
|
1785
|
+
return isAbsolute(expanded) ? expanded : resolvePath(cwd, expanded);
|
|
1511
1786
|
}
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
return val;
|
|
1518
|
-
warnings.push(invalid(source, path2));
|
|
1519
|
-
return;
|
|
1787
|
+
|
|
1788
|
+
// src/runtime.ts
|
|
1789
|
+
function throwIfAborted(signal) {
|
|
1790
|
+
if (signal?.aborted)
|
|
1791
|
+
throw new Error("Operation aborted");
|
|
1520
1792
|
}
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
warnings.push(invalid(source, path2));
|
|
1528
|
-
return;
|
|
1793
|
+
|
|
1794
|
+
// src/fs-error.ts
|
|
1795
|
+
var SYSLOG_PATH_SUFFIX = /,\s+[a-z]+(\s+'.*')?$/;
|
|
1796
|
+
function strerror(err) {
|
|
1797
|
+
const raw = String(err?.message ?? String(err));
|
|
1798
|
+
return raw.replace(SYSLOG_PATH_SUFFIX, "");
|
|
1529
1799
|
}
|
|
1530
|
-
function
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
warnings.push(invalid(source, "readseek.imageMode"));
|
|
1537
|
-
return;
|
|
1800
|
+
function formatFsError(err, domain) {
|
|
1801
|
+
const errno = typeof err.code === "string" ? err.code : "EIO";
|
|
1802
|
+
return {
|
|
1803
|
+
code: errno,
|
|
1804
|
+
message: `${domain}: ${errno}: ${strerror(err)}`
|
|
1805
|
+
};
|
|
1538
1806
|
}
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1807
|
+
|
|
1808
|
+
// src/edit-render-helpers.ts
|
|
1809
|
+
var VARIANT_KEYS = ["set_line", "replace_lines", "insert_after", "replace"];
|
|
1810
|
+
function countEditTypes(edits) {
|
|
1811
|
+
const counts = {
|
|
1812
|
+
set_line: 0,
|
|
1813
|
+
replace_lines: 0,
|
|
1814
|
+
insert_after: 0,
|
|
1815
|
+
replace: 0,
|
|
1816
|
+
total: 0
|
|
1817
|
+
};
|
|
1818
|
+
if (!edits)
|
|
1819
|
+
return counts;
|
|
1820
|
+
for (const edit of edits) {
|
|
1821
|
+
counts.total++;
|
|
1822
|
+
if (edit && typeof edit === "object") {
|
|
1823
|
+
for (const key of VARIANT_KEYS) {
|
|
1824
|
+
if (key in edit) {
|
|
1825
|
+
counts[key]++;
|
|
1826
|
+
break;
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1553
1829
|
}
|
|
1554
|
-
});
|
|
1555
|
-
return tools;
|
|
1556
|
-
}
|
|
1557
|
-
function readDisplaySettings(raw, source, warnings) {
|
|
1558
|
-
if (!("display" in raw))
|
|
1559
|
-
return;
|
|
1560
|
-
if (!isRecord(raw.display)) {
|
|
1561
|
-
warnings.push(invalid(source, "readseek.display"));
|
|
1562
|
-
return;
|
|
1563
1830
|
}
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1831
|
+
return counts;
|
|
1832
|
+
}
|
|
1833
|
+
function parseDiffStats(diff) {
|
|
1834
|
+
if (!diff)
|
|
1835
|
+
return { added: 0, removed: 0 };
|
|
1836
|
+
let added = 0;
|
|
1837
|
+
let removed = 0;
|
|
1838
|
+
for (const line of diff.split(`
|
|
1839
|
+
`)) {
|
|
1840
|
+
if (line.startsWith("+++") || line.startsWith("---"))
|
|
1841
|
+
continue;
|
|
1842
|
+
if (line.startsWith("+"))
|
|
1843
|
+
added++;
|
|
1844
|
+
else if (line.startsWith("-"))
|
|
1845
|
+
removed++;
|
|
1570
1846
|
}
|
|
1571
|
-
return
|
|
1847
|
+
return { added, removed };
|
|
1572
1848
|
}
|
|
1573
|
-
function
|
|
1574
|
-
const
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
warnings.push({ source, message: "Invalid readseek settings: expected a JSON object" });
|
|
1578
|
-
return { settings, warnings };
|
|
1849
|
+
function formatEditCallText(args, argsComplete) {
|
|
1850
|
+
const rawPath = typeof args?.path === "string" ? args.path : null;
|
|
1851
|
+
if (!argsComplete) {
|
|
1852
|
+
return { path: rawPath, suffix: undefined };
|
|
1579
1853
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1854
|
+
const edits = args?.edits;
|
|
1855
|
+
if (!Array.isArray(edits) || edits.length === 0) {
|
|
1856
|
+
return { path: rawPath, suffix: undefined };
|
|
1857
|
+
}
|
|
1858
|
+
const counts = countEditTypes(edits);
|
|
1859
|
+
const parts = [];
|
|
1860
|
+
for (const key of VARIANT_KEYS) {
|
|
1861
|
+
if (counts[key] > 0) {
|
|
1862
|
+
parts.push(`${counts[key]} ${key}`);
|
|
1588
1863
|
}
|
|
1589
|
-
return { settings, warnings };
|
|
1590
1864
|
}
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1865
|
+
const word = counts.total === 1 ? "edit" : "edits";
|
|
1866
|
+
const suffix = `${counts.total} ${word} (${parts.join(", ")})`;
|
|
1867
|
+
return { path: rawPath, suffix };
|
|
1868
|
+
}
|
|
1869
|
+
function formatEditResultText(input) {
|
|
1870
|
+
const { isError, diff, warnings, noopEdits, errorText } = input;
|
|
1871
|
+
const isNoOp = isError && (Array.isArray(noopEdits) && noopEdits.length > 0 || errorText.includes("No changes made"));
|
|
1872
|
+
const stats = parseDiffStats(diff);
|
|
1873
|
+
const hasDiffStats = stats.added > 0 || stats.removed > 0;
|
|
1874
|
+
const diffStats = hasDiffStats ? `+${stats.added} / -${stats.removed}` : undefined;
|
|
1875
|
+
let warningsBadge;
|
|
1876
|
+
if (warnings.length === 1) {
|
|
1877
|
+
warningsBadge = "⚠ 1 warning";
|
|
1878
|
+
} else if (warnings.length > 1) {
|
|
1879
|
+
warningsBadge = `⚠ ${warnings.length} warnings`;
|
|
1594
1880
|
}
|
|
1595
|
-
const
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
settings.timeoutMs = timeoutMs;
|
|
1609
|
-
if ("grep" in section) {
|
|
1610
|
-
if (isRecord(section.grep)) {
|
|
1611
|
-
warnUnknownKeys(section.grep, READSEEK_GREP_KEYS, "readseek.grep", source, warnings);
|
|
1612
|
-
const grep = {};
|
|
1613
|
-
const maxLines = readPositive(section.grep, "maxLines", "readseek.grep.maxLines", source, warnings);
|
|
1614
|
-
if (maxLines !== undefined)
|
|
1615
|
-
grep.maxLines = maxLines;
|
|
1616
|
-
const maxBytes = readPositive(section.grep, "maxBytes", "readseek.grep.maxBytes", source, warnings);
|
|
1617
|
-
if (maxBytes !== undefined)
|
|
1618
|
-
grep.maxBytes = maxBytes;
|
|
1619
|
-
if (Object.keys(grep).length > 0)
|
|
1620
|
-
settings.grep = grep;
|
|
1621
|
-
} else {
|
|
1622
|
-
warnings.push(invalid(source, "readseek.grep"));
|
|
1881
|
+
const showErrorText = isError ? errorText : undefined;
|
|
1882
|
+
let semanticBadge;
|
|
1883
|
+
if (!isError && !isNoOp && input.semanticClassification) {
|
|
1884
|
+
switch (input.semanticClassification) {
|
|
1885
|
+
case "whitespace-only":
|
|
1886
|
+
semanticBadge = "ws-only";
|
|
1887
|
+
break;
|
|
1888
|
+
case "semantic":
|
|
1889
|
+
semanticBadge = "✓ semantic";
|
|
1890
|
+
break;
|
|
1891
|
+
case "mixed":
|
|
1892
|
+
semanticBadge = "mixed";
|
|
1893
|
+
break;
|
|
1623
1894
|
}
|
|
1624
1895
|
}
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1896
|
+
return {
|
|
1897
|
+
diffStats,
|
|
1898
|
+
noOp: isNoOp,
|
|
1899
|
+
warningsBadge,
|
|
1900
|
+
errorText: showErrorText,
|
|
1901
|
+
semanticBadge
|
|
1902
|
+
};
|
|
1629
1903
|
}
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1904
|
+
|
|
1905
|
+
// src/readseek-value.ts
|
|
1906
|
+
function buildReadSeekLineWithHash(line, hash, raw) {
|
|
1907
|
+
return {
|
|
1908
|
+
line,
|
|
1909
|
+
hash,
|
|
1910
|
+
anchor: `${line}:${hash}`,
|
|
1911
|
+
raw,
|
|
1912
|
+
display: escapeControlCharsForDisplay(raw)
|
|
1913
|
+
};
|
|
1637
1914
|
}
|
|
1638
|
-
function
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
try {
|
|
1649
|
-
const text = readFileSync(path2, "utf8");
|
|
1650
|
-
result = validateSettings(JSON.parse(text), path2);
|
|
1651
|
-
} catch (error) {
|
|
1652
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1653
|
-
result = { settings: {}, warnings: [{ source: path2, message: `Invalid JSON: ${message}` }] };
|
|
1915
|
+
function buildReadSeekLine(line, raw) {
|
|
1916
|
+
return buildReadSeekLineWithHash(line, computeLineHash(raw), raw);
|
|
1917
|
+
}
|
|
1918
|
+
function formatAnchoredFileBlocks(files, lineSuffix) {
|
|
1919
|
+
const blocks = [];
|
|
1920
|
+
for (const file of files) {
|
|
1921
|
+
blocks.push(`--- ${file.displayPath} ---`);
|
|
1922
|
+
for (const line of file.lines) {
|
|
1923
|
+
blocks.push(`>>${line.anchor}|${line.display}${lineSuffix?.(line) ?? ""}`);
|
|
1924
|
+
}
|
|
1654
1925
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1926
|
+
return blocks.join(`
|
|
1927
|
+
`);
|
|
1657
1928
|
}
|
|
1658
|
-
function
|
|
1659
|
-
|
|
1660
|
-
const grep = { ...base.grep ?? {}, ...override.grep ?? {} };
|
|
1661
|
-
if (Object.keys(grep).length > 0)
|
|
1662
|
-
settings.grep = grep;
|
|
1663
|
-
const display = { ...base.display ?? {}, ...override.display ?? {} };
|
|
1664
|
-
if (Object.keys(display).length > 0)
|
|
1665
|
-
settings.display = display;
|
|
1666
|
-
return settings;
|
|
1929
|
+
function buildReadSeekWarning(code, message, metadata = {}) {
|
|
1930
|
+
return { code, message, ...metadata };
|
|
1667
1931
|
}
|
|
1668
|
-
function
|
|
1669
|
-
const globalResult = readSettingsFile(globalSettingsPath());
|
|
1670
|
-
const projectResult = readSettingsFile(projectSettingsPath());
|
|
1932
|
+
function buildReadSeekError(code, message, hint, details) {
|
|
1671
1933
|
return {
|
|
1672
|
-
|
|
1673
|
-
|
|
1934
|
+
code,
|
|
1935
|
+
message,
|
|
1936
|
+
...hint !== undefined ? { hint } : {},
|
|
1937
|
+
...details !== undefined ? { details } : {}
|
|
1674
1938
|
};
|
|
1675
1939
|
}
|
|
1676
|
-
function
|
|
1677
|
-
return
|
|
1678
|
-
}
|
|
1679
|
-
|
|
1680
|
-
|
|
1940
|
+
function buildToolErrorResult(tool, code, message, opts = {}) {
|
|
1941
|
+
return {
|
|
1942
|
+
content: [{ type: "text", text: message }],
|
|
1943
|
+
isError: true,
|
|
1944
|
+
details: {
|
|
1945
|
+
readSeekValue: {
|
|
1946
|
+
tool,
|
|
1947
|
+
...opts.extra ?? {},
|
|
1948
|
+
ok: false,
|
|
1949
|
+
...opts.path !== undefined ? { path: opts.path } : {},
|
|
1950
|
+
error: buildReadSeekError(code, message, opts.hint, opts.details)
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
};
|
|
1681
1954
|
}
|
|
1682
|
-
function
|
|
1683
|
-
return
|
|
1955
|
+
function buildReadSeekEditResult(input) {
|
|
1956
|
+
return {
|
|
1957
|
+
tool: "edit",
|
|
1958
|
+
ok: input.ok ?? true,
|
|
1959
|
+
path: input.path,
|
|
1960
|
+
summary: input.summary,
|
|
1961
|
+
diff: input.diff,
|
|
1962
|
+
...input.diffData ? { diffData: input.diffData } : {},
|
|
1963
|
+
firstChangedLine: input.firstChangedLine,
|
|
1964
|
+
warnings: [...input.warnings],
|
|
1965
|
+
noopEdits: [...input.noopEdits],
|
|
1966
|
+
...input.semanticSummary ? { semanticSummary: input.semanticSummary } : {}
|
|
1967
|
+
};
|
|
1684
1968
|
}
|
|
1685
1969
|
|
|
1686
|
-
// src/
|
|
1687
|
-
var
|
|
1688
|
-
function
|
|
1689
|
-
return
|
|
1690
|
-
}
|
|
1691
|
-
function normalizeKind(kind) {
|
|
1692
|
-
if (kind === "constructor")
|
|
1693
|
-
return "method" /* Method */;
|
|
1694
|
-
if (Object.values(SymbolKind).includes(kind))
|
|
1695
|
-
return kind;
|
|
1696
|
-
return "unknown" /* Unknown */;
|
|
1697
|
-
}
|
|
1698
|
-
function parentQualifiedNameFor(qualifiedName) {
|
|
1699
|
-
const lastDot = qualifiedName.lastIndexOf(".");
|
|
1700
|
-
return lastDot === -1 ? "" : qualifiedName.slice(0, lastDot);
|
|
1970
|
+
// src/edit-output.ts
|
|
1971
|
+
var EDIT_OPERATION_NAMES = ["set_line", "replace_lines", "insert_after", "replace"];
|
|
1972
|
+
function hasNewText(value) {
|
|
1973
|
+
return typeof value === "object" && value !== null && typeof value.new_text === "string";
|
|
1701
1974
|
}
|
|
1702
|
-
function
|
|
1703
|
-
const
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
entries.push({ parentQualifiedName, symbol: fileSymbol });
|
|
1975
|
+
function getVisibleDiffStats(diff) {
|
|
1976
|
+
const stats = parseDiffStats(diff);
|
|
1977
|
+
if (stats.added > 0 || stats.removed > 0)
|
|
1978
|
+
return stats;
|
|
1979
|
+
if (!diff.includes("→"))
|
|
1980
|
+
return stats;
|
|
1981
|
+
if (diff.includes("→ [deleted]"))
|
|
1982
|
+
return { added: 0, removed: 1 };
|
|
1983
|
+
return { added: 1, removed: 1 };
|
|
1984
|
+
}
|
|
1985
|
+
function buildVisibleSummary(displayPath, diff, edits) {
|
|
1986
|
+
let stats = getVisibleDiffStats(diff);
|
|
1987
|
+
const counts = countEditTypes(edits);
|
|
1988
|
+
const editCount = counts.total || 1;
|
|
1989
|
+
if (counts.total > 0 && counts.insert_after === counts.total && stats.removed > 0 && stats.added === stats.removed + counts.insert_after) {
|
|
1990
|
+
stats = { added: counts.insert_after, removed: 0 };
|
|
1719
1991
|
}
|
|
1720
|
-
const
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1992
|
+
const changeWord = editCount === 1 ? "change" : "changes";
|
|
1993
|
+
const changedLineCount = Math.max(stats.added, stats.removed);
|
|
1994
|
+
const lineWord = changedLineCount === 1 ? "line" : "lines";
|
|
1995
|
+
return `Edited ${displayPath} (${editCount} ${changeWord}, +${stats.added} -${stats.removed} ${lineWord})`;
|
|
1996
|
+
}
|
|
1997
|
+
function extractNewTextValues(edits) {
|
|
1998
|
+
const values = [];
|
|
1999
|
+
for (const edit of edits ?? []) {
|
|
2000
|
+
if (!edit || typeof edit !== "object")
|
|
1725
2001
|
continue;
|
|
2002
|
+
const operations = edit;
|
|
2003
|
+
for (const operationName of EDIT_OPERATION_NAMES) {
|
|
2004
|
+
const operation = operations[operationName];
|
|
2005
|
+
if (hasNewText(operation))
|
|
2006
|
+
values.push(operation.new_text);
|
|
1726
2007
|
}
|
|
1727
|
-
parent.children ??= [];
|
|
1728
|
-
parent.children.push(entry.symbol);
|
|
1729
2008
|
}
|
|
1730
|
-
return
|
|
2009
|
+
return values;
|
|
1731
2010
|
}
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
if (
|
|
1736
|
-
return
|
|
1737
|
-
return
|
|
2011
|
+
function formatWhitespaceOnlyWarning(semanticSummary, edits) {
|
|
2012
|
+
if (semanticSummary?.classification !== "whitespace-only")
|
|
2013
|
+
return;
|
|
2014
|
+
if (!extractNewTextValues(edits).some((text) => /\S/.test(text)))
|
|
2015
|
+
return;
|
|
2016
|
+
return "⚠ Edit classified as whitespace-only — if you intended a behavior change, re-read to verify.";
|
|
1738
2017
|
}
|
|
1739
|
-
function
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
return
|
|
1745
|
-
|
|
2018
|
+
function formatReplaceHint(edits, noopEdits) {
|
|
2019
|
+
if ((noopEdits ?? []).length > 0)
|
|
2020
|
+
return;
|
|
2021
|
+
const counts = countEditTypes(edits);
|
|
2022
|
+
if (counts.replace === 0)
|
|
2023
|
+
return;
|
|
2024
|
+
if (counts.replace !== counts.total)
|
|
2025
|
+
return;
|
|
2026
|
+
return "[info: this edit used replace (unverified). For safer future edits, prefer set_line/replace_lines with an anchor from readSeek_digest/readSeek_grep/readSeek_search.]";
|
|
1746
2027
|
}
|
|
1747
|
-
function
|
|
1748
|
-
const
|
|
1749
|
-
const
|
|
1750
|
-
const
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
2028
|
+
function buildEditOutput(input) {
|
|
2029
|
+
const summary = `Updated ${input.displayPath}`;
|
|
2030
|
+
const visibleSummary = buildVisibleSummary(input.displayPath, input.diff, input.edits);
|
|
2031
|
+
const semanticWarning = formatWhitespaceOnlyWarning(input.semanticSummary, input.edits);
|
|
2032
|
+
const warningText = input.warnings.length ? `
|
|
2033
|
+
|
|
2034
|
+
Warnings:
|
|
2035
|
+
${input.warnings.join(`
|
|
2036
|
+
`)}` : "";
|
|
2037
|
+
const replaceHint = formatReplaceHint(input.edits, input.noopEdits);
|
|
2038
|
+
let text = visibleSummary;
|
|
2039
|
+
if (semanticWarning)
|
|
2040
|
+
text += `
|
|
2041
|
+
${semanticWarning}`;
|
|
2042
|
+
text += warningText;
|
|
2043
|
+
if (replaceHint)
|
|
2044
|
+
text += `
|
|
2045
|
+
${replaceHint}`;
|
|
2046
|
+
return {
|
|
2047
|
+
text,
|
|
2048
|
+
patch: input.patch ?? "",
|
|
2049
|
+
readSeekValue: buildReadSeekEditResult({
|
|
2050
|
+
path: input.path,
|
|
2051
|
+
summary,
|
|
2052
|
+
diff: input.diff,
|
|
2053
|
+
...input.diffData ? { diffData: input.diffData } : {},
|
|
2054
|
+
firstChangedLine: input.firstChangedLine,
|
|
2055
|
+
warnings: input.warnings,
|
|
2056
|
+
noopEdits: input.noopEdits,
|
|
2057
|
+
...input.semanticSummary ? { semanticSummary: input.semanticSummary } : {}
|
|
2058
|
+
})
|
|
2059
|
+
};
|
|
1755
2060
|
}
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
2061
|
+
|
|
2062
|
+
// src/edit-classify.ts
|
|
2063
|
+
import * as Diff2 from "diff";
|
|
2064
|
+
function classifyEdit(oldContent, newContent) {
|
|
2065
|
+
if (oldContent === newContent) {
|
|
2066
|
+
return { classification: "no-op" };
|
|
1761
2067
|
}
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
result = true;
|
|
1778
|
-
break;
|
|
1779
|
-
}
|
|
1780
|
-
} catch {}
|
|
2068
|
+
const oldLines = oldContent.split(`
|
|
2069
|
+
`);
|
|
2070
|
+
const newLines = newContent.split(`
|
|
2071
|
+
`);
|
|
2072
|
+
if (oldLines.length === newLines.length) {
|
|
2073
|
+
let hasWhitespaceChange2 = false;
|
|
2074
|
+
let hasSemanticChange2 = false;
|
|
2075
|
+
for (let i = 0;i < oldLines.length; i++) {
|
|
2076
|
+
if (oldLines[i] === newLines[i])
|
|
2077
|
+
continue;
|
|
2078
|
+
if (oldLines[i].trim() === newLines[i].trim()) {
|
|
2079
|
+
hasWhitespaceChange2 = true;
|
|
2080
|
+
} else {
|
|
2081
|
+
hasSemanticChange2 = true;
|
|
2082
|
+
}
|
|
1781
2083
|
}
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
2084
|
+
if (hasSemanticChange2 && hasWhitespaceChange2)
|
|
2085
|
+
return { classification: "mixed" };
|
|
2086
|
+
if (hasWhitespaceChange2)
|
|
2087
|
+
return { classification: "whitespace-only" };
|
|
2088
|
+
return { classification: "semantic" };
|
|
1786
2089
|
}
|
|
1787
|
-
|
|
1788
|
-
|
|
2090
|
+
const parts = Diff2.diffLines(oldContent, newContent);
|
|
2091
|
+
let hasWhitespaceChange = false;
|
|
2092
|
+
let hasSemanticChange = false;
|
|
2093
|
+
for (const part of parts) {
|
|
2094
|
+
if (!part.added && !part.removed)
|
|
2095
|
+
continue;
|
|
2096
|
+
const lines = part.value.split(`
|
|
2097
|
+
`);
|
|
2098
|
+
if (lines[lines.length - 1] === "")
|
|
2099
|
+
lines.pop();
|
|
2100
|
+
for (const line of lines) {
|
|
2101
|
+
if (line.trim() === "") {
|
|
2102
|
+
hasWhitespaceChange = true;
|
|
2103
|
+
} else {
|
|
2104
|
+
hasSemanticChange = true;
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
if (hasSemanticChange && hasWhitespaceChange)
|
|
2109
|
+
return { classification: "mixed" };
|
|
2110
|
+
if (hasWhitespaceChange)
|
|
2111
|
+
return { classification: "whitespace-only" };
|
|
2112
|
+
return { classification: "semantic" };
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
// src/readseek-client.ts
|
|
2116
|
+
import { existsSync, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
2117
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
2118
|
+
import { homedir as homedir3 } from "node:os";
|
|
2119
|
+
import path2 from "node:path";
|
|
2120
|
+
|
|
2121
|
+
// src/readseek/enums.ts
|
|
2122
|
+
var SymbolKind;
|
|
2123
|
+
((SymbolKind2) => {
|
|
2124
|
+
SymbolKind2["Class"] = "class";
|
|
2125
|
+
SymbolKind2["Function"] = "function";
|
|
2126
|
+
SymbolKind2["Method"] = "method";
|
|
2127
|
+
SymbolKind2["Variable"] = "variable";
|
|
2128
|
+
SymbolKind2["Constant"] = "constant";
|
|
2129
|
+
SymbolKind2["Interface"] = "interface";
|
|
2130
|
+
SymbolKind2["Type"] = "type";
|
|
2131
|
+
SymbolKind2["Enum"] = "enum";
|
|
2132
|
+
SymbolKind2["Struct"] = "struct";
|
|
2133
|
+
SymbolKind2["Import"] = "import";
|
|
2134
|
+
SymbolKind2["Module"] = "module";
|
|
2135
|
+
SymbolKind2["Namespace"] = "namespace";
|
|
2136
|
+
SymbolKind2["Property"] = "property";
|
|
2137
|
+
SymbolKind2["Heading"] = "heading";
|
|
2138
|
+
SymbolKind2["Table"] = "table";
|
|
2139
|
+
SymbolKind2["View"] = "view";
|
|
2140
|
+
SymbolKind2["Procedure"] = "procedure";
|
|
2141
|
+
SymbolKind2["Trigger"] = "trigger";
|
|
2142
|
+
SymbolKind2["Index"] = "index";
|
|
2143
|
+
SymbolKind2["Schema"] = "schema";
|
|
2144
|
+
SymbolKind2["Signal"] = "signal";
|
|
2145
|
+
SymbolKind2["Unknown"] = "unknown";
|
|
2146
|
+
})(SymbolKind ||= {});
|
|
2147
|
+
|
|
2148
|
+
// src/readseek-settings.ts
|
|
2149
|
+
import { readFileSync, statSync } from "node:fs";
|
|
2150
|
+
import { homedir as homedir2 } from "node:os";
|
|
2151
|
+
import { join } from "node:path";
|
|
2152
|
+
var READSEEK_TOOL_REPLACEMENTS = {
|
|
2153
|
+
edit: "readSeek_edit",
|
|
2154
|
+
grep: "readSeek_grep",
|
|
2155
|
+
write: "readSeek_write"
|
|
2156
|
+
};
|
|
2157
|
+
var READSEEK_KEYS = ["overrideTools", "imageMode", "syntaxValidation", "timeoutMs", "grep", "display"];
|
|
2158
|
+
var READSEEK_GREP_KEYS = ["maxLines", "maxBytes"];
|
|
2159
|
+
var READSEEK_DISPLAY_KEYS = ["grep", "edit", "write"];
|
|
2160
|
+
var DEFAULT_DISPLAY_MODES = {
|
|
2161
|
+
grep: "compact",
|
|
2162
|
+
edit: "expanded",
|
|
2163
|
+
write: "expanded"
|
|
2164
|
+
};
|
|
2165
|
+
function globalSettingsPath() {
|
|
2166
|
+
return join(homedir2(), ".pi/agent/settings.json");
|
|
2167
|
+
}
|
|
2168
|
+
function projectSettingsPath() {
|
|
2169
|
+
return join(process.cwd(), ".pi/settings.json");
|
|
1789
2170
|
}
|
|
1790
|
-
function
|
|
1791
|
-
|
|
1792
|
-
return home ? path2.join(home, ".pi", "readseek") : null;
|
|
2171
|
+
function isRecord(value) {
|
|
2172
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1793
2173
|
}
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
return resolveReadSeekTimeoutMs() ?? DEFAULT_READSEEK_TIMEOUT_MS;
|
|
2174
|
+
function invalid(source, path2) {
|
|
2175
|
+
return { source, path: path2, message: `Invalid readseek setting at ${path2}` };
|
|
1797
2176
|
}
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
}
|
|
2177
|
+
function warnUnknownKeys(raw, known, prefix, source, warnings) {
|
|
2178
|
+
for (const key of Object.keys(raw)) {
|
|
2179
|
+
if (known.includes(key))
|
|
2180
|
+
continue;
|
|
2181
|
+
const path2 = `${prefix}.${key}`;
|
|
2182
|
+
warnings.push({ source, path: path2, message: `Unknown readseek setting at ${path2}` });
|
|
2183
|
+
}
|
|
1805
2184
|
}
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
if (
|
|
1811
|
-
return
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
});
|
|
1815
|
-
return defaultReadSeekDirInit;
|
|
2185
|
+
function readPositive(raw, key, path2, source, warnings) {
|
|
2186
|
+
if (!(key in raw))
|
|
2187
|
+
return;
|
|
2188
|
+
const val = raw[key];
|
|
2189
|
+
if (typeof val === "number" && Number.isSafeInteger(val) && val > 0)
|
|
2190
|
+
return val;
|
|
2191
|
+
warnings.push(invalid(source, path2));
|
|
2192
|
+
return;
|
|
1816
2193
|
}
|
|
1817
|
-
|
|
1818
|
-
if (
|
|
1819
|
-
return
|
|
1820
|
-
const
|
|
1821
|
-
|
|
2194
|
+
function readEnum(raw, key, values, path2, source, warnings) {
|
|
2195
|
+
if (!(key in raw))
|
|
2196
|
+
return;
|
|
2197
|
+
const val = raw[key];
|
|
2198
|
+
if (typeof val === "string" && values.includes(val))
|
|
2199
|
+
return val;
|
|
2200
|
+
warnings.push(invalid(source, path2));
|
|
2201
|
+
return;
|
|
1822
2202
|
}
|
|
1823
|
-
|
|
1824
|
-
|
|
2203
|
+
function readImageMode(raw, source, warnings) {
|
|
2204
|
+
if (!("imageMode" in raw))
|
|
2205
|
+
return;
|
|
2206
|
+
const val = raw.imageMode;
|
|
2207
|
+
if (val === "on" || val === "off" || val === "auto")
|
|
2208
|
+
return val;
|
|
2209
|
+
warnings.push(invalid(source, "readseek.imageMode"));
|
|
2210
|
+
return;
|
|
1825
2211
|
}
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
2212
|
+
function readOverrideTools(raw, source, warnings) {
|
|
2213
|
+
if (!("overrideTools" in raw))
|
|
2214
|
+
return;
|
|
2215
|
+
const value = raw.overrideTools;
|
|
2216
|
+
if (!Array.isArray(value)) {
|
|
2217
|
+
warnings.push(invalid(source, "readseek.overrideTools"));
|
|
2218
|
+
return;
|
|
2219
|
+
}
|
|
2220
|
+
const tools = [];
|
|
2221
|
+
value.forEach((tool, index) => {
|
|
2222
|
+
if (typeof tool === "string" && Object.hasOwn(READSEEK_TOOL_REPLACEMENTS, tool)) {
|
|
2223
|
+
tools.push(tool);
|
|
2224
|
+
} else {
|
|
2225
|
+
warnings.push(invalid(source, `readseek.overrideTools[${index}]`));
|
|
2226
|
+
}
|
|
1832
2227
|
});
|
|
2228
|
+
return tools;
|
|
1833
2229
|
}
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
2230
|
+
function readDisplaySettings(raw, source, warnings) {
|
|
2231
|
+
if (!("display" in raw))
|
|
2232
|
+
return;
|
|
2233
|
+
if (!isRecord(raw.display)) {
|
|
2234
|
+
warnings.push(invalid(source, "readseek.display"));
|
|
2235
|
+
return;
|
|
2236
|
+
}
|
|
2237
|
+
warnUnknownKeys(raw.display, READSEEK_DISPLAY_KEYS, "readseek.display", source, warnings);
|
|
2238
|
+
const display = {};
|
|
2239
|
+
for (const tool of READSEEK_DISPLAY_KEYS) {
|
|
2240
|
+
const mode = readEnum(raw.display, tool, ["compact", "expanded"], `readseek.display.${tool}`, source, warnings);
|
|
2241
|
+
if (mode !== undefined)
|
|
2242
|
+
display[tool] = mode;
|
|
2243
|
+
}
|
|
2244
|
+
return Object.keys(display).length > 0 ? display : undefined;
|
|
1847
2245
|
}
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
signal.removeEventListener("abort", onAbort);
|
|
1863
|
-
reject(signal.reason);
|
|
1864
|
-
};
|
|
1865
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
1866
|
-
predecessor.then(() => {
|
|
1867
|
-
signal.removeEventListener("abort", onAbort);
|
|
1868
|
-
resolve();
|
|
1869
|
-
}, (error) => {
|
|
1870
|
-
signal.removeEventListener("abort", onAbort);
|
|
1871
|
-
reject(error);
|
|
1872
|
-
});
|
|
2246
|
+
function validateSettings(raw, source) {
|
|
2247
|
+
const settings = {};
|
|
2248
|
+
const warnings = [];
|
|
2249
|
+
if (!isRecord(raw)) {
|
|
2250
|
+
warnings.push({ source, message: "Invalid readseek settings: expected a JSON object" });
|
|
2251
|
+
return { settings, warnings };
|
|
2252
|
+
}
|
|
2253
|
+
if (!("readseek" in raw)) {
|
|
2254
|
+
const misplaced = READSEEK_KEYS.filter((key) => (key in raw));
|
|
2255
|
+
if (misplaced.length > 0) {
|
|
2256
|
+
warnings.push({
|
|
2257
|
+
source,
|
|
2258
|
+
path: misplaced[0],
|
|
2259
|
+
message: `Readseek setting at top level: move ${misplaced.join(", ")} under "readseek"`
|
|
1873
2260
|
});
|
|
1874
|
-
} else {
|
|
1875
|
-
await predecessor;
|
|
1876
2261
|
}
|
|
1877
|
-
|
|
1878
|
-
return await runReadSeekRaw(args, {
|
|
1879
|
-
...options,
|
|
1880
|
-
timeoutMs: options.timeoutMs ?? resolveReadSeekTimeoutMs() ?? DEFAULT_READSEEK_VISION_TIMEOUT_MS
|
|
1881
|
-
});
|
|
1882
|
-
} finally {
|
|
1883
|
-
release();
|
|
2262
|
+
return { settings, warnings };
|
|
1884
2263
|
}
|
|
2264
|
+
if (!isRecord(raw.readseek)) {
|
|
2265
|
+
warnings.push(invalid(source, "readseek"));
|
|
2266
|
+
return { settings, warnings };
|
|
2267
|
+
}
|
|
2268
|
+
const section = raw.readseek;
|
|
2269
|
+
warnUnknownKeys(section, READSEEK_KEYS, "readseek", source, warnings);
|
|
2270
|
+
const overrideTools = readOverrideTools(section, source, warnings);
|
|
2271
|
+
if (overrideTools !== undefined)
|
|
2272
|
+
settings.overrideTools = overrideTools;
|
|
2273
|
+
const imageMode = readImageMode(section, source, warnings);
|
|
2274
|
+
if (imageMode !== undefined)
|
|
2275
|
+
settings.imageMode = imageMode;
|
|
2276
|
+
const syntaxValidation = readEnum(section, "syntaxValidation", ["warn", "block", "off"], "readseek.syntaxValidation", source, warnings);
|
|
2277
|
+
if (syntaxValidation !== undefined)
|
|
2278
|
+
settings.syntaxValidation = syntaxValidation;
|
|
2279
|
+
const timeoutMs = readPositive(section, "timeoutMs", "readseek.timeoutMs", source, warnings);
|
|
2280
|
+
if (timeoutMs !== undefined)
|
|
2281
|
+
settings.timeoutMs = timeoutMs;
|
|
2282
|
+
if ("grep" in section) {
|
|
2283
|
+
if (isRecord(section.grep)) {
|
|
2284
|
+
warnUnknownKeys(section.grep, READSEEK_GREP_KEYS, "readseek.grep", source, warnings);
|
|
2285
|
+
const grep = {};
|
|
2286
|
+
const maxLines = readPositive(section.grep, "maxLines", "readseek.grep.maxLines", source, warnings);
|
|
2287
|
+
if (maxLines !== undefined)
|
|
2288
|
+
grep.maxLines = maxLines;
|
|
2289
|
+
const maxBytes = readPositive(section.grep, "maxBytes", "readseek.grep.maxBytes", source, warnings);
|
|
2290
|
+
if (maxBytes !== undefined)
|
|
2291
|
+
grep.maxBytes = maxBytes;
|
|
2292
|
+
if (Object.keys(grep).length > 0)
|
|
2293
|
+
settings.grep = grep;
|
|
2294
|
+
} else {
|
|
2295
|
+
warnings.push(invalid(source, "readseek.grep"));
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
const display = readDisplaySettings(section, source, warnings);
|
|
2299
|
+
if (display !== undefined)
|
|
2300
|
+
settings.display = display;
|
|
2301
|
+
return { settings, warnings };
|
|
1885
2302
|
}
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
}
|
|
1894
|
-
function requireString(value, field) {
|
|
1895
|
-
if (typeof value !== "string")
|
|
1896
|
-
throw new Error(`invalid readseek ${field}`);
|
|
1897
|
-
return value;
|
|
1898
|
-
}
|
|
1899
|
-
function requireBoolean(value, field) {
|
|
1900
|
-
if (typeof value !== "boolean")
|
|
1901
|
-
throw new Error(`invalid readseek ${field}: expected boolean`);
|
|
1902
|
-
return value;
|
|
1903
|
-
}
|
|
1904
|
-
function parseMapOutput(value) {
|
|
1905
|
-
if (!value || typeof value !== "object")
|
|
1906
|
-
throw new Error("invalid readseek map output");
|
|
1907
|
-
const output = value;
|
|
1908
|
-
const symbols = output.symbols;
|
|
1909
|
-
if (!Array.isArray(symbols))
|
|
1910
|
-
throw new Error("invalid readseek symbols");
|
|
1911
|
-
return {
|
|
1912
|
-
file: requireString(output.file, "file"),
|
|
1913
|
-
language: requireString(output.language, "language"),
|
|
1914
|
-
line_count: requireNumber(output.line_count, "line_count"),
|
|
1915
|
-
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1916
|
-
symbols: symbols.map((symbol) => {
|
|
1917
|
-
if (!symbol || typeof symbol !== "object")
|
|
1918
|
-
throw new Error("invalid readseek symbol");
|
|
1919
|
-
const item = symbol;
|
|
1920
|
-
return {
|
|
1921
|
-
kind: requireString(item.kind, "symbol.kind"),
|
|
1922
|
-
name: requireString(item.name, "symbol.name"),
|
|
1923
|
-
qualified_name: requireString(item.qualified_name, "symbol.qualified_name"),
|
|
1924
|
-
start_line: requireNumber(item.start_line, "symbol.start_line"),
|
|
1925
|
-
end_line: requireNumber(item.end_line, "symbol.end_line"),
|
|
1926
|
-
start_hash: requireString(item.start_hash, "symbol.start_hash"),
|
|
1927
|
-
end_hash: requireString(item.end_hash, "symbol.end_hash")
|
|
1928
|
-
};
|
|
1929
|
-
})
|
|
1930
|
-
};
|
|
1931
|
-
}
|
|
1932
|
-
function parseHashline(value, field) {
|
|
1933
|
-
if (!value || typeof value !== "object")
|
|
1934
|
-
throw new Error(`invalid readseek ${field}`);
|
|
1935
|
-
const item = value;
|
|
1936
|
-
return {
|
|
1937
|
-
line: requireNumber(item.line, `${field}.line`),
|
|
1938
|
-
hash: requireString(item.hash, `${field}.hash`),
|
|
1939
|
-
text: requireString(item.text, `${field}.text`)
|
|
1940
|
-
};
|
|
1941
|
-
}
|
|
1942
|
-
function parseSearchHashlines(value, field) {
|
|
1943
|
-
if (!Array.isArray(value))
|
|
1944
|
-
throw new Error(`invalid readseek ${field}`);
|
|
1945
|
-
return value.map((line) => parseHashline(line, field));
|
|
1946
|
-
}
|
|
1947
|
-
function parseSearchOutput(value) {
|
|
1948
|
-
if (!value || typeof value !== "object")
|
|
1949
|
-
throw new Error("invalid readseek search output");
|
|
1950
|
-
const output = value;
|
|
1951
|
-
if (!Array.isArray(output.results))
|
|
1952
|
-
throw new Error("invalid readseek search results");
|
|
1953
|
-
return {
|
|
1954
|
-
results: output.results.map((result) => {
|
|
1955
|
-
if (!result || typeof result !== "object")
|
|
1956
|
-
throw new Error("invalid readseek search result");
|
|
1957
|
-
const file = result;
|
|
1958
|
-
if (!Array.isArray(file.matches))
|
|
1959
|
-
throw new Error("invalid readseek search matches");
|
|
1960
|
-
return {
|
|
1961
|
-
file: requireString(file.file, "search.file"),
|
|
1962
|
-
language: requireString(file.language, "search.language"),
|
|
1963
|
-
file_hash: requireString(file.file_hash, "search.file_hash"),
|
|
1964
|
-
matches: file.matches.map((match) => {
|
|
1965
|
-
if (!match || typeof match !== "object")
|
|
1966
|
-
throw new Error("invalid readseek search match");
|
|
1967
|
-
const item = match;
|
|
1968
|
-
if (!Array.isArray(item.captures))
|
|
1969
|
-
throw new Error("invalid readseek search captures");
|
|
1970
|
-
return {
|
|
1971
|
-
pattern_index: item.pattern_index === undefined ? 0 : requireNumber(item.pattern_index, "search.match.pattern_index"),
|
|
1972
|
-
start_line: requireNumber(item.start_line, "search.match.start_line"),
|
|
1973
|
-
end_line: requireNumber(item.end_line, "search.match.end_line"),
|
|
1974
|
-
start_hash: requireString(item.start_hash, "search.match.start_hash"),
|
|
1975
|
-
end_hash: requireString(item.end_hash, "search.match.end_hash"),
|
|
1976
|
-
hashlines: parseSearchHashlines(item.hashlines, "search.match.hashlines"),
|
|
1977
|
-
captures: item.captures.map((capture) => {
|
|
1978
|
-
if (!capture || typeof capture !== "object")
|
|
1979
|
-
throw new Error("invalid readseek search capture");
|
|
1980
|
-
const captureItem = capture;
|
|
1981
|
-
return {
|
|
1982
|
-
name: requireString(captureItem.name, "search.capture.name"),
|
|
1983
|
-
start_line: requireNumber(captureItem.start_line, "search.capture.start_line"),
|
|
1984
|
-
end_line: requireNumber(captureItem.end_line, "search.capture.end_line"),
|
|
1985
|
-
start_hash: requireString(captureItem.start_hash, "search.capture.start_hash"),
|
|
1986
|
-
end_hash: requireString(captureItem.end_hash, "search.capture.end_hash"),
|
|
1987
|
-
hashlines: parseSearchHashlines(captureItem.hashlines, "search.capture.hashlines")
|
|
1988
|
-
};
|
|
1989
|
-
})
|
|
1990
|
-
};
|
|
1991
|
-
})
|
|
1992
|
-
};
|
|
1993
|
-
})
|
|
1994
|
-
};
|
|
1995
|
-
}
|
|
1996
|
-
function optionalString(value, field) {
|
|
1997
|
-
if (value === undefined || value === null)
|
|
1998
|
-
return;
|
|
1999
|
-
return requireString(value, field);
|
|
2303
|
+
var settingsFileCache = new Map;
|
|
2304
|
+
function statFileOrNull(path2) {
|
|
2305
|
+
try {
|
|
2306
|
+
return statSync(path2);
|
|
2307
|
+
} catch {
|
|
2308
|
+
return null;
|
|
2309
|
+
}
|
|
2000
2310
|
}
|
|
2001
|
-
function
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
enclosingSymbol: enclosing
|
|
2021
|
-
};
|
|
2022
|
-
})
|
|
2023
|
-
};
|
|
2311
|
+
function readSettingsFile(path2) {
|
|
2312
|
+
const stats = statFileOrNull(path2);
|
|
2313
|
+
if (!stats) {
|
|
2314
|
+
settingsFileCache.delete(path2);
|
|
2315
|
+
return { settings: {}, warnings: [] };
|
|
2316
|
+
}
|
|
2317
|
+
const cached = settingsFileCache.get(path2);
|
|
2318
|
+
if (cached && cached.mtimeMs === stats.mtimeMs && cached.size === stats.size)
|
|
2319
|
+
return cached.result;
|
|
2320
|
+
let result;
|
|
2321
|
+
try {
|
|
2322
|
+
const text = readFileSync(path2, "utf8");
|
|
2323
|
+
result = validateSettings(JSON.parse(text), path2);
|
|
2324
|
+
} catch (error) {
|
|
2325
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2326
|
+
result = { settings: {}, warnings: [{ source: path2, message: `Invalid JSON: ${message}` }] };
|
|
2327
|
+
}
|
|
2328
|
+
settingsFileCache.set(path2, { mtimeMs: stats.mtimeMs, size: stats.size, result });
|
|
2329
|
+
return result;
|
|
2024
2330
|
}
|
|
2025
|
-
function
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2331
|
+
function mergeSettings(base, override) {
|
|
2332
|
+
const settings = { ...base, ...override };
|
|
2333
|
+
const grep = { ...base.grep ?? {}, ...override.grep ?? {} };
|
|
2334
|
+
if (Object.keys(grep).length > 0)
|
|
2335
|
+
settings.grep = grep;
|
|
2336
|
+
const display = { ...base.display ?? {}, ...override.display ?? {} };
|
|
2337
|
+
if (Object.keys(display).length > 0)
|
|
2338
|
+
settings.display = display;
|
|
2339
|
+
return settings;
|
|
2029
2340
|
}
|
|
2030
|
-
function
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
const output = value;
|
|
2034
|
-
if (!Array.isArray(output.diagnostics))
|
|
2035
|
-
throw new Error("invalid readseek diagnostics");
|
|
2341
|
+
function resolveReadSeekJsonSettings() {
|
|
2342
|
+
const globalResult = readSettingsFile(globalSettingsPath());
|
|
2343
|
+
const projectResult = readSettingsFile(projectSettingsPath());
|
|
2036
2344
|
return {
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
diagnostics: output.diagnostics.map((diagnostic) => {
|
|
2040
|
-
if (!diagnostic || typeof diagnostic !== "object")
|
|
2041
|
-
throw new Error("invalid readseek diagnostic");
|
|
2042
|
-
const item = diagnostic;
|
|
2043
|
-
return {
|
|
2044
|
-
kind: parseDiagnosticKind(item.kind),
|
|
2045
|
-
start_line: requireNumber(item.start_line, "diagnostic.start_line"),
|
|
2046
|
-
end_line: requireNumber(item.end_line, "diagnostic.end_line")
|
|
2047
|
-
};
|
|
2048
|
-
})
|
|
2345
|
+
settings: mergeSettings(globalResult.settings, projectResult.settings),
|
|
2346
|
+
warnings: [...globalResult.warnings, ...projectResult.warnings]
|
|
2049
2347
|
};
|
|
2050
2348
|
}
|
|
2051
|
-
function
|
|
2052
|
-
return
|
|
2349
|
+
function resolveReadSeekSyntaxValidation() {
|
|
2350
|
+
return resolveReadSeekJsonSettings().settings.syntaxValidation;
|
|
2053
2351
|
}
|
|
2054
|
-
function
|
|
2055
|
-
|
|
2056
|
-
return;
|
|
2057
|
-
if (!Array.isArray(value))
|
|
2058
|
-
throw new Error("invalid readseek detect objects");
|
|
2059
|
-
return value.map((object) => {
|
|
2060
|
-
if (!object || typeof object !== "object")
|
|
2061
|
-
throw new Error("invalid readseek detect object");
|
|
2062
|
-
const item = object;
|
|
2063
|
-
const bbox = item.bbox;
|
|
2064
|
-
if (!Array.isArray(bbox) || bbox.length !== 4)
|
|
2065
|
-
throw new Error("invalid readseek detect object.bbox");
|
|
2066
|
-
return {
|
|
2067
|
-
label: requireString(item.label, "object.label"),
|
|
2068
|
-
bbox: bbox.map((n, i) => requireNumber(n, `object.bbox[${i}]`))
|
|
2069
|
-
};
|
|
2070
|
-
});
|
|
2352
|
+
function resolveReadSeekTimeoutMs() {
|
|
2353
|
+
return resolveReadSeekJsonSettings().settings.timeoutMs;
|
|
2071
2354
|
}
|
|
2072
|
-
function
|
|
2073
|
-
|
|
2074
|
-
return value;
|
|
2075
|
-
throw new Error("invalid image encoding");
|
|
2355
|
+
function resolveReadSeekToolDisplayMode(tool) {
|
|
2356
|
+
return resolveReadSeekJsonSettings().settings.display?.[tool] ?? DEFAULT_DISPLAY_MODES[tool];
|
|
2076
2357
|
}
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
const file = requireString(output.file, "file");
|
|
2083
|
-
const mime = optionalString(output.mime, "mime");
|
|
2084
|
-
if (type === "application/pdf") {
|
|
2085
|
-
if (output.format !== "pdf")
|
|
2086
|
-
throw new Error("invalid readseek PDF format");
|
|
2087
|
-
return {
|
|
2088
|
-
kind: "pdf",
|
|
2089
|
-
type,
|
|
2090
|
-
file,
|
|
2091
|
-
mime,
|
|
2092
|
-
format: output.format,
|
|
2093
|
-
pages: requireNumber(output.pages, "pages")
|
|
2094
|
-
};
|
|
2095
|
-
}
|
|
2096
|
-
if (output.width !== undefined || output.height !== undefined) {
|
|
2097
|
-
return {
|
|
2098
|
-
kind: "image",
|
|
2099
|
-
type,
|
|
2100
|
-
file,
|
|
2101
|
-
mime,
|
|
2102
|
-
format: requireString(output.format, "format"),
|
|
2103
|
-
width: requireNumber(output.width, "width"),
|
|
2104
|
-
height: requireNumber(output.height, "height"),
|
|
2105
|
-
animated: requireBoolean(output.animated, "animated"),
|
|
2106
|
-
encoding: output.encoding === undefined ? undefined : parseImageEncoding(output.encoding),
|
|
2107
|
-
data: output.data === undefined ? undefined : requireString(output.data, "data"),
|
|
2108
|
-
ocr: parseOcrText(output.ocr),
|
|
2109
|
-
caption: optionalString(output.caption, "caption"),
|
|
2110
|
-
objects: parseDetectedObjects(output.objects)
|
|
2111
|
-
};
|
|
2112
|
-
}
|
|
2113
|
-
if (output.language !== undefined) {
|
|
2114
|
-
return {
|
|
2115
|
-
kind: "source",
|
|
2116
|
-
type,
|
|
2117
|
-
file,
|
|
2118
|
-
language: requireString(output.language, "language"),
|
|
2119
|
-
engine: optionalString(output.engine, "engine"),
|
|
2120
|
-
supported: requireBoolean(output.supported, "supported"),
|
|
2121
|
-
mime,
|
|
2122
|
-
syntax: optionalString(output.syntax, "syntax")
|
|
2123
|
-
};
|
|
2124
|
-
}
|
|
2125
|
-
return { kind: "text", type, file, mime };
|
|
2358
|
+
|
|
2359
|
+
// src/readseek-client.ts
|
|
2360
|
+
var resolveFromHost = createRequire2(import.meta.url).resolve;
|
|
2361
|
+
function readSeekBinaryPath() {
|
|
2362
|
+
return resolveReadSeekBinaryFromPath() ?? resolveReadSeekBinaryFromPackages(resolveFromHost);
|
|
2126
2363
|
}
|
|
2127
|
-
function
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
return {
|
|
2135
|
-
content_hash: requireString(cache.content_hash, "cache.content_hash"),
|
|
2136
|
-
state
|
|
2137
|
-
};
|
|
2364
|
+
function readSeekBinaryAvailability() {
|
|
2365
|
+
try {
|
|
2366
|
+
readSeekBinaryPath();
|
|
2367
|
+
return { available: true };
|
|
2368
|
+
} catch (err) {
|
|
2369
|
+
return { available: false, reason: classifyReadSeekFailure(err).message };
|
|
2370
|
+
}
|
|
2138
2371
|
}
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
language: requireString(output.language, "language"),
|
|
2148
|
-
engine: optionalString(output.engine, "engine"),
|
|
2149
|
-
line_count: requireNumber(output.line_count, "line_count"),
|
|
2150
|
-
file_hash: requireString(output.file_hash, "file_hash"),
|
|
2151
|
-
line: requireNumber(output.line, "line"),
|
|
2152
|
-
column: requireNumber(output.column, "column"),
|
|
2153
|
-
line_hash: requireString(output.line_hash, "line_hash"),
|
|
2154
|
-
identifier: identifier && typeof identifier === "object" ? {
|
|
2155
|
-
text: requireString(identifier.text, "identifier.text"),
|
|
2156
|
-
start_column: requireNumber(identifier.start_column, "identifier.start_column"),
|
|
2157
|
-
end_column: requireNumber(identifier.end_column, "identifier.end_column"),
|
|
2158
|
-
start_byte: requireNumber(identifier.start_byte, "identifier.start_byte"),
|
|
2159
|
-
end_byte: requireNumber(identifier.end_byte, "identifier.end_byte")
|
|
2160
|
-
} : undefined,
|
|
2161
|
-
symbol: symbol && typeof symbol === "object" ? {
|
|
2162
|
-
name: requireString(symbol.name, "symbol.name"),
|
|
2163
|
-
kind: requireString(symbol.kind, "symbol.kind"),
|
|
2164
|
-
qualified_name: requireString(symbol.qualified_name, "symbol.qualified_name"),
|
|
2165
|
-
start_line: requireNumber(symbol.start_line, "symbol.start_line"),
|
|
2166
|
-
end_line: requireNumber(symbol.end_line, "symbol.end_line")
|
|
2167
|
-
} : undefined
|
|
2168
|
-
};
|
|
2372
|
+
var READSEEK_REPO_PACKAGE_NAMES = new Set(["@jarkkojs/readseek", "readseek"]);
|
|
2373
|
+
var defaultReadSeekDirInit;
|
|
2374
|
+
function directoryExists(dirPath) {
|
|
2375
|
+
try {
|
|
2376
|
+
return statSync2(dirPath).isDirectory();
|
|
2377
|
+
} catch {
|
|
2378
|
+
return false;
|
|
2379
|
+
}
|
|
2169
2380
|
}
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
const
|
|
2174
|
-
if (
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2381
|
+
var ownRepositoryByDir = new Map;
|
|
2382
|
+
function isOwnReadSeekRepository(cwd = process.cwd()) {
|
|
2383
|
+
const start = path2.resolve(cwd);
|
|
2384
|
+
const cached = ownRepositoryByDir.get(start);
|
|
2385
|
+
if (cached !== undefined)
|
|
2386
|
+
return cached;
|
|
2387
|
+
let dir = start;
|
|
2388
|
+
let result = false;
|
|
2389
|
+
while (true) {
|
|
2390
|
+
const packageJsonPath = path2.join(dir, "package.json");
|
|
2391
|
+
if (existsSync(packageJsonPath)) {
|
|
2392
|
+
try {
|
|
2393
|
+
const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
|
|
2394
|
+
if (typeof packageJson.name === "string" && READSEEK_REPO_PACKAGE_NAMES.has(packageJson.name)) {
|
|
2395
|
+
result = true;
|
|
2396
|
+
break;
|
|
2397
|
+
}
|
|
2398
|
+
} catch {}
|
|
2399
|
+
}
|
|
2400
|
+
const parent = path2.dirname(dir);
|
|
2401
|
+
if (parent === dir)
|
|
2402
|
+
break;
|
|
2403
|
+
dir = parent;
|
|
2404
|
+
}
|
|
2405
|
+
ownRepositoryByDir.set(start, result);
|
|
2189
2406
|
return result;
|
|
2190
2407
|
}
|
|
2191
|
-
function
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2408
|
+
function defaultReadSeekDir() {
|
|
2409
|
+
const home = homedir3();
|
|
2410
|
+
return home ? path2.join(home, ".pi", "readseek") : null;
|
|
2411
|
+
}
|
|
2412
|
+
var DEFAULT_READSEEK_VISION_TIMEOUT_MS = 30 * 60000;
|
|
2413
|
+
function readSeekTimeoutMs() {
|
|
2414
|
+
return resolveReadSeekTimeoutMs() ?? DEFAULT_READSEEK_TIMEOUT_MS;
|
|
2415
|
+
}
|
|
2416
|
+
async function spawnReadSeekRaw(args, options = {}) {
|
|
2417
|
+
return runReadSeekNodeText(readSeekBinaryPath(), args, {
|
|
2418
|
+
signal: options.signal,
|
|
2419
|
+
stdin: options.stdin,
|
|
2420
|
+
timeoutMs: options.timeoutMs ?? readSeekTimeoutMs(),
|
|
2421
|
+
cancelable: options.cancelable
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
2424
|
+
async function ensureDefaultReadSeekDir() {
|
|
2425
|
+
const dir = defaultReadSeekDir();
|
|
2426
|
+
if (!dir)
|
|
2427
|
+
return null;
|
|
2428
|
+
if (directoryExists(dir))
|
|
2429
|
+
return dir;
|
|
2430
|
+
defaultReadSeekDirInit ??= spawnReadSeekRaw(["--readseek-dir", dir, "init"]).then(() => directoryExists(dir) ? dir : null).catch(() => null).finally(() => {
|
|
2431
|
+
defaultReadSeekDirInit = undefined;
|
|
2202
2432
|
});
|
|
2433
|
+
return defaultReadSeekDirInit;
|
|
2434
|
+
}
|
|
2435
|
+
async function readSeekInvocationArgs(args) {
|
|
2436
|
+
if (isOwnReadSeekRepository())
|
|
2437
|
+
return [...args];
|
|
2438
|
+
const readSeekDir = await ensureDefaultReadSeekDir();
|
|
2439
|
+
return readSeekDir ? ["--readseek-dir", readSeekDir, ...args] : [...args];
|
|
2440
|
+
}
|
|
2441
|
+
async function runReadSeekRaw(args, options = {}) {
|
|
2442
|
+
return spawnReadSeekRaw(await readSeekInvocationArgs(args), options);
|
|
2203
2443
|
}
|
|
2204
|
-
async function
|
|
2205
|
-
|
|
2206
|
-
const runOptions = {
|
|
2444
|
+
async function runReadSeek(args, options = {}) {
|
|
2445
|
+
return runReadSeekNodeJson(readSeekBinaryPath(), await readSeekInvocationArgs(args), {
|
|
2207
2446
|
signal: options.signal,
|
|
2208
2447
|
stdin: options.stdin,
|
|
2209
|
-
timeoutMs: options.timeoutMs
|
|
2210
|
-
|
|
2211
|
-
|
|
2448
|
+
timeoutMs: options.timeoutMs ?? readSeekTimeoutMs(),
|
|
2449
|
+
cancelable: options.cancelable
|
|
2450
|
+
});
|
|
2451
|
+
}
|
|
2452
|
+
var visionInvocationTail = Promise.resolve();
|
|
2453
|
+
async function runReadSeekVisionRaw(args, options = {}) {
|
|
2454
|
+
let release;
|
|
2455
|
+
const gate = new Promise((resolve) => {
|
|
2456
|
+
release = resolve;
|
|
2457
|
+
});
|
|
2458
|
+
const predecessor = visionInvocationTail;
|
|
2459
|
+
visionInvocationTail = predecessor.then(() => gate);
|
|
2460
|
+
const signal = options.signal;
|
|
2461
|
+
try {
|
|
2462
|
+
if (signal) {
|
|
2463
|
+
signal.throwIfAborted();
|
|
2464
|
+
await new Promise((resolve, reject) => {
|
|
2465
|
+
const onAbort = () => {
|
|
2466
|
+
signal.removeEventListener("abort", onAbort);
|
|
2467
|
+
reject(signal.reason);
|
|
2468
|
+
};
|
|
2469
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2470
|
+
predecessor.then(() => {
|
|
2471
|
+
signal.removeEventListener("abort", onAbort);
|
|
2472
|
+
resolve();
|
|
2473
|
+
}, (error) => {
|
|
2474
|
+
signal.removeEventListener("abort", onAbort);
|
|
2475
|
+
reject(error);
|
|
2476
|
+
});
|
|
2477
|
+
});
|
|
2478
|
+
} else {
|
|
2479
|
+
await predecessor;
|
|
2480
|
+
}
|
|
2481
|
+
signal?.throwIfAborted();
|
|
2482
|
+
return await runReadSeekRaw(args, {
|
|
2483
|
+
...options,
|
|
2484
|
+
timeoutMs: options.timeoutMs ?? resolveReadSeekTimeoutMs() ?? DEFAULT_READSEEK_VISION_TIMEOUT_MS
|
|
2485
|
+
});
|
|
2486
|
+
} finally {
|
|
2487
|
+
release();
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
async function runReadSeekVision(args, options = {}) {
|
|
2491
|
+
return JSON.parse(await runReadSeekVisionRaw(args, options));
|
|
2492
|
+
}
|
|
2493
|
+
var runner = {
|
|
2494
|
+
text: (args, options = {}) => options.vision ? runReadSeekVisionRaw(args, options) : runReadSeekRaw(args, options),
|
|
2495
|
+
json: (args, options = {}) => options.vision ? runReadSeekVision(args, options) : runReadSeek(args, options)
|
|
2496
|
+
};
|
|
2497
|
+
var client = createReadSeekClient(runner);
|
|
2498
|
+
var readSeekView = client.view;
|
|
2499
|
+
var readSeekDigestNative = client.digestRaw;
|
|
2500
|
+
var readSeekDigest = client.digest;
|
|
2501
|
+
var readSeekDigestContent = client.digestContent;
|
|
2502
|
+
var readSeekImage = client.image;
|
|
2503
|
+
var readSeekPreparedImage = client.preparedImage;
|
|
2504
|
+
var readSeekSearch = client.search;
|
|
2505
|
+
var readSeekRefs = client.refs;
|
|
2506
|
+
var readSeekDef = client.def;
|
|
2507
|
+
var readSeekCheck = client.check;
|
|
2508
|
+
var readSeekIdentify = client.identify;
|
|
2509
|
+
var readSeekDetect = client.detect;
|
|
2510
|
+
var readSeekEdit = client.edit;
|
|
2511
|
+
var readSeekRename = client.rename;
|
|
2512
|
+
function normalizeLanguage(language) {
|
|
2513
|
+
return language === "java" ? "Java" : language;
|
|
2514
|
+
}
|
|
2515
|
+
function normalizeKind(kind) {
|
|
2516
|
+
if (kind === "constructor")
|
|
2517
|
+
return "method" /* Method */;
|
|
2518
|
+
if (Object.values(SymbolKind).includes(kind))
|
|
2519
|
+
return kind;
|
|
2520
|
+
return "unknown" /* Unknown */;
|
|
2212
2521
|
}
|
|
2213
|
-
|
|
2214
|
-
|
|
2522
|
+
function parentQualifiedNameFor(qualifiedName) {
|
|
2523
|
+
const lastDot = qualifiedName.lastIndexOf(".");
|
|
2524
|
+
return lastDot === -1 ? "" : qualifiedName.slice(0, lastDot);
|
|
2215
2525
|
}
|
|
2216
|
-
|
|
2217
|
-
|
|
2526
|
+
function symbolsFromReadSeek(symbols) {
|
|
2527
|
+
const symbolsByQualifiedName = new Map;
|
|
2528
|
+
const entries = [];
|
|
2529
|
+
for (const symbol of symbols) {
|
|
2530
|
+
const parentQualifiedName = parentQualifiedNameFor(symbol.qualified_name);
|
|
2531
|
+
const fileSymbol = {
|
|
2532
|
+
name: symbol.name,
|
|
2533
|
+
kind: normalizeKind(symbol.kind),
|
|
2534
|
+
startLine: symbol.start_line,
|
|
2535
|
+
endLine: symbol.end_line
|
|
2536
|
+
};
|
|
2537
|
+
const bucket = symbolsByQualifiedName.get(symbol.qualified_name);
|
|
2538
|
+
if (bucket)
|
|
2539
|
+
bucket.push(fileSymbol);
|
|
2540
|
+
else
|
|
2541
|
+
symbolsByQualifiedName.set(symbol.qualified_name, [fileSymbol]);
|
|
2542
|
+
entries.push({ parentQualifiedName, symbol: fileSymbol });
|
|
2543
|
+
}
|
|
2544
|
+
const roots = [];
|
|
2545
|
+
for (const entry of entries) {
|
|
2546
|
+
const parent = entry.parentQualifiedName ? symbolsByQualifiedName.get(entry.parentQualifiedName)?.[0] : undefined;
|
|
2547
|
+
if (!parent) {
|
|
2548
|
+
roots.push(entry.symbol);
|
|
2549
|
+
continue;
|
|
2550
|
+
}
|
|
2551
|
+
parent.children ??= [];
|
|
2552
|
+
parent.children.push(entry.symbol);
|
|
2553
|
+
}
|
|
2554
|
+
return roots;
|
|
2218
2555
|
}
|
|
2219
2556
|
function fileMapFromReadSeekOutput(output, filePath, totalBytes) {
|
|
2220
2557
|
if (output.language === "unknown" && output.symbols.length === 0)
|
|
@@ -2229,196 +2566,11 @@ function fileMapFromReadSeekOutput(output, filePath, totalBytes) {
|
|
|
2229
2566
|
};
|
|
2230
2567
|
}
|
|
2231
2568
|
async function readSeekMap(filePath, totalBytes, options = {}) {
|
|
2232
|
-
const envelope = await
|
|
2569
|
+
const envelope = await client.digest(filePath, { select: "map", signal: options.signal });
|
|
2233
2570
|
if (!envelope.map)
|
|
2234
2571
|
return null;
|
|
2235
2572
|
return fileMapFromReadSeekOutput(envelope.map, filePath, totalBytes);
|
|
2236
2573
|
}
|
|
2237
|
-
async function readSeekSearch(target, pattern, options = {}) {
|
|
2238
|
-
const args = buildSearchArgs({
|
|
2239
|
-
path: target,
|
|
2240
|
-
pattern,
|
|
2241
|
-
language: options.language,
|
|
2242
|
-
cached: options.cached,
|
|
2243
|
-
others: options.others,
|
|
2244
|
-
ignored: options.ignored
|
|
2245
|
-
});
|
|
2246
|
-
return parseSearchOutput(await runReadSeek(args, { signal: options.signal })).results;
|
|
2247
|
-
}
|
|
2248
|
-
async function readSeekRefs(target, options = {}) {
|
|
2249
|
-
const args = buildRefsArgs({
|
|
2250
|
-
path: target,
|
|
2251
|
-
name: options.name,
|
|
2252
|
-
scope: options.scope,
|
|
2253
|
-
line: options.line,
|
|
2254
|
-
column: options.column,
|
|
2255
|
-
language: options.language,
|
|
2256
|
-
cached: options.cached,
|
|
2257
|
-
others: options.others,
|
|
2258
|
-
ignored: options.ignored
|
|
2259
|
-
});
|
|
2260
|
-
return parseRefsOutput(await runReadSeek(args, { signal: options.signal })).references;
|
|
2261
|
-
}
|
|
2262
|
-
async function readSeekCheck(filePath, content, options = {}) {
|
|
2263
|
-
const envelope = await readSeekDigest("-", {
|
|
2264
|
-
select: "diagnostics",
|
|
2265
|
-
stdin: content,
|
|
2266
|
-
stdinName: filePath,
|
|
2267
|
-
signal: options.signal
|
|
2268
|
-
});
|
|
2269
|
-
if (!envelope.diagnostics)
|
|
2270
|
-
throw new Error("invalid readseek diagnostics: missing diagnostics facet");
|
|
2271
|
-
return envelope.diagnostics;
|
|
2272
|
-
}
|
|
2273
|
-
function parseEditOutput(value) {
|
|
2274
|
-
if (!value || typeof value !== "object")
|
|
2275
|
-
throw new Error("invalid readseek edit output");
|
|
2276
|
-
const output = value;
|
|
2277
|
-
return {
|
|
2278
|
-
file: requireString(output.file, "file"),
|
|
2279
|
-
before_hash: requireString(output.before_hash, "before_hash"),
|
|
2280
|
-
file_hash: requireString(output.file_hash, "file_hash"),
|
|
2281
|
-
plan_hash: requireString(output.plan_hash, "plan_hash"),
|
|
2282
|
-
applied: requireBoolean(output.applied, "applied"),
|
|
2283
|
-
changed: requireBoolean(output.changed, "changed"),
|
|
2284
|
-
edit_count: requireNumber(output.edit_count, "edit_count"),
|
|
2285
|
-
before_content: optionalString(output.before_content, "before_content"),
|
|
2286
|
-
content: optionalString(output.content, "content")
|
|
2287
|
-
};
|
|
2288
|
-
}
|
|
2289
|
-
async function readSeekEdit(filePath, edits, options = {}) {
|
|
2290
|
-
const args = buildEditArgs({
|
|
2291
|
-
path: filePath,
|
|
2292
|
-
apply: options.apply,
|
|
2293
|
-
planHash: options.planHash,
|
|
2294
|
-
language: options.language
|
|
2295
|
-
});
|
|
2296
|
-
return parseEditOutput(await runReadSeek(args, {
|
|
2297
|
-
signal: options.signal,
|
|
2298
|
-
stdin: JSON.stringify({ edits }),
|
|
2299
|
-
timeoutMs: options.timeoutMs,
|
|
2300
|
-
cancelable: options.cancelable
|
|
2301
|
-
}));
|
|
2302
|
-
}
|
|
2303
|
-
function parseRenameConflicts(value, field) {
|
|
2304
|
-
if (value === undefined)
|
|
2305
|
-
return [];
|
|
2306
|
-
if (!Array.isArray(value))
|
|
2307
|
-
throw new Error(`invalid readseek ${field}`);
|
|
2308
|
-
return value.map((item) => {
|
|
2309
|
-
if (!item || typeof item !== "object")
|
|
2310
|
-
throw new Error(`invalid readseek ${field} entry`);
|
|
2311
|
-
const c = item;
|
|
2312
|
-
return {
|
|
2313
|
-
line: requireNumber(c.line, `${field}.line`),
|
|
2314
|
-
column: requireNumber(c.column, `${field}.column`),
|
|
2315
|
-
reason: requireString(c.reason, `${field}.reason`)
|
|
2316
|
-
};
|
|
2317
|
-
});
|
|
2318
|
-
}
|
|
2319
|
-
function parseRenameEdits(value, field) {
|
|
2320
|
-
if (value === undefined)
|
|
2321
|
-
return [];
|
|
2322
|
-
if (!Array.isArray(value))
|
|
2323
|
-
throw new Error(`invalid readseek ${field}`);
|
|
2324
|
-
return value.map((item) => {
|
|
2325
|
-
if (!item || typeof item !== "object")
|
|
2326
|
-
throw new Error(`invalid readseek ${field} entry`);
|
|
2327
|
-
const e = item;
|
|
2328
|
-
return {
|
|
2329
|
-
line: requireNumber(e.line, `${field}.line`),
|
|
2330
|
-
start_column: requireNumber(e.start_column, `${field}.start_column`),
|
|
2331
|
-
end_column: requireNumber(e.end_column, `${field}.end_column`),
|
|
2332
|
-
start_byte: requireNumber(e.start_byte, `${field}.start_byte`),
|
|
2333
|
-
end_byte: requireNumber(e.end_byte, `${field}.end_byte`),
|
|
2334
|
-
occurrence: requireString(e.occurrence, `${field}.occurrence`),
|
|
2335
|
-
line_hash: requireString(e.line_hash, `${field}.line_hash`),
|
|
2336
|
-
text: requireString(e.text, `${field}.text`)
|
|
2337
|
-
};
|
|
2338
|
-
});
|
|
2339
|
-
}
|
|
2340
|
-
function parseRenameOutput(value) {
|
|
2341
|
-
if (!value || typeof value !== "object")
|
|
2342
|
-
throw new Error("invalid readseek rename output");
|
|
2343
|
-
const output = value;
|
|
2344
|
-
const others = output.others;
|
|
2345
|
-
if (others !== undefined && !Array.isArray(others))
|
|
2346
|
-
throw new Error("invalid readseek others");
|
|
2347
|
-
return {
|
|
2348
|
-
file: requireString(output.file, "file"),
|
|
2349
|
-
language: requireString(output.language, "language"),
|
|
2350
|
-
engine: optionalString(output.engine, "engine"),
|
|
2351
|
-
file_hash: requireString(output.file_hash, "file_hash"),
|
|
2352
|
-
old_name: requireString(output.old_name, "old_name"),
|
|
2353
|
-
new_name: requireString(output.new_name, "new_name"),
|
|
2354
|
-
applied: requireBoolean(output.applied, "applied"),
|
|
2355
|
-
conflicts: parseRenameConflicts(output.conflicts, "conflicts"),
|
|
2356
|
-
edits: parseRenameEdits(output.edits, "edits"),
|
|
2357
|
-
others: others?.map((entry) => {
|
|
2358
|
-
if (!entry || typeof entry !== "object")
|
|
2359
|
-
throw new Error("invalid readseek other");
|
|
2360
|
-
const o = entry;
|
|
2361
|
-
return {
|
|
2362
|
-
file: requireString(o.file, "other.file"),
|
|
2363
|
-
language: requireString(o.language, "other.language"),
|
|
2364
|
-
engine: optionalString(o.engine, "other.engine"),
|
|
2365
|
-
file_hash: requireString(o.file_hash, "other.file_hash"),
|
|
2366
|
-
conflicts: parseRenameConflicts(o.conflicts, "other.conflicts"),
|
|
2367
|
-
edits: parseRenameEdits(o.edits, "other.edits")
|
|
2368
|
-
};
|
|
2369
|
-
}) ?? []
|
|
2370
|
-
};
|
|
2371
|
-
}
|
|
2372
|
-
async function readSeekRename(filePath, options) {
|
|
2373
|
-
const args = buildRenameArgs({
|
|
2374
|
-
path: filePath,
|
|
2375
|
-
to: options.to,
|
|
2376
|
-
line: options.line,
|
|
2377
|
-
column: options.column,
|
|
2378
|
-
workspace: options.workspace,
|
|
2379
|
-
apply: options.apply,
|
|
2380
|
-
language: options.language,
|
|
2381
|
-
cached: options.cached,
|
|
2382
|
-
others: options.others,
|
|
2383
|
-
ignored: options.ignored
|
|
2384
|
-
});
|
|
2385
|
-
return parseRenameOutput(await runReadSeek(args, { signal: options.signal }));
|
|
2386
|
-
}
|
|
2387
|
-
function parseDefCompact(value) {
|
|
2388
|
-
if (!value || typeof value !== "object")
|
|
2389
|
-
throw new Error("invalid readseek def output");
|
|
2390
|
-
const output = value;
|
|
2391
|
-
const locations = output.locations;
|
|
2392
|
-
if (!Array.isArray(locations))
|
|
2393
|
-
throw new Error("invalid readseek def locations");
|
|
2394
|
-
return locations.map((loc) => {
|
|
2395
|
-
if (!loc || typeof loc !== "object")
|
|
2396
|
-
throw new Error("invalid readseek def location");
|
|
2397
|
-
const item = loc;
|
|
2398
|
-
return {
|
|
2399
|
-
file: requireString(item.file, "location.file"),
|
|
2400
|
-
line: requireNumber(item.line, "location.line"),
|
|
2401
|
-
column: requireNumber(item.column, "location.column"),
|
|
2402
|
-
line_hash: requireString(item.line_hash, "location.line_hash"),
|
|
2403
|
-
text: requireString(item.text, "location.text"),
|
|
2404
|
-
kind: optionalString(item.kind, "location.kind"),
|
|
2405
|
-
name: optionalString(item.name, "location.name"),
|
|
2406
|
-
qualified_name: optionalString(item.qualified_name, "location.qualified_name")
|
|
2407
|
-
};
|
|
2408
|
-
});
|
|
2409
|
-
}
|
|
2410
|
-
async function readSeekDef(target, options) {
|
|
2411
|
-
const args = buildDefArgs({
|
|
2412
|
-
path: target,
|
|
2413
|
-
name: options.name,
|
|
2414
|
-
format: "plain",
|
|
2415
|
-
language: options.language,
|
|
2416
|
-
cached: options.cached,
|
|
2417
|
-
others: options.others,
|
|
2418
|
-
ignored: options.ignored
|
|
2419
|
-
});
|
|
2420
|
-
return parseDefCompact(await runReadSeek(args, { signal: options.signal }));
|
|
2421
|
-
}
|
|
2422
2574
|
|
|
2423
2575
|
// src/edit-syntax-validate.ts
|
|
2424
2576
|
function dedupeSortLines(diagnostics) {
|
|
@@ -5584,7 +5736,7 @@ var READSEEK_TOOL_ENTRIES = [
|
|
|
5584
5736
|
{ builtIn: null, readSeekName: "readSeek_rename" },
|
|
5585
5737
|
{ builtIn: "write", readSeekName: "readSeek_write" },
|
|
5586
5738
|
{ builtIn: null, readSeekName: "readSeek_def" },
|
|
5587
|
-
{ builtIn:
|
|
5739
|
+
{ builtIn: null, readSeekName: "readSeek_digest" },
|
|
5588
5740
|
{ builtIn: null, readSeekName: "readSeek_view" }
|
|
5589
5741
|
];
|
|
5590
5742
|
function formatSettingsWarning(warning) {
|
|
@@ -5605,12 +5757,10 @@ function piReadSeekExtension(pi) {
|
|
|
5605
5757
|
const replacedBuiltIns = new Set(settings.overrideTools ?? []);
|
|
5606
5758
|
const binaryAvailable = readSeekBinaryAvailability().available;
|
|
5607
5759
|
const swap = (builtIn, readSeekName) => binaryAvailable && replacedBuiltIns.has(builtIn) ? builtIn : readSeekName;
|
|
5608
|
-
const readName = swap("read", "readSeek_digest");
|
|
5609
5760
|
const editName = swap("edit", "readSeek_edit");
|
|
5610
5761
|
const writeName = swap("write", "readSeek_write");
|
|
5611
5762
|
const grepName = swap("grep", "readSeek_grep");
|
|
5612
5763
|
const toolAliases = {
|
|
5613
|
-
readSeek_digest: readName,
|
|
5614
5764
|
readSeek_edit: editName,
|
|
5615
5765
|
readSeek_grep: grepName,
|
|
5616
5766
|
readSeek_write: writeName
|
|
@@ -5625,7 +5775,7 @@ function piReadSeekExtension(pi) {
|
|
|
5625
5775
|
registerRefsTool(pi, { onFileAnchored: markAnchored });
|
|
5626
5776
|
registerRenameTool(pi, { onFileMutated: forgetAnchors });
|
|
5627
5777
|
registerDefTool(pi, { onFileAnchored: markAnchored });
|
|
5628
|
-
registerDigestTool(pi, { onFileAnchored: markAnchored, name:
|
|
5778
|
+
registerDigestTool(pi, { onFileAnchored: markAnchored, name: "readSeek_digest", toolAliases });
|
|
5629
5779
|
registerViewTool(pi);
|
|
5630
5780
|
registerWriteTool(pi, { onFileAnchored: markAnchored, name: writeName });
|
|
5631
5781
|
pi.on("before_agent_start", (event) => {
|