opencode-readseek 0.9.15 → 0.10.0
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/LICENSE +176 -0
- package/README.md +15 -6
- package/dist/index.js +938 -282
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -6,21 +6,15 @@ import { createRequire as createRequire2 } from "node:module";
|
|
|
6
6
|
import { stat } from "node:fs/promises";
|
|
7
7
|
|
|
8
8
|
// ../readseek-api/src/contract.ts
|
|
9
|
+
var INSPECT_FACETS = [
|
|
10
|
+
"metadata",
|
|
11
|
+
"content",
|
|
12
|
+
"map",
|
|
13
|
+
"diagnostics",
|
|
14
|
+
"identity"
|
|
15
|
+
];
|
|
9
16
|
var VISION_MODES = ["none", "all", "ocr", "caption", "objects"];
|
|
10
|
-
var VISION_ANALYSIS_MODES = ["all", "ocr", "caption", "objects"];
|
|
11
17
|
var VISION_LEVELS = ["low", "medium", "high"];
|
|
12
|
-
var DOCUMENT_NODE_KINDS = [
|
|
13
|
-
"artifact",
|
|
14
|
-
"footer",
|
|
15
|
-
"header",
|
|
16
|
-
"heading",
|
|
17
|
-
"marginal_label",
|
|
18
|
-
"page",
|
|
19
|
-
"page_number",
|
|
20
|
-
"paragraph",
|
|
21
|
-
"section",
|
|
22
|
-
"structural_section"
|
|
23
|
-
];
|
|
24
18
|
var EDIT_VARIANTS = [
|
|
25
19
|
"set_line",
|
|
26
20
|
"replace_lines",
|
|
@@ -28,12 +22,13 @@ var EDIT_VARIANTS = [
|
|
|
28
22
|
"replace",
|
|
29
23
|
"replace_symbol"
|
|
30
24
|
];
|
|
25
|
+
var GIT_SCOPES = ["tracked", "untracked", "ignored", "all"];
|
|
31
26
|
var TOOL_NAMES = [
|
|
32
|
-
"
|
|
27
|
+
"inspect",
|
|
33
28
|
"edit",
|
|
34
29
|
"write",
|
|
35
30
|
"grep",
|
|
36
|
-
"
|
|
31
|
+
"extract",
|
|
37
32
|
"search",
|
|
38
33
|
"def",
|
|
39
34
|
"refs",
|
|
@@ -55,27 +50,24 @@ var PARAM_DESCRIPTIONS = {
|
|
|
55
50
|
line: "One-based cursor line; required with scope",
|
|
56
51
|
renameLine: "One-based cursor line of the symbol to rename",
|
|
57
52
|
column: "One-based cursor byte column for disambiguation",
|
|
58
|
-
|
|
59
|
-
others: "In a Git repository, search untracked files",
|
|
60
|
-
ignored: "With others=true, include ignored untracked files",
|
|
53
|
+
gitScope: "In a Git repository, which files to search: tracked, untracked, ignored (untracked plus ignored), or all. Defaults to tracked plus untracked.",
|
|
61
54
|
select: "Facets: metadata, content, map, diagnostics, identity (default content)",
|
|
62
55
|
selectComma: "Comma-separated facets: metadata, content, map, diagnostics, identity",
|
|
63
|
-
|
|
56
|
+
selector: "Positional selector: line:N[:COLUMN], hash:H[:COLUMN], symbol:NAME, page:N, node:ID, or asset:ID. Columns require the identity facet, which in turn rejects symbol selectors",
|
|
57
|
+
assetSelector: "Document asset selector in the form asset:ID",
|
|
58
|
+
query: "KDL selection query, @path, or '-' for standard input",
|
|
59
|
+
stdinName: "File name hint when the target is '-'",
|
|
64
60
|
end: "Last line to include",
|
|
65
61
|
limit: "Maximum number of lines to include",
|
|
62
|
+
inspectDepth: "Maximum structural depth for map output; defaults to 1 in agent tools",
|
|
66
63
|
offset: "One-based starting line",
|
|
67
64
|
readLimit: "Maximum lines to return",
|
|
68
65
|
symbolRead: "Symbol name to read",
|
|
69
66
|
bundle: "Include related symbols from the same file; requires symbol",
|
|
70
67
|
image: "Standalone image mode: none, all, ocr, caption, objects. Required for standalone images.",
|
|
71
|
-
|
|
72
|
-
visionModeView: "Analyze selected assets: all, ocr, caption, or objects",
|
|
68
|
+
visionModeInspect: "Standalone image mode: none, all, caption, objects, or ocr",
|
|
73
69
|
visionLevel: "Vision inference level: low (default), medium, or high",
|
|
74
|
-
|
|
75
|
-
page: "One-based source page",
|
|
76
|
-
kind: "Node kind filter",
|
|
77
|
-
depth: "Maximum depth below selected roots",
|
|
78
|
-
outline: "Return outline nodes only",
|
|
70
|
+
output: "Destination file for extracted binary data; stdout is not supported",
|
|
79
71
|
edits: "Use set_line, replace_lines, insert_after, replace_symbol, or replace",
|
|
80
72
|
editVariant: "Exactly one nested edit variant",
|
|
81
73
|
setLine: 'Replace one line: {"set_line":{"anchor":"LINE:HASH","new_text":"..."}}',
|
|
@@ -112,11 +104,11 @@ var PARAM_DESCRIPTIONS = {
|
|
|
112
104
|
scopeContext: "Context lines within each symbol"
|
|
113
105
|
};
|
|
114
106
|
var TOOL_DESCRIPTIONS = {
|
|
115
|
-
|
|
107
|
+
inspect: "Inspect source content, images, and documents through the native CLI envelope. Use it before edits to obtain fresh `LINE:HASH` anchors; use shallow maps for structural overviews, diagnostics for syntax checks, and identity for cursor context.",
|
|
116
108
|
edit: 'Edit existing text files with fresh `LINE:HASH` anchors. Read or search first when no fresh anchors are available. Each `edits[]` item must use exactly one nested object: `{"set_line":{"anchor":"LINE:HASH","new_text":"..."}}`, `{"replace_lines":{"start_anchor":"LINE:HASH","end_anchor":"LINE:HASH","new_text":"..."}}`, `{"insert_after":{"anchor":"LINE:HASH","new_text":"..."}}`, `{"replace_symbol":{"symbol":"name","new_body":"..."}}`, or `{"replace":{"old_text":"...","new_text":"..."}}`.',
|
|
117
109
|
write: "Create or replace a complete text file and return `LINE:HASH` anchors. Use `edit` for small changes to an existing file.",
|
|
118
110
|
grep: "Search text or regex in files and return edit-ready `LINE:HASH` anchors. Use for identifiers, strings, configuration, errors, comments, and documentation.",
|
|
119
|
-
|
|
111
|
+
extract: "Extract a selected document asset into a destination file. Use an `asset:ID` selector discovered through `inspect`; extracted binary data is never returned on stdout.",
|
|
120
112
|
search: "Search code by AST structure using ast-grep-style patterns. Results include line hashes usable as `LINE:HASH` anchors. Use `grep` for identifiers, strings, comments, configuration, documentation, or other plain text and regex.",
|
|
121
113
|
def: "Go to a symbol definition or declaration by name. Accepts qualified or unqualified names; use this instead of text search for functions, methods, classes, types, and other declarations.",
|
|
122
114
|
refs: "Find references or usages of an identifier by name or cursor binding. Results include enclosing symbols and line hashes usable as `LINE:HASH` anchors. Without cursor scope, results are identifier-name matches; with scope, they follow one binding and exclude shadows. Use text search for literals, and use this before renaming or deleting a symbol.",
|
|
@@ -128,18 +120,11 @@ function renderToolDescription(name, aliases = {}) {
|
|
|
128
120
|
throw new Error(`Missing TOOL_DESCRIPTIONS for ${name}`);
|
|
129
121
|
return TOOL_NAMES.reduce((description, toolName) => description.replaceAll(`\`${toolName}\``, `\`${aliases[toolName] ?? toolName}\``), desc);
|
|
130
122
|
}
|
|
131
|
-
var
|
|
132
|
-
|
|
133
|
-
type: "
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
others: {
|
|
137
|
-
type: "boolean",
|
|
138
|
-
description: PARAM_DESCRIPTIONS.others
|
|
139
|
-
},
|
|
140
|
-
ignored: {
|
|
141
|
-
type: "boolean",
|
|
142
|
-
description: PARAM_DESCRIPTIONS.ignored
|
|
123
|
+
var gitScopeSchema = {
|
|
124
|
+
gitScope: {
|
|
125
|
+
type: "string",
|
|
126
|
+
enum: GIT_SCOPES,
|
|
127
|
+
description: PARAM_DESCRIPTIONS.gitScope
|
|
143
128
|
}
|
|
144
129
|
};
|
|
145
130
|
var editItemSchema = {
|
|
@@ -205,28 +190,31 @@ var editItemSchema = {
|
|
|
205
190
|
};
|
|
206
191
|
var TOOL_CONTRACTS = [
|
|
207
192
|
{
|
|
208
|
-
name: "
|
|
209
|
-
title: "
|
|
210
|
-
description: TOOL_DESCRIPTIONS.
|
|
193
|
+
name: "inspect",
|
|
194
|
+
title: "Inspect input",
|
|
195
|
+
description: TOOL_DESCRIPTIONS.inspect,
|
|
211
196
|
inputSchema: {
|
|
212
197
|
type: "object",
|
|
213
198
|
properties: {
|
|
214
199
|
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
200
|
+
selector: { type: "string", description: PARAM_DESCRIPTIONS.selector },
|
|
215
201
|
select: { type: "string", description: PARAM_DESCRIPTIONS.select },
|
|
216
|
-
|
|
202
|
+
query: { type: "string", description: PARAM_DESCRIPTIONS.query },
|
|
217
203
|
end: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.end },
|
|
218
204
|
limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.limit },
|
|
205
|
+
depth: { type: "integer", minimum: 0, description: PARAM_DESCRIPTIONS.inspectDepth },
|
|
219
206
|
language: { type: "string", description: PARAM_DESCRIPTIONS.languageOverride },
|
|
220
207
|
visionMode: {
|
|
221
208
|
type: "string",
|
|
222
209
|
enum: [...VISION_MODES],
|
|
223
|
-
description: PARAM_DESCRIPTIONS.
|
|
210
|
+
description: PARAM_DESCRIPTIONS.visionModeInspect
|
|
224
211
|
},
|
|
225
212
|
visionLevel: {
|
|
226
213
|
type: "string",
|
|
227
214
|
enum: [...VISION_LEVELS],
|
|
228
215
|
description: PARAM_DESCRIPTIONS.visionLevel
|
|
229
|
-
}
|
|
216
|
+
},
|
|
217
|
+
stdinName: { type: "string", description: PARAM_DESCRIPTIONS.stdinName }
|
|
230
218
|
},
|
|
231
219
|
required: ["path"],
|
|
232
220
|
additionalProperties: false
|
|
@@ -300,34 +288,27 @@ var TOOL_CONTRACTS = [
|
|
|
300
288
|
}
|
|
301
289
|
},
|
|
302
290
|
{
|
|
303
|
-
name: "
|
|
304
|
-
title: "
|
|
305
|
-
description: TOOL_DESCRIPTIONS.
|
|
291
|
+
name: "extract",
|
|
292
|
+
title: "Extract asset",
|
|
293
|
+
description: TOOL_DESCRIPTIONS.extract,
|
|
306
294
|
inputSchema: {
|
|
307
295
|
type: "object",
|
|
308
296
|
properties: {
|
|
309
297
|
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
310
|
-
|
|
311
|
-
page: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.page },
|
|
312
|
-
kind: {
|
|
298
|
+
selector: {
|
|
313
299
|
type: "string",
|
|
314
|
-
|
|
315
|
-
description: PARAM_DESCRIPTIONS.
|
|
300
|
+
pattern: "^asset:.+",
|
|
301
|
+
description: PARAM_DESCRIPTIONS.assetSelector
|
|
316
302
|
},
|
|
317
|
-
|
|
318
|
-
outline: { type: "boolean", description: PARAM_DESCRIPTIONS.outline },
|
|
319
|
-
visionMode: {
|
|
303
|
+
output: {
|
|
320
304
|
type: "string",
|
|
321
|
-
|
|
322
|
-
|
|
305
|
+
minLength: 1,
|
|
306
|
+
pattern: "^(?!-$).+",
|
|
307
|
+
description: PARAM_DESCRIPTIONS.output
|
|
323
308
|
},
|
|
324
|
-
|
|
325
|
-
type: "string",
|
|
326
|
-
enum: [...VISION_LEVELS],
|
|
327
|
-
description: PARAM_DESCRIPTIONS.visionLevel
|
|
328
|
-
}
|
|
309
|
+
stdinName: { type: "string", description: PARAM_DESCRIPTIONS.stdinName }
|
|
329
310
|
},
|
|
330
|
-
required: ["path"],
|
|
311
|
+
required: ["path", "selector", "output"],
|
|
331
312
|
additionalProperties: false
|
|
332
313
|
}
|
|
333
314
|
},
|
|
@@ -341,7 +322,8 @@ var TOOL_CONTRACTS = [
|
|
|
341
322
|
pattern: { type: "string", description: PARAM_DESCRIPTIONS.pattern },
|
|
342
323
|
path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
|
|
343
324
|
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
344
|
-
|
|
325
|
+
limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.matchLimit },
|
|
326
|
+
...gitScopeSchema
|
|
345
327
|
},
|
|
346
328
|
required: ["pattern"],
|
|
347
329
|
additionalProperties: false
|
|
@@ -357,7 +339,7 @@ var TOOL_CONTRACTS = [
|
|
|
357
339
|
name: { type: "string", description: PARAM_DESCRIPTIONS.name },
|
|
358
340
|
path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
|
|
359
341
|
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
360
|
-
...
|
|
342
|
+
...gitScopeSchema
|
|
361
343
|
},
|
|
362
344
|
required: ["name"],
|
|
363
345
|
additionalProperties: false
|
|
@@ -376,7 +358,7 @@ var TOOL_CONTRACTS = [
|
|
|
376
358
|
scope: { type: "boolean", description: PARAM_DESCRIPTIONS.scope },
|
|
377
359
|
line: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.line },
|
|
378
360
|
column: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.column },
|
|
379
|
-
...
|
|
361
|
+
...gitScopeSchema
|
|
380
362
|
},
|
|
381
363
|
additionalProperties: false
|
|
382
364
|
}
|
|
@@ -394,7 +376,9 @@ var TOOL_CONTRACTS = [
|
|
|
394
376
|
to: { type: "string", description: PARAM_DESCRIPTIONS.to },
|
|
395
377
|
workspace: { type: "boolean", description: PARAM_DESCRIPTIONS.workspace },
|
|
396
378
|
apply: { type: "boolean", description: PARAM_DESCRIPTIONS.apply },
|
|
397
|
-
planHash: { type: "string", description: PARAM_DESCRIPTIONS.planHash }
|
|
379
|
+
planHash: { type: "string", description: PARAM_DESCRIPTIONS.planHash },
|
|
380
|
+
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
381
|
+
...gitScopeSchema
|
|
398
382
|
},
|
|
399
383
|
required: ["path", "line", "to"],
|
|
400
384
|
additionalProperties: false
|
|
@@ -403,14 +387,14 @@ var TOOL_CONTRACTS = [
|
|
|
403
387
|
];
|
|
404
388
|
// ../readseek-api/src/policy.ts
|
|
405
389
|
var BUILTIN_FALLBACKS = {
|
|
406
|
-
read: "
|
|
390
|
+
read: "inspect",
|
|
407
391
|
edit: "edit",
|
|
408
392
|
write: "write",
|
|
409
393
|
grep: "grep",
|
|
410
394
|
apply_patch: "edit"
|
|
411
395
|
};
|
|
412
396
|
var PREFERENCE_LEADS = {
|
|
413
|
-
|
|
397
|
+
inspect: "Preferred for reading source with anchors and facets.",
|
|
414
398
|
edit: "Preferred for editing existing text files with fresh LINE:HASH anchors.",
|
|
415
399
|
write: "Preferred for creating or replacing a complete text file; returns LINE:HASH anchors.",
|
|
416
400
|
grep: "Preferred text/regex search; returns edit-ready LINE:HASH anchors.",
|
|
@@ -418,10 +402,10 @@ var PREFERENCE_LEADS = {
|
|
|
418
402
|
def: "Preferred for go-to-definition or declaration lookup by name.",
|
|
419
403
|
refs: "Preferred for finding identifier-name matches or binding-specific references before rename or delete.",
|
|
420
404
|
rename: "Preferred for renaming a symbol with binding-aware, verified edits.",
|
|
421
|
-
|
|
405
|
+
extract: "Preferred for saving a selected document asset to a file."
|
|
422
406
|
};
|
|
423
407
|
var FALLBACK_TEMPLATES = {
|
|
424
|
-
read: "Prefer `
|
|
408
|
+
read: "Prefer `inspect` with fresh LINE:HASH anchors.",
|
|
425
409
|
edit: "Prefer `edit` with fresh LINE:HASH anchors.",
|
|
426
410
|
write: "Prefer `write` so the result returns LINE:HASH anchors.",
|
|
427
411
|
grep: "Prefer `grep` for text/regex search with edit-ready anchors.",
|
|
@@ -444,7 +428,7 @@ function renderFallbackToolDescription(builtIn, aliases = {}) {
|
|
|
444
428
|
}
|
|
445
429
|
function renderToolRoutingPolicy(options = {}) {
|
|
446
430
|
const aliases = options.aliases ?? {};
|
|
447
|
-
const
|
|
431
|
+
const inspect = resolveToolName("inspect", aliases);
|
|
448
432
|
const edit = resolveToolName("edit", aliases);
|
|
449
433
|
const write = resolveToolName("write", aliases);
|
|
450
434
|
const grep = resolveToolName("grep", aliases);
|
|
@@ -452,11 +436,11 @@ function renderToolRoutingPolicy(options = {}) {
|
|
|
452
436
|
const def = resolveToolName("def", aliases);
|
|
453
437
|
const refs = resolveToolName("refs", aliases);
|
|
454
438
|
const rename = resolveToolName("rename", aliases);
|
|
455
|
-
const
|
|
439
|
+
const extract = resolveToolName("extract", aliases);
|
|
456
440
|
if (options.style === "server") {
|
|
457
441
|
return [
|
|
458
|
-
"Structural code navigation, document
|
|
459
|
-
"For structural questions, prefer these tools to text Grep/Bash: definitions, references, AST search
|
|
442
|
+
"Structural code navigation, document inspection, and hash-verified edits with readseek.",
|
|
443
|
+
"For structural questions, prefer these tools to text Grep/Bash: targeted definitions, references, and AST search; shallow inspect maps; diagnostics and identity; and document inspection.",
|
|
460
444
|
`Use ${edit} for LINE:HASH-anchored mutations (preview with apply=false; apply defaults to true and re-checks plan_hash).`,
|
|
461
445
|
"Paths resolve against CLAUDE_PROJECT_DIR. Native Read/Grep/Edit hooks provide passive context;",
|
|
462
446
|
"call MCP tools for active navigation, document questions, or anchored edits."
|
|
@@ -467,7 +451,7 @@ function renderToolRoutingPolicy(options = {}) {
|
|
|
467
451
|
if (options.preferOverBuiltIns) {
|
|
468
452
|
bullets.push("- Prefer ReadSeek tools over built-ins when they can do the job.");
|
|
469
453
|
}
|
|
470
|
-
bullets.push(`- Read with ${
|
|
454
|
+
bullets.push(`- Read with ${inspect} first; ${edit} needs fresh LINE:HASH anchors.`, `- Use ${grep} for text/regex, ${search} for AST patterns, ${def}/${refs} for targeted symbols, and shallow ${inspect} maps only for file overviews.`, `- Use ${edit} for existing files, ${write} for whole-file creation or replacement, and ${rename} for symbol renames.`, `- Use ${inspect} for document structure; run ${inspect} with select diagnostics after source edits for a quick syntax check.`, `- Use ${extract} to save a selected document asset.`);
|
|
471
455
|
if (options.demoteBuiltIns && options.demoteBuiltIns.length > 0) {
|
|
472
456
|
bullets.push(`- Do not use built-in ${options.demoteBuiltIns.join(", ")} when a ReadSeek tool can do the job.`);
|
|
473
457
|
}
|
|
@@ -502,12 +486,6 @@ function tryParseLineAnchor(anchor) {
|
|
|
502
486
|
}
|
|
503
487
|
|
|
504
488
|
// ../readseek-api/src/validation.ts
|
|
505
|
-
function validateGitSelection(input) {
|
|
506
|
-
if (input.ignored && !input.others) {
|
|
507
|
-
return { ok: false, error: "ignored requires others=true" };
|
|
508
|
-
}
|
|
509
|
-
return { ok: true };
|
|
510
|
-
}
|
|
511
489
|
function validateRefsNameVsScope(input) {
|
|
512
490
|
if (input.scope) {
|
|
513
491
|
if (input.line === undefined) {
|
|
@@ -524,11 +502,8 @@ function validateRefsNameVsScope(input) {
|
|
|
524
502
|
return { ok: true };
|
|
525
503
|
}
|
|
526
504
|
function validateVisionLevelVsMode(input) {
|
|
527
|
-
if (input.
|
|
528
|
-
return { ok: false, error: "
|
|
529
|
-
}
|
|
530
|
-
if (input.visionLevel !== undefined && input.visionMode === undefined) {
|
|
531
|
-
return { ok: false, error: "visionLevel requires visionMode" };
|
|
505
|
+
if (input.visionLevel !== undefined && !["all", "ocr", "caption", "objects"].includes(input.visionMode ?? "")) {
|
|
506
|
+
return { ok: false, error: "visionLevel requires an analysis visionMode" };
|
|
532
507
|
}
|
|
533
508
|
return { ok: true };
|
|
534
509
|
}
|
|
@@ -599,21 +574,54 @@ function validateExactlyOneEditVariant(edits) {
|
|
|
599
574
|
}
|
|
600
575
|
var MIN_SAFE_INTEGER = BigInt(Number.MIN_SAFE_INTEGER);
|
|
601
576
|
var MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
|
|
577
|
+
// ../readseek-api/src/edit-plan.ts
|
|
578
|
+
import { Document, Node, format } from "@bgotink/kdl";
|
|
579
|
+
function node(name, properties, children = []) {
|
|
580
|
+
const result = Node.create(name);
|
|
581
|
+
for (const [key, value] of Object.entries(properties))
|
|
582
|
+
result.setProperty(key, value);
|
|
583
|
+
if (children.length > 0)
|
|
584
|
+
result.children = new Document(children);
|
|
585
|
+
return result;
|
|
586
|
+
}
|
|
587
|
+
function editNode(edit) {
|
|
588
|
+
if ("set_line" in edit) {
|
|
589
|
+
const value = edit.set_line;
|
|
590
|
+
return node("set-line", { anchor: value.anchor, text: value.new_text });
|
|
591
|
+
}
|
|
592
|
+
if ("replace_lines" in edit) {
|
|
593
|
+
const value = edit.replace_lines;
|
|
594
|
+
return node("replace-lines", { start: value.start_anchor, end: value.end_anchor, text: value.new_text });
|
|
595
|
+
}
|
|
596
|
+
if ("insert_after" in edit) {
|
|
597
|
+
const value = edit.insert_after;
|
|
598
|
+
return node("insert-after", { anchor: value.anchor, text: value.new_text });
|
|
599
|
+
}
|
|
600
|
+
if ("replace_symbol" in edit) {
|
|
601
|
+
const value = edit.replace_symbol;
|
|
602
|
+
return node("replace-symbol", { symbol: value.symbol, body: value.new_body });
|
|
603
|
+
}
|
|
604
|
+
const value = edit.replace;
|
|
605
|
+
return node("replace", {
|
|
606
|
+
old: value.old_text,
|
|
607
|
+
new: value.new_text,
|
|
608
|
+
...value.all === undefined ? {} : { all: value.all }
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
function serializeEditPlan(edits) {
|
|
612
|
+
const validation = validateExactlyOneEditVariant(edits);
|
|
613
|
+
if (!validation.ok)
|
|
614
|
+
throw new Error(validation.error);
|
|
615
|
+
return format(new Document([node("plan", {}, validation.edits.map(editNode))]));
|
|
616
|
+
}
|
|
602
617
|
// ../readseek-api/src/argv.ts
|
|
618
|
+
var DEFAULT_INSPECT_MAP_DEPTH = 1;
|
|
603
619
|
function buildLineLocation(line, column) {
|
|
604
620
|
return column === undefined ? `line:${line}` : `line:${line}:${column}`;
|
|
605
621
|
}
|
|
606
|
-
function
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
throw new Error(validated.error);
|
|
610
|
-
}
|
|
611
|
-
if (input.cached)
|
|
612
|
-
args.push("--cached");
|
|
613
|
-
if (input.others)
|
|
614
|
-
args.push("--others");
|
|
615
|
-
if (input.ignored)
|
|
616
|
-
args.push("--ignored");
|
|
622
|
+
function appendGitScopeArgs(args, input) {
|
|
623
|
+
if (input.gitScope !== undefined)
|
|
624
|
+
args.push("--git-scope", input.gitScope);
|
|
617
625
|
return args;
|
|
618
626
|
}
|
|
619
627
|
function pushOptionalString(args, flag, value) {
|
|
@@ -642,51 +650,163 @@ function selectArg(select) {
|
|
|
642
650
|
return;
|
|
643
651
|
return Array.isArray(select) ? select.join(",") : select;
|
|
644
652
|
}
|
|
645
|
-
function
|
|
653
|
+
function selectedInspectFacets(select) {
|
|
654
|
+
const facets = (selectArg(select) ?? "content").split(",").map((facet) => facet.trim());
|
|
655
|
+
for (const facet of facets) {
|
|
656
|
+
if (!INSPECT_FACETS.includes(facet)) {
|
|
657
|
+
throw new Error(`unknown inspect facet: ${facet}`);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
return facets;
|
|
661
|
+
}
|
|
662
|
+
function hasInspectFacet(facets, facet) {
|
|
663
|
+
return facets.includes(facet);
|
|
664
|
+
}
|
|
665
|
+
function validateTarget(command, target, stdinName) {
|
|
666
|
+
if (target.length === 0)
|
|
667
|
+
throw new Error(`${command} requires a target file path or '-'`);
|
|
668
|
+
if (stdinName !== undefined && stdinName.length > 0 && target !== "-") {
|
|
669
|
+
throw new Error("stdinName requires '-' as the target");
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
function validatePositiveInteger(value, name) {
|
|
673
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
674
|
+
throw new Error(`${name} must be a positive integer`);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
var SELECTOR_KINDS = ["line", "hash", "symbol", "page", "node", "asset"];
|
|
678
|
+
function isSelectorKind(value) {
|
|
679
|
+
return SELECTOR_KINDS.includes(value);
|
|
680
|
+
}
|
|
681
|
+
function parseSelector(text) {
|
|
682
|
+
const separator = text.indexOf(":");
|
|
683
|
+
if (separator < 0) {
|
|
684
|
+
throw new Error("selector must start with line:, hash:, symbol:, page:, node:, or asset:");
|
|
685
|
+
}
|
|
686
|
+
const kind = text.slice(0, separator);
|
|
687
|
+
const value = text.slice(separator + 1);
|
|
688
|
+
if (value.length === 0)
|
|
689
|
+
throw new Error(`${kind} selector must not be empty`);
|
|
690
|
+
if (!isSelectorKind(kind)) {
|
|
691
|
+
throw new Error(`unknown selector kind \`${kind}\`; expected line, hash, symbol, page, node, or asset`);
|
|
692
|
+
}
|
|
693
|
+
if (kind === "line" || kind === "hash") {
|
|
694
|
+
const parts = value.split(":");
|
|
695
|
+
if (parts.length > 2)
|
|
696
|
+
throw new Error("selector has too many components");
|
|
697
|
+
if (parts.length === 2) {
|
|
698
|
+
const column = Number(parts[1]);
|
|
699
|
+
if (!Number.isSafeInteger(column))
|
|
700
|
+
throw new Error(`invalid column \`${parts[1]}\``);
|
|
701
|
+
if (column < 1)
|
|
702
|
+
throw new Error("column must be greater than zero");
|
|
703
|
+
return { kind, value: parts[0], column };
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if (kind === "page") {
|
|
707
|
+
if (value.includes(":"))
|
|
708
|
+
throw new Error("page selector has too many components");
|
|
709
|
+
const page = Number(value);
|
|
710
|
+
if (!Number.isSafeInteger(page))
|
|
711
|
+
throw new Error(`invalid page \`${value}\``);
|
|
712
|
+
if (page < 1)
|
|
713
|
+
throw new Error("page must be greater than zero");
|
|
714
|
+
}
|
|
715
|
+
return { kind, value };
|
|
716
|
+
}
|
|
717
|
+
function buildInspectArgs(input) {
|
|
718
|
+
validateTarget("inspect", input.path, input.stdinName);
|
|
719
|
+
if (input.selector !== undefined && input.selector.length === 0) {
|
|
720
|
+
throw new Error("selector must not be empty");
|
|
721
|
+
}
|
|
646
722
|
const vision = validateVisionLevelVsMode({
|
|
647
723
|
visionMode: input.visionMode,
|
|
648
724
|
visionLevel: input.visionLevel
|
|
649
725
|
});
|
|
650
726
|
if (!vision.ok)
|
|
651
727
|
throw new Error(vision.error);
|
|
728
|
+
const facets = selectedInspectFacets(input.select);
|
|
729
|
+
const hasContent = hasInspectFacet(facets, "content");
|
|
730
|
+
const hasIdentity = hasInspectFacet(facets, "identity");
|
|
731
|
+
const hasMap = hasInspectFacet(facets, "map");
|
|
732
|
+
const hasSourceFacet = hasMap || hasInspectFacet(facets, "diagnostics") || hasIdentity;
|
|
733
|
+
if (input.end !== undefined)
|
|
734
|
+
validatePositiveInteger(input.end, "end");
|
|
735
|
+
if (input.limit !== undefined)
|
|
736
|
+
validatePositiveInteger(input.limit, "limit");
|
|
737
|
+
if (input.end !== undefined && input.limit !== undefined) {
|
|
738
|
+
throw new Error("cannot combine end with limit");
|
|
739
|
+
}
|
|
740
|
+
if (input.depth !== undefined) {
|
|
741
|
+
if (!Number.isSafeInteger(input.depth) || input.depth < 0) {
|
|
742
|
+
throw new Error("depth must be a non-negative integer");
|
|
743
|
+
}
|
|
744
|
+
if (!hasMap)
|
|
745
|
+
throw new Error("depth requires the map facet");
|
|
746
|
+
}
|
|
747
|
+
if (input.selector !== undefined && input.query !== undefined) {
|
|
748
|
+
throw new Error("cannot combine selector with query");
|
|
749
|
+
}
|
|
750
|
+
if (!hasContent && (input.end !== undefined || input.limit !== undefined || input.query !== undefined)) {
|
|
751
|
+
throw new Error("ranges and queries require the content facet");
|
|
752
|
+
}
|
|
753
|
+
if (hasIdentity && input.selector === undefined) {
|
|
754
|
+
throw new Error("the identity facet requires a selector");
|
|
755
|
+
}
|
|
756
|
+
if (input.selector !== undefined && !hasContent && !hasIdentity) {
|
|
757
|
+
throw new Error("selectors require the content or identity facet");
|
|
758
|
+
}
|
|
759
|
+
if (input.selector !== undefined) {
|
|
760
|
+
const selector = parseSelector(input.selector);
|
|
761
|
+
if (selector.kind === "symbol" && hasIdentity) {
|
|
762
|
+
throw new Error("the identity facet does not accept symbol selectors");
|
|
763
|
+
}
|
|
764
|
+
if (selector.column !== undefined && !hasIdentity) {
|
|
765
|
+
throw new Error("selector columns require the identity facet");
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
const hasVision = input.visionMode !== undefined || input.visionLevel !== undefined;
|
|
769
|
+
if (hasVision && !hasContent) {
|
|
770
|
+
throw new Error("vision options require the content facet");
|
|
771
|
+
}
|
|
772
|
+
if (hasVision && hasSourceFacet) {
|
|
773
|
+
throw new Error("vision options cannot be combined with source facets");
|
|
774
|
+
}
|
|
652
775
|
const args = [];
|
|
653
776
|
if (input.readseekDir !== undefined)
|
|
654
777
|
args.push("--readseek-dir", input.readseekDir);
|
|
655
|
-
args.push("
|
|
778
|
+
args.push("inspect", input.path);
|
|
779
|
+
if (input.selector !== undefined)
|
|
780
|
+
args.push(input.selector);
|
|
656
781
|
const select = selectArg(input.select);
|
|
657
782
|
if (select !== undefined)
|
|
658
783
|
args.push("--select", select);
|
|
659
|
-
pushOptionalString(args, "--
|
|
784
|
+
pushOptionalString(args, "--query", input.query);
|
|
660
785
|
pushOptionalNumber(args, "--end", input.end);
|
|
661
786
|
pushOptionalNumber(args, "--limit", input.limit);
|
|
787
|
+
pushOptionalNumber(args, "--depth", input.depth);
|
|
662
788
|
pushOptionalString(args, "--language", input.language);
|
|
663
789
|
pushOptionalString(args, "--vision-mode", input.visionMode);
|
|
664
790
|
pushOptionalString(args, "--vision-level", input.visionLevel);
|
|
665
791
|
pushOptionalString(args, "--stdin-name", input.stdinName);
|
|
792
|
+
args.push("--kdl");
|
|
666
793
|
return args;
|
|
667
794
|
}
|
|
668
|
-
function
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
795
|
+
function buildExtractArgs(input) {
|
|
796
|
+
validateTarget("extract", input.path, input.stdinName);
|
|
797
|
+
if (input.selector.length === 0)
|
|
798
|
+
throw new Error("extract requires an asset selector");
|
|
799
|
+
if (!input.selector.startsWith("asset:") || input.selector === "asset:") {
|
|
800
|
+
throw new Error("extract requires an asset: selector");
|
|
801
|
+
}
|
|
802
|
+
if (input.output.length === 0 || input.output === "-") {
|
|
803
|
+
throw new Error("extract output must be a file path");
|
|
804
|
+
}
|
|
676
805
|
const args = [];
|
|
677
806
|
if (input.readseekDir !== undefined)
|
|
678
807
|
args.push("--readseek-dir", input.readseekDir);
|
|
679
|
-
args.push("
|
|
680
|
-
|
|
681
|
-
args.push("--format", input.format);
|
|
682
|
-
pushOptionalString(args, "--node", input.node);
|
|
683
|
-
pushOptionalNumber(args, "--page", input.page);
|
|
684
|
-
pushOptionalString(args, "--kind", input.kind);
|
|
685
|
-
pushOptionalNumber(args, "--depth", input.depth);
|
|
686
|
-
if (input.outline)
|
|
687
|
-
args.push("--outline");
|
|
688
|
-
pushOptionalString(args, "--vision-mode", input.visionMode);
|
|
689
|
-
pushOptionalString(args, "--vision-level", input.visionLevel);
|
|
808
|
+
args.push("--output", input.output, "extract", input.path, input.selector);
|
|
809
|
+
pushOptionalString(args, "--stdin-name", input.stdinName);
|
|
690
810
|
return args;
|
|
691
811
|
}
|
|
692
812
|
function buildSearchArgs(input) {
|
|
@@ -694,15 +814,17 @@ function buildSearchArgs(input) {
|
|
|
694
814
|
pushOptionalString(args, "--path", input.path);
|
|
695
815
|
pushOptionalString(args, "--language", input.language);
|
|
696
816
|
pushOptionalNumber(args, "--limit", input.limit);
|
|
697
|
-
|
|
817
|
+
appendGitScopeArgs(args, input);
|
|
818
|
+
args.push("--kdl");
|
|
819
|
+
return args;
|
|
698
820
|
}
|
|
699
821
|
function buildDefArgs(input) {
|
|
700
822
|
const args = ["def", input.name];
|
|
701
823
|
pushOptionalString(args, "--path", input.path);
|
|
702
|
-
if (input.format !== undefined)
|
|
703
|
-
args.push("--format", input.format);
|
|
704
824
|
pushOptionalString(args, "--language", input.language);
|
|
705
|
-
|
|
825
|
+
appendGitScopeArgs(args, input);
|
|
826
|
+
args.push("--kdl");
|
|
827
|
+
return args;
|
|
706
828
|
}
|
|
707
829
|
function buildRefsArgs(input) {
|
|
708
830
|
const validated = validateRefsNameVsScope(input);
|
|
@@ -717,10 +839,10 @@ function buildRefsArgs(input) {
|
|
|
717
839
|
pushOptionalNumber(args, "--line", input.line);
|
|
718
840
|
pushOptionalNumber(args, "--column", input.column);
|
|
719
841
|
}
|
|
720
|
-
if (input.format !== undefined)
|
|
721
|
-
args.push("--format", input.format);
|
|
722
842
|
pushOptionalString(args, "--language", input.language);
|
|
723
|
-
|
|
843
|
+
appendGitScopeArgs(args, input);
|
|
844
|
+
args.push("--kdl");
|
|
845
|
+
return args;
|
|
724
846
|
}
|
|
725
847
|
function buildEditArgs(input) {
|
|
726
848
|
const args = ["edit", input.path];
|
|
@@ -732,6 +854,7 @@ function buildEditArgs(input) {
|
|
|
732
854
|
pushOptionalString(args, "--language", input.language);
|
|
733
855
|
if (input.requestFromStdin)
|
|
734
856
|
args.push("--edits", "-");
|
|
857
|
+
args.push("--kdl");
|
|
735
858
|
return args;
|
|
736
859
|
}
|
|
737
860
|
function buildRenameArgs(input) {
|
|
@@ -744,7 +867,9 @@ function buildRenameArgs(input) {
|
|
|
744
867
|
flagOrder: input.flagOrder ?? "plan-hash-first"
|
|
745
868
|
});
|
|
746
869
|
pushOptionalString(args, "--language", input.language);
|
|
747
|
-
|
|
870
|
+
appendGitScopeArgs(args, input);
|
|
871
|
+
args.push("--kdl");
|
|
872
|
+
return args;
|
|
748
873
|
}
|
|
749
874
|
// ../readseek-api/src/platform.ts
|
|
750
875
|
import { accessSync, constants as fsConstants, existsSync, statSync } from "node:fs";
|
|
@@ -760,6 +885,8 @@ var READSEEK_PLATFORM_PACKAGES = {
|
|
|
760
885
|
};
|
|
761
886
|
var resolveFromPackage = createRequire(import.meta.url).resolve;
|
|
762
887
|
function readSeekBinaryName(platform = process.platform) {
|
|
888
|
+
if (platform === "linux")
|
|
889
|
+
return "readseek.AppImage";
|
|
763
890
|
return platform === "win32" ? "readseek.exe" : "readseek";
|
|
764
891
|
}
|
|
765
892
|
function readSeekPlatformKey(platform = process.platform, arch = process.arch) {
|
|
@@ -791,9 +918,9 @@ function resolveReadSeekBinaryFromPackages(resolver = resolveFromPackage, option
|
|
|
791
918
|
const platformPackage = READSEEK_PLATFORM_PACKAGES[key];
|
|
792
919
|
if (!platformPackage) {
|
|
793
920
|
const supported = Object.keys(READSEEK_PLATFORM_PACKAGES).join(", ");
|
|
794
|
-
throw new Error(`@jarkkojs/readseek ships no binary for ${key}; it supports ${supported}`);
|
|
921
|
+
throw new Error(`@jarkkojs/readseek-api ships no binary for ${key}; it supports ${supported}`);
|
|
795
922
|
}
|
|
796
|
-
const metaPackage = options.metaPackage ?? "@jarkkojs/readseek";
|
|
923
|
+
const metaPackage = options.metaPackage ?? "@jarkkojs/readseek-api";
|
|
797
924
|
const metaPackageJson = resolve(`${metaPackage}/package.json`);
|
|
798
925
|
const metaPackageDir = path.dirname(metaPackageJson);
|
|
799
926
|
let packageJsonPath;
|
|
@@ -839,22 +966,304 @@ function isSubPath(parent, candidate) {
|
|
|
839
966
|
const relative = path2.relative(parent, candidate);
|
|
840
967
|
return relative === "" || !relative.startsWith(`..${path2.sep}`) && relative !== ".." && !path2.isAbsolute(relative);
|
|
841
968
|
}
|
|
842
|
-
// ../readseek-api/src/
|
|
843
|
-
var READSEEK_USAGE_HINT =
|
|
969
|
+
// ../readseek-api/src/error.ts
|
|
970
|
+
var READSEEK_USAGE_HINT = /(?:^|\n)\s*Run readseek(?: [\w-]+)* --help for more information\.?\s*$/;
|
|
844
971
|
function cleanReadSeekErrorText(stderr) {
|
|
845
972
|
return stderr.replace(READSEEK_USAGE_HINT, "").replace(/^error:\s*/i, "").replace(/\s*\n\s*/g, " ").trim();
|
|
846
973
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
974
|
+
// ../readseek-api/src/kdl.ts
|
|
975
|
+
import { parse } from "@bgotink/kdl";
|
|
976
|
+
function convertNode(node) {
|
|
977
|
+
if (node.getTag() !== null)
|
|
978
|
+
throw new Error("KDL node annotations are not supported");
|
|
979
|
+
const properties = new Map;
|
|
980
|
+
for (const entry of node.entries) {
|
|
981
|
+
const name = entry.getName();
|
|
982
|
+
if (name === null)
|
|
983
|
+
throw new Error("KDL positional arguments are not supported");
|
|
984
|
+
if (properties.has(name))
|
|
985
|
+
throw new Error(`KDL duplicate property '${name}'`);
|
|
986
|
+
if (entry.getTag() !== null)
|
|
987
|
+
throw new Error("KDL value annotations are not supported");
|
|
988
|
+
const value = entry.getValue();
|
|
989
|
+
if (value === null)
|
|
990
|
+
throw new Error("KDL null values are not supported");
|
|
991
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
992
|
+
throw new Error("KDL number out of range");
|
|
993
|
+
}
|
|
994
|
+
properties.set(name, value);
|
|
995
|
+
}
|
|
996
|
+
return {
|
|
997
|
+
name: node.getName(),
|
|
998
|
+
properties,
|
|
999
|
+
children: node.children?.nodes.map(convertNode) ?? []
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
function parseKdlDocument(text) {
|
|
1003
|
+
const document = parse(text);
|
|
1004
|
+
if (document.nodes.length !== 1)
|
|
1005
|
+
throw new Error("KDL document must contain exactly one root node");
|
|
1006
|
+
return convertNode(document.nodes[0]);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// ../readseek-api/src/output-kdl.ts
|
|
1010
|
+
function properties(node) {
|
|
1011
|
+
return Object.fromEntries(node.properties);
|
|
1012
|
+
}
|
|
1013
|
+
function children(node, name) {
|
|
1014
|
+
return node.children.filter((child) => child.name === name);
|
|
1015
|
+
}
|
|
1016
|
+
function child(node, name) {
|
|
1017
|
+
return node.children.find((candidate) => candidate.name === name);
|
|
1018
|
+
}
|
|
1019
|
+
function requireProperty(node, name) {
|
|
1020
|
+
const value = node.properties.get(name);
|
|
1021
|
+
if (value === undefined)
|
|
1022
|
+
throw new Error(`invalid readseek KDL: ${node.name} is missing '${name}'`);
|
|
1023
|
+
return value;
|
|
1024
|
+
}
|
|
1025
|
+
function line(node) {
|
|
1026
|
+
const anchor = requireProperty(node, "anchor");
|
|
1027
|
+
const content = requireProperty(node, "content");
|
|
1028
|
+
if (typeof anchor !== "string" || typeof content !== "string") {
|
|
1029
|
+
throw new Error("invalid readseek KDL line");
|
|
1030
|
+
}
|
|
1031
|
+
const match = /^(\d+):([0-9a-f]+)$/.exec(anchor);
|
|
1032
|
+
if (!match)
|
|
1033
|
+
throw new Error(`invalid readseek KDL line anchor: ${anchor}`);
|
|
1034
|
+
return {
|
|
1035
|
+
line: Number(match[1]),
|
|
1036
|
+
hash: match[2],
|
|
1037
|
+
text: content
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
function mapNode(node) {
|
|
1041
|
+
return {
|
|
1042
|
+
...properties(node),
|
|
1043
|
+
children: children(node, "node").map(mapNode)
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
function sourceContent(node) {
|
|
1047
|
+
const result = properties(node);
|
|
1048
|
+
result.hashlines = children(node, "line").map(line);
|
|
1049
|
+
const objects = children(node, "object");
|
|
1050
|
+
if (objects.length > 0) {
|
|
1051
|
+
result.objects = objects.map((object) => ({
|
|
1052
|
+
label: requireProperty(object, "label"),
|
|
1053
|
+
bbox: [
|
|
1054
|
+
requireProperty(object, "xmin"),
|
|
1055
|
+
requireProperty(object, "ymin"),
|
|
1056
|
+
requireProperty(object, "xmax"),
|
|
1057
|
+
requireProperty(object, "ymax")
|
|
1058
|
+
]
|
|
1059
|
+
}));
|
|
1060
|
+
}
|
|
1061
|
+
return result;
|
|
1062
|
+
}
|
|
1063
|
+
function sourceMap(node) {
|
|
1064
|
+
const root = child(node, "node");
|
|
1065
|
+
return {
|
|
1066
|
+
...properties(node),
|
|
1067
|
+
...root ? { root: mapNode(root) } : {},
|
|
1068
|
+
symbols: children(node, "symbol").map(properties)
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
function diagnostics(node) {
|
|
1072
|
+
return {
|
|
1073
|
+
...properties(node),
|
|
1074
|
+
diagnostics: children(node, "diagnostic").map(properties)
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
function identity(node) {
|
|
1078
|
+
const result = properties(node);
|
|
1079
|
+
const identifier = child(node, "identifier");
|
|
1080
|
+
const symbol = child(node, "symbol");
|
|
1081
|
+
if (identifier)
|
|
1082
|
+
result.identifier = properties(identifier);
|
|
1083
|
+
if (symbol)
|
|
1084
|
+
result.symbol = properties(symbol);
|
|
1085
|
+
return result;
|
|
1086
|
+
}
|
|
1087
|
+
function sourceDocument(root) {
|
|
1088
|
+
const result = {};
|
|
1089
|
+
const metadata = child(root, "metadata");
|
|
1090
|
+
const cache = child(root, "cache");
|
|
1091
|
+
const content = child(root, "content");
|
|
1092
|
+
const map = child(root, "map");
|
|
1093
|
+
const diagnosticOutput = child(root, "diagnostics");
|
|
1094
|
+
const identityOutput = child(root, "identity");
|
|
1095
|
+
if (metadata)
|
|
1096
|
+
result.metadata = properties(metadata);
|
|
1097
|
+
if (cache)
|
|
1098
|
+
result.cache = properties(cache);
|
|
1099
|
+
if (content)
|
|
1100
|
+
result.content = sourceContent(content);
|
|
1101
|
+
if (map)
|
|
1102
|
+
result.map = sourceMap(map);
|
|
1103
|
+
if (diagnosticOutput)
|
|
1104
|
+
result.diagnostics = diagnostics(diagnosticOutput);
|
|
1105
|
+
if (identityOutput)
|
|
1106
|
+
result.identity = identity(identityOutput);
|
|
1107
|
+
return result;
|
|
1108
|
+
}
|
|
1109
|
+
function hit(node) {
|
|
1110
|
+
return {
|
|
1111
|
+
...properties(node),
|
|
1112
|
+
hashlines: children(node, "line").map(line),
|
|
1113
|
+
captures: children(node, "capture").map((capture) => ({
|
|
1114
|
+
...properties(capture),
|
|
1115
|
+
hashlines: children(capture, "line").map(line)
|
|
1116
|
+
}))
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
function search(root) {
|
|
1120
|
+
return {
|
|
1121
|
+
...properties(root),
|
|
1122
|
+
results: children(root, "match").map((match) => ({
|
|
1123
|
+
...properties(match),
|
|
1124
|
+
matches: children(match, "hit").map(hit)
|
|
1125
|
+
})),
|
|
1126
|
+
documents: children(root, "document").map((documentNode) => ({
|
|
1127
|
+
...properties(documentNode),
|
|
1128
|
+
hits: children(documentNode, "hit").map(properties)
|
|
1129
|
+
}))
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
function references(root) {
|
|
1133
|
+
const parsed = children(root, "reference").map((reference) => {
|
|
1134
|
+
const sourceLine = child(reference, "line");
|
|
1135
|
+
if (!sourceLine)
|
|
1136
|
+
throw new Error("invalid readseek KDL reference: missing line");
|
|
1137
|
+
const parsedLine = line(sourceLine);
|
|
1138
|
+
const symbol = child(reference, "symbol");
|
|
1139
|
+
return {
|
|
1140
|
+
...properties(reference),
|
|
1141
|
+
line_hash: parsedLine.hash,
|
|
1142
|
+
text: parsedLine.text,
|
|
1143
|
+
...symbol ? { symbol: properties(symbol) } : {}
|
|
1144
|
+
};
|
|
1145
|
+
});
|
|
1146
|
+
return {
|
|
1147
|
+
references: parsed,
|
|
1148
|
+
locations: parsed.map((reference) => {
|
|
1149
|
+
const { symbol, ...location } = reference;
|
|
1150
|
+
return symbol && typeof symbol === "object" ? { ...location, ...symbol } : location;
|
|
1151
|
+
})
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
function definitions(root) {
|
|
1155
|
+
return {
|
|
1156
|
+
locations: children(root, "definition").map((definition) => {
|
|
1157
|
+
const symbol = child(definition, "symbol");
|
|
1158
|
+
const sourceLine = child(definition, "line");
|
|
1159
|
+
if (!symbol || !sourceLine) {
|
|
1160
|
+
throw new Error("invalid readseek KDL definition: missing symbol or line");
|
|
1161
|
+
}
|
|
1162
|
+
const symbolProperties = properties(symbol);
|
|
1163
|
+
const parsedLine = line(sourceLine);
|
|
1164
|
+
const definitionColumn = definition.properties.get("column");
|
|
1165
|
+
const nameByte = symbol.properties.get("name_byte");
|
|
1166
|
+
const startByte = symbol.properties.get("start_byte");
|
|
1167
|
+
let column = 1;
|
|
1168
|
+
if (typeof definitionColumn === "number") {
|
|
1169
|
+
column = definitionColumn;
|
|
1170
|
+
} else if (typeof nameByte === "number" && typeof startByte === "number") {
|
|
1171
|
+
column = nameByte - startByte + 1;
|
|
1172
|
+
}
|
|
1173
|
+
return {
|
|
1174
|
+
file: requireProperty(definition, "file"),
|
|
1175
|
+
line: parsedLine.line,
|
|
1176
|
+
column,
|
|
1177
|
+
line_hash: parsedLine.hash,
|
|
1178
|
+
text: parsedLine.text,
|
|
1179
|
+
kind: symbolProperties.kind,
|
|
1180
|
+
name: symbolProperties.name,
|
|
1181
|
+
qualified_name: symbolProperties.qualified_name
|
|
1182
|
+
};
|
|
1183
|
+
})
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
function edit(root) {
|
|
1187
|
+
const result = properties(root);
|
|
1188
|
+
for (const name of ["before_content", "content"]) {
|
|
1189
|
+
const contentNode = child(root, name);
|
|
1190
|
+
if (contentNode)
|
|
1191
|
+
result[name] = requireProperty(contentNode, "content");
|
|
1192
|
+
}
|
|
1193
|
+
return result;
|
|
1194
|
+
}
|
|
1195
|
+
function renameEdit(node) {
|
|
1196
|
+
const sourceLine = child(node, "line");
|
|
1197
|
+
if (!sourceLine)
|
|
1198
|
+
throw new Error("invalid readseek KDL rename edit: missing line");
|
|
1199
|
+
return {
|
|
1200
|
+
...properties(node),
|
|
1201
|
+
text: requireProperty(sourceLine, "content")
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1204
|
+
function renameFile(node) {
|
|
1205
|
+
return {
|
|
1206
|
+
...properties(node),
|
|
1207
|
+
edits: children(node, "edit").map(renameEdit),
|
|
1208
|
+
conflicts: children(node, "conflict").map(properties)
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
function rename(root) {
|
|
1212
|
+
return {
|
|
1213
|
+
...renameFile(root),
|
|
1214
|
+
others: children(root, "other").map(renameFile)
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
function genericNode(node) {
|
|
1218
|
+
const result = properties(node);
|
|
1219
|
+
const textNode = child(node, "text");
|
|
1220
|
+
const bbox = child(node, "bbox");
|
|
1221
|
+
if (textNode)
|
|
1222
|
+
result.text = requireProperty(textNode, "content");
|
|
1223
|
+
if (bbox)
|
|
1224
|
+
result.bbox = properties(bbox);
|
|
1225
|
+
const objects = children(node, "object");
|
|
1226
|
+
if (objects.length > 0)
|
|
1227
|
+
result.objects = objects.map(genericNode);
|
|
1228
|
+
return result;
|
|
1229
|
+
}
|
|
1230
|
+
function pdfDocument(root) {
|
|
1231
|
+
return {
|
|
1232
|
+
...properties(root),
|
|
1233
|
+
nodes: children(root, "node").map(genericNode),
|
|
1234
|
+
assets: children(root, "asset").map(genericNode),
|
|
1235
|
+
vision: children(root, "vision").map(genericNode)
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
function parseReadSeekKdl(text) {
|
|
1239
|
+
const root = parseKdlDocument(text);
|
|
1240
|
+
switch (root.name) {
|
|
1241
|
+
case "document":
|
|
1242
|
+
return root.properties.has("format") ? pdfDocument(root) : sourceDocument(root);
|
|
1243
|
+
case "search":
|
|
1244
|
+
return search(root);
|
|
1245
|
+
case "check":
|
|
1246
|
+
return diagnostics(root);
|
|
1247
|
+
case "refs":
|
|
1248
|
+
return references(root);
|
|
1249
|
+
case "def":
|
|
1250
|
+
return definitions(root);
|
|
1251
|
+
case "edit":
|
|
1252
|
+
return edit(root);
|
|
1253
|
+
case "edit-batch":
|
|
1254
|
+
return { ...properties(root), files: children(root, "file").map(edit) };
|
|
1255
|
+
case "rename":
|
|
1256
|
+
return rename(root);
|
|
1257
|
+
default:
|
|
1258
|
+
throw new Error(`unsupported readseek KDL root: ${root.name}`);
|
|
853
1259
|
}
|
|
854
1260
|
}
|
|
855
1261
|
// ../readseek-api/src/process.ts
|
|
856
1262
|
var DEFAULT_READSEEK_TIMEOUT_MS = 120000;
|
|
857
1263
|
var DEFAULT_MAX_OUTPUT_BYTES = 32 * 1024 * 1024;
|
|
1264
|
+
function readSeekProcessEnvironment() {
|
|
1265
|
+
return process.platform === "linux" ? { ...process.env, APPIMAGE_EXTRACT_AND_RUN: "1" } : process.env;
|
|
1266
|
+
}
|
|
858
1267
|
function resolveProcessLimits(options = {}) {
|
|
859
1268
|
return {
|
|
860
1269
|
timeoutMs: options.timeoutMs ?? DEFAULT_READSEEK_TIMEOUT_MS,
|
|
@@ -885,8 +1294,8 @@ function ensureSuccessfulStdout(result) {
|
|
|
885
1294
|
return result.stdout;
|
|
886
1295
|
throw failureFromProcessResult(result);
|
|
887
1296
|
}
|
|
888
|
-
function
|
|
889
|
-
return
|
|
1297
|
+
function parseSuccessfulKdl(result) {
|
|
1298
|
+
return parseReadSeekKdl(ensureSuccessfulStdout(result));
|
|
890
1299
|
}
|
|
891
1300
|
// ../readseek-api/src/process-bun.ts
|
|
892
1301
|
function getBunSpawn() {
|
|
@@ -936,7 +1345,7 @@ async function runReadSeekBun(binaryPath, args, options = {}) {
|
|
|
936
1345
|
const stdin = options.stdin === undefined ? undefined : new TextEncoder().encode(options.stdin);
|
|
937
1346
|
const spawnOptions = {
|
|
938
1347
|
cwd: options.cwd,
|
|
939
|
-
env:
|
|
1348
|
+
env: readSeekProcessEnvironment(),
|
|
940
1349
|
stderr: "pipe",
|
|
941
1350
|
stdin,
|
|
942
1351
|
stdout: "pipe"
|
|
@@ -970,26 +1379,254 @@ async function runReadSeekBun(binaryPath, args, options = {}) {
|
|
|
970
1379
|
} finally {
|
|
971
1380
|
if (timeout !== undefined)
|
|
972
1381
|
clearTimeout(timeout);
|
|
1382
|
+
pending.catch(() => {
|
|
1383
|
+
return;
|
|
1384
|
+
});
|
|
973
1385
|
}
|
|
974
1386
|
}
|
|
975
1387
|
async function runReadSeekBunText(binaryPath, args, options = {}) {
|
|
976
1388
|
return ensureSuccessfulStdout(await runReadSeekBun(binaryPath, args, options));
|
|
977
1389
|
}
|
|
978
|
-
async function
|
|
979
|
-
return
|
|
980
|
-
}
|
|
981
|
-
// src/
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
var
|
|
985
|
-
var
|
|
986
|
-
var
|
|
987
|
-
var
|
|
988
|
-
var
|
|
989
|
-
var
|
|
990
|
-
var
|
|
991
|
-
var
|
|
992
|
-
|
|
1390
|
+
async function runReadSeekBunKdl(binaryPath, args, options = {}) {
|
|
1391
|
+
return parseSuccessfulKdl(await runReadSeekBun(binaryPath, args, options));
|
|
1392
|
+
}
|
|
1393
|
+
// ../readseek-api/src/client-output.ts
|
|
1394
|
+
import { Type } from "@sinclair/typebox";
|
|
1395
|
+
import { AssertError, Value } from "@sinclair/typebox/value";
|
|
1396
|
+
var Int = Type.Integer({ minimum: Number.MIN_SAFE_INTEGER, maximum: Number.MAX_SAFE_INTEGER });
|
|
1397
|
+
var Str = Type.String();
|
|
1398
|
+
var Bool = Type.Boolean();
|
|
1399
|
+
var OptStr = Type.Optional(Str);
|
|
1400
|
+
var OptInt = Type.Optional(Int);
|
|
1401
|
+
var Hashline = Type.Object({ line: Int, hash: Str, text: Str });
|
|
1402
|
+
var Hashlines = Type.Array(Hashline);
|
|
1403
|
+
var ReadOutput = Type.Object({
|
|
1404
|
+
file: Str,
|
|
1405
|
+
language: Str,
|
|
1406
|
+
line_count: Int,
|
|
1407
|
+
file_hash: Str,
|
|
1408
|
+
start_line: Int,
|
|
1409
|
+
end_line: Int,
|
|
1410
|
+
hashlines: Hashlines
|
|
1411
|
+
});
|
|
1412
|
+
var SymbolOutput = Type.Object({
|
|
1413
|
+
kind: Str,
|
|
1414
|
+
name: Str,
|
|
1415
|
+
qualified_name: OptStr,
|
|
1416
|
+
start_line: Int,
|
|
1417
|
+
end_line: Int,
|
|
1418
|
+
start_hash: OptStr,
|
|
1419
|
+
end_hash: OptStr,
|
|
1420
|
+
start_anchor: OptStr,
|
|
1421
|
+
end_anchor: OptStr
|
|
1422
|
+
});
|
|
1423
|
+
var MapNode = Type.Recursive((Self) => Type.Object({
|
|
1424
|
+
kind: Str,
|
|
1425
|
+
name: Str,
|
|
1426
|
+
start_line: Int,
|
|
1427
|
+
end_line: Int,
|
|
1428
|
+
start_anchor: Str,
|
|
1429
|
+
end_anchor: Str,
|
|
1430
|
+
children: Type.Array(Self)
|
|
1431
|
+
}));
|
|
1432
|
+
var MapOutput = Type.Object({
|
|
1433
|
+
file: Str,
|
|
1434
|
+
language: Str,
|
|
1435
|
+
line_count: Int,
|
|
1436
|
+
file_hash: Str,
|
|
1437
|
+
root: Type.Optional(MapNode),
|
|
1438
|
+
symbols: Type.Array(SymbolOutput)
|
|
1439
|
+
});
|
|
1440
|
+
var SearchSpan = {
|
|
1441
|
+
start_byte: OptInt,
|
|
1442
|
+
end_byte: OptInt,
|
|
1443
|
+
text: OptStr,
|
|
1444
|
+
start_line: Int,
|
|
1445
|
+
end_line: Int,
|
|
1446
|
+
start_hash: Str,
|
|
1447
|
+
end_hash: Str,
|
|
1448
|
+
hashlines: Hashlines
|
|
1449
|
+
};
|
|
1450
|
+
var SearchCapture = Type.Object({ name: Str, ...SearchSpan });
|
|
1451
|
+
var SearchMatch = Type.Object({
|
|
1452
|
+
pattern_index: Type.Integer({ default: 0 }),
|
|
1453
|
+
...SearchSpan,
|
|
1454
|
+
captures: Type.Array(SearchCapture)
|
|
1455
|
+
});
|
|
1456
|
+
var SearchFileOutput = Type.Object({
|
|
1457
|
+
file: Str,
|
|
1458
|
+
language: Str,
|
|
1459
|
+
file_hash: Str,
|
|
1460
|
+
matches: Type.Array(SearchMatch)
|
|
1461
|
+
});
|
|
1462
|
+
var SearchEnvelope = Type.Object({
|
|
1463
|
+
results: Type.Array(SearchFileOutput),
|
|
1464
|
+
returned_matches: OptInt,
|
|
1465
|
+
truncated: Type.Optional(Bool),
|
|
1466
|
+
byte_basis: OptStr
|
|
1467
|
+
});
|
|
1468
|
+
var RefsEnvelope = Type.Object({
|
|
1469
|
+
references: Type.Array(Type.Object({
|
|
1470
|
+
file: Str,
|
|
1471
|
+
line: Int,
|
|
1472
|
+
column: Int,
|
|
1473
|
+
line_hash: Str,
|
|
1474
|
+
text: Str,
|
|
1475
|
+
symbol: Type.Optional(Type.Object({ qualified_name: OptStr }))
|
|
1476
|
+
}))
|
|
1477
|
+
});
|
|
1478
|
+
var Diagnostic = Type.Object({
|
|
1479
|
+
kind: Type.Union([Type.Literal("error"), Type.Literal("missing")]),
|
|
1480
|
+
start_line: Int,
|
|
1481
|
+
end_line: Int
|
|
1482
|
+
});
|
|
1483
|
+
var CheckEnvelope = Type.Object({
|
|
1484
|
+
file_hash: OptStr,
|
|
1485
|
+
error_count: Int,
|
|
1486
|
+
missing_count: Int,
|
|
1487
|
+
diagnostics: Type.Array(Diagnostic)
|
|
1488
|
+
});
|
|
1489
|
+
var DetectedObject = Type.Object({
|
|
1490
|
+
label: Str,
|
|
1491
|
+
bbox: Type.Tuple([Int, Int, Int, Int])
|
|
1492
|
+
});
|
|
1493
|
+
var PreparedImage = Type.Object({
|
|
1494
|
+
mime: Str,
|
|
1495
|
+
encoding: Type.Literal("base64"),
|
|
1496
|
+
data: Str
|
|
1497
|
+
});
|
|
1498
|
+
var SourceDetection = Type.Object({
|
|
1499
|
+
kind: Type.Literal("source"),
|
|
1500
|
+
type: Str,
|
|
1501
|
+
file: Str,
|
|
1502
|
+
language: Str,
|
|
1503
|
+
engine: OptStr,
|
|
1504
|
+
supported: Bool,
|
|
1505
|
+
mime: OptStr,
|
|
1506
|
+
syntax: OptStr
|
|
1507
|
+
});
|
|
1508
|
+
var ImageDetection = Type.Object({
|
|
1509
|
+
kind: Type.Literal("image"),
|
|
1510
|
+
type: Str,
|
|
1511
|
+
file: Str,
|
|
1512
|
+
mime: OptStr,
|
|
1513
|
+
format: Str,
|
|
1514
|
+
width: Int,
|
|
1515
|
+
height: Int,
|
|
1516
|
+
animated: Bool,
|
|
1517
|
+
encoding: Type.Optional(Type.Literal("base64")),
|
|
1518
|
+
data: OptStr,
|
|
1519
|
+
ocr: OptStr,
|
|
1520
|
+
caption: OptStr,
|
|
1521
|
+
objects: Type.Optional(Type.Array(DetectedObject))
|
|
1522
|
+
});
|
|
1523
|
+
var TextDetection = Type.Object({
|
|
1524
|
+
kind: Type.Literal("text"),
|
|
1525
|
+
type: Str,
|
|
1526
|
+
file: Str,
|
|
1527
|
+
mime: OptStr
|
|
1528
|
+
});
|
|
1529
|
+
var PdfDetection = Type.Object({
|
|
1530
|
+
kind: Type.Literal("pdf"),
|
|
1531
|
+
type: Type.Literal("application/pdf"),
|
|
1532
|
+
file: Str,
|
|
1533
|
+
mime: OptStr,
|
|
1534
|
+
format: Type.Literal("pdf"),
|
|
1535
|
+
pages: Int
|
|
1536
|
+
});
|
|
1537
|
+
var Detection = Type.Union([SourceDetection, ImageDetection, TextDetection, PdfDetection]);
|
|
1538
|
+
var SourceDetectionInput = Type.Omit(SourceDetection, ["kind"]);
|
|
1539
|
+
var ImageDetectionInput = Type.Omit(ImageDetection, ["kind"]);
|
|
1540
|
+
var TextDetectionInput = Type.Omit(TextDetection, ["kind"]);
|
|
1541
|
+
var PdfDetectionInput = Type.Omit(PdfDetection, ["kind"]);
|
|
1542
|
+
var UnspannedContent = Type.Omit(ReadOutput, ["start_line", "end_line"]);
|
|
1543
|
+
var PdfInspectMetadata = Type.Object({ source: Str, format: Type.Literal("pdf"), pages: Int });
|
|
1544
|
+
var DocumentCache = Type.Object({
|
|
1545
|
+
content_hash: Str,
|
|
1546
|
+
state: Type.Union([Type.Literal("built"), Type.Literal("reused")])
|
|
1547
|
+
});
|
|
1548
|
+
var Identifier = Type.Object({
|
|
1549
|
+
text: Str,
|
|
1550
|
+
start_column: Int,
|
|
1551
|
+
end_column: Int,
|
|
1552
|
+
start_byte: Int,
|
|
1553
|
+
end_byte: Int
|
|
1554
|
+
});
|
|
1555
|
+
var IdentifyEnvelope = Type.Object({
|
|
1556
|
+
file: Str,
|
|
1557
|
+
language: Str,
|
|
1558
|
+
engine: OptStr,
|
|
1559
|
+
line_count: Int,
|
|
1560
|
+
file_hash: Str,
|
|
1561
|
+
line: Int,
|
|
1562
|
+
column: Int,
|
|
1563
|
+
line_hash: Str,
|
|
1564
|
+
identifier: Type.Optional(Identifier),
|
|
1565
|
+
symbol: Type.Optional(Type.Object({
|
|
1566
|
+
name: Str,
|
|
1567
|
+
kind: Str,
|
|
1568
|
+
qualified_name: Str,
|
|
1569
|
+
start_line: Int,
|
|
1570
|
+
end_line: Int
|
|
1571
|
+
}))
|
|
1572
|
+
});
|
|
1573
|
+
var EditOutputSchema = Type.Object({
|
|
1574
|
+
file: Str,
|
|
1575
|
+
before_hash: Str,
|
|
1576
|
+
file_hash: Str,
|
|
1577
|
+
plan_hash: Str,
|
|
1578
|
+
applied: Bool,
|
|
1579
|
+
changed: Bool,
|
|
1580
|
+
edit_count: Int,
|
|
1581
|
+
before_content: OptStr,
|
|
1582
|
+
content: OptStr
|
|
1583
|
+
});
|
|
1584
|
+
var RenameConflictSchema = Type.Object({ line: Int, column: Int, reason: Str });
|
|
1585
|
+
var RenameEditSchema = Type.Object({
|
|
1586
|
+
line: Int,
|
|
1587
|
+
start_column: Int,
|
|
1588
|
+
end_column: Int,
|
|
1589
|
+
start_byte: Int,
|
|
1590
|
+
end_byte: Int,
|
|
1591
|
+
occurrence: Str,
|
|
1592
|
+
line_hash: Str,
|
|
1593
|
+
text: Str
|
|
1594
|
+
});
|
|
1595
|
+
var Conflicts = Type.Array(RenameConflictSchema, { default: [] });
|
|
1596
|
+
var Edits = Type.Array(RenameEditSchema, { default: [] });
|
|
1597
|
+
var RenameFileOutputSchema = Type.Object({
|
|
1598
|
+
file: Str,
|
|
1599
|
+
language: Str,
|
|
1600
|
+
engine: OptStr,
|
|
1601
|
+
file_hash: Str,
|
|
1602
|
+
conflicts: Conflicts,
|
|
1603
|
+
edits: Edits
|
|
1604
|
+
});
|
|
1605
|
+
var RenameOutputSchema = Type.Object({
|
|
1606
|
+
file: Str,
|
|
1607
|
+
language: Str,
|
|
1608
|
+
engine: OptStr,
|
|
1609
|
+
file_hash: Str,
|
|
1610
|
+
old_name: Str,
|
|
1611
|
+
new_name: Str,
|
|
1612
|
+
plan_hash: Str,
|
|
1613
|
+
applied: Bool,
|
|
1614
|
+
conflicts: Conflicts,
|
|
1615
|
+
edits: Edits,
|
|
1616
|
+
others: Type.Array(RenameFileOutputSchema, { default: [] })
|
|
1617
|
+
});
|
|
1618
|
+
var DefEnvelope = Type.Object({
|
|
1619
|
+
locations: Type.Array(Type.Object({
|
|
1620
|
+
file: Str,
|
|
1621
|
+
line: Int,
|
|
1622
|
+
column: Int,
|
|
1623
|
+
line_hash: Str,
|
|
1624
|
+
text: Str,
|
|
1625
|
+
kind: OptStr,
|
|
1626
|
+
name: OptStr,
|
|
1627
|
+
qualified_name: OptStr
|
|
1628
|
+
}))
|
|
1629
|
+
});
|
|
993
1630
|
// src/util.ts
|
|
994
1631
|
import path3 from "node:path";
|
|
995
1632
|
function render(value) {
|
|
@@ -1025,15 +1662,17 @@ function collectAnchoredFiles(value, files) {
|
|
|
1025
1662
|
for (const item of Object.values(output))
|
|
1026
1663
|
collectAnchoredFiles(item, files);
|
|
1027
1664
|
}
|
|
1028
|
-
function renameFiles(value) {
|
|
1665
|
+
function renameFiles(value, directory = process.cwd()) {
|
|
1029
1666
|
const output = record(value);
|
|
1030
1667
|
const files = [];
|
|
1031
|
-
if (typeof output.file === "string" && items(output.edits).length > 0)
|
|
1032
|
-
files.push(path3.resolve(output.file));
|
|
1668
|
+
if (typeof output.file === "string" && items(output.edits).length > 0) {
|
|
1669
|
+
files.push(path3.resolve(directory, output.file));
|
|
1670
|
+
}
|
|
1033
1671
|
for (const other of items(output.others)) {
|
|
1034
1672
|
const item = record(other);
|
|
1035
|
-
if (typeof item.file === "string" && items(item.edits).length > 0)
|
|
1036
|
-
files.push(path3.resolve(item.file));
|
|
1673
|
+
if (typeof item.file === "string" && items(item.edits).length > 0) {
|
|
1674
|
+
files.push(path3.resolve(directory, item.file));
|
|
1675
|
+
}
|
|
1037
1676
|
}
|
|
1038
1677
|
return files;
|
|
1039
1678
|
}
|
|
@@ -1083,25 +1722,21 @@ function resolveImagePolicy(options) {
|
|
|
1083
1722
|
return value;
|
|
1084
1723
|
throw new Error('opencode-readseek imageMode must be "on", "auto", or "off"');
|
|
1085
1724
|
}
|
|
1086
|
-
function
|
|
1087
|
-
return
|
|
1725
|
+
function inspectArgs(filePath, select, options = {}) {
|
|
1726
|
+
return buildInspectArgs({ path: filePath, select, ...options });
|
|
1088
1727
|
}
|
|
1089
|
-
function
|
|
1728
|
+
function requireInspectFacet(envelope, facet) {
|
|
1090
1729
|
const value = record(envelope)[facet];
|
|
1091
1730
|
if (value === undefined || value === null || typeof value !== "object") {
|
|
1092
|
-
throw new Error(`
|
|
1731
|
+
throw new Error(`inspect returned no ${facet} facet`);
|
|
1093
1732
|
}
|
|
1094
1733
|
return value;
|
|
1095
1734
|
}
|
|
1096
|
-
function withAt(args, line, column) {
|
|
1097
|
-
args.push("--at", buildLineLocation(line, column));
|
|
1098
|
-
}
|
|
1099
1735
|
function readRangeArgs(filePath, start, end) {
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
return digestArgs(filePath, "content", extra);
|
|
1736
|
+
return inspectArgs(filePath, "content", {
|
|
1737
|
+
selector: start > 1 ? `line:${start}` : undefined,
|
|
1738
|
+
end
|
|
1739
|
+
});
|
|
1105
1740
|
}
|
|
1106
1741
|
function readSeekBinaryPath() {
|
|
1107
1742
|
return resolveReadSeekBinaryOverride() ?? resolveReadSeekBinaryFromPackages(resolveFromHost);
|
|
@@ -1130,7 +1765,7 @@ async function runReadSeekRaw(context, args, options = {}) {
|
|
|
1130
1765
|
}
|
|
1131
1766
|
async function runReadSeek(context, args, options = {}) {
|
|
1132
1767
|
context.abort.throwIfAborted();
|
|
1133
|
-
return preserveAbortReason(context.abort, options.cancelable !== false, () =>
|
|
1768
|
+
return preserveAbortReason(context.abort, options.cancelable !== false, () => runReadSeekBunKdl(readSeekBinaryPath(), args, {
|
|
1134
1769
|
cwd: context.directory,
|
|
1135
1770
|
signal: context.abort,
|
|
1136
1771
|
cancelable: options.cancelable,
|
|
@@ -1138,12 +1773,12 @@ async function runReadSeek(context, args, options = {}) {
|
|
|
1138
1773
|
timeoutMs: options.timeoutMs
|
|
1139
1774
|
}));
|
|
1140
1775
|
}
|
|
1141
|
-
async function
|
|
1142
|
-
return
|
|
1776
|
+
async function runInspectContent(context, args, options = {}) {
|
|
1777
|
+
return requireInspectFacet(await runReadSeek(context, args, options), "content");
|
|
1143
1778
|
}
|
|
1144
1779
|
async function postMutationPreview(context, args, filePath, changed) {
|
|
1145
1780
|
try {
|
|
1146
|
-
return await
|
|
1781
|
+
return await runInspectContent(context, args, {
|
|
1147
1782
|
cancelable: false,
|
|
1148
1783
|
timeoutMs: DEFAULT_READSEEK_TIMEOUT_MS
|
|
1149
1784
|
});
|
|
@@ -1172,21 +1807,11 @@ async function ensureReadSeekCache(context) {
|
|
|
1172
1807
|
if (error.code !== "ENOENT")
|
|
1173
1808
|
throw error;
|
|
1174
1809
|
}
|
|
1175
|
-
readSeekCacheInit ??= runReadSeekRaw(context, ["--readseek-dir", cacheDir, "init"]).then(() => cacheDir).finally(() => {
|
|
1810
|
+
readSeekCacheInit ??= runReadSeekRaw(context, ["--readseek-dir", cacheDir, "init"], { cancelable: false }).then(() => cacheDir).finally(() => {
|
|
1176
1811
|
readSeekCacheInit = undefined;
|
|
1177
1812
|
});
|
|
1178
1813
|
return readSeekCacheInit;
|
|
1179
1814
|
}
|
|
1180
|
-
function boundDocumentOutput(value) {
|
|
1181
|
-
const lines = value.split(`
|
|
1182
|
-
`);
|
|
1183
|
-
let output = lines.length > MAX_DOCUMENT_OUTPUT_LINES ? lines.slice(0, MAX_DOCUMENT_OUTPUT_LINES).join(`
|
|
1184
|
-
`) : value;
|
|
1185
|
-
const truncated = output !== value || Buffer.byteLength(output, "utf8") > MAX_DOCUMENT_OUTPUT_BYTES;
|
|
1186
|
-
output = utf8Prefix(output, MAX_DOCUMENT_OUTPUT_BYTES);
|
|
1187
|
-
return truncated ? `${output}
|
|
1188
|
-
[… document view truncated; narrow it with page, node, kind, or depth]` : output;
|
|
1189
|
-
}
|
|
1190
1815
|
|
|
1191
1816
|
// src/policy.ts
|
|
1192
1817
|
var OPENCODE_TOOL_ALIASES = Object.fromEntries(TOOL_NAMES.map((name) => [name, `readseek_${name}`]));
|
|
@@ -1231,12 +1856,12 @@ class SessionAnchors {
|
|
|
1231
1856
|
planRename(sessionID, output) {
|
|
1232
1857
|
if (!output || typeof output !== "object")
|
|
1233
1858
|
return;
|
|
1234
|
-
const
|
|
1235
|
-
if (
|
|
1859
|
+
const record = output;
|
|
1860
|
+
if (record.applied === true) {
|
|
1236
1861
|
this.#renamePlans.delete(sessionID);
|
|
1237
1862
|
return;
|
|
1238
1863
|
}
|
|
1239
|
-
const { file, old_name: oldName, new_name: newName, others } =
|
|
1864
|
+
const { file, old_name: oldName, new_name: newName, others } = record;
|
|
1240
1865
|
if (typeof file !== "string" || typeof oldName !== "string" || typeof newName !== "string")
|
|
1241
1866
|
return;
|
|
1242
1867
|
const files = new Set([path4.resolve(file)]);
|
|
@@ -1274,6 +1899,14 @@ ${[...paths].sort().map((filePath) => `- ${filePath}`).join(`
|
|
|
1274
1899
|
import { readFile as readFile2, stat as stat3 } from "node:fs/promises";
|
|
1275
1900
|
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
1276
1901
|
|
|
1902
|
+
// src/constants.ts
|
|
1903
|
+
var DEFAULT_VISION_TIMEOUT_MS = 30 * 60 * 1000;
|
|
1904
|
+
var DEFAULT_READ_LIMIT = 2000;
|
|
1905
|
+
var EDIT_RESULT_READ_LIMIT = 40;
|
|
1906
|
+
var MAX_PERMISSION_DIFF_BYTES = 32 * 1024;
|
|
1907
|
+
var IMAGE_MODES = [...VISION_MODES];
|
|
1908
|
+
var VISION_LEVEL_VALUES = [...VISION_LEVELS];
|
|
1909
|
+
|
|
1277
1910
|
// src/edits.ts
|
|
1278
1911
|
import { lstat, mkdir, open, readFile } from "node:fs/promises";
|
|
1279
1912
|
import path5 from "node:path";
|
|
@@ -1336,9 +1969,9 @@ async function writeHandle(handle, content) {
|
|
|
1336
1969
|
async function writeText(filePath, expected, content) {
|
|
1337
1970
|
await mkdir(path5.dirname(filePath), { recursive: true });
|
|
1338
1971
|
if (!expected.exists) {
|
|
1339
|
-
let
|
|
1972
|
+
let handle;
|
|
1340
1973
|
try {
|
|
1341
|
-
|
|
1974
|
+
handle = await open(filePath, "wx+");
|
|
1342
1975
|
} catch (error) {
|
|
1343
1976
|
if (error.code === "EEXIST") {
|
|
1344
1977
|
throw new Error(`refusing to overwrite changed file: ${filePath}`);
|
|
@@ -1346,9 +1979,9 @@ async function writeText(filePath, expected, content) {
|
|
|
1346
1979
|
throw error;
|
|
1347
1980
|
}
|
|
1348
1981
|
try {
|
|
1349
|
-
await writeHandle(
|
|
1982
|
+
await writeHandle(handle, content);
|
|
1350
1983
|
} finally {
|
|
1351
|
-
await
|
|
1984
|
+
await handle.close();
|
|
1352
1985
|
}
|
|
1353
1986
|
return;
|
|
1354
1987
|
}
|
|
@@ -1592,10 +2225,10 @@ async function authorizeEdit(context, filePaths, diff = "", authorizeExternalPat
|
|
|
1592
2225
|
import { tool } from "@opencode-ai/plugin";
|
|
1593
2226
|
function initialTitle(kind, args) {
|
|
1594
2227
|
switch (kind) {
|
|
1595
|
-
case "
|
|
1596
|
-
return `
|
|
1597
|
-
case "
|
|
1598
|
-
return `
|
|
2228
|
+
case "inspect":
|
|
2229
|
+
return `Inspect ${args.path}`;
|
|
2230
|
+
case "extract":
|
|
2231
|
+
return `Extract ${args.selector} to ${args.output}`;
|
|
1599
2232
|
case "search":
|
|
1600
2233
|
return `Search ${args.pattern}`;
|
|
1601
2234
|
case "grep":
|
|
@@ -1615,9 +2248,9 @@ function initialTitle(kind, args) {
|
|
|
1615
2248
|
function resultPresentation(kind, args, value) {
|
|
1616
2249
|
const output = record(value);
|
|
1617
2250
|
switch (kind) {
|
|
1618
|
-
case "
|
|
1619
|
-
return { title: `
|
|
1620
|
-
case "
|
|
2251
|
+
case "extract":
|
|
2252
|
+
return { title: `Extracted ${args.selector} to ${args.output}`, metadata: {} };
|
|
2253
|
+
case "inspect": {
|
|
1621
2254
|
const map = record(output.map);
|
|
1622
2255
|
const diagnostics = record(output.diagnostics);
|
|
1623
2256
|
const identity = record(output.identity);
|
|
@@ -1650,7 +2283,7 @@ function resultPresentation(kind, args, value) {
|
|
|
1650
2283
|
if (typeof content.end_line === "number")
|
|
1651
2284
|
metadata.end_line = content.end_line;
|
|
1652
2285
|
return {
|
|
1653
|
-
title: summary.length > 0 ? `
|
|
2286
|
+
title: summary.length > 0 ? `Inspected ${args.path} (${summary.join(", ")})` : `Inspected ${args.path}`,
|
|
1654
2287
|
metadata
|
|
1655
2288
|
};
|
|
1656
2289
|
}
|
|
@@ -1690,18 +2323,18 @@ function resultPresentation(kind, args, value) {
|
|
|
1690
2323
|
}
|
|
1691
2324
|
return { title: initialTitle(kind, args), metadata: {} };
|
|
1692
2325
|
}
|
|
1693
|
-
function readseekTool(description, args, kind, execute
|
|
2326
|
+
function readseekTool(description, args, kind, execute) {
|
|
1694
2327
|
return tool({
|
|
1695
2328
|
description,
|
|
1696
2329
|
args,
|
|
1697
|
-
async execute(
|
|
1698
|
-
const title = initialTitle(kind,
|
|
2330
|
+
async execute(args, context) {
|
|
2331
|
+
const title = initialTitle(kind, args);
|
|
1699
2332
|
context.metadata({ title });
|
|
1700
|
-
const result = await execute(
|
|
1701
|
-
const presentation = resultPresentation(kind,
|
|
2333
|
+
const result = await execute(args, context);
|
|
2334
|
+
const presentation = resultPresentation(kind, args, result);
|
|
1702
2335
|
return {
|
|
1703
2336
|
title: presentation.title,
|
|
1704
|
-
|
|
2337
|
+
output: render(result),
|
|
1705
2338
|
metadata: presentation.metadata
|
|
1706
2339
|
};
|
|
1707
2340
|
}
|
|
@@ -1711,51 +2344,18 @@ function readseekTool(description, args, kind, execute, formatResult = (result)
|
|
|
1711
2344
|
// src/tools.ts
|
|
1712
2345
|
function createReadSeekTools(imagePolicy) {
|
|
1713
2346
|
return {
|
|
1714
|
-
|
|
1715
|
-
path: tool2.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
1716
|
-
node: tool2.schema.string().min(1).optional().describe(PARAM_DESCRIPTIONS.node),
|
|
1717
|
-
page: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.page),
|
|
1718
|
-
kind: tool2.schema.enum(DOCUMENT_NODE_KIND_VALUES).optional().describe(PARAM_DESCRIPTIONS.kind),
|
|
1719
|
-
depth: tool2.schema.number().int().nonnegative().optional().describe(PARAM_DESCRIPTIONS.depth),
|
|
1720
|
-
outline: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.outline),
|
|
1721
|
-
visionMode: tool2.schema.enum(VISION_ANALYSIS_MODE_VALUES).optional().describe(PARAM_DESCRIPTIONS.visionModeView),
|
|
1722
|
-
visionLevel: tool2.schema.enum(VISION_LEVEL_VALUES).optional().describe(PARAM_DESCRIPTIONS.visionLevel)
|
|
1723
|
-
}, "view", async (input, context) => {
|
|
1724
|
-
const filePath = resolvePath(context.directory, input.path);
|
|
1725
|
-
await authorizeRead(context, filePath);
|
|
1726
|
-
const visionMode = input.visionMode;
|
|
1727
|
-
const visionLevel = input.visionLevel;
|
|
1728
|
-
const vision = validateVisionLevelVsMode({
|
|
1729
|
-
visionMode,
|
|
1730
|
-
visionLevel,
|
|
1731
|
-
outline: input.outline
|
|
1732
|
-
});
|
|
1733
|
-
if (!vision.ok)
|
|
1734
|
-
throw new Error(vision.error);
|
|
1735
|
-
const cacheDir = await ensureReadSeekCache(context);
|
|
1736
|
-
const args = buildViewArgs({
|
|
1737
|
-
path: filePath,
|
|
1738
|
-
node: input.node,
|
|
1739
|
-
page: input.page,
|
|
1740
|
-
kind: input.kind,
|
|
1741
|
-
depth: input.depth,
|
|
1742
|
-
outline: input.outline,
|
|
1743
|
-
visionMode,
|
|
1744
|
-
visionLevel,
|
|
1745
|
-
readseekDir: cacheDir
|
|
1746
|
-
});
|
|
1747
|
-
return runReadSeekRaw(context, args, visionMode === undefined ? {} : { timeoutMs: DEFAULT_VISION_TIMEOUT_MS });
|
|
1748
|
-
}, (result) => ({ output: boundDocumentOutput(String(result)) })),
|
|
1749
|
-
readseek_digest: readseekTool(openCodeToolDescription("digest"), {
|
|
2347
|
+
readseek_inspect: readseekTool(openCodeToolDescription("inspect"), {
|
|
1750
2348
|
path: tool2.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
2349
|
+
selector: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.selector),
|
|
1751
2350
|
select: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.selectComma),
|
|
1752
|
-
|
|
2351
|
+
query: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.query),
|
|
1753
2352
|
end: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.end),
|
|
1754
2353
|
limit: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.limit),
|
|
2354
|
+
depth: tool2.schema.number().int().nonnegative().optional().describe(PARAM_DESCRIPTIONS.inspectDepth),
|
|
1755
2355
|
language: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.languageOverride),
|
|
1756
|
-
visionMode: tool2.schema.enum(IMAGE_MODES).optional().describe(PARAM_DESCRIPTIONS.
|
|
2356
|
+
visionMode: tool2.schema.enum(IMAGE_MODES).optional().describe(PARAM_DESCRIPTIONS.visionModeInspect),
|
|
1757
2357
|
visionLevel: tool2.schema.enum(VISION_LEVEL_VALUES).optional().describe(PARAM_DESCRIPTIONS.visionLevel)
|
|
1758
|
-
}, "
|
|
2358
|
+
}, "inspect", async (input, context) => {
|
|
1759
2359
|
const filePath = resolvePath(context.directory, input.path);
|
|
1760
2360
|
await authorizeRead(context, filePath);
|
|
1761
2361
|
const select = (input.select ?? "content").trim() || "content";
|
|
@@ -1765,12 +2365,14 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1765
2365
|
if (!vision.ok)
|
|
1766
2366
|
throw new Error(vision.error);
|
|
1767
2367
|
const cacheDir = await ensureReadSeekCache(context);
|
|
1768
|
-
const args =
|
|
2368
|
+
const args = buildInspectArgs({
|
|
1769
2369
|
path: filePath,
|
|
2370
|
+
selector: input.selector,
|
|
1770
2371
|
select,
|
|
1771
|
-
|
|
2372
|
+
query: input.query,
|
|
1772
2373
|
end: input.end,
|
|
1773
2374
|
limit: input.limit,
|
|
2375
|
+
depth: input.depth ?? (select.split(",").some((facet) => facet.trim() === "map") ? DEFAULT_INSPECT_MAP_DEPTH : undefined),
|
|
1774
2376
|
language: input.language,
|
|
1775
2377
|
visionMode,
|
|
1776
2378
|
visionLevel,
|
|
@@ -1778,6 +2380,37 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1778
2380
|
});
|
|
1779
2381
|
return runReadSeek(context, args, visionMode === undefined || visionMode === "none" ? {} : { timeoutMs: DEFAULT_VISION_TIMEOUT_MS });
|
|
1780
2382
|
}),
|
|
2383
|
+
readseek_extract: readseekTool(openCodeToolDescription("extract"), {
|
|
2384
|
+
path: tool2.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
2385
|
+
selector: tool2.schema.string().min(1).describe(PARAM_DESCRIPTIONS.assetSelector),
|
|
2386
|
+
output: tool2.schema.string().min(1).describe(PARAM_DESCRIPTIONS.output)
|
|
2387
|
+
}, "extract", async (input, context) => {
|
|
2388
|
+
const selector = String(input.selector ?? "").trim();
|
|
2389
|
+
const output = String(input.output ?? "").trim();
|
|
2390
|
+
if (!selector.startsWith("asset:") || selector === "asset:") {
|
|
2391
|
+
throw new Error("extract requires an asset: selector");
|
|
2392
|
+
}
|
|
2393
|
+
if (!output || output === "-")
|
|
2394
|
+
throw new Error("extract output must be a file path");
|
|
2395
|
+
const filePath = resolvePath(context.directory, input.path);
|
|
2396
|
+
const outputPath = resolvePath(context.directory, output);
|
|
2397
|
+
await authorizeRead(context, filePath);
|
|
2398
|
+
await rejectSymlinkMutation(outputPath);
|
|
2399
|
+
await authorizeEdit(context, [outputPath]);
|
|
2400
|
+
const cacheDir = await ensureReadSeekCache(context);
|
|
2401
|
+
await runReadSeekRaw(context, buildExtractArgs({
|
|
2402
|
+
path: filePath,
|
|
2403
|
+
selector,
|
|
2404
|
+
output: outputPath,
|
|
2405
|
+
readseekDir: cacheDir
|
|
2406
|
+
}), { cancelable: false, timeoutMs: 120000 });
|
|
2407
|
+
return {
|
|
2408
|
+
file: filePath,
|
|
2409
|
+
selector,
|
|
2410
|
+
output: outputPath,
|
|
2411
|
+
changed: true
|
|
2412
|
+
};
|
|
2413
|
+
}),
|
|
1781
2414
|
readseek_grep: readseekTool(openCodeToolDescription("grep"), {
|
|
1782
2415
|
pattern: tool2.schema.string().describe(PARAM_DESCRIPTIONS.grepPattern),
|
|
1783
2416
|
path: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
@@ -1850,7 +2483,7 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1850
2483
|
}
|
|
1851
2484
|
const linesByNumber = new Map;
|
|
1852
2485
|
for (const [start, end] of mergedRanges) {
|
|
1853
|
-
const output = record(await
|
|
2486
|
+
const output = record(await runInspectContent(context, readRangeArgs(filePath, start, end)));
|
|
1854
2487
|
for (const hashline of items(output.hashlines)) {
|
|
1855
2488
|
const line = record(hashline).line;
|
|
1856
2489
|
if (typeof line === "number" && !linesByNumber.has(line))
|
|
@@ -1870,18 +2503,14 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1870
2503
|
pattern: tool2.schema.string().describe(PARAM_DESCRIPTIONS.pattern),
|
|
1871
2504
|
path: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
1872
2505
|
language: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
1873
|
-
|
|
1874
|
-
others: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.others),
|
|
1875
|
-
ignored: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.ignored)
|
|
2506
|
+
gitScope: tool2.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
1876
2507
|
}, "search", async (input, context) => {
|
|
1877
2508
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
1878
2509
|
const args = buildSearchArgs({
|
|
1879
2510
|
path: target,
|
|
1880
2511
|
pattern: input.pattern,
|
|
1881
2512
|
language: input.language,
|
|
1882
|
-
|
|
1883
|
-
others: input.others,
|
|
1884
|
-
ignored: input.ignored
|
|
2513
|
+
gitScope: input.gitScope
|
|
1885
2514
|
});
|
|
1886
2515
|
await authorizeSearch(context, target, input.pattern);
|
|
1887
2516
|
return runReadSeek(context, args);
|
|
@@ -1890,19 +2519,14 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1890
2519
|
name: tool2.schema.string().describe(PARAM_DESCRIPTIONS.name),
|
|
1891
2520
|
path: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
1892
2521
|
language: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
1893
|
-
|
|
1894
|
-
others: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.others),
|
|
1895
|
-
ignored: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.ignored)
|
|
2522
|
+
gitScope: tool2.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
1896
2523
|
}, "def", async (input, context) => {
|
|
1897
2524
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
1898
2525
|
const args = buildDefArgs({
|
|
1899
2526
|
path: target,
|
|
1900
2527
|
name: input.name,
|
|
1901
2528
|
language: input.language,
|
|
1902
|
-
|
|
1903
|
-
cached: input.cached,
|
|
1904
|
-
others: input.others,
|
|
1905
|
-
ignored: input.ignored
|
|
2529
|
+
gitScope: input.gitScope
|
|
1906
2530
|
});
|
|
1907
2531
|
await authorizeSearch(context, target, input.name);
|
|
1908
2532
|
return runReadSeek(context, args);
|
|
@@ -1914,9 +2538,7 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1914
2538
|
scope: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.scope),
|
|
1915
2539
|
line: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.line),
|
|
1916
2540
|
column: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.column),
|
|
1917
|
-
|
|
1918
|
-
others: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.others),
|
|
1919
|
-
ignored: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.ignored)
|
|
2541
|
+
gitScope: tool2.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
1920
2542
|
}, "refs", async (input, context) => {
|
|
1921
2543
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
1922
2544
|
const name = input.name;
|
|
@@ -1927,9 +2549,7 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1927
2549
|
line: input.line,
|
|
1928
2550
|
column: input.column,
|
|
1929
2551
|
language: input.language,
|
|
1930
|
-
|
|
1931
|
-
others: input.others,
|
|
1932
|
-
ignored: input.ignored
|
|
2552
|
+
gitScope: input.gitScope
|
|
1933
2553
|
});
|
|
1934
2554
|
await authorizeSearch(context, target, name ?? "*");
|
|
1935
2555
|
return runReadSeek(context, args);
|
|
@@ -1945,10 +2565,12 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1945
2565
|
const filePath = resolvePath(context.directory, input.path);
|
|
1946
2566
|
await authorizeRead(context, filePath);
|
|
1947
2567
|
if (input.workspace) {
|
|
1948
|
-
const
|
|
1949
|
-
|
|
2568
|
+
const column = input.column;
|
|
2569
|
+
const identifyArgs = inspectArgs(filePath, "identity", {
|
|
2570
|
+
selector: `line:${input.line}${column === undefined ? "" : `:${column}`}`
|
|
2571
|
+
});
|
|
1950
2572
|
const envelope = await runReadSeek(context, identifyArgs);
|
|
1951
|
-
const name = identifiedName(
|
|
2573
|
+
const name = identifiedName(requireInspectFacet(envelope, "identity"));
|
|
1952
2574
|
if (!name)
|
|
1953
2575
|
throw new Error("readseek could not identify a binding at the rename cursor");
|
|
1954
2576
|
await authorizeSearch(context, context.directory, name);
|
|
@@ -1963,7 +2585,7 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1963
2585
|
const plan = await runReadSeek(context, args);
|
|
1964
2586
|
if (input.apply === false)
|
|
1965
2587
|
return plan;
|
|
1966
|
-
const files = renameFiles(plan);
|
|
2588
|
+
const files = renameFiles(plan, context.directory);
|
|
1967
2589
|
const planHash = record(plan).plan_hash;
|
|
1968
2590
|
if (typeof planHash !== "string" || planHash.length === 0) {
|
|
1969
2591
|
throw new Error("readseek rename plan did not include a plan hash");
|
|
@@ -2027,7 +2649,7 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2027
2649
|
await authorizeRead(context, filePath);
|
|
2028
2650
|
await rejectSymlinkMutation(filePath);
|
|
2029
2651
|
const edits = input.edits;
|
|
2030
|
-
const request =
|
|
2652
|
+
const request = serializeEditPlan(edits);
|
|
2031
2653
|
const baseArgs = buildEditArgs({
|
|
2032
2654
|
path: filePath,
|
|
2033
2655
|
language: input.language,
|
|
@@ -2079,19 +2701,53 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2079
2701
|
await authorizeEdit(context, [filePath], simpleDiff(filePath, before.content, content), false);
|
|
2080
2702
|
context.abort.throwIfAborted();
|
|
2081
2703
|
await writeText(filePath, before, content);
|
|
2082
|
-
return postMutationPreview(context,
|
|
2704
|
+
return postMutationPreview(context, inspectArgs(filePath, "content", { end: DEFAULT_READ_LIMIT }), filePath, !before.exists || before.content !== content);
|
|
2083
2705
|
});
|
|
2084
2706
|
})
|
|
2085
2707
|
};
|
|
2086
2708
|
}
|
|
2087
2709
|
|
|
2710
|
+
// v2.ts
|
|
2711
|
+
import { define } from "@opencode-ai/plugin/v2/promise";
|
|
2712
|
+
var GUIDANCE = "For structural code navigation, consult the readseek skill when ReadSeek tools are unavailable and the readseek CLI is installed.";
|
|
2713
|
+
var ReadSeekV2Plugin = define({
|
|
2714
|
+
id: "opencode-readseek",
|
|
2715
|
+
setup: async (ctx) => {
|
|
2716
|
+
await ctx.skill.transform((draft) => {
|
|
2717
|
+
draft.source({
|
|
2718
|
+
type: "embedded",
|
|
2719
|
+
skill: {
|
|
2720
|
+
name: "readseek",
|
|
2721
|
+
description: "Use for structural code navigation and anchored edits when the readseek CLI is installed.",
|
|
2722
|
+
location: "/plugin/opencode-readseek/SKILL.md",
|
|
2723
|
+
content: `# ReadSeek CLI
|
|
2724
|
+
|
|
2725
|
+
When ReadSeek tools are unavailable, use the readseek CLI for structural navigation and anchored edits if it is installed and available on PATH. Run \`readseek --help\` to inspect supported commands before invoking them. Get fresh anchors before editing; re-read after external file changes.
|
|
2726
|
+
|
|
2727
|
+
This V2 plugin supplies guidance only; it does not register readseek_* tools. If the CLI is unavailable, use OpenCode's built-in tools instead.`
|
|
2728
|
+
}
|
|
2729
|
+
});
|
|
2730
|
+
});
|
|
2731
|
+
await ctx.agent.transform((draft) => {
|
|
2732
|
+
for (const agent of draft.list()) {
|
|
2733
|
+
draft.update(agent.id, (item) => {
|
|
2734
|
+
if (!item.system?.includes(GUIDANCE))
|
|
2735
|
+
item.system = [item.system, GUIDANCE].filter(Boolean).join(`
|
|
2736
|
+
|
|
2737
|
+
`);
|
|
2738
|
+
});
|
|
2739
|
+
}
|
|
2740
|
+
});
|
|
2741
|
+
}
|
|
2742
|
+
});
|
|
2743
|
+
|
|
2088
2744
|
// index.ts
|
|
2089
2745
|
var ReadSeekPlugin = async (_input, options) => {
|
|
2090
2746
|
const anchors = new SessionAnchors;
|
|
2091
2747
|
const imagePolicy = resolveImagePolicy(options);
|
|
2092
2748
|
return {
|
|
2093
2749
|
tool: createReadSeekTools(imagePolicy),
|
|
2094
|
-
"experimental.chat.system.transform": async (
|
|
2750
|
+
"experimental.chat.system.transform": async (_input, output) => {
|
|
2095
2751
|
output.system.push(TOOL_POLICY);
|
|
2096
2752
|
},
|
|
2097
2753
|
"tool.definition": async (input, output) => {
|
|
@@ -2137,8 +2793,8 @@ var ReadSeekPlugin = async (_input, options) => {
|
|
|
2137
2793
|
}
|
|
2138
2794
|
};
|
|
2139
2795
|
};
|
|
2140
|
-
var opencode_readseek_default = {
|
|
2796
|
+
var opencode_readseek_default = { ...ReadSeekV2Plugin, server: ReadSeekPlugin };
|
|
2141
2797
|
export {
|
|
2142
|
-
|
|
2143
|
-
|
|
2798
|
+
ReadSeekPlugin,
|
|
2799
|
+
opencode_readseek_default as default
|
|
2144
2800
|
};
|