pi-readseek 0.9.9 → 0.9.11
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 +12 -8
- package/dist/index.ts +1456 -1231
- package/package.json +5 -5
package/dist/index.ts
CHANGED
|
@@ -661,6 +661,7 @@ function buildRenameArgs(input) {
|
|
|
661
661
|
return appendGitSelectionArgs(args, input);
|
|
662
662
|
}
|
|
663
663
|
// ../readseek-api/src/platform.ts
|
|
664
|
+
import { accessSync, constants as fsConstants } from "node:fs";
|
|
664
665
|
import { createRequire } from "node:module";
|
|
665
666
|
import path from "node:path";
|
|
666
667
|
var READSEEK_PLATFORM_PACKAGES = {
|
|
@@ -689,10 +690,17 @@ function resolveReadSeekBinaryFromPackages(resolver = resolveFromPackage, option
|
|
|
689
690
|
const metaPackage = options.metaPackage ?? "@jarkkojs/readseek";
|
|
690
691
|
const metaPackageJson = resolve(`${metaPackage}/package.json`);
|
|
691
692
|
const metaPackageDir = path.dirname(metaPackageJson);
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
693
|
+
let packageJsonPath;
|
|
694
|
+
try {
|
|
695
|
+
packageJsonPath = resolve(`${platformPackage}/package.json`);
|
|
696
|
+
} catch {
|
|
697
|
+
packageJsonPath = resolve(`${platformPackage}/package.json`, {
|
|
698
|
+
paths: [metaPackageDir]
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
const binaryPath = path.join(path.dirname(packageJsonPath), "bin", readSeekBinaryName(platform));
|
|
702
|
+
accessSync(binaryPath, fsConstants.X_OK);
|
|
703
|
+
return binaryPath;
|
|
696
704
|
}
|
|
697
705
|
// ../readseek-api/src/json.ts
|
|
698
706
|
var READSEEK_USAGE_HINT = /\n\s*Run readseek(?: [\w-]+)* --help for more information\.?\s*$/;
|
|
@@ -854,139 +862,805 @@ async function runReadSeekNodeText(binaryPath, args, options = {}) {
|
|
|
854
862
|
async function runReadSeekNodeJson(binaryPath, args, options = {}) {
|
|
855
863
|
return parseSuccessfulJson(await runReadSeekNode(binaryPath, args, options));
|
|
856
864
|
}
|
|
857
|
-
// src/
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
const
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
865
|
+
// ../readseek-api/src/client.ts
|
|
866
|
+
function classifyReadSeekFailure(err) {
|
|
867
|
+
const failure = err;
|
|
868
|
+
const message = String(failure?.message ?? err);
|
|
869
|
+
const missing = failure?.code === "ENOENT" || /Cannot find package|Cannot find module|no such file/i.test(message);
|
|
870
|
+
if (missing) {
|
|
871
|
+
return { code: "readseek-not-installed", message, hint: "Run npm install to install @jarkkojs/readseek." };
|
|
872
|
+
}
|
|
873
|
+
return { code: "readseek-execution-error", message };
|
|
864
874
|
}
|
|
865
|
-
function
|
|
866
|
-
|
|
875
|
+
function requireNumber(value, field) {
|
|
876
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value))
|
|
877
|
+
throw new Error(`invalid readseek ${field}: expected safe integer`);
|
|
878
|
+
return value;
|
|
867
879
|
}
|
|
868
|
-
function
|
|
869
|
-
if (
|
|
870
|
-
|
|
871
|
-
return
|
|
880
|
+
function requireString(value, field) {
|
|
881
|
+
if (typeof value !== "string")
|
|
882
|
+
throw new Error(`invalid readseek ${field}`);
|
|
883
|
+
return value;
|
|
872
884
|
}
|
|
873
|
-
function
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
return [name, alias];
|
|
878
|
-
}));
|
|
885
|
+
function requireBoolean(value, field) {
|
|
886
|
+
if (typeof value !== "boolean")
|
|
887
|
+
throw new Error(`invalid readseek ${field}: expected boolean`);
|
|
888
|
+
return value;
|
|
879
889
|
}
|
|
880
|
-
function
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
890
|
+
function optionalString(value, field) {
|
|
891
|
+
if (value === undefined || value === null)
|
|
892
|
+
return;
|
|
893
|
+
return requireString(value, field);
|
|
894
|
+
}
|
|
895
|
+
function parseHashline(value, field) {
|
|
896
|
+
if (!value || typeof value !== "object")
|
|
897
|
+
throw new Error(`invalid readseek ${field}`);
|
|
898
|
+
const item = value;
|
|
888
899
|
return {
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
...preferenceGuideline ? [preferenceGuideline] : [],
|
|
893
|
-
...renderCompactGuidelines(toolName, aliases)
|
|
894
|
-
]
|
|
900
|
+
line: requireNumber(item.line, `${field}.line`),
|
|
901
|
+
hash: requireString(item.hash, `${field}.hash`),
|
|
902
|
+
text: requireString(item.text, `${field}.text`)
|
|
895
903
|
};
|
|
896
904
|
}
|
|
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;
|
|
905
|
+
function parseSearchHashlines(value, field) {
|
|
906
|
+
if (!Array.isArray(value))
|
|
907
|
+
throw new Error(`invalid readseek ${field}`);
|
|
908
|
+
return value.map((line) => parseHashline(line, field));
|
|
916
909
|
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
910
|
+
function parseReadOutput(value) {
|
|
911
|
+
if (!value || typeof value !== "object")
|
|
912
|
+
throw new Error("invalid readseek read output");
|
|
913
|
+
const output = value;
|
|
914
|
+
const hashlines = output.hashlines;
|
|
915
|
+
if (!Array.isArray(hashlines))
|
|
916
|
+
throw new Error("invalid readseek hashlines");
|
|
917
|
+
return {
|
|
918
|
+
file: requireString(output.file, "file"),
|
|
919
|
+
language: requireString(output.language, "language"),
|
|
920
|
+
line_count: requireNumber(output.line_count, "line_count"),
|
|
921
|
+
file_hash: requireString(output.file_hash, "file_hash"),
|
|
922
|
+
start_line: requireNumber(output.start_line, "start_line"),
|
|
923
|
+
end_line: requireNumber(output.end_line, "end_line"),
|
|
924
|
+
hashlines: hashlines.map((line) => parseHashline(line, "hashline"))
|
|
925
|
+
};
|
|
927
926
|
}
|
|
928
|
-
function
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
927
|
+
function parseMapOutput(value) {
|
|
928
|
+
if (!value || typeof value !== "object")
|
|
929
|
+
throw new Error("invalid readseek map output");
|
|
930
|
+
const output = value;
|
|
931
|
+
const symbols = output.symbols;
|
|
932
|
+
if (!Array.isArray(symbols))
|
|
933
|
+
throw new Error("invalid readseek symbols");
|
|
934
|
+
return {
|
|
935
|
+
file: requireString(output.file, "file"),
|
|
936
|
+
language: requireString(output.language, "language"),
|
|
937
|
+
line_count: requireNumber(output.line_count, "line_count"),
|
|
938
|
+
file_hash: requireString(output.file_hash, "file_hash"),
|
|
939
|
+
symbols: symbols.map((symbol) => {
|
|
940
|
+
if (!symbol || typeof symbol !== "object")
|
|
941
|
+
throw new Error("invalid readseek symbol");
|
|
942
|
+
const item = symbol;
|
|
943
|
+
return {
|
|
944
|
+
kind: requireString(item.kind, "symbol.kind"),
|
|
945
|
+
name: requireString(item.name, "symbol.name"),
|
|
946
|
+
qualified_name: requireString(item.qualified_name, "symbol.qualified_name"),
|
|
947
|
+
start_line: requireNumber(item.start_line, "symbol.start_line"),
|
|
948
|
+
end_line: requireNumber(item.end_line, "symbol.end_line"),
|
|
949
|
+
start_hash: requireString(item.start_hash, "symbol.start_hash"),
|
|
950
|
+
end_hash: requireString(item.end_hash, "symbol.end_hash")
|
|
951
|
+
};
|
|
952
|
+
})
|
|
953
|
+
};
|
|
933
954
|
}
|
|
934
|
-
function
|
|
935
|
-
if (
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
955
|
+
function parseSearchOutput(value) {
|
|
956
|
+
if (!value || typeof value !== "object")
|
|
957
|
+
throw new Error("invalid readseek search output");
|
|
958
|
+
const output = value;
|
|
959
|
+
if (!Array.isArray(output.results))
|
|
960
|
+
throw new Error("invalid readseek search results");
|
|
961
|
+
return output.results.map((result) => {
|
|
962
|
+
if (!result || typeof result !== "object")
|
|
963
|
+
throw new Error("invalid readseek search result");
|
|
964
|
+
const file = result;
|
|
965
|
+
if (!Array.isArray(file.matches))
|
|
966
|
+
throw new Error("invalid readseek search matches");
|
|
967
|
+
return {
|
|
968
|
+
file: requireString(file.file, "search.file"),
|
|
969
|
+
language: requireString(file.language, "search.language"),
|
|
970
|
+
file_hash: requireString(file.file_hash, "search.file_hash"),
|
|
971
|
+
matches: file.matches.map((match) => {
|
|
972
|
+
if (!match || typeof match !== "object")
|
|
973
|
+
throw new Error("invalid readseek search match");
|
|
974
|
+
const item = match;
|
|
975
|
+
if (!Array.isArray(item.captures))
|
|
976
|
+
throw new Error("invalid readseek search captures");
|
|
977
|
+
return {
|
|
978
|
+
pattern_index: item.pattern_index === undefined ? 0 : requireNumber(item.pattern_index, "search.match.pattern_index"),
|
|
979
|
+
start_line: requireNumber(item.start_line, "search.match.start_line"),
|
|
980
|
+
end_line: requireNumber(item.end_line, "search.match.end_line"),
|
|
981
|
+
start_hash: requireString(item.start_hash, "search.match.start_hash"),
|
|
982
|
+
end_hash: requireString(item.end_hash, "search.match.end_hash"),
|
|
983
|
+
hashlines: parseSearchHashlines(item.hashlines, "search.match.hashlines"),
|
|
984
|
+
captures: item.captures.map((capture) => {
|
|
985
|
+
if (!capture || typeof capture !== "object")
|
|
986
|
+
throw new Error("invalid readseek search capture");
|
|
987
|
+
const captureItem = capture;
|
|
988
|
+
return {
|
|
989
|
+
name: requireString(captureItem.name, "search.capture.name"),
|
|
990
|
+
start_line: requireNumber(captureItem.start_line, "search.capture.start_line"),
|
|
991
|
+
end_line: requireNumber(captureItem.end_line, "search.capture.end_line"),
|
|
992
|
+
start_hash: requireString(captureItem.start_hash, "search.capture.start_hash"),
|
|
993
|
+
end_hash: requireString(captureItem.end_hash, "search.capture.end_hash"),
|
|
994
|
+
hashlines: parseSearchHashlines(captureItem.hashlines, "search.capture.hashlines")
|
|
995
|
+
};
|
|
996
|
+
})
|
|
997
|
+
};
|
|
998
|
+
})
|
|
999
|
+
};
|
|
1000
|
+
});
|
|
939
1001
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1002
|
+
function parseRefsOutput(value) {
|
|
1003
|
+
if (!value || typeof value !== "object")
|
|
1004
|
+
throw new Error("invalid readseek refs output");
|
|
1005
|
+
const output = value;
|
|
1006
|
+
if (!Array.isArray(output.references))
|
|
1007
|
+
throw new Error("invalid readseek references");
|
|
1008
|
+
return output.references.map((reference) => {
|
|
1009
|
+
if (!reference || typeof reference !== "object")
|
|
1010
|
+
throw new Error("invalid readseek reference");
|
|
1011
|
+
const item = reference;
|
|
1012
|
+
const symbol = item.symbol;
|
|
1013
|
+
const enclosing = symbol && typeof symbol === "object" ? optionalString(symbol.qualified_name, "reference.symbol.qualified_name") : undefined;
|
|
1014
|
+
return {
|
|
1015
|
+
file: requireString(item.file, "reference.file"),
|
|
1016
|
+
line: requireNumber(item.line, "reference.line"),
|
|
1017
|
+
column: requireNumber(item.column, "reference.column"),
|
|
1018
|
+
line_hash: requireString(item.line_hash, "reference.line_hash"),
|
|
1019
|
+
text: requireString(item.text, "reference.text"),
|
|
1020
|
+
enclosingSymbol: enclosing
|
|
1021
|
+
};
|
|
944
1022
|
});
|
|
945
1023
|
}
|
|
946
|
-
function
|
|
947
|
-
|
|
1024
|
+
function parseDiagnosticKind(value) {
|
|
1025
|
+
if (value === "error" || value === "missing")
|
|
1026
|
+
return value;
|
|
1027
|
+
throw new Error("invalid readseek diagnostic.kind");
|
|
948
1028
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1029
|
+
function parseCheckOutput(value) {
|
|
1030
|
+
if (!value || typeof value !== "object")
|
|
1031
|
+
throw new Error("invalid readseek check output");
|
|
1032
|
+
const output = value;
|
|
1033
|
+
if (!Array.isArray(output.diagnostics))
|
|
1034
|
+
throw new Error("invalid readseek diagnostics");
|
|
1035
|
+
return {
|
|
1036
|
+
errorCount: requireNumber(output.error_count, "error_count"),
|
|
1037
|
+
missingCount: requireNumber(output.missing_count, "missing_count"),
|
|
1038
|
+
diagnostics: output.diagnostics.map((diagnostic) => {
|
|
1039
|
+
if (!diagnostic || typeof diagnostic !== "object")
|
|
1040
|
+
throw new Error("invalid readseek diagnostic");
|
|
1041
|
+
const item = diagnostic;
|
|
1042
|
+
return {
|
|
1043
|
+
kind: parseDiagnosticKind(item.kind),
|
|
1044
|
+
start_line: requireNumber(item.start_line, "diagnostic.start_line"),
|
|
1045
|
+
end_line: requireNumber(item.end_line, "diagnostic.end_line")
|
|
1046
|
+
};
|
|
1047
|
+
})
|
|
1048
|
+
};
|
|
955
1049
|
}
|
|
956
|
-
function
|
|
957
|
-
|
|
1050
|
+
function parseDetectedObjects(value) {
|
|
1051
|
+
if (value === undefined || value === null)
|
|
1052
|
+
return;
|
|
1053
|
+
if (!Array.isArray(value))
|
|
1054
|
+
throw new Error("invalid readseek detect objects");
|
|
1055
|
+
return value.map((object) => {
|
|
1056
|
+
if (!object || typeof object !== "object")
|
|
1057
|
+
throw new Error("invalid readseek detect object");
|
|
1058
|
+
const item = object;
|
|
1059
|
+
const bbox = item.bbox;
|
|
1060
|
+
if (!Array.isArray(bbox) || bbox.length !== 4)
|
|
1061
|
+
throw new Error("invalid readseek detect object.bbox");
|
|
1062
|
+
return {
|
|
1063
|
+
label: requireString(item.label, "object.label"),
|
|
1064
|
+
bbox: bbox.map((n, i) => requireNumber(n, `object.bbox[${i}]`))
|
|
1065
|
+
};
|
|
1066
|
+
});
|
|
958
1067
|
}
|
|
959
|
-
function
|
|
960
|
-
|
|
1068
|
+
function parseImageEncoding(value) {
|
|
1069
|
+
if (value === "base64")
|
|
1070
|
+
return value;
|
|
1071
|
+
throw new Error("invalid image encoding");
|
|
961
1072
|
}
|
|
962
|
-
function
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
const
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
const
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1073
|
+
function parseDetectOutput(value) {
|
|
1074
|
+
if (!value || typeof value !== "object")
|
|
1075
|
+
throw new Error("invalid readseek detect output");
|
|
1076
|
+
const output = value;
|
|
1077
|
+
const type = requireString(output.type, "type");
|
|
1078
|
+
const file = requireString(output.file, "file");
|
|
1079
|
+
const mime = optionalString(output.mime, "mime");
|
|
1080
|
+
if (type === "application/pdf") {
|
|
1081
|
+
if (output.format !== "pdf")
|
|
1082
|
+
throw new Error("invalid readseek PDF format");
|
|
1083
|
+
return {
|
|
1084
|
+
kind: "pdf",
|
|
1085
|
+
type,
|
|
1086
|
+
file,
|
|
1087
|
+
mime,
|
|
1088
|
+
format: output.format,
|
|
1089
|
+
pages: requireNumber(output.pages, "pages")
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
if (output.width !== undefined || output.height !== undefined) {
|
|
1093
|
+
return {
|
|
1094
|
+
kind: "image",
|
|
1095
|
+
type,
|
|
1096
|
+
file,
|
|
1097
|
+
mime,
|
|
1098
|
+
format: requireString(output.format, "format"),
|
|
1099
|
+
width: requireNumber(output.width, "width"),
|
|
1100
|
+
height: requireNumber(output.height, "height"),
|
|
1101
|
+
animated: requireBoolean(output.animated, "animated"),
|
|
1102
|
+
encoding: output.encoding === undefined ? undefined : parseImageEncoding(output.encoding),
|
|
1103
|
+
data: output.data === undefined ? undefined : requireString(output.data, "data"),
|
|
1104
|
+
ocr: optionalString(output.ocr, "ocr"),
|
|
1105
|
+
caption: optionalString(output.caption, "caption"),
|
|
1106
|
+
objects: parseDetectedObjects(output.objects)
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
if (output.language !== undefined) {
|
|
1110
|
+
return {
|
|
1111
|
+
kind: "source",
|
|
1112
|
+
type,
|
|
1113
|
+
file,
|
|
1114
|
+
language: requireString(output.language, "language"),
|
|
1115
|
+
engine: optionalString(output.engine, "engine"),
|
|
1116
|
+
supported: requireBoolean(output.supported, "supported"),
|
|
1117
|
+
mime,
|
|
1118
|
+
syntax: optionalString(output.syntax, "syntax")
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
return { kind: "text", type, file, mime };
|
|
1122
|
+
}
|
|
1123
|
+
function parseDocumentCache(value) {
|
|
1124
|
+
if (!value || typeof value !== "object")
|
|
1125
|
+
throw new Error("invalid readseek document cache");
|
|
1126
|
+
const cache = value;
|
|
1127
|
+
const state = requireString(cache.state, "cache.state");
|
|
1128
|
+
if (state !== "built" && state !== "reused")
|
|
1129
|
+
throw new Error("invalid readseek cache.state");
|
|
1130
|
+
return {
|
|
1131
|
+
content_hash: requireString(cache.content_hash, "cache.content_hash"),
|
|
1132
|
+
state
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
function parseIdentifyOutput(value) {
|
|
1136
|
+
if (!value || typeof value !== "object")
|
|
1137
|
+
throw new Error("invalid readseek identify output");
|
|
1138
|
+
const output = value;
|
|
1139
|
+
const identifier = output.identifier;
|
|
1140
|
+
const symbol = output.symbol;
|
|
1141
|
+
return {
|
|
1142
|
+
file: requireString(output.file, "file"),
|
|
1143
|
+
language: requireString(output.language, "language"),
|
|
1144
|
+
engine: optionalString(output.engine, "engine"),
|
|
1145
|
+
line_count: requireNumber(output.line_count, "line_count"),
|
|
1146
|
+
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1147
|
+
line: requireNumber(output.line, "line"),
|
|
1148
|
+
column: requireNumber(output.column, "column"),
|
|
1149
|
+
line_hash: requireString(output.line_hash, "line_hash"),
|
|
1150
|
+
identifier: identifier && typeof identifier === "object" ? {
|
|
1151
|
+
text: requireString(identifier.text, "identifier.text"),
|
|
1152
|
+
start_column: requireNumber(identifier.start_column, "identifier.start_column"),
|
|
1153
|
+
end_column: requireNumber(identifier.end_column, "identifier.end_column"),
|
|
1154
|
+
start_byte: requireNumber(identifier.start_byte, "identifier.start_byte"),
|
|
1155
|
+
end_byte: requireNumber(identifier.end_byte, "identifier.end_byte")
|
|
1156
|
+
} : undefined,
|
|
1157
|
+
symbol: symbol && typeof symbol === "object" ? {
|
|
1158
|
+
name: requireString(symbol.name, "symbol.name"),
|
|
1159
|
+
kind: requireString(symbol.kind, "symbol.kind"),
|
|
1160
|
+
qualified_name: requireString(symbol.qualified_name, "symbol.qualified_name"),
|
|
1161
|
+
start_line: requireNumber(symbol.start_line, "symbol.start_line"),
|
|
1162
|
+
end_line: requireNumber(symbol.end_line, "symbol.end_line")
|
|
1163
|
+
} : undefined
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
function parseImageContent(value) {
|
|
1167
|
+
const detection = parseDetectOutput(value);
|
|
1168
|
+
if (detection.kind !== "image")
|
|
1169
|
+
throw new Error("invalid readseek image content");
|
|
1170
|
+
return detection;
|
|
1171
|
+
}
|
|
1172
|
+
function parseTextContent(value) {
|
|
1173
|
+
if (!value || typeof value !== "object")
|
|
1174
|
+
throw new Error("invalid readseek content");
|
|
1175
|
+
const output = value;
|
|
1176
|
+
if (output.hashlines !== undefined && output.start_line === undefined) {
|
|
1177
|
+
const hashlines = parseSearchHashlines(output.hashlines, "content.hashlines");
|
|
1178
|
+
const first = hashlines[0];
|
|
1179
|
+
const last = hashlines[hashlines.length - 1];
|
|
1180
|
+
return {
|
|
1181
|
+
file: requireString(output.file, "file"),
|
|
1182
|
+
language: requireString(output.language, "language"),
|
|
1183
|
+
line_count: requireNumber(output.line_count, "line_count"),
|
|
1184
|
+
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1185
|
+
start_line: first?.line ?? 1,
|
|
1186
|
+
end_line: last?.line ?? 0,
|
|
1187
|
+
hashlines
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
return parseReadOutput(value);
|
|
1191
|
+
}
|
|
1192
|
+
function parseDigestEnvelope(value) {
|
|
1193
|
+
if (!value || typeof value !== "object")
|
|
1194
|
+
throw new Error("invalid readseek digest output");
|
|
1195
|
+
const output = value;
|
|
1196
|
+
if (output.metadata === undefined)
|
|
1197
|
+
throw new Error("invalid readseek digest metadata");
|
|
1198
|
+
const result = {
|
|
1199
|
+
metadata: parseDetectOutput(output.metadata)
|
|
1200
|
+
};
|
|
1201
|
+
if (output.cache !== undefined)
|
|
1202
|
+
result.cache = parseDocumentCache(output.cache);
|
|
1203
|
+
if (output.content !== undefined)
|
|
1204
|
+
result.content = output.content;
|
|
1205
|
+
if (output.map !== undefined)
|
|
1206
|
+
result.map = parseMapOutput(output.map);
|
|
1207
|
+
if (output.diagnostics !== undefined)
|
|
1208
|
+
result.diagnostics = parseCheckOutput(output.diagnostics);
|
|
1209
|
+
if (output.identity !== undefined)
|
|
1210
|
+
result.identity = parseIdentifyOutput(output.identity);
|
|
1211
|
+
return result;
|
|
1212
|
+
}
|
|
1213
|
+
function parseEditOutput(value) {
|
|
1214
|
+
if (!value || typeof value !== "object")
|
|
1215
|
+
throw new Error("invalid readseek edit output");
|
|
1216
|
+
const output = value;
|
|
1217
|
+
return {
|
|
1218
|
+
file: requireString(output.file, "file"),
|
|
1219
|
+
before_hash: requireString(output.before_hash, "before_hash"),
|
|
1220
|
+
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1221
|
+
plan_hash: requireString(output.plan_hash, "plan_hash"),
|
|
1222
|
+
applied: requireBoolean(output.applied, "applied"),
|
|
1223
|
+
changed: requireBoolean(output.changed, "changed"),
|
|
1224
|
+
edit_count: requireNumber(output.edit_count, "edit_count"),
|
|
1225
|
+
before_content: optionalString(output.before_content, "before_content"),
|
|
1226
|
+
content: optionalString(output.content, "content")
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
function parseRenameConflicts(value, field) {
|
|
1230
|
+
if (value === undefined)
|
|
1231
|
+
return [];
|
|
1232
|
+
if (!Array.isArray(value))
|
|
1233
|
+
throw new Error(`invalid readseek ${field}`);
|
|
1234
|
+
return value.map((item) => {
|
|
1235
|
+
if (!item || typeof item !== "object")
|
|
1236
|
+
throw new Error(`invalid readseek ${field} entry`);
|
|
1237
|
+
const c = item;
|
|
1238
|
+
return {
|
|
1239
|
+
line: requireNumber(c.line, `${field}.line`),
|
|
1240
|
+
column: requireNumber(c.column, `${field}.column`),
|
|
1241
|
+
reason: requireString(c.reason, `${field}.reason`)
|
|
1242
|
+
};
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
function parseRenameEdits(value, field) {
|
|
1246
|
+
if (value === undefined)
|
|
1247
|
+
return [];
|
|
1248
|
+
if (!Array.isArray(value))
|
|
1249
|
+
throw new Error(`invalid readseek ${field} entry`);
|
|
1250
|
+
return value.map((item) => {
|
|
1251
|
+
if (!item || typeof item !== "object")
|
|
1252
|
+
throw new Error(`invalid readseek ${field} entry`);
|
|
1253
|
+
const e = item;
|
|
1254
|
+
return {
|
|
1255
|
+
line: requireNumber(e.line, `${field}.line`),
|
|
1256
|
+
start_column: requireNumber(e.start_column, `${field}.start_column`),
|
|
1257
|
+
end_column: requireNumber(e.end_column, `${field}.end_column`),
|
|
1258
|
+
start_byte: requireNumber(e.start_byte, `${field}.start_byte`),
|
|
1259
|
+
end_byte: requireNumber(e.end_byte, `${field}.end_byte`),
|
|
1260
|
+
occurrence: requireString(e.occurrence, `${field}.occurrence`),
|
|
1261
|
+
line_hash: requireString(e.line_hash, `${field}.line_hash`),
|
|
1262
|
+
text: requireString(e.text, `${field}.text`)
|
|
1263
|
+
};
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
function parseRenameOutput(value) {
|
|
1267
|
+
if (!value || typeof value !== "object")
|
|
1268
|
+
throw new Error("invalid readseek rename output");
|
|
1269
|
+
const output = value;
|
|
1270
|
+
const others = output.others;
|
|
1271
|
+
if (others !== undefined && !Array.isArray(others))
|
|
1272
|
+
throw new Error("invalid readseek others");
|
|
1273
|
+
return {
|
|
1274
|
+
file: requireString(output.file, "file"),
|
|
1275
|
+
language: requireString(output.language, "language"),
|
|
1276
|
+
engine: optionalString(output.engine, "engine"),
|
|
1277
|
+
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1278
|
+
old_name: requireString(output.old_name, "old_name"),
|
|
1279
|
+
new_name: requireString(output.new_name, "new_name"),
|
|
1280
|
+
applied: requireBoolean(output.applied, "applied"),
|
|
1281
|
+
conflicts: parseRenameConflicts(output.conflicts, "conflicts"),
|
|
1282
|
+
edits: parseRenameEdits(output.edits, "edits"),
|
|
1283
|
+
others: others?.map((entry) => {
|
|
1284
|
+
if (!entry || typeof entry !== "object")
|
|
1285
|
+
throw new Error("invalid readseek other");
|
|
1286
|
+
const o = entry;
|
|
1287
|
+
return {
|
|
1288
|
+
file: requireString(o.file, "other.file"),
|
|
1289
|
+
language: requireString(o.language, "other.language"),
|
|
1290
|
+
engine: optionalString(o.engine, "other.engine"),
|
|
1291
|
+
file_hash: requireString(o.file_hash, "other.file_hash"),
|
|
1292
|
+
conflicts: parseRenameConflicts(o.conflicts, "other.conflicts"),
|
|
1293
|
+
edits: parseRenameEdits(o.edits, "other.edits")
|
|
1294
|
+
};
|
|
1295
|
+
}) ?? []
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
function parseDefCompact(value) {
|
|
1299
|
+
if (!value || typeof value !== "object")
|
|
1300
|
+
throw new Error("invalid readseek def output");
|
|
1301
|
+
const output = value;
|
|
1302
|
+
const locations = output.locations;
|
|
1303
|
+
if (!Array.isArray(locations))
|
|
1304
|
+
throw new Error("invalid readseek def locations");
|
|
1305
|
+
return locations.map((loc) => {
|
|
1306
|
+
if (!loc || typeof loc !== "object")
|
|
1307
|
+
throw new Error("invalid readseek def location");
|
|
1308
|
+
const item = loc;
|
|
1309
|
+
return {
|
|
1310
|
+
file: requireString(item.file, "location.file"),
|
|
1311
|
+
line: requireNumber(item.line, "location.line"),
|
|
1312
|
+
column: requireNumber(item.column, "location.column"),
|
|
1313
|
+
line_hash: requireString(item.line_hash, "location.line_hash"),
|
|
1314
|
+
text: requireString(item.text, "location.text"),
|
|
1315
|
+
kind: optionalString(item.kind, "location.kind"),
|
|
1316
|
+
name: optionalString(item.name, "location.name"),
|
|
1317
|
+
qualified_name: optionalString(item.qualified_name, "location.qualified_name")
|
|
1318
|
+
};
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
function createReadSeekClient(runner) {
|
|
1322
|
+
function digestArgs(target, options) {
|
|
1323
|
+
return buildDigestArgs({
|
|
1324
|
+
path: target,
|
|
1325
|
+
select: options.select ?? "content",
|
|
1326
|
+
at: options.at,
|
|
1327
|
+
end: options.end,
|
|
1328
|
+
limit: options.limit,
|
|
1329
|
+
language: options.language,
|
|
1330
|
+
visionMode: options.visionMode,
|
|
1331
|
+
visionLevel: options.visionLevel,
|
|
1332
|
+
stdinName: options.stdin !== undefined && target === "-" ? options.stdinName ?? target : undefined
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1335
|
+
async function runDigestRaw(target, options = {}) {
|
|
1336
|
+
const args = digestArgs(target, options);
|
|
1337
|
+
const runOptions = {
|
|
1338
|
+
signal: options.signal,
|
|
1339
|
+
stdin: options.stdin,
|
|
1340
|
+
timeoutMs: options.timeoutMs,
|
|
1341
|
+
vision: options.vision
|
|
1342
|
+
};
|
|
1343
|
+
return runner.json(args, runOptions);
|
|
1344
|
+
}
|
|
1345
|
+
async function digest(target, options = {}) {
|
|
1346
|
+
return parseDigestEnvelope(await runDigestRaw(target, options));
|
|
1347
|
+
}
|
|
1348
|
+
function requireDigestContent(envelope, field) {
|
|
1349
|
+
if (envelope.content === undefined)
|
|
1350
|
+
throw new Error(`invalid readseek ${field}: missing content`);
|
|
1351
|
+
return envelope.content;
|
|
1352
|
+
}
|
|
1353
|
+
async function digestContent(filePath, options = {}) {
|
|
1354
|
+
const envelope = await digest(filePath, {
|
|
1355
|
+
select: "content",
|
|
1356
|
+
at: options.startLine !== undefined ? buildLineLocation(options.startLine) : undefined,
|
|
1357
|
+
end: options.endLine,
|
|
1358
|
+
visionMode: options.visionMode,
|
|
1359
|
+
signal: options.signal,
|
|
1360
|
+
vision: options.vision
|
|
1361
|
+
});
|
|
1362
|
+
if (envelope.metadata.kind === "pdf") {
|
|
1363
|
+
return { metadata: envelope.metadata, cache: envelope.cache };
|
|
1364
|
+
}
|
|
1365
|
+
const content = requireDigestContent(envelope, "content");
|
|
1366
|
+
if (envelope.metadata.kind === "image") {
|
|
1367
|
+
return { metadata: envelope.metadata, image: parseImageContent(content) };
|
|
1368
|
+
}
|
|
1369
|
+
return { metadata: envelope.metadata, text: parseTextContent(content) };
|
|
1370
|
+
}
|
|
1371
|
+
return {
|
|
1372
|
+
async view(filePath, options = {}) {
|
|
1373
|
+
const args = buildViewArgs({
|
|
1374
|
+
path: filePath,
|
|
1375
|
+
node: options.node,
|
|
1376
|
+
page: options.page,
|
|
1377
|
+
kind: options.kind,
|
|
1378
|
+
depth: options.depth,
|
|
1379
|
+
outline: options.outline,
|
|
1380
|
+
visionMode: options.visionMode,
|
|
1381
|
+
visionLevel: options.visionLevel
|
|
1382
|
+
});
|
|
1383
|
+
return runner.text(args, {
|
|
1384
|
+
signal: options.signal,
|
|
1385
|
+
vision: options.visionMode !== undefined
|
|
1386
|
+
});
|
|
1387
|
+
},
|
|
1388
|
+
digestRaw(target, options = {}) {
|
|
1389
|
+
return runDigestRaw(target, options);
|
|
1390
|
+
},
|
|
1391
|
+
digest,
|
|
1392
|
+
digestContent,
|
|
1393
|
+
async image(filePath, modes, options = {}) {
|
|
1394
|
+
const requestedModes = modes.length > 1 ? ["all"] : modes;
|
|
1395
|
+
const results = await Promise.allSettled(requestedModes.map(async (mode) => {
|
|
1396
|
+
const digested = await digestContent(filePath, {
|
|
1397
|
+
visionMode: mode,
|
|
1398
|
+
signal: options.signal,
|
|
1399
|
+
vision: true
|
|
1400
|
+
});
|
|
1401
|
+
if (!digested.image)
|
|
1402
|
+
throw new Error(`readseek returned no image analysis for ${filePath}`);
|
|
1403
|
+
return digested.image;
|
|
1404
|
+
}));
|
|
1405
|
+
let merged;
|
|
1406
|
+
for (const result of results) {
|
|
1407
|
+
if (result.status !== "fulfilled")
|
|
1408
|
+
continue;
|
|
1409
|
+
const detection = result.value;
|
|
1410
|
+
merged = merged === undefined ? detection : {
|
|
1411
|
+
...merged,
|
|
1412
|
+
ocr: detection.ocr ?? merged.ocr,
|
|
1413
|
+
caption: detection.caption ?? merged.caption,
|
|
1414
|
+
objects: detection.objects ?? merged.objects
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1417
|
+
if (merged !== undefined)
|
|
1418
|
+
return merged;
|
|
1419
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
1420
|
+
if (failure?.status === "rejected")
|
|
1421
|
+
throw failure.reason;
|
|
1422
|
+
throw new Error(`readseek returned no image analysis for ${filePath}`);
|
|
1423
|
+
},
|
|
1424
|
+
async preparedImage(filePath, options = {}) {
|
|
1425
|
+
const digested = await digestContent(filePath, {
|
|
1426
|
+
visionMode: "none",
|
|
1427
|
+
signal: options.signal
|
|
1428
|
+
});
|
|
1429
|
+
const output = digested.image;
|
|
1430
|
+
if (!output || output.encoding !== "base64" || output.data === undefined || output.mime === undefined) {
|
|
1431
|
+
throw new Error(`readseek returned no prepared image for ${filePath}`);
|
|
1432
|
+
}
|
|
1433
|
+
return { mime: output.mime, encoding: output.encoding, data: output.data };
|
|
1434
|
+
},
|
|
1435
|
+
async search(target, pattern, options = {}) {
|
|
1436
|
+
const args = buildSearchArgs({
|
|
1437
|
+
path: target,
|
|
1438
|
+
pattern,
|
|
1439
|
+
language: options.language,
|
|
1440
|
+
cached: options.cached,
|
|
1441
|
+
others: options.others,
|
|
1442
|
+
ignored: options.ignored
|
|
1443
|
+
});
|
|
1444
|
+
return parseSearchOutput(await runner.json(args, { signal: options.signal }));
|
|
1445
|
+
},
|
|
1446
|
+
async refs(target, options = {}) {
|
|
1447
|
+
const args = buildRefsArgs({
|
|
1448
|
+
path: target,
|
|
1449
|
+
name: options.name,
|
|
1450
|
+
scope: options.scope,
|
|
1451
|
+
line: options.line,
|
|
1452
|
+
column: options.column,
|
|
1453
|
+
language: options.language,
|
|
1454
|
+
cached: options.cached,
|
|
1455
|
+
others: options.others,
|
|
1456
|
+
ignored: options.ignored
|
|
1457
|
+
});
|
|
1458
|
+
return parseRefsOutput(await runner.json(args, { signal: options.signal }));
|
|
1459
|
+
},
|
|
1460
|
+
async def(target, options) {
|
|
1461
|
+
const args = buildDefArgs({
|
|
1462
|
+
path: target,
|
|
1463
|
+
name: options.name,
|
|
1464
|
+
format: "plain",
|
|
1465
|
+
language: options.language,
|
|
1466
|
+
cached: options.cached,
|
|
1467
|
+
others: options.others,
|
|
1468
|
+
ignored: options.ignored
|
|
1469
|
+
});
|
|
1470
|
+
return parseDefCompact(await runner.json(args, { signal: options.signal }));
|
|
1471
|
+
},
|
|
1472
|
+
async check(filePath, content, options = {}) {
|
|
1473
|
+
const envelope = await digest("-", {
|
|
1474
|
+
select: "diagnostics",
|
|
1475
|
+
stdin: content,
|
|
1476
|
+
stdinName: filePath,
|
|
1477
|
+
signal: options.signal
|
|
1478
|
+
});
|
|
1479
|
+
if (!envelope.diagnostics)
|
|
1480
|
+
throw new Error("invalid readseek diagnostics: missing diagnostics facet");
|
|
1481
|
+
return envelope.diagnostics;
|
|
1482
|
+
},
|
|
1483
|
+
async identify(filePath, content, options = {}) {
|
|
1484
|
+
const envelope = await digest("-", {
|
|
1485
|
+
select: "identity",
|
|
1486
|
+
stdin: content,
|
|
1487
|
+
stdinName: filePath,
|
|
1488
|
+
at: options.line !== undefined ? buildLineLocation(options.line, options.column) : undefined,
|
|
1489
|
+
language: options.language,
|
|
1490
|
+
signal: options.signal
|
|
1491
|
+
});
|
|
1492
|
+
if (!envelope.identity)
|
|
1493
|
+
throw new Error("invalid readseek identity: missing identity facet");
|
|
1494
|
+
return envelope.identity;
|
|
1495
|
+
},
|
|
1496
|
+
async detect(filePath, options = {}) {
|
|
1497
|
+
const envelope = await digest(filePath, { select: "metadata", signal: options.signal });
|
|
1498
|
+
return envelope.metadata;
|
|
1499
|
+
},
|
|
1500
|
+
async edit(filePath, edits, options = {}) {
|
|
1501
|
+
const args = buildEditArgs({
|
|
1502
|
+
path: filePath,
|
|
1503
|
+
apply: options.apply,
|
|
1504
|
+
planHash: options.planHash,
|
|
1505
|
+
language: options.language
|
|
1506
|
+
});
|
|
1507
|
+
return parseEditOutput(await runner.json(args, {
|
|
1508
|
+
signal: options.signal,
|
|
1509
|
+
stdin: JSON.stringify({ edits }),
|
|
1510
|
+
timeoutMs: options.timeoutMs,
|
|
1511
|
+
cancelable: options.cancelable
|
|
1512
|
+
}));
|
|
1513
|
+
},
|
|
1514
|
+
async rename(filePath, options) {
|
|
1515
|
+
const args = buildRenameArgs({
|
|
1516
|
+
path: filePath,
|
|
1517
|
+
to: options.to,
|
|
1518
|
+
line: options.line,
|
|
1519
|
+
column: options.column,
|
|
1520
|
+
workspace: options.workspace,
|
|
1521
|
+
apply: options.apply,
|
|
1522
|
+
language: options.language,
|
|
1523
|
+
cached: options.cached,
|
|
1524
|
+
others: options.others,
|
|
1525
|
+
ignored: options.ignored
|
|
1526
|
+
});
|
|
1527
|
+
return parseRenameOutput(await runner.json(args, { signal: options.signal }));
|
|
1528
|
+
}
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
// src/tool-prompt-metadata.ts
|
|
1532
|
+
var REPLACEABLE_BY_TOOL = new Map(REPLACEABLE_TOOLS.map((entry) => [entry.tool, entry]));
|
|
1533
|
+
function toolNameFromPrompt(fileName) {
|
|
1534
|
+
const name = fileName.replace(/\.md$/, "");
|
|
1535
|
+
if (!TOOL_NAMES.includes(name))
|
|
1536
|
+
throw new Error(`unknown ReadSeek tool prompt: ${fileName}`);
|
|
1537
|
+
return name;
|
|
1538
|
+
}
|
|
1539
|
+
function promptFileName(promptUrl) {
|
|
1540
|
+
return promptUrl.pathname.split("/").pop() ?? "";
|
|
1541
|
+
}
|
|
1542
|
+
function rewriteRegisteredToolNames(value, toolAliases) {
|
|
1543
|
+
if (!toolAliases)
|
|
1544
|
+
return value;
|
|
1545
|
+
return Object.entries(toolAliases).reduce((rewritten, [canonicalName, registeredName]) => rewritten.replaceAll(canonicalName, registeredName), value);
|
|
1546
|
+
}
|
|
1547
|
+
function descriptionAliases(toolName, registeredName, toolAliases) {
|
|
1548
|
+
return Object.fromEntries(TOOL_NAMES.map((name) => {
|
|
1549
|
+
const readSeekName = `readSeek_${name}`;
|
|
1550
|
+
const alias = name === toolName && registeredName ? registeredName : toolAliases?.[readSeekName] ?? readSeekName;
|
|
1551
|
+
return [name, alias];
|
|
1552
|
+
}));
|
|
1553
|
+
}
|
|
1554
|
+
function defineToolPromptMetadata(options) {
|
|
1555
|
+
const fileName = promptFileName(options.promptUrl);
|
|
1556
|
+
const toolName = toolNameFromPrompt(fileName);
|
|
1557
|
+
const replaceable = REPLACEABLE_BY_TOOL.get(toolName);
|
|
1558
|
+
const defaultReadSeekName = `readSeek_${toolName}`;
|
|
1559
|
+
const registeredName = options.registeredName ?? (replaceable ? defaultReadSeekName : undefined);
|
|
1560
|
+
const preferenceGuideline = replaceable && registeredName ? registeredName === defaultReadSeekName ? `Prefer ${registeredName} over ${replaceable.builtIn} when both are available; ${replaceable.benefit}` : `Use ${registeredName}; ${replaceable.benefit}` : undefined;
|
|
1561
|
+
const aliases = descriptionAliases(toolName, registeredName, options.toolAliases);
|
|
1562
|
+
return {
|
|
1563
|
+
description: renderToolDescription(toolName, aliases),
|
|
1564
|
+
promptSnippet: rewriteRegisteredToolNames(options.promptSnippet, options.toolAliases),
|
|
1565
|
+
promptGuidelines: [
|
|
1566
|
+
...preferenceGuideline ? [preferenceGuideline] : [],
|
|
1567
|
+
...renderCompactGuidelines(toolName, aliases)
|
|
1568
|
+
]
|
|
1569
|
+
};
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
// src/edit-diff.ts
|
|
1573
|
+
import * as Diff from "diff";
|
|
1574
|
+
|
|
1575
|
+
// src/hashline.ts
|
|
1576
|
+
import xxhashWasm from "xxhash-wasm";
|
|
1577
|
+
var HASH_LEN = 3;
|
|
1578
|
+
var RADIX = 16;
|
|
1579
|
+
var HASH_MOD = RADIX ** HASH_LEN;
|
|
1580
|
+
var DICT = Array.from({ length: HASH_MOD }, (_, i) => i.toString(RADIX).padStart(HASH_LEN, "0"));
|
|
1581
|
+
var HASHLINE_STATE_KEY = Symbol.for("pi-readseek.hashlineState.v1");
|
|
1582
|
+
function getHashlineState() {
|
|
1583
|
+
const globalObject = globalThis;
|
|
1584
|
+
const state = globalObject[HASHLINE_STATE_KEY] ?? {
|
|
1585
|
+
h32Fn: null,
|
|
1586
|
+
initPromise: null
|
|
1587
|
+
};
|
|
1588
|
+
globalObject[HASHLINE_STATE_KEY] = state;
|
|
1589
|
+
return state;
|
|
1590
|
+
}
|
|
1591
|
+
async function ensureHashInit() {
|
|
1592
|
+
const state = getHashlineState();
|
|
1593
|
+
if (state.h32Fn)
|
|
1594
|
+
return;
|
|
1595
|
+
if (!state.initPromise) {
|
|
1596
|
+
state.initPromise = xxhashWasm().then((hasher) => {
|
|
1597
|
+
state.h32Fn = hasher.h32;
|
|
1598
|
+
});
|
|
1599
|
+
}
|
|
1600
|
+
await state.initPromise;
|
|
1601
|
+
}
|
|
1602
|
+
function xxh32(input) {
|
|
1603
|
+
const state = getHashlineState();
|
|
1604
|
+
if (!state.h32Fn)
|
|
1605
|
+
throw new Error("Hash not initialized — call ensureHashInit() first");
|
|
1606
|
+
return state.h32Fn(input, 0) >>> 0;
|
|
1607
|
+
}
|
|
1608
|
+
function computeLineHash(line) {
|
|
1609
|
+
if (line.endsWith("\r"))
|
|
1610
|
+
line = line.slice(0, -1);
|
|
1611
|
+
line = line.replace(/\s+/g, "");
|
|
1612
|
+
return DICT[xxh32(line) % HASH_MOD];
|
|
1613
|
+
}
|
|
1614
|
+
var DISPLAY_CONTROL_CHAR_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f]/g;
|
|
1615
|
+
function escapeControlCharsForDisplay(text) {
|
|
1616
|
+
return text.replace(DISPLAY_CONTROL_CHAR_RE, (ch) => {
|
|
1617
|
+
return `\\u${ch.charCodeAt(0).toString(16).padStart(4, "0")}`;
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1620
|
+
function formatHashlineDisplay(lineNumber, content) {
|
|
1621
|
+
return `${lineNumber}:${computeLineHash(content)}|${escapeControlCharsForDisplay(content)}`;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
// src/edit-diff.ts
|
|
1625
|
+
function normalizeToLF(text) {
|
|
1626
|
+
return text.replace(/\r\n/g, `
|
|
1627
|
+
`).replace(/\r/g, `
|
|
1628
|
+
`);
|
|
1629
|
+
}
|
|
1630
|
+
function stripBom(content) {
|
|
1631
|
+
return content.startsWith("\uFEFF") ? { bom: "\uFEFF", text: content.slice(1) } : { bom: "", text: content };
|
|
1632
|
+
}
|
|
1633
|
+
function hasBareCarriageReturn(content) {
|
|
1634
|
+
return content.replace(/\r\n/g, "").includes("\r");
|
|
1635
|
+
}
|
|
1636
|
+
function generateDiffString(oldContent, newContent, contextLines = 4) {
|
|
1637
|
+
const parts = Diff.diffLines(oldContent, newContent);
|
|
1638
|
+
const output = [];
|
|
1639
|
+
const maxLineNum = Math.max(oldContent.split(`
|
|
1640
|
+
`).length, newContent.split(`
|
|
1641
|
+
`).length);
|
|
1642
|
+
const lineNumWidth = String(maxLineNum).length;
|
|
1643
|
+
let oldLineNum = 1;
|
|
1644
|
+
let newLineNum = 1;
|
|
1645
|
+
let lastWasChange = false;
|
|
1646
|
+
let firstChangedLine;
|
|
1647
|
+
for (let i = 0;i < parts.length; i++) {
|
|
1648
|
+
const part = parts[i];
|
|
1649
|
+
const raw = part.value.split(`
|
|
1650
|
+
`);
|
|
1651
|
+
if (raw[raw.length - 1] === "")
|
|
1652
|
+
raw.pop();
|
|
1653
|
+
if (part.added || part.removed) {
|
|
1654
|
+
if (firstChangedLine === undefined)
|
|
1655
|
+
firstChangedLine = newLineNum;
|
|
1656
|
+
for (const line of raw) {
|
|
1657
|
+
if (part.added) {
|
|
1658
|
+
output.push(`+${String(newLineNum).padStart(lineNumWidth, " ")} ${line}`);
|
|
1659
|
+
newLineNum++;
|
|
1660
|
+
} else {
|
|
1661
|
+
output.push(`-${String(oldLineNum).padStart(lineNumWidth, " ")} ${line}`);
|
|
1662
|
+
oldLineNum++;
|
|
1663
|
+
}
|
|
990
1664
|
}
|
|
991
1665
|
lastWasChange = true;
|
|
992
1666
|
continue;
|
|
@@ -1257,964 +1931,630 @@ function buildReadSeekWarning(code, message, metadata = {}) {
|
|
|
1257
1931
|
function buildReadSeekError(code, message, hint, details) {
|
|
1258
1932
|
return {
|
|
1259
1933
|
code,
|
|
1260
|
-
message,
|
|
1261
|
-
...hint !== undefined ? { hint } : {},
|
|
1262
|
-
...details !== undefined ? { details } : {}
|
|
1263
|
-
};
|
|
1264
|
-
}
|
|
1265
|
-
function buildToolErrorResult(tool, code, message, opts = {}) {
|
|
1266
|
-
return {
|
|
1267
|
-
content: [{ type: "text", text: message }],
|
|
1268
|
-
isError: true,
|
|
1269
|
-
details: {
|
|
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
|
-
};
|
|
1279
|
-
}
|
|
1280
|
-
function buildReadSeekEditResult(input) {
|
|
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
|
-
};
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
// src/edit-output.ts
|
|
1296
|
-
var EDIT_OPERATION_NAMES = ["set_line", "replace_lines", "insert_after", "replace"];
|
|
1297
|
-
function hasNewText(value) {
|
|
1298
|
-
return typeof value === "object" && value !== null && typeof value.new_text === "string";
|
|
1299
|
-
}
|
|
1300
|
-
function getVisibleDiffStats(diff) {
|
|
1301
|
-
const stats = parseDiffStats(diff);
|
|
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 };
|
|
1309
|
-
}
|
|
1310
|
-
function buildVisibleSummary(displayPath, diff, edits) {
|
|
1311
|
-
let stats = getVisibleDiffStats(diff);
|
|
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})`;
|
|
1321
|
-
}
|
|
1322
|
-
function extractNewTextValues(edits) {
|
|
1323
|
-
const values = [];
|
|
1324
|
-
for (const edit of edits ?? []) {
|
|
1325
|
-
if (!edit || typeof edit !== "object")
|
|
1326
|
-
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
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
return values;
|
|
1335
|
-
}
|
|
1336
|
-
function formatWhitespaceOnlyWarning(semanticSummary, edits) {
|
|
1337
|
-
if (semanticSummary?.classification !== "whitespace-only")
|
|
1338
|
-
return;
|
|
1339
|
-
if (!extractNewTextValues(edits).some((text) => /\S/.test(text)))
|
|
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
|
-
}
|
|
1393
|
-
const oldLines = oldContent.split(`
|
|
1394
|
-
`);
|
|
1395
|
-
const newLines = newContent.split(`
|
|
1396
|
-
`);
|
|
1397
|
-
if (oldLines.length === newLines.length) {
|
|
1398
|
-
let hasWhitespaceChange2 = false;
|
|
1399
|
-
let hasSemanticChange2 = false;
|
|
1400
|
-
for (let i = 0;i < oldLines.length; i++) {
|
|
1401
|
-
if (oldLines[i] === newLines[i])
|
|
1402
|
-
continue;
|
|
1403
|
-
if (oldLines[i].trim() === newLines[i].trim()) {
|
|
1404
|
-
hasWhitespaceChange2 = true;
|
|
1405
|
-
} else {
|
|
1406
|
-
hasSemanticChange2 = true;
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
if (hasSemanticChange2 && hasWhitespaceChange2)
|
|
1410
|
-
return { classification: "mixed" };
|
|
1411
|
-
if (hasWhitespaceChange2)
|
|
1412
|
-
return { classification: "whitespace-only" };
|
|
1413
|
-
return { classification: "semantic" };
|
|
1414
|
-
}
|
|
1415
|
-
const parts = Diff2.diffLines(oldContent, newContent);
|
|
1416
|
-
let hasWhitespaceChange = false;
|
|
1417
|
-
let hasSemanticChange = false;
|
|
1418
|
-
for (const part of parts) {
|
|
1419
|
-
if (!part.added && !part.removed)
|
|
1420
|
-
continue;
|
|
1421
|
-
const lines = part.value.split(`
|
|
1422
|
-
`);
|
|
1423
|
-
if (lines[lines.length - 1] === "")
|
|
1424
|
-
lines.pop();
|
|
1425
|
-
for (const line of lines) {
|
|
1426
|
-
if (line.trim() === "") {
|
|
1427
|
-
hasWhitespaceChange = true;
|
|
1428
|
-
} else {
|
|
1429
|
-
hasSemanticChange = true;
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
if (hasSemanticChange && hasWhitespaceChange)
|
|
1434
|
-
return { classification: "mixed" };
|
|
1435
|
-
if (hasWhitespaceChange)
|
|
1436
|
-
return { classification: "whitespace-only" };
|
|
1437
|
-
return { classification: "semantic" };
|
|
1438
|
-
}
|
|
1439
|
-
|
|
1440
|
-
// src/readseek-client.ts
|
|
1441
|
-
import { existsSync, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
1442
|
-
import { createRequire as createRequire2 } from "node:module";
|
|
1443
|
-
import { homedir as homedir3 } from "node:os";
|
|
1444
|
-
import path2 from "node:path";
|
|
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");
|
|
1934
|
+
message,
|
|
1935
|
+
...hint !== undefined ? { hint } : {},
|
|
1936
|
+
...details !== undefined ? { details } : {}
|
|
1937
|
+
};
|
|
1494
1938
|
}
|
|
1495
|
-
function
|
|
1496
|
-
return
|
|
1939
|
+
function buildToolErrorResult(tool, code, message, opts = {}) {
|
|
1940
|
+
return {
|
|
1941
|
+
content: [{ type: "text", text: message }],
|
|
1942
|
+
isError: true,
|
|
1943
|
+
details: {
|
|
1944
|
+
readSeekValue: {
|
|
1945
|
+
tool,
|
|
1946
|
+
...opts.extra ?? {},
|
|
1947
|
+
ok: false,
|
|
1948
|
+
...opts.path !== undefined ? { path: opts.path } : {},
|
|
1949
|
+
error: buildReadSeekError(code, message, opts.hint, opts.details)
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
};
|
|
1497
1953
|
}
|
|
1498
|
-
function
|
|
1499
|
-
return
|
|
1954
|
+
function buildReadSeekEditResult(input) {
|
|
1955
|
+
return {
|
|
1956
|
+
tool: "edit",
|
|
1957
|
+
ok: input.ok ?? true,
|
|
1958
|
+
path: input.path,
|
|
1959
|
+
summary: input.summary,
|
|
1960
|
+
diff: input.diff,
|
|
1961
|
+
...input.diffData ? { diffData: input.diffData } : {},
|
|
1962
|
+
firstChangedLine: input.firstChangedLine,
|
|
1963
|
+
warnings: [...input.warnings],
|
|
1964
|
+
noopEdits: [...input.noopEdits],
|
|
1965
|
+
...input.semanticSummary ? { semanticSummary: input.semanticSummary } : {}
|
|
1966
|
+
};
|
|
1500
1967
|
}
|
|
1501
|
-
|
|
1502
|
-
|
|
1968
|
+
|
|
1969
|
+
// src/edit-output.ts
|
|
1970
|
+
var EDIT_OPERATION_NAMES = ["set_line", "replace_lines", "insert_after", "replace"];
|
|
1971
|
+
function hasNewText(value) {
|
|
1972
|
+
return typeof value === "object" && value !== null && typeof value.new_text === "string";
|
|
1503
1973
|
}
|
|
1504
|
-
function
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1974
|
+
function getVisibleDiffStats(diff) {
|
|
1975
|
+
const stats = parseDiffStats(diff);
|
|
1976
|
+
if (stats.added > 0 || stats.removed > 0)
|
|
1977
|
+
return stats;
|
|
1978
|
+
if (!diff.includes("→"))
|
|
1979
|
+
return stats;
|
|
1980
|
+
if (diff.includes("→ [deleted]"))
|
|
1981
|
+
return { added: 0, removed: 1 };
|
|
1982
|
+
return { added: 1, removed: 1 };
|
|
1983
|
+
}
|
|
1984
|
+
function buildVisibleSummary(displayPath, diff, edits) {
|
|
1985
|
+
let stats = getVisibleDiffStats(diff);
|
|
1986
|
+
const counts = countEditTypes(edits);
|
|
1987
|
+
const editCount = counts.total || 1;
|
|
1988
|
+
if (counts.total > 0 && counts.insert_after === counts.total && stats.removed > 0 && stats.added === stats.removed + counts.insert_after) {
|
|
1989
|
+
stats = { added: counts.insert_after, removed: 0 };
|
|
1510
1990
|
}
|
|
1991
|
+
const changeWord = editCount === 1 ? "change" : "changes";
|
|
1992
|
+
const changedLineCount = Math.max(stats.added, stats.removed);
|
|
1993
|
+
const lineWord = changedLineCount === 1 ? "line" : "lines";
|
|
1994
|
+
return `Edited ${displayPath} (${editCount} ${changeWord}, +${stats.added} -${stats.removed} ${lineWord})`;
|
|
1511
1995
|
}
|
|
1512
|
-
function
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1996
|
+
function extractNewTextValues(edits) {
|
|
1997
|
+
const values = [];
|
|
1998
|
+
for (const edit of edits ?? []) {
|
|
1999
|
+
if (!edit || typeof edit !== "object")
|
|
2000
|
+
continue;
|
|
2001
|
+
const operations = edit;
|
|
2002
|
+
for (const operationName of EDIT_OPERATION_NAMES) {
|
|
2003
|
+
const operation = operations[operationName];
|
|
2004
|
+
if (hasNewText(operation))
|
|
2005
|
+
values.push(operation.new_text);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
return values;
|
|
1520
2009
|
}
|
|
1521
|
-
function
|
|
1522
|
-
if (
|
|
2010
|
+
function formatWhitespaceOnlyWarning(semanticSummary, edits) {
|
|
2011
|
+
if (semanticSummary?.classification !== "whitespace-only")
|
|
1523
2012
|
return;
|
|
1524
|
-
|
|
1525
|
-
if (typeof val === "string" && values.includes(val))
|
|
1526
|
-
return val;
|
|
1527
|
-
warnings.push(invalid(source, path2));
|
|
1528
|
-
return;
|
|
1529
|
-
}
|
|
1530
|
-
function readImageMode(raw, source, warnings) {
|
|
1531
|
-
if (!("imageMode" in raw))
|
|
2013
|
+
if (!extractNewTextValues(edits).some((text) => /\S/.test(text)))
|
|
1532
2014
|
return;
|
|
1533
|
-
|
|
1534
|
-
if (val === "on" || val === "off" || val === "auto")
|
|
1535
|
-
return val;
|
|
1536
|
-
warnings.push(invalid(source, "readseek.imageMode"));
|
|
1537
|
-
return;
|
|
2015
|
+
return "⚠ Edit classified as whitespace-only — if you intended a behavior change, re-read to verify.";
|
|
1538
2016
|
}
|
|
1539
|
-
function
|
|
1540
|
-
if (
|
|
1541
|
-
return;
|
|
1542
|
-
const value = raw.overrideTools;
|
|
1543
|
-
if (!Array.isArray(value)) {
|
|
1544
|
-
warnings.push(invalid(source, "readseek.overrideTools"));
|
|
2017
|
+
function formatReplaceHint(edits, noopEdits) {
|
|
2018
|
+
if ((noopEdits ?? []).length > 0)
|
|
1545
2019
|
return;
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
value.forEach((tool, index) => {
|
|
1549
|
-
if (typeof tool === "string" && Object.hasOwn(READSEEK_TOOL_REPLACEMENTS, tool)) {
|
|
1550
|
-
tools.push(tool);
|
|
1551
|
-
} else {
|
|
1552
|
-
warnings.push(invalid(source, `readseek.overrideTools[${index}]`));
|
|
1553
|
-
}
|
|
1554
|
-
});
|
|
1555
|
-
return tools;
|
|
1556
|
-
}
|
|
1557
|
-
function readDisplaySettings(raw, source, warnings) {
|
|
1558
|
-
if (!("display" in raw))
|
|
2020
|
+
const counts = countEditTypes(edits);
|
|
2021
|
+
if (counts.replace === 0)
|
|
1559
2022
|
return;
|
|
1560
|
-
if (
|
|
1561
|
-
warnings.push(invalid(source, "readseek.display"));
|
|
2023
|
+
if (counts.replace !== counts.total)
|
|
1562
2024
|
return;
|
|
1563
|
-
|
|
1564
|
-
warnUnknownKeys(raw.display, READSEEK_DISPLAY_KEYS, "readseek.display", source, warnings);
|
|
1565
|
-
const display = {};
|
|
1566
|
-
for (const tool of READSEEK_DISPLAY_KEYS) {
|
|
1567
|
-
const mode = readEnum(raw.display, tool, ["compact", "expanded"], `readseek.display.${tool}`, source, warnings);
|
|
1568
|
-
if (mode !== undefined)
|
|
1569
|
-
display[tool] = mode;
|
|
1570
|
-
}
|
|
1571
|
-
return Object.keys(display).length > 0 ? display : undefined;
|
|
1572
|
-
}
|
|
1573
|
-
function validateSettings(raw, source) {
|
|
1574
|
-
const settings = {};
|
|
1575
|
-
const warnings = [];
|
|
1576
|
-
if (!isRecord(raw)) {
|
|
1577
|
-
warnings.push({ source, message: "Invalid readseek settings: expected a JSON object" });
|
|
1578
|
-
return { settings, warnings };
|
|
1579
|
-
}
|
|
1580
|
-
if (!("readseek" in raw)) {
|
|
1581
|
-
const misplaced = READSEEK_KEYS.filter((key) => (key in raw));
|
|
1582
|
-
if (misplaced.length > 0) {
|
|
1583
|
-
warnings.push({
|
|
1584
|
-
source,
|
|
1585
|
-
path: misplaced[0],
|
|
1586
|
-
message: `Readseek setting at top level: move ${misplaced.join(", ")} under "readseek"`
|
|
1587
|
-
});
|
|
1588
|
-
}
|
|
1589
|
-
return { settings, warnings };
|
|
1590
|
-
}
|
|
1591
|
-
if (!isRecord(raw.readseek)) {
|
|
1592
|
-
warnings.push(invalid(source, "readseek"));
|
|
1593
|
-
return { settings, warnings };
|
|
1594
|
-
}
|
|
1595
|
-
const section = raw.readseek;
|
|
1596
|
-
warnUnknownKeys(section, READSEEK_KEYS, "readseek", source, warnings);
|
|
1597
|
-
const overrideTools = readOverrideTools(section, source, warnings);
|
|
1598
|
-
if (overrideTools !== undefined)
|
|
1599
|
-
settings.overrideTools = overrideTools;
|
|
1600
|
-
const imageMode = readImageMode(section, source, warnings);
|
|
1601
|
-
if (imageMode !== undefined)
|
|
1602
|
-
settings.imageMode = imageMode;
|
|
1603
|
-
const syntaxValidation = readEnum(section, "syntaxValidation", ["warn", "block", "off"], "readseek.syntaxValidation", source, warnings);
|
|
1604
|
-
if (syntaxValidation !== undefined)
|
|
1605
|
-
settings.syntaxValidation = syntaxValidation;
|
|
1606
|
-
const timeoutMs = readPositive(section, "timeoutMs", "readseek.timeoutMs", source, warnings);
|
|
1607
|
-
if (timeoutMs !== undefined)
|
|
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"));
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
const display = readDisplaySettings(section, source, warnings);
|
|
1626
|
-
if (display !== undefined)
|
|
1627
|
-
settings.display = display;
|
|
1628
|
-
return { settings, warnings };
|
|
1629
|
-
}
|
|
1630
|
-
var settingsFileCache = new Map;
|
|
1631
|
-
function statFileOrNull(path2) {
|
|
1632
|
-
try {
|
|
1633
|
-
return statSync(path2);
|
|
1634
|
-
} catch {
|
|
1635
|
-
return null;
|
|
1636
|
-
}
|
|
1637
|
-
}
|
|
1638
|
-
function readSettingsFile(path2) {
|
|
1639
|
-
const stats = statFileOrNull(path2);
|
|
1640
|
-
if (!stats) {
|
|
1641
|
-
settingsFileCache.delete(path2);
|
|
1642
|
-
return { settings: {}, warnings: [] };
|
|
1643
|
-
}
|
|
1644
|
-
const cached = settingsFileCache.get(path2);
|
|
1645
|
-
if (cached && cached.mtimeMs === stats.mtimeMs && cached.size === stats.size)
|
|
1646
|
-
return cached.result;
|
|
1647
|
-
let result;
|
|
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}` }] };
|
|
1654
|
-
}
|
|
1655
|
-
settingsFileCache.set(path2, { mtimeMs: stats.mtimeMs, size: stats.size, result });
|
|
1656
|
-
return result;
|
|
1657
|
-
}
|
|
1658
|
-
function mergeSettings(base, override) {
|
|
1659
|
-
const settings = { ...base, ...override };
|
|
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;
|
|
2025
|
+
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.]";
|
|
1667
2026
|
}
|
|
1668
|
-
function
|
|
1669
|
-
const
|
|
1670
|
-
const
|
|
2027
|
+
function buildEditOutput(input) {
|
|
2028
|
+
const summary = `Updated ${input.displayPath}`;
|
|
2029
|
+
const visibleSummary = buildVisibleSummary(input.displayPath, input.diff, input.edits);
|
|
2030
|
+
const semanticWarning = formatWhitespaceOnlyWarning(input.semanticSummary, input.edits);
|
|
2031
|
+
const warningText = input.warnings.length ? `
|
|
2032
|
+
|
|
2033
|
+
Warnings:
|
|
2034
|
+
${input.warnings.join(`
|
|
2035
|
+
`)}` : "";
|
|
2036
|
+
const replaceHint = formatReplaceHint(input.edits, input.noopEdits);
|
|
2037
|
+
let text = visibleSummary;
|
|
2038
|
+
if (semanticWarning)
|
|
2039
|
+
text += `
|
|
2040
|
+
${semanticWarning}`;
|
|
2041
|
+
text += warningText;
|
|
2042
|
+
if (replaceHint)
|
|
2043
|
+
text += `
|
|
2044
|
+
${replaceHint}`;
|
|
1671
2045
|
return {
|
|
1672
|
-
|
|
1673
|
-
|
|
2046
|
+
text,
|
|
2047
|
+
patch: input.patch ?? "",
|
|
2048
|
+
readSeekValue: buildReadSeekEditResult({
|
|
2049
|
+
path: input.path,
|
|
2050
|
+
summary,
|
|
2051
|
+
diff: input.diff,
|
|
2052
|
+
...input.diffData ? { diffData: input.diffData } : {},
|
|
2053
|
+
firstChangedLine: input.firstChangedLine,
|
|
2054
|
+
warnings: input.warnings,
|
|
2055
|
+
noopEdits: input.noopEdits,
|
|
2056
|
+
...input.semanticSummary ? { semanticSummary: input.semanticSummary } : {}
|
|
2057
|
+
})
|
|
1674
2058
|
};
|
|
1675
2059
|
}
|
|
1676
|
-
function resolveReadSeekSyntaxValidation() {
|
|
1677
|
-
return resolveReadSeekJsonSettings().settings.syntaxValidation;
|
|
1678
|
-
}
|
|
1679
|
-
function resolveReadSeekTimeoutMs() {
|
|
1680
|
-
return resolveReadSeekJsonSettings().settings.timeoutMs;
|
|
1681
|
-
}
|
|
1682
|
-
function resolveReadSeekToolDisplayMode(tool) {
|
|
1683
|
-
return resolveReadSeekJsonSettings().settings.display?.[tool] ?? DEFAULT_DISPLAY_MODES[tool];
|
|
1684
|
-
}
|
|
1685
2060
|
|
|
1686
|
-
// src/
|
|
1687
|
-
|
|
1688
|
-
function
|
|
1689
|
-
|
|
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);
|
|
1701
|
-
}
|
|
1702
|
-
function symbolsFromReadSeek(symbols) {
|
|
1703
|
-
const symbolsByQualifiedName = new Map;
|
|
1704
|
-
const entries = [];
|
|
1705
|
-
for (const symbol of symbols) {
|
|
1706
|
-
const parentQualifiedName = parentQualifiedNameFor(symbol.qualified_name);
|
|
1707
|
-
const fileSymbol = {
|
|
1708
|
-
name: symbol.name,
|
|
1709
|
-
kind: normalizeKind(symbol.kind),
|
|
1710
|
-
startLine: symbol.start_line,
|
|
1711
|
-
endLine: symbol.end_line
|
|
1712
|
-
};
|
|
1713
|
-
const bucket = symbolsByQualifiedName.get(symbol.qualified_name);
|
|
1714
|
-
if (bucket)
|
|
1715
|
-
bucket.push(fileSymbol);
|
|
1716
|
-
else
|
|
1717
|
-
symbolsByQualifiedName.set(symbol.qualified_name, [fileSymbol]);
|
|
1718
|
-
entries.push({ parentQualifiedName, symbol: fileSymbol });
|
|
2061
|
+
// src/edit-classify.ts
|
|
2062
|
+
import * as Diff2 from "diff";
|
|
2063
|
+
function classifyEdit(oldContent, newContent) {
|
|
2064
|
+
if (oldContent === newContent) {
|
|
2065
|
+
return { classification: "no-op" };
|
|
1719
2066
|
}
|
|
1720
|
-
const
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
2067
|
+
const oldLines = oldContent.split(`
|
|
2068
|
+
`);
|
|
2069
|
+
const newLines = newContent.split(`
|
|
2070
|
+
`);
|
|
2071
|
+
if (oldLines.length === newLines.length) {
|
|
2072
|
+
let hasWhitespaceChange2 = false;
|
|
2073
|
+
let hasSemanticChange2 = false;
|
|
2074
|
+
for (let i = 0;i < oldLines.length; i++) {
|
|
2075
|
+
if (oldLines[i] === newLines[i])
|
|
2076
|
+
continue;
|
|
2077
|
+
if (oldLines[i].trim() === newLines[i].trim()) {
|
|
2078
|
+
hasWhitespaceChange2 = true;
|
|
2079
|
+
} else {
|
|
2080
|
+
hasSemanticChange2 = true;
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
if (hasSemanticChange2 && hasWhitespaceChange2)
|
|
2084
|
+
return { classification: "mixed" };
|
|
2085
|
+
if (hasWhitespaceChange2)
|
|
2086
|
+
return { classification: "whitespace-only" };
|
|
2087
|
+
return { classification: "semantic" };
|
|
2088
|
+
}
|
|
2089
|
+
const parts = Diff2.diffLines(oldContent, newContent);
|
|
2090
|
+
let hasWhitespaceChange = false;
|
|
2091
|
+
let hasSemanticChange = false;
|
|
2092
|
+
for (const part of parts) {
|
|
2093
|
+
if (!part.added && !part.removed)
|
|
1725
2094
|
continue;
|
|
2095
|
+
const lines = part.value.split(`
|
|
2096
|
+
`);
|
|
2097
|
+
if (lines[lines.length - 1] === "")
|
|
2098
|
+
lines.pop();
|
|
2099
|
+
for (const line of lines) {
|
|
2100
|
+
if (line.trim() === "") {
|
|
2101
|
+
hasWhitespaceChange = true;
|
|
2102
|
+
} else {
|
|
2103
|
+
hasSemanticChange = true;
|
|
2104
|
+
}
|
|
1726
2105
|
}
|
|
1727
|
-
parent.children ??= [];
|
|
1728
|
-
parent.children.push(entry.symbol);
|
|
1729
2106
|
}
|
|
1730
|
-
|
|
2107
|
+
if (hasSemanticChange && hasWhitespaceChange)
|
|
2108
|
+
return { classification: "mixed" };
|
|
2109
|
+
if (hasWhitespaceChange)
|
|
2110
|
+
return { classification: "whitespace-only" };
|
|
2111
|
+
return { classification: "semantic" };
|
|
1731
2112
|
}
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
2113
|
+
|
|
2114
|
+
// src/readseek-client.ts
|
|
2115
|
+
import { existsSync, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
2116
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
2117
|
+
import { homedir as homedir3 } from "node:os";
|
|
2118
|
+
import path2 from "node:path";
|
|
2119
|
+
|
|
2120
|
+
// src/readseek/enums.ts
|
|
2121
|
+
var SymbolKind;
|
|
2122
|
+
((SymbolKind2) => {
|
|
2123
|
+
SymbolKind2["Class"] = "class";
|
|
2124
|
+
SymbolKind2["Function"] = "function";
|
|
2125
|
+
SymbolKind2["Method"] = "method";
|
|
2126
|
+
SymbolKind2["Variable"] = "variable";
|
|
2127
|
+
SymbolKind2["Constant"] = "constant";
|
|
2128
|
+
SymbolKind2["Interface"] = "interface";
|
|
2129
|
+
SymbolKind2["Type"] = "type";
|
|
2130
|
+
SymbolKind2["Enum"] = "enum";
|
|
2131
|
+
SymbolKind2["Struct"] = "struct";
|
|
2132
|
+
SymbolKind2["Import"] = "import";
|
|
2133
|
+
SymbolKind2["Module"] = "module";
|
|
2134
|
+
SymbolKind2["Namespace"] = "namespace";
|
|
2135
|
+
SymbolKind2["Property"] = "property";
|
|
2136
|
+
SymbolKind2["Heading"] = "heading";
|
|
2137
|
+
SymbolKind2["Table"] = "table";
|
|
2138
|
+
SymbolKind2["View"] = "view";
|
|
2139
|
+
SymbolKind2["Procedure"] = "procedure";
|
|
2140
|
+
SymbolKind2["Trigger"] = "trigger";
|
|
2141
|
+
SymbolKind2["Index"] = "index";
|
|
2142
|
+
SymbolKind2["Schema"] = "schema";
|
|
2143
|
+
SymbolKind2["Signal"] = "signal";
|
|
2144
|
+
SymbolKind2["Unknown"] = "unknown";
|
|
2145
|
+
})(SymbolKind ||= {});
|
|
2146
|
+
|
|
2147
|
+
// src/readseek-settings.ts
|
|
2148
|
+
import { readFileSync, statSync } from "node:fs";
|
|
2149
|
+
import { homedir as homedir2 } from "node:os";
|
|
2150
|
+
import { join } from "node:path";
|
|
2151
|
+
var READSEEK_TOOL_REPLACEMENTS = {
|
|
2152
|
+
read: "readSeek_digest",
|
|
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");
|
|
1738
2167
|
}
|
|
1739
|
-
function
|
|
1740
|
-
|
|
1741
|
-
readSeekBinaryPath();
|
|
1742
|
-
return { available: true };
|
|
1743
|
-
} catch (err) {
|
|
1744
|
-
return { available: false, reason: classifyReadSeekFailure(err).message };
|
|
1745
|
-
}
|
|
2168
|
+
function projectSettingsPath() {
|
|
2169
|
+
return join(process.cwd(), ".pi/settings.json");
|
|
1746
2170
|
}
|
|
1747
|
-
function
|
|
1748
|
-
|
|
1749
|
-
const message = String(failure?.message ?? err);
|
|
1750
|
-
const missing = failure?.code === "ENOENT" || /Cannot find package|Cannot find module|no such file/i.test(message);
|
|
1751
|
-
if (missing) {
|
|
1752
|
-
return { code: "readseek-not-installed", message, hint: "Run npm install to install @jarkkojs/readseek." };
|
|
1753
|
-
}
|
|
1754
|
-
return { code: "readseek-execution-error", message };
|
|
2171
|
+
function isRecord(value) {
|
|
2172
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1755
2173
|
}
|
|
1756
|
-
function
|
|
1757
|
-
|
|
1758
|
-
return statSync2(dirPath).isDirectory();
|
|
1759
|
-
} catch {
|
|
1760
|
-
return false;
|
|
1761
|
-
}
|
|
2174
|
+
function invalid(source, path2) {
|
|
2175
|
+
return { source, path: path2, message: `Invalid readseek setting at ${path2}` };
|
|
1762
2176
|
}
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
let dir = start;
|
|
1770
|
-
let result = false;
|
|
1771
|
-
while (true) {
|
|
1772
|
-
const packageJsonPath = path2.join(dir, "package.json");
|
|
1773
|
-
if (existsSync(packageJsonPath)) {
|
|
1774
|
-
try {
|
|
1775
|
-
const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
|
|
1776
|
-
if (typeof packageJson.name === "string" && READSEEK_REPO_PACKAGE_NAMES.has(packageJson.name)) {
|
|
1777
|
-
result = true;
|
|
1778
|
-
break;
|
|
1779
|
-
}
|
|
1780
|
-
} catch {}
|
|
1781
|
-
}
|
|
1782
|
-
const parent = path2.dirname(dir);
|
|
1783
|
-
if (parent === dir)
|
|
1784
|
-
break;
|
|
1785
|
-
dir = parent;
|
|
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}` });
|
|
1786
2183
|
}
|
|
1787
|
-
ownRepositoryByDir.set(start, result);
|
|
1788
|
-
return result;
|
|
1789
|
-
}
|
|
1790
|
-
function defaultReadSeekDir() {
|
|
1791
|
-
const home = homedir3();
|
|
1792
|
-
return home ? path2.join(home, ".pi", "readseek") : null;
|
|
1793
|
-
}
|
|
1794
|
-
var DEFAULT_READSEEK_VISION_TIMEOUT_MS = 30 * 60000;
|
|
1795
|
-
function readSeekTimeoutMs() {
|
|
1796
|
-
return resolveReadSeekTimeoutMs() ?? DEFAULT_READSEEK_TIMEOUT_MS;
|
|
1797
|
-
}
|
|
1798
|
-
async function spawnReadSeekRaw(args, options = {}) {
|
|
1799
|
-
return runReadSeekNodeText(readSeekBinaryPath(), args, {
|
|
1800
|
-
signal: options.signal,
|
|
1801
|
-
stdin: options.stdin,
|
|
1802
|
-
timeoutMs: options.timeoutMs ?? readSeekTimeoutMs(),
|
|
1803
|
-
cancelable: options.cancelable
|
|
1804
|
-
});
|
|
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
|
});
|
|
2261
|
+
}
|
|
2262
|
+
return { settings, warnings };
|
|
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;
|
|
1874
2294
|
} else {
|
|
1875
|
-
|
|
2295
|
+
warnings.push(invalid(source, "readseek.grep"));
|
|
1876
2296
|
}
|
|
1877
|
-
signal?.throwIfAborted();
|
|
1878
|
-
return await runReadSeekRaw(args, {
|
|
1879
|
-
...options,
|
|
1880
|
-
timeoutMs: options.timeoutMs ?? resolveReadSeekTimeoutMs() ?? DEFAULT_READSEEK_VISION_TIMEOUT_MS
|
|
1881
|
-
});
|
|
1882
|
-
} finally {
|
|
1883
|
-
release();
|
|
1884
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
|
-
};
|
|
2303
|
+
var settingsFileCache = new Map;
|
|
2304
|
+
function statFileOrNull(path2) {
|
|
2305
|
+
try {
|
|
2306
|
+
return statSync(path2);
|
|
2307
|
+
} catch {
|
|
2308
|
+
return null;
|
|
2309
|
+
}
|
|
1995
2310
|
}
|
|
1996
|
-
function
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
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;
|
|
2000
2330
|
}
|
|
2001
|
-
function
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
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;
|
|
2340
|
+
}
|
|
2341
|
+
function resolveReadSeekJsonSettings() {
|
|
2342
|
+
const globalResult = readSettingsFile(globalSettingsPath());
|
|
2343
|
+
const projectResult = readSettingsFile(projectSettingsPath());
|
|
2007
2344
|
return {
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
throw new Error("invalid readseek reference");
|
|
2011
|
-
const item = reference;
|
|
2012
|
-
const symbol = item.symbol;
|
|
2013
|
-
const enclosing = symbol && typeof symbol === "object" ? optionalString(symbol.qualified_name, "reference.symbol.qualified_name") : undefined;
|
|
2014
|
-
return {
|
|
2015
|
-
file: requireString(item.file, "reference.file"),
|
|
2016
|
-
line: requireNumber(item.line, "reference.line"),
|
|
2017
|
-
column: requireNumber(item.column, "reference.column"),
|
|
2018
|
-
line_hash: requireString(item.line_hash, "reference.line_hash"),
|
|
2019
|
-
text: requireString(item.text, "reference.text"),
|
|
2020
|
-
enclosingSymbol: enclosing
|
|
2021
|
-
};
|
|
2022
|
-
})
|
|
2345
|
+
settings: mergeSettings(globalResult.settings, projectResult.settings),
|
|
2346
|
+
warnings: [...globalResult.warnings, ...projectResult.warnings]
|
|
2023
2347
|
};
|
|
2024
2348
|
}
|
|
2025
|
-
function
|
|
2026
|
-
|
|
2027
|
-
return value;
|
|
2028
|
-
throw new Error("invalid readseek diagnostic.kind");
|
|
2349
|
+
function resolveReadSeekImageMode() {
|
|
2350
|
+
return resolveReadSeekJsonSettings().settings.imageMode ?? "auto";
|
|
2029
2351
|
}
|
|
2030
|
-
function
|
|
2031
|
-
|
|
2032
|
-
throw new Error("invalid readseek check output");
|
|
2033
|
-
const output = value;
|
|
2034
|
-
if (!Array.isArray(output.diagnostics))
|
|
2035
|
-
throw new Error("invalid readseek diagnostics");
|
|
2036
|
-
return {
|
|
2037
|
-
errorCount: requireNumber(output.error_count, "error_count"),
|
|
2038
|
-
missingCount: requireNumber(output.missing_count, "missing_count"),
|
|
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
|
-
})
|
|
2049
|
-
};
|
|
2352
|
+
function resolveReadSeekSyntaxValidation() {
|
|
2353
|
+
return resolveReadSeekJsonSettings().settings.syntaxValidation;
|
|
2050
2354
|
}
|
|
2051
|
-
function
|
|
2052
|
-
return
|
|
2355
|
+
function resolveReadSeekTimeoutMs() {
|
|
2356
|
+
return resolveReadSeekJsonSettings().settings.timeoutMs;
|
|
2053
2357
|
}
|
|
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
|
-
});
|
|
2358
|
+
function resolveReadSeekToolDisplayMode(tool) {
|
|
2359
|
+
return resolveReadSeekJsonSettings().settings.display?.[tool] ?? DEFAULT_DISPLAY_MODES[tool];
|
|
2071
2360
|
}
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2361
|
+
|
|
2362
|
+
// src/readseek-client.ts
|
|
2363
|
+
var resolveFromHost = createRequire2(import.meta.url).resolve;
|
|
2364
|
+
function readSeekBinaryPath() {
|
|
2365
|
+
return resolveReadSeekBinaryFromPackages(resolveFromHost);
|
|
2076
2366
|
}
|
|
2077
|
-
function
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
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
|
-
};
|
|
2367
|
+
function readSeekBinaryAvailability() {
|
|
2368
|
+
try {
|
|
2369
|
+
readSeekBinaryPath();
|
|
2370
|
+
return { available: true };
|
|
2371
|
+
} catch (err) {
|
|
2372
|
+
return { available: false, reason: classifyReadSeekFailure(err).message };
|
|
2095
2373
|
}
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
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
|
-
};
|
|
2374
|
+
}
|
|
2375
|
+
var READSEEK_REPO_PACKAGE_NAMES = new Set(["@jarkkojs/readseek", "readseek"]);
|
|
2376
|
+
var defaultReadSeekDirInit;
|
|
2377
|
+
function directoryExists(dirPath) {
|
|
2378
|
+
try {
|
|
2379
|
+
return statSync2(dirPath).isDirectory();
|
|
2380
|
+
} catch {
|
|
2381
|
+
return false;
|
|
2112
2382
|
}
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2383
|
+
}
|
|
2384
|
+
var ownRepositoryByDir = new Map;
|
|
2385
|
+
function isOwnReadSeekRepository(cwd = process.cwd()) {
|
|
2386
|
+
const start = path2.resolve(cwd);
|
|
2387
|
+
const cached = ownRepositoryByDir.get(start);
|
|
2388
|
+
if (cached !== undefined)
|
|
2389
|
+
return cached;
|
|
2390
|
+
let dir = start;
|
|
2391
|
+
let result = false;
|
|
2392
|
+
while (true) {
|
|
2393
|
+
const packageJsonPath = path2.join(dir, "package.json");
|
|
2394
|
+
if (existsSync(packageJsonPath)) {
|
|
2395
|
+
try {
|
|
2396
|
+
const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
|
|
2397
|
+
if (typeof packageJson.name === "string" && READSEEK_REPO_PACKAGE_NAMES.has(packageJson.name)) {
|
|
2398
|
+
result = true;
|
|
2399
|
+
break;
|
|
2400
|
+
}
|
|
2401
|
+
} catch {}
|
|
2402
|
+
}
|
|
2403
|
+
const parent = path2.dirname(dir);
|
|
2404
|
+
if (parent === dir)
|
|
2405
|
+
break;
|
|
2406
|
+
dir = parent;
|
|
2124
2407
|
}
|
|
2125
|
-
|
|
2408
|
+
ownRepositoryByDir.set(start, result);
|
|
2409
|
+
return result;
|
|
2126
2410
|
}
|
|
2127
|
-
function
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
const cache = value;
|
|
2131
|
-
const state = requireString(cache.state, "cache.state");
|
|
2132
|
-
if (state !== "built" && state !== "reused")
|
|
2133
|
-
throw new Error("invalid readseek cache.state");
|
|
2134
|
-
return {
|
|
2135
|
-
content_hash: requireString(cache.content_hash, "cache.content_hash"),
|
|
2136
|
-
state
|
|
2137
|
-
};
|
|
2411
|
+
function defaultReadSeekDir() {
|
|
2412
|
+
const home = homedir3();
|
|
2413
|
+
return home ? path2.join(home, ".pi", "readseek") : null;
|
|
2138
2414
|
}
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
const output = value;
|
|
2143
|
-
const identifier = output.identifier;
|
|
2144
|
-
const symbol = output.symbol;
|
|
2145
|
-
return {
|
|
2146
|
-
file: requireString(output.file, "file"),
|
|
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
|
-
};
|
|
2415
|
+
var DEFAULT_READSEEK_VISION_TIMEOUT_MS = 30 * 60000;
|
|
2416
|
+
function readSeekTimeoutMs() {
|
|
2417
|
+
return resolveReadSeekTimeoutMs() ?? DEFAULT_READSEEK_TIMEOUT_MS;
|
|
2169
2418
|
}
|
|
2170
|
-
function
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2419
|
+
async function spawnReadSeekRaw(args, options = {}) {
|
|
2420
|
+
return runReadSeekNodeText(readSeekBinaryPath(), args, {
|
|
2421
|
+
signal: options.signal,
|
|
2422
|
+
stdin: options.stdin,
|
|
2423
|
+
timeoutMs: options.timeoutMs ?? readSeekTimeoutMs(),
|
|
2424
|
+
cancelable: options.cancelable
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2427
|
+
async function ensureDefaultReadSeekDir() {
|
|
2428
|
+
const dir = defaultReadSeekDir();
|
|
2429
|
+
if (!dir)
|
|
2430
|
+
return null;
|
|
2431
|
+
if (directoryExists(dir))
|
|
2432
|
+
return dir;
|
|
2433
|
+
defaultReadSeekDirInit ??= spawnReadSeekRaw(["--readseek-dir", dir, "init"]).then(() => directoryExists(dir) ? dir : null).catch(() => null).finally(() => {
|
|
2434
|
+
defaultReadSeekDirInit = undefined;
|
|
2435
|
+
});
|
|
2436
|
+
return defaultReadSeekDirInit;
|
|
2437
|
+
}
|
|
2438
|
+
async function readSeekInvocationArgs(args) {
|
|
2439
|
+
if (isOwnReadSeekRepository())
|
|
2440
|
+
return [...args];
|
|
2441
|
+
const readSeekDir = await ensureDefaultReadSeekDir();
|
|
2442
|
+
return readSeekDir ? ["--readseek-dir", readSeekDir, ...args] : [...args];
|
|
2190
2443
|
}
|
|
2191
|
-
function
|
|
2192
|
-
return
|
|
2193
|
-
path: target,
|
|
2194
|
-
select: options.select ?? "content",
|
|
2195
|
-
at: options.at,
|
|
2196
|
-
end: options.end,
|
|
2197
|
-
limit: options.limit,
|
|
2198
|
-
language: options.language,
|
|
2199
|
-
visionMode: options.visionMode,
|
|
2200
|
-
visionLevel: options.visionLevel,
|
|
2201
|
-
stdinName: options.stdin !== undefined && target === "-" ? options.stdinName ?? target : undefined
|
|
2202
|
-
});
|
|
2444
|
+
async function runReadSeekRaw(args, options = {}) {
|
|
2445
|
+
return spawnReadSeekRaw(await readSeekInvocationArgs(args), options);
|
|
2203
2446
|
}
|
|
2204
|
-
async function
|
|
2205
|
-
|
|
2206
|
-
const runOptions = {
|
|
2447
|
+
async function runReadSeek(args, options = {}) {
|
|
2448
|
+
return runReadSeekNodeJson(readSeekBinaryPath(), await readSeekInvocationArgs(args), {
|
|
2207
2449
|
signal: options.signal,
|
|
2208
2450
|
stdin: options.stdin,
|
|
2209
|
-
timeoutMs: options.timeoutMs
|
|
2210
|
-
|
|
2211
|
-
|
|
2451
|
+
timeoutMs: options.timeoutMs ?? readSeekTimeoutMs(),
|
|
2452
|
+
cancelable: options.cancelable
|
|
2453
|
+
});
|
|
2454
|
+
}
|
|
2455
|
+
var visionInvocationTail = Promise.resolve();
|
|
2456
|
+
async function runReadSeekVisionRaw(args, options = {}) {
|
|
2457
|
+
let release;
|
|
2458
|
+
const gate = new Promise((resolve) => {
|
|
2459
|
+
release = resolve;
|
|
2460
|
+
});
|
|
2461
|
+
const predecessor = visionInvocationTail;
|
|
2462
|
+
visionInvocationTail = predecessor.then(() => gate);
|
|
2463
|
+
const signal = options.signal;
|
|
2464
|
+
try {
|
|
2465
|
+
if (signal) {
|
|
2466
|
+
signal.throwIfAborted();
|
|
2467
|
+
await new Promise((resolve, reject) => {
|
|
2468
|
+
const onAbort = () => {
|
|
2469
|
+
signal.removeEventListener("abort", onAbort);
|
|
2470
|
+
reject(signal.reason);
|
|
2471
|
+
};
|
|
2472
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2473
|
+
predecessor.then(() => {
|
|
2474
|
+
signal.removeEventListener("abort", onAbort);
|
|
2475
|
+
resolve();
|
|
2476
|
+
}, (error) => {
|
|
2477
|
+
signal.removeEventListener("abort", onAbort);
|
|
2478
|
+
reject(error);
|
|
2479
|
+
});
|
|
2480
|
+
});
|
|
2481
|
+
} else {
|
|
2482
|
+
await predecessor;
|
|
2483
|
+
}
|
|
2484
|
+
signal?.throwIfAborted();
|
|
2485
|
+
return await runReadSeekRaw(args, {
|
|
2486
|
+
...options,
|
|
2487
|
+
timeoutMs: options.timeoutMs ?? resolveReadSeekTimeoutMs() ?? DEFAULT_READSEEK_VISION_TIMEOUT_MS
|
|
2488
|
+
});
|
|
2489
|
+
} finally {
|
|
2490
|
+
release();
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
async function runReadSeekVision(args, options = {}) {
|
|
2494
|
+
return JSON.parse(await runReadSeekVisionRaw(args, options));
|
|
2495
|
+
}
|
|
2496
|
+
var runner = {
|
|
2497
|
+
text: (args, options = {}) => options.vision ? runReadSeekVisionRaw(args, options) : runReadSeekRaw(args, options),
|
|
2498
|
+
json: (args, options = {}) => options.vision ? runReadSeekVision(args, options) : runReadSeek(args, options)
|
|
2499
|
+
};
|
|
2500
|
+
var client = createReadSeekClient(runner);
|
|
2501
|
+
var readSeekView = client.view;
|
|
2502
|
+
var readSeekDigestNative = client.digestRaw;
|
|
2503
|
+
var readSeekDigest = client.digest;
|
|
2504
|
+
var readSeekDigestContent = client.digestContent;
|
|
2505
|
+
var readSeekImage = client.image;
|
|
2506
|
+
var readSeekPreparedImage = client.preparedImage;
|
|
2507
|
+
var readSeekSearch = client.search;
|
|
2508
|
+
var readSeekRefs = client.refs;
|
|
2509
|
+
var readSeekDef = client.def;
|
|
2510
|
+
var readSeekCheck = client.check;
|
|
2511
|
+
var readSeekIdentify = client.identify;
|
|
2512
|
+
var readSeekDetect = client.detect;
|
|
2513
|
+
var readSeekEdit = client.edit;
|
|
2514
|
+
var readSeekRename = client.rename;
|
|
2515
|
+
function normalizeLanguage(language) {
|
|
2516
|
+
return language === "java" ? "Java" : language;
|
|
2517
|
+
}
|
|
2518
|
+
function normalizeKind(kind) {
|
|
2519
|
+
if (kind === "constructor")
|
|
2520
|
+
return "method" /* Method */;
|
|
2521
|
+
if (Object.values(SymbolKind).includes(kind))
|
|
2522
|
+
return kind;
|
|
2523
|
+
return "unknown" /* Unknown */;
|
|
2212
2524
|
}
|
|
2213
|
-
|
|
2214
|
-
|
|
2525
|
+
function parentQualifiedNameFor(qualifiedName) {
|
|
2526
|
+
const lastDot = qualifiedName.lastIndexOf(".");
|
|
2527
|
+
return lastDot === -1 ? "" : qualifiedName.slice(0, lastDot);
|
|
2215
2528
|
}
|
|
2216
|
-
|
|
2217
|
-
|
|
2529
|
+
function symbolsFromReadSeek(symbols) {
|
|
2530
|
+
const symbolsByQualifiedName = new Map;
|
|
2531
|
+
const entries = [];
|
|
2532
|
+
for (const symbol of symbols) {
|
|
2533
|
+
const parentQualifiedName = parentQualifiedNameFor(symbol.qualified_name);
|
|
2534
|
+
const fileSymbol = {
|
|
2535
|
+
name: symbol.name,
|
|
2536
|
+
kind: normalizeKind(symbol.kind),
|
|
2537
|
+
startLine: symbol.start_line,
|
|
2538
|
+
endLine: symbol.end_line
|
|
2539
|
+
};
|
|
2540
|
+
const bucket = symbolsByQualifiedName.get(symbol.qualified_name);
|
|
2541
|
+
if (bucket)
|
|
2542
|
+
bucket.push(fileSymbol);
|
|
2543
|
+
else
|
|
2544
|
+
symbolsByQualifiedName.set(symbol.qualified_name, [fileSymbol]);
|
|
2545
|
+
entries.push({ parentQualifiedName, symbol: fileSymbol });
|
|
2546
|
+
}
|
|
2547
|
+
const roots = [];
|
|
2548
|
+
for (const entry of entries) {
|
|
2549
|
+
const parent = entry.parentQualifiedName ? symbolsByQualifiedName.get(entry.parentQualifiedName)?.[0] : undefined;
|
|
2550
|
+
if (!parent) {
|
|
2551
|
+
roots.push(entry.symbol);
|
|
2552
|
+
continue;
|
|
2553
|
+
}
|
|
2554
|
+
parent.children ??= [];
|
|
2555
|
+
parent.children.push(entry.symbol);
|
|
2556
|
+
}
|
|
2557
|
+
return roots;
|
|
2218
2558
|
}
|
|
2219
2559
|
function fileMapFromReadSeekOutput(output, filePath, totalBytes) {
|
|
2220
2560
|
if (output.language === "unknown" && output.symbols.length === 0)
|
|
@@ -2229,196 +2569,11 @@ function fileMapFromReadSeekOutput(output, filePath, totalBytes) {
|
|
|
2229
2569
|
};
|
|
2230
2570
|
}
|
|
2231
2571
|
async function readSeekMap(filePath, totalBytes, options = {}) {
|
|
2232
|
-
const envelope = await
|
|
2572
|
+
const envelope = await client.digest(filePath, { select: "map", signal: options.signal });
|
|
2233
2573
|
if (!envelope.map)
|
|
2234
2574
|
return null;
|
|
2235
2575
|
return fileMapFromReadSeekOutput(envelope.map, filePath, totalBytes);
|
|
2236
2576
|
}
|
|
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
2577
|
|
|
2423
2578
|
// src/edit-syntax-validate.ts
|
|
2424
2579
|
function dedupeSortLines(diagnostics) {
|
|
@@ -5308,6 +5463,36 @@ function summarizeEnvelope(envelope, facets) {
|
|
|
5308
5463
|
parts.unshift("metadata");
|
|
5309
5464
|
return `digested ${parts.join(",") || "metadata"}`;
|
|
5310
5465
|
}
|
|
5466
|
+
function parseImageEnvelope(envelope) {
|
|
5467
|
+
if (!envelope || typeof envelope !== "object")
|
|
5468
|
+
return;
|
|
5469
|
+
const record = envelope;
|
|
5470
|
+
if (!record.metadata || typeof record.metadata !== "object")
|
|
5471
|
+
return;
|
|
5472
|
+
const metadata = record.metadata;
|
|
5473
|
+
if (typeof metadata.type !== "string" || !metadata.type.startsWith("image/"))
|
|
5474
|
+
return;
|
|
5475
|
+
if (!record.content || typeof record.content !== "object")
|
|
5476
|
+
return { metadata };
|
|
5477
|
+
const content = record.content;
|
|
5478
|
+
const mime = content.mime ?? content.type ?? metadata.mime ?? metadata.type;
|
|
5479
|
+
const prepared = content.encoding === "base64" && typeof content.data === "string" && typeof mime === "string" ? { data: content.data, mimeType: mime } : undefined;
|
|
5480
|
+
return { metadata, prepared };
|
|
5481
|
+
}
|
|
5482
|
+
function buildDigestSuccess(filePath, facets, envelope, content = [{ type: "text", text: JSON.stringify(envelope) }]) {
|
|
5483
|
+
return {
|
|
5484
|
+
content,
|
|
5485
|
+
details: {
|
|
5486
|
+
readSeekValue: {
|
|
5487
|
+
tool: "digest",
|
|
5488
|
+
ok: true,
|
|
5489
|
+
path: filePath,
|
|
5490
|
+
select: facets,
|
|
5491
|
+
envelope
|
|
5492
|
+
}
|
|
5493
|
+
}
|
|
5494
|
+
};
|
|
5495
|
+
}
|
|
5311
5496
|
async function executeDigest(opts) {
|
|
5312
5497
|
const { params, signal, cwd, onFileAnchored } = opts;
|
|
5313
5498
|
const p = params;
|
|
@@ -5319,6 +5504,8 @@ async function executeDigest(opts) {
|
|
|
5319
5504
|
path: p.path
|
|
5320
5505
|
});
|
|
5321
5506
|
}
|
|
5507
|
+
const facets = selectedFacets(select);
|
|
5508
|
+
const appliesImagePolicy = opts.imageMode !== undefined && facets.includes("content");
|
|
5322
5509
|
const end = coerceObviousBase10Int(p.end, "end");
|
|
5323
5510
|
if (!end.ok || end.value !== undefined && end.value < 1) {
|
|
5324
5511
|
const message = end.ok ? `Invalid end: expected a positive integer, received ${end.value}.` : end.message;
|
|
@@ -5335,41 +5522,67 @@ async function executeDigest(opts) {
|
|
|
5335
5522
|
path: p.path
|
|
5336
5523
|
});
|
|
5337
5524
|
}
|
|
5338
|
-
if (p.visionLevel !== undefined && p.visionMode === undefined) {
|
|
5525
|
+
if (p.visionLevel !== undefined && p.visionMode === undefined && !appliesImagePolicy) {
|
|
5339
5526
|
return buildToolErrorResult("digest", "invalid-params-combo", "visionLevel requires visionMode.", {
|
|
5340
5527
|
path: p.path
|
|
5341
5528
|
});
|
|
5342
5529
|
}
|
|
5343
5530
|
const filePath = resolveToCwd(p.path, cwd);
|
|
5344
|
-
const
|
|
5345
|
-
const
|
|
5531
|
+
const commonOptions = { select, signal };
|
|
5532
|
+
const textOptions = {
|
|
5533
|
+
...commonOptions,
|
|
5534
|
+
at,
|
|
5535
|
+
end: end.value,
|
|
5536
|
+
limit: limit.value,
|
|
5537
|
+
language: p.language
|
|
5538
|
+
};
|
|
5539
|
+
const hasTextOptions = at !== undefined || end.value !== undefined || limit.value !== undefined || p.language !== undefined;
|
|
5540
|
+
const hasVisionOptions = p.visionMode !== undefined || p.visionLevel !== undefined;
|
|
5541
|
+
const initialVisionMode = appliesImagePolicy ? undefined : p.visionMode;
|
|
5542
|
+
const initialVisionLevel = appliesImagePolicy ? undefined : p.visionLevel;
|
|
5346
5543
|
try {
|
|
5347
5544
|
const envelope = await readSeekDigestNative(filePath, {
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5545
|
+
...appliesImagePolicy ? commonOptions : textOptions,
|
|
5546
|
+
visionMode: initialVisionMode,
|
|
5547
|
+
visionLevel: initialVisionLevel,
|
|
5548
|
+
vision: initialVisionMode !== undefined && initialVisionMode !== "none"
|
|
5549
|
+
});
|
|
5550
|
+
const image = appliesImagePolicy ? parseImageEnvelope(envelope) : undefined;
|
|
5551
|
+
if (image) {
|
|
5552
|
+
const imageDetails = { metadata: image.metadata };
|
|
5553
|
+
if (opts.imageMode === "off") {
|
|
5554
|
+
return buildDigestSuccess(filePath, facets, imageDetails, [
|
|
5555
|
+
{ type: "text", text: `[Skipped standalone image: ${p.path}; readseek.imageMode is off]` }
|
|
5556
|
+
]);
|
|
5557
|
+
}
|
|
5558
|
+
if (opts.imageMode === "auto" && opts.modelSupportsImages && image.prepared) {
|
|
5559
|
+
return buildDigestSuccess(filePath, facets, imageDetails, [
|
|
5560
|
+
{ type: "image", data: image.prepared.data, mimeType: image.prepared.mimeType }
|
|
5561
|
+
]);
|
|
5562
|
+
}
|
|
5563
|
+
const visionMode = p.visionMode && p.visionMode !== "none" ? p.visionMode : "all";
|
|
5564
|
+
const analyzedEnvelope = await readSeekDigestNative(filePath, {
|
|
5565
|
+
...commonOptions,
|
|
5566
|
+
visionMode,
|
|
5567
|
+
visionLevel: p.visionLevel,
|
|
5568
|
+
vision: true
|
|
5569
|
+
});
|
|
5570
|
+
return buildDigestSuccess(filePath, facets, analyzedEnvelope);
|
|
5571
|
+
}
|
|
5572
|
+
if (p.visionLevel !== undefined && p.visionMode === undefined) {
|
|
5573
|
+
return buildToolErrorResult("digest", "invalid-params-combo", "visionLevel requires visionMode.", {
|
|
5574
|
+
path: p.path
|
|
5575
|
+
});
|
|
5576
|
+
}
|
|
5577
|
+
const textEnvelope = appliesImagePolicy && (hasTextOptions || hasVisionOptions) ? await readSeekDigestNative(filePath, {
|
|
5578
|
+
...textOptions,
|
|
5353
5579
|
visionMode: p.visionMode,
|
|
5354
5580
|
visionLevel: p.visionLevel,
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
if (hasDigestAnchors(envelope))
|
|
5581
|
+
vision: p.visionMode !== undefined && p.visionMode !== "none"
|
|
5582
|
+
}) : envelope;
|
|
5583
|
+
if (hasDigestAnchors(textEnvelope))
|
|
5359
5584
|
onFileAnchored?.(filePath);
|
|
5360
|
-
|
|
5361
|
-
return {
|
|
5362
|
-
content: [{ type: "text", text }],
|
|
5363
|
-
details: {
|
|
5364
|
-
readSeekValue: {
|
|
5365
|
-
tool: "digest",
|
|
5366
|
-
ok: true,
|
|
5367
|
-
path: filePath,
|
|
5368
|
-
select: facets,
|
|
5369
|
-
envelope
|
|
5370
|
-
}
|
|
5371
|
-
}
|
|
5372
|
-
};
|
|
5585
|
+
return buildDigestSuccess(filePath, facets, textEnvelope);
|
|
5373
5586
|
} catch (err) {
|
|
5374
5587
|
const failure = classifyReadSeekFailure(err);
|
|
5375
5588
|
return buildToolErrorResult("digest", failure.code, failure.message, failure.hint ? { hint: failure.hint } : {});
|
|
@@ -5391,7 +5604,14 @@ function registerDigestTool(pi, options = {}) {
|
|
|
5391
5604
|
promptGuidelines: promptMetadata.promptGuidelines,
|
|
5392
5605
|
parameters: digestSchema,
|
|
5393
5606
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
5394
|
-
return executeDigest({
|
|
5607
|
+
return executeDigest({
|
|
5608
|
+
params,
|
|
5609
|
+
signal,
|
|
5610
|
+
cwd: ctx.cwd,
|
|
5611
|
+
onFileAnchored: options.onFileAnchored,
|
|
5612
|
+
imageMode: options.imageMode,
|
|
5613
|
+
modelSupportsImages: ctx.model?.input.includes("image") ?? false
|
|
5614
|
+
});
|
|
5395
5615
|
},
|
|
5396
5616
|
renderCall(args, theme, ...rest) {
|
|
5397
5617
|
const context = rest[0] ?? {};
|
|
@@ -5604,7 +5824,7 @@ function piReadSeekExtension(pi) {
|
|
|
5604
5824
|
const { settings } = resolveReadSeekJsonSettings();
|
|
5605
5825
|
const replacedBuiltIns = new Set(settings.overrideTools ?? []);
|
|
5606
5826
|
const binaryAvailable = readSeekBinaryAvailability().available;
|
|
5607
|
-
const swap = (builtIn, readSeekName) =>
|
|
5827
|
+
const swap = (builtIn, readSeekName) => replacedBuiltIns.has(builtIn) ? builtIn : readSeekName;
|
|
5608
5828
|
const readName = swap("read", "readSeek_digest");
|
|
5609
5829
|
const editName = swap("edit", "readSeek_edit");
|
|
5610
5830
|
const writeName = swap("write", "readSeek_write");
|
|
@@ -5625,7 +5845,12 @@ function piReadSeekExtension(pi) {
|
|
|
5625
5845
|
registerRefsTool(pi, { onFileAnchored: markAnchored });
|
|
5626
5846
|
registerRenameTool(pi, { onFileMutated: forgetAnchors });
|
|
5627
5847
|
registerDefTool(pi, { onFileAnchored: markAnchored });
|
|
5628
|
-
registerDigestTool(pi, {
|
|
5848
|
+
registerDigestTool(pi, {
|
|
5849
|
+
onFileAnchored: markAnchored,
|
|
5850
|
+
name: readName,
|
|
5851
|
+
toolAliases,
|
|
5852
|
+
imageMode: replacedBuiltIns.has("read") ? resolveReadSeekImageMode() : undefined
|
|
5853
|
+
});
|
|
5629
5854
|
registerViewTool(pi);
|
|
5630
5855
|
registerWriteTool(pi, { onFileAnchored: markAnchored, name: writeName });
|
|
5631
5856
|
pi.on("before_agent_start", (event) => {
|
|
@@ -5641,7 +5866,7 @@ ${toolRoutingPolicy(policyAliases)}` };
|
|
|
5641
5866
|
const problems = warnings.map(formatSettingsWarning);
|
|
5642
5867
|
const availability = readSeekBinaryAvailability();
|
|
5643
5868
|
if (!availability.available) {
|
|
5644
|
-
problems.push(`readseek
|
|
5869
|
+
problems.push(`readseek installation is broken: ${availability.reason}`);
|
|
5645
5870
|
}
|
|
5646
5871
|
for (const problem of problems) {
|
|
5647
5872
|
if (ctx.hasUI)
|