opencode-readseek 0.9.16 → 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/README.md +13 -5
- package/dist/index.js +929 -291
- 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,28 +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",
|
|
66
|
-
|
|
62
|
+
inspectDepth: "Maximum structural depth for map output; defaults to 1 in agent tools",
|
|
67
63
|
offset: "One-based starting line",
|
|
68
64
|
readLimit: "Maximum lines to return",
|
|
69
65
|
symbolRead: "Symbol name to read",
|
|
70
66
|
bundle: "Include related symbols from the same file; requires symbol",
|
|
71
67
|
image: "Standalone image mode: none, all, ocr, caption, objects. Required for standalone images.",
|
|
72
|
-
|
|
73
|
-
visionModeView: "Analyze selected assets: all, ocr, caption, or objects",
|
|
68
|
+
visionModeInspect: "Standalone image mode: none, all, caption, objects, or ocr",
|
|
74
69
|
visionLevel: "Vision inference level: low (default), medium, or high",
|
|
75
|
-
|
|
76
|
-
page: "One-based source page",
|
|
77
|
-
kind: "Node kind filter",
|
|
78
|
-
depth: "Maximum depth below selected roots",
|
|
79
|
-
outline: "Return outline nodes only",
|
|
70
|
+
output: "Destination file for extracted binary data; stdout is not supported",
|
|
80
71
|
edits: "Use set_line, replace_lines, insert_after, replace_symbol, or replace",
|
|
81
72
|
editVariant: "Exactly one nested edit variant",
|
|
82
73
|
setLine: 'Replace one line: {"set_line":{"anchor":"LINE:HASH","new_text":"..."}}',
|
|
@@ -113,11 +104,11 @@ var PARAM_DESCRIPTIONS = {
|
|
|
113
104
|
scopeContext: "Context lines within each symbol"
|
|
114
105
|
};
|
|
115
106
|
var TOOL_DESCRIPTIONS = {
|
|
116
|
-
|
|
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.",
|
|
117
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":"..."}}`.',
|
|
118
109
|
write: "Create or replace a complete text file and return `LINE:HASH` anchors. Use `edit` for small changes to an existing file.",
|
|
119
110
|
grep: "Search text or regex in files and return edit-ready `LINE:HASH` anchors. Use for identifiers, strings, configuration, errors, comments, and documentation.",
|
|
120
|
-
|
|
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.",
|
|
121
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.",
|
|
122
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.",
|
|
123
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.",
|
|
@@ -129,18 +120,11 @@ function renderToolDescription(name, aliases = {}) {
|
|
|
129
120
|
throw new Error(`Missing TOOL_DESCRIPTIONS for ${name}`);
|
|
130
121
|
return TOOL_NAMES.reduce((description, toolName) => description.replaceAll(`\`${toolName}\``, `\`${aliases[toolName] ?? toolName}\``), desc);
|
|
131
122
|
}
|
|
132
|
-
var
|
|
133
|
-
|
|
134
|
-
type: "
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
others: {
|
|
138
|
-
type: "boolean",
|
|
139
|
-
description: PARAM_DESCRIPTIONS.others
|
|
140
|
-
},
|
|
141
|
-
ignored: {
|
|
142
|
-
type: "boolean",
|
|
143
|
-
description: PARAM_DESCRIPTIONS.ignored
|
|
123
|
+
var gitScopeSchema = {
|
|
124
|
+
gitScope: {
|
|
125
|
+
type: "string",
|
|
126
|
+
enum: GIT_SCOPES,
|
|
127
|
+
description: PARAM_DESCRIPTIONS.gitScope
|
|
144
128
|
}
|
|
145
129
|
};
|
|
146
130
|
var editItemSchema = {
|
|
@@ -206,29 +190,31 @@ var editItemSchema = {
|
|
|
206
190
|
};
|
|
207
191
|
var TOOL_CONTRACTS = [
|
|
208
192
|
{
|
|
209
|
-
name: "
|
|
210
|
-
title: "
|
|
211
|
-
description: TOOL_DESCRIPTIONS.
|
|
193
|
+
name: "inspect",
|
|
194
|
+
title: "Inspect input",
|
|
195
|
+
description: TOOL_DESCRIPTIONS.inspect,
|
|
212
196
|
inputSchema: {
|
|
213
197
|
type: "object",
|
|
214
198
|
properties: {
|
|
215
199
|
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
200
|
+
selector: { type: "string", description: PARAM_DESCRIPTIONS.selector },
|
|
216
201
|
select: { type: "string", description: PARAM_DESCRIPTIONS.select },
|
|
217
|
-
|
|
202
|
+
query: { type: "string", description: PARAM_DESCRIPTIONS.query },
|
|
218
203
|
end: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.end },
|
|
219
204
|
limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.limit },
|
|
220
|
-
depth: { type: "integer", minimum: 0, description: PARAM_DESCRIPTIONS.
|
|
205
|
+
depth: { type: "integer", minimum: 0, description: PARAM_DESCRIPTIONS.inspectDepth },
|
|
221
206
|
language: { type: "string", description: PARAM_DESCRIPTIONS.languageOverride },
|
|
222
207
|
visionMode: {
|
|
223
208
|
type: "string",
|
|
224
209
|
enum: [...VISION_MODES],
|
|
225
|
-
description: PARAM_DESCRIPTIONS.
|
|
210
|
+
description: PARAM_DESCRIPTIONS.visionModeInspect
|
|
226
211
|
},
|
|
227
212
|
visionLevel: {
|
|
228
213
|
type: "string",
|
|
229
214
|
enum: [...VISION_LEVELS],
|
|
230
215
|
description: PARAM_DESCRIPTIONS.visionLevel
|
|
231
|
-
}
|
|
216
|
+
},
|
|
217
|
+
stdinName: { type: "string", description: PARAM_DESCRIPTIONS.stdinName }
|
|
232
218
|
},
|
|
233
219
|
required: ["path"],
|
|
234
220
|
additionalProperties: false
|
|
@@ -302,34 +288,27 @@ var TOOL_CONTRACTS = [
|
|
|
302
288
|
}
|
|
303
289
|
},
|
|
304
290
|
{
|
|
305
|
-
name: "
|
|
306
|
-
title: "
|
|
307
|
-
description: TOOL_DESCRIPTIONS.
|
|
291
|
+
name: "extract",
|
|
292
|
+
title: "Extract asset",
|
|
293
|
+
description: TOOL_DESCRIPTIONS.extract,
|
|
308
294
|
inputSchema: {
|
|
309
295
|
type: "object",
|
|
310
296
|
properties: {
|
|
311
297
|
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
312
|
-
|
|
313
|
-
page: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.page },
|
|
314
|
-
kind: {
|
|
298
|
+
selector: {
|
|
315
299
|
type: "string",
|
|
316
|
-
|
|
317
|
-
description: PARAM_DESCRIPTIONS.
|
|
300
|
+
pattern: "^asset:.+",
|
|
301
|
+
description: PARAM_DESCRIPTIONS.assetSelector
|
|
318
302
|
},
|
|
319
|
-
|
|
320
|
-
outline: { type: "boolean", description: PARAM_DESCRIPTIONS.outline },
|
|
321
|
-
visionMode: {
|
|
303
|
+
output: {
|
|
322
304
|
type: "string",
|
|
323
|
-
|
|
324
|
-
|
|
305
|
+
minLength: 1,
|
|
306
|
+
pattern: "^(?!-$).+",
|
|
307
|
+
description: PARAM_DESCRIPTIONS.output
|
|
325
308
|
},
|
|
326
|
-
|
|
327
|
-
type: "string",
|
|
328
|
-
enum: [...VISION_LEVELS],
|
|
329
|
-
description: PARAM_DESCRIPTIONS.visionLevel
|
|
330
|
-
}
|
|
309
|
+
stdinName: { type: "string", description: PARAM_DESCRIPTIONS.stdinName }
|
|
331
310
|
},
|
|
332
|
-
required: ["path"],
|
|
311
|
+
required: ["path", "selector", "output"],
|
|
333
312
|
additionalProperties: false
|
|
334
313
|
}
|
|
335
314
|
},
|
|
@@ -343,7 +322,8 @@ var TOOL_CONTRACTS = [
|
|
|
343
322
|
pattern: { type: "string", description: PARAM_DESCRIPTIONS.pattern },
|
|
344
323
|
path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
|
|
345
324
|
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
346
|
-
|
|
325
|
+
limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.matchLimit },
|
|
326
|
+
...gitScopeSchema
|
|
347
327
|
},
|
|
348
328
|
required: ["pattern"],
|
|
349
329
|
additionalProperties: false
|
|
@@ -359,7 +339,7 @@ var TOOL_CONTRACTS = [
|
|
|
359
339
|
name: { type: "string", description: PARAM_DESCRIPTIONS.name },
|
|
360
340
|
path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
|
|
361
341
|
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
362
|
-
...
|
|
342
|
+
...gitScopeSchema
|
|
363
343
|
},
|
|
364
344
|
required: ["name"],
|
|
365
345
|
additionalProperties: false
|
|
@@ -378,7 +358,7 @@ var TOOL_CONTRACTS = [
|
|
|
378
358
|
scope: { type: "boolean", description: PARAM_DESCRIPTIONS.scope },
|
|
379
359
|
line: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.line },
|
|
380
360
|
column: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.column },
|
|
381
|
-
...
|
|
361
|
+
...gitScopeSchema
|
|
382
362
|
},
|
|
383
363
|
additionalProperties: false
|
|
384
364
|
}
|
|
@@ -396,7 +376,9 @@ var TOOL_CONTRACTS = [
|
|
|
396
376
|
to: { type: "string", description: PARAM_DESCRIPTIONS.to },
|
|
397
377
|
workspace: { type: "boolean", description: PARAM_DESCRIPTIONS.workspace },
|
|
398
378
|
apply: { type: "boolean", description: PARAM_DESCRIPTIONS.apply },
|
|
399
|
-
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
|
|
400
382
|
},
|
|
401
383
|
required: ["path", "line", "to"],
|
|
402
384
|
additionalProperties: false
|
|
@@ -405,14 +387,14 @@ var TOOL_CONTRACTS = [
|
|
|
405
387
|
];
|
|
406
388
|
// ../readseek-api/src/policy.ts
|
|
407
389
|
var BUILTIN_FALLBACKS = {
|
|
408
|
-
read: "
|
|
390
|
+
read: "inspect",
|
|
409
391
|
edit: "edit",
|
|
410
392
|
write: "write",
|
|
411
393
|
grep: "grep",
|
|
412
394
|
apply_patch: "edit"
|
|
413
395
|
};
|
|
414
396
|
var PREFERENCE_LEADS = {
|
|
415
|
-
|
|
397
|
+
inspect: "Preferred for reading source with anchors and facets.",
|
|
416
398
|
edit: "Preferred for editing existing text files with fresh LINE:HASH anchors.",
|
|
417
399
|
write: "Preferred for creating or replacing a complete text file; returns LINE:HASH anchors.",
|
|
418
400
|
grep: "Preferred text/regex search; returns edit-ready LINE:HASH anchors.",
|
|
@@ -420,10 +402,10 @@ var PREFERENCE_LEADS = {
|
|
|
420
402
|
def: "Preferred for go-to-definition or declaration lookup by name.",
|
|
421
403
|
refs: "Preferred for finding identifier-name matches or binding-specific references before rename or delete.",
|
|
422
404
|
rename: "Preferred for renaming a symbol with binding-aware, verified edits.",
|
|
423
|
-
|
|
405
|
+
extract: "Preferred for saving a selected document asset to a file."
|
|
424
406
|
};
|
|
425
407
|
var FALLBACK_TEMPLATES = {
|
|
426
|
-
read: "Prefer `
|
|
408
|
+
read: "Prefer `inspect` with fresh LINE:HASH anchors.",
|
|
427
409
|
edit: "Prefer `edit` with fresh LINE:HASH anchors.",
|
|
428
410
|
write: "Prefer `write` so the result returns LINE:HASH anchors.",
|
|
429
411
|
grep: "Prefer `grep` for text/regex search with edit-ready anchors.",
|
|
@@ -446,7 +428,7 @@ function renderFallbackToolDescription(builtIn, aliases = {}) {
|
|
|
446
428
|
}
|
|
447
429
|
function renderToolRoutingPolicy(options = {}) {
|
|
448
430
|
const aliases = options.aliases ?? {};
|
|
449
|
-
const
|
|
431
|
+
const inspect = resolveToolName("inspect", aliases);
|
|
450
432
|
const edit = resolveToolName("edit", aliases);
|
|
451
433
|
const write = resolveToolName("write", aliases);
|
|
452
434
|
const grep = resolveToolName("grep", aliases);
|
|
@@ -454,11 +436,11 @@ function renderToolRoutingPolicy(options = {}) {
|
|
|
454
436
|
const def = resolveToolName("def", aliases);
|
|
455
437
|
const refs = resolveToolName("refs", aliases);
|
|
456
438
|
const rename = resolveToolName("rename", aliases);
|
|
457
|
-
const
|
|
439
|
+
const extract = resolveToolName("extract", aliases);
|
|
458
440
|
if (options.style === "server") {
|
|
459
441
|
return [
|
|
460
|
-
"Structural code navigation, document
|
|
461
|
-
"For structural questions, prefer these tools to text Grep/Bash: targeted definitions, references, and AST search; shallow
|
|
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.",
|
|
462
444
|
`Use ${edit} for LINE:HASH-anchored mutations (preview with apply=false; apply defaults to true and re-checks plan_hash).`,
|
|
463
445
|
"Paths resolve against CLAUDE_PROJECT_DIR. Native Read/Grep/Edit hooks provide passive context;",
|
|
464
446
|
"call MCP tools for active navigation, document questions, or anchored edits."
|
|
@@ -469,7 +451,7 @@ function renderToolRoutingPolicy(options = {}) {
|
|
|
469
451
|
if (options.preferOverBuiltIns) {
|
|
470
452
|
bullets.push("- Prefer ReadSeek tools over built-ins when they can do the job.");
|
|
471
453
|
}
|
|
472
|
-
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.`);
|
|
473
455
|
if (options.demoteBuiltIns && options.demoteBuiltIns.length > 0) {
|
|
474
456
|
bullets.push(`- Do not use built-in ${options.demoteBuiltIns.join(", ")} when a ReadSeek tool can do the job.`);
|
|
475
457
|
}
|
|
@@ -504,12 +486,6 @@ function tryParseLineAnchor(anchor) {
|
|
|
504
486
|
}
|
|
505
487
|
|
|
506
488
|
// ../readseek-api/src/validation.ts
|
|
507
|
-
function validateGitSelection(input) {
|
|
508
|
-
if (input.ignored && !input.others) {
|
|
509
|
-
return { ok: false, error: "ignored requires others=true" };
|
|
510
|
-
}
|
|
511
|
-
return { ok: true };
|
|
512
|
-
}
|
|
513
489
|
function validateRefsNameVsScope(input) {
|
|
514
490
|
if (input.scope) {
|
|
515
491
|
if (input.line === undefined) {
|
|
@@ -526,11 +502,8 @@ function validateRefsNameVsScope(input) {
|
|
|
526
502
|
return { ok: true };
|
|
527
503
|
}
|
|
528
504
|
function validateVisionLevelVsMode(input) {
|
|
529
|
-
if (input.
|
|
530
|
-
return { ok: false, error: "
|
|
531
|
-
}
|
|
532
|
-
if (input.visionLevel !== undefined && input.visionMode === undefined) {
|
|
533
|
-
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" };
|
|
534
507
|
}
|
|
535
508
|
return { ok: true };
|
|
536
509
|
}
|
|
@@ -601,22 +574,54 @@ function validateExactlyOneEditVariant(edits) {
|
|
|
601
574
|
}
|
|
602
575
|
var MIN_SAFE_INTEGER = BigInt(Number.MIN_SAFE_INTEGER);
|
|
603
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
|
+
}
|
|
604
617
|
// ../readseek-api/src/argv.ts
|
|
605
|
-
var
|
|
618
|
+
var DEFAULT_INSPECT_MAP_DEPTH = 1;
|
|
606
619
|
function buildLineLocation(line, column) {
|
|
607
620
|
return column === undefined ? `line:${line}` : `line:${line}:${column}`;
|
|
608
621
|
}
|
|
609
|
-
function
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
throw new Error(validated.error);
|
|
613
|
-
}
|
|
614
|
-
if (input.cached)
|
|
615
|
-
args.push("--cached");
|
|
616
|
-
if (input.others)
|
|
617
|
-
args.push("--others");
|
|
618
|
-
if (input.ignored)
|
|
619
|
-
args.push("--ignored");
|
|
622
|
+
function appendGitScopeArgs(args, input) {
|
|
623
|
+
if (input.gitScope !== undefined)
|
|
624
|
+
args.push("--git-scope", input.gitScope);
|
|
620
625
|
return args;
|
|
621
626
|
}
|
|
622
627
|
function pushOptionalString(args, flag, value) {
|
|
@@ -645,33 +650,138 @@ function selectArg(select) {
|
|
|
645
650
|
return;
|
|
646
651
|
return Array.isArray(select) ? select.join(",") : select;
|
|
647
652
|
}
|
|
648
|
-
function
|
|
649
|
-
const
|
|
650
|
-
|
|
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 };
|
|
651
716
|
}
|
|
652
|
-
function
|
|
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
|
+
}
|
|
653
722
|
const vision = validateVisionLevelVsMode({
|
|
654
723
|
visionMode: input.visionMode,
|
|
655
724
|
visionLevel: input.visionLevel
|
|
656
725
|
});
|
|
657
726
|
if (!vision.ok)
|
|
658
727
|
throw new Error(vision.error);
|
|
659
|
-
const
|
|
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
|
+
}
|
|
660
740
|
if (input.depth !== undefined) {
|
|
661
|
-
if (!Number.
|
|
741
|
+
if (!Number.isSafeInteger(input.depth) || input.depth < 0) {
|
|
662
742
|
throw new Error("depth must be a non-negative integer");
|
|
663
743
|
}
|
|
664
|
-
if (!
|
|
744
|
+
if (!hasMap)
|
|
665
745
|
throw new Error("depth requires the map facet");
|
|
666
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
|
+
}
|
|
667
775
|
const args = [];
|
|
668
776
|
if (input.readseekDir !== undefined)
|
|
669
777
|
args.push("--readseek-dir", input.readseekDir);
|
|
670
|
-
args.push("
|
|
778
|
+
args.push("inspect", input.path);
|
|
779
|
+
if (input.selector !== undefined)
|
|
780
|
+
args.push(input.selector);
|
|
671
781
|
const select = selectArg(input.select);
|
|
672
782
|
if (select !== undefined)
|
|
673
783
|
args.push("--select", select);
|
|
674
|
-
pushOptionalString(args, "--
|
|
784
|
+
pushOptionalString(args, "--query", input.query);
|
|
675
785
|
pushOptionalNumber(args, "--end", input.end);
|
|
676
786
|
pushOptionalNumber(args, "--limit", input.limit);
|
|
677
787
|
pushOptionalNumber(args, "--depth", input.depth);
|
|
@@ -679,30 +789,24 @@ function buildDigestArgs(input) {
|
|
|
679
789
|
pushOptionalString(args, "--vision-mode", input.visionMode);
|
|
680
790
|
pushOptionalString(args, "--vision-level", input.visionLevel);
|
|
681
791
|
pushOptionalString(args, "--stdin-name", input.stdinName);
|
|
792
|
+
args.push("--kdl");
|
|
682
793
|
return args;
|
|
683
794
|
}
|
|
684
|
-
function
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
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
|
+
}
|
|
692
805
|
const args = [];
|
|
693
806
|
if (input.readseekDir !== undefined)
|
|
694
807
|
args.push("--readseek-dir", input.readseekDir);
|
|
695
|
-
args.push("
|
|
696
|
-
|
|
697
|
-
args.push("--format", input.format);
|
|
698
|
-
pushOptionalString(args, "--node", input.node);
|
|
699
|
-
pushOptionalNumber(args, "--page", input.page);
|
|
700
|
-
pushOptionalString(args, "--kind", input.kind);
|
|
701
|
-
pushOptionalNumber(args, "--depth", input.depth);
|
|
702
|
-
if (input.outline)
|
|
703
|
-
args.push("--outline");
|
|
704
|
-
pushOptionalString(args, "--vision-mode", input.visionMode);
|
|
705
|
-
pushOptionalString(args, "--vision-level", input.visionLevel);
|
|
808
|
+
args.push("--output", input.output, "extract", input.path, input.selector);
|
|
809
|
+
pushOptionalString(args, "--stdin-name", input.stdinName);
|
|
706
810
|
return args;
|
|
707
811
|
}
|
|
708
812
|
function buildSearchArgs(input) {
|
|
@@ -710,15 +814,17 @@ function buildSearchArgs(input) {
|
|
|
710
814
|
pushOptionalString(args, "--path", input.path);
|
|
711
815
|
pushOptionalString(args, "--language", input.language);
|
|
712
816
|
pushOptionalNumber(args, "--limit", input.limit);
|
|
713
|
-
|
|
817
|
+
appendGitScopeArgs(args, input);
|
|
818
|
+
args.push("--kdl");
|
|
819
|
+
return args;
|
|
714
820
|
}
|
|
715
821
|
function buildDefArgs(input) {
|
|
716
822
|
const args = ["def", input.name];
|
|
717
823
|
pushOptionalString(args, "--path", input.path);
|
|
718
|
-
if (input.format !== undefined)
|
|
719
|
-
args.push("--format", input.format);
|
|
720
824
|
pushOptionalString(args, "--language", input.language);
|
|
721
|
-
|
|
825
|
+
appendGitScopeArgs(args, input);
|
|
826
|
+
args.push("--kdl");
|
|
827
|
+
return args;
|
|
722
828
|
}
|
|
723
829
|
function buildRefsArgs(input) {
|
|
724
830
|
const validated = validateRefsNameVsScope(input);
|
|
@@ -733,10 +839,10 @@ function buildRefsArgs(input) {
|
|
|
733
839
|
pushOptionalNumber(args, "--line", input.line);
|
|
734
840
|
pushOptionalNumber(args, "--column", input.column);
|
|
735
841
|
}
|
|
736
|
-
if (input.format !== undefined)
|
|
737
|
-
args.push("--format", input.format);
|
|
738
842
|
pushOptionalString(args, "--language", input.language);
|
|
739
|
-
|
|
843
|
+
appendGitScopeArgs(args, input);
|
|
844
|
+
args.push("--kdl");
|
|
845
|
+
return args;
|
|
740
846
|
}
|
|
741
847
|
function buildEditArgs(input) {
|
|
742
848
|
const args = ["edit", input.path];
|
|
@@ -748,6 +854,7 @@ function buildEditArgs(input) {
|
|
|
748
854
|
pushOptionalString(args, "--language", input.language);
|
|
749
855
|
if (input.requestFromStdin)
|
|
750
856
|
args.push("--edits", "-");
|
|
857
|
+
args.push("--kdl");
|
|
751
858
|
return args;
|
|
752
859
|
}
|
|
753
860
|
function buildRenameArgs(input) {
|
|
@@ -760,7 +867,9 @@ function buildRenameArgs(input) {
|
|
|
760
867
|
flagOrder: input.flagOrder ?? "plan-hash-first"
|
|
761
868
|
});
|
|
762
869
|
pushOptionalString(args, "--language", input.language);
|
|
763
|
-
|
|
870
|
+
appendGitScopeArgs(args, input);
|
|
871
|
+
args.push("--kdl");
|
|
872
|
+
return args;
|
|
764
873
|
}
|
|
765
874
|
// ../readseek-api/src/platform.ts
|
|
766
875
|
import { accessSync, constants as fsConstants, existsSync, statSync } from "node:fs";
|
|
@@ -776,6 +885,8 @@ var READSEEK_PLATFORM_PACKAGES = {
|
|
|
776
885
|
};
|
|
777
886
|
var resolveFromPackage = createRequire(import.meta.url).resolve;
|
|
778
887
|
function readSeekBinaryName(platform = process.platform) {
|
|
888
|
+
if (platform === "linux")
|
|
889
|
+
return "readseek.AppImage";
|
|
779
890
|
return platform === "win32" ? "readseek.exe" : "readseek";
|
|
780
891
|
}
|
|
781
892
|
function readSeekPlatformKey(platform = process.platform, arch = process.arch) {
|
|
@@ -855,22 +966,304 @@ function isSubPath(parent, candidate) {
|
|
|
855
966
|
const relative = path2.relative(parent, candidate);
|
|
856
967
|
return relative === "" || !relative.startsWith(`..${path2.sep}`) && relative !== ".." && !path2.isAbsolute(relative);
|
|
857
968
|
}
|
|
858
|
-
// ../readseek-api/src/
|
|
859
|
-
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*$/;
|
|
860
971
|
function cleanReadSeekErrorText(stderr) {
|
|
861
972
|
return stderr.replace(READSEEK_USAGE_HINT, "").replace(/^error:\s*/i, "").replace(/\s*\n\s*/g, " ").trim();
|
|
862
973
|
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
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}`);
|
|
869
1259
|
}
|
|
870
1260
|
}
|
|
871
1261
|
// ../readseek-api/src/process.ts
|
|
872
1262
|
var DEFAULT_READSEEK_TIMEOUT_MS = 120000;
|
|
873
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
|
+
}
|
|
874
1267
|
function resolveProcessLimits(options = {}) {
|
|
875
1268
|
return {
|
|
876
1269
|
timeoutMs: options.timeoutMs ?? DEFAULT_READSEEK_TIMEOUT_MS,
|
|
@@ -901,8 +1294,8 @@ function ensureSuccessfulStdout(result) {
|
|
|
901
1294
|
return result.stdout;
|
|
902
1295
|
throw failureFromProcessResult(result);
|
|
903
1296
|
}
|
|
904
|
-
function
|
|
905
|
-
return
|
|
1297
|
+
function parseSuccessfulKdl(result) {
|
|
1298
|
+
return parseReadSeekKdl(ensureSuccessfulStdout(result));
|
|
906
1299
|
}
|
|
907
1300
|
// ../readseek-api/src/process-bun.ts
|
|
908
1301
|
function getBunSpawn() {
|
|
@@ -952,7 +1345,7 @@ async function runReadSeekBun(binaryPath, args, options = {}) {
|
|
|
952
1345
|
const stdin = options.stdin === undefined ? undefined : new TextEncoder().encode(options.stdin);
|
|
953
1346
|
const spawnOptions = {
|
|
954
1347
|
cwd: options.cwd,
|
|
955
|
-
env:
|
|
1348
|
+
env: readSeekProcessEnvironment(),
|
|
956
1349
|
stderr: "pipe",
|
|
957
1350
|
stdin,
|
|
958
1351
|
stdout: "pipe"
|
|
@@ -986,26 +1379,254 @@ async function runReadSeekBun(binaryPath, args, options = {}) {
|
|
|
986
1379
|
} finally {
|
|
987
1380
|
if (timeout !== undefined)
|
|
988
1381
|
clearTimeout(timeout);
|
|
1382
|
+
pending.catch(() => {
|
|
1383
|
+
return;
|
|
1384
|
+
});
|
|
989
1385
|
}
|
|
990
1386
|
}
|
|
991
1387
|
async function runReadSeekBunText(binaryPath, args, options = {}) {
|
|
992
1388
|
return ensureSuccessfulStdout(await runReadSeekBun(binaryPath, args, options));
|
|
993
1389
|
}
|
|
994
|
-
async function
|
|
995
|
-
return
|
|
996
|
-
}
|
|
997
|
-
// src/
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
var
|
|
1001
|
-
var
|
|
1002
|
-
var
|
|
1003
|
-
var
|
|
1004
|
-
var
|
|
1005
|
-
var
|
|
1006
|
-
var
|
|
1007
|
-
var
|
|
1008
|
-
|
|
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
|
+
});
|
|
1009
1630
|
// src/util.ts
|
|
1010
1631
|
import path3 from "node:path";
|
|
1011
1632
|
function render(value) {
|
|
@@ -1041,15 +1662,17 @@ function collectAnchoredFiles(value, files) {
|
|
|
1041
1662
|
for (const item of Object.values(output))
|
|
1042
1663
|
collectAnchoredFiles(item, files);
|
|
1043
1664
|
}
|
|
1044
|
-
function renameFiles(value) {
|
|
1665
|
+
function renameFiles(value, directory = process.cwd()) {
|
|
1045
1666
|
const output = record(value);
|
|
1046
1667
|
const files = [];
|
|
1047
|
-
if (typeof output.file === "string" && items(output.edits).length > 0)
|
|
1048
|
-
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
|
+
}
|
|
1049
1671
|
for (const other of items(output.others)) {
|
|
1050
1672
|
const item = record(other);
|
|
1051
|
-
if (typeof item.file === "string" && items(item.edits).length > 0)
|
|
1052
|
-
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
|
+
}
|
|
1053
1676
|
}
|
|
1054
1677
|
return files;
|
|
1055
1678
|
}
|
|
@@ -1099,25 +1722,21 @@ function resolveImagePolicy(options) {
|
|
|
1099
1722
|
return value;
|
|
1100
1723
|
throw new Error('opencode-readseek imageMode must be "on", "auto", or "off"');
|
|
1101
1724
|
}
|
|
1102
|
-
function
|
|
1103
|
-
return
|
|
1725
|
+
function inspectArgs(filePath, select, options = {}) {
|
|
1726
|
+
return buildInspectArgs({ path: filePath, select, ...options });
|
|
1104
1727
|
}
|
|
1105
|
-
function
|
|
1728
|
+
function requireInspectFacet(envelope, facet) {
|
|
1106
1729
|
const value = record(envelope)[facet];
|
|
1107
1730
|
if (value === undefined || value === null || typeof value !== "object") {
|
|
1108
|
-
throw new Error(`
|
|
1731
|
+
throw new Error(`inspect returned no ${facet} facet`);
|
|
1109
1732
|
}
|
|
1110
1733
|
return value;
|
|
1111
1734
|
}
|
|
1112
|
-
function withAt(args, line, column) {
|
|
1113
|
-
args.push("--at", buildLineLocation(line, column));
|
|
1114
|
-
}
|
|
1115
1735
|
function readRangeArgs(filePath, start, end) {
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
return digestArgs(filePath, "content", extra);
|
|
1736
|
+
return inspectArgs(filePath, "content", {
|
|
1737
|
+
selector: start > 1 ? `line:${start}` : undefined,
|
|
1738
|
+
end
|
|
1739
|
+
});
|
|
1121
1740
|
}
|
|
1122
1741
|
function readSeekBinaryPath() {
|
|
1123
1742
|
return resolveReadSeekBinaryOverride() ?? resolveReadSeekBinaryFromPackages(resolveFromHost);
|
|
@@ -1146,7 +1765,7 @@ async function runReadSeekRaw(context, args, options = {}) {
|
|
|
1146
1765
|
}
|
|
1147
1766
|
async function runReadSeek(context, args, options = {}) {
|
|
1148
1767
|
context.abort.throwIfAborted();
|
|
1149
|
-
return preserveAbortReason(context.abort, options.cancelable !== false, () =>
|
|
1768
|
+
return preserveAbortReason(context.abort, options.cancelable !== false, () => runReadSeekBunKdl(readSeekBinaryPath(), args, {
|
|
1150
1769
|
cwd: context.directory,
|
|
1151
1770
|
signal: context.abort,
|
|
1152
1771
|
cancelable: options.cancelable,
|
|
@@ -1154,12 +1773,12 @@ async function runReadSeek(context, args, options = {}) {
|
|
|
1154
1773
|
timeoutMs: options.timeoutMs
|
|
1155
1774
|
}));
|
|
1156
1775
|
}
|
|
1157
|
-
async function
|
|
1158
|
-
return
|
|
1776
|
+
async function runInspectContent(context, args, options = {}) {
|
|
1777
|
+
return requireInspectFacet(await runReadSeek(context, args, options), "content");
|
|
1159
1778
|
}
|
|
1160
1779
|
async function postMutationPreview(context, args, filePath, changed) {
|
|
1161
1780
|
try {
|
|
1162
|
-
return await
|
|
1781
|
+
return await runInspectContent(context, args, {
|
|
1163
1782
|
cancelable: false,
|
|
1164
1783
|
timeoutMs: DEFAULT_READSEEK_TIMEOUT_MS
|
|
1165
1784
|
});
|
|
@@ -1188,21 +1807,11 @@ async function ensureReadSeekCache(context) {
|
|
|
1188
1807
|
if (error.code !== "ENOENT")
|
|
1189
1808
|
throw error;
|
|
1190
1809
|
}
|
|
1191
|
-
readSeekCacheInit ??= runReadSeekRaw(context, ["--readseek-dir", cacheDir, "init"]).then(() => cacheDir).finally(() => {
|
|
1810
|
+
readSeekCacheInit ??= runReadSeekRaw(context, ["--readseek-dir", cacheDir, "init"], { cancelable: false }).then(() => cacheDir).finally(() => {
|
|
1192
1811
|
readSeekCacheInit = undefined;
|
|
1193
1812
|
});
|
|
1194
1813
|
return readSeekCacheInit;
|
|
1195
1814
|
}
|
|
1196
|
-
function boundDocumentOutput(value) {
|
|
1197
|
-
const lines = value.split(`
|
|
1198
|
-
`);
|
|
1199
|
-
let output = lines.length > MAX_DOCUMENT_OUTPUT_LINES ? lines.slice(0, MAX_DOCUMENT_OUTPUT_LINES).join(`
|
|
1200
|
-
`) : value;
|
|
1201
|
-
const truncated = output !== value || Buffer.byteLength(output, "utf8") > MAX_DOCUMENT_OUTPUT_BYTES;
|
|
1202
|
-
output = utf8Prefix(output, MAX_DOCUMENT_OUTPUT_BYTES);
|
|
1203
|
-
return truncated ? `${output}
|
|
1204
|
-
[… document view truncated; narrow it with page, node, kind, or depth]` : output;
|
|
1205
|
-
}
|
|
1206
1815
|
|
|
1207
1816
|
// src/policy.ts
|
|
1208
1817
|
var OPENCODE_TOOL_ALIASES = Object.fromEntries(TOOL_NAMES.map((name) => [name, `readseek_${name}`]));
|
|
@@ -1247,12 +1856,12 @@ class SessionAnchors {
|
|
|
1247
1856
|
planRename(sessionID, output) {
|
|
1248
1857
|
if (!output || typeof output !== "object")
|
|
1249
1858
|
return;
|
|
1250
|
-
const
|
|
1251
|
-
if (
|
|
1859
|
+
const record = output;
|
|
1860
|
+
if (record.applied === true) {
|
|
1252
1861
|
this.#renamePlans.delete(sessionID);
|
|
1253
1862
|
return;
|
|
1254
1863
|
}
|
|
1255
|
-
const { file, old_name: oldName, new_name: newName, others } =
|
|
1864
|
+
const { file, old_name: oldName, new_name: newName, others } = record;
|
|
1256
1865
|
if (typeof file !== "string" || typeof oldName !== "string" || typeof newName !== "string")
|
|
1257
1866
|
return;
|
|
1258
1867
|
const files = new Set([path4.resolve(file)]);
|
|
@@ -1290,6 +1899,14 @@ ${[...paths].sort().map((filePath) => `- ${filePath}`).join(`
|
|
|
1290
1899
|
import { readFile as readFile2, stat as stat3 } from "node:fs/promises";
|
|
1291
1900
|
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
1292
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
|
+
|
|
1293
1910
|
// src/edits.ts
|
|
1294
1911
|
import { lstat, mkdir, open, readFile } from "node:fs/promises";
|
|
1295
1912
|
import path5 from "node:path";
|
|
@@ -1352,9 +1969,9 @@ async function writeHandle(handle, content) {
|
|
|
1352
1969
|
async function writeText(filePath, expected, content) {
|
|
1353
1970
|
await mkdir(path5.dirname(filePath), { recursive: true });
|
|
1354
1971
|
if (!expected.exists) {
|
|
1355
|
-
let
|
|
1972
|
+
let handle;
|
|
1356
1973
|
try {
|
|
1357
|
-
|
|
1974
|
+
handle = await open(filePath, "wx+");
|
|
1358
1975
|
} catch (error) {
|
|
1359
1976
|
if (error.code === "EEXIST") {
|
|
1360
1977
|
throw new Error(`refusing to overwrite changed file: ${filePath}`);
|
|
@@ -1362,9 +1979,9 @@ async function writeText(filePath, expected, content) {
|
|
|
1362
1979
|
throw error;
|
|
1363
1980
|
}
|
|
1364
1981
|
try {
|
|
1365
|
-
await writeHandle(
|
|
1982
|
+
await writeHandle(handle, content);
|
|
1366
1983
|
} finally {
|
|
1367
|
-
await
|
|
1984
|
+
await handle.close();
|
|
1368
1985
|
}
|
|
1369
1986
|
return;
|
|
1370
1987
|
}
|
|
@@ -1608,10 +2225,10 @@ async function authorizeEdit(context, filePaths, diff = "", authorizeExternalPat
|
|
|
1608
2225
|
import { tool } from "@opencode-ai/plugin";
|
|
1609
2226
|
function initialTitle(kind, args) {
|
|
1610
2227
|
switch (kind) {
|
|
1611
|
-
case "
|
|
1612
|
-
return `
|
|
1613
|
-
case "
|
|
1614
|
-
return `
|
|
2228
|
+
case "inspect":
|
|
2229
|
+
return `Inspect ${args.path}`;
|
|
2230
|
+
case "extract":
|
|
2231
|
+
return `Extract ${args.selector} to ${args.output}`;
|
|
1615
2232
|
case "search":
|
|
1616
2233
|
return `Search ${args.pattern}`;
|
|
1617
2234
|
case "grep":
|
|
@@ -1631,9 +2248,9 @@ function initialTitle(kind, args) {
|
|
|
1631
2248
|
function resultPresentation(kind, args, value) {
|
|
1632
2249
|
const output = record(value);
|
|
1633
2250
|
switch (kind) {
|
|
1634
|
-
case "
|
|
1635
|
-
return { title: `
|
|
1636
|
-
case "
|
|
2251
|
+
case "extract":
|
|
2252
|
+
return { title: `Extracted ${args.selector} to ${args.output}`, metadata: {} };
|
|
2253
|
+
case "inspect": {
|
|
1637
2254
|
const map = record(output.map);
|
|
1638
2255
|
const diagnostics = record(output.diagnostics);
|
|
1639
2256
|
const identity = record(output.identity);
|
|
@@ -1666,7 +2283,7 @@ function resultPresentation(kind, args, value) {
|
|
|
1666
2283
|
if (typeof content.end_line === "number")
|
|
1667
2284
|
metadata.end_line = content.end_line;
|
|
1668
2285
|
return {
|
|
1669
|
-
title: summary.length > 0 ? `
|
|
2286
|
+
title: summary.length > 0 ? `Inspected ${args.path} (${summary.join(", ")})` : `Inspected ${args.path}`,
|
|
1670
2287
|
metadata
|
|
1671
2288
|
};
|
|
1672
2289
|
}
|
|
@@ -1706,18 +2323,18 @@ function resultPresentation(kind, args, value) {
|
|
|
1706
2323
|
}
|
|
1707
2324
|
return { title: initialTitle(kind, args), metadata: {} };
|
|
1708
2325
|
}
|
|
1709
|
-
function readseekTool(description, args, kind, execute
|
|
2326
|
+
function readseekTool(description, args, kind, execute) {
|
|
1710
2327
|
return tool({
|
|
1711
2328
|
description,
|
|
1712
2329
|
args,
|
|
1713
|
-
async execute(
|
|
1714
|
-
const title = initialTitle(kind,
|
|
2330
|
+
async execute(args, context) {
|
|
2331
|
+
const title = initialTitle(kind, args);
|
|
1715
2332
|
context.metadata({ title });
|
|
1716
|
-
const result = await execute(
|
|
1717
|
-
const presentation = resultPresentation(kind,
|
|
2333
|
+
const result = await execute(args, context);
|
|
2334
|
+
const presentation = resultPresentation(kind, args, result);
|
|
1718
2335
|
return {
|
|
1719
2336
|
title: presentation.title,
|
|
1720
|
-
|
|
2337
|
+
output: render(result),
|
|
1721
2338
|
metadata: presentation.metadata
|
|
1722
2339
|
};
|
|
1723
2340
|
}
|
|
@@ -1727,52 +2344,18 @@ function readseekTool(description, args, kind, execute, formatResult = (result)
|
|
|
1727
2344
|
// src/tools.ts
|
|
1728
2345
|
function createReadSeekTools(imagePolicy) {
|
|
1729
2346
|
return {
|
|
1730
|
-
|
|
1731
|
-
path: tool2.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
1732
|
-
node: tool2.schema.string().min(1).optional().describe(PARAM_DESCRIPTIONS.node),
|
|
1733
|
-
page: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.page),
|
|
1734
|
-
kind: tool2.schema.enum(DOCUMENT_NODE_KIND_VALUES).optional().describe(PARAM_DESCRIPTIONS.kind),
|
|
1735
|
-
depth: tool2.schema.number().int().nonnegative().optional().describe(PARAM_DESCRIPTIONS.depth),
|
|
1736
|
-
outline: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.outline),
|
|
1737
|
-
visionMode: tool2.schema.enum(VISION_ANALYSIS_MODE_VALUES).optional().describe(PARAM_DESCRIPTIONS.visionModeView),
|
|
1738
|
-
visionLevel: tool2.schema.enum(VISION_LEVEL_VALUES).optional().describe(PARAM_DESCRIPTIONS.visionLevel)
|
|
1739
|
-
}, "view", async (input, context) => {
|
|
1740
|
-
const filePath = resolvePath(context.directory, input.path);
|
|
1741
|
-
await authorizeRead(context, filePath);
|
|
1742
|
-
const visionMode = input.visionMode;
|
|
1743
|
-
const visionLevel = input.visionLevel;
|
|
1744
|
-
const vision = validateVisionLevelVsMode({
|
|
1745
|
-
visionMode,
|
|
1746
|
-
visionLevel,
|
|
1747
|
-
outline: input.outline
|
|
1748
|
-
});
|
|
1749
|
-
if (!vision.ok)
|
|
1750
|
-
throw new Error(vision.error);
|
|
1751
|
-
const cacheDir = await ensureReadSeekCache(context);
|
|
1752
|
-
const args = buildViewArgs({
|
|
1753
|
-
path: filePath,
|
|
1754
|
-
node: input.node,
|
|
1755
|
-
page: input.page,
|
|
1756
|
-
kind: input.kind,
|
|
1757
|
-
depth: input.depth,
|
|
1758
|
-
outline: input.outline,
|
|
1759
|
-
visionMode,
|
|
1760
|
-
visionLevel,
|
|
1761
|
-
readseekDir: cacheDir
|
|
1762
|
-
});
|
|
1763
|
-
return runReadSeekRaw(context, args, visionMode === undefined ? {} : { timeoutMs: DEFAULT_VISION_TIMEOUT_MS });
|
|
1764
|
-
}, (result) => ({ output: boundDocumentOutput(String(result)) })),
|
|
1765
|
-
readseek_digest: readseekTool(openCodeToolDescription("digest"), {
|
|
2347
|
+
readseek_inspect: readseekTool(openCodeToolDescription("inspect"), {
|
|
1766
2348
|
path: tool2.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
2349
|
+
selector: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.selector),
|
|
1767
2350
|
select: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.selectComma),
|
|
1768
|
-
|
|
2351
|
+
query: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.query),
|
|
1769
2352
|
end: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.end),
|
|
1770
2353
|
limit: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.limit),
|
|
1771
|
-
depth: tool2.schema.number().int().nonnegative().optional().describe(PARAM_DESCRIPTIONS.
|
|
2354
|
+
depth: tool2.schema.number().int().nonnegative().optional().describe(PARAM_DESCRIPTIONS.inspectDepth),
|
|
1772
2355
|
language: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.languageOverride),
|
|
1773
|
-
visionMode: tool2.schema.enum(IMAGE_MODES).optional().describe(PARAM_DESCRIPTIONS.
|
|
2356
|
+
visionMode: tool2.schema.enum(IMAGE_MODES).optional().describe(PARAM_DESCRIPTIONS.visionModeInspect),
|
|
1774
2357
|
visionLevel: tool2.schema.enum(VISION_LEVEL_VALUES).optional().describe(PARAM_DESCRIPTIONS.visionLevel)
|
|
1775
|
-
}, "
|
|
2358
|
+
}, "inspect", async (input, context) => {
|
|
1776
2359
|
const filePath = resolvePath(context.directory, input.path);
|
|
1777
2360
|
await authorizeRead(context, filePath);
|
|
1778
2361
|
const select = (input.select ?? "content").trim() || "content";
|
|
@@ -1782,13 +2365,14 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1782
2365
|
if (!vision.ok)
|
|
1783
2366
|
throw new Error(vision.error);
|
|
1784
2367
|
const cacheDir = await ensureReadSeekCache(context);
|
|
1785
|
-
const args =
|
|
2368
|
+
const args = buildInspectArgs({
|
|
1786
2369
|
path: filePath,
|
|
2370
|
+
selector: input.selector,
|
|
1787
2371
|
select,
|
|
1788
|
-
|
|
2372
|
+
query: input.query,
|
|
1789
2373
|
end: input.end,
|
|
1790
2374
|
limit: input.limit,
|
|
1791
|
-
depth: input.depth ?? (select.split(",").some((facet) => facet.trim() === "map") ?
|
|
2375
|
+
depth: input.depth ?? (select.split(",").some((facet) => facet.trim() === "map") ? DEFAULT_INSPECT_MAP_DEPTH : undefined),
|
|
1792
2376
|
language: input.language,
|
|
1793
2377
|
visionMode,
|
|
1794
2378
|
visionLevel,
|
|
@@ -1796,6 +2380,37 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1796
2380
|
});
|
|
1797
2381
|
return runReadSeek(context, args, visionMode === undefined || visionMode === "none" ? {} : { timeoutMs: DEFAULT_VISION_TIMEOUT_MS });
|
|
1798
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
|
+
}),
|
|
1799
2414
|
readseek_grep: readseekTool(openCodeToolDescription("grep"), {
|
|
1800
2415
|
pattern: tool2.schema.string().describe(PARAM_DESCRIPTIONS.grepPattern),
|
|
1801
2416
|
path: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
@@ -1868,7 +2483,7 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1868
2483
|
}
|
|
1869
2484
|
const linesByNumber = new Map;
|
|
1870
2485
|
for (const [start, end] of mergedRanges) {
|
|
1871
|
-
const output = record(await
|
|
2486
|
+
const output = record(await runInspectContent(context, readRangeArgs(filePath, start, end)));
|
|
1872
2487
|
for (const hashline of items(output.hashlines)) {
|
|
1873
2488
|
const line = record(hashline).line;
|
|
1874
2489
|
if (typeof line === "number" && !linesByNumber.has(line))
|
|
@@ -1888,18 +2503,14 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1888
2503
|
pattern: tool2.schema.string().describe(PARAM_DESCRIPTIONS.pattern),
|
|
1889
2504
|
path: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
1890
2505
|
language: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
1891
|
-
|
|
1892
|
-
others: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.others),
|
|
1893
|
-
ignored: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.ignored)
|
|
2506
|
+
gitScope: tool2.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
1894
2507
|
}, "search", async (input, context) => {
|
|
1895
2508
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
1896
2509
|
const args = buildSearchArgs({
|
|
1897
2510
|
path: target,
|
|
1898
2511
|
pattern: input.pattern,
|
|
1899
2512
|
language: input.language,
|
|
1900
|
-
|
|
1901
|
-
others: input.others,
|
|
1902
|
-
ignored: input.ignored
|
|
2513
|
+
gitScope: input.gitScope
|
|
1903
2514
|
});
|
|
1904
2515
|
await authorizeSearch(context, target, input.pattern);
|
|
1905
2516
|
return runReadSeek(context, args);
|
|
@@ -1908,19 +2519,14 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1908
2519
|
name: tool2.schema.string().describe(PARAM_DESCRIPTIONS.name),
|
|
1909
2520
|
path: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
1910
2521
|
language: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
1911
|
-
|
|
1912
|
-
others: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.others),
|
|
1913
|
-
ignored: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.ignored)
|
|
2522
|
+
gitScope: tool2.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
1914
2523
|
}, "def", async (input, context) => {
|
|
1915
2524
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
1916
2525
|
const args = buildDefArgs({
|
|
1917
2526
|
path: target,
|
|
1918
2527
|
name: input.name,
|
|
1919
2528
|
language: input.language,
|
|
1920
|
-
|
|
1921
|
-
cached: input.cached,
|
|
1922
|
-
others: input.others,
|
|
1923
|
-
ignored: input.ignored
|
|
2529
|
+
gitScope: input.gitScope
|
|
1924
2530
|
});
|
|
1925
2531
|
await authorizeSearch(context, target, input.name);
|
|
1926
2532
|
return runReadSeek(context, args);
|
|
@@ -1932,9 +2538,7 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1932
2538
|
scope: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.scope),
|
|
1933
2539
|
line: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.line),
|
|
1934
2540
|
column: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.column),
|
|
1935
|
-
|
|
1936
|
-
others: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.others),
|
|
1937
|
-
ignored: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.ignored)
|
|
2541
|
+
gitScope: tool2.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
1938
2542
|
}, "refs", async (input, context) => {
|
|
1939
2543
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
1940
2544
|
const name = input.name;
|
|
@@ -1945,9 +2549,7 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1945
2549
|
line: input.line,
|
|
1946
2550
|
column: input.column,
|
|
1947
2551
|
language: input.language,
|
|
1948
|
-
|
|
1949
|
-
others: input.others,
|
|
1950
|
-
ignored: input.ignored
|
|
2552
|
+
gitScope: input.gitScope
|
|
1951
2553
|
});
|
|
1952
2554
|
await authorizeSearch(context, target, name ?? "*");
|
|
1953
2555
|
return runReadSeek(context, args);
|
|
@@ -1963,10 +2565,12 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1963
2565
|
const filePath = resolvePath(context.directory, input.path);
|
|
1964
2566
|
await authorizeRead(context, filePath);
|
|
1965
2567
|
if (input.workspace) {
|
|
1966
|
-
const
|
|
1967
|
-
|
|
2568
|
+
const column = input.column;
|
|
2569
|
+
const identifyArgs = inspectArgs(filePath, "identity", {
|
|
2570
|
+
selector: `line:${input.line}${column === undefined ? "" : `:${column}`}`
|
|
2571
|
+
});
|
|
1968
2572
|
const envelope = await runReadSeek(context, identifyArgs);
|
|
1969
|
-
const name = identifiedName(
|
|
2573
|
+
const name = identifiedName(requireInspectFacet(envelope, "identity"));
|
|
1970
2574
|
if (!name)
|
|
1971
2575
|
throw new Error("readseek could not identify a binding at the rename cursor");
|
|
1972
2576
|
await authorizeSearch(context, context.directory, name);
|
|
@@ -1981,7 +2585,7 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1981
2585
|
const plan = await runReadSeek(context, args);
|
|
1982
2586
|
if (input.apply === false)
|
|
1983
2587
|
return plan;
|
|
1984
|
-
const files = renameFiles(plan);
|
|
2588
|
+
const files = renameFiles(plan, context.directory);
|
|
1985
2589
|
const planHash = record(plan).plan_hash;
|
|
1986
2590
|
if (typeof planHash !== "string" || planHash.length === 0) {
|
|
1987
2591
|
throw new Error("readseek rename plan did not include a plan hash");
|
|
@@ -2045,7 +2649,7 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2045
2649
|
await authorizeRead(context, filePath);
|
|
2046
2650
|
await rejectSymlinkMutation(filePath);
|
|
2047
2651
|
const edits = input.edits;
|
|
2048
|
-
const request =
|
|
2652
|
+
const request = serializeEditPlan(edits);
|
|
2049
2653
|
const baseArgs = buildEditArgs({
|
|
2050
2654
|
path: filePath,
|
|
2051
2655
|
language: input.language,
|
|
@@ -2097,19 +2701,53 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2097
2701
|
await authorizeEdit(context, [filePath], simpleDiff(filePath, before.content, content), false);
|
|
2098
2702
|
context.abort.throwIfAborted();
|
|
2099
2703
|
await writeText(filePath, before, content);
|
|
2100
|
-
return postMutationPreview(context,
|
|
2704
|
+
return postMutationPreview(context, inspectArgs(filePath, "content", { end: DEFAULT_READ_LIMIT }), filePath, !before.exists || before.content !== content);
|
|
2101
2705
|
});
|
|
2102
2706
|
})
|
|
2103
2707
|
};
|
|
2104
2708
|
}
|
|
2105
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
|
+
|
|
2106
2744
|
// index.ts
|
|
2107
2745
|
var ReadSeekPlugin = async (_input, options) => {
|
|
2108
2746
|
const anchors = new SessionAnchors;
|
|
2109
2747
|
const imagePolicy = resolveImagePolicy(options);
|
|
2110
2748
|
return {
|
|
2111
2749
|
tool: createReadSeekTools(imagePolicy),
|
|
2112
|
-
"experimental.chat.system.transform": async (
|
|
2750
|
+
"experimental.chat.system.transform": async (_input, output) => {
|
|
2113
2751
|
output.system.push(TOOL_POLICY);
|
|
2114
2752
|
},
|
|
2115
2753
|
"tool.definition": async (input, output) => {
|
|
@@ -2155,8 +2793,8 @@ var ReadSeekPlugin = async (_input, options) => {
|
|
|
2155
2793
|
}
|
|
2156
2794
|
};
|
|
2157
2795
|
};
|
|
2158
|
-
var opencode_readseek_default = {
|
|
2796
|
+
var opencode_readseek_default = { ...ReadSeekV2Plugin, server: ReadSeekPlugin };
|
|
2159
2797
|
export {
|
|
2160
|
-
|
|
2161
|
-
|
|
2798
|
+
ReadSeekPlugin,
|
|
2799
|
+
opencode_readseek_default as default
|
|
2162
2800
|
};
|