opencode-readseek 0.9.16 → 0.10.1
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 +934 -290
- 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,260 @@ 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
|
|
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
|
+
function mapNodeShape(child) {
|
|
1424
|
+
return Type.Object({
|
|
1425
|
+
kind: Str,
|
|
1426
|
+
name: Str,
|
|
1427
|
+
start_line: Int,
|
|
1428
|
+
end_line: Int,
|
|
1429
|
+
start_anchor: Str,
|
|
1430
|
+
end_anchor: Str,
|
|
1431
|
+
children: Type.Array(child)
|
|
1432
|
+
});
|
|
996
1433
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
var
|
|
1001
|
-
var
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1434
|
+
function recursiveMapNode() {
|
|
1435
|
+
return Type.Recursive((self) => mapNodeShape(self));
|
|
1436
|
+
}
|
|
1437
|
+
var MapNode = typeof Type.Recursive === "function" ? recursiveMapNode() : Type.Cyclic({ MapNode: mapNodeShape(Type.Ref("MapNode")) }, "MapNode");
|
|
1438
|
+
var MapOutput = Type.Object({
|
|
1439
|
+
file: Str,
|
|
1440
|
+
language: Str,
|
|
1441
|
+
line_count: Int,
|
|
1442
|
+
file_hash: Str,
|
|
1443
|
+
root: Type.Optional(MapNode),
|
|
1444
|
+
symbols: Type.Array(SymbolOutput)
|
|
1445
|
+
});
|
|
1446
|
+
var SearchSpan = {
|
|
1447
|
+
start_byte: OptInt,
|
|
1448
|
+
end_byte: OptInt,
|
|
1449
|
+
text: OptStr,
|
|
1450
|
+
start_line: Int,
|
|
1451
|
+
end_line: Int,
|
|
1452
|
+
start_hash: Str,
|
|
1453
|
+
end_hash: Str,
|
|
1454
|
+
hashlines: Hashlines
|
|
1455
|
+
};
|
|
1456
|
+
var SearchCapture = Type.Object({ name: Str, ...SearchSpan });
|
|
1457
|
+
var SearchMatch = Type.Object({
|
|
1458
|
+
pattern_index: Type.Integer({ default: 0 }),
|
|
1459
|
+
...SearchSpan,
|
|
1460
|
+
captures: Type.Array(SearchCapture)
|
|
1461
|
+
});
|
|
1462
|
+
var SearchFileOutput = Type.Object({
|
|
1463
|
+
file: Str,
|
|
1464
|
+
language: Str,
|
|
1465
|
+
file_hash: Str,
|
|
1466
|
+
matches: Type.Array(SearchMatch)
|
|
1467
|
+
});
|
|
1468
|
+
var SearchEnvelope = Type.Object({
|
|
1469
|
+
results: Type.Array(SearchFileOutput),
|
|
1470
|
+
returned_matches: OptInt,
|
|
1471
|
+
truncated: Type.Optional(Bool),
|
|
1472
|
+
byte_basis: OptStr
|
|
1473
|
+
});
|
|
1474
|
+
var RefsEnvelope = Type.Object({
|
|
1475
|
+
references: Type.Array(Type.Object({
|
|
1476
|
+
file: Str,
|
|
1477
|
+
line: Int,
|
|
1478
|
+
column: Int,
|
|
1479
|
+
line_hash: Str,
|
|
1480
|
+
text: Str,
|
|
1481
|
+
symbol: Type.Optional(Type.Object({ qualified_name: OptStr }))
|
|
1482
|
+
}))
|
|
1483
|
+
});
|
|
1484
|
+
var Diagnostic = Type.Object({
|
|
1485
|
+
kind: Type.Union([Type.Literal("error"), Type.Literal("missing")]),
|
|
1486
|
+
start_line: Int,
|
|
1487
|
+
end_line: Int
|
|
1488
|
+
});
|
|
1489
|
+
var CheckEnvelope = Type.Object({
|
|
1490
|
+
file_hash: OptStr,
|
|
1491
|
+
error_count: Int,
|
|
1492
|
+
missing_count: Int,
|
|
1493
|
+
diagnostics: Type.Array(Diagnostic)
|
|
1494
|
+
});
|
|
1495
|
+
var DetectedObject = Type.Object({
|
|
1496
|
+
label: Str,
|
|
1497
|
+
bbox: Type.Tuple([Int, Int, Int, Int])
|
|
1498
|
+
});
|
|
1499
|
+
var PreparedImage = Type.Object({
|
|
1500
|
+
mime: Str,
|
|
1501
|
+
encoding: Type.Literal("base64"),
|
|
1502
|
+
data: Str
|
|
1503
|
+
});
|
|
1504
|
+
var SourceDetection = Type.Object({
|
|
1505
|
+
kind: Type.Literal("source"),
|
|
1506
|
+
type: Str,
|
|
1507
|
+
file: Str,
|
|
1508
|
+
language: Str,
|
|
1509
|
+
engine: OptStr,
|
|
1510
|
+
supported: Bool,
|
|
1511
|
+
mime: OptStr,
|
|
1512
|
+
syntax: OptStr
|
|
1513
|
+
});
|
|
1514
|
+
var ImageDetection = Type.Object({
|
|
1515
|
+
kind: Type.Literal("image"),
|
|
1516
|
+
type: Str,
|
|
1517
|
+
file: Str,
|
|
1518
|
+
mime: OptStr,
|
|
1519
|
+
format: Str,
|
|
1520
|
+
width: Int,
|
|
1521
|
+
height: Int,
|
|
1522
|
+
animated: Bool,
|
|
1523
|
+
encoding: Type.Optional(Type.Literal("base64")),
|
|
1524
|
+
data: OptStr,
|
|
1525
|
+
ocr: OptStr,
|
|
1526
|
+
caption: OptStr,
|
|
1527
|
+
objects: Type.Optional(Type.Array(DetectedObject))
|
|
1528
|
+
});
|
|
1529
|
+
var TextDetection = Type.Object({
|
|
1530
|
+
kind: Type.Literal("text"),
|
|
1531
|
+
type: Str,
|
|
1532
|
+
file: Str,
|
|
1533
|
+
mime: OptStr
|
|
1534
|
+
});
|
|
1535
|
+
var PdfDetection = Type.Object({
|
|
1536
|
+
kind: Type.Literal("pdf"),
|
|
1537
|
+
type: Type.Literal("application/pdf"),
|
|
1538
|
+
file: Str,
|
|
1539
|
+
mime: OptStr,
|
|
1540
|
+
format: Type.Literal("pdf"),
|
|
1541
|
+
pages: Int
|
|
1542
|
+
});
|
|
1543
|
+
var Detection = Type.Union([SourceDetection, ImageDetection, TextDetection, PdfDetection]);
|
|
1544
|
+
var SourceDetectionInput = Type.Omit(SourceDetection, ["kind"]);
|
|
1545
|
+
var ImageDetectionInput = Type.Omit(ImageDetection, ["kind"]);
|
|
1546
|
+
var TextDetectionInput = Type.Omit(TextDetection, ["kind"]);
|
|
1547
|
+
var PdfDetectionInput = Type.Omit(PdfDetection, ["kind"]);
|
|
1548
|
+
var UnspannedContent = Type.Omit(ReadOutput, ["start_line", "end_line"]);
|
|
1549
|
+
var PdfInspectMetadata = Type.Object({ source: Str, format: Type.Literal("pdf"), pages: Int });
|
|
1550
|
+
var DocumentCache = Type.Object({
|
|
1551
|
+
content_hash: Str,
|
|
1552
|
+
state: Type.Union([Type.Literal("built"), Type.Literal("reused")])
|
|
1553
|
+
});
|
|
1554
|
+
var Identifier = Type.Object({
|
|
1555
|
+
text: Str,
|
|
1556
|
+
start_column: Int,
|
|
1557
|
+
end_column: Int,
|
|
1558
|
+
start_byte: Int,
|
|
1559
|
+
end_byte: Int
|
|
1560
|
+
});
|
|
1561
|
+
var IdentifyEnvelope = Type.Object({
|
|
1562
|
+
file: Str,
|
|
1563
|
+
language: Str,
|
|
1564
|
+
engine: OptStr,
|
|
1565
|
+
line_count: Int,
|
|
1566
|
+
file_hash: Str,
|
|
1567
|
+
line: Int,
|
|
1568
|
+
column: Int,
|
|
1569
|
+
line_hash: Str,
|
|
1570
|
+
identifier: Type.Optional(Identifier),
|
|
1571
|
+
symbol: Type.Optional(Type.Object({
|
|
1572
|
+
name: Str,
|
|
1573
|
+
kind: Str,
|
|
1574
|
+
qualified_name: Str,
|
|
1575
|
+
start_line: Int,
|
|
1576
|
+
end_line: Int
|
|
1577
|
+
}))
|
|
1578
|
+
});
|
|
1579
|
+
var EditOutputSchema = Type.Object({
|
|
1580
|
+
file: Str,
|
|
1581
|
+
before_hash: Str,
|
|
1582
|
+
file_hash: Str,
|
|
1583
|
+
plan_hash: Str,
|
|
1584
|
+
applied: Bool,
|
|
1585
|
+
changed: Bool,
|
|
1586
|
+
edit_count: Int,
|
|
1587
|
+
before_content: OptStr,
|
|
1588
|
+
content: OptStr
|
|
1589
|
+
});
|
|
1590
|
+
var RenameConflictSchema = Type.Object({ line: Int, column: Int, reason: Str });
|
|
1591
|
+
var RenameEditSchema = Type.Object({
|
|
1592
|
+
line: Int,
|
|
1593
|
+
start_column: Int,
|
|
1594
|
+
end_column: Int,
|
|
1595
|
+
start_byte: Int,
|
|
1596
|
+
end_byte: Int,
|
|
1597
|
+
occurrence: Str,
|
|
1598
|
+
line_hash: Str,
|
|
1599
|
+
text: Str
|
|
1600
|
+
});
|
|
1601
|
+
var Conflicts = Type.Array(RenameConflictSchema, { default: [] });
|
|
1602
|
+
var Edits = Type.Array(RenameEditSchema, { default: [] });
|
|
1603
|
+
var RenameFileOutputSchema = Type.Object({
|
|
1604
|
+
file: Str,
|
|
1605
|
+
language: Str,
|
|
1606
|
+
engine: OptStr,
|
|
1607
|
+
file_hash: Str,
|
|
1608
|
+
conflicts: Conflicts,
|
|
1609
|
+
edits: Edits
|
|
1610
|
+
});
|
|
1611
|
+
var RenameOutputSchema = Type.Object({
|
|
1612
|
+
file: Str,
|
|
1613
|
+
language: Str,
|
|
1614
|
+
engine: OptStr,
|
|
1615
|
+
file_hash: Str,
|
|
1616
|
+
old_name: Str,
|
|
1617
|
+
new_name: Str,
|
|
1618
|
+
plan_hash: Str,
|
|
1619
|
+
applied: Bool,
|
|
1620
|
+
conflicts: Conflicts,
|
|
1621
|
+
edits: Edits,
|
|
1622
|
+
others: Type.Array(RenameFileOutputSchema, { default: [] })
|
|
1623
|
+
});
|
|
1624
|
+
var DefEnvelope = Type.Object({
|
|
1625
|
+
locations: Type.Array(Type.Object({
|
|
1626
|
+
file: Str,
|
|
1627
|
+
line: Int,
|
|
1628
|
+
column: Int,
|
|
1629
|
+
line_hash: Str,
|
|
1630
|
+
text: Str,
|
|
1631
|
+
kind: OptStr,
|
|
1632
|
+
name: OptStr,
|
|
1633
|
+
qualified_name: OptStr
|
|
1634
|
+
}))
|
|
1635
|
+
});
|
|
1009
1636
|
// src/util.ts
|
|
1010
1637
|
import path3 from "node:path";
|
|
1011
1638
|
function render(value) {
|
|
@@ -1041,15 +1668,17 @@ function collectAnchoredFiles(value, files) {
|
|
|
1041
1668
|
for (const item of Object.values(output))
|
|
1042
1669
|
collectAnchoredFiles(item, files);
|
|
1043
1670
|
}
|
|
1044
|
-
function renameFiles(value) {
|
|
1671
|
+
function renameFiles(value, directory = process.cwd()) {
|
|
1045
1672
|
const output = record(value);
|
|
1046
1673
|
const files = [];
|
|
1047
|
-
if (typeof output.file === "string" && items(output.edits).length > 0)
|
|
1048
|
-
files.push(path3.resolve(output.file));
|
|
1674
|
+
if (typeof output.file === "string" && items(output.edits).length > 0) {
|
|
1675
|
+
files.push(path3.resolve(directory, output.file));
|
|
1676
|
+
}
|
|
1049
1677
|
for (const other of items(output.others)) {
|
|
1050
1678
|
const item = record(other);
|
|
1051
|
-
if (typeof item.file === "string" && items(item.edits).length > 0)
|
|
1052
|
-
files.push(path3.resolve(item.file));
|
|
1679
|
+
if (typeof item.file === "string" && items(item.edits).length > 0) {
|
|
1680
|
+
files.push(path3.resolve(directory, item.file));
|
|
1681
|
+
}
|
|
1053
1682
|
}
|
|
1054
1683
|
return files;
|
|
1055
1684
|
}
|
|
@@ -1099,25 +1728,21 @@ function resolveImagePolicy(options) {
|
|
|
1099
1728
|
return value;
|
|
1100
1729
|
throw new Error('opencode-readseek imageMode must be "on", "auto", or "off"');
|
|
1101
1730
|
}
|
|
1102
|
-
function
|
|
1103
|
-
return
|
|
1731
|
+
function inspectArgs(filePath, select, options = {}) {
|
|
1732
|
+
return buildInspectArgs({ path: filePath, select, ...options });
|
|
1104
1733
|
}
|
|
1105
|
-
function
|
|
1734
|
+
function requireInspectFacet(envelope, facet) {
|
|
1106
1735
|
const value = record(envelope)[facet];
|
|
1107
1736
|
if (value === undefined || value === null || typeof value !== "object") {
|
|
1108
|
-
throw new Error(`
|
|
1737
|
+
throw new Error(`inspect returned no ${facet} facet`);
|
|
1109
1738
|
}
|
|
1110
1739
|
return value;
|
|
1111
1740
|
}
|
|
1112
|
-
function withAt(args, line, column) {
|
|
1113
|
-
args.push("--at", buildLineLocation(line, column));
|
|
1114
|
-
}
|
|
1115
1741
|
function readRangeArgs(filePath, start, end) {
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
return digestArgs(filePath, "content", extra);
|
|
1742
|
+
return inspectArgs(filePath, "content", {
|
|
1743
|
+
selector: start > 1 ? `line:${start}` : undefined,
|
|
1744
|
+
end
|
|
1745
|
+
});
|
|
1121
1746
|
}
|
|
1122
1747
|
function readSeekBinaryPath() {
|
|
1123
1748
|
return resolveReadSeekBinaryOverride() ?? resolveReadSeekBinaryFromPackages(resolveFromHost);
|
|
@@ -1146,7 +1771,7 @@ async function runReadSeekRaw(context, args, options = {}) {
|
|
|
1146
1771
|
}
|
|
1147
1772
|
async function runReadSeek(context, args, options = {}) {
|
|
1148
1773
|
context.abort.throwIfAborted();
|
|
1149
|
-
return preserveAbortReason(context.abort, options.cancelable !== false, () =>
|
|
1774
|
+
return preserveAbortReason(context.abort, options.cancelable !== false, () => runReadSeekBunKdl(readSeekBinaryPath(), args, {
|
|
1150
1775
|
cwd: context.directory,
|
|
1151
1776
|
signal: context.abort,
|
|
1152
1777
|
cancelable: options.cancelable,
|
|
@@ -1154,12 +1779,12 @@ async function runReadSeek(context, args, options = {}) {
|
|
|
1154
1779
|
timeoutMs: options.timeoutMs
|
|
1155
1780
|
}));
|
|
1156
1781
|
}
|
|
1157
|
-
async function
|
|
1158
|
-
return
|
|
1782
|
+
async function runInspectContent(context, args, options = {}) {
|
|
1783
|
+
return requireInspectFacet(await runReadSeek(context, args, options), "content");
|
|
1159
1784
|
}
|
|
1160
1785
|
async function postMutationPreview(context, args, filePath, changed) {
|
|
1161
1786
|
try {
|
|
1162
|
-
return await
|
|
1787
|
+
return await runInspectContent(context, args, {
|
|
1163
1788
|
cancelable: false,
|
|
1164
1789
|
timeoutMs: DEFAULT_READSEEK_TIMEOUT_MS
|
|
1165
1790
|
});
|
|
@@ -1188,21 +1813,11 @@ async function ensureReadSeekCache(context) {
|
|
|
1188
1813
|
if (error.code !== "ENOENT")
|
|
1189
1814
|
throw error;
|
|
1190
1815
|
}
|
|
1191
|
-
readSeekCacheInit ??= runReadSeekRaw(context, ["--readseek-dir", cacheDir, "init"]).then(() => cacheDir).finally(() => {
|
|
1816
|
+
readSeekCacheInit ??= runReadSeekRaw(context, ["--readseek-dir", cacheDir, "init"], { cancelable: false }).then(() => cacheDir).finally(() => {
|
|
1192
1817
|
readSeekCacheInit = undefined;
|
|
1193
1818
|
});
|
|
1194
1819
|
return readSeekCacheInit;
|
|
1195
1820
|
}
|
|
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
1821
|
|
|
1207
1822
|
// src/policy.ts
|
|
1208
1823
|
var OPENCODE_TOOL_ALIASES = Object.fromEntries(TOOL_NAMES.map((name) => [name, `readseek_${name}`]));
|
|
@@ -1247,12 +1862,12 @@ class SessionAnchors {
|
|
|
1247
1862
|
planRename(sessionID, output) {
|
|
1248
1863
|
if (!output || typeof output !== "object")
|
|
1249
1864
|
return;
|
|
1250
|
-
const
|
|
1251
|
-
if (
|
|
1865
|
+
const record = output;
|
|
1866
|
+
if (record.applied === true) {
|
|
1252
1867
|
this.#renamePlans.delete(sessionID);
|
|
1253
1868
|
return;
|
|
1254
1869
|
}
|
|
1255
|
-
const { file, old_name: oldName, new_name: newName, others } =
|
|
1870
|
+
const { file, old_name: oldName, new_name: newName, others } = record;
|
|
1256
1871
|
if (typeof file !== "string" || typeof oldName !== "string" || typeof newName !== "string")
|
|
1257
1872
|
return;
|
|
1258
1873
|
const files = new Set([path4.resolve(file)]);
|
|
@@ -1290,6 +1905,14 @@ ${[...paths].sort().map((filePath) => `- ${filePath}`).join(`
|
|
|
1290
1905
|
import { readFile as readFile2, stat as stat3 } from "node:fs/promises";
|
|
1291
1906
|
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
1292
1907
|
|
|
1908
|
+
// src/constants.ts
|
|
1909
|
+
var DEFAULT_VISION_TIMEOUT_MS = 30 * 60 * 1000;
|
|
1910
|
+
var DEFAULT_READ_LIMIT = 2000;
|
|
1911
|
+
var EDIT_RESULT_READ_LIMIT = 40;
|
|
1912
|
+
var MAX_PERMISSION_DIFF_BYTES = 32 * 1024;
|
|
1913
|
+
var IMAGE_MODES = [...VISION_MODES];
|
|
1914
|
+
var VISION_LEVEL_VALUES = [...VISION_LEVELS];
|
|
1915
|
+
|
|
1293
1916
|
// src/edits.ts
|
|
1294
1917
|
import { lstat, mkdir, open, readFile } from "node:fs/promises";
|
|
1295
1918
|
import path5 from "node:path";
|
|
@@ -1352,9 +1975,9 @@ async function writeHandle(handle, content) {
|
|
|
1352
1975
|
async function writeText(filePath, expected, content) {
|
|
1353
1976
|
await mkdir(path5.dirname(filePath), { recursive: true });
|
|
1354
1977
|
if (!expected.exists) {
|
|
1355
|
-
let
|
|
1978
|
+
let handle;
|
|
1356
1979
|
try {
|
|
1357
|
-
|
|
1980
|
+
handle = await open(filePath, "wx+");
|
|
1358
1981
|
} catch (error) {
|
|
1359
1982
|
if (error.code === "EEXIST") {
|
|
1360
1983
|
throw new Error(`refusing to overwrite changed file: ${filePath}`);
|
|
@@ -1362,9 +1985,9 @@ async function writeText(filePath, expected, content) {
|
|
|
1362
1985
|
throw error;
|
|
1363
1986
|
}
|
|
1364
1987
|
try {
|
|
1365
|
-
await writeHandle(
|
|
1988
|
+
await writeHandle(handle, content);
|
|
1366
1989
|
} finally {
|
|
1367
|
-
await
|
|
1990
|
+
await handle.close();
|
|
1368
1991
|
}
|
|
1369
1992
|
return;
|
|
1370
1993
|
}
|
|
@@ -1608,10 +2231,10 @@ async function authorizeEdit(context, filePaths, diff = "", authorizeExternalPat
|
|
|
1608
2231
|
import { tool } from "@opencode-ai/plugin";
|
|
1609
2232
|
function initialTitle(kind, args) {
|
|
1610
2233
|
switch (kind) {
|
|
1611
|
-
case "
|
|
1612
|
-
return `
|
|
1613
|
-
case "
|
|
1614
|
-
return `
|
|
2234
|
+
case "inspect":
|
|
2235
|
+
return `Inspect ${args.path}`;
|
|
2236
|
+
case "extract":
|
|
2237
|
+
return `Extract ${args.selector} to ${args.output}`;
|
|
1615
2238
|
case "search":
|
|
1616
2239
|
return `Search ${args.pattern}`;
|
|
1617
2240
|
case "grep":
|
|
@@ -1631,9 +2254,9 @@ function initialTitle(kind, args) {
|
|
|
1631
2254
|
function resultPresentation(kind, args, value) {
|
|
1632
2255
|
const output = record(value);
|
|
1633
2256
|
switch (kind) {
|
|
1634
|
-
case "
|
|
1635
|
-
return { title: `
|
|
1636
|
-
case "
|
|
2257
|
+
case "extract":
|
|
2258
|
+
return { title: `Extracted ${args.selector} to ${args.output}`, metadata: {} };
|
|
2259
|
+
case "inspect": {
|
|
1637
2260
|
const map = record(output.map);
|
|
1638
2261
|
const diagnostics = record(output.diagnostics);
|
|
1639
2262
|
const identity = record(output.identity);
|
|
@@ -1666,7 +2289,7 @@ function resultPresentation(kind, args, value) {
|
|
|
1666
2289
|
if (typeof content.end_line === "number")
|
|
1667
2290
|
metadata.end_line = content.end_line;
|
|
1668
2291
|
return {
|
|
1669
|
-
title: summary.length > 0 ? `
|
|
2292
|
+
title: summary.length > 0 ? `Inspected ${args.path} (${summary.join(", ")})` : `Inspected ${args.path}`,
|
|
1670
2293
|
metadata
|
|
1671
2294
|
};
|
|
1672
2295
|
}
|
|
@@ -1706,18 +2329,18 @@ function resultPresentation(kind, args, value) {
|
|
|
1706
2329
|
}
|
|
1707
2330
|
return { title: initialTitle(kind, args), metadata: {} };
|
|
1708
2331
|
}
|
|
1709
|
-
function readseekTool(description, args, kind, execute
|
|
2332
|
+
function readseekTool(description, args, kind, execute) {
|
|
1710
2333
|
return tool({
|
|
1711
2334
|
description,
|
|
1712
2335
|
args,
|
|
1713
|
-
async execute(
|
|
1714
|
-
const title = initialTitle(kind,
|
|
2336
|
+
async execute(args, context) {
|
|
2337
|
+
const title = initialTitle(kind, args);
|
|
1715
2338
|
context.metadata({ title });
|
|
1716
|
-
const result = await execute(
|
|
1717
|
-
const presentation = resultPresentation(kind,
|
|
2339
|
+
const result = await execute(args, context);
|
|
2340
|
+
const presentation = resultPresentation(kind, args, result);
|
|
1718
2341
|
return {
|
|
1719
2342
|
title: presentation.title,
|
|
1720
|
-
|
|
2343
|
+
output: render(result),
|
|
1721
2344
|
metadata: presentation.metadata
|
|
1722
2345
|
};
|
|
1723
2346
|
}
|
|
@@ -1727,52 +2350,18 @@ function readseekTool(description, args, kind, execute, formatResult = (result)
|
|
|
1727
2350
|
// src/tools.ts
|
|
1728
2351
|
function createReadSeekTools(imagePolicy) {
|
|
1729
2352
|
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"), {
|
|
2353
|
+
readseek_inspect: readseekTool(openCodeToolDescription("inspect"), {
|
|
1766
2354
|
path: tool2.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
2355
|
+
selector: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.selector),
|
|
1767
2356
|
select: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.selectComma),
|
|
1768
|
-
|
|
2357
|
+
query: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.query),
|
|
1769
2358
|
end: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.end),
|
|
1770
2359
|
limit: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.limit),
|
|
1771
|
-
depth: tool2.schema.number().int().nonnegative().optional().describe(PARAM_DESCRIPTIONS.
|
|
2360
|
+
depth: tool2.schema.number().int().nonnegative().optional().describe(PARAM_DESCRIPTIONS.inspectDepth),
|
|
1772
2361
|
language: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.languageOverride),
|
|
1773
|
-
visionMode: tool2.schema.enum(IMAGE_MODES).optional().describe(PARAM_DESCRIPTIONS.
|
|
2362
|
+
visionMode: tool2.schema.enum(IMAGE_MODES).optional().describe(PARAM_DESCRIPTIONS.visionModeInspect),
|
|
1774
2363
|
visionLevel: tool2.schema.enum(VISION_LEVEL_VALUES).optional().describe(PARAM_DESCRIPTIONS.visionLevel)
|
|
1775
|
-
}, "
|
|
2364
|
+
}, "inspect", async (input, context) => {
|
|
1776
2365
|
const filePath = resolvePath(context.directory, input.path);
|
|
1777
2366
|
await authorizeRead(context, filePath);
|
|
1778
2367
|
const select = (input.select ?? "content").trim() || "content";
|
|
@@ -1782,13 +2371,14 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1782
2371
|
if (!vision.ok)
|
|
1783
2372
|
throw new Error(vision.error);
|
|
1784
2373
|
const cacheDir = await ensureReadSeekCache(context);
|
|
1785
|
-
const args =
|
|
2374
|
+
const args = buildInspectArgs({
|
|
1786
2375
|
path: filePath,
|
|
2376
|
+
selector: input.selector,
|
|
1787
2377
|
select,
|
|
1788
|
-
|
|
2378
|
+
query: input.query,
|
|
1789
2379
|
end: input.end,
|
|
1790
2380
|
limit: input.limit,
|
|
1791
|
-
depth: input.depth ?? (select.split(",").some((facet) => facet.trim() === "map") ?
|
|
2381
|
+
depth: input.depth ?? (select.split(",").some((facet) => facet.trim() === "map") ? DEFAULT_INSPECT_MAP_DEPTH : undefined),
|
|
1792
2382
|
language: input.language,
|
|
1793
2383
|
visionMode,
|
|
1794
2384
|
visionLevel,
|
|
@@ -1796,6 +2386,37 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1796
2386
|
});
|
|
1797
2387
|
return runReadSeek(context, args, visionMode === undefined || visionMode === "none" ? {} : { timeoutMs: DEFAULT_VISION_TIMEOUT_MS });
|
|
1798
2388
|
}),
|
|
2389
|
+
readseek_extract: readseekTool(openCodeToolDescription("extract"), {
|
|
2390
|
+
path: tool2.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
2391
|
+
selector: tool2.schema.string().min(1).describe(PARAM_DESCRIPTIONS.assetSelector),
|
|
2392
|
+
output: tool2.schema.string().min(1).describe(PARAM_DESCRIPTIONS.output)
|
|
2393
|
+
}, "extract", async (input, context) => {
|
|
2394
|
+
const selector = String(input.selector ?? "").trim();
|
|
2395
|
+
const output = String(input.output ?? "").trim();
|
|
2396
|
+
if (!selector.startsWith("asset:") || selector === "asset:") {
|
|
2397
|
+
throw new Error("extract requires an asset: selector");
|
|
2398
|
+
}
|
|
2399
|
+
if (!output || output === "-")
|
|
2400
|
+
throw new Error("extract output must be a file path");
|
|
2401
|
+
const filePath = resolvePath(context.directory, input.path);
|
|
2402
|
+
const outputPath = resolvePath(context.directory, output);
|
|
2403
|
+
await authorizeRead(context, filePath);
|
|
2404
|
+
await rejectSymlinkMutation(outputPath);
|
|
2405
|
+
await authorizeEdit(context, [outputPath]);
|
|
2406
|
+
const cacheDir = await ensureReadSeekCache(context);
|
|
2407
|
+
await runReadSeekRaw(context, buildExtractArgs({
|
|
2408
|
+
path: filePath,
|
|
2409
|
+
selector,
|
|
2410
|
+
output: outputPath,
|
|
2411
|
+
readseekDir: cacheDir
|
|
2412
|
+
}), { cancelable: false, timeoutMs: 120000 });
|
|
2413
|
+
return {
|
|
2414
|
+
file: filePath,
|
|
2415
|
+
selector,
|
|
2416
|
+
output: outputPath,
|
|
2417
|
+
changed: true
|
|
2418
|
+
};
|
|
2419
|
+
}),
|
|
1799
2420
|
readseek_grep: readseekTool(openCodeToolDescription("grep"), {
|
|
1800
2421
|
pattern: tool2.schema.string().describe(PARAM_DESCRIPTIONS.grepPattern),
|
|
1801
2422
|
path: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
@@ -1868,7 +2489,7 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1868
2489
|
}
|
|
1869
2490
|
const linesByNumber = new Map;
|
|
1870
2491
|
for (const [start, end] of mergedRanges) {
|
|
1871
|
-
const output = record(await
|
|
2492
|
+
const output = record(await runInspectContent(context, readRangeArgs(filePath, start, end)));
|
|
1872
2493
|
for (const hashline of items(output.hashlines)) {
|
|
1873
2494
|
const line = record(hashline).line;
|
|
1874
2495
|
if (typeof line === "number" && !linesByNumber.has(line))
|
|
@@ -1888,18 +2509,14 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1888
2509
|
pattern: tool2.schema.string().describe(PARAM_DESCRIPTIONS.pattern),
|
|
1889
2510
|
path: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
1890
2511
|
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)
|
|
2512
|
+
gitScope: tool2.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
1894
2513
|
}, "search", async (input, context) => {
|
|
1895
2514
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
1896
2515
|
const args = buildSearchArgs({
|
|
1897
2516
|
path: target,
|
|
1898
2517
|
pattern: input.pattern,
|
|
1899
2518
|
language: input.language,
|
|
1900
|
-
|
|
1901
|
-
others: input.others,
|
|
1902
|
-
ignored: input.ignored
|
|
2519
|
+
gitScope: input.gitScope
|
|
1903
2520
|
});
|
|
1904
2521
|
await authorizeSearch(context, target, input.pattern);
|
|
1905
2522
|
return runReadSeek(context, args);
|
|
@@ -1908,19 +2525,14 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1908
2525
|
name: tool2.schema.string().describe(PARAM_DESCRIPTIONS.name),
|
|
1909
2526
|
path: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
1910
2527
|
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)
|
|
2528
|
+
gitScope: tool2.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
1914
2529
|
}, "def", async (input, context) => {
|
|
1915
2530
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
1916
2531
|
const args = buildDefArgs({
|
|
1917
2532
|
path: target,
|
|
1918
2533
|
name: input.name,
|
|
1919
2534
|
language: input.language,
|
|
1920
|
-
|
|
1921
|
-
cached: input.cached,
|
|
1922
|
-
others: input.others,
|
|
1923
|
-
ignored: input.ignored
|
|
2535
|
+
gitScope: input.gitScope
|
|
1924
2536
|
});
|
|
1925
2537
|
await authorizeSearch(context, target, input.name);
|
|
1926
2538
|
return runReadSeek(context, args);
|
|
@@ -1932,9 +2544,7 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1932
2544
|
scope: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.scope),
|
|
1933
2545
|
line: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.line),
|
|
1934
2546
|
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)
|
|
2547
|
+
gitScope: tool2.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
1938
2548
|
}, "refs", async (input, context) => {
|
|
1939
2549
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
1940
2550
|
const name = input.name;
|
|
@@ -1945,9 +2555,7 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1945
2555
|
line: input.line,
|
|
1946
2556
|
column: input.column,
|
|
1947
2557
|
language: input.language,
|
|
1948
|
-
|
|
1949
|
-
others: input.others,
|
|
1950
|
-
ignored: input.ignored
|
|
2558
|
+
gitScope: input.gitScope
|
|
1951
2559
|
});
|
|
1952
2560
|
await authorizeSearch(context, target, name ?? "*");
|
|
1953
2561
|
return runReadSeek(context, args);
|
|
@@ -1963,10 +2571,12 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1963
2571
|
const filePath = resolvePath(context.directory, input.path);
|
|
1964
2572
|
await authorizeRead(context, filePath);
|
|
1965
2573
|
if (input.workspace) {
|
|
1966
|
-
const
|
|
1967
|
-
|
|
2574
|
+
const column = input.column;
|
|
2575
|
+
const identifyArgs = inspectArgs(filePath, "identity", {
|
|
2576
|
+
selector: `line:${input.line}${column === undefined ? "" : `:${column}`}`
|
|
2577
|
+
});
|
|
1968
2578
|
const envelope = await runReadSeek(context, identifyArgs);
|
|
1969
|
-
const name = identifiedName(
|
|
2579
|
+
const name = identifiedName(requireInspectFacet(envelope, "identity"));
|
|
1970
2580
|
if (!name)
|
|
1971
2581
|
throw new Error("readseek could not identify a binding at the rename cursor");
|
|
1972
2582
|
await authorizeSearch(context, context.directory, name);
|
|
@@ -1981,7 +2591,7 @@ function createReadSeekTools(imagePolicy) {
|
|
|
1981
2591
|
const plan = await runReadSeek(context, args);
|
|
1982
2592
|
if (input.apply === false)
|
|
1983
2593
|
return plan;
|
|
1984
|
-
const files = renameFiles(plan);
|
|
2594
|
+
const files = renameFiles(plan, context.directory);
|
|
1985
2595
|
const planHash = record(plan).plan_hash;
|
|
1986
2596
|
if (typeof planHash !== "string" || planHash.length === 0) {
|
|
1987
2597
|
throw new Error("readseek rename plan did not include a plan hash");
|
|
@@ -2045,7 +2655,7 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2045
2655
|
await authorizeRead(context, filePath);
|
|
2046
2656
|
await rejectSymlinkMutation(filePath);
|
|
2047
2657
|
const edits = input.edits;
|
|
2048
|
-
const request =
|
|
2658
|
+
const request = serializeEditPlan(edits);
|
|
2049
2659
|
const baseArgs = buildEditArgs({
|
|
2050
2660
|
path: filePath,
|
|
2051
2661
|
language: input.language,
|
|
@@ -2097,19 +2707,53 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2097
2707
|
await authorizeEdit(context, [filePath], simpleDiff(filePath, before.content, content), false);
|
|
2098
2708
|
context.abort.throwIfAborted();
|
|
2099
2709
|
await writeText(filePath, before, content);
|
|
2100
|
-
return postMutationPreview(context,
|
|
2710
|
+
return postMutationPreview(context, inspectArgs(filePath, "content", { end: DEFAULT_READ_LIMIT }), filePath, !before.exists || before.content !== content);
|
|
2101
2711
|
});
|
|
2102
2712
|
})
|
|
2103
2713
|
};
|
|
2104
2714
|
}
|
|
2105
2715
|
|
|
2716
|
+
// v2.ts
|
|
2717
|
+
import { define } from "@opencode-ai/plugin/v2/promise";
|
|
2718
|
+
var GUIDANCE = "For structural code navigation, consult the readseek skill when ReadSeek tools are unavailable and the readseek CLI is installed.";
|
|
2719
|
+
var ReadSeekV2Plugin = define({
|
|
2720
|
+
id: "opencode-readseek",
|
|
2721
|
+
setup: async (ctx) => {
|
|
2722
|
+
await ctx.skill.transform((draft) => {
|
|
2723
|
+
draft.source({
|
|
2724
|
+
type: "embedded",
|
|
2725
|
+
skill: {
|
|
2726
|
+
name: "readseek",
|
|
2727
|
+
description: "Use for structural code navigation and anchored edits when the readseek CLI is installed.",
|
|
2728
|
+
location: "/plugin/opencode-readseek/SKILL.md",
|
|
2729
|
+
content: `# ReadSeek CLI
|
|
2730
|
+
|
|
2731
|
+
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.
|
|
2732
|
+
|
|
2733
|
+
This V2 plugin supplies guidance only; it does not register readseek_* tools. If the CLI is unavailable, use OpenCode's built-in tools instead.`
|
|
2734
|
+
}
|
|
2735
|
+
});
|
|
2736
|
+
});
|
|
2737
|
+
await ctx.agent.transform((draft) => {
|
|
2738
|
+
for (const agent of draft.list()) {
|
|
2739
|
+
draft.update(agent.id, (item) => {
|
|
2740
|
+
if (!item.system?.includes(GUIDANCE))
|
|
2741
|
+
item.system = [item.system, GUIDANCE].filter(Boolean).join(`
|
|
2742
|
+
|
|
2743
|
+
`);
|
|
2744
|
+
});
|
|
2745
|
+
}
|
|
2746
|
+
});
|
|
2747
|
+
}
|
|
2748
|
+
});
|
|
2749
|
+
|
|
2106
2750
|
// index.ts
|
|
2107
2751
|
var ReadSeekPlugin = async (_input, options) => {
|
|
2108
2752
|
const anchors = new SessionAnchors;
|
|
2109
2753
|
const imagePolicy = resolveImagePolicy(options);
|
|
2110
2754
|
return {
|
|
2111
2755
|
tool: createReadSeekTools(imagePolicy),
|
|
2112
|
-
"experimental.chat.system.transform": async (
|
|
2756
|
+
"experimental.chat.system.transform": async (_input, output) => {
|
|
2113
2757
|
output.system.push(TOOL_POLICY);
|
|
2114
2758
|
},
|
|
2115
2759
|
"tool.definition": async (input, output) => {
|
|
@@ -2155,8 +2799,8 @@ var ReadSeekPlugin = async (_input, options) => {
|
|
|
2155
2799
|
}
|
|
2156
2800
|
};
|
|
2157
2801
|
};
|
|
2158
|
-
var opencode_readseek_default = {
|
|
2802
|
+
var opencode_readseek_default = { ...ReadSeekV2Plugin, server: ReadSeekPlugin };
|
|
2159
2803
|
export {
|
|
2160
|
-
|
|
2161
|
-
|
|
2804
|
+
ReadSeekPlugin,
|
|
2805
|
+
opencode_readseek_default as default
|
|
2162
2806
|
};
|