opencode-readseek 0.9.6 → 0.9.7
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/dist/index.js +825 -726
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// index.ts
|
|
2
|
-
import
|
|
2
|
+
import path7 from "node:path";
|
|
3
|
+
|
|
4
|
+
// src/client.ts
|
|
3
5
|
import { createRequire as createRequire2 } from "node:module";
|
|
4
6
|
import { homedir } from "node:os";
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
+
import path3 from "node:path";
|
|
8
|
+
import { stat } from "node:fs/promises";
|
|
7
9
|
|
|
8
10
|
// ../readseek-api/src/contract.ts
|
|
9
11
|
var VISION_MODES = ["none", "all", "ocr", "caption", "objects"];
|
|
@@ -34,6 +36,8 @@ var TOOL_NAMES = [
|
|
|
34
36
|
];
|
|
35
37
|
var PARAM_DESCRIPTIONS = {
|
|
36
38
|
path: "Absolute or relative file path",
|
|
39
|
+
projectPath: "Path relative to the project directory",
|
|
40
|
+
projectSearchPath: "File or directory to search; defaults to the project directory",
|
|
37
41
|
searchPath: "File or directory to search; defaults to the current directory",
|
|
38
42
|
language: "Language override when auto-detection is ambiguous",
|
|
39
43
|
languageOverride: "Language override",
|
|
@@ -87,6 +91,7 @@ var PARAM_DESCRIPTIONS = {
|
|
|
87
91
|
symbol: "Mapped symbol name",
|
|
88
92
|
newBody: "Complete replacement body for the symbol",
|
|
89
93
|
apply: "When supported by the host, apply the verified plan instead of previewing only",
|
|
94
|
+
applyDefaultTrue: "Apply the verified edits; defaults to true",
|
|
90
95
|
planHash: "Require the apply plan to match this dry-run plan hash",
|
|
91
96
|
workspace: "Rename across the project",
|
|
92
97
|
to: "New symbol name",
|
|
@@ -391,6 +396,88 @@ var TOOL_CONTRACTS = [
|
|
|
391
396
|
}
|
|
392
397
|
}
|
|
393
398
|
];
|
|
399
|
+
// ../readseek-api/src/policy.ts
|
|
400
|
+
var BUILTIN_FALLBACKS = {
|
|
401
|
+
read: "digest",
|
|
402
|
+
edit: "edit",
|
|
403
|
+
write: "write",
|
|
404
|
+
grep: "grep",
|
|
405
|
+
apply_patch: "edit"
|
|
406
|
+
};
|
|
407
|
+
var PREFERENCE_LEADS = {
|
|
408
|
+
digest: "Preferred for reading source with anchors and facets.",
|
|
409
|
+
edit: "Preferred for editing existing text files with fresh LINE:HASH anchors.",
|
|
410
|
+
write: "Preferred for creating or replacing a complete text file; returns LINE:HASH anchors.",
|
|
411
|
+
grep: "Preferred text/regex search; returns edit-ready LINE:HASH anchors.",
|
|
412
|
+
search: "Preferred for AST or structural code search with ast-grep-style patterns.",
|
|
413
|
+
def: "Preferred for go-to-definition or declaration lookup by name.",
|
|
414
|
+
refs: "Preferred for finding identifier-name matches or binding-specific references before rename or delete.",
|
|
415
|
+
rename: "Preferred for renaming a symbol with binding-aware, verified edits.",
|
|
416
|
+
view: "Preferred for digesting document structure or selected content."
|
|
417
|
+
};
|
|
418
|
+
var FALLBACK_TEMPLATES = {
|
|
419
|
+
read: "Fallback only. Prefer `digest` with fresh LINE:HASH anchors.",
|
|
420
|
+
edit: "Fallback only. Prefer `edit` with fresh LINE:HASH anchors.",
|
|
421
|
+
write: "Fallback only. Prefer `write` so the result returns LINE:HASH anchors.",
|
|
422
|
+
grep: "Fallback only. Prefer `grep` for text/regex search with edit-ready anchors.",
|
|
423
|
+
apply_patch: "Fallback only. Prefer `edit` for existing text files."
|
|
424
|
+
};
|
|
425
|
+
function resolveToolName(name, aliases = {}) {
|
|
426
|
+
return aliases[name] ?? name;
|
|
427
|
+
}
|
|
428
|
+
function rewriteBacktickedToolNames(text, aliases = {}) {
|
|
429
|
+
return TOOL_NAMES.reduce((rewritten, toolName) => rewritten.replaceAll(`\`${toolName}\``, `\`${resolveToolName(toolName, aliases)}\``), text);
|
|
430
|
+
}
|
|
431
|
+
function renderPreferredToolDescription(name, aliases = {}) {
|
|
432
|
+
return rewriteBacktickedToolNames(PREFERENCE_LEADS[name], aliases);
|
|
433
|
+
}
|
|
434
|
+
function renderFallbackToolDescription(builtIn, aliases = {}) {
|
|
435
|
+
const template = FALLBACK_TEMPLATES[builtIn];
|
|
436
|
+
if (!template)
|
|
437
|
+
return;
|
|
438
|
+
return rewriteBacktickedToolNames(template, aliases);
|
|
439
|
+
}
|
|
440
|
+
function renderToolRoutingPolicy(options = {}) {
|
|
441
|
+
const aliases = options.aliases ?? {};
|
|
442
|
+
const digest = resolveToolName("digest", aliases);
|
|
443
|
+
const edit = resolveToolName("edit", aliases);
|
|
444
|
+
const write = resolveToolName("write", aliases);
|
|
445
|
+
const grep = resolveToolName("grep", aliases);
|
|
446
|
+
const search = resolveToolName("search", aliases);
|
|
447
|
+
const def = resolveToolName("def", aliases);
|
|
448
|
+
const refs = resolveToolName("refs", aliases);
|
|
449
|
+
const rename = resolveToolName("rename", aliases);
|
|
450
|
+
const view = resolveToolName("view", aliases);
|
|
451
|
+
if (options.style === "server") {
|
|
452
|
+
return [
|
|
453
|
+
"Structural code navigation, document viewing, and hash-verified edits with readseek.",
|
|
454
|
+
"For structural questions, prefer these tools to text Grep/Bash: definitions, references, AST search, multi-facet digest (content/map/diagnostics/identity), and document views.",
|
|
455
|
+
`Use ${edit} for LINE:HASH-anchored mutations (preview with apply=false; apply defaults to true and re-checks plan_hash).`,
|
|
456
|
+
"Paths resolve against CLAUDE_PROJECT_DIR. Native Read/Grep/Edit hooks provide passive context;",
|
|
457
|
+
"call MCP tools for active navigation, document questions, or anchored edits."
|
|
458
|
+
].join(" ");
|
|
459
|
+
}
|
|
460
|
+
const title = options.title ?? "ReadSeek tool policy:";
|
|
461
|
+
const bullets = [];
|
|
462
|
+
if (options.preferOverBuiltIns) {
|
|
463
|
+
bullets.push("- Prefer ReadSeek tools over built-ins when they can do the job.");
|
|
464
|
+
}
|
|
465
|
+
bullets.push(`- Read with ${digest} first; ${edit} needs fresh LINE:HASH anchors.`, `- Use ${grep} for text/regex, ${search} for AST patterns, and ${digest}/${def}/${refs} for symbol navigation.`, `- Use ${edit} for existing files, ${write} for whole-file creation or replacement, and ${rename} for symbol renames.`, `- Use ${view} for document structure; run ${digest} with select diagnostics after source edits for a quick syntax check.`);
|
|
466
|
+
if (options.demoteBuiltIns && options.demoteBuiltIns.length > 0) {
|
|
467
|
+
bullets.push(`- Do not use built-in ${options.demoteBuiltIns.join(", ")} when a ReadSeek tool can do the job.`);
|
|
468
|
+
}
|
|
469
|
+
return [title, ...bullets].join(`
|
|
470
|
+
`);
|
|
471
|
+
}
|
|
472
|
+
function preferredToolDescriptionMap(aliases = {}) {
|
|
473
|
+
return Object.fromEntries(TOOL_NAMES.map((name) => [resolveToolName(name, aliases), renderPreferredToolDescription(name, aliases)]));
|
|
474
|
+
}
|
|
475
|
+
function fallbackToolDescriptionMap(builtIns = Object.keys(BUILTIN_FALLBACKS), aliases = {}) {
|
|
476
|
+
return Object.fromEntries(builtIns.flatMap((builtIn) => {
|
|
477
|
+
const description = renderFallbackToolDescription(builtIn, aliases);
|
|
478
|
+
return description ? [[builtIn, description]] : [];
|
|
479
|
+
}));
|
|
480
|
+
}
|
|
394
481
|
// ../readseek-api/src/validation.ts
|
|
395
482
|
function validateGitSelection(input) {
|
|
396
483
|
if (input.ignored && !input.others) {
|
|
@@ -739,48 +826,103 @@ async function runReadSeekBunText(binaryPath, args, options = {}) {
|
|
|
739
826
|
async function runReadSeekBunJson(binaryPath, args, options = {}) {
|
|
740
827
|
return parseSuccessfulJson(await runReadSeekBun(binaryPath, args, options));
|
|
741
828
|
}
|
|
742
|
-
//
|
|
743
|
-
var resolveFromHost = createRequire2(import.meta.url).resolve;
|
|
829
|
+
// src/constants.ts
|
|
744
830
|
var DEFAULT_VISION_TIMEOUT_MS = 30 * 60 * 1000;
|
|
745
831
|
var DEFAULT_READ_LIMIT = 2000;
|
|
746
832
|
var EDIT_RESULT_READ_LIMIT = 40;
|
|
747
833
|
var MAX_DOCUMENT_OUTPUT_BYTES = 256 * 1024;
|
|
748
834
|
var MAX_DOCUMENT_OUTPUT_LINES = 2000;
|
|
749
|
-
var
|
|
750
|
-
"ReadSeek tool policy:",
|
|
751
|
-
"- Prefer readseek_* tools over built-ins when they can do the job.",
|
|
752
|
-
"- Read with readseek_digest first; readseek_edit needs fresh LINE:HASH anchors.",
|
|
753
|
-
"- Use readseek_grep for text/regex, readseek_search for AST patterns, and readseek_digest/def/refs for symbol navigation.",
|
|
754
|
-
"- Use readseek_edit for existing files, readseek_write for whole-file creation or replacement, and readseek_rename for symbol renames.",
|
|
755
|
-
"- Do not use built-in read, edit, write, grep, or apply_patch when a ReadSeek tool can do the job.",
|
|
756
|
-
"- Run readseek_digest with select=diagnostics after source edits for a quick syntax check."
|
|
757
|
-
].join(`
|
|
758
|
-
`);
|
|
759
|
-
var OPENCODE_TOOL_ALIASES = Object.fromEntries(TOOL_NAMES.map((name) => [name, `readseek_${name}`]));
|
|
760
|
-
function openCodeToolDescription(name) {
|
|
761
|
-
return renderToolDescription(name, OPENCODE_TOOL_ALIASES);
|
|
762
|
-
}
|
|
763
|
-
var PREFERRED_TOOL_DESCRIPTIONS = {
|
|
764
|
-
readseek_edit: "Preferred tool for editing existing text files with fresh LINE:HASH anchors.",
|
|
765
|
-
readseek_write: "Preferred tool for creating or replacing a complete text file; returns LINE:HASH anchors.",
|
|
766
|
-
readseek_rename: "Use to rename a symbol with binding-aware, verified edits.",
|
|
767
|
-
readseek_grep: "Preferred text/regex search; returns edit-ready LINE:HASH anchors.",
|
|
768
|
-
readseek_search: "Use for AST or structural code search with ast-grep-style patterns.",
|
|
769
|
-
readseek_digest: "Use to digest native facets (metadata, content, map, diagnostics, identity) and return the full envelope.",
|
|
770
|
-
readseek_def: "Use for go-to-definition or declaration lookup by name.",
|
|
771
|
-
readseek_refs: "Use to find identifier-name matches or binding-specific references before rename or delete.",
|
|
772
|
-
readseek_view: "Use to digest document structure or selected content."
|
|
773
|
-
};
|
|
774
|
-
var FALLBACK_TOOL_DESCRIPTIONS = {
|
|
775
|
-
edit: "Fallback only. Prefer readseek_edit with fresh LINE:HASH anchors.",
|
|
776
|
-
write: "Fallback only. Prefer readseek_write so the result returns LINE:HASH anchors.",
|
|
777
|
-
grep: "Fallback only. Prefer readseek_grep for text/regex search with edit-ready anchors.",
|
|
778
|
-
apply_patch: "Fallback only. Prefer readseek_edit for existing text files."
|
|
779
|
-
};
|
|
835
|
+
var MAX_PERMISSION_DIFF_BYTES = 32 * 1024;
|
|
780
836
|
var IMAGE_MODES = [...VISION_MODES];
|
|
781
837
|
var VISION_ANALYSIS_MODE_VALUES = [...VISION_ANALYSIS_MODES];
|
|
782
838
|
var VISION_LEVEL_VALUES = [...VISION_LEVELS];
|
|
783
839
|
var DOCUMENT_NODE_KIND_VALUES = [...DOCUMENT_NODE_KINDS];
|
|
840
|
+
|
|
841
|
+
// src/util.ts
|
|
842
|
+
import path2 from "node:path";
|
|
843
|
+
function render(value) {
|
|
844
|
+
return JSON.stringify(value, null, 2);
|
|
845
|
+
}
|
|
846
|
+
function record(value) {
|
|
847
|
+
return value && typeof value === "object" ? value : {};
|
|
848
|
+
}
|
|
849
|
+
function items(value) {
|
|
850
|
+
return Array.isArray(value) ? value : [];
|
|
851
|
+
}
|
|
852
|
+
function identifiedName(value) {
|
|
853
|
+
if (!value || typeof value !== "object")
|
|
854
|
+
return;
|
|
855
|
+
const identifier = value.identifier;
|
|
856
|
+
if (!identifier || typeof identifier !== "object")
|
|
857
|
+
return;
|
|
858
|
+
const text = identifier.text;
|
|
859
|
+
return typeof text === "string" ? text : undefined;
|
|
860
|
+
}
|
|
861
|
+
function collectAnchoredFiles(value, files) {
|
|
862
|
+
if (Array.isArray(value)) {
|
|
863
|
+
for (const item of value)
|
|
864
|
+
collectAnchoredFiles(item, files);
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
if (!value || typeof value !== "object")
|
|
868
|
+
return;
|
|
869
|
+
const output = value;
|
|
870
|
+
const hashlines = output.hashlines;
|
|
871
|
+
if (typeof output.file === "string" && Array.isArray(hashlines) && hashlines.some((line) => typeof record(line).line === "number" && typeof record(line).hash === "string"))
|
|
872
|
+
files.add(output.file);
|
|
873
|
+
for (const item of Object.values(output))
|
|
874
|
+
collectAnchoredFiles(item, files);
|
|
875
|
+
}
|
|
876
|
+
function renameFiles(value) {
|
|
877
|
+
const output = record(value);
|
|
878
|
+
const files = [];
|
|
879
|
+
if (typeof output.file === "string" && items(output.edits).length > 0)
|
|
880
|
+
files.push(path2.resolve(output.file));
|
|
881
|
+
for (const other of items(output.others)) {
|
|
882
|
+
const item = record(other);
|
|
883
|
+
if (typeof item.file === "string" && items(item.edits).length > 0)
|
|
884
|
+
files.push(path2.resolve(item.file));
|
|
885
|
+
}
|
|
886
|
+
return files;
|
|
887
|
+
}
|
|
888
|
+
function normalizeToLf(content) {
|
|
889
|
+
return content.replace(/\r\n/g, `
|
|
890
|
+
`).replace(/\r/g, `
|
|
891
|
+
`);
|
|
892
|
+
}
|
|
893
|
+
function diffLines(content) {
|
|
894
|
+
if (content === "")
|
|
895
|
+
return [];
|
|
896
|
+
const lines = content.split(`
|
|
897
|
+
`);
|
|
898
|
+
if (lines.at(-1) === "")
|
|
899
|
+
lines.pop();
|
|
900
|
+
return lines.map((line) => line.replace(/\r/g, "\\r"));
|
|
901
|
+
}
|
|
902
|
+
function hasFinalNewline(content) {
|
|
903
|
+
return content === "" || /[\r\n]$/.test(content);
|
|
904
|
+
}
|
|
905
|
+
function diffRange(start, count) {
|
|
906
|
+
return count === 0 ? `${start},0` : `${start + 1},${count}`;
|
|
907
|
+
}
|
|
908
|
+
function utf8Prefix(value, maxBytes) {
|
|
909
|
+
const bytes = Buffer.from(value, "utf8");
|
|
910
|
+
if (bytes.length <= maxBytes)
|
|
911
|
+
return value;
|
|
912
|
+
let end = maxBytes;
|
|
913
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
914
|
+
while (end > 0) {
|
|
915
|
+
try {
|
|
916
|
+
return decoder.decode(bytes.subarray(0, end));
|
|
917
|
+
} catch {
|
|
918
|
+
end--;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return "";
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// src/client.ts
|
|
925
|
+
var resolveFromHost = createRequire2(import.meta.url).resolve;
|
|
784
926
|
function resolveImagePolicy(options) {
|
|
785
927
|
const value = options?.imageMode;
|
|
786
928
|
if (value === undefined)
|
|
@@ -799,6 +941,119 @@ function requireDigestFacet(envelope, facet) {
|
|
|
799
941
|
}
|
|
800
942
|
return value;
|
|
801
943
|
}
|
|
944
|
+
function withAt(args, line, column) {
|
|
945
|
+
args.push("--at", buildLineLocation(line, column));
|
|
946
|
+
}
|
|
947
|
+
function readRangeArgs(filePath, start, end) {
|
|
948
|
+
const extra = [];
|
|
949
|
+
if (start > 1)
|
|
950
|
+
withAt(extra, start);
|
|
951
|
+
extra.push("--end", String(end));
|
|
952
|
+
return digestArgs(filePath, "content", extra);
|
|
953
|
+
}
|
|
954
|
+
function readSeekBinaryPath() {
|
|
955
|
+
return resolveReadSeekBinaryFromPackages(resolveFromHost);
|
|
956
|
+
}
|
|
957
|
+
async function preserveAbortReason(signal, cancelable, invoke) {
|
|
958
|
+
try {
|
|
959
|
+
const value = await invoke();
|
|
960
|
+
if (cancelable)
|
|
961
|
+
signal.throwIfAborted();
|
|
962
|
+
return value;
|
|
963
|
+
} catch (error) {
|
|
964
|
+
if (cancelable)
|
|
965
|
+
signal.throwIfAborted();
|
|
966
|
+
throw error;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
async function runReadSeekRaw(context, args, options = {}) {
|
|
970
|
+
context.abort.throwIfAborted();
|
|
971
|
+
return preserveAbortReason(context.abort, options.cancelable !== false, () => runReadSeekBunText(readSeekBinaryPath(), args, {
|
|
972
|
+
cwd: context.directory,
|
|
973
|
+
signal: context.abort,
|
|
974
|
+
cancelable: options.cancelable,
|
|
975
|
+
stdin: options.stdin,
|
|
976
|
+
timeoutMs: options.timeoutMs
|
|
977
|
+
}));
|
|
978
|
+
}
|
|
979
|
+
async function runReadSeek(context, args, options = {}) {
|
|
980
|
+
context.abort.throwIfAborted();
|
|
981
|
+
return preserveAbortReason(context.abort, options.cancelable !== false, () => runReadSeekBunJson(readSeekBinaryPath(), args, {
|
|
982
|
+
cwd: context.directory,
|
|
983
|
+
signal: context.abort,
|
|
984
|
+
cancelable: options.cancelable,
|
|
985
|
+
stdin: options.stdin,
|
|
986
|
+
timeoutMs: options.timeoutMs
|
|
987
|
+
}));
|
|
988
|
+
}
|
|
989
|
+
async function runDigestContent(context, args, options = {}) {
|
|
990
|
+
return requireDigestFacet(await runReadSeek(context, args, options), "content");
|
|
991
|
+
}
|
|
992
|
+
async function postMutationPreview(context, args, filePath, changed) {
|
|
993
|
+
try {
|
|
994
|
+
return await runDigestContent(context, args, {
|
|
995
|
+
cancelable: false,
|
|
996
|
+
timeoutMs: DEFAULT_READSEEK_TIMEOUT_MS
|
|
997
|
+
});
|
|
998
|
+
} catch (error) {
|
|
999
|
+
const previewError = error instanceof Error ? error.message : String(error);
|
|
1000
|
+
return {
|
|
1001
|
+
file: filePath,
|
|
1002
|
+
changed,
|
|
1003
|
+
preview_error: previewError,
|
|
1004
|
+
warning: "File mutation completed, but the post-write preview failed. Do not retry the mutation; read the file to verify content and refresh anchors."
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
var readSeekCacheInit;
|
|
1009
|
+
function readSeekCacheDir() {
|
|
1010
|
+
const xdgCacheHome = process.env.XDG_CACHE_HOME?.trim();
|
|
1011
|
+
const cacheHome = xdgCacheHome || path3.join(homedir(), ".cache");
|
|
1012
|
+
return path3.join(cacheHome, "opencode", "readseek");
|
|
1013
|
+
}
|
|
1014
|
+
async function ensureReadSeekCache(context) {
|
|
1015
|
+
const cacheDir = readSeekCacheDir();
|
|
1016
|
+
try {
|
|
1017
|
+
const info = await stat(cacheDir);
|
|
1018
|
+
if (!info.isDirectory())
|
|
1019
|
+
throw new Error(`readseek cache is not a directory: ${cacheDir}`);
|
|
1020
|
+
return cacheDir;
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
if (error.code !== "ENOENT")
|
|
1023
|
+
throw error;
|
|
1024
|
+
}
|
|
1025
|
+
readSeekCacheInit ??= runReadSeekRaw(context, ["--readseek-dir", cacheDir, "init"]).then(() => cacheDir).finally(() => {
|
|
1026
|
+
readSeekCacheInit = undefined;
|
|
1027
|
+
});
|
|
1028
|
+
return readSeekCacheInit;
|
|
1029
|
+
}
|
|
1030
|
+
function boundDocumentOutput(value) {
|
|
1031
|
+
const lines = value.split(`
|
|
1032
|
+
`);
|
|
1033
|
+
let output = lines.length > MAX_DOCUMENT_OUTPUT_LINES ? lines.slice(0, MAX_DOCUMENT_OUTPUT_LINES).join(`
|
|
1034
|
+
`) : value;
|
|
1035
|
+
const truncated = output !== value || Buffer.byteLength(output, "utf8") > MAX_DOCUMENT_OUTPUT_BYTES;
|
|
1036
|
+
output = utf8Prefix(output, MAX_DOCUMENT_OUTPUT_BYTES);
|
|
1037
|
+
return truncated ? `${output}
|
|
1038
|
+
[… document view truncated; narrow it with page, node, kind, or depth]` : output;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// src/policy.ts
|
|
1042
|
+
var OPENCODE_TOOL_ALIASES = Object.fromEntries(TOOL_NAMES.map((name) => [name, `readseek_${name}`]));
|
|
1043
|
+
var TOOL_POLICY = renderToolRoutingPolicy({
|
|
1044
|
+
aliases: OPENCODE_TOOL_ALIASES,
|
|
1045
|
+
preferOverBuiltIns: true,
|
|
1046
|
+
demoteBuiltIns: ["read", "edit", "write", "grep", "apply_patch"]
|
|
1047
|
+
});
|
|
1048
|
+
var PREFERRED_TOOL_DESCRIPTIONS = preferredToolDescriptionMap(OPENCODE_TOOL_ALIASES);
|
|
1049
|
+
var FALLBACK_TOOL_DESCRIPTIONS = fallbackToolDescriptionMap(["edit", "write", "grep", "apply_patch"], OPENCODE_TOOL_ALIASES);
|
|
1050
|
+
function openCodeToolDescription(name) {
|
|
1051
|
+
return renderToolDescription(name, OPENCODE_TOOL_ALIASES);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// src/session-anchors.ts
|
|
1055
|
+
import path4 from "node:path";
|
|
1056
|
+
|
|
802
1057
|
class SessionAnchors {
|
|
803
1058
|
#pathsBySession = new Map;
|
|
804
1059
|
#renamePlans = new Map;
|
|
@@ -811,7 +1066,7 @@ class SessionAnchors {
|
|
|
811
1066
|
paths.add(filePath);
|
|
812
1067
|
}
|
|
813
1068
|
forget(filePath) {
|
|
814
|
-
const absolutePath =
|
|
1069
|
+
const absolutePath = path4.resolve(filePath);
|
|
815
1070
|
for (const paths of this.#pathsBySession.values())
|
|
816
1071
|
paths.delete(absolutePath);
|
|
817
1072
|
for (const [sessionID, plan] of this.#renamePlans) {
|
|
@@ -826,22 +1081,22 @@ class SessionAnchors {
|
|
|
826
1081
|
planRename(sessionID, output) {
|
|
827
1082
|
if (!output || typeof output !== "object")
|
|
828
1083
|
return;
|
|
829
|
-
const
|
|
830
|
-
if (
|
|
1084
|
+
const record2 = output;
|
|
1085
|
+
if (record2.applied === true) {
|
|
831
1086
|
this.#renamePlans.delete(sessionID);
|
|
832
1087
|
return;
|
|
833
1088
|
}
|
|
834
|
-
const { file, old_name: oldName, new_name: newName, others } =
|
|
1089
|
+
const { file, old_name: oldName, new_name: newName, others } = record2;
|
|
835
1090
|
if (typeof file !== "string" || typeof oldName !== "string" || typeof newName !== "string")
|
|
836
1091
|
return;
|
|
837
|
-
const files = new Set([
|
|
1092
|
+
const files = new Set([path4.resolve(file)]);
|
|
838
1093
|
if (Array.isArray(others)) {
|
|
839
1094
|
for (const item of others) {
|
|
840
1095
|
if (!item || typeof item !== "object")
|
|
841
1096
|
continue;
|
|
842
1097
|
const otherFile = item.file;
|
|
843
1098
|
if (typeof otherFile === "string")
|
|
844
|
-
files.add(
|
|
1099
|
+
files.add(path4.resolve(otherFile));
|
|
845
1100
|
}
|
|
846
1101
|
}
|
|
847
1102
|
this.#renamePlans.set(sessionID, { summary: `${oldName} -> ${newName}`, files });
|
|
@@ -864,253 +1119,41 @@ ${[...paths].sort().map((filePath) => `- ${filePath}`).join(`
|
|
|
864
1119
|
`);
|
|
865
1120
|
}
|
|
866
1121
|
}
|
|
867
|
-
|
|
868
|
-
|
|
1122
|
+
|
|
1123
|
+
// src/tools.ts
|
|
1124
|
+
import { readFile as readFile2, stat as stat3 } from "node:fs/promises";
|
|
1125
|
+
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
1126
|
+
|
|
1127
|
+
// src/edits.ts
|
|
1128
|
+
import { lstat, mkdir, open, readFile } from "node:fs/promises";
|
|
1129
|
+
import path5 from "node:path";
|
|
1130
|
+
var fileMutationTails = new Map;
|
|
1131
|
+
async function withFileMutationQueue(filePath, operation) {
|
|
1132
|
+
const key = path5.resolve(filePath);
|
|
1133
|
+
const previous = fileMutationTails.get(key);
|
|
1134
|
+
let release = () => {};
|
|
1135
|
+
const current = new Promise((resolve) => {
|
|
1136
|
+
release = resolve;
|
|
1137
|
+
});
|
|
1138
|
+
fileMutationTails.set(key, current);
|
|
1139
|
+
if (previous)
|
|
1140
|
+
await previous;
|
|
1141
|
+
try {
|
|
1142
|
+
return await operation();
|
|
1143
|
+
} finally {
|
|
1144
|
+
release();
|
|
1145
|
+
if (fileMutationTails.get(key) === current)
|
|
1146
|
+
fileMutationTails.delete(key);
|
|
1147
|
+
}
|
|
869
1148
|
}
|
|
870
|
-
function
|
|
871
|
-
const
|
|
872
|
-
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
const parsed = path2.parse(unresolved);
|
|
879
|
-
const parts = unresolved.slice(parsed.root.length).split(path2.sep).filter(Boolean);
|
|
880
|
-
let resolved = parsed.root;
|
|
881
|
-
let restart = false;
|
|
882
|
-
for (let index = 0;index < parts.length; index++) {
|
|
883
|
-
const candidate = path2.join(resolved, parts[index]);
|
|
884
|
-
const info = await lstat(candidate);
|
|
885
|
-
if (!info.isSymbolicLink()) {
|
|
886
|
-
resolved = candidate;
|
|
887
|
-
continue;
|
|
888
|
-
}
|
|
889
|
-
symlinks++;
|
|
890
|
-
if (symlinks > 40) {
|
|
891
|
-
const error = new Error(`too many symbolic links: ${filePath}`);
|
|
892
|
-
error.code = "ELOOP";
|
|
893
|
-
throw error;
|
|
894
|
-
}
|
|
895
|
-
const target = await readlink(candidate);
|
|
896
|
-
unresolved = path2.resolve(path2.dirname(candidate), target, ...parts.slice(index + 1));
|
|
897
|
-
restart = true;
|
|
898
|
-
break;
|
|
899
|
-
}
|
|
900
|
-
if (!restart)
|
|
901
|
-
return resolved;
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
async function portableRealpath(filePath) {
|
|
905
|
-
try {
|
|
906
|
-
return await realpath(filePath);
|
|
907
|
-
} catch (error) {
|
|
908
|
-
if (error.code !== "EACCES")
|
|
909
|
-
throw error;
|
|
910
|
-
return resolveSymlinks(filePath);
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
async function canonicalPath(filePath) {
|
|
914
|
-
const missing = [];
|
|
915
|
-
let existing = filePath;
|
|
916
|
-
while (true) {
|
|
917
|
-
try {
|
|
918
|
-
return path2.join(await portableRealpath(existing), ...missing.reverse());
|
|
919
|
-
} catch (error) {
|
|
920
|
-
if (error.code !== "ENOENT")
|
|
921
|
-
throw error;
|
|
922
|
-
const parent = path2.dirname(existing);
|
|
923
|
-
if (parent === existing)
|
|
924
|
-
throw error;
|
|
925
|
-
missing.push(path2.basename(existing));
|
|
926
|
-
existing = parent;
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
async function authorizeExternal(context, filePath) {
|
|
931
|
-
const [accessPath, directory, worktree] = await Promise.all([
|
|
932
|
-
canonicalPath(filePath),
|
|
933
|
-
canonicalPath(context.directory),
|
|
934
|
-
context.worktree === "/" ? Promise.resolve("/") : canonicalPath(context.worktree)
|
|
935
|
-
]);
|
|
936
|
-
if (containsPath(directory, accessPath) || worktree !== "/" && containsPath(worktree, accessPath)) {
|
|
937
|
-
return;
|
|
938
|
-
}
|
|
939
|
-
const info = await stat(accessPath).catch(() => {
|
|
940
|
-
return;
|
|
941
|
-
});
|
|
942
|
-
const parentDir = info?.isDirectory() ? accessPath : path2.dirname(accessPath);
|
|
943
|
-
const pattern = path2.join(parentDir, "*").replaceAll("\\", "/");
|
|
944
|
-
await context.ask({
|
|
945
|
-
permission: "external_directory",
|
|
946
|
-
patterns: [pattern],
|
|
947
|
-
always: [pattern],
|
|
948
|
-
metadata: { filepath: filePath, parentDir }
|
|
949
|
-
});
|
|
950
|
-
}
|
|
951
|
-
async function rejectSymlinkMutation(filePath) {
|
|
952
|
-
const info = await lstat(filePath).catch((error) => {
|
|
953
|
-
if (error.code === "ENOENT")
|
|
954
|
-
return;
|
|
955
|
-
throw error;
|
|
956
|
-
});
|
|
957
|
-
if (info?.isSymbolicLink())
|
|
958
|
-
throw new Error(`refusing to modify symbolic link: ${filePath}`);
|
|
959
|
-
}
|
|
960
|
-
async function authorizeRead(context, filePath) {
|
|
961
|
-
await authorizeExternal(context, filePath);
|
|
962
|
-
await context.ask({
|
|
963
|
-
permission: "read",
|
|
964
|
-
patterns: [path2.relative(context.worktree, filePath).replaceAll("\\", "/")],
|
|
965
|
-
always: ["*"],
|
|
966
|
-
metadata: {}
|
|
967
|
-
});
|
|
968
|
-
}
|
|
969
|
-
async function authorizeSearch(context, filePath, pattern) {
|
|
970
|
-
await context.ask({
|
|
971
|
-
permission: "grep",
|
|
972
|
-
patterns: [pattern],
|
|
973
|
-
always: ["*"],
|
|
974
|
-
metadata: { pattern, path: filePath }
|
|
975
|
-
});
|
|
976
|
-
await authorizeExternal(context, filePath);
|
|
977
|
-
}
|
|
978
|
-
async function authorizeEdit(context, filePaths, diff = "", authorizeExternalPaths = true) {
|
|
979
|
-
if (authorizeExternalPaths) {
|
|
980
|
-
for (const filePath of filePaths)
|
|
981
|
-
await authorizeExternal(context, filePath);
|
|
982
|
-
}
|
|
983
|
-
const patterns = filePaths.map((filePath) => path2.relative(context.worktree, filePath).replaceAll("\\", "/"));
|
|
984
|
-
await context.ask({
|
|
985
|
-
permission: "edit",
|
|
986
|
-
patterns,
|
|
987
|
-
always: ["*"],
|
|
988
|
-
metadata: { filepath: filePaths.join(", "), diff }
|
|
989
|
-
});
|
|
990
|
-
}
|
|
991
|
-
function withAt(args, line, column) {
|
|
992
|
-
args.push("--at", buildLineLocation(line, column));
|
|
993
|
-
}
|
|
994
|
-
function readRangeArgs(filePath, start, end) {
|
|
995
|
-
const extra = [];
|
|
996
|
-
if (start > 1)
|
|
997
|
-
withAt(extra, start);
|
|
998
|
-
extra.push("--end", String(end));
|
|
999
|
-
return digestArgs(filePath, "content", extra);
|
|
1000
|
-
}
|
|
1001
|
-
async function runDigestContent(context, args, options = {}) {
|
|
1002
|
-
return requireDigestFacet(await runReadSeek(context, args, options), "content");
|
|
1003
|
-
}
|
|
1004
|
-
function readSeekBinaryPath() {
|
|
1005
|
-
return resolveReadSeekBinaryFromPackages(resolveFromHost);
|
|
1006
|
-
}
|
|
1007
|
-
async function preserveAbortReason(signal, cancelable, invoke) {
|
|
1008
|
-
try {
|
|
1009
|
-
const value = await invoke();
|
|
1010
|
-
if (cancelable)
|
|
1011
|
-
signal.throwIfAborted();
|
|
1012
|
-
return value;
|
|
1013
|
-
} catch (error) {
|
|
1014
|
-
if (cancelable)
|
|
1015
|
-
signal.throwIfAborted();
|
|
1016
|
-
throw error;
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
async function runReadSeekRaw(context, args, options = {}) {
|
|
1020
|
-
context.abort.throwIfAborted();
|
|
1021
|
-
return preserveAbortReason(context.abort, options.cancelable !== false, () => runReadSeekBunText(readSeekBinaryPath(), args, {
|
|
1022
|
-
cwd: context.directory,
|
|
1023
|
-
signal: context.abort,
|
|
1024
|
-
cancelable: options.cancelable,
|
|
1025
|
-
stdin: options.stdin,
|
|
1026
|
-
timeoutMs: options.timeoutMs
|
|
1027
|
-
}));
|
|
1028
|
-
}
|
|
1029
|
-
async function runReadSeek(context, args, options = {}) {
|
|
1030
|
-
context.abort.throwIfAborted();
|
|
1031
|
-
return preserveAbortReason(context.abort, options.cancelable !== false, () => runReadSeekBunJson(readSeekBinaryPath(), args, {
|
|
1032
|
-
cwd: context.directory,
|
|
1033
|
-
signal: context.abort,
|
|
1034
|
-
cancelable: options.cancelable,
|
|
1035
|
-
stdin: options.stdin,
|
|
1036
|
-
timeoutMs: options.timeoutMs
|
|
1037
|
-
}));
|
|
1038
|
-
}
|
|
1039
|
-
async function postMutationPreview(context, args, filePath, changed) {
|
|
1040
|
-
try {
|
|
1041
|
-
return await runDigestContent(context, args, {
|
|
1042
|
-
cancelable: false,
|
|
1043
|
-
timeoutMs: DEFAULT_READSEEK_TIMEOUT_MS
|
|
1044
|
-
});
|
|
1045
|
-
} catch (error) {
|
|
1046
|
-
const previewError = error instanceof Error ? error.message : String(error);
|
|
1047
|
-
return {
|
|
1048
|
-
file: filePath,
|
|
1049
|
-
changed,
|
|
1050
|
-
preview_error: previewError,
|
|
1051
|
-
warning: "File mutation completed, but the post-write preview failed. Do not retry the mutation; read the file to verify content and refresh anchors."
|
|
1052
|
-
};
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
var readSeekCacheInit;
|
|
1056
|
-
function readSeekCacheDir() {
|
|
1057
|
-
const xdgCacheHome = process.env.XDG_CACHE_HOME?.trim();
|
|
1058
|
-
const cacheHome = xdgCacheHome || path2.join(homedir(), ".cache");
|
|
1059
|
-
return path2.join(cacheHome, "opencode", "readseek");
|
|
1060
|
-
}
|
|
1061
|
-
async function ensureReadSeekCache(context) {
|
|
1062
|
-
const cacheDir = readSeekCacheDir();
|
|
1063
|
-
try {
|
|
1064
|
-
const info = await stat(cacheDir);
|
|
1065
|
-
if (!info.isDirectory())
|
|
1066
|
-
throw new Error(`readseek cache is not a directory: ${cacheDir}`);
|
|
1067
|
-
return cacheDir;
|
|
1068
|
-
} catch (error) {
|
|
1069
|
-
if (error.code !== "ENOENT")
|
|
1070
|
-
throw error;
|
|
1071
|
-
}
|
|
1072
|
-
readSeekCacheInit ??= runReadSeekRaw(context, ["--readseek-dir", cacheDir, "init"]).then(() => cacheDir).finally(() => {
|
|
1073
|
-
readSeekCacheInit = undefined;
|
|
1074
|
-
});
|
|
1075
|
-
return readSeekCacheInit;
|
|
1076
|
-
}
|
|
1077
|
-
function render(value) {
|
|
1078
|
-
return JSON.stringify(value, null, 2);
|
|
1079
|
-
}
|
|
1080
|
-
function collectAnchoredFiles(value, files) {
|
|
1081
|
-
if (Array.isArray(value)) {
|
|
1082
|
-
for (const item of value)
|
|
1083
|
-
collectAnchoredFiles(item, files);
|
|
1084
|
-
return;
|
|
1085
|
-
}
|
|
1086
|
-
if (!value || typeof value !== "object")
|
|
1087
|
-
return;
|
|
1088
|
-
const output = value;
|
|
1089
|
-
const hashlines = output.hashlines;
|
|
1090
|
-
if (typeof output.file === "string" && Array.isArray(hashlines) && hashlines.some((line) => typeof record(line).line === "number" && typeof record(line).hash === "string"))
|
|
1091
|
-
files.add(output.file);
|
|
1092
|
-
for (const item of Object.values(output))
|
|
1093
|
-
collectAnchoredFiles(item, files);
|
|
1094
|
-
}
|
|
1095
|
-
var fileMutationTails = new Map;
|
|
1096
|
-
var MAX_PERMISSION_DIFF_BYTES = 32 * 1024;
|
|
1097
|
-
async function withFileMutationQueue(filePath, operation) {
|
|
1098
|
-
const key = path2.resolve(filePath);
|
|
1099
|
-
const previous = fileMutationTails.get(key);
|
|
1100
|
-
let release = () => {};
|
|
1101
|
-
const current = new Promise((resolve) => {
|
|
1102
|
-
release = resolve;
|
|
1103
|
-
});
|
|
1104
|
-
fileMutationTails.set(key, current);
|
|
1105
|
-
if (previous)
|
|
1106
|
-
await previous;
|
|
1107
|
-
try {
|
|
1108
|
-
return await operation();
|
|
1109
|
-
} finally {
|
|
1110
|
-
release();
|
|
1111
|
-
if (fileMutationTails.get(key) === current)
|
|
1112
|
-
fileMutationTails.delete(key);
|
|
1113
|
-
}
|
|
1149
|
+
function parseAnchor(anchor) {
|
|
1150
|
+
const match = /^(\d+):([0-9a-fA-F]{3})$/.exec(anchor.trim());
|
|
1151
|
+
if (!match)
|
|
1152
|
+
throw new Error(`invalid LINE:HASH anchor: ${anchor}`);
|
|
1153
|
+
const line = Number(match[1]);
|
|
1154
|
+
if (line === 0)
|
|
1155
|
+
throw new Error(`anchor line must be greater than zero: ${anchor}`);
|
|
1156
|
+
return { line, hash: match[2].toLowerCase() };
|
|
1114
1157
|
}
|
|
1115
1158
|
function validateEdits(value) {
|
|
1116
1159
|
if (!Array.isArray(value) || value.length === 0)
|
|
@@ -1153,15 +1196,6 @@ function validateEdits(value) {
|
|
|
1153
1196
|
}
|
|
1154
1197
|
}
|
|
1155
1198
|
}
|
|
1156
|
-
function parseAnchor(anchor) {
|
|
1157
|
-
const match = /^(\d+):([0-9a-fA-F]{3})$/.exec(anchor.trim());
|
|
1158
|
-
if (!match)
|
|
1159
|
-
throw new Error(`invalid LINE:HASH anchor: ${anchor}`);
|
|
1160
|
-
const line = Number(match[1]);
|
|
1161
|
-
if (line === 0)
|
|
1162
|
-
throw new Error(`anchor line must be greater than zero: ${anchor}`);
|
|
1163
|
-
return { line, hash: match[2].toLowerCase() };
|
|
1164
|
-
}
|
|
1165
1199
|
function firstChangedLine(before, after) {
|
|
1166
1200
|
const beforeLines = normalizeToLf(before).split(`
|
|
1167
1201
|
`);
|
|
@@ -1195,7 +1229,7 @@ async function writeHandle(handle, content) {
|
|
|
1195
1229
|
}
|
|
1196
1230
|
}
|
|
1197
1231
|
async function writeText(filePath, expected, content) {
|
|
1198
|
-
await mkdir(
|
|
1232
|
+
await mkdir(path5.dirname(filePath), { recursive: true });
|
|
1199
1233
|
if (!expected.exists) {
|
|
1200
1234
|
let handle2;
|
|
1201
1235
|
try {
|
|
@@ -1232,63 +1266,6 @@ async function writeText(filePath, expected, content) {
|
|
|
1232
1266
|
await handle.close();
|
|
1233
1267
|
}
|
|
1234
1268
|
}
|
|
1235
|
-
function renameFiles(value) {
|
|
1236
|
-
const output = record(value);
|
|
1237
|
-
const files = [];
|
|
1238
|
-
if (typeof output.file === "string" && items(output.edits).length > 0)
|
|
1239
|
-
files.push(path2.resolve(output.file));
|
|
1240
|
-
for (const other of items(output.others)) {
|
|
1241
|
-
const item = record(other);
|
|
1242
|
-
if (typeof item.file === "string" && items(item.edits).length > 0)
|
|
1243
|
-
files.push(path2.resolve(item.file));
|
|
1244
|
-
}
|
|
1245
|
-
return files;
|
|
1246
|
-
}
|
|
1247
|
-
function normalizeToLf(content) {
|
|
1248
|
-
return content.replace(/\r\n/g, `
|
|
1249
|
-
`).replace(/\r/g, `
|
|
1250
|
-
`);
|
|
1251
|
-
}
|
|
1252
|
-
function diffLines(content) {
|
|
1253
|
-
if (content === "")
|
|
1254
|
-
return [];
|
|
1255
|
-
const lines = content.split(`
|
|
1256
|
-
`);
|
|
1257
|
-
if (lines.at(-1) === "")
|
|
1258
|
-
lines.pop();
|
|
1259
|
-
return lines.map((line) => line.replace(/\r/g, "\\r"));
|
|
1260
|
-
}
|
|
1261
|
-
function hasFinalNewline(content) {
|
|
1262
|
-
return content === "" || /[\r\n]$/.test(content);
|
|
1263
|
-
}
|
|
1264
|
-
function diffRange(start, count) {
|
|
1265
|
-
return count === 0 ? `${start},0` : `${start + 1},${count}`;
|
|
1266
|
-
}
|
|
1267
|
-
function utf8Prefix(value, maxBytes) {
|
|
1268
|
-
const bytes = Buffer.from(value, "utf8");
|
|
1269
|
-
if (bytes.length <= maxBytes)
|
|
1270
|
-
return value;
|
|
1271
|
-
let end = maxBytes;
|
|
1272
|
-
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
1273
|
-
while (end > 0) {
|
|
1274
|
-
try {
|
|
1275
|
-
return decoder.decode(bytes.subarray(0, end));
|
|
1276
|
-
} catch {
|
|
1277
|
-
end--;
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
return "";
|
|
1281
|
-
}
|
|
1282
|
-
function boundDocumentOutput(value) {
|
|
1283
|
-
const lines = value.split(`
|
|
1284
|
-
`);
|
|
1285
|
-
let output = lines.length > MAX_DOCUMENT_OUTPUT_LINES ? lines.slice(0, MAX_DOCUMENT_OUTPUT_LINES).join(`
|
|
1286
|
-
`) : value;
|
|
1287
|
-
const truncated = output !== value || Buffer.byteLength(output, "utf8") > MAX_DOCUMENT_OUTPUT_BYTES;
|
|
1288
|
-
output = utf8Prefix(output, MAX_DOCUMENT_OUTPUT_BYTES);
|
|
1289
|
-
return truncated ? `${output}
|
|
1290
|
-
[… document view truncated; narrow it with page, node, kind, or depth]` : output;
|
|
1291
|
-
}
|
|
1292
1269
|
function previewDiffLine(prefix, line) {
|
|
1293
1270
|
const value = `${prefix}${line}`;
|
|
1294
1271
|
const maxBytes = 256;
|
|
@@ -1381,21 +1358,137 @@ function simpleDiff(filePath, before, after) {
|
|
|
1381
1358
|
].join(`
|
|
1382
1359
|
`);
|
|
1383
1360
|
}
|
|
1384
|
-
|
|
1385
|
-
|
|
1361
|
+
|
|
1362
|
+
// src/paths.ts
|
|
1363
|
+
import { lstat as lstat2, readlink, realpath, stat as stat2 } from "node:fs/promises";
|
|
1364
|
+
import path6 from "node:path";
|
|
1365
|
+
function resolvePath(directory, filePath) {
|
|
1366
|
+
return path6.resolve(directory, filePath);
|
|
1367
|
+
}
|
|
1368
|
+
function containsPath(directory, filePath) {
|
|
1369
|
+
const relative = path6.relative(directory, filePath);
|
|
1370
|
+
return relative === "" || !relative.startsWith(`..${path6.sep}`) && relative !== ".." && !path6.isAbsolute(relative);
|
|
1371
|
+
}
|
|
1372
|
+
async function resolveSymlinks(filePath) {
|
|
1373
|
+
let unresolved = path6.resolve(filePath);
|
|
1374
|
+
let symlinks = 0;
|
|
1375
|
+
while (true) {
|
|
1376
|
+
const parsed = path6.parse(unresolved);
|
|
1377
|
+
const parts = unresolved.slice(parsed.root.length).split(path6.sep).filter(Boolean);
|
|
1378
|
+
let resolved = parsed.root;
|
|
1379
|
+
let restart = false;
|
|
1380
|
+
for (let index = 0;index < parts.length; index++) {
|
|
1381
|
+
const candidate = path6.join(resolved, parts[index]);
|
|
1382
|
+
const info = await lstat2(candidate);
|
|
1383
|
+
if (!info.isSymbolicLink()) {
|
|
1384
|
+
resolved = candidate;
|
|
1385
|
+
continue;
|
|
1386
|
+
}
|
|
1387
|
+
symlinks++;
|
|
1388
|
+
if (symlinks > 40) {
|
|
1389
|
+
const error = new Error(`too many symbolic links: ${filePath}`);
|
|
1390
|
+
error.code = "ELOOP";
|
|
1391
|
+
throw error;
|
|
1392
|
+
}
|
|
1393
|
+
const target = await readlink(candidate);
|
|
1394
|
+
unresolved = path6.resolve(path6.dirname(candidate), target, ...parts.slice(index + 1));
|
|
1395
|
+
restart = true;
|
|
1396
|
+
break;
|
|
1397
|
+
}
|
|
1398
|
+
if (!restart)
|
|
1399
|
+
return resolved;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
async function portableRealpath(filePath) {
|
|
1403
|
+
try {
|
|
1404
|
+
return await realpath(filePath);
|
|
1405
|
+
} catch (error) {
|
|
1406
|
+
if (error.code !== "EACCES")
|
|
1407
|
+
throw error;
|
|
1408
|
+
return resolveSymlinks(filePath);
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
async function canonicalPath(filePath) {
|
|
1412
|
+
const missing = [];
|
|
1413
|
+
let existing = filePath;
|
|
1414
|
+
while (true) {
|
|
1415
|
+
try {
|
|
1416
|
+
return path6.join(await portableRealpath(existing), ...missing.reverse());
|
|
1417
|
+
} catch (error) {
|
|
1418
|
+
if (error.code !== "ENOENT")
|
|
1419
|
+
throw error;
|
|
1420
|
+
const parent = path6.dirname(existing);
|
|
1421
|
+
if (parent === existing)
|
|
1422
|
+
throw error;
|
|
1423
|
+
missing.push(path6.basename(existing));
|
|
1424
|
+
existing = parent;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
async function authorizeExternal(context, filePath) {
|
|
1429
|
+
const [accessPath, directory, worktree] = await Promise.all([
|
|
1430
|
+
canonicalPath(filePath),
|
|
1431
|
+
canonicalPath(context.directory),
|
|
1432
|
+
context.worktree === "/" ? Promise.resolve("/") : canonicalPath(context.worktree)
|
|
1433
|
+
]);
|
|
1434
|
+
if (containsPath(directory, accessPath) || worktree !== "/" && containsPath(worktree, accessPath)) {
|
|
1386
1435
|
return;
|
|
1387
|
-
|
|
1388
|
-
|
|
1436
|
+
}
|
|
1437
|
+
const info = await stat2(accessPath).catch(() => {
|
|
1389
1438
|
return;
|
|
1390
|
-
|
|
1391
|
-
|
|
1439
|
+
});
|
|
1440
|
+
const parentDir = info?.isDirectory() ? accessPath : path6.dirname(accessPath);
|
|
1441
|
+
const pattern = path6.join(parentDir, "*").replaceAll("\\", "/");
|
|
1442
|
+
await context.ask({
|
|
1443
|
+
permission: "external_directory",
|
|
1444
|
+
patterns: [pattern],
|
|
1445
|
+
always: [pattern],
|
|
1446
|
+
metadata: { filepath: filePath, parentDir }
|
|
1447
|
+
});
|
|
1392
1448
|
}
|
|
1393
|
-
function
|
|
1394
|
-
|
|
1449
|
+
async function rejectSymlinkMutation(filePath) {
|
|
1450
|
+
const info = await lstat2(filePath).catch((error) => {
|
|
1451
|
+
if (error.code === "ENOENT")
|
|
1452
|
+
return;
|
|
1453
|
+
throw error;
|
|
1454
|
+
});
|
|
1455
|
+
if (info?.isSymbolicLink())
|
|
1456
|
+
throw new Error(`refusing to modify symbolic link: ${filePath}`);
|
|
1395
1457
|
}
|
|
1396
|
-
function
|
|
1397
|
-
|
|
1458
|
+
async function authorizeRead(context, filePath) {
|
|
1459
|
+
await authorizeExternal(context, filePath);
|
|
1460
|
+
await context.ask({
|
|
1461
|
+
permission: "read",
|
|
1462
|
+
patterns: [path6.relative(context.worktree, filePath).replaceAll("\\", "/")],
|
|
1463
|
+
always: ["*"],
|
|
1464
|
+
metadata: {}
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
async function authorizeSearch(context, filePath, pattern) {
|
|
1468
|
+
await context.ask({
|
|
1469
|
+
permission: "grep",
|
|
1470
|
+
patterns: [pattern],
|
|
1471
|
+
always: ["*"],
|
|
1472
|
+
metadata: { pattern, path: filePath }
|
|
1473
|
+
});
|
|
1474
|
+
await authorizeExternal(context, filePath);
|
|
1475
|
+
}
|
|
1476
|
+
async function authorizeEdit(context, filePaths, diff = "", authorizeExternalPaths = true) {
|
|
1477
|
+
if (authorizeExternalPaths) {
|
|
1478
|
+
for (const filePath of filePaths)
|
|
1479
|
+
await authorizeExternal(context, filePath);
|
|
1480
|
+
}
|
|
1481
|
+
const patterns = filePaths.map((filePath) => path6.relative(context.worktree, filePath).replaceAll("\\", "/"));
|
|
1482
|
+
await context.ask({
|
|
1483
|
+
permission: "edit",
|
|
1484
|
+
patterns,
|
|
1485
|
+
always: ["*"],
|
|
1486
|
+
metadata: { filepath: filePaths.join(", "), diff }
|
|
1487
|
+
});
|
|
1398
1488
|
}
|
|
1489
|
+
|
|
1490
|
+
// src/presentation.ts
|
|
1491
|
+
import { tool } from "@opencode-ai/plugin";
|
|
1399
1492
|
function initialTitle(kind, args) {
|
|
1400
1493
|
switch (kind) {
|
|
1401
1494
|
case "read":
|
|
@@ -1531,384 +1624,390 @@ function readseekTool(description, args, kind, execute, formatResult = (result)
|
|
|
1531
1624
|
}
|
|
1532
1625
|
});
|
|
1533
1626
|
}
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
const imageModes = imagePolicy === "auto" ? ["none", "all", "ocr", "caption", "objects"] : ["all", "ocr", "caption", "objects"];
|
|
1627
|
+
|
|
1628
|
+
// src/tools.ts
|
|
1629
|
+
function createReadSeekTools(imagePolicy) {
|
|
1538
1630
|
return {
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1631
|
+
readseek_view: readseekTool(openCodeToolDescription("view"), {
|
|
1632
|
+
path: tool2.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
1633
|
+
node: tool2.schema.string().min(1).optional().describe(PARAM_DESCRIPTIONS.node),
|
|
1634
|
+
page: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.page),
|
|
1635
|
+
kind: tool2.schema.enum(DOCUMENT_NODE_KIND_VALUES).optional().describe(PARAM_DESCRIPTIONS.kind),
|
|
1636
|
+
depth: tool2.schema.number().int().nonnegative().optional().describe(PARAM_DESCRIPTIONS.depth),
|
|
1637
|
+
outline: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.outline),
|
|
1638
|
+
visionMode: tool2.schema.enum(VISION_ANALYSIS_MODE_VALUES).optional().describe(PARAM_DESCRIPTIONS.visionModeView),
|
|
1639
|
+
visionLevel: tool2.schema.enum(VISION_LEVEL_VALUES).optional().describe(PARAM_DESCRIPTIONS.visionLevel)
|
|
1640
|
+
}, "view", async (input, context) => {
|
|
1641
|
+
const filePath = resolvePath(context.directory, input.path);
|
|
1642
|
+
await authorizeRead(context, filePath);
|
|
1643
|
+
const visionMode = input.visionMode;
|
|
1644
|
+
const visionLevel = input.visionLevel;
|
|
1645
|
+
const vision = validateVisionLevelVsMode({
|
|
1646
|
+
visionMode,
|
|
1647
|
+
visionLevel,
|
|
1648
|
+
outline: input.outline
|
|
1649
|
+
});
|
|
1650
|
+
if (!vision.ok)
|
|
1651
|
+
throw new Error(vision.error);
|
|
1652
|
+
const cacheDir = await ensureReadSeekCache(context);
|
|
1653
|
+
const args = buildViewArgs({
|
|
1654
|
+
path: filePath,
|
|
1655
|
+
node: input.node,
|
|
1656
|
+
page: input.page,
|
|
1657
|
+
kind: input.kind,
|
|
1658
|
+
depth: input.depth,
|
|
1659
|
+
outline: input.outline,
|
|
1660
|
+
visionMode,
|
|
1661
|
+
visionLevel,
|
|
1662
|
+
readseekDir: cacheDir
|
|
1663
|
+
});
|
|
1664
|
+
return runReadSeekRaw(context, args, visionMode === undefined ? {} : { timeoutMs: DEFAULT_VISION_TIMEOUT_MS });
|
|
1665
|
+
}, (result) => ({ output: boundDocumentOutput(String(result)) })),
|
|
1666
|
+
readseek_digest: readseekTool(openCodeToolDescription("digest"), {
|
|
1667
|
+
path: tool2.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
1668
|
+
select: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.selectComma),
|
|
1669
|
+
at: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.at),
|
|
1670
|
+
end: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.end),
|
|
1671
|
+
limit: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.limit),
|
|
1672
|
+
language: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.languageOverride),
|
|
1673
|
+
visionMode: tool2.schema.enum(IMAGE_MODES).optional().describe(PARAM_DESCRIPTIONS.visionModeDigest),
|
|
1674
|
+
visionLevel: tool2.schema.enum(VISION_LEVEL_VALUES).optional().describe(PARAM_DESCRIPTIONS.visionLevel)
|
|
1675
|
+
}, "digest", async (input, context) => {
|
|
1676
|
+
const filePath = resolvePath(context.directory, input.path);
|
|
1677
|
+
await authorizeRead(context, filePath);
|
|
1678
|
+
const select = (input.select ?? "content").trim() || "content";
|
|
1679
|
+
const visionMode = input.visionMode;
|
|
1680
|
+
const visionLevel = input.visionLevel;
|
|
1681
|
+
const vision = validateVisionLevelVsMode({ visionMode, visionLevel });
|
|
1682
|
+
if (!vision.ok)
|
|
1683
|
+
throw new Error(vision.error);
|
|
1684
|
+
const cacheDir = await ensureReadSeekCache(context);
|
|
1685
|
+
const args = buildDigestArgs({
|
|
1686
|
+
path: filePath,
|
|
1687
|
+
select,
|
|
1688
|
+
at: input.at,
|
|
1689
|
+
end: input.end,
|
|
1690
|
+
limit: input.limit,
|
|
1691
|
+
language: input.language,
|
|
1692
|
+
visionMode,
|
|
1693
|
+
visionLevel,
|
|
1694
|
+
readseekDir: cacheDir
|
|
1695
|
+
});
|
|
1696
|
+
return runReadSeek(context, args, visionMode === undefined || visionMode === "none" ? {} : { timeoutMs: DEFAULT_VISION_TIMEOUT_MS });
|
|
1697
|
+
}),
|
|
1698
|
+
readseek_grep: readseekTool(openCodeToolDescription("grep"), {
|
|
1699
|
+
pattern: tool2.schema.string().describe(PARAM_DESCRIPTIONS.grepPattern),
|
|
1700
|
+
path: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
1701
|
+
glob: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.glob),
|
|
1702
|
+
literal: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.literal),
|
|
1703
|
+
ignoreCase: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.ignoreCase),
|
|
1704
|
+
context: tool2.schema.number().int().nonnegative().optional().describe(PARAM_DESCRIPTIONS.context),
|
|
1705
|
+
limit: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.matchLimit)
|
|
1706
|
+
}, "grep", async (input, context) => {
|
|
1707
|
+
const target = resolvePath(context.directory, input.path ?? ".");
|
|
1708
|
+
const pattern = input.pattern;
|
|
1709
|
+
await authorizeSearch(context, target, pattern);
|
|
1710
|
+
let expression;
|
|
1711
|
+
try {
|
|
1712
|
+
const source = input.literal ? pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") : pattern;
|
|
1713
|
+
expression = new RegExp(source, input.ignoreCase ? "i" : "");
|
|
1714
|
+
} catch (error) {
|
|
1715
|
+
throw new Error(`invalid regular expression: ${error instanceof Error ? error.message : String(error)}`);
|
|
1716
|
+
}
|
|
1717
|
+
const info = await stat3(target);
|
|
1718
|
+
const files = [];
|
|
1719
|
+
if (info.isFile())
|
|
1720
|
+
files.push(target);
|
|
1721
|
+
else if (info.isDirectory()) {
|
|
1722
|
+
const glob = new Bun.Glob(input.glob ?? "**/*");
|
|
1723
|
+
for await (const filePath of glob.scan({ cwd: target, absolute: true, onlyFiles: true, followSymlinks: false })) {
|
|
1724
|
+
files.push(filePath);
|
|
1625
1725
|
}
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
const
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
let truncated = false;
|
|
1642
|
-
for (const filePath of files.sort()) {
|
|
1643
|
-
context.abort.throwIfAborted();
|
|
1644
|
-
if (truncated)
|
|
1645
|
-
break;
|
|
1646
|
-
const buffer = await readFile(filePath);
|
|
1647
|
-
if (buffer.includes(0))
|
|
1648
|
-
continue;
|
|
1649
|
-
const lines = buffer.toString("utf8").replace(/\r\n/g, `
|
|
1726
|
+
} else
|
|
1727
|
+
throw new Error(`grep target is not a file or directory: ${target}`);
|
|
1728
|
+
const maxMatches = input.limit ?? 100;
|
|
1729
|
+
const contextLines = input.context ?? 0;
|
|
1730
|
+
const results = [];
|
|
1731
|
+
let totalMatches = 0;
|
|
1732
|
+
let truncated = false;
|
|
1733
|
+
for (const filePath of files.sort()) {
|
|
1734
|
+
context.abort.throwIfAborted();
|
|
1735
|
+
if (truncated)
|
|
1736
|
+
break;
|
|
1737
|
+
const buffer = await readFile2(filePath);
|
|
1738
|
+
if (buffer.includes(0))
|
|
1739
|
+
continue;
|
|
1740
|
+
const lines = buffer.toString("utf8").replace(/\r\n/g, `
|
|
1650
1741
|
`).split(`
|
|
1651
1742
|
`);
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
continue;
|
|
1658
|
-
if (totalMatches === maxMatches) {
|
|
1659
|
-
truncated = true;
|
|
1660
|
-
break;
|
|
1661
|
-
}
|
|
1662
|
-
const line = index + 1;
|
|
1663
|
-
matchLines.add(line);
|
|
1664
|
-
ranges.push([Math.max(1, line - contextLines), Math.min(lines.length, line + contextLines)]);
|
|
1665
|
-
totalMatches++;
|
|
1666
|
-
}
|
|
1667
|
-
if (ranges.length === 0)
|
|
1743
|
+
const matchLines = new Set;
|
|
1744
|
+
const ranges = [];
|
|
1745
|
+
for (let index = 0;index < lines.length; index++) {
|
|
1746
|
+
expression.lastIndex = 0;
|
|
1747
|
+
if (!expression.test(lines[index] ?? ""))
|
|
1668
1748
|
continue;
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
if (previous && start <= previous[1] + 1)
|
|
1673
|
-
previous[1] = Math.max(previous[1], end);
|
|
1674
|
-
else
|
|
1675
|
-
mergedRanges.push([start, end]);
|
|
1676
|
-
}
|
|
1677
|
-
const linesByNumber = new Map;
|
|
1678
|
-
for (const [start, end] of mergedRanges) {
|
|
1679
|
-
const output = record(await runDigestContent(context, readRangeArgs(filePath, start, end)));
|
|
1680
|
-
for (const hashline of items(output.hashlines)) {
|
|
1681
|
-
const line = record(hashline).line;
|
|
1682
|
-
if (typeof line === "number" && !linesByNumber.has(line))
|
|
1683
|
-
linesByNumber.set(line, hashline);
|
|
1684
|
-
}
|
|
1749
|
+
if (totalMatches === maxMatches) {
|
|
1750
|
+
truncated = true;
|
|
1751
|
+
break;
|
|
1685
1752
|
}
|
|
1686
|
-
const
|
|
1687
|
-
|
|
1753
|
+
const line = index + 1;
|
|
1754
|
+
matchLines.add(line);
|
|
1755
|
+
ranges.push([Math.max(1, line - contextLines), Math.min(lines.length, line + contextLines)]);
|
|
1756
|
+
totalMatches++;
|
|
1757
|
+
}
|
|
1758
|
+
if (ranges.length === 0)
|
|
1759
|
+
continue;
|
|
1760
|
+
const mergedRanges = [];
|
|
1761
|
+
for (const [start, end] of ranges) {
|
|
1762
|
+
const previous = mergedRanges.at(-1);
|
|
1763
|
+
if (previous && start <= previous[1] + 1)
|
|
1764
|
+
previous[1] = Math.max(previous[1], end);
|
|
1765
|
+
else
|
|
1766
|
+
mergedRanges.push([start, end]);
|
|
1767
|
+
}
|
|
1768
|
+
const linesByNumber = new Map;
|
|
1769
|
+
for (const [start, end] of mergedRanges) {
|
|
1770
|
+
const output = record(await runDigestContent(context, readRangeArgs(filePath, start, end)));
|
|
1771
|
+
for (const hashline of items(output.hashlines)) {
|
|
1688
1772
|
const line = record(hashline).line;
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1773
|
+
if (typeof line === "number" && !linesByNumber.has(line))
|
|
1774
|
+
linesByNumber.set(line, hashline);
|
|
1775
|
+
}
|
|
1692
1776
|
}
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
path: tool.schema.string().optional().describe("File or directory to search; defaults to the project directory"),
|
|
1698
|
-
language: tool.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
1699
|
-
cached: tool.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.cached),
|
|
1700
|
-
others: tool.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.others),
|
|
1701
|
-
ignored: tool.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.ignored)
|
|
1702
|
-
}, "search", async (input, context) => {
|
|
1703
|
-
const target = resolvePath(context.directory, input.path ?? ".");
|
|
1704
|
-
const args = buildSearchArgs({
|
|
1705
|
-
path: target,
|
|
1706
|
-
pattern: input.pattern,
|
|
1707
|
-
language: input.language,
|
|
1708
|
-
cached: input.cached,
|
|
1709
|
-
others: input.others,
|
|
1710
|
-
ignored: input.ignored
|
|
1711
|
-
});
|
|
1712
|
-
await authorizeSearch(context, target, input.pattern);
|
|
1713
|
-
return runReadSeek(context, args);
|
|
1714
|
-
}),
|
|
1715
|
-
readseek_def: readseekTool(openCodeToolDescription("def"), {
|
|
1716
|
-
name: tool.schema.string().describe(PARAM_DESCRIPTIONS.name),
|
|
1717
|
-
path: tool.schema.string().optional().describe("File or directory to search; defaults to the project directory"),
|
|
1718
|
-
language: tool.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
1719
|
-
cached: tool.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.cached),
|
|
1720
|
-
others: tool.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.others),
|
|
1721
|
-
ignored: tool.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.ignored)
|
|
1722
|
-
}, "def", async (input, context) => {
|
|
1723
|
-
const target = resolvePath(context.directory, input.path ?? ".");
|
|
1724
|
-
const args = buildDefArgs({
|
|
1725
|
-
path: target,
|
|
1726
|
-
name: input.name,
|
|
1727
|
-
language: input.language,
|
|
1728
|
-
format: "plain",
|
|
1729
|
-
cached: input.cached,
|
|
1730
|
-
others: input.others,
|
|
1731
|
-
ignored: input.ignored
|
|
1777
|
+
const hashlines = [...linesByNumber.values()];
|
|
1778
|
+
const matches = hashlines.filter((hashline) => {
|
|
1779
|
+
const line = record(hashline).line;
|
|
1780
|
+
return typeof line === "number" && matchLines.has(line);
|
|
1732
1781
|
});
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
}
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1782
|
+
results.push({ file: filePath, matches, hashlines });
|
|
1783
|
+
}
|
|
1784
|
+
return { results, truncated };
|
|
1785
|
+
}),
|
|
1786
|
+
readseek_search: readseekTool(openCodeToolDescription("search"), {
|
|
1787
|
+
pattern: tool2.schema.string().describe(PARAM_DESCRIPTIONS.pattern),
|
|
1788
|
+
path: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
1789
|
+
language: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
1790
|
+
cached: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.cached),
|
|
1791
|
+
others: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.others),
|
|
1792
|
+
ignored: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.ignored)
|
|
1793
|
+
}, "search", async (input, context) => {
|
|
1794
|
+
const target = resolvePath(context.directory, input.path ?? ".");
|
|
1795
|
+
const args = buildSearchArgs({
|
|
1796
|
+
path: target,
|
|
1797
|
+
pattern: input.pattern,
|
|
1798
|
+
language: input.language,
|
|
1799
|
+
cached: input.cached,
|
|
1800
|
+
others: input.others,
|
|
1801
|
+
ignored: input.ignored
|
|
1802
|
+
});
|
|
1803
|
+
await authorizeSearch(context, target, input.pattern);
|
|
1804
|
+
return runReadSeek(context, args);
|
|
1805
|
+
}),
|
|
1806
|
+
readseek_def: readseekTool(openCodeToolDescription("def"), {
|
|
1807
|
+
name: tool2.schema.string().describe(PARAM_DESCRIPTIONS.name),
|
|
1808
|
+
path: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
1809
|
+
language: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
1810
|
+
cached: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.cached),
|
|
1811
|
+
others: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.others),
|
|
1812
|
+
ignored: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.ignored)
|
|
1813
|
+
}, "def", async (input, context) => {
|
|
1814
|
+
const target = resolvePath(context.directory, input.path ?? ".");
|
|
1815
|
+
const args = buildDefArgs({
|
|
1816
|
+
path: target,
|
|
1817
|
+
name: input.name,
|
|
1818
|
+
language: input.language,
|
|
1819
|
+
format: "plain",
|
|
1820
|
+
cached: input.cached,
|
|
1821
|
+
others: input.others,
|
|
1822
|
+
ignored: input.ignored
|
|
1823
|
+
});
|
|
1824
|
+
await authorizeSearch(context, target, input.name);
|
|
1825
|
+
return runReadSeek(context, args);
|
|
1826
|
+
}),
|
|
1827
|
+
readseek_refs: readseekTool(openCodeToolDescription("refs"), {
|
|
1828
|
+
name: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.refsName),
|
|
1829
|
+
path: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
1830
|
+
language: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
1831
|
+
scope: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.scope),
|
|
1832
|
+
line: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.line),
|
|
1833
|
+
column: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.column),
|
|
1834
|
+
cached: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.cached),
|
|
1835
|
+
others: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.others),
|
|
1836
|
+
ignored: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.ignored)
|
|
1837
|
+
}, "refs", async (input, context) => {
|
|
1838
|
+
const target = resolvePath(context.directory, input.path ?? ".");
|
|
1839
|
+
const name = input.name;
|
|
1840
|
+
const args = buildRefsArgs({
|
|
1841
|
+
path: target,
|
|
1842
|
+
name,
|
|
1843
|
+
scope: input.scope,
|
|
1844
|
+
line: input.line,
|
|
1845
|
+
column: input.column,
|
|
1846
|
+
language: input.language,
|
|
1847
|
+
cached: input.cached,
|
|
1848
|
+
others: input.others,
|
|
1849
|
+
ignored: input.ignored
|
|
1850
|
+
});
|
|
1851
|
+
await authorizeSearch(context, target, name ?? "*");
|
|
1852
|
+
return runReadSeek(context, args);
|
|
1853
|
+
}),
|
|
1854
|
+
readseek_rename: readseekTool(openCodeToolDescription("rename"), {
|
|
1855
|
+
path: tool2.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
1856
|
+
line: tool2.schema.number().int().positive().describe(PARAM_DESCRIPTIONS.renameLine),
|
|
1857
|
+
column: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.column),
|
|
1858
|
+
to: tool2.schema.string().min(1).describe(PARAM_DESCRIPTIONS.to),
|
|
1859
|
+
workspace: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.workspace),
|
|
1860
|
+
apply: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.applyDefaultTrue)
|
|
1861
|
+
}, "rename", async (input, context) => {
|
|
1862
|
+
const filePath = resolvePath(context.directory, input.path);
|
|
1863
|
+
await authorizeRead(context, filePath);
|
|
1864
|
+
if (input.workspace) {
|
|
1865
|
+
const identifyArgs = digestArgs(filePath, "identity");
|
|
1866
|
+
withAt(identifyArgs, input.line, input.column);
|
|
1867
|
+
const envelope = await runReadSeek(context, identifyArgs);
|
|
1868
|
+
const name = identifiedName(requireDigestFacet(envelope, "identity"));
|
|
1869
|
+
if (!name)
|
|
1870
|
+
throw new Error("readseek could not identify a binding at the rename cursor");
|
|
1871
|
+
await authorizeSearch(context, context.directory, name);
|
|
1872
|
+
}
|
|
1873
|
+
const args = buildRenameArgs({
|
|
1874
|
+
path: filePath,
|
|
1875
|
+
to: input.to,
|
|
1876
|
+
line: input.line,
|
|
1877
|
+
column: input.column,
|
|
1878
|
+
workspace: input.workspace ? context.directory : undefined
|
|
1879
|
+
});
|
|
1880
|
+
const plan = await runReadSeek(context, args);
|
|
1881
|
+
if (input.apply === false)
|
|
1882
|
+
return plan;
|
|
1883
|
+
const files = renameFiles(plan);
|
|
1884
|
+
const planHash = record(plan).plan_hash;
|
|
1885
|
+
if (typeof planHash !== "string" || planHash.length === 0) {
|
|
1886
|
+
throw new Error("readseek rename plan did not include a plan hash");
|
|
1887
|
+
}
|
|
1888
|
+
await authorizeEdit(context, files);
|
|
1889
|
+
return runReadSeek(context, buildRenameArgs({
|
|
1890
|
+
path: filePath,
|
|
1891
|
+
to: input.to,
|
|
1892
|
+
line: input.line,
|
|
1893
|
+
column: input.column,
|
|
1894
|
+
workspace: input.workspace ? context.directory : undefined,
|
|
1895
|
+
apply: true,
|
|
1896
|
+
planHash
|
|
1897
|
+
}), {
|
|
1898
|
+
cancelable: false,
|
|
1899
|
+
timeoutMs: DEFAULT_READSEEK_TIMEOUT_MS
|
|
1900
|
+
});
|
|
1901
|
+
}),
|
|
1902
|
+
readseek_edit: readseekTool(openCodeToolDescription("edit"), {
|
|
1903
|
+
path: tool2.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
1904
|
+
edits: tool2.schema.array(tool2.schema.union([
|
|
1905
|
+
tool2.schema.object({
|
|
1906
|
+
set_line: tool2.schema.object({
|
|
1907
|
+
anchor: tool2.schema.string().describe(PARAM_DESCRIPTIONS.anchor),
|
|
1908
|
+
new_text: tool2.schema.string().describe(PARAM_DESCRIPTIONS.newTextLine)
|
|
1909
|
+
}).strict()
|
|
1910
|
+
}).strict(),
|
|
1911
|
+
tool2.schema.object({
|
|
1912
|
+
replace_lines: tool2.schema.object({
|
|
1913
|
+
start_anchor: tool2.schema.string().describe(PARAM_DESCRIPTIONS.startAnchor),
|
|
1914
|
+
end_anchor: tool2.schema.string().describe(PARAM_DESCRIPTIONS.endAnchor),
|
|
1915
|
+
new_text: tool2.schema.string().describe(PARAM_DESCRIPTIONS.newTextRange)
|
|
1916
|
+
}).strict()
|
|
1917
|
+
}).strict(),
|
|
1918
|
+
tool2.schema.object({
|
|
1919
|
+
insert_after: tool2.schema.object({
|
|
1920
|
+
anchor: tool2.schema.string().describe(PARAM_DESCRIPTIONS.insertAnchor),
|
|
1921
|
+
new_text: tool2.schema.string().describe(PARAM_DESCRIPTIONS.newTextInsert)
|
|
1922
|
+
}).strict()
|
|
1923
|
+
}).strict(),
|
|
1924
|
+
tool2.schema.object({
|
|
1925
|
+
replace: tool2.schema.object({
|
|
1926
|
+
old_text: tool2.schema.string().describe(PARAM_DESCRIPTIONS.oldText),
|
|
1927
|
+
new_text: tool2.schema.string().describe(PARAM_DESCRIPTIONS.newText),
|
|
1928
|
+
all: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.all)
|
|
1929
|
+
}).strict()
|
|
1930
|
+
}).strict(),
|
|
1931
|
+
tool2.schema.object({
|
|
1932
|
+
replace_symbol: tool2.schema.object({
|
|
1933
|
+
symbol: tool2.schema.string().describe(PARAM_DESCRIPTIONS.symbol),
|
|
1934
|
+
new_body: tool2.schema.string().describe(PARAM_DESCRIPTIONS.newBody)
|
|
1935
|
+
}).strict()
|
|
1936
|
+
}).strict()
|
|
1937
|
+
])).min(1).describe(PARAM_DESCRIPTIONS.edits),
|
|
1938
|
+
language: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.languageSymbol)
|
|
1939
|
+
}, "edit", async (input, context) => {
|
|
1940
|
+
const filePath = resolvePath(context.directory, input.path);
|
|
1941
|
+
validateEdits(input.edits);
|
|
1942
|
+
return withFileMutationQueue(filePath, async () => {
|
|
1943
|
+
context.abort.throwIfAborted();
|
|
1772
1944
|
await authorizeRead(context, filePath);
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
const name = identifiedName(requireDigestFacet(envelope, "identity"));
|
|
1778
|
-
if (!name)
|
|
1779
|
-
throw new Error("readseek could not identify a binding at the rename cursor");
|
|
1780
|
-
await authorizeSearch(context, context.directory, name);
|
|
1781
|
-
}
|
|
1782
|
-
const args = buildRenameArgs({
|
|
1945
|
+
await rejectSymlinkMutation(filePath);
|
|
1946
|
+
const edits = input.edits;
|
|
1947
|
+
const request = JSON.stringify({ edits });
|
|
1948
|
+
const baseArgs = buildEditArgs({
|
|
1783
1949
|
path: filePath,
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
column: input.column,
|
|
1787
|
-
workspace: input.workspace ? context.directory : undefined
|
|
1950
|
+
language: input.language,
|
|
1951
|
+
requestFromStdin: true
|
|
1788
1952
|
});
|
|
1789
|
-
const plan = await runReadSeek(context,
|
|
1790
|
-
if (
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
const
|
|
1794
|
-
|
|
1795
|
-
|
|
1953
|
+
const plan = record(await runReadSeek(context, baseArgs, { stdin: request }));
|
|
1954
|
+
if (typeof plan.before_content !== "string" || typeof plan.content !== "string" || typeof plan.plan_hash !== "string") {
|
|
1955
|
+
throw new Error("readseek edit plan is missing before_content, content, or plan_hash");
|
|
1956
|
+
}
|
|
1957
|
+
const before = plan.before_content;
|
|
1958
|
+
const after = plan.content;
|
|
1959
|
+
const previewStart = Math.max(1, firstChangedLine(before, after) - 3);
|
|
1960
|
+
const previewArgs = readRangeArgs(filePath, previewStart, previewStart + EDIT_RESULT_READ_LIMIT - 1);
|
|
1961
|
+
if (before === after) {
|
|
1962
|
+
return postMutationPreview(context, previewArgs, filePath, false);
|
|
1796
1963
|
}
|
|
1797
|
-
await authorizeEdit(context,
|
|
1798
|
-
|
|
1964
|
+
await authorizeEdit(context, [filePath], simpleDiff(filePath, before, after), false);
|
|
1965
|
+
context.abort.throwIfAborted();
|
|
1966
|
+
const applied = record(await runReadSeek(context, buildEditArgs({
|
|
1799
1967
|
path: filePath,
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
column: input.column,
|
|
1803
|
-
workspace: input.workspace ? context.directory : undefined,
|
|
1968
|
+
language: input.language,
|
|
1969
|
+
requestFromStdin: true,
|
|
1804
1970
|
apply: true,
|
|
1805
|
-
planHash
|
|
1971
|
+
planHash: plan.plan_hash
|
|
1806
1972
|
}), {
|
|
1807
1973
|
cancelable: false,
|
|
1974
|
+
stdin: request,
|
|
1808
1975
|
timeoutMs: DEFAULT_READSEEK_TIMEOUT_MS
|
|
1809
|
-
});
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
}).strict()
|
|
1845
|
-
}).strict()
|
|
1846
|
-
])).min(1).describe(PARAM_DESCRIPTIONS.edits),
|
|
1847
|
-
language: tool.schema.string().optional().describe(PARAM_DESCRIPTIONS.languageSymbol)
|
|
1848
|
-
}, "edit", async (input, context) => {
|
|
1849
|
-
const filePath = resolvePath(context.directory, input.path);
|
|
1850
|
-
validateEdits(input.edits);
|
|
1851
|
-
return withFileMutationQueue(filePath, async () => {
|
|
1852
|
-
context.abort.throwIfAborted();
|
|
1853
|
-
await authorizeRead(context, filePath);
|
|
1854
|
-
await rejectSymlinkMutation(filePath);
|
|
1855
|
-
const edits = input.edits;
|
|
1856
|
-
const request = JSON.stringify({ edits });
|
|
1857
|
-
const baseArgs = buildEditArgs({
|
|
1858
|
-
path: filePath,
|
|
1859
|
-
language: input.language,
|
|
1860
|
-
requestFromStdin: true
|
|
1861
|
-
});
|
|
1862
|
-
const plan = record(await runReadSeek(context, baseArgs, { stdin: request }));
|
|
1863
|
-
if (typeof plan.before_content !== "string" || typeof plan.content !== "string" || typeof plan.plan_hash !== "string") {
|
|
1864
|
-
throw new Error("readseek edit plan is missing before_content, content, or plan_hash");
|
|
1865
|
-
}
|
|
1866
|
-
const before = plan.before_content;
|
|
1867
|
-
const after = plan.content;
|
|
1868
|
-
const previewStart = Math.max(1, firstChangedLine(before, after) - 3);
|
|
1869
|
-
const previewArgs = readRangeArgs(filePath, previewStart, previewStart + EDIT_RESULT_READ_LIMIT - 1);
|
|
1870
|
-
if (before === after) {
|
|
1871
|
-
return postMutationPreview(context, previewArgs, filePath, false);
|
|
1872
|
-
}
|
|
1873
|
-
await authorizeEdit(context, [filePath], simpleDiff(filePath, before, after), false);
|
|
1874
|
-
context.abort.throwIfAborted();
|
|
1875
|
-
const applied = record(await runReadSeek(context, buildEditArgs({
|
|
1876
|
-
path: filePath,
|
|
1877
|
-
language: input.language,
|
|
1878
|
-
requestFromStdin: true,
|
|
1879
|
-
apply: true,
|
|
1880
|
-
planHash: plan.plan_hash
|
|
1881
|
-
}), {
|
|
1882
|
-
cancelable: false,
|
|
1883
|
-
stdin: request,
|
|
1884
|
-
timeoutMs: DEFAULT_READSEEK_TIMEOUT_MS
|
|
1885
|
-
}));
|
|
1886
|
-
if (applied.applied !== true) {
|
|
1887
|
-
throw new Error("readseek did not confirm that the edit was applied");
|
|
1888
|
-
}
|
|
1889
|
-
return postMutationPreview(context, previewArgs, filePath, true);
|
|
1890
|
-
});
|
|
1891
|
-
}),
|
|
1892
|
-
readseek_write: readseekTool(openCodeToolDescription("write"), {
|
|
1893
|
-
path: tool.schema.string().describe("Path relative to the project directory"),
|
|
1894
|
-
content: tool.schema.string().describe("Complete text file content")
|
|
1895
|
-
}, "write", async (input, context) => {
|
|
1896
|
-
const filePath = resolvePath(context.directory, input.path);
|
|
1897
|
-
await authorizeExternal(context, filePath);
|
|
1898
|
-
return withFileMutationQueue(filePath, async () => {
|
|
1899
|
-
context.abort.throwIfAborted();
|
|
1900
|
-
await rejectSymlinkMutation(filePath);
|
|
1901
|
-
const before = await readTextSnapshot(filePath);
|
|
1902
|
-
const content = input.content;
|
|
1903
|
-
if (content.includes("\x00"))
|
|
1904
|
-
throw new Error("write content must be text");
|
|
1905
|
-
await authorizeEdit(context, [filePath], simpleDiff(filePath, before.content, content), false);
|
|
1906
|
-
context.abort.throwIfAborted();
|
|
1907
|
-
await writeText(filePath, before, content);
|
|
1908
|
-
return postMutationPreview(context, digestArgs(filePath, "content", ["--end", String(DEFAULT_READ_LIMIT)]), filePath, !before.exists || before.content !== content);
|
|
1909
|
-
});
|
|
1910
|
-
})
|
|
1911
|
-
},
|
|
1976
|
+
}));
|
|
1977
|
+
if (applied.applied !== true) {
|
|
1978
|
+
throw new Error("readseek did not confirm that the edit was applied");
|
|
1979
|
+
}
|
|
1980
|
+
return postMutationPreview(context, previewArgs, filePath, true);
|
|
1981
|
+
});
|
|
1982
|
+
}),
|
|
1983
|
+
readseek_write: readseekTool(openCodeToolDescription("write"), {
|
|
1984
|
+
path: tool2.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
1985
|
+
content: tool2.schema.string().describe(PARAM_DESCRIPTIONS.content)
|
|
1986
|
+
}, "write", async (input, context) => {
|
|
1987
|
+
const filePath = resolvePath(context.directory, input.path);
|
|
1988
|
+
await authorizeExternal(context, filePath);
|
|
1989
|
+
return withFileMutationQueue(filePath, async () => {
|
|
1990
|
+
context.abort.throwIfAborted();
|
|
1991
|
+
await rejectSymlinkMutation(filePath);
|
|
1992
|
+
const before = await readTextSnapshot(filePath);
|
|
1993
|
+
const content = input.content;
|
|
1994
|
+
if (content.includes("\x00"))
|
|
1995
|
+
throw new Error("write content must be text");
|
|
1996
|
+
await authorizeEdit(context, [filePath], simpleDiff(filePath, before.content, content), false);
|
|
1997
|
+
context.abort.throwIfAborted();
|
|
1998
|
+
await writeText(filePath, before, content);
|
|
1999
|
+
return postMutationPreview(context, digestArgs(filePath, "content", ["--end", String(DEFAULT_READ_LIMIT)]), filePath, !before.exists || before.content !== content);
|
|
2000
|
+
});
|
|
2001
|
+
})
|
|
2002
|
+
};
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
// index.ts
|
|
2006
|
+
var ReadSeekPlugin = async (_input, options) => {
|
|
2007
|
+
const anchors = new SessionAnchors;
|
|
2008
|
+
const imagePolicy = resolveImagePolicy(options);
|
|
2009
|
+
return {
|
|
2010
|
+
tool: createReadSeekTools(imagePolicy),
|
|
1912
2011
|
"experimental.chat.system.transform": async (_input2, output) => {
|
|
1913
2012
|
output.system.push(TOOL_POLICY);
|
|
1914
2013
|
},
|
|
@@ -1945,7 +2044,7 @@ var ReadSeekPlugin = async (_input, options) => {
|
|
|
1945
2044
|
const files = new Set;
|
|
1946
2045
|
collectAnchoredFiles(result, files);
|
|
1947
2046
|
for (const filePath of files)
|
|
1948
|
-
anchors.mark(input.sessionID,
|
|
2047
|
+
anchors.mark(input.sessionID, path7.resolve(filePath));
|
|
1949
2048
|
} catch {}
|
|
1950
2049
|
},
|
|
1951
2050
|
"experimental.session.compacting": async (input, output) => {
|