opencode-readseek 0.10.7 → 0.11.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 +50 -5
- package/dist/index.js +1623 -1437
- package/dist/v2.js +23 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
// index.ts
|
|
2
|
-
import
|
|
2
|
+
import path8 from "node:path";
|
|
3
3
|
|
|
4
4
|
// src/client.ts
|
|
5
5
|
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
|
-
];
|
|
16
|
-
var VISION_MODES = ["none", "all", "ocr", "caption", "objects"];
|
|
17
9
|
var VISION_LEVELS = ["low", "medium", "high"];
|
|
18
10
|
var EDIT_VARIANTS = [
|
|
19
11
|
"set_line",
|
|
@@ -24,15 +16,22 @@ var EDIT_VARIANTS = [
|
|
|
24
16
|
];
|
|
25
17
|
var GIT_SCOPES = ["tracked", "untracked", "ignored", "all"];
|
|
26
18
|
var TOOL_NAMES = [
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
19
|
+
"show_content",
|
|
20
|
+
"show_structure",
|
|
21
|
+
"show_metadata",
|
|
22
|
+
"show_assets",
|
|
23
|
+
"check_syntax",
|
|
24
|
+
"check_rules",
|
|
25
|
+
"generate_caption",
|
|
26
|
+
"detect_objects",
|
|
27
|
+
"recognize_text",
|
|
28
|
+
"find_nodes",
|
|
29
|
+
"find_text",
|
|
30
|
+
"find_definitions",
|
|
31
|
+
"find_references",
|
|
32
|
+
"edit_text",
|
|
33
|
+
"rename_symbol",
|
|
34
|
+
"write_file"
|
|
36
35
|
];
|
|
37
36
|
var PARAM_DESCRIPTIONS = {
|
|
38
37
|
path: "Absolute or relative file path",
|
|
@@ -43,31 +42,30 @@ var PARAM_DESCRIPTIONS = {
|
|
|
43
42
|
languageOverride: "Language override",
|
|
44
43
|
languageSymbol: "Language override for symbol replacement",
|
|
45
44
|
pattern: "AST pattern (ast-grep style), such as console.log($$$ARGS)",
|
|
45
|
+
patternContext: "Surrounding code context for sub-statement patterns",
|
|
46
|
+
selector: "Target AST node kind to extract from pattern context",
|
|
46
47
|
grepPattern: "Regex pattern; set literal for exact text",
|
|
47
48
|
name: "Qualified or unqualified symbol name",
|
|
48
49
|
refsName: "Identifier to find references for",
|
|
49
|
-
scope: "Restrict results to the binding at the cursor",
|
|
50
|
-
line: "One-based cursor line; required with scope",
|
|
51
|
-
renameLine: "One-based cursor line of the symbol to rename",
|
|
52
|
-
column: "One-based cursor byte column for disambiguation",
|
|
53
50
|
gitScope: "In a Git repository, which files to search: tracked, untracked, ignored (untracked plus ignored), or all. Defaults to tracked plus untracked.",
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
query: "KDL selection query, @path, or '-' for standard input",
|
|
51
|
+
at: "Location selector: line:N[:COLUMN], hash:H[:COLUMN], symbol:NAME, page:N, node:ID, or asset:ID",
|
|
52
|
+
assetAt: "Document selector: page:N, node:ID, or asset:ID",
|
|
53
|
+
rule: "KDL node selection rule, @path, or '-' for standard input",
|
|
54
|
+
rules: "KDL structural checks, @path, or '-' for standard input",
|
|
59
55
|
stdinName: "File name hint when the target is '-'",
|
|
60
56
|
end: "Last line to include",
|
|
61
57
|
limit: "Maximum number of lines to include",
|
|
62
|
-
|
|
58
|
+
depth: "Maximum structural depth for structure output; defaults to 1 in agent tools",
|
|
63
59
|
offset: "One-based starting line",
|
|
64
60
|
readLimit: "Maximum lines to return",
|
|
65
61
|
symbolRead: "Symbol name to read",
|
|
66
62
|
bundle: "Include related symbols from the same file; requires symbol",
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
63
|
+
level: "Explicit inference level: low (default), medium, or high",
|
|
64
|
+
include: "Additional caption tasks: text and/or objects",
|
|
65
|
+
kind: "Restrict structure to this node kind",
|
|
66
|
+
outline: "Return a symbol outline",
|
|
67
|
+
noAnchors: "Omit source line anchors",
|
|
68
|
+
text: "Literal native text query; adapter find_text also supports regex via pattern",
|
|
71
69
|
edits: "Use set_line, replace_lines, insert_after, replace_symbol, or replace",
|
|
72
70
|
editVariant: "Exactly one nested edit variant",
|
|
73
71
|
setLine: 'Replace one line: {"set_line":{"anchor":"LINE:HASH","new_text":"..."}}',
|
|
@@ -104,15 +102,22 @@ var PARAM_DESCRIPTIONS = {
|
|
|
104
102
|
scopeContext: "Context lines within each symbol"
|
|
105
103
|
};
|
|
106
104
|
var TOOL_DESCRIPTIONS = {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
105
|
+
show_content: "Read source, document content, or image descriptors without inference. Obtain fresh `LINE:HASH` anchors before edits; use at with a column for cursor context. Document results include typed nodes and cached asset paths.",
|
|
106
|
+
show_structure: "Show parsed source or document hierarchy. Use shallow depth for file overviews.",
|
|
107
|
+
show_metadata: "Show format, source identity, language, dimensions, or page count without decoding image content or inference.",
|
|
108
|
+
show_assets: "Return selected document asset descriptors with usable cached paths. Adapters attach images from those paths.",
|
|
109
|
+
check_syntax: "Check parser syntax diagnostics. Exit 0 is clean, 1 has findings, 2 is failure; this is not type checking.",
|
|
110
|
+
check_rules: "Evaluate KDL structural assertions. Exit 0 is clean, 1 has findings, 2 is failure.",
|
|
111
|
+
generate_caption: "Explicitly infer a caption using an installed model. Include text and objects to request OCR and detection too; never downloads models automatically.",
|
|
112
|
+
detect_objects: "Explicitly infer object labels and bounding boxes using an installed model.",
|
|
113
|
+
recognize_text: "Explicitly recognize text from pixels using an installed model; reading never silently invokes OCR.",
|
|
114
|
+
find_nodes: "Find AST nodes by pattern or KDL selection rule. Returns edit-ready anchors. Use `find_text` for text and regex.",
|
|
115
|
+
find_text: "Search text or regex and return edit-ready `LINE:HASH` anchors. Native CLI text is literal; adapters preserve regex, glob, context, summary, and symbol grouping.",
|
|
116
|
+
find_definitions: "Find a symbol definition or declaration by qualified or unqualified name.",
|
|
117
|
+
find_references: "Find identifier-name matches with name, or binding-scoped references with at. Use before renaming or deleting a symbol.",
|
|
118
|
+
edit_text: "Edit existing files with fresh `LINE:HASH` anchors. Each edits item contains exactly one set_line, replace_lines, insert_after, replace_symbol, or replace object. Preview before applying a verified plan.",
|
|
119
|
+
rename_symbol: "Rename a symbol at a line or hash cursor with binding-aware edits and stale-plan protection.",
|
|
120
|
+
write_file: "Create or replace a complete text file and return `LINE:HASH` anchors. Use `edit_text` for small changes."
|
|
116
121
|
};
|
|
117
122
|
function renderToolDescription(name, aliases = {}) {
|
|
118
123
|
const desc = TOOL_DESCRIPTIONS[name];
|
|
@@ -188,228 +193,62 @@ var editItemSchema = {
|
|
|
188
193
|
},
|
|
189
194
|
description: PARAM_DESCRIPTIONS.editVariant
|
|
190
195
|
};
|
|
196
|
+
var string = (description) => ({ type: "string", description });
|
|
197
|
+
var integer = (description, minimum = 1) => ({ type: "integer", minimum, description });
|
|
198
|
+
var path = { path: string(PARAM_DESCRIPTIONS.path) };
|
|
199
|
+
var at = { at: string(PARAM_DESCRIPTIONS.at) };
|
|
200
|
+
var lang = { language: string(PARAM_DESCRIPTIONS.language) };
|
|
201
|
+
var stdin = { stdinName: string(PARAM_DESCRIPTIONS.stdinName) };
|
|
202
|
+
var noAnchors = { noAnchors: { type: "boolean", description: PARAM_DESCRIPTIONS.noAnchors } };
|
|
203
|
+
var level = { level: { type: "string", enum: VISION_LEVELS, description: PARAM_DESCRIPTIONS.level } };
|
|
204
|
+
var limit = { limit: integer(PARAM_DESCRIPTIONS.matchLimit) };
|
|
205
|
+
var apply = { apply: { type: "boolean", description: PARAM_DESCRIPTIONS.apply }, planHash: string(PARAM_DESCRIPTIONS.planHash) };
|
|
206
|
+
function tool(name, title, properties, required) {
|
|
207
|
+
return { name, title, description: TOOL_DESCRIPTIONS[name], inputSchema: { type: "object", properties, required, additionalProperties: false } };
|
|
208
|
+
}
|
|
191
209
|
var TOOL_CONTRACTS = [
|
|
192
|
-
{
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
type: "string",
|
|
209
|
-
enum: [...VISION_MODES],
|
|
210
|
-
description: PARAM_DESCRIPTIONS.visionModeInspect
|
|
211
|
-
},
|
|
212
|
-
visionLevel: {
|
|
213
|
-
type: "string",
|
|
214
|
-
enum: [...VISION_LEVELS],
|
|
215
|
-
description: PARAM_DESCRIPTIONS.visionLevel
|
|
216
|
-
},
|
|
217
|
-
stdinName: { type: "string", description: PARAM_DESCRIPTIONS.stdinName }
|
|
218
|
-
},
|
|
219
|
-
required: ["path"],
|
|
220
|
-
additionalProperties: false
|
|
221
|
-
}
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
name: "edit",
|
|
225
|
-
title: "Hash-verified edit",
|
|
226
|
-
description: TOOL_DESCRIPTIONS.edit,
|
|
227
|
-
inputSchema: {
|
|
228
|
-
type: "object",
|
|
229
|
-
properties: {
|
|
230
|
-
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
231
|
-
edits: {
|
|
232
|
-
type: "array",
|
|
233
|
-
minItems: 1,
|
|
234
|
-
description: PARAM_DESCRIPTIONS.edits,
|
|
235
|
-
items: editItemSchema
|
|
236
|
-
},
|
|
237
|
-
language: { type: "string", description: PARAM_DESCRIPTIONS.languageSymbol },
|
|
238
|
-
apply: { type: "boolean", description: PARAM_DESCRIPTIONS.apply },
|
|
239
|
-
planHash: { type: "string", description: PARAM_DESCRIPTIONS.planHash },
|
|
240
|
-
postEditVerify: { type: "boolean", description: PARAM_DESCRIPTIONS.postEditVerify }
|
|
241
|
-
},
|
|
242
|
-
required: ["path", "edits"],
|
|
243
|
-
additionalProperties: false
|
|
244
|
-
}
|
|
245
|
-
},
|
|
246
|
-
{
|
|
247
|
-
name: "write",
|
|
248
|
-
title: "Write file",
|
|
249
|
-
description: TOOL_DESCRIPTIONS.write,
|
|
250
|
-
inputSchema: {
|
|
251
|
-
type: "object",
|
|
252
|
-
properties: {
|
|
253
|
-
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
254
|
-
content: { type: "string", description: PARAM_DESCRIPTIONS.content }
|
|
255
|
-
},
|
|
256
|
-
required: ["path", "content"],
|
|
257
|
-
additionalProperties: false
|
|
258
|
-
}
|
|
259
|
-
},
|
|
260
|
-
{
|
|
261
|
-
name: "grep",
|
|
262
|
-
title: "Text search",
|
|
263
|
-
description: TOOL_DESCRIPTIONS.grep,
|
|
264
|
-
inputSchema: {
|
|
265
|
-
type: "object",
|
|
266
|
-
properties: {
|
|
267
|
-
pattern: { type: "string", description: PARAM_DESCRIPTIONS.grepPattern },
|
|
268
|
-
path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
|
|
269
|
-
glob: { type: "string", description: PARAM_DESCRIPTIONS.glob },
|
|
270
|
-
ignoreCase: { type: "boolean", description: PARAM_DESCRIPTIONS.ignoreCase },
|
|
271
|
-
literal: { type: "boolean", description: PARAM_DESCRIPTIONS.literal },
|
|
272
|
-
context: { type: "integer", minimum: 0, description: PARAM_DESCRIPTIONS.context },
|
|
273
|
-
limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.matchLimit },
|
|
274
|
-
summary: { type: "boolean", description: PARAM_DESCRIPTIONS.summary },
|
|
275
|
-
scope: {
|
|
276
|
-
type: "string",
|
|
277
|
-
enum: ["symbol"],
|
|
278
|
-
description: PARAM_DESCRIPTIONS.grepScope
|
|
279
|
-
},
|
|
280
|
-
scopeContext: {
|
|
281
|
-
type: "integer",
|
|
282
|
-
minimum: 0,
|
|
283
|
-
description: PARAM_DESCRIPTIONS.scopeContext
|
|
284
|
-
}
|
|
285
|
-
},
|
|
286
|
-
required: ["pattern"],
|
|
287
|
-
additionalProperties: false
|
|
288
|
-
}
|
|
289
|
-
},
|
|
290
|
-
{
|
|
291
|
-
name: "extract",
|
|
292
|
-
title: "Extract asset",
|
|
293
|
-
description: TOOL_DESCRIPTIONS.extract,
|
|
294
|
-
inputSchema: {
|
|
295
|
-
type: "object",
|
|
296
|
-
properties: {
|
|
297
|
-
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
298
|
-
selector: {
|
|
299
|
-
type: "string",
|
|
300
|
-
pattern: "^asset:.+",
|
|
301
|
-
description: PARAM_DESCRIPTIONS.assetSelector
|
|
302
|
-
},
|
|
303
|
-
output: {
|
|
304
|
-
type: "string",
|
|
305
|
-
minLength: 1,
|
|
306
|
-
pattern: "^(?!-$).+",
|
|
307
|
-
description: PARAM_DESCRIPTIONS.output
|
|
308
|
-
},
|
|
309
|
-
stdinName: { type: "string", description: PARAM_DESCRIPTIONS.stdinName }
|
|
310
|
-
},
|
|
311
|
-
required: ["path", "selector", "output"],
|
|
312
|
-
additionalProperties: false
|
|
313
|
-
}
|
|
314
|
-
},
|
|
315
|
-
{
|
|
316
|
-
name: "search",
|
|
317
|
-
title: "AST search",
|
|
318
|
-
description: TOOL_DESCRIPTIONS.search,
|
|
319
|
-
inputSchema: {
|
|
320
|
-
type: "object",
|
|
321
|
-
properties: {
|
|
322
|
-
pattern: { type: "string", description: PARAM_DESCRIPTIONS.pattern },
|
|
323
|
-
path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
|
|
324
|
-
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
325
|
-
limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.matchLimit },
|
|
326
|
-
...gitScopeSchema
|
|
327
|
-
},
|
|
328
|
-
required: ["pattern"],
|
|
329
|
-
additionalProperties: false
|
|
330
|
-
}
|
|
331
|
-
},
|
|
332
|
-
{
|
|
333
|
-
name: "def",
|
|
334
|
-
title: "Find definition",
|
|
335
|
-
description: TOOL_DESCRIPTIONS.def,
|
|
336
|
-
inputSchema: {
|
|
337
|
-
type: "object",
|
|
338
|
-
properties: {
|
|
339
|
-
name: { type: "string", description: PARAM_DESCRIPTIONS.name },
|
|
340
|
-
path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
|
|
341
|
-
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
342
|
-
...gitScopeSchema
|
|
343
|
-
},
|
|
344
|
-
required: ["name"],
|
|
345
|
-
additionalProperties: false
|
|
346
|
-
}
|
|
347
|
-
},
|
|
348
|
-
{
|
|
349
|
-
name: "refs",
|
|
350
|
-
title: "Find references",
|
|
351
|
-
description: TOOL_DESCRIPTIONS.refs,
|
|
352
|
-
inputSchema: {
|
|
353
|
-
type: "object",
|
|
354
|
-
properties: {
|
|
355
|
-
name: { type: "string", description: PARAM_DESCRIPTIONS.refsName },
|
|
356
|
-
path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
|
|
357
|
-
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
358
|
-
scope: { type: "boolean", description: PARAM_DESCRIPTIONS.scope },
|
|
359
|
-
line: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.line },
|
|
360
|
-
column: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.column },
|
|
361
|
-
...gitScopeSchema
|
|
362
|
-
},
|
|
363
|
-
additionalProperties: false
|
|
364
|
-
}
|
|
365
|
-
},
|
|
366
|
-
{
|
|
367
|
-
name: "rename",
|
|
368
|
-
title: "Rename symbol",
|
|
369
|
-
description: TOOL_DESCRIPTIONS.rename,
|
|
370
|
-
inputSchema: {
|
|
371
|
-
type: "object",
|
|
372
|
-
properties: {
|
|
373
|
-
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
374
|
-
line: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.renameLine },
|
|
375
|
-
column: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.column },
|
|
376
|
-
to: { type: "string", description: PARAM_DESCRIPTIONS.to },
|
|
377
|
-
workspace: { type: "boolean", description: PARAM_DESCRIPTIONS.workspace },
|
|
378
|
-
apply: { type: "boolean", description: PARAM_DESCRIPTIONS.apply },
|
|
379
|
-
planHash: { type: "string", description: PARAM_DESCRIPTIONS.planHash },
|
|
380
|
-
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
381
|
-
...gitScopeSchema
|
|
382
|
-
},
|
|
383
|
-
required: ["path", "line", "to"],
|
|
384
|
-
additionalProperties: false
|
|
385
|
-
}
|
|
386
|
-
}
|
|
210
|
+
tool("show_content", "Show content", { ...path, ...at, ...lang, ...stdin, ...noAnchors, end: integer(PARAM_DESCRIPTIONS.end), limit: integer(PARAM_DESCRIPTIONS.limit) }, ["path"]),
|
|
211
|
+
tool("show_structure", "Show structure", { ...path, ...at, ...lang, ...stdin, depth: integer(PARAM_DESCRIPTIONS.depth, 0), kind: string(PARAM_DESCRIPTIONS.kind), outline: { type: "boolean", description: PARAM_DESCRIPTIONS.outline } }, ["path"]),
|
|
212
|
+
tool("show_metadata", "Show metadata", { ...path, ...lang, ...stdin }, ["path"]),
|
|
213
|
+
tool("show_assets", "Show assets", { ...path, at: string(PARAM_DESCRIPTIONS.assetAt), ...stdin }, ["path"]),
|
|
214
|
+
tool("check_syntax", "Check syntax", { ...path, ...lang, ...stdin }, ["path"]),
|
|
215
|
+
tool("check_rules", "Check rules", { ...path, ...gitScopeSchema, rules: string(PARAM_DESCRIPTIONS.rules) }, ["rules"]),
|
|
216
|
+
tool("generate_caption", "Generate caption", { ...path, ...at, ...stdin, ...level, include: { type: "array", items: { type: "string", enum: ["text", "objects"] }, minItems: 1, uniqueItems: true, description: PARAM_DESCRIPTIONS.include } }, ["path"]),
|
|
217
|
+
tool("detect_objects", "Detect objects", { ...path, ...at, ...stdin, ...level }, ["path"]),
|
|
218
|
+
tool("recognize_text", "Recognize text", { ...path, ...at, ...stdin, ...level }, ["path"]),
|
|
219
|
+
tool("find_nodes", "Find nodes", { ...path, ...lang, ...stdin, ...limit, ...gitScopeSchema, ...noAnchors, pattern: string(PARAM_DESCRIPTIONS.pattern), context: string(PARAM_DESCRIPTIONS.patternContext), selector: string(PARAM_DESCRIPTIONS.selector), rule: string(PARAM_DESCRIPTIONS.rule) }, []),
|
|
220
|
+
tool("find_text", "Find text", { ...path, ...limit, ...gitScopeSchema, text: string(PARAM_DESCRIPTIONS.text), pattern: string(PARAM_DESCRIPTIONS.grepPattern), glob: string(PARAM_DESCRIPTIONS.glob), ignoreCase: { type: "boolean", description: PARAM_DESCRIPTIONS.ignoreCase }, literal: { type: "boolean", description: PARAM_DESCRIPTIONS.literal }, context: integer(PARAM_DESCRIPTIONS.context, 0), summary: { type: "boolean", description: PARAM_DESCRIPTIONS.summary }, scope: { type: "string", enum: ["symbol"], description: PARAM_DESCRIPTIONS.grepScope }, scopeContext: integer(PARAM_DESCRIPTIONS.scopeContext, 0) }, []),
|
|
221
|
+
tool("find_definitions", "Find definitions", { ...path, ...lang, ...gitScopeSchema, ...noAnchors, name: string(PARAM_DESCRIPTIONS.name) }, ["name"]),
|
|
222
|
+
tool("find_references", "Find references", { ...path, ...at, ...lang, ...gitScopeSchema, ...noAnchors, name: string(PARAM_DESCRIPTIONS.refsName) }, []),
|
|
223
|
+
tool("edit_text", "Hash-verified edit", { ...path, ...lang, ...gitScopeSchema, ...apply, edits: { type: "array", minItems: 1, items: editItemSchema, description: PARAM_DESCRIPTIONS.edits }, postEditVerify: { type: "boolean", description: PARAM_DESCRIPTIONS.postEditVerify } }, ["path", "edits"]),
|
|
224
|
+
tool("rename_symbol", "Rename symbol", { ...path, ...at, ...lang, ...gitScopeSchema, ...apply, to: string(PARAM_DESCRIPTIONS.to), workspace: { type: "boolean", description: PARAM_DESCRIPTIONS.workspace } }, ["path", "at", "to"]),
|
|
225
|
+
tool("write_file", "Write file", { ...path, content: string(PARAM_DESCRIPTIONS.content) }, ["path", "content"])
|
|
387
226
|
];
|
|
388
227
|
// ../readseek-api/src/policy.ts
|
|
389
228
|
var BUILTIN_FALLBACKS = {
|
|
390
|
-
read: "
|
|
391
|
-
edit: "
|
|
392
|
-
write: "
|
|
393
|
-
grep: "
|
|
394
|
-
apply_patch: "
|
|
229
|
+
read: "show_content",
|
|
230
|
+
edit: "edit_text",
|
|
231
|
+
write: "write_file",
|
|
232
|
+
grep: "find_text",
|
|
233
|
+
apply_patch: "edit_text"
|
|
395
234
|
};
|
|
396
235
|
var PREFERENCE_LEADS = {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
236
|
+
show_content: "Preferred for reading source with anchors.",
|
|
237
|
+
edit_text: "Preferred for editing existing text files with fresh LINE:HASH anchors.",
|
|
238
|
+
write_file: "Preferred for creating or replacing a complete text file; returns LINE:HASH anchors.",
|
|
239
|
+
find_text: "Preferred text/regex find_nodes; returns edit_text-ready LINE:HASH anchors.",
|
|
240
|
+
find_nodes: "Preferred for AST or structural code find_nodes with ast-find_text-style patterns.",
|
|
241
|
+
find_definitions: "Preferred for go-to-definition or declaration lookup by name.",
|
|
242
|
+
find_references: "Preferred for finding identifier-name matches or binding-specific references before rename_symbol or delete.",
|
|
243
|
+
rename_symbol: "Preferred for renaming a symbol with binding-aware, verified edits.",
|
|
244
|
+
show_assets: "Preferred for saving a selected document asset to a file."
|
|
406
245
|
};
|
|
407
246
|
var FALLBACK_TEMPLATES = {
|
|
408
|
-
read: "Prefer `
|
|
409
|
-
edit: "Prefer `
|
|
410
|
-
write: "Prefer `
|
|
411
|
-
grep: "Prefer `
|
|
412
|
-
apply_patch: "Prefer `
|
|
247
|
+
read: "Prefer `show_content` with fresh LINE:HASH anchors.",
|
|
248
|
+
edit: "Prefer `edit_text` with fresh LINE:HASH anchors.",
|
|
249
|
+
write: "Prefer `write_file` so the result returns LINE:HASH anchors.",
|
|
250
|
+
grep: "Prefer `find_text` for text/regex search with edit-ready anchors.",
|
|
251
|
+
apply_patch: "Prefer `edit_text` for existing text files."
|
|
413
252
|
};
|
|
414
253
|
function resolveToolName(name, aliases = {}) {
|
|
415
254
|
return aliases[name] ?? name;
|
|
@@ -418,7 +257,7 @@ function rewriteBacktickedToolNames(text, aliases = {}) {
|
|
|
418
257
|
return TOOL_NAMES.reduce((rewritten, toolName) => rewritten.replaceAll(`\`${toolName}\``, `\`${resolveToolName(toolName, aliases)}\``), text);
|
|
419
258
|
}
|
|
420
259
|
function renderPreferredToolDescription(name, aliases = {}) {
|
|
421
|
-
return rewriteBacktickedToolNames(PREFERENCE_LEADS[name], aliases);
|
|
260
|
+
return rewriteBacktickedToolNames(PREFERENCE_LEADS[name] ?? `Preferred for ${name.replaceAll("_", " ")} with ReadSeek.`, aliases);
|
|
422
261
|
}
|
|
423
262
|
function renderFallbackToolDescription(builtIn, aliases = {}) {
|
|
424
263
|
const template = FALLBACK_TEMPLATES[builtIn];
|
|
@@ -428,20 +267,20 @@ function renderFallbackToolDescription(builtIn, aliases = {}) {
|
|
|
428
267
|
}
|
|
429
268
|
function renderToolRoutingPolicy(options = {}) {
|
|
430
269
|
const aliases = options.aliases ?? {};
|
|
431
|
-
const
|
|
432
|
-
const
|
|
433
|
-
const
|
|
434
|
-
const
|
|
435
|
-
const
|
|
436
|
-
const
|
|
437
|
-
const
|
|
438
|
-
const
|
|
439
|
-
const
|
|
270
|
+
const show_content = resolveToolName("show_content", aliases);
|
|
271
|
+
const edit_text = resolveToolName("edit_text", aliases);
|
|
272
|
+
const write_file = resolveToolName("write_file", aliases);
|
|
273
|
+
const find_text = resolveToolName("find_text", aliases);
|
|
274
|
+
const find_nodes = resolveToolName("find_nodes", aliases);
|
|
275
|
+
const find_definitions = resolveToolName("find_definitions", aliases);
|
|
276
|
+
const find_references = resolveToolName("find_references", aliases);
|
|
277
|
+
const rename_symbol = resolveToolName("rename_symbol", aliases);
|
|
278
|
+
const show_assets = resolveToolName("show_assets", aliases);
|
|
440
279
|
if (options.style === "server") {
|
|
441
280
|
return [
|
|
442
281
|
"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
|
|
444
|
-
`Use ${
|
|
282
|
+
"For structural questions, prefer these tools to text Grep/Bash: targeted definitions, references, and AST patterns; shallow show_structure views; check_syntax diagnostics and show_content cursor context; and document inspection.",
|
|
283
|
+
`Use ${edit_text} for LINE:HASH-anchored mutations (preview with apply=false; apply defaults to true and re-checks plan_hash).`,
|
|
445
284
|
"Paths resolve against CLAUDE_PROJECT_DIR. Native Read/Grep/Edit hooks provide passive context;",
|
|
446
285
|
"call MCP tools for active navigation, document questions, or anchored edits."
|
|
447
286
|
].join(" ");
|
|
@@ -451,7 +290,7 @@ function renderToolRoutingPolicy(options = {}) {
|
|
|
451
290
|
if (options.preferOverBuiltIns) {
|
|
452
291
|
bullets.push("- Prefer ReadSeek tools over built-ins when they can do the job.");
|
|
453
292
|
}
|
|
454
|
-
bullets.push(`- Read with ${
|
|
293
|
+
bullets.push(`- Read with ${show_content} first; ${edit_text} needs fresh LINE:HASH anchors.`, `- Use ${find_text} for text/regex, ${find_nodes} for AST patterns, ${find_definitions}/${find_references} for targeted symbols, and shallow ${resolveToolName("show_structure", aliases)} structures only for file overviews.`, `- Use ${edit_text} for existing files, ${write_file} for whole-file creation or replacement, and ${rename_symbol} for symbol renames.`, `- Use ${resolveToolName("show_structure", aliases)} for document structure; run ${resolveToolName("check_syntax", aliases)} after source edits for a quick syntax check.`, `- Use ${show_assets} to materialize selected document assets and read their returned paths.`);
|
|
455
294
|
if (options.demoteBuiltIns && options.demoteBuiltIns.length > 0) {
|
|
456
295
|
bullets.push(`- Do not use built-in ${options.demoteBuiltIns.join(", ")} when a ReadSeek tool can do the job.`);
|
|
457
296
|
}
|
|
@@ -486,25 +325,9 @@ function tryParseLineAnchor(anchor) {
|
|
|
486
325
|
}
|
|
487
326
|
|
|
488
327
|
// ../readseek-api/src/validation.ts
|
|
489
|
-
function
|
|
490
|
-
if (input.
|
|
491
|
-
|
|
492
|
-
return { ok: false, error: "scope requires line" };
|
|
493
|
-
}
|
|
494
|
-
return { ok: true };
|
|
495
|
-
}
|
|
496
|
-
if (input.line !== undefined || input.column !== undefined) {
|
|
497
|
-
return { ok: false, error: "line/column require scope=true" };
|
|
498
|
-
}
|
|
499
|
-
if (typeof input.name !== "string" || input.name.length === 0) {
|
|
500
|
-
return { ok: false, error: "name is required when scope is not set" };
|
|
501
|
-
}
|
|
502
|
-
return { ok: true };
|
|
503
|
-
}
|
|
504
|
-
function validateVisionLevelVsMode(input) {
|
|
505
|
-
if (input.visionLevel !== undefined && !["all", "ocr", "caption", "objects"].includes(input.visionMode ?? "")) {
|
|
506
|
-
return { ok: false, error: "visionLevel requires an analysis visionMode" };
|
|
507
|
-
}
|
|
328
|
+
function validateReferencesSelection(input) {
|
|
329
|
+
if (Boolean(input.name) === Boolean(input.at))
|
|
330
|
+
return { ok: false, error: "exactly one of name or at is required" };
|
|
508
331
|
return { ok: true };
|
|
509
332
|
}
|
|
510
333
|
var VARIANT_FIELDS = {
|
|
@@ -574,51 +397,8 @@ function validateExactlyOneEditVariant(edits) {
|
|
|
574
397
|
}
|
|
575
398
|
var MIN_SAFE_INTEGER = BigInt(Number.MIN_SAFE_INTEGER);
|
|
576
399
|
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
|
-
}
|
|
617
400
|
// ../readseek-api/src/argv.ts
|
|
618
|
-
var
|
|
619
|
-
function buildLineLocation(line, column) {
|
|
620
|
-
return column === undefined ? `line:${line}` : `line:${line}:${column}`;
|
|
621
|
-
}
|
|
401
|
+
var DEFAULT_STRUCTURE_DEPTH = 1;
|
|
622
402
|
function appendGitScopeArgs(args, input) {
|
|
623
403
|
if (input.gitScope !== undefined)
|
|
624
404
|
args.push("--git-scope", input.gitScope);
|
|
@@ -645,30 +425,6 @@ function appendApplyAndPlanHash(args, input) {
|
|
|
645
425
|
pushOptionalString(args, "--plan-hash", input.planHash);
|
|
646
426
|
return args;
|
|
647
427
|
}
|
|
648
|
-
function selectArg(select) {
|
|
649
|
-
if (select === undefined)
|
|
650
|
-
return;
|
|
651
|
-
return Array.isArray(select) ? select.join(",") : select;
|
|
652
|
-
}
|
|
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
428
|
function validatePositiveInteger(value, name) {
|
|
673
429
|
if (!Number.isSafeInteger(value) || value < 1) {
|
|
674
430
|
throw new Error(`${name} must be a positive integer`);
|
|
@@ -714,224 +470,840 @@ function parseSelector(text) {
|
|
|
714
470
|
}
|
|
715
471
|
return { kind, value };
|
|
716
472
|
}
|
|
717
|
-
function
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
473
|
+
function start(verb, object, input) {
|
|
474
|
+
if (!input.path)
|
|
475
|
+
throw new Error(`${verb} ${object} requires a path`);
|
|
476
|
+
if (input.stdinName !== undefined && input.path !== "-")
|
|
477
|
+
throw new Error("stdinName requires '-' as the target");
|
|
478
|
+
const args = [];
|
|
479
|
+
pushOptionalString(args, "--readseek-dir", input.readseekDir);
|
|
480
|
+
args.push(verb, object, input.path);
|
|
481
|
+
pushOptionalString(args, "--stdin-name", input.stdinName);
|
|
482
|
+
return args;
|
|
483
|
+
}
|
|
484
|
+
function at2(args, value) {
|
|
485
|
+
if (value !== undefined) {
|
|
486
|
+
parseSelector(value);
|
|
487
|
+
args.push("--at", value);
|
|
721
488
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
489
|
+
}
|
|
490
|
+
function finish(args) {
|
|
491
|
+
args.push("--kdl");
|
|
492
|
+
return args;
|
|
493
|
+
}
|
|
494
|
+
function language(args, input) {
|
|
495
|
+
pushOptionalString(args, "--language", input.language);
|
|
496
|
+
}
|
|
497
|
+
function limit2(args, value) {
|
|
498
|
+
if (value !== undefined)
|
|
499
|
+
validatePositiveInteger(value, "limit");
|
|
500
|
+
pushOptionalNumber(args, "--limit", value);
|
|
501
|
+
}
|
|
502
|
+
function anchors(args, input) {
|
|
503
|
+
if (input.noAnchors)
|
|
504
|
+
args.push("--no-anchors");
|
|
505
|
+
}
|
|
506
|
+
function buildShowContentArgs(input) {
|
|
733
507
|
if (input.end !== undefined)
|
|
734
508
|
validatePositiveInteger(input.end, "end");
|
|
735
|
-
if (input.limit !== undefined)
|
|
736
|
-
validatePositiveInteger(input.limit, "limit");
|
|
737
|
-
if (input.end !== undefined && input.limit !== undefined) {
|
|
509
|
+
if (input.end !== undefined && input.limit !== undefined)
|
|
738
510
|
throw new Error("cannot combine end with limit");
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
if (!Number.isSafeInteger(input.depth) || input.depth < 0) {
|
|
742
|
-
throw new Error("depth must be a non-negative integer");
|
|
743
|
-
}
|
|
744
|
-
if (!hasMap)
|
|
745
|
-
throw new Error("depth requires the map facet");
|
|
746
|
-
}
|
|
747
|
-
if (input.selector !== undefined && input.query !== undefined) {
|
|
748
|
-
throw new Error("cannot combine selector with query");
|
|
749
|
-
}
|
|
750
|
-
if (!hasContent && (input.end !== undefined || input.limit !== undefined || input.query !== undefined)) {
|
|
751
|
-
throw new Error("ranges and queries require the content facet");
|
|
752
|
-
}
|
|
753
|
-
if (hasIdentity && input.selector === undefined) {
|
|
754
|
-
throw new Error("the identity facet requires a selector");
|
|
755
|
-
}
|
|
756
|
-
if (input.selector !== undefined && !hasContent && !hasIdentity) {
|
|
757
|
-
throw new Error("selectors require the content or identity facet");
|
|
758
|
-
}
|
|
759
|
-
if (input.selector !== undefined) {
|
|
760
|
-
const selector = parseSelector(input.selector);
|
|
761
|
-
if (selector.kind === "symbol" && hasIdentity) {
|
|
762
|
-
throw new Error("the identity facet does not accept symbol selectors");
|
|
763
|
-
}
|
|
764
|
-
if (selector.column !== undefined && !hasIdentity) {
|
|
765
|
-
throw new Error("selector columns require the identity facet");
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
const hasVision = input.visionMode !== undefined || input.visionLevel !== undefined;
|
|
769
|
-
if (hasVision && !hasContent) {
|
|
770
|
-
throw new Error("vision options require the content facet");
|
|
771
|
-
}
|
|
772
|
-
if (hasVision && hasSourceFacet) {
|
|
773
|
-
throw new Error("vision options cannot be combined with source facets");
|
|
774
|
-
}
|
|
775
|
-
const args = [];
|
|
776
|
-
if (input.readseekDir !== undefined)
|
|
777
|
-
args.push("--readseek-dir", input.readseekDir);
|
|
778
|
-
args.push("inspect", input.path);
|
|
779
|
-
if (input.selector !== undefined)
|
|
780
|
-
args.push(input.selector);
|
|
781
|
-
const select = selectArg(input.select);
|
|
782
|
-
if (select !== undefined)
|
|
783
|
-
args.push("--select", select);
|
|
784
|
-
pushOptionalString(args, "--query", input.query);
|
|
511
|
+
const args = start("show", "content", input);
|
|
512
|
+
at2(args, input.at);
|
|
785
513
|
pushOptionalNumber(args, "--end", input.end);
|
|
786
|
-
|
|
514
|
+
limit2(args, input.limit);
|
|
515
|
+
language(args, input);
|
|
516
|
+
anchors(args, input);
|
|
517
|
+
return finish(args);
|
|
518
|
+
}
|
|
519
|
+
function buildShowStructureArgs(input) {
|
|
520
|
+
if (input.depth !== undefined && (!Number.isSafeInteger(input.depth) || input.depth < 0))
|
|
521
|
+
throw new Error("depth must be a non-negative integer");
|
|
522
|
+
const args = start("show", "structure", input);
|
|
523
|
+
at2(args, input.at);
|
|
787
524
|
pushOptionalNumber(args, "--depth", input.depth);
|
|
788
|
-
pushOptionalString(args, "--
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
args
|
|
525
|
+
pushOptionalString(args, "--kind", input.kind);
|
|
526
|
+
language(args, input);
|
|
527
|
+
if (input.outline)
|
|
528
|
+
args.push("--outline");
|
|
529
|
+
return finish(args);
|
|
530
|
+
}
|
|
531
|
+
function buildShowMetadataArgs(input) {
|
|
532
|
+
const args = start("show", "metadata", input);
|
|
533
|
+
language(args, input);
|
|
534
|
+
return finish(args);
|
|
535
|
+
}
|
|
536
|
+
function buildShowAssetsArgs(input) {
|
|
537
|
+
if (input.at !== undefined && !["page", "node", "asset"].includes(parseSelector(input.at).kind))
|
|
538
|
+
throw new Error("assets requires a page, node, or asset selector");
|
|
539
|
+
const args = start("show", "assets", input);
|
|
540
|
+
at2(args, input.at);
|
|
541
|
+
return finish(args);
|
|
542
|
+
}
|
|
543
|
+
function buildCheckSyntaxArgs(input) {
|
|
544
|
+
const args = start("check", "syntax", input);
|
|
545
|
+
language(args, input);
|
|
546
|
+
return finish(args);
|
|
547
|
+
}
|
|
548
|
+
function visionArgs(verb, object, input) {
|
|
549
|
+
if (input.level !== undefined && !VISION_LEVELS.includes(input.level))
|
|
550
|
+
throw new Error("invalid vision level");
|
|
551
|
+
if (input.at !== undefined && !["page", "node", "asset"].includes(parseSelector(input.at).kind))
|
|
552
|
+
throw new Error("vision requires a document selector");
|
|
553
|
+
const args = start(verb, object, input);
|
|
554
|
+
at2(args, input.at);
|
|
555
|
+
pushOptionalString(args, "--level", input.level);
|
|
793
556
|
return args;
|
|
794
557
|
}
|
|
795
|
-
function
|
|
796
|
-
|
|
797
|
-
if (input.
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
558
|
+
function buildGenerateCaptionArgs(input) {
|
|
559
|
+
const args = visionArgs("generate", "caption", input);
|
|
560
|
+
if (input.include !== undefined) {
|
|
561
|
+
if (input.include.length === 0 || input.include.some((task) => task !== "text" && task !== "objects"))
|
|
562
|
+
throw new Error("include requires text or objects");
|
|
563
|
+
args.push("--include", input.include.join(","));
|
|
564
|
+
}
|
|
565
|
+
return finish(args);
|
|
566
|
+
}
|
|
567
|
+
function buildDetectObjectsArgs(input) {
|
|
568
|
+
return finish(visionArgs("detect", "objects", input));
|
|
569
|
+
}
|
|
570
|
+
function buildRecognizeTextArgs(input) {
|
|
571
|
+
return finish(visionArgs("recognize", "text", input));
|
|
572
|
+
}
|
|
573
|
+
function buildFindNodesArgs(input) {
|
|
574
|
+
const hasPattern = Boolean(input.pattern);
|
|
575
|
+
const hasRule = Boolean(input.rule);
|
|
576
|
+
const hasContext = Boolean(input.context);
|
|
577
|
+
if (hasRule) {
|
|
578
|
+
if (hasPattern || hasContext || input.selector)
|
|
579
|
+
throw new Error("rule cannot be combined with pattern, context, or selector");
|
|
580
|
+
} else if (!hasPattern && !hasContext) {
|
|
581
|
+
throw new Error("find nodes requires pattern, context, or rule");
|
|
582
|
+
}
|
|
583
|
+
if (hasContext && !input.selector) {
|
|
584
|
+
throw new Error("context requires selector");
|
|
585
|
+
}
|
|
586
|
+
if (hasPattern && hasContext) {
|
|
587
|
+
throw new Error("pattern and context cannot be combined");
|
|
588
|
+
}
|
|
589
|
+
const args = start("find", "nodes", input);
|
|
590
|
+
pushOptionalString(args, "--pattern", input.pattern);
|
|
591
|
+
pushOptionalString(args, "--context", input.context);
|
|
592
|
+
pushOptionalString(args, "--selector", input.selector);
|
|
593
|
+
pushOptionalString(args, "--rule", input.rule);
|
|
594
|
+
language(args, input);
|
|
595
|
+
limit2(args, input.limit);
|
|
596
|
+
appendGitScopeArgs(args, input);
|
|
597
|
+
anchors(args, input);
|
|
598
|
+
return finish(args);
|
|
811
599
|
}
|
|
812
|
-
function
|
|
813
|
-
const args =
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
pushOptionalNumber(args, "--limit", input.limit);
|
|
600
|
+
function buildFindTextArgs(input) {
|
|
601
|
+
const args = start("find", "text", input);
|
|
602
|
+
args.push("--text", input.text);
|
|
603
|
+
limit2(args, input.limit);
|
|
817
604
|
appendGitScopeArgs(args, input);
|
|
818
|
-
args
|
|
819
|
-
return args;
|
|
605
|
+
return finish(args);
|
|
820
606
|
}
|
|
821
|
-
function
|
|
822
|
-
const args =
|
|
823
|
-
|
|
824
|
-
|
|
607
|
+
function buildFindDefinitionsArgs(input) {
|
|
608
|
+
const args = start("find", "definitions", input);
|
|
609
|
+
args.push("--name", input.name);
|
|
610
|
+
language(args, input);
|
|
825
611
|
appendGitScopeArgs(args, input);
|
|
826
|
-
args
|
|
827
|
-
return args;
|
|
612
|
+
anchors(args, input);
|
|
613
|
+
return finish(args);
|
|
828
614
|
}
|
|
829
|
-
function
|
|
830
|
-
const validated =
|
|
615
|
+
function buildFindReferencesArgs(input) {
|
|
616
|
+
const validated = validateReferencesSelection(input);
|
|
831
617
|
if (!validated.ok)
|
|
832
618
|
throw new Error(validated.error);
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
pushOptionalString(args, "--
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
pushOptionalNumber(args, "--line", input.line);
|
|
840
|
-
pushOptionalNumber(args, "--column", input.column);
|
|
841
|
-
}
|
|
842
|
-
pushOptionalString(args, "--language", input.language);
|
|
619
|
+
if (input.at !== undefined && !["line", "hash"].includes(parseSelector(input.at).kind))
|
|
620
|
+
throw new Error("references requires a line or hash selector");
|
|
621
|
+
const args = start("find", "references", input);
|
|
622
|
+
pushOptionalString(args, "--name", input.name);
|
|
623
|
+
at2(args, input.at);
|
|
624
|
+
language(args, input);
|
|
843
625
|
appendGitScopeArgs(args, input);
|
|
844
|
-
args
|
|
845
|
-
return args;
|
|
626
|
+
anchors(args, input);
|
|
627
|
+
return finish(args);
|
|
846
628
|
}
|
|
847
|
-
function
|
|
848
|
-
const args =
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
629
|
+
function buildCheckRulesArgs(input) {
|
|
630
|
+
const args = start("check", "rules", input);
|
|
631
|
+
args.push("--rules", input.rules);
|
|
632
|
+
appendGitScopeArgs(args, input);
|
|
633
|
+
return finish(args);
|
|
634
|
+
}
|
|
635
|
+
function buildEditTextArgs(input) {
|
|
636
|
+
const args = start("edit", "text", input);
|
|
637
|
+
appendApplyAndPlanHash(args, input);
|
|
638
|
+
language(args, input);
|
|
639
|
+
appendGitScopeArgs(args, input);
|
|
855
640
|
if (input.requestFromStdin)
|
|
856
641
|
args.push("--edits", "-");
|
|
857
|
-
args
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
642
|
+
return finish(args);
|
|
643
|
+
}
|
|
644
|
+
function buildRenameSymbolArgs(input) {
|
|
645
|
+
if (!["line", "hash"].includes(parseSelector(input.at).kind))
|
|
646
|
+
throw new Error("rename requires a line or hash selector");
|
|
647
|
+
const args = start("rename", "symbol", input);
|
|
648
|
+
at2(args, input.at);
|
|
649
|
+
args.push("--to", input.to);
|
|
862
650
|
if (input.workspace !== undefined)
|
|
863
651
|
args.push(`--workspace=${input.workspace}`);
|
|
864
|
-
appendApplyAndPlanHash(args, {
|
|
865
|
-
|
|
866
|
-
planHash: input.planHash,
|
|
867
|
-
flagOrder: input.flagOrder ?? "plan-hash-first"
|
|
868
|
-
});
|
|
869
|
-
pushOptionalString(args, "--language", input.language);
|
|
652
|
+
appendApplyAndPlanHash(args, { ...input, flagOrder: input.flagOrder ?? "plan-hash-first" });
|
|
653
|
+
language(args, input);
|
|
870
654
|
appendGitScopeArgs(args, input);
|
|
871
|
-
args
|
|
872
|
-
return args;
|
|
873
|
-
}
|
|
874
|
-
// ../readseek-api/src/platform.ts
|
|
875
|
-
import { accessSync, constants as fsConstants, existsSync, statSync } from "node:fs";
|
|
876
|
-
import { createRequire } from "node:module";
|
|
877
|
-
import { homedir } from "node:os";
|
|
878
|
-
import path from "node:path";
|
|
879
|
-
var READSEEK_BINARY_ENV = "READSEEK_BINARY";
|
|
880
|
-
var READSEEK_PLATFORM_PACKAGES = {
|
|
881
|
-
"darwin-arm64": "@jarkkojs/readseek-darwin-arm64",
|
|
882
|
-
"linux-arm64": "@jarkkojs/readseek-linux-arm64",
|
|
883
|
-
"linux-x64": "@jarkkojs/readseek-linux-x64",
|
|
884
|
-
"win32-x64": "@jarkkojs/readseek-win32-x64"
|
|
885
|
-
};
|
|
886
|
-
var resolveFromPackage = createRequire(import.meta.url).resolve;
|
|
887
|
-
function readSeekBinaryName(platform = process.platform) {
|
|
888
|
-
if (platform === "linux")
|
|
889
|
-
return "readseek.AppImage";
|
|
890
|
-
return platform === "win32" ? "readseek.exe" : "readseek";
|
|
655
|
+
return finish(args);
|
|
891
656
|
}
|
|
892
|
-
|
|
893
|
-
|
|
657
|
+
// ../readseek-api/src/client-output.ts
|
|
658
|
+
import { Type } from "@sinclair/typebox";
|
|
659
|
+
import { AssertError, Value } from "@sinclair/typebox/value";
|
|
660
|
+
var Int = Type.Integer({ minimum: Number.MIN_SAFE_INTEGER, maximum: Number.MAX_SAFE_INTEGER });
|
|
661
|
+
var Str = Type.String();
|
|
662
|
+
var Bool = Type.Boolean();
|
|
663
|
+
var OptStr = Type.Optional(Str);
|
|
664
|
+
var OptInt = Type.Optional(Int);
|
|
665
|
+
var Hashline = Type.Object({ line: Int, hash: Str, text: Str });
|
|
666
|
+
var Hashlines = Type.Array(Hashline);
|
|
667
|
+
var ReadOutput = Type.Object({
|
|
668
|
+
file: Str,
|
|
669
|
+
language: Str,
|
|
670
|
+
line_count: Int,
|
|
671
|
+
file_hash: Str,
|
|
672
|
+
start_line: Int,
|
|
673
|
+
end_line: Int,
|
|
674
|
+
hashlines: Hashlines,
|
|
675
|
+
truncated: Type.Optional(Bool)
|
|
676
|
+
});
|
|
677
|
+
var SymbolOutput = Type.Object({
|
|
678
|
+
kind: Str,
|
|
679
|
+
name: Str,
|
|
680
|
+
qualified_name: OptStr,
|
|
681
|
+
start_line: Int,
|
|
682
|
+
end_line: Int,
|
|
683
|
+
start_hash: OptStr,
|
|
684
|
+
end_hash: OptStr,
|
|
685
|
+
start_anchor: OptStr,
|
|
686
|
+
end_anchor: OptStr
|
|
687
|
+
});
|
|
688
|
+
function mapNodeShape(child) {
|
|
689
|
+
return Type.Object({
|
|
690
|
+
kind: Str,
|
|
691
|
+
name: Str,
|
|
692
|
+
start_line: Int,
|
|
693
|
+
end_line: Int,
|
|
694
|
+
start_anchor: Str,
|
|
695
|
+
end_anchor: Str,
|
|
696
|
+
children: Type.Array(child)
|
|
697
|
+
});
|
|
894
698
|
}
|
|
895
|
-
function
|
|
896
|
-
|
|
897
|
-
if (binaryPath === undefined)
|
|
898
|
-
return null;
|
|
899
|
-
if (!path.isAbsolute(binaryPath)) {
|
|
900
|
-
throw new Error(`${READSEEK_BINARY_ENV} must be an absolute path to the readseek executable`);
|
|
901
|
-
}
|
|
902
|
-
try {
|
|
903
|
-
if (!statSync(binaryPath).isFile())
|
|
904
|
-
throw new Error("not a regular file");
|
|
905
|
-
accessSync(binaryPath, fsConstants.X_OK);
|
|
906
|
-
} catch (error) {
|
|
907
|
-
throw new Error(`${READSEEK_BINARY_ENV} does not point to an executable file: ${binaryPath}`, {
|
|
908
|
-
cause: error
|
|
909
|
-
});
|
|
910
|
-
}
|
|
911
|
-
return binaryPath;
|
|
699
|
+
function recursiveStructureNode() {
|
|
700
|
+
return Type.Recursive((self) => mapNodeShape(self));
|
|
912
701
|
}
|
|
913
|
-
function
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
702
|
+
var StructureNode = typeof Type.Recursive === "function" ? recursiveStructureNode() : Type.Cyclic({ StructureNode: mapNodeShape(Type.Ref("StructureNode")) }, "StructureNode");
|
|
703
|
+
var StructureOutput = Type.Object({
|
|
704
|
+
file: Str,
|
|
705
|
+
language: Str,
|
|
706
|
+
line_count: Int,
|
|
707
|
+
file_hash: Str,
|
|
708
|
+
root: Type.Optional(StructureNode),
|
|
709
|
+
symbols: Type.Array(SymbolOutput)
|
|
710
|
+
});
|
|
711
|
+
var SearchSpan = {
|
|
712
|
+
start_byte: OptInt,
|
|
713
|
+
end_byte: OptInt,
|
|
714
|
+
text: OptStr,
|
|
715
|
+
start_line: Int,
|
|
716
|
+
end_line: Int,
|
|
717
|
+
start_hash: Str,
|
|
718
|
+
end_hash: Str,
|
|
719
|
+
hashlines: Hashlines
|
|
720
|
+
};
|
|
721
|
+
var SearchCapture = Type.Object({ name: Str, ...SearchSpan });
|
|
722
|
+
var SearchMatch = Type.Object({
|
|
723
|
+
pattern_index: Type.Integer({ default: 0 }),
|
|
724
|
+
...SearchSpan,
|
|
725
|
+
captures: Type.Array(SearchCapture)
|
|
726
|
+
});
|
|
727
|
+
var SearchFileOutput = Type.Object({
|
|
728
|
+
file: Str,
|
|
729
|
+
language: Str,
|
|
730
|
+
file_hash: Str,
|
|
731
|
+
matches: Type.Array(SearchMatch)
|
|
732
|
+
});
|
|
733
|
+
var SearchEnvelope = Type.Object({
|
|
734
|
+
results: Type.Array(SearchFileOutput),
|
|
735
|
+
returned_matches: OptInt,
|
|
736
|
+
truncated: Type.Optional(Bool),
|
|
737
|
+
byte_basis: OptStr
|
|
738
|
+
});
|
|
739
|
+
var RefsEnvelope = Type.Object({
|
|
740
|
+
references: Type.Array(Type.Object({
|
|
741
|
+
file: Str,
|
|
742
|
+
line: Int,
|
|
743
|
+
column: Int,
|
|
744
|
+
line_hash: Str,
|
|
745
|
+
text: Str,
|
|
746
|
+
symbol: Type.Optional(Type.Object({ qualified_name: OptStr }))
|
|
747
|
+
}))
|
|
748
|
+
});
|
|
749
|
+
var Diagnostic = Type.Object({
|
|
750
|
+
kind: Type.Union([Type.Literal("error"), Type.Literal("missing")]),
|
|
751
|
+
start_line: Int,
|
|
752
|
+
end_line: Int
|
|
753
|
+
});
|
|
754
|
+
var CheckEnvelope = Type.Object({
|
|
755
|
+
file_hash: OptStr,
|
|
756
|
+
error_count: Int,
|
|
757
|
+
missing_count: Int,
|
|
758
|
+
diagnostics: Type.Array(Diagnostic)
|
|
759
|
+
});
|
|
760
|
+
var DetectedObject = Type.Object({
|
|
761
|
+
label: Str,
|
|
762
|
+
bbox: Type.Tuple([Int, Int, Int, Int])
|
|
763
|
+
});
|
|
764
|
+
var SourceDetection = Type.Object({
|
|
765
|
+
kind: Type.Literal("source"),
|
|
766
|
+
type: Str,
|
|
767
|
+
file: Str,
|
|
768
|
+
language: Str,
|
|
769
|
+
engine: OptStr,
|
|
770
|
+
supported: Bool,
|
|
771
|
+
mime: OptStr,
|
|
772
|
+
syntax: OptStr
|
|
773
|
+
});
|
|
774
|
+
var ImageDetection = Type.Object({
|
|
775
|
+
kind: Type.Literal("image"),
|
|
776
|
+
type: Str,
|
|
777
|
+
file: Str,
|
|
778
|
+
mime: OptStr,
|
|
779
|
+
format: Str,
|
|
780
|
+
width: Int,
|
|
781
|
+
height: Int,
|
|
782
|
+
animated: Bool,
|
|
783
|
+
ocr: OptStr,
|
|
784
|
+
caption: OptStr,
|
|
785
|
+
objects: Type.Optional(Type.Array(DetectedObject))
|
|
786
|
+
});
|
|
787
|
+
var TextDetection = Type.Object({
|
|
788
|
+
kind: Type.Literal("text"),
|
|
789
|
+
type: Str,
|
|
790
|
+
file: Str,
|
|
791
|
+
mime: OptStr
|
|
792
|
+
});
|
|
793
|
+
var PdfDetection = Type.Object({
|
|
794
|
+
kind: Type.Literal("pdf"),
|
|
795
|
+
type: Type.Literal("application/pdf"),
|
|
796
|
+
file: Str,
|
|
797
|
+
mime: OptStr,
|
|
798
|
+
format: Type.Literal("pdf"),
|
|
799
|
+
pages: Int
|
|
800
|
+
});
|
|
801
|
+
var Detection = Type.Union([SourceDetection, ImageDetection, TextDetection, PdfDetection]);
|
|
802
|
+
var SourceDetectionInput = Type.Omit(SourceDetection, ["kind"]);
|
|
803
|
+
var ImageDetectionInput = Type.Omit(ImageDetection, ["kind"]);
|
|
804
|
+
var TextDetectionInput = Type.Omit(TextDetection, ["kind"]);
|
|
805
|
+
var PdfDetectionInput = Type.Omit(PdfDetection, ["kind"]);
|
|
806
|
+
var UnspannedContent = Type.Omit(ReadOutput, ["start_line", "end_line"]);
|
|
807
|
+
var PdfMetadata = Type.Object({ source: Str, format: Type.Literal("pdf"), pages: Int });
|
|
808
|
+
var DocumentCache = Type.Object({
|
|
809
|
+
content_hash: Str,
|
|
810
|
+
state: Type.Union([Type.Literal("built"), Type.Literal("reused")])
|
|
811
|
+
});
|
|
812
|
+
var Identifier = Type.Object({
|
|
813
|
+
text: Str,
|
|
814
|
+
start_column: Int,
|
|
815
|
+
end_column: Int,
|
|
816
|
+
start_byte: Int,
|
|
817
|
+
end_byte: Int
|
|
818
|
+
});
|
|
819
|
+
var CursorEnvelope = Type.Object({
|
|
820
|
+
file: Str,
|
|
821
|
+
language: Str,
|
|
822
|
+
engine: OptStr,
|
|
823
|
+
line_count: Int,
|
|
824
|
+
file_hash: Str,
|
|
825
|
+
line: Int,
|
|
826
|
+
column: Int,
|
|
827
|
+
line_hash: Str,
|
|
828
|
+
identifier: Type.Optional(Identifier),
|
|
829
|
+
symbol: Type.Optional(Type.Object({
|
|
830
|
+
name: Str,
|
|
831
|
+
kind: Str,
|
|
832
|
+
qualified_name: Str,
|
|
833
|
+
start_line: Int,
|
|
834
|
+
end_line: Int
|
|
835
|
+
}))
|
|
836
|
+
});
|
|
837
|
+
var EditOutputSchema = Type.Object({
|
|
838
|
+
file: Str,
|
|
839
|
+
before_hash: Str,
|
|
840
|
+
file_hash: Str,
|
|
841
|
+
plan_hash: Str,
|
|
842
|
+
applied: Bool,
|
|
843
|
+
changed: Bool,
|
|
844
|
+
edit_count: Int,
|
|
845
|
+
before_content: OptStr,
|
|
846
|
+
content: OptStr
|
|
847
|
+
});
|
|
848
|
+
var RenameConflictSchema = Type.Object({ line: Int, column: Int, reason: Str });
|
|
849
|
+
var RenameEditSchema = Type.Object({
|
|
850
|
+
line: Int,
|
|
851
|
+
start_column: Int,
|
|
852
|
+
end_column: Int,
|
|
853
|
+
start_byte: Int,
|
|
854
|
+
end_byte: Int,
|
|
855
|
+
occurrence: Str,
|
|
856
|
+
line_hash: Str,
|
|
857
|
+
text: Str
|
|
858
|
+
});
|
|
859
|
+
var Conflicts = Type.Array(RenameConflictSchema, { default: [] });
|
|
860
|
+
var Edits = Type.Array(RenameEditSchema, { default: [] });
|
|
861
|
+
var RenameFileOutputSchema = Type.Object({
|
|
862
|
+
file: Str,
|
|
863
|
+
language: Str,
|
|
864
|
+
engine: OptStr,
|
|
865
|
+
file_hash: Str,
|
|
866
|
+
conflicts: Conflicts,
|
|
867
|
+
edits: Edits
|
|
868
|
+
});
|
|
869
|
+
var RenameOutputSchema = Type.Object({
|
|
870
|
+
file: Str,
|
|
871
|
+
language: Str,
|
|
872
|
+
engine: OptStr,
|
|
873
|
+
file_hash: Str,
|
|
874
|
+
old_name: Str,
|
|
875
|
+
new_name: Str,
|
|
876
|
+
plan_hash: Str,
|
|
877
|
+
applied: Bool,
|
|
878
|
+
conflicts: Conflicts,
|
|
879
|
+
edits: Edits,
|
|
880
|
+
others: Type.Array(RenameFileOutputSchema, { default: [] })
|
|
881
|
+
});
|
|
882
|
+
var DefEnvelope = Type.Object({
|
|
883
|
+
locations: Type.Array(Type.Object({
|
|
884
|
+
file: Str,
|
|
885
|
+
line: Int,
|
|
886
|
+
column: Int,
|
|
887
|
+
line_hash: Str,
|
|
888
|
+
text: Str,
|
|
889
|
+
kind: OptStr,
|
|
890
|
+
name: OptStr,
|
|
891
|
+
qualified_name: OptStr
|
|
892
|
+
}))
|
|
893
|
+
});
|
|
894
|
+
var Bbox = Type.Object({ x: Type.Number(), y: Type.Number(), width: Type.Number(), height: Type.Number() });
|
|
895
|
+
var Position = { page: OptInt, destination: OptStr, bbox: Type.Optional(Bbox) };
|
|
896
|
+
var Provenance = { inferred: Type.Optional(Bool), model: OptStr, level: OptStr, task: OptStr };
|
|
897
|
+
var ImageDescriptor = Type.Object({
|
|
898
|
+
id: OptStr,
|
|
899
|
+
kind: Type.Optional(Type.Literal("image")),
|
|
900
|
+
content_identity: OptStr,
|
|
901
|
+
path: Str,
|
|
902
|
+
mime: Str,
|
|
903
|
+
width: Int,
|
|
904
|
+
height: Int,
|
|
905
|
+
caption: OptStr,
|
|
906
|
+
ocr: OptStr,
|
|
907
|
+
objects: Type.Optional(Type.Array(DetectedObject)),
|
|
908
|
+
...Provenance
|
|
909
|
+
});
|
|
910
|
+
var DocumentNode = Type.Object({
|
|
911
|
+
id: Str,
|
|
912
|
+
kind: Str,
|
|
913
|
+
parent_id: OptStr,
|
|
914
|
+
title: OptStr,
|
|
915
|
+
level: OptInt,
|
|
916
|
+
column: OptInt,
|
|
917
|
+
text: OptStr,
|
|
918
|
+
...Position
|
|
919
|
+
});
|
|
920
|
+
var DocumentAsset = Type.Object({
|
|
921
|
+
id: Str,
|
|
922
|
+
kind: Str,
|
|
923
|
+
mime: Str,
|
|
924
|
+
path: OptStr,
|
|
925
|
+
content_identity: OptStr,
|
|
926
|
+
bytes: OptInt,
|
|
927
|
+
...Position
|
|
928
|
+
});
|
|
929
|
+
var DocumentVision = Type.Object({
|
|
930
|
+
asset_id: Str,
|
|
931
|
+
mode: OptStr,
|
|
932
|
+
caption: OptStr,
|
|
933
|
+
ocr: OptStr,
|
|
934
|
+
objects: Type.Optional(Type.Array(DetectedObject)),
|
|
935
|
+
caption_requested: Type.Optional(Bool),
|
|
936
|
+
objects_requested: Type.Optional(Bool),
|
|
937
|
+
ocr_requested: Type.Optional(Bool),
|
|
938
|
+
...Provenance
|
|
939
|
+
});
|
|
940
|
+
var DocumentOutput = Type.Object({
|
|
941
|
+
id: OptStr,
|
|
942
|
+
format: Str,
|
|
943
|
+
source: Str,
|
|
944
|
+
title: OptStr,
|
|
945
|
+
pages: Int,
|
|
946
|
+
nodes: Type.Array(DocumentNode, { default: [] }),
|
|
947
|
+
assets: Type.Array(DocumentAsset, { default: [] }),
|
|
948
|
+
vision: Type.Array(DocumentVision, { default: [] })
|
|
949
|
+
});
|
|
950
|
+
var TextHit = Type.Object({
|
|
951
|
+
line: Int,
|
|
952
|
+
column: Int,
|
|
953
|
+
text: Str,
|
|
954
|
+
start_line: Int,
|
|
955
|
+
end_line: Int,
|
|
956
|
+
start_byte: Int,
|
|
957
|
+
end_byte: Int,
|
|
958
|
+
hashlines: Hashlines
|
|
959
|
+
});
|
|
960
|
+
var DocumentHit = Type.Object({ node_id: Str, kind: Str, snippet: Str, page: OptInt });
|
|
961
|
+
var TextSearchOutput = Type.Object({
|
|
962
|
+
text: Str,
|
|
963
|
+
mode: Type.Literal("literal"),
|
|
964
|
+
total_matches: Int,
|
|
965
|
+
returned_matches: Int,
|
|
966
|
+
truncated: Bool,
|
|
967
|
+
results: Type.Array(Type.Object({ file: Str, file_hash: Str, matches: Type.Array(TextHit) })),
|
|
968
|
+
documents: Type.Array(Type.Object({ file: Str, content_hash: Str, hits: Type.Array(DocumentHit) }))
|
|
969
|
+
});
|
|
970
|
+
var RuleFinding = Type.Object({
|
|
971
|
+
name: Str,
|
|
972
|
+
message: Str,
|
|
973
|
+
path: Str,
|
|
974
|
+
language: Str,
|
|
975
|
+
start_byte: Int,
|
|
976
|
+
end_byte: Int,
|
|
977
|
+
line: Int,
|
|
978
|
+
column: Int,
|
|
979
|
+
end_line: Int,
|
|
980
|
+
end_column: Int
|
|
981
|
+
});
|
|
982
|
+
var RulesOutput = Type.Object({
|
|
983
|
+
complete: Bool,
|
|
984
|
+
check_count: Int,
|
|
985
|
+
file_count: Int,
|
|
986
|
+
violation_count: Int,
|
|
987
|
+
diagnostics: Type.Array(RuleFinding)
|
|
988
|
+
});
|
|
989
|
+
var SourceAssetsOutput = Type.Object({ file: OptStr, metadata: Type.Optional(Detection), assets: Type.Array(DocumentAsset, { default: [] }) });
|
|
990
|
+
|
|
991
|
+
// ../readseek-api/src/edit-plan.ts
|
|
992
|
+
import { Document, Node, format } from "@bgotink/kdl";
|
|
993
|
+
function node(name, properties, children = []) {
|
|
994
|
+
const result = Node.create(name);
|
|
995
|
+
for (const [key, value] of Object.entries(properties))
|
|
996
|
+
result.setProperty(key, value);
|
|
997
|
+
if (children.length > 0)
|
|
998
|
+
result.children = new Document(children);
|
|
999
|
+
return result;
|
|
1000
|
+
}
|
|
1001
|
+
function editNode(edit) {
|
|
1002
|
+
if ("set_line" in edit) {
|
|
1003
|
+
const value = edit.set_line;
|
|
1004
|
+
return node("set-line", { anchor: value.anchor, text: value.new_text });
|
|
1005
|
+
}
|
|
1006
|
+
if ("replace_lines" in edit) {
|
|
1007
|
+
const value = edit.replace_lines;
|
|
1008
|
+
return node("replace-lines", { start: value.start_anchor, end: value.end_anchor, text: value.new_text });
|
|
1009
|
+
}
|
|
1010
|
+
if ("insert_after" in edit) {
|
|
1011
|
+
const value = edit.insert_after;
|
|
1012
|
+
return node("insert-after", { anchor: value.anchor, text: value.new_text });
|
|
1013
|
+
}
|
|
1014
|
+
if ("replace_symbol" in edit) {
|
|
1015
|
+
const value = edit.replace_symbol;
|
|
1016
|
+
return node("replace-symbol", { symbol: value.symbol, body: value.new_body });
|
|
1017
|
+
}
|
|
1018
|
+
const value = edit.replace;
|
|
1019
|
+
return node("replace", {
|
|
1020
|
+
old: value.old_text,
|
|
1021
|
+
new: value.new_text,
|
|
1022
|
+
...value.all === undefined ? {} : { all: value.all }
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
function serializeEditPlan(edits) {
|
|
1026
|
+
const validation = validateExactlyOneEditVariant(edits);
|
|
1027
|
+
if (!validation.ok)
|
|
1028
|
+
throw new Error(validation.error);
|
|
1029
|
+
return format(new Document([node("plan", {}, validation.edits.map(editNode))]));
|
|
1030
|
+
}
|
|
1031
|
+
// ../readseek-api/src/show-presentation.ts
|
|
1032
|
+
function isShowTool(name) {
|
|
1033
|
+
return name === "show_content" || name === "show_structure" || name === "show_metadata" || name === "show_assets";
|
|
1034
|
+
}
|
|
1035
|
+
function record(value) {
|
|
1036
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1037
|
+
}
|
|
1038
|
+
function records(value) {
|
|
1039
|
+
return Array.isArray(value) ? value.map(record) : [];
|
|
1040
|
+
}
|
|
1041
|
+
function text(value) {
|
|
1042
|
+
return typeof value === "string" ? value : "";
|
|
1043
|
+
}
|
|
1044
|
+
function count(value) {
|
|
1045
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : undefined;
|
|
1046
|
+
}
|
|
1047
|
+
function safe(value) {
|
|
1048
|
+
return value.replace(/[\x00-\x09\x0b-\x1f\x7f-\x9f]/g, (char) => {
|
|
1049
|
+
if (char === "\t")
|
|
1050
|
+
return "\\t";
|
|
1051
|
+
if (char === "\r")
|
|
1052
|
+
return "\\r";
|
|
1053
|
+
return `\\x${char.charCodeAt(0).toString(16).padStart(2, "0")}`;
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
function inline(value) {
|
|
1057
|
+
return safe(text(value)).replaceAll(`
|
|
1058
|
+
`, "\\n");
|
|
1059
|
+
}
|
|
1060
|
+
function plural(n, unit) {
|
|
1061
|
+
return `${n} ${unit}${n === 1 ? "" : "s"}`;
|
|
1062
|
+
}
|
|
1063
|
+
function fields(value, omit = []) {
|
|
1064
|
+
return Object.entries(value).flatMap(([key, item]) => {
|
|
1065
|
+
if (omit.includes(key) || item === undefined || item === null || typeof item === "object")
|
|
1066
|
+
return [];
|
|
1067
|
+
const label = key.replaceAll("_", " ");
|
|
1068
|
+
return [`${label[0]?.toUpperCase() ?? ""}${label.slice(1)}: ${safe(String(item))}`];
|
|
1069
|
+
}).join(`
|
|
1070
|
+
`);
|
|
1071
|
+
}
|
|
1072
|
+
function location(value) {
|
|
1073
|
+
const start = text(value.start_anchor) || (count(value.start_line) !== undefined ? `${value.start_line}${text(value.start_hash) ? `:${value.start_hash}` : ""}` : "");
|
|
1074
|
+
const end = text(value.end_anchor) || (count(value.end_line) !== undefined ? `${value.end_line}${text(value.end_hash) ? `:${value.end_hash}` : ""}` : "");
|
|
1075
|
+
return start ? ` ${inline(start)}${end && end !== start ? ` - ${inline(end)}` : ""}` : "";
|
|
1076
|
+
}
|
|
1077
|
+
function sourceStructure(structure) {
|
|
1078
|
+
const sections = [];
|
|
1079
|
+
if (structure.root) {
|
|
1080
|
+
const lines = [];
|
|
1081
|
+
const stack = [{ node: record(structure.root), depth: 0 }];
|
|
1082
|
+
while (stack.length > 0) {
|
|
1083
|
+
const { node, depth } = stack.pop();
|
|
1084
|
+
lines.push(`${" ".repeat(Math.min(depth, 12))}${inline(node.kind) || "node"}${text(node.name) ? ` ${inline(node.name)}` : ""}${location(node)}`);
|
|
1085
|
+
const children = records(node.children);
|
|
1086
|
+
for (let i = children.length - 1;i >= 0; i--)
|
|
1087
|
+
stack.push({ node: children[i], depth: depth + 1 });
|
|
1088
|
+
}
|
|
1089
|
+
sections.push(`Tree
|
|
1090
|
+
${lines.join(`
|
|
1091
|
+
`)}`);
|
|
1092
|
+
}
|
|
1093
|
+
const symbols = records(structure.symbols);
|
|
1094
|
+
if (symbols.length > 0) {
|
|
1095
|
+
sections.push(`Symbols
|
|
1096
|
+
${symbols.map((symbol) => `${inline(symbol.kind) || "symbol"} ${inline(symbol.qualified_name || symbol.name)}${location(symbol)}`).join(`
|
|
1097
|
+
`)}`);
|
|
1098
|
+
}
|
|
1099
|
+
return sections.join(`
|
|
1100
|
+
|
|
1101
|
+
`) || "No structure found in this selection.";
|
|
1102
|
+
}
|
|
1103
|
+
function documentNodes(nodes, content) {
|
|
1104
|
+
const depths = new Map;
|
|
1105
|
+
return nodes.map((node) => {
|
|
1106
|
+
const depth = Math.min((depths.get(text(node.parent_id)) ?? -1) + 1, 12);
|
|
1107
|
+
depths.set(text(node.id), depth);
|
|
1108
|
+
const indent = " ".repeat(depth);
|
|
1109
|
+
const label = inline(node.title) || inline(node.kind) || "node";
|
|
1110
|
+
const page = count(node.page);
|
|
1111
|
+
const selector = text(node.id) ? `node:${inline(node.id)}` : "";
|
|
1112
|
+
const position = [selector, page !== undefined ? `page:${page}` : ""].filter(Boolean).join(" · ");
|
|
1113
|
+
const heading = `${indent}${label}${position ? ` [${position}]` : ""}`;
|
|
1114
|
+
const body = text(node.text);
|
|
1115
|
+
return content && body ? `${heading}
|
|
1116
|
+
${safe(body).split(`
|
|
1117
|
+
`).map((line) => indent + line).join(`
|
|
1118
|
+
`)}` : heading;
|
|
1119
|
+
}).join(content ? `
|
|
1120
|
+
|
|
1121
|
+
` : `
|
|
1122
|
+
`);
|
|
1123
|
+
}
|
|
1124
|
+
function assetList(assets) {
|
|
1125
|
+
return assets.map((asset) => {
|
|
1126
|
+
const id = text(asset.id);
|
|
1127
|
+
const page = count(asset.page);
|
|
1128
|
+
const heading = [id ? `asset:${inline(id)}` : inline(asset.kind) || "image", inline(asset.mime), page !== undefined ? `page:${page}` : ""].filter(Boolean).join(" · ");
|
|
1129
|
+
const details = fields(asset, ["id", "kind", "mime", "page", "bbox", "content_identity"]);
|
|
1130
|
+
return `${heading}${details ? `
|
|
1131
|
+
${details.split(`
|
|
1132
|
+
`).map((line) => ` ${line}`).join(`
|
|
1133
|
+
`)}` : ""}`;
|
|
1134
|
+
}).join(`
|
|
1135
|
+
|
|
1136
|
+
`);
|
|
1137
|
+
}
|
|
1138
|
+
function presentShowResult(tool, value, options = {}) {
|
|
1139
|
+
const output = record(value);
|
|
1140
|
+
const metadata = record(output.metadata);
|
|
1141
|
+
const content = record(output.content);
|
|
1142
|
+
const cursor = record(output.cursor);
|
|
1143
|
+
const structure = record(output.structure);
|
|
1144
|
+
const document = typeof output.format === "string" && (Array.isArray(output.nodes) || typeof output.source === "string");
|
|
1145
|
+
const path = inline(options.path || output.source || output.file || content.file || structure.file || metadata.file || cursor.file) || "(unknown path)";
|
|
1146
|
+
const summary = [];
|
|
1147
|
+
const result = { summary: "", body: "", notices: [], metadata: {} };
|
|
1148
|
+
if (tool === "show_metadata") {
|
|
1149
|
+
const data = Object.keys(metadata).length > 0 ? metadata : output;
|
|
1150
|
+
if (text(data.format || data.language || data.type))
|
|
1151
|
+
summary.push(inline(data.format || data.language || data.type));
|
|
1152
|
+
if (count(data.pages) !== undefined) {
|
|
1153
|
+
result.metadata.pages = data.pages;
|
|
1154
|
+
summary.push(plural(result.metadata.pages, "page"));
|
|
1155
|
+
}
|
|
1156
|
+
result.body = fields(data) || "No metadata available.";
|
|
1157
|
+
} else if (tool === "show_assets") {
|
|
1158
|
+
const assets = records(output.assets);
|
|
1159
|
+
result.metadata.assets = assets.length;
|
|
1160
|
+
summary.push(plural(assets.length, "asset"));
|
|
1161
|
+
result.body = assetList(assets) || "No assets in this selection.";
|
|
1162
|
+
} else if (document) {
|
|
1163
|
+
const nodes = records(output.nodes);
|
|
1164
|
+
const assets = records(output.assets);
|
|
1165
|
+
result.metadata.nodes = nodes.length;
|
|
1166
|
+
if (count(output.pages) !== undefined)
|
|
1167
|
+
result.metadata.pages = output.pages;
|
|
1168
|
+
summary.push(plural(nodes.length, "node"));
|
|
1169
|
+
if (result.metadata.pages !== undefined)
|
|
1170
|
+
summary.push(plural(result.metadata.pages, "page"));
|
|
1171
|
+
const sections = [text(output.title) ? safe(text(output.title)) : "", documentNodes(nodes, tool === "show_content")];
|
|
1172
|
+
if (assets.length > 0) {
|
|
1173
|
+
result.metadata.assets = assets.length;
|
|
1174
|
+
sections.push(`Assets
|
|
1175
|
+
${assetList(assets)}`);
|
|
1176
|
+
}
|
|
1177
|
+
result.body = sections.filter(Boolean).join(`
|
|
1178
|
+
|
|
1179
|
+
`) || "No content in this selection.";
|
|
1180
|
+
} else if (tool === "show_structure") {
|
|
1181
|
+
const symbols = records(structure.symbols);
|
|
1182
|
+
result.metadata.symbols = symbols.length;
|
|
1183
|
+
summary.push(plural(symbols.length, "symbol"));
|
|
1184
|
+
result.body = sourceStructure(structure);
|
|
1185
|
+
} else if (content.kind === "image" || count(content.width) !== undefined && count(content.height) !== undefined || count(metadata.width) !== undefined && count(metadata.height) !== undefined || text(content.mime).startsWith("image/") || text(metadata.mime).startsWith("image/") || text(metadata.type).startsWith("image/")) {
|
|
1186
|
+
const description = Object.keys(content).length > 0 ? content : Object.keys(metadata).length > 0 ? metadata : output;
|
|
1187
|
+
if (count(description.width) !== undefined && count(description.height) !== undefined) {
|
|
1188
|
+
summary.push(`${description.width} × ${description.height}`);
|
|
1189
|
+
} else if (count(metadata.width) !== undefined && count(metadata.height) !== undefined) {
|
|
1190
|
+
summary.push(`${metadata.width} × ${metadata.height}`);
|
|
1191
|
+
}
|
|
1192
|
+
const mime = text(description.mime || metadata.mime || metadata.type);
|
|
1193
|
+
if (mime)
|
|
1194
|
+
summary.push(inline(mime));
|
|
1195
|
+
result.body = fields(description, ["hashlines"]) || fields(metadata) || "No content in this selection.";
|
|
1196
|
+
} else if (Array.isArray(content.hashlines) || Array.isArray(cursor.hashlines)) {
|
|
1197
|
+
const lines = records(content.hashlines ?? cursor.hashlines);
|
|
1198
|
+
const start = count(content.start_line) ?? count(lines[0]?.line);
|
|
1199
|
+
const end = count(content.end_line) ?? count(lines.at(-1)?.line);
|
|
1200
|
+
const total = count(content.line_count) ?? count(cursor.line_count);
|
|
1201
|
+
if (start !== undefined && end !== undefined && start <= end) {
|
|
1202
|
+
result.metadata.start_line = start;
|
|
1203
|
+
result.metadata.end_line = end;
|
|
1204
|
+
summary.push(`lines ${start}-${end}${total !== undefined ? ` of ${total}` : ""}`);
|
|
1205
|
+
} else
|
|
1206
|
+
summary.push(plural(lines.length, "line"));
|
|
1207
|
+
const lang = text(metadata.language || content.language);
|
|
1208
|
+
if (lang)
|
|
1209
|
+
summary.push(inline(lang));
|
|
1210
|
+
const rows = lines.map((line) => `${line.line}${options.noAnchors ? "" : `:${text(line.hash)}`} | ${safe(text(line.text)).replaceAll("\\n", "\\\\n")}`);
|
|
1211
|
+
const identifier = record(cursor.identifier);
|
|
1212
|
+
if (count(cursor.line) !== undefined)
|
|
1213
|
+
result.metadata.line = cursor.line;
|
|
1214
|
+
if (count(cursor.column) !== undefined)
|
|
1215
|
+
result.metadata.column = cursor.column;
|
|
1216
|
+
if (Object.keys(cursor).length > 0) {
|
|
1217
|
+
const symbol = record(cursor.symbol);
|
|
1218
|
+
const description = [text(identifier.text), text(symbol.qualified_name || symbol.name), text(symbol.kind)].filter(Boolean);
|
|
1219
|
+
rows.unshift(`Cursor: ${cursor.line ?? "?"}:${cursor.column ?? "?"}${description.length > 0 ? ` · ${[...new Set(description)].map(inline).join(" · ")}` : ""}`, "");
|
|
1220
|
+
}
|
|
1221
|
+
result.body = rows.join(`
|
|
1222
|
+
`) || "Empty source selection.";
|
|
1223
|
+
if (start !== undefined && start > 1)
|
|
1224
|
+
result.notices.push(`Earlier lines omitted. Read preceding content with at: line:1 and limit: ${start - 1}.`);
|
|
1225
|
+
if (end !== undefined && total !== undefined && end < total) {
|
|
1226
|
+
result.notices.push(`More lines available. Continue with at: line:${end + 1}.`);
|
|
1227
|
+
} else if (content.truncated === true && (end === undefined || total === undefined)) {
|
|
1228
|
+
result.notices.push(`Content is truncated.${end !== undefined ? ` Continue with at: line:${end + 1}.` : " Select a smaller range with at and limit."}`);
|
|
1229
|
+
}
|
|
1230
|
+
} else {
|
|
1231
|
+
const description = Object.keys(content).length > 0 ? content : output;
|
|
1232
|
+
result.body = fields(description) || "No content in this selection.";
|
|
1233
|
+
}
|
|
1234
|
+
if (tool === "show_structure")
|
|
1235
|
+
result.notices.push("Structure is limited to the requested depth, kind, and outline. Use a deeper depth or focus with at to inspect further.");
|
|
1236
|
+
if (text(output.warning))
|
|
1237
|
+
result.notices.push(safe(text(output.warning)));
|
|
1238
|
+
result.summary = summary.join(" · ") || "complete";
|
|
1239
|
+
return result;
|
|
1240
|
+
}
|
|
1241
|
+
function formatShowResult(presentation) {
|
|
1242
|
+
return [presentation.summary, presentation.body, ...presentation.notices.map((notice) => `Note: ${notice}`)].filter(Boolean).join(`
|
|
1243
|
+
|
|
1244
|
+
`);
|
|
1245
|
+
}
|
|
1246
|
+
// ../readseek-api/src/platform.ts
|
|
1247
|
+
import { accessSync, constants as fsConstants, existsSync, statSync } from "node:fs";
|
|
1248
|
+
import { createRequire } from "node:module";
|
|
1249
|
+
import { homedir } from "node:os";
|
|
1250
|
+
import path2 from "node:path";
|
|
1251
|
+
var READSEEK_BINARY_ENV = "READSEEK_BINARY";
|
|
1252
|
+
var READSEEK_PLATFORM_PACKAGES = {
|
|
1253
|
+
"darwin-arm64": "@jarkkojs/readseek-darwin-arm64",
|
|
1254
|
+
"linux-arm64": "@jarkkojs/readseek-linux-arm64",
|
|
1255
|
+
"linux-x64": "@jarkkojs/readseek-linux-x64",
|
|
1256
|
+
"win32-x64": "@jarkkojs/readseek-win32-x64"
|
|
1257
|
+
};
|
|
1258
|
+
var resolveFromPackage = createRequire(import.meta.url).resolve;
|
|
1259
|
+
function readSeekBinaryName(platform = process.platform) {
|
|
1260
|
+
if (platform === "linux")
|
|
1261
|
+
return "readseek.AppImage";
|
|
1262
|
+
return platform === "win32" ? "readseek.exe" : "readseek";
|
|
1263
|
+
}
|
|
1264
|
+
function readSeekPlatformKey(platform = process.platform, arch = process.arch) {
|
|
1265
|
+
return `${platform}-${arch}`;
|
|
1266
|
+
}
|
|
1267
|
+
function resolveReadSeekBinaryOverride(environment = process.env) {
|
|
1268
|
+
const binaryPath = environment[READSEEK_BINARY_ENV];
|
|
1269
|
+
if (binaryPath === undefined)
|
|
1270
|
+
return null;
|
|
1271
|
+
if (!path2.isAbsolute(binaryPath)) {
|
|
1272
|
+
throw new Error(`${READSEEK_BINARY_ENV} must be an absolute path to the readseek executable`);
|
|
1273
|
+
}
|
|
1274
|
+
try {
|
|
1275
|
+
if (!statSync(binaryPath).isFile())
|
|
1276
|
+
throw new Error("not a regular file");
|
|
1277
|
+
accessSync(binaryPath, fsConstants.X_OK);
|
|
1278
|
+
} catch (error) {
|
|
1279
|
+
throw new Error(`${READSEEK_BINARY_ENV} does not point to an executable file: ${binaryPath}`, {
|
|
1280
|
+
cause: error
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
return binaryPath;
|
|
1284
|
+
}
|
|
1285
|
+
function resolveReadSeekBinaryFromPackages(resolver = resolveFromPackage, options = {}) {
|
|
1286
|
+
const resolve = typeof resolver === "function" ? resolver : resolver.resolve.bind(resolver);
|
|
1287
|
+
const platform = options.platform ?? process.platform;
|
|
1288
|
+
const arch = options.arch ?? process.arch;
|
|
1289
|
+
const key = readSeekPlatformKey(platform, arch);
|
|
1290
|
+
const platformPackage = READSEEK_PLATFORM_PACKAGES[key];
|
|
1291
|
+
if (!platformPackage) {
|
|
1292
|
+
const supported = Object.keys(READSEEK_PLATFORM_PACKAGES).join(", ");
|
|
1293
|
+
throw new Error(`@jarkkojs/readseek ships no binary for ${key}; it supports ${supported}`);
|
|
1294
|
+
}
|
|
1295
|
+
const metaPackage = options.metaPackage ?? "@jarkkojs/readseek";
|
|
1296
|
+
let packageJsonPath;
|
|
1297
|
+
try {
|
|
1298
|
+
const metaPackageJson = resolve(`${metaPackage}/package.json`);
|
|
1299
|
+
const metaPackageDir = path2.dirname(metaPackageJson);
|
|
1300
|
+
packageJsonPath = resolve(`${platformPackage}/package.json`, {
|
|
1301
|
+
paths: [metaPackageDir]
|
|
930
1302
|
});
|
|
931
1303
|
} catch {
|
|
932
1304
|
packageJsonPath = resolve(`${platformPackage}/package.json`);
|
|
933
1305
|
}
|
|
934
|
-
const binaryPath =
|
|
1306
|
+
const binaryPath = path2.join(path2.dirname(packageJsonPath), "bin", readSeekBinaryName(platform));
|
|
935
1307
|
accessSync(binaryPath, fsConstants.X_OK);
|
|
936
1308
|
return binaryPath;
|
|
937
1309
|
}
|
|
@@ -942,18 +1314,18 @@ function resolveReadSeekCacheDir(subPath, options = {}) {
|
|
|
942
1314
|
const cwd = options.cwd ?? process.cwd();
|
|
943
1315
|
const localDirName = options.localDirName ?? ".readseek";
|
|
944
1316
|
if (localDirName && localDirName.length > 0) {
|
|
945
|
-
const local =
|
|
1317
|
+
const local = path2.join(cwd, localDirName);
|
|
946
1318
|
if (existsSync(local))
|
|
947
1319
|
return local;
|
|
948
1320
|
}
|
|
949
1321
|
let base;
|
|
950
1322
|
if (platform === "win32") {
|
|
951
|
-
base = env.LOCALAPPDATA ??
|
|
1323
|
+
base = env.LOCALAPPDATA ?? path2.join(home, "AppData", "Local");
|
|
952
1324
|
} else {
|
|
953
1325
|
const xdg = env.XDG_CACHE_HOME?.trim();
|
|
954
|
-
base = xdg &&
|
|
1326
|
+
base = xdg && path2.isAbsolute(xdg) ? xdg : path2.join(home, ".cache");
|
|
955
1327
|
}
|
|
956
|
-
const pathModule = platform === "win32" ?
|
|
1328
|
+
const pathModule = platform === "win32" ? path2.win32 : path2;
|
|
957
1329
|
if (subPath === undefined) {
|
|
958
1330
|
return pathModule.join(base, "readseek");
|
|
959
1331
|
}
|
|
@@ -961,10 +1333,10 @@ function resolveReadSeekCacheDir(subPath, options = {}) {
|
|
|
961
1333
|
return pathModule.join(base, ...parts);
|
|
962
1334
|
}
|
|
963
1335
|
// ../readseek-api/src/path.ts
|
|
964
|
-
import
|
|
1336
|
+
import path3 from "node:path";
|
|
965
1337
|
function isSubPath(parent, candidate) {
|
|
966
|
-
const relative =
|
|
967
|
-
return relative === "" || !relative.startsWith(`..${
|
|
1338
|
+
const relative = path3.relative(parent, candidate);
|
|
1339
|
+
return relative === "" || !relative.startsWith(`..${path3.sep}`) && relative !== ".." && !path3.isAbsolute(relative);
|
|
968
1340
|
}
|
|
969
1341
|
// ../readseek-api/src/error.ts
|
|
970
1342
|
var READSEEK_USAGE_HINT = /(?:^|\n)\s*Run readseek(?: [\w-]+)* --help for more information\.?\s*$/;
|
|
@@ -1011,634 +1383,420 @@ function properties(node) {
|
|
|
1011
1383
|
return Object.fromEntries(node.properties);
|
|
1012
1384
|
}
|
|
1013
1385
|
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}`);
|
|
1259
|
-
}
|
|
1386
|
+
return node.children.filter((child) => child.name === name);
|
|
1260
1387
|
}
|
|
1261
|
-
|
|
1262
|
-
|
|
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;
|
|
1388
|
+
function child(node, name) {
|
|
1389
|
+
return node.children.find((candidate) => candidate.name === name);
|
|
1266
1390
|
}
|
|
1267
|
-
function
|
|
1391
|
+
function requireProperty(node, name) {
|
|
1392
|
+
const value = node.properties.get(name);
|
|
1393
|
+
if (value === undefined)
|
|
1394
|
+
throw new Error(`invalid readseek KDL: ${node.name} is missing '${name}'`);
|
|
1395
|
+
return value;
|
|
1396
|
+
}
|
|
1397
|
+
function line(node) {
|
|
1398
|
+
const anchor = requireProperty(node, "anchor");
|
|
1399
|
+
const content = requireProperty(node, "content");
|
|
1400
|
+
if (typeof anchor !== "string" || typeof content !== "string") {
|
|
1401
|
+
throw new Error("invalid readseek KDL line");
|
|
1402
|
+
}
|
|
1403
|
+
const match = /^(\d+):([0-9a-f]+)$/.exec(anchor);
|
|
1404
|
+
if (!match)
|
|
1405
|
+
throw new Error(`invalid readseek KDL line anchor: ${anchor}`);
|
|
1268
1406
|
return {
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1407
|
+
line: Number(match[1]),
|
|
1408
|
+
hash: match[2],
|
|
1409
|
+
text: content
|
|
1272
1410
|
};
|
|
1273
1411
|
}
|
|
1274
|
-
function
|
|
1275
|
-
|
|
1276
|
-
|
|
1412
|
+
function mapNode(node) {
|
|
1413
|
+
return {
|
|
1414
|
+
...properties(node),
|
|
1415
|
+
children: children(node, "node").map(mapNode)
|
|
1416
|
+
};
|
|
1277
1417
|
}
|
|
1278
|
-
function
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
if (
|
|
1283
|
-
|
|
1418
|
+
function sourceContent(node) {
|
|
1419
|
+
const result = properties(node);
|
|
1420
|
+
result.hashlines = children(node, "line").map(line);
|
|
1421
|
+
const objects = children(node, "object");
|
|
1422
|
+
if (objects.length > 0) {
|
|
1423
|
+
result.objects = objects.map((object) => ({
|
|
1424
|
+
label: requireProperty(object, "label"),
|
|
1425
|
+
bbox: [
|
|
1426
|
+
requireProperty(object, "xmin"),
|
|
1427
|
+
requireProperty(object, "ymin"),
|
|
1428
|
+
requireProperty(object, "xmax"),
|
|
1429
|
+
requireProperty(object, "ymax")
|
|
1430
|
+
]
|
|
1431
|
+
}));
|
|
1284
1432
|
}
|
|
1285
|
-
|
|
1286
|
-
if (cleaned)
|
|
1287
|
-
return new Error(cleaned);
|
|
1288
|
-
if (result.signal)
|
|
1289
|
-
return new Error(`readseek killed by signal ${result.signal}`);
|
|
1290
|
-
return new Error(`readseek exited with status ${result.exitCode}`);
|
|
1433
|
+
return result;
|
|
1291
1434
|
}
|
|
1292
|
-
function
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1435
|
+
function sourceMap(node) {
|
|
1436
|
+
const root = child(node, "node");
|
|
1437
|
+
return {
|
|
1438
|
+
...properties(node),
|
|
1439
|
+
...root ? { root: mapNode(root) } : {},
|
|
1440
|
+
symbols: children(node, "symbol").map(properties)
|
|
1441
|
+
};
|
|
1296
1442
|
}
|
|
1297
|
-
function
|
|
1298
|
-
return
|
|
1443
|
+
function diagnostics(node) {
|
|
1444
|
+
return {
|
|
1445
|
+
...properties(node),
|
|
1446
|
+
diagnostics: children(node, "diagnostic").map(properties)
|
|
1447
|
+
};
|
|
1299
1448
|
}
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
const
|
|
1303
|
-
const
|
|
1304
|
-
if (
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1449
|
+
function identity(node) {
|
|
1450
|
+
const result = properties(node);
|
|
1451
|
+
const identifier = child(node, "identifier");
|
|
1452
|
+
const symbol = child(node, "symbol");
|
|
1453
|
+
if (identifier)
|
|
1454
|
+
result.identifier = properties(identifier);
|
|
1455
|
+
if (symbol)
|
|
1456
|
+
result.symbol = properties(symbol);
|
|
1457
|
+
return result;
|
|
1308
1458
|
}
|
|
1309
|
-
|
|
1310
|
-
const
|
|
1311
|
-
const
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
offset += chunk.byteLength;
|
|
1336
|
-
}
|
|
1337
|
-
return new TextDecoder().decode(bytes);
|
|
1459
|
+
function sourceDocument(root) {
|
|
1460
|
+
const result = {};
|
|
1461
|
+
const metadata = child(root, "metadata");
|
|
1462
|
+
const cache = child(root, "cache");
|
|
1463
|
+
const content = child(root, "content");
|
|
1464
|
+
const structure = child(root, "structure");
|
|
1465
|
+
const diagnosticOutput = child(root, "diagnostics");
|
|
1466
|
+
const identityOutput = child(root, "cursor");
|
|
1467
|
+
if (metadata)
|
|
1468
|
+
result.metadata = properties(metadata);
|
|
1469
|
+
if (!metadata && !content && !structure && !diagnosticOutput && !identityOutput && root.properties.has("file"))
|
|
1470
|
+
result.file = root.properties.get("file");
|
|
1471
|
+
const assets = children(root, "asset");
|
|
1472
|
+
if (assets.length > 0)
|
|
1473
|
+
result.assets = assets.map(genericNode);
|
|
1474
|
+
if (cache)
|
|
1475
|
+
result.cache = properties(cache);
|
|
1476
|
+
if (content)
|
|
1477
|
+
result.content = sourceContent(content);
|
|
1478
|
+
if (structure)
|
|
1479
|
+
result.structure = sourceMap(structure);
|
|
1480
|
+
if (diagnosticOutput)
|
|
1481
|
+
result.diagnostics = diagnostics(diagnosticOutput);
|
|
1482
|
+
if (identityOutput)
|
|
1483
|
+
result.cursor = identity(identityOutput);
|
|
1484
|
+
return result;
|
|
1338
1485
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
cwd: options.cwd,
|
|
1348
|
-
env: readSeekProcessEnvironment(),
|
|
1349
|
-
stderr: "pipe",
|
|
1350
|
-
stdin,
|
|
1351
|
-
stdout: "pipe"
|
|
1352
|
-
};
|
|
1353
|
-
const child = spawn([binaryPath, ...args], limits.cancelable && options.signal ? { ...spawnOptions, killSignal: "SIGKILL", signal: options.signal } : spawnOptions);
|
|
1354
|
-
const killForOverflow = () => {
|
|
1355
|
-
try {
|
|
1356
|
-
child.kill("SIGKILL");
|
|
1357
|
-
} catch {}
|
|
1486
|
+
function hit(node) {
|
|
1487
|
+
return {
|
|
1488
|
+
...properties(node),
|
|
1489
|
+
hashlines: children(node, "line").map(line),
|
|
1490
|
+
captures: children(node, "capture").map((capture) => ({
|
|
1491
|
+
...properties(capture),
|
|
1492
|
+
hashlines: children(capture, "line").map(line)
|
|
1493
|
+
}))
|
|
1358
1494
|
};
|
|
1359
|
-
let timeout;
|
|
1360
|
-
const timeoutResult = new Promise((_resolve, reject) => {
|
|
1361
|
-
timeout = setTimeout(() => {
|
|
1362
|
-
try {
|
|
1363
|
-
child.kill("SIGKILL");
|
|
1364
|
-
} catch {}
|
|
1365
|
-
reject(failureFromProcessResult({ stdout: "", stderr: "", exitCode: null, signal: null }, { timedOut: true, timeoutMs: limits.timeoutMs }));
|
|
1366
|
-
}, limits.timeoutMs);
|
|
1367
|
-
timeout.unref?.();
|
|
1368
|
-
});
|
|
1369
|
-
const pending = Promise.all([
|
|
1370
|
-
readBoundedText(child.stdout, "stdout", limits.maxOutputBytes, killForOverflow),
|
|
1371
|
-
readBoundedText(child.stderr, "stderr", limits.maxOutputBytes, killForOverflow),
|
|
1372
|
-
child.exited
|
|
1373
|
-
]);
|
|
1374
|
-
try {
|
|
1375
|
-
const [stdout, stderr, exitCode] = await Promise.race([pending, timeoutResult]);
|
|
1376
|
-
if (limits.cancelable && options.signal?.aborted)
|
|
1377
|
-
throw readSeekCancellationError(options.signal);
|
|
1378
|
-
return { stdout, stderr, exitCode, signal: null };
|
|
1379
|
-
} finally {
|
|
1380
|
-
if (timeout !== undefined)
|
|
1381
|
-
clearTimeout(timeout);
|
|
1382
|
-
pending.catch(() => {
|
|
1383
|
-
return;
|
|
1384
|
-
});
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1387
|
-
async function runReadSeekBunText(binaryPath, args, options = {}) {
|
|
1388
|
-
return ensureSuccessfulStdout(await runReadSeekBun(binaryPath, args, options));
|
|
1389
1495
|
}
|
|
1390
|
-
|
|
1391
|
-
return
|
|
1496
|
+
function search(root) {
|
|
1497
|
+
return {
|
|
1498
|
+
...properties(root),
|
|
1499
|
+
results: children(root, "match").map((match) => ({
|
|
1500
|
+
...properties(match),
|
|
1501
|
+
matches: children(match, "hit").map(hit)
|
|
1502
|
+
})),
|
|
1503
|
+
documents: children(root, "document").map((documentNode) => ({
|
|
1504
|
+
...properties(documentNode),
|
|
1505
|
+
hits: children(documentNode, "hit").map(properties)
|
|
1506
|
+
}))
|
|
1507
|
+
};
|
|
1392
1508
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
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)
|
|
1509
|
+
function references(root) {
|
|
1510
|
+
const parsed = children(root, "reference").map((reference) => {
|
|
1511
|
+
const sourceLine = child(reference, "line");
|
|
1512
|
+
if (!sourceLine)
|
|
1513
|
+
throw new Error("invalid readseek KDL reference: missing line");
|
|
1514
|
+
const parsedLine = line(sourceLine);
|
|
1515
|
+
const symbol = child(reference, "symbol");
|
|
1516
|
+
return {
|
|
1517
|
+
...properties(reference),
|
|
1518
|
+
line_hash: parsedLine.hash,
|
|
1519
|
+
text: parsedLine.text,
|
|
1520
|
+
...symbol ? { symbol: properties(symbol) } : {}
|
|
1521
|
+
};
|
|
1432
1522
|
});
|
|
1523
|
+
return {
|
|
1524
|
+
references: parsed,
|
|
1525
|
+
locations: parsed.map((reference) => {
|
|
1526
|
+
const { symbol, ...location } = reference;
|
|
1527
|
+
return symbol && typeof symbol === "object" ? { ...location, ...symbol } : location;
|
|
1528
|
+
})
|
|
1529
|
+
};
|
|
1433
1530
|
}
|
|
1434
|
-
function
|
|
1435
|
-
return
|
|
1531
|
+
function definitions(root) {
|
|
1532
|
+
return {
|
|
1533
|
+
locations: children(root, "definition").map((definition) => {
|
|
1534
|
+
const symbol = child(definition, "symbol");
|
|
1535
|
+
const sourceLine = child(definition, "line");
|
|
1536
|
+
if (!symbol || !sourceLine) {
|
|
1537
|
+
throw new Error("invalid readseek KDL definition: missing symbol or line");
|
|
1538
|
+
}
|
|
1539
|
+
const symbolProperties = properties(symbol);
|
|
1540
|
+
const parsedLine = line(sourceLine);
|
|
1541
|
+
const definitionColumn = definition.properties.get("column");
|
|
1542
|
+
const nameByte = symbol.properties.get("name_byte");
|
|
1543
|
+
const startByte = symbol.properties.get("start_byte");
|
|
1544
|
+
let column = 1;
|
|
1545
|
+
if (typeof definitionColumn === "number") {
|
|
1546
|
+
column = definitionColumn;
|
|
1547
|
+
} else if (typeof nameByte === "number" && typeof startByte === "number") {
|
|
1548
|
+
column = nameByte - startByte + 1;
|
|
1549
|
+
}
|
|
1550
|
+
return {
|
|
1551
|
+
file: requireProperty(definition, "file"),
|
|
1552
|
+
line: parsedLine.line,
|
|
1553
|
+
column,
|
|
1554
|
+
line_hash: parsedLine.hash,
|
|
1555
|
+
text: parsedLine.text,
|
|
1556
|
+
kind: symbolProperties.kind,
|
|
1557
|
+
name: symbolProperties.name,
|
|
1558
|
+
qualified_name: symbolProperties.qualified_name
|
|
1559
|
+
};
|
|
1560
|
+
})
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
function edit(root) {
|
|
1564
|
+
const result = properties(root);
|
|
1565
|
+
for (const name of ["before_content", "content"]) {
|
|
1566
|
+
const contentNode = child(root, name);
|
|
1567
|
+
if (contentNode)
|
|
1568
|
+
result[name] = requireProperty(contentNode, "content");
|
|
1569
|
+
}
|
|
1570
|
+
return result;
|
|
1571
|
+
}
|
|
1572
|
+
function renameEdit(node) {
|
|
1573
|
+
const sourceLine = child(node, "line");
|
|
1574
|
+
if (!sourceLine)
|
|
1575
|
+
throw new Error("invalid readseek KDL rename edit: missing line");
|
|
1576
|
+
return {
|
|
1577
|
+
...properties(node),
|
|
1578
|
+
text: requireProperty(sourceLine, "content")
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
function renameFile(node) {
|
|
1582
|
+
return {
|
|
1583
|
+
...properties(node),
|
|
1584
|
+
edits: children(node, "edit").map(renameEdit),
|
|
1585
|
+
conflicts: children(node, "conflict").map(properties)
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
function rename(root) {
|
|
1589
|
+
return {
|
|
1590
|
+
...renameFile(root),
|
|
1591
|
+
others: children(root, "other").map(renameFile)
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
function genericNode(node) {
|
|
1595
|
+
const result = properties(node);
|
|
1596
|
+
const textNode = child(node, "text");
|
|
1597
|
+
const bbox = child(node, "bbox");
|
|
1598
|
+
if (textNode)
|
|
1599
|
+
result.text = requireProperty(textNode, "content");
|
|
1600
|
+
if (bbox)
|
|
1601
|
+
result.bbox = properties(bbox);
|
|
1602
|
+
const objects = children(node, "object");
|
|
1603
|
+
if (objects.length > 0)
|
|
1604
|
+
result.objects = objects.map((object) => ({
|
|
1605
|
+
label: requireProperty(object, "label"),
|
|
1606
|
+
bbox: [requireProperty(object, "x0"), requireProperty(object, "y0"), requireProperty(object, "x1"), requireProperty(object, "y1")]
|
|
1607
|
+
}));
|
|
1608
|
+
return result;
|
|
1609
|
+
}
|
|
1610
|
+
function pdfDocument(root) {
|
|
1611
|
+
return {
|
|
1612
|
+
...properties(root),
|
|
1613
|
+
nodes: children(root, "node").map(genericNode),
|
|
1614
|
+
assets: children(root, "asset").map(genericNode),
|
|
1615
|
+
vision: children(root, "vision").map(genericNode)
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
function parseReadSeekKdl(text) {
|
|
1619
|
+
const root = parseKdlDocument(text);
|
|
1620
|
+
switch (root.name) {
|
|
1621
|
+
case "document":
|
|
1622
|
+
return root.properties.has("format") ? pdfDocument(root) : sourceDocument(root);
|
|
1623
|
+
case "search":
|
|
1624
|
+
return search(root);
|
|
1625
|
+
case "check":
|
|
1626
|
+
return diagnostics(root);
|
|
1627
|
+
case "refs":
|
|
1628
|
+
return references(root);
|
|
1629
|
+
case "def":
|
|
1630
|
+
return definitions(root);
|
|
1631
|
+
case "edit":
|
|
1632
|
+
return edit(root);
|
|
1633
|
+
case "edit-batch":
|
|
1634
|
+
return { ...properties(root), files: children(root, "file").map(edit) };
|
|
1635
|
+
case "rename":
|
|
1636
|
+
return rename(root);
|
|
1637
|
+
default:
|
|
1638
|
+
throw new Error(`unsupported readseek KDL root: ${root.name}`);
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
// ../readseek-api/src/process.ts
|
|
1642
|
+
var DEFAULT_READSEEK_TIMEOUT_MS = 120000;
|
|
1643
|
+
var DEFAULT_MAX_OUTPUT_BYTES = 32 * 1024 * 1024;
|
|
1644
|
+
function readSeekProcessEnvironment() {
|
|
1645
|
+
return process.platform === "linux" ? { ...process.env, APPIMAGE_EXTRACT_AND_RUN: "1" } : process.env;
|
|
1646
|
+
}
|
|
1647
|
+
function resolveProcessLimits(options = {}) {
|
|
1648
|
+
return {
|
|
1649
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_READSEEK_TIMEOUT_MS,
|
|
1650
|
+
maxOutputBytes: options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES,
|
|
1651
|
+
cancelable: options.cancelable !== false
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
function readSeekCancellationError(signal) {
|
|
1655
|
+
const reason = signal?.reason;
|
|
1656
|
+
return reason instanceof Error && reason.name !== "AbortError" ? reason : new Error("readseek cancelled");
|
|
1657
|
+
}
|
|
1658
|
+
function failureFromProcessResult(result, options = {}) {
|
|
1659
|
+
if (options.overflow) {
|
|
1660
|
+
return new Error(`readseek ${options.overflow} exceeded the ${options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES}-byte output limit`);
|
|
1661
|
+
}
|
|
1662
|
+
if (options.timedOut) {
|
|
1663
|
+
return new Error(`readseek timed out after ${options.timeoutMs ?? DEFAULT_READSEEK_TIMEOUT_MS} ms`);
|
|
1664
|
+
}
|
|
1665
|
+
const cleaned = cleanReadSeekErrorText(result.stderr);
|
|
1666
|
+
if (cleaned)
|
|
1667
|
+
return new Error(cleaned);
|
|
1668
|
+
if (result.signal)
|
|
1669
|
+
return new Error(`readseek killed by signal ${result.signal}`);
|
|
1670
|
+
return new Error(`readseek exited with status ${result.exitCode}`);
|
|
1671
|
+
}
|
|
1672
|
+
function acceptsReadSeekExitCode(args, exitCode) {
|
|
1673
|
+
if (exitCode === 0)
|
|
1674
|
+
return true;
|
|
1675
|
+
if (exitCode !== 1)
|
|
1676
|
+
return false;
|
|
1677
|
+
let index = 0;
|
|
1678
|
+
const valued = new Set(["-C", "--directory", "--output", "--readseek-dir"]);
|
|
1679
|
+
while (index < args.length) {
|
|
1680
|
+
const arg = args[index];
|
|
1681
|
+
if (valued.has(arg)) {
|
|
1682
|
+
index += 2;
|
|
1683
|
+
continue;
|
|
1684
|
+
}
|
|
1685
|
+
if ([...valued].some((flag) => arg.startsWith(`${flag}=`)) || arg === "--kdl") {
|
|
1686
|
+
index++;
|
|
1687
|
+
continue;
|
|
1688
|
+
}
|
|
1689
|
+
return arg === "find" || arg === "check";
|
|
1690
|
+
}
|
|
1691
|
+
return false;
|
|
1692
|
+
}
|
|
1693
|
+
function ensureSuccessfulStdout(result, args = []) {
|
|
1694
|
+
if (!result.signal && acceptsReadSeekExitCode(args, result.exitCode))
|
|
1695
|
+
return result.stdout;
|
|
1696
|
+
throw failureFromProcessResult(result);
|
|
1697
|
+
}
|
|
1698
|
+
function parseSuccessfulKdl(result, args = []) {
|
|
1699
|
+
return parseReadSeekKdl(ensureSuccessfulStdout(result, args));
|
|
1700
|
+
}
|
|
1701
|
+
// ../readseek-api/src/process-bun.ts
|
|
1702
|
+
function getBunSpawn() {
|
|
1703
|
+
const bunGlobal = globalThis;
|
|
1704
|
+
const spawn = bunGlobal.Bun?.spawn;
|
|
1705
|
+
if (typeof spawn !== "function") {
|
|
1706
|
+
throw new Error("Bun.spawn is unavailable in this runtime");
|
|
1707
|
+
}
|
|
1708
|
+
return spawn;
|
|
1709
|
+
}
|
|
1710
|
+
async function readBoundedText(stream, streamName, maxOutputBytes, onOverflow) {
|
|
1711
|
+
const reader = stream.getReader();
|
|
1712
|
+
const chunks = [];
|
|
1713
|
+
let totalBytes = 0;
|
|
1714
|
+
try {
|
|
1715
|
+
while (true) {
|
|
1716
|
+
const { done, value } = await reader.read();
|
|
1717
|
+
if (done)
|
|
1718
|
+
break;
|
|
1719
|
+
totalBytes += value.byteLength;
|
|
1720
|
+
if (totalBytes > maxOutputBytes) {
|
|
1721
|
+
onOverflow();
|
|
1722
|
+
await reader.cancel().catch(() => {
|
|
1723
|
+
return;
|
|
1724
|
+
});
|
|
1725
|
+
throw failureFromProcessResult({ stdout: "", stderr: "", exitCode: null, signal: null }, { overflow: streamName, maxOutputBytes });
|
|
1726
|
+
}
|
|
1727
|
+
chunks.push(value);
|
|
1728
|
+
}
|
|
1729
|
+
} finally {
|
|
1730
|
+
reader.releaseLock();
|
|
1731
|
+
}
|
|
1732
|
+
const bytes = new Uint8Array(totalBytes);
|
|
1733
|
+
let offset = 0;
|
|
1734
|
+
for (const chunk of chunks) {
|
|
1735
|
+
bytes.set(chunk, offset);
|
|
1736
|
+
offset += chunk.byteLength;
|
|
1737
|
+
}
|
|
1738
|
+
return new TextDecoder().decode(bytes);
|
|
1739
|
+
}
|
|
1740
|
+
async function runReadSeekBun(binaryPath, args, options = {}) {
|
|
1741
|
+
const limits = resolveProcessLimits(options);
|
|
1742
|
+
if (limits.cancelable && options.signal?.aborted) {
|
|
1743
|
+
throw readSeekCancellationError(options.signal);
|
|
1744
|
+
}
|
|
1745
|
+
const spawn = getBunSpawn();
|
|
1746
|
+
const stdin = options.stdin === undefined ? undefined : new TextEncoder().encode(options.stdin);
|
|
1747
|
+
const spawnOptions = {
|
|
1748
|
+
cwd: options.cwd,
|
|
1749
|
+
env: readSeekProcessEnvironment(),
|
|
1750
|
+
stderr: "pipe",
|
|
1751
|
+
stdin,
|
|
1752
|
+
stdout: "pipe"
|
|
1753
|
+
};
|
|
1754
|
+
const child = spawn([binaryPath, ...args], limits.cancelable && options.signal ? { ...spawnOptions, killSignal: "SIGKILL", signal: options.signal } : spawnOptions);
|
|
1755
|
+
const killForOverflow = () => {
|
|
1756
|
+
try {
|
|
1757
|
+
child.kill("SIGKILL");
|
|
1758
|
+
} catch {}
|
|
1759
|
+
};
|
|
1760
|
+
let timeout;
|
|
1761
|
+
const timeoutResult = new Promise((_resolve, reject) => {
|
|
1762
|
+
timeout = setTimeout(() => {
|
|
1763
|
+
try {
|
|
1764
|
+
child.kill("SIGKILL");
|
|
1765
|
+
} catch {}
|
|
1766
|
+
reject(failureFromProcessResult({ stdout: "", stderr: "", exitCode: null, signal: null }, { timedOut: true, timeoutMs: limits.timeoutMs }));
|
|
1767
|
+
}, limits.timeoutMs);
|
|
1768
|
+
timeout.unref?.();
|
|
1769
|
+
});
|
|
1770
|
+
const pending = Promise.all([
|
|
1771
|
+
readBoundedText(child.stdout, "stdout", limits.maxOutputBytes, killForOverflow),
|
|
1772
|
+
readBoundedText(child.stderr, "stderr", limits.maxOutputBytes, killForOverflow),
|
|
1773
|
+
child.exited
|
|
1774
|
+
]);
|
|
1775
|
+
try {
|
|
1776
|
+
const [stdout, stderr, exitCode] = await Promise.race([pending, timeoutResult]);
|
|
1777
|
+
if (limits.cancelable && options.signal?.aborted)
|
|
1778
|
+
throw readSeekCancellationError(options.signal);
|
|
1779
|
+
return { stdout, stderr, exitCode, signal: null };
|
|
1780
|
+
} finally {
|
|
1781
|
+
if (timeout !== undefined)
|
|
1782
|
+
clearTimeout(timeout);
|
|
1783
|
+
pending.catch(() => {
|
|
1784
|
+
return;
|
|
1785
|
+
});
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
async function runReadSeekBunText(binaryPath, args, options = {}) {
|
|
1789
|
+
return ensureSuccessfulStdout(await runReadSeekBun(binaryPath, args, options), args);
|
|
1790
|
+
}
|
|
1791
|
+
async function runReadSeekBunKdl(binaryPath, args, options = {}) {
|
|
1792
|
+
return parseSuccessfulKdl(await runReadSeekBun(binaryPath, args, options), args);
|
|
1436
1793
|
}
|
|
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
|
-
});
|
|
1636
1794
|
// src/util.ts
|
|
1637
|
-
import
|
|
1795
|
+
import path4 from "node:path";
|
|
1638
1796
|
function render(value) {
|
|
1639
1797
|
return JSON.stringify(value, null, 2);
|
|
1640
1798
|
}
|
|
1641
|
-
function
|
|
1799
|
+
function record2(value) {
|
|
1642
1800
|
return value && typeof value === "object" ? value : {};
|
|
1643
1801
|
}
|
|
1644
1802
|
function items(value) {
|
|
@@ -1663,21 +1821,21 @@ function collectAnchoredFiles(value, files) {
|
|
|
1663
1821
|
return;
|
|
1664
1822
|
const output = value;
|
|
1665
1823
|
const hashlines = output.hashlines;
|
|
1666
|
-
if (typeof output.file === "string" && Array.isArray(hashlines) && hashlines.some((line) => typeof
|
|
1824
|
+
if (typeof output.file === "string" && Array.isArray(hashlines) && hashlines.some((line) => typeof record2(line).line === "number" && typeof record2(line).hash === "string"))
|
|
1667
1825
|
files.add(output.file);
|
|
1668
1826
|
for (const item of Object.values(output))
|
|
1669
1827
|
collectAnchoredFiles(item, files);
|
|
1670
1828
|
}
|
|
1671
1829
|
function renameFiles(value, directory = process.cwd()) {
|
|
1672
|
-
const output =
|
|
1830
|
+
const output = record2(value);
|
|
1673
1831
|
const files = [];
|
|
1674
1832
|
if (typeof output.file === "string" && items(output.edits).length > 0) {
|
|
1675
|
-
files.push(
|
|
1833
|
+
files.push(path4.resolve(directory, output.file));
|
|
1676
1834
|
}
|
|
1677
1835
|
for (const other of items(output.others)) {
|
|
1678
|
-
const item =
|
|
1836
|
+
const item = record2(other);
|
|
1679
1837
|
if (typeof item.file === "string" && items(item.edits).length > 0) {
|
|
1680
|
-
files.push(
|
|
1838
|
+
files.push(path4.resolve(directory, item.file));
|
|
1681
1839
|
}
|
|
1682
1840
|
}
|
|
1683
1841
|
return files;
|
|
@@ -1728,21 +1886,18 @@ function resolveImagePolicy(options) {
|
|
|
1728
1886
|
return value;
|
|
1729
1887
|
throw new Error('opencode-readseek imageMode must be "on", "auto", or "off"');
|
|
1730
1888
|
}
|
|
1731
|
-
function
|
|
1732
|
-
return
|
|
1889
|
+
function contentArgs(filePath, options = {}) {
|
|
1890
|
+
return buildShowContentArgs({ path: filePath, ...options, at: options.at ?? (options.end === undefined ? undefined : "line:1") });
|
|
1733
1891
|
}
|
|
1734
|
-
function
|
|
1735
|
-
const value =
|
|
1892
|
+
function requireContent(envelope) {
|
|
1893
|
+
const value = record2(envelope).content;
|
|
1736
1894
|
if (value === undefined || value === null || typeof value !== "object") {
|
|
1737
|
-
throw new Error(
|
|
1895
|
+
throw new Error("show content returned no source content");
|
|
1738
1896
|
}
|
|
1739
1897
|
return value;
|
|
1740
1898
|
}
|
|
1741
1899
|
function readRangeArgs(filePath, start, end) {
|
|
1742
|
-
return
|
|
1743
|
-
selector: start > 1 ? `line:${start}` : undefined,
|
|
1744
|
-
end
|
|
1745
|
-
});
|
|
1900
|
+
return contentArgs(filePath, { at: `line:${start}`, end });
|
|
1746
1901
|
}
|
|
1747
1902
|
function readSeekBinaryPath() {
|
|
1748
1903
|
return resolveReadSeekBinaryOverride() ?? resolveReadSeekBinaryFromPackages(resolveFromHost);
|
|
@@ -1779,12 +1934,12 @@ async function runReadSeek(context, args, options = {}) {
|
|
|
1779
1934
|
timeoutMs: options.timeoutMs
|
|
1780
1935
|
}));
|
|
1781
1936
|
}
|
|
1782
|
-
async function
|
|
1783
|
-
return
|
|
1937
|
+
async function runContent(context, args, options = {}) {
|
|
1938
|
+
return requireContent(await runReadSeek(context, args, options));
|
|
1784
1939
|
}
|
|
1785
1940
|
async function postMutationPreview(context, args, filePath, changed) {
|
|
1786
1941
|
try {
|
|
1787
|
-
return await
|
|
1942
|
+
return await runContent(context, args, {
|
|
1788
1943
|
cancelable: false,
|
|
1789
1944
|
timeoutMs: DEFAULT_READSEEK_TIMEOUT_MS
|
|
1790
1945
|
});
|
|
@@ -1813,7 +1968,7 @@ async function ensureReadSeekCache(context) {
|
|
|
1813
1968
|
if (error.code !== "ENOENT")
|
|
1814
1969
|
throw error;
|
|
1815
1970
|
}
|
|
1816
|
-
readSeekCacheInit ??= runReadSeekRaw(context, ["--readseek-dir", cacheDir, "
|
|
1971
|
+
readSeekCacheInit ??= runReadSeekRaw(context, ["--readseek-dir", cacheDir, "initialize", "index"], { cancelable: false }).then(() => cacheDir).finally(() => {
|
|
1817
1972
|
readSeekCacheInit = undefined;
|
|
1818
1973
|
});
|
|
1819
1974
|
return readSeekCacheInit;
|
|
@@ -1833,7 +1988,7 @@ function openCodeToolDescription(name) {
|
|
|
1833
1988
|
}
|
|
1834
1989
|
|
|
1835
1990
|
// src/session-anchors.ts
|
|
1836
|
-
import
|
|
1991
|
+
import path5 from "node:path";
|
|
1837
1992
|
|
|
1838
1993
|
class SessionAnchors {
|
|
1839
1994
|
#pathsBySession = new Map;
|
|
@@ -1847,7 +2002,7 @@ class SessionAnchors {
|
|
|
1847
2002
|
paths.add(filePath);
|
|
1848
2003
|
}
|
|
1849
2004
|
forget(filePath) {
|
|
1850
|
-
const absolutePath =
|
|
2005
|
+
const absolutePath = path5.resolve(filePath);
|
|
1851
2006
|
for (const paths of this.#pathsBySession.values())
|
|
1852
2007
|
paths.delete(absolutePath);
|
|
1853
2008
|
for (const [sessionID, plan] of this.#renamePlans) {
|
|
@@ -1870,14 +2025,14 @@ class SessionAnchors {
|
|
|
1870
2025
|
const { file, old_name: oldName, new_name: newName, others } = record;
|
|
1871
2026
|
if (typeof file !== "string" || typeof oldName !== "string" || typeof newName !== "string")
|
|
1872
2027
|
return;
|
|
1873
|
-
const files = new Set([
|
|
2028
|
+
const files = new Set([path5.resolve(file)]);
|
|
1874
2029
|
if (Array.isArray(others)) {
|
|
1875
2030
|
for (const item of others) {
|
|
1876
2031
|
if (!item || typeof item !== "object")
|
|
1877
2032
|
continue;
|
|
1878
2033
|
const otherFile = item.file;
|
|
1879
2034
|
if (typeof otherFile === "string")
|
|
1880
|
-
files.add(
|
|
2035
|
+
files.add(path5.resolve(otherFile));
|
|
1881
2036
|
}
|
|
1882
2037
|
}
|
|
1883
2038
|
this.#renamePlans.set(sessionID, { summary: `${oldName} -> ${newName}`, files });
|
|
@@ -1903,22 +2058,21 @@ ${[...paths].sort().map((filePath) => `- ${filePath}`).join(`
|
|
|
1903
2058
|
|
|
1904
2059
|
// src/tools.ts
|
|
1905
2060
|
import { readFile as readFile2, stat as stat3 } from "node:fs/promises";
|
|
1906
|
-
import { tool as
|
|
2061
|
+
import { tool as tool3 } from "@opencode-ai/plugin";
|
|
1907
2062
|
|
|
1908
2063
|
// src/constants.ts
|
|
1909
2064
|
var DEFAULT_VISION_TIMEOUT_MS = 30 * 60 * 1000;
|
|
1910
2065
|
var DEFAULT_READ_LIMIT = 2000;
|
|
1911
2066
|
var EDIT_RESULT_READ_LIMIT = 40;
|
|
1912
2067
|
var MAX_PERMISSION_DIFF_BYTES = 32 * 1024;
|
|
1913
|
-
var IMAGE_MODES = [...VISION_MODES];
|
|
1914
2068
|
var VISION_LEVEL_VALUES = [...VISION_LEVELS];
|
|
1915
2069
|
|
|
1916
2070
|
// src/edits.ts
|
|
1917
2071
|
import { lstat, mkdir, open, readFile } from "node:fs/promises";
|
|
1918
|
-
import
|
|
2072
|
+
import path6 from "node:path";
|
|
1919
2073
|
var fileMutationTails = new Map;
|
|
1920
2074
|
async function withFileMutationQueue(filePath, operation) {
|
|
1921
|
-
const key =
|
|
2075
|
+
const key = path6.resolve(filePath);
|
|
1922
2076
|
const previous = fileMutationTails.get(key);
|
|
1923
2077
|
let release = () => {};
|
|
1924
2078
|
const current = new Promise((resolve) => {
|
|
@@ -1973,7 +2127,7 @@ async function writeHandle(handle, content) {
|
|
|
1973
2127
|
}
|
|
1974
2128
|
}
|
|
1975
2129
|
async function writeText(filePath, expected, content) {
|
|
1976
|
-
await mkdir(
|
|
2130
|
+
await mkdir(path6.dirname(filePath), { recursive: true });
|
|
1977
2131
|
if (!expected.exists) {
|
|
1978
2132
|
let handle;
|
|
1979
2133
|
try {
|
|
@@ -2105,20 +2259,20 @@ function simpleDiff(filePath, before, after) {
|
|
|
2105
2259
|
|
|
2106
2260
|
// src/paths.ts
|
|
2107
2261
|
import { lstat as lstat2, readlink, realpath, stat as stat2 } from "node:fs/promises";
|
|
2108
|
-
import
|
|
2262
|
+
import path7 from "node:path";
|
|
2109
2263
|
function resolvePath(directory, filePath) {
|
|
2110
|
-
return
|
|
2264
|
+
return path7.resolve(directory, filePath);
|
|
2111
2265
|
}
|
|
2112
2266
|
async function resolveSymlinks(filePath) {
|
|
2113
|
-
let unresolved =
|
|
2267
|
+
let unresolved = path7.resolve(filePath);
|
|
2114
2268
|
let symlinks = 0;
|
|
2115
2269
|
while (true) {
|
|
2116
|
-
const parsed =
|
|
2117
|
-
const parts = unresolved.slice(parsed.root.length).split(
|
|
2270
|
+
const parsed = path7.parse(unresolved);
|
|
2271
|
+
const parts = unresolved.slice(parsed.root.length).split(path7.sep).filter(Boolean);
|
|
2118
2272
|
let resolved = parsed.root;
|
|
2119
2273
|
let restart = false;
|
|
2120
2274
|
for (let index = 0;index < parts.length; index++) {
|
|
2121
|
-
const candidate =
|
|
2275
|
+
const candidate = path7.join(resolved, parts[index]);
|
|
2122
2276
|
const info = await lstat2(candidate);
|
|
2123
2277
|
if (!info.isSymbolicLink()) {
|
|
2124
2278
|
resolved = candidate;
|
|
@@ -2131,7 +2285,7 @@ async function resolveSymlinks(filePath) {
|
|
|
2131
2285
|
throw error;
|
|
2132
2286
|
}
|
|
2133
2287
|
const target = await readlink(candidate);
|
|
2134
|
-
unresolved =
|
|
2288
|
+
unresolved = path7.resolve(path7.dirname(candidate), target, ...parts.slice(index + 1));
|
|
2135
2289
|
restart = true;
|
|
2136
2290
|
break;
|
|
2137
2291
|
}
|
|
@@ -2153,14 +2307,14 @@ async function canonicalPath(filePath) {
|
|
|
2153
2307
|
let existing = filePath;
|
|
2154
2308
|
while (true) {
|
|
2155
2309
|
try {
|
|
2156
|
-
return
|
|
2310
|
+
return path7.join(await portableRealpath(existing), ...missing.reverse());
|
|
2157
2311
|
} catch (error) {
|
|
2158
2312
|
if (error.code !== "ENOENT")
|
|
2159
2313
|
throw error;
|
|
2160
|
-
const parent =
|
|
2314
|
+
const parent = path7.dirname(existing);
|
|
2161
2315
|
if (parent === existing)
|
|
2162
2316
|
throw error;
|
|
2163
|
-
missing.push(
|
|
2317
|
+
missing.push(path7.basename(existing));
|
|
2164
2318
|
existing = parent;
|
|
2165
2319
|
}
|
|
2166
2320
|
}
|
|
@@ -2177,8 +2331,8 @@ async function authorizeExternal(context, filePath) {
|
|
|
2177
2331
|
const info = await stat2(accessPath).catch(() => {
|
|
2178
2332
|
return;
|
|
2179
2333
|
});
|
|
2180
|
-
const parentDir = info?.isDirectory() ? accessPath :
|
|
2181
|
-
const pattern =
|
|
2334
|
+
const parentDir = info?.isDirectory() ? accessPath : path7.dirname(accessPath);
|
|
2335
|
+
const pattern = path7.join(parentDir, "*").replaceAll("\\", "/");
|
|
2182
2336
|
await context.ask({
|
|
2183
2337
|
permission: "external_directory",
|
|
2184
2338
|
patterns: [pattern],
|
|
@@ -2199,7 +2353,7 @@ async function authorizeRead(context, filePath) {
|
|
|
2199
2353
|
await authorizeExternal(context, filePath);
|
|
2200
2354
|
await context.ask({
|
|
2201
2355
|
permission: "read",
|
|
2202
|
-
patterns: [
|
|
2356
|
+
patterns: [path7.relative(context.worktree, filePath).replaceAll("\\", "/")],
|
|
2203
2357
|
always: ["*"],
|
|
2204
2358
|
metadata: {}
|
|
2205
2359
|
});
|
|
@@ -2218,7 +2372,7 @@ async function authorizeEdit(context, filePaths, diff = "", authorizeExternalPat
|
|
|
2218
2372
|
for (const filePath of filePaths)
|
|
2219
2373
|
await authorizeExternal(context, filePath);
|
|
2220
2374
|
}
|
|
2221
|
-
const patterns = filePaths.map((filePath) =>
|
|
2375
|
+
const patterns = filePaths.map((filePath) => path7.relative(context.worktree, filePath).replaceAll("\\", "/"));
|
|
2222
2376
|
await context.ask({
|
|
2223
2377
|
permission: "edit",
|
|
2224
2378
|
patterns,
|
|
@@ -2228,109 +2382,92 @@ async function authorizeEdit(context, filePaths, diff = "", authorizeExternalPat
|
|
|
2228
2382
|
}
|
|
2229
2383
|
|
|
2230
2384
|
// src/presentation.ts
|
|
2231
|
-
import {
|
|
2385
|
+
import { pathToFileURL } from "node:url";
|
|
2386
|
+
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
2232
2387
|
function initialTitle(kind, args) {
|
|
2233
2388
|
switch (kind) {
|
|
2234
|
-
case "
|
|
2235
|
-
|
|
2236
|
-
case "
|
|
2237
|
-
|
|
2238
|
-
case "
|
|
2239
|
-
|
|
2240
|
-
case "
|
|
2241
|
-
return
|
|
2242
|
-
case "
|
|
2389
|
+
case "show_structure":
|
|
2390
|
+
case "show_metadata":
|
|
2391
|
+
case "check_syntax":
|
|
2392
|
+
case "check_rules":
|
|
2393
|
+
case "generate_caption":
|
|
2394
|
+
case "detect_objects":
|
|
2395
|
+
case "recognize_text":
|
|
2396
|
+
return `${kind.replaceAll("_", " ")} ${args.path}`;
|
|
2397
|
+
case "show_content":
|
|
2398
|
+
return `Show content ${args.path}`;
|
|
2399
|
+
case "show_assets":
|
|
2400
|
+
return `Show assets ${args.path}`;
|
|
2401
|
+
case "find_nodes":
|
|
2402
|
+
return `Find nodes ${args.pattern ?? args.context ?? args.rule}`;
|
|
2403
|
+
case "find_text":
|
|
2404
|
+
return `Find text ${args.pattern ?? args.text}`;
|
|
2405
|
+
case "find_definitions":
|
|
2243
2406
|
return `Find definition ${args.name}`;
|
|
2244
|
-
case "
|
|
2407
|
+
case "find_references":
|
|
2245
2408
|
return `Find references to ${args.name}`;
|
|
2246
|
-
case "
|
|
2247
|
-
return `Rename ${args.path}:${args.
|
|
2248
|
-
case "
|
|
2409
|
+
case "rename_symbol":
|
|
2410
|
+
return `Rename ${args.path}:${args.at} to ${args.to}`;
|
|
2411
|
+
case "edit_text":
|
|
2249
2412
|
return `Edit ${args.path}`;
|
|
2250
|
-
case "
|
|
2413
|
+
case "write_file":
|
|
2251
2414
|
return `Write ${args.path}`;
|
|
2252
2415
|
}
|
|
2253
2416
|
}
|
|
2254
2417
|
function resultPresentation(kind, args, value) {
|
|
2255
|
-
const output =
|
|
2418
|
+
const output = record2(value);
|
|
2419
|
+
if (isShowTool(kind)) {
|
|
2420
|
+
const presentation = presentShowResult(kind, value, args);
|
|
2421
|
+
return { title: presentation.summary, metadata: presentation.metadata };
|
|
2422
|
+
}
|
|
2256
2423
|
switch (kind) {
|
|
2257
|
-
case "
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
const
|
|
2261
|
-
|
|
2262
|
-
const identity = record(output.identity);
|
|
2263
|
-
const content = record(output.content);
|
|
2264
|
-
const metadata = {};
|
|
2265
|
-
const summary = [];
|
|
2266
|
-
if (output.map !== undefined) {
|
|
2267
|
-
const symbols = items(map.symbols).length;
|
|
2268
|
-
metadata.symbols = symbols;
|
|
2269
|
-
summary.push(`${symbols} symbols`);
|
|
2270
|
-
}
|
|
2271
|
-
if (output.diagnostics !== undefined) {
|
|
2272
|
-
const errors = typeof diagnostics.error_count === "number" ? diagnostics.error_count : 0;
|
|
2273
|
-
const missing = typeof diagnostics.missing_count === "number" ? diagnostics.missing_count : 0;
|
|
2274
|
-
metadata.error_count = errors;
|
|
2275
|
-
metadata.missing_count = missing;
|
|
2276
|
-
summary.push(`${errors} errors, ${missing} missing`);
|
|
2277
|
-
}
|
|
2278
|
-
if (output.identity !== undefined) {
|
|
2279
|
-
if (typeof identity.line === "number")
|
|
2280
|
-
metadata.line = identity.line;
|
|
2281
|
-
if (typeof identity.column === "number")
|
|
2282
|
-
metadata.column = identity.column;
|
|
2283
|
-
const identifier = record(identity.identifier).text;
|
|
2284
|
-
if (typeof identifier === "string")
|
|
2285
|
-
summary.push(identifier);
|
|
2286
|
-
}
|
|
2287
|
-
if (typeof content.start_line === "number")
|
|
2288
|
-
metadata.start_line = content.start_line;
|
|
2289
|
-
if (typeof content.end_line === "number")
|
|
2290
|
-
metadata.end_line = content.end_line;
|
|
2291
|
-
return {
|
|
2292
|
-
title: summary.length > 0 ? `Inspected ${args.path} (${summary.join(", ")})` : `Inspected ${args.path}`,
|
|
2293
|
-
metadata
|
|
2294
|
-
};
|
|
2424
|
+
case "check_syntax": {
|
|
2425
|
+
const diagnostics = record2(output.diagnostics ?? output);
|
|
2426
|
+
const errors = typeof diagnostics.error_count === "number" ? diagnostics.error_count : 0;
|
|
2427
|
+
const missing = typeof diagnostics.missing_count === "number" ? diagnostics.missing_count : 0;
|
|
2428
|
+
return { title: `Syntax ${args.path} (${errors} errors, ${missing} missing)`, metadata: { error_count: errors, missing_count: missing } };
|
|
2295
2429
|
}
|
|
2296
|
-
case "
|
|
2430
|
+
case "find_nodes": {
|
|
2297
2431
|
const results = items(output.results);
|
|
2298
|
-
const matches = results.reduce((total, result) => total + items(
|
|
2432
|
+
const matches = results.reduce((total, result) => total + items(record2(result).matches).length, 0);
|
|
2299
2433
|
return { title: `Found ${matches} matches`, metadata: { results: results.length, matches } };
|
|
2300
2434
|
}
|
|
2301
|
-
case "
|
|
2435
|
+
case "find_text": {
|
|
2302
2436
|
const results = items(output.results);
|
|
2303
|
-
const matches = results.reduce((total, result) =>
|
|
2437
|
+
const matches = results.reduce((total, result) => {
|
|
2438
|
+
const entry = record2(result);
|
|
2439
|
+
return total + (typeof entry.count === "number" ? entry.count : items(entry.matches).length);
|
|
2440
|
+
}, 0);
|
|
2304
2441
|
return { title: `Found ${matches} matches`, metadata: { results: results.length, matches } };
|
|
2305
2442
|
}
|
|
2306
|
-
case "
|
|
2443
|
+
case "find_definitions": {
|
|
2307
2444
|
const locations = items(output.locations).length;
|
|
2308
2445
|
return { title: `Found ${locations} definitions`, metadata: { locations } };
|
|
2309
2446
|
}
|
|
2310
|
-
case "
|
|
2447
|
+
case "find_references": {
|
|
2311
2448
|
const references = items(output.references).length;
|
|
2312
2449
|
return { title: `Found ${references} references`, metadata: { references } };
|
|
2313
2450
|
}
|
|
2314
|
-
case "
|
|
2451
|
+
case "rename_symbol": {
|
|
2315
2452
|
const oldName = output.old_name;
|
|
2316
2453
|
const newName = output.new_name;
|
|
2317
|
-
const otherOutputs = items(output.others).map(
|
|
2454
|
+
const otherOutputs = items(output.others).map(record2);
|
|
2318
2455
|
const edits = otherOutputs.reduce((total, item) => total + items(item.edits).length, items(output.edits).length);
|
|
2319
2456
|
const conflicts = otherOutputs.reduce((total, item) => total + items(item.conflicts).length, items(output.conflicts).length);
|
|
2320
2457
|
const others = otherOutputs.length;
|
|
2321
2458
|
const title = typeof oldName === "string" && typeof newName === "string" ? `${output.applied === true ? "Renamed" : "Plan"} ${oldName} -> ${newName}` : initialTitle(kind, args);
|
|
2322
2459
|
return { title, metadata: { edits, conflicts, others } };
|
|
2323
2460
|
}
|
|
2324
|
-
case "
|
|
2325
|
-
case "
|
|
2461
|
+
case "edit_text":
|
|
2462
|
+
case "write_file": {
|
|
2326
2463
|
const preview = typeof output.preview_error === "string" ? " (preview unavailable)" : "";
|
|
2327
|
-
return { title: `${kind === "
|
|
2464
|
+
return { title: `${kind === "edit_text" ? "Edited" : "Wrote"} ${args.path}${preview}`, metadata: {} };
|
|
2328
2465
|
}
|
|
2329
2466
|
}
|
|
2330
2467
|
return { title: initialTitle(kind, args), metadata: {} };
|
|
2331
2468
|
}
|
|
2332
|
-
function readseekTool(description, args, kind, execute) {
|
|
2333
|
-
return
|
|
2469
|
+
function readseekTool(description, args, kind, execute, imagePolicy) {
|
|
2470
|
+
return tool2({
|
|
2334
2471
|
description,
|
|
2335
2472
|
args,
|
|
2336
2473
|
async execute(args, context) {
|
|
@@ -2338,10 +2475,29 @@ function readseekTool(description, args, kind, execute) {
|
|
|
2338
2475
|
context.metadata({ title });
|
|
2339
2476
|
const result = await execute(args, context);
|
|
2340
2477
|
const presentation = resultPresentation(kind, args, result);
|
|
2478
|
+
const attachments = [];
|
|
2479
|
+
const imagePaths = new Map;
|
|
2480
|
+
const visit = (value) => {
|
|
2481
|
+
if (!value || typeof value !== "object")
|
|
2482
|
+
return;
|
|
2483
|
+
const item = value;
|
|
2484
|
+
if (typeof item.path === "string" && typeof item.mime === "string" && item.mime.startsWith("image/"))
|
|
2485
|
+
imagePaths.set(item.path, item.mime);
|
|
2486
|
+
for (const child of Object.values(item))
|
|
2487
|
+
visit(child);
|
|
2488
|
+
};
|
|
2489
|
+
if (imagePolicy !== undefined)
|
|
2490
|
+
visit(result);
|
|
2491
|
+
for (const [path, mime] of imagePaths) {
|
|
2492
|
+
if (imagePolicy !== "off")
|
|
2493
|
+
attachments.push({ type: "file", mime, url: pathToFileURL(path).href });
|
|
2494
|
+
}
|
|
2495
|
+
const displayResult = imagePolicy === "off" && imagePaths.size > 0 ? { ...record2(result), warning: "Image attachments are disabled; use the returned paths to view the images." } : result;
|
|
2341
2496
|
return {
|
|
2497
|
+
...attachments.length > 0 ? { attachments } : {},
|
|
2342
2498
|
title: presentation.title,
|
|
2343
|
-
output: render(
|
|
2344
|
-
metadata: presentation.metadata
|
|
2499
|
+
output: isShowTool(kind) ? formatShowResult(presentShowResult(kind, displayResult, args)) : render(displayResult),
|
|
2500
|
+
metadata: { ...presentation.metadata, ...isShowTool(kind) ? { readseek: result } : {} }
|
|
2345
2501
|
};
|
|
2346
2502
|
}
|
|
2347
2503
|
});
|
|
@@ -2350,88 +2506,30 @@ function readseekTool(description, args, kind, execute) {
|
|
|
2350
2506
|
// src/tools.ts
|
|
2351
2507
|
function createReadSeekTools(imagePolicy) {
|
|
2352
2508
|
return {
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
}, "
|
|
2365
|
-
const filePath = resolvePath(context.directory, input.path);
|
|
2366
|
-
await authorizeRead(context, filePath);
|
|
2367
|
-
const select = (input.select ?? "content").trim() || "content";
|
|
2368
|
-
const visionMode = input.visionMode;
|
|
2369
|
-
const visionLevel = input.visionLevel;
|
|
2370
|
-
const vision = validateVisionLevelVsMode({ visionMode, visionLevel });
|
|
2371
|
-
if (!vision.ok)
|
|
2372
|
-
throw new Error(vision.error);
|
|
2373
|
-
const cacheDir = await ensureReadSeekCache(context);
|
|
2374
|
-
const args = buildInspectArgs({
|
|
2375
|
-
path: filePath,
|
|
2376
|
-
selector: input.selector,
|
|
2377
|
-
select,
|
|
2378
|
-
query: input.query,
|
|
2379
|
-
end: input.end,
|
|
2380
|
-
limit: input.limit,
|
|
2381
|
-
depth: input.depth ?? (select.split(",").some((facet) => facet.trim() === "map") ? DEFAULT_INSPECT_MAP_DEPTH : undefined),
|
|
2382
|
-
language: input.language,
|
|
2383
|
-
visionMode,
|
|
2384
|
-
visionLevel,
|
|
2385
|
-
readseekDir: cacheDir
|
|
2386
|
-
});
|
|
2387
|
-
return runReadSeek(context, args, visionMode === undefined || visionMode === "none" ? {} : { timeoutMs: DEFAULT_VISION_TIMEOUT_MS });
|
|
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
|
-
}),
|
|
2420
|
-
readseek_grep: readseekTool(openCodeToolDescription("grep"), {
|
|
2421
|
-
pattern: tool2.schema.string().describe(PARAM_DESCRIPTIONS.grepPattern),
|
|
2422
|
-
path: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
2423
|
-
glob: tool2.schema.string().optional().describe(PARAM_DESCRIPTIONS.glob),
|
|
2424
|
-
literal: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.literal),
|
|
2425
|
-
ignoreCase: tool2.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.ignoreCase),
|
|
2426
|
-
context: tool2.schema.number().int().nonnegative().optional().describe(PARAM_DESCRIPTIONS.context),
|
|
2427
|
-
limit: tool2.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.matchLimit)
|
|
2428
|
-
}, "grep", async (input, context) => {
|
|
2509
|
+
...createAccessTools(imagePolicy),
|
|
2510
|
+
readseek_find_text: readseekTool(openCodeToolDescription("find_text"), {
|
|
2511
|
+
pattern: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.grepPattern),
|
|
2512
|
+
text: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.text),
|
|
2513
|
+
summary: tool3.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.summary),
|
|
2514
|
+
path: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
2515
|
+
glob: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.glob),
|
|
2516
|
+
literal: tool3.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.literal),
|
|
2517
|
+
ignoreCase: tool3.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.ignoreCase),
|
|
2518
|
+
context: tool3.schema.number().int().nonnegative().optional().describe(PARAM_DESCRIPTIONS.context),
|
|
2519
|
+
limit: tool3.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.matchLimit)
|
|
2520
|
+
}, "find_text", async (input, context) => {
|
|
2429
2521
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
2430
|
-
|
|
2522
|
+
if (input.pattern === undefined === (input.text === undefined)) {
|
|
2523
|
+
throw new Error("exactly one of pattern or text is required");
|
|
2524
|
+
}
|
|
2525
|
+
const pattern = input.pattern ?? input.text;
|
|
2431
2526
|
await authorizeSearch(context, target, pattern);
|
|
2527
|
+
if (input.text !== undefined && !input.glob && !input.ignoreCase && !input.context && !input.summary) {
|
|
2528
|
+
return runReadSeek(context, buildFindTextArgs({ path: target, text: pattern, limit: input.limit }));
|
|
2529
|
+
}
|
|
2432
2530
|
let expression;
|
|
2433
2531
|
try {
|
|
2434
|
-
const source = input.literal ? pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") : pattern;
|
|
2532
|
+
const source = input.literal || input.text !== undefined ? pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") : pattern;
|
|
2435
2533
|
expression = new RegExp(source, input.ignoreCase ? "i" : "");
|
|
2436
2534
|
} catch (error) {
|
|
2437
2535
|
throw new Error(`invalid regular expression: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -2479,6 +2577,10 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2479
2577
|
}
|
|
2480
2578
|
if (ranges.length === 0)
|
|
2481
2579
|
continue;
|
|
2580
|
+
if (input.summary) {
|
|
2581
|
+
results.push({ file: filePath, count: matchLines.size });
|
|
2582
|
+
continue;
|
|
2583
|
+
}
|
|
2482
2584
|
const mergedRanges = [];
|
|
2483
2585
|
for (const [start, end] of ranges) {
|
|
2484
2586
|
const previous = mergedRanges.at(-1);
|
|
@@ -2489,46 +2591,59 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2489
2591
|
}
|
|
2490
2592
|
const linesByNumber = new Map;
|
|
2491
2593
|
for (const [start, end] of mergedRanges) {
|
|
2492
|
-
const output =
|
|
2594
|
+
const output = record2(await runContent(context, readRangeArgs(filePath, start, end)));
|
|
2493
2595
|
for (const hashline of items(output.hashlines)) {
|
|
2494
|
-
const line =
|
|
2596
|
+
const line = record2(hashline).line;
|
|
2495
2597
|
if (typeof line === "number" && !linesByNumber.has(line))
|
|
2496
2598
|
linesByNumber.set(line, hashline);
|
|
2497
2599
|
}
|
|
2498
2600
|
}
|
|
2499
2601
|
const hashlines = [...linesByNumber.values()];
|
|
2500
2602
|
const matches = hashlines.filter((hashline) => {
|
|
2501
|
-
const line =
|
|
2603
|
+
const line = record2(hashline).line;
|
|
2502
2604
|
return typeof line === "number" && matchLines.has(line);
|
|
2503
2605
|
});
|
|
2504
2606
|
results.push({ file: filePath, matches, hashlines });
|
|
2505
2607
|
}
|
|
2506
2608
|
return { results, truncated };
|
|
2507
2609
|
}),
|
|
2508
|
-
|
|
2509
|
-
pattern:
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2610
|
+
readseek_find_nodes: readseekTool(openCodeToolDescription("find_nodes"), {
|
|
2611
|
+
pattern: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.pattern),
|
|
2612
|
+
context: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.patternContext),
|
|
2613
|
+
selector: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.selector),
|
|
2614
|
+
rule: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.rule),
|
|
2615
|
+
limit: tool3.schema.number().int().positive().optional(),
|
|
2616
|
+
path: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
2617
|
+
language: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
2618
|
+
gitScope: tool3.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
2619
|
+
}, "find_nodes", async (input, context) => {
|
|
2514
2620
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
2515
|
-
const args =
|
|
2621
|
+
const args = buildFindNodesArgs({
|
|
2516
2622
|
path: target,
|
|
2517
2623
|
pattern: input.pattern,
|
|
2624
|
+
context: input.context,
|
|
2625
|
+
selector: input.selector,
|
|
2626
|
+
rule: input.rule,
|
|
2627
|
+
limit: input.limit,
|
|
2518
2628
|
language: input.language,
|
|
2519
2629
|
gitScope: input.gitScope
|
|
2520
2630
|
});
|
|
2521
|
-
await authorizeSearch(context, target, input.pattern);
|
|
2631
|
+
await authorizeSearch(context, target, input.pattern ?? input.context ?? input.rule);
|
|
2632
|
+
if (input.rule?.startsWith("@")) {
|
|
2633
|
+
await authorizeRead(context, resolvePath(context.directory, input.rule.slice(1)));
|
|
2634
|
+
}
|
|
2635
|
+
if (input.rule === "-")
|
|
2636
|
+
throw new Error("rule stdin is unavailable in OpenCode tools; use inline KDL or @path");
|
|
2522
2637
|
return runReadSeek(context, args);
|
|
2523
2638
|
}),
|
|
2524
|
-
|
|
2525
|
-
name:
|
|
2526
|
-
path:
|
|
2527
|
-
language:
|
|
2528
|
-
gitScope:
|
|
2529
|
-
}, "
|
|
2639
|
+
readseek_find_definitions: readseekTool(openCodeToolDescription("find_definitions"), {
|
|
2640
|
+
name: tool3.schema.string().describe(PARAM_DESCRIPTIONS.name),
|
|
2641
|
+
path: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
2642
|
+
language: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
2643
|
+
gitScope: tool3.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
2644
|
+
}, "find_definitions", async (input, context) => {
|
|
2530
2645
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
2531
|
-
const args =
|
|
2646
|
+
const args = buildFindDefinitionsArgs({
|
|
2532
2647
|
path: target,
|
|
2533
2648
|
name: input.name,
|
|
2534
2649
|
language: input.language,
|
|
@@ -2537,117 +2652,119 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2537
2652
|
await authorizeSearch(context, target, input.name);
|
|
2538
2653
|
return runReadSeek(context, args);
|
|
2539
2654
|
}),
|
|
2540
|
-
|
|
2541
|
-
name:
|
|
2542
|
-
path:
|
|
2543
|
-
language:
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
gitScope: tool2.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
2548
|
-
}, "refs", async (input, context) => {
|
|
2655
|
+
readseek_find_references: readseekTool(openCodeToolDescription("find_references"), {
|
|
2656
|
+
name: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.refsName),
|
|
2657
|
+
path: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
2658
|
+
language: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
2659
|
+
at: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.at),
|
|
2660
|
+
gitScope: tool3.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
2661
|
+
}, "find_references", async (input, context) => {
|
|
2549
2662
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
2550
2663
|
const name = input.name;
|
|
2551
|
-
const args =
|
|
2664
|
+
const args = buildFindReferencesArgs({
|
|
2552
2665
|
path: target,
|
|
2553
2666
|
name,
|
|
2554
|
-
|
|
2555
|
-
line: input.line,
|
|
2556
|
-
column: input.column,
|
|
2667
|
+
at: input.at,
|
|
2557
2668
|
language: input.language,
|
|
2558
2669
|
gitScope: input.gitScope
|
|
2559
2670
|
});
|
|
2560
2671
|
await authorizeSearch(context, target, name ?? "*");
|
|
2561
2672
|
return runReadSeek(context, args);
|
|
2562
2673
|
}),
|
|
2563
|
-
|
|
2564
|
-
path:
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2674
|
+
readseek_rename_symbol: readseekTool(openCodeToolDescription("rename_symbol"), {
|
|
2675
|
+
path: tool3.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
2676
|
+
at: tool3.schema.string().describe(PARAM_DESCRIPTIONS.at),
|
|
2677
|
+
to: tool3.schema.string().min(1).describe(PARAM_DESCRIPTIONS.to),
|
|
2678
|
+
workspace: tool3.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.workspace),
|
|
2679
|
+
apply: tool3.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.applyDefaultTrue),
|
|
2680
|
+
planHash: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.planHash),
|
|
2681
|
+
language: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
2682
|
+
gitScope: tool3.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
2683
|
+
}, "rename_symbol", async (input, context) => {
|
|
2571
2684
|
const filePath = resolvePath(context.directory, input.path);
|
|
2572
2685
|
await authorizeRead(context, filePath);
|
|
2573
2686
|
if (input.workspace) {
|
|
2574
|
-
const
|
|
2575
|
-
const
|
|
2576
|
-
selector: `line:${input.line}${column === undefined ? "" : `:${column}`}`
|
|
2577
|
-
});
|
|
2578
|
-
const envelope = await runReadSeek(context, identifyArgs);
|
|
2579
|
-
const name = identifiedName(requireInspectFacet(envelope, "identity"));
|
|
2687
|
+
const envelope = await runReadSeek(context, contentArgs(filePath, { at: input.at }));
|
|
2688
|
+
const name = identifiedName(record2(envelope).cursor);
|
|
2580
2689
|
if (!name)
|
|
2581
2690
|
throw new Error("readseek could not identify a binding at the rename cursor");
|
|
2582
2691
|
await authorizeSearch(context, context.directory, name);
|
|
2583
2692
|
}
|
|
2584
|
-
const args =
|
|
2693
|
+
const args = buildRenameSymbolArgs({
|
|
2585
2694
|
path: filePath,
|
|
2586
2695
|
to: input.to,
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2696
|
+
at: input.at,
|
|
2697
|
+
workspace: input.workspace ? context.directory : undefined,
|
|
2698
|
+
language: input.language,
|
|
2699
|
+
gitScope: input.gitScope
|
|
2590
2700
|
});
|
|
2591
2701
|
const plan = await runReadSeek(context, args);
|
|
2702
|
+
if (input.planHash !== undefined && record2(plan).plan_hash !== input.planHash) {
|
|
2703
|
+
throw new Error("readseek rename plan does not match planHash");
|
|
2704
|
+
}
|
|
2592
2705
|
if (input.apply === false)
|
|
2593
2706
|
return plan;
|
|
2594
2707
|
const files = renameFiles(plan, context.directory);
|
|
2595
|
-
const planHash =
|
|
2708
|
+
const planHash = record2(plan).plan_hash;
|
|
2596
2709
|
if (typeof planHash !== "string" || planHash.length === 0) {
|
|
2597
2710
|
throw new Error("readseek rename plan did not include a plan hash");
|
|
2598
2711
|
}
|
|
2599
2712
|
await authorizeEdit(context, files);
|
|
2600
|
-
return runReadSeek(context,
|
|
2713
|
+
return runReadSeek(context, buildRenameSymbolArgs({
|
|
2601
2714
|
path: filePath,
|
|
2602
2715
|
to: input.to,
|
|
2603
|
-
|
|
2604
|
-
column: input.column,
|
|
2716
|
+
at: input.at,
|
|
2605
2717
|
workspace: input.workspace ? context.directory : undefined,
|
|
2606
2718
|
apply: true,
|
|
2607
|
-
planHash
|
|
2719
|
+
planHash,
|
|
2720
|
+
language: input.language,
|
|
2721
|
+
gitScope: input.gitScope
|
|
2608
2722
|
}), {
|
|
2609
2723
|
cancelable: false,
|
|
2610
2724
|
timeoutMs: DEFAULT_READSEEK_TIMEOUT_MS
|
|
2611
2725
|
});
|
|
2612
2726
|
}),
|
|
2613
|
-
|
|
2614
|
-
path:
|
|
2615
|
-
edits:
|
|
2616
|
-
|
|
2617
|
-
set_line:
|
|
2618
|
-
anchor:
|
|
2619
|
-
new_text:
|
|
2727
|
+
readseek_edit_text: readseekTool(openCodeToolDescription("edit_text"), {
|
|
2728
|
+
path: tool3.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
2729
|
+
edits: tool3.schema.array(tool3.schema.union([
|
|
2730
|
+
tool3.schema.object({
|
|
2731
|
+
set_line: tool3.schema.object({
|
|
2732
|
+
anchor: tool3.schema.string().describe(PARAM_DESCRIPTIONS.anchor),
|
|
2733
|
+
new_text: tool3.schema.string().describe(PARAM_DESCRIPTIONS.newTextLine)
|
|
2620
2734
|
}).strict()
|
|
2621
2735
|
}).strict(),
|
|
2622
|
-
|
|
2623
|
-
replace_lines:
|
|
2624
|
-
start_anchor:
|
|
2625
|
-
end_anchor:
|
|
2626
|
-
new_text:
|
|
2736
|
+
tool3.schema.object({
|
|
2737
|
+
replace_lines: tool3.schema.object({
|
|
2738
|
+
start_anchor: tool3.schema.string().describe(PARAM_DESCRIPTIONS.startAnchor),
|
|
2739
|
+
end_anchor: tool3.schema.string().describe(PARAM_DESCRIPTIONS.endAnchor),
|
|
2740
|
+
new_text: tool3.schema.string().describe(PARAM_DESCRIPTIONS.newTextRange)
|
|
2627
2741
|
}).strict()
|
|
2628
2742
|
}).strict(),
|
|
2629
|
-
|
|
2630
|
-
insert_after:
|
|
2631
|
-
anchor:
|
|
2632
|
-
new_text:
|
|
2743
|
+
tool3.schema.object({
|
|
2744
|
+
insert_after: tool3.schema.object({
|
|
2745
|
+
anchor: tool3.schema.string().describe(PARAM_DESCRIPTIONS.insertAnchor),
|
|
2746
|
+
new_text: tool3.schema.string().describe(PARAM_DESCRIPTIONS.newTextInsert)
|
|
2633
2747
|
}).strict()
|
|
2634
2748
|
}).strict(),
|
|
2635
|
-
|
|
2636
|
-
replace:
|
|
2637
|
-
old_text:
|
|
2638
|
-
new_text:
|
|
2639
|
-
all:
|
|
2749
|
+
tool3.schema.object({
|
|
2750
|
+
replace: tool3.schema.object({
|
|
2751
|
+
old_text: tool3.schema.string().describe(PARAM_DESCRIPTIONS.oldText),
|
|
2752
|
+
new_text: tool3.schema.string().describe(PARAM_DESCRIPTIONS.newText),
|
|
2753
|
+
all: tool3.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.all)
|
|
2640
2754
|
}).strict()
|
|
2641
2755
|
}).strict(),
|
|
2642
|
-
|
|
2643
|
-
replace_symbol:
|
|
2644
|
-
symbol:
|
|
2645
|
-
new_body:
|
|
2756
|
+
tool3.schema.object({
|
|
2757
|
+
replace_symbol: tool3.schema.object({
|
|
2758
|
+
symbol: tool3.schema.string().describe(PARAM_DESCRIPTIONS.symbol),
|
|
2759
|
+
new_body: tool3.schema.string().describe(PARAM_DESCRIPTIONS.newBody)
|
|
2646
2760
|
}).strict()
|
|
2647
2761
|
}).strict()
|
|
2648
2762
|
])).min(1).describe(PARAM_DESCRIPTIONS.edits),
|
|
2649
|
-
language:
|
|
2650
|
-
|
|
2763
|
+
language: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.languageSymbol),
|
|
2764
|
+
apply: tool3.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.applyDefaultTrue),
|
|
2765
|
+
planHash: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.planHash),
|
|
2766
|
+
gitScope: tool3.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
2767
|
+
}, "edit_text", async (input, context) => {
|
|
2651
2768
|
const filePath = resolvePath(context.directory, input.path);
|
|
2652
2769
|
validateEdits(input.edits);
|
|
2653
2770
|
return withFileMutationQueue(filePath, async () => {
|
|
@@ -2656,15 +2773,21 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2656
2773
|
await rejectSymlinkMutation(filePath);
|
|
2657
2774
|
const edits = input.edits;
|
|
2658
2775
|
const request = serializeEditPlan(edits);
|
|
2659
|
-
const baseArgs =
|
|
2776
|
+
const baseArgs = buildEditTextArgs({
|
|
2660
2777
|
path: filePath,
|
|
2661
2778
|
language: input.language,
|
|
2779
|
+
gitScope: input.gitScope,
|
|
2662
2780
|
requestFromStdin: true
|
|
2663
2781
|
});
|
|
2664
|
-
const plan =
|
|
2782
|
+
const plan = record2(await runReadSeek(context, baseArgs, { stdin: request }));
|
|
2665
2783
|
if (typeof plan.before_content !== "string" || typeof plan.content !== "string" || typeof plan.plan_hash !== "string") {
|
|
2666
2784
|
throw new Error("readseek edit plan is missing before_content, content, or plan_hash");
|
|
2667
2785
|
}
|
|
2786
|
+
if (input.planHash !== undefined && plan.plan_hash !== input.planHash) {
|
|
2787
|
+
throw new Error("readseek edit plan does not match planHash");
|
|
2788
|
+
}
|
|
2789
|
+
if (input.apply === false)
|
|
2790
|
+
return plan;
|
|
2668
2791
|
const before = plan.before_content;
|
|
2669
2792
|
const after = plan.content;
|
|
2670
2793
|
const previewStart = Math.max(1, firstChangedLine(before, after) - 3);
|
|
@@ -2674,9 +2797,10 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2674
2797
|
}
|
|
2675
2798
|
await authorizeEdit(context, [filePath], simpleDiff(filePath, before, after), false);
|
|
2676
2799
|
context.abort.throwIfAborted();
|
|
2677
|
-
const applied =
|
|
2800
|
+
const applied = record2(await runReadSeek(context, buildEditTextArgs({
|
|
2678
2801
|
path: filePath,
|
|
2679
2802
|
language: input.language,
|
|
2803
|
+
gitScope: input.gitScope,
|
|
2680
2804
|
requestFromStdin: true,
|
|
2681
2805
|
apply: true,
|
|
2682
2806
|
planHash: plan.plan_hash
|
|
@@ -2691,10 +2815,10 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2691
2815
|
return postMutationPreview(context, previewArgs, filePath, true);
|
|
2692
2816
|
});
|
|
2693
2817
|
}),
|
|
2694
|
-
|
|
2695
|
-
path:
|
|
2696
|
-
content:
|
|
2697
|
-
}, "
|
|
2818
|
+
readseek_write_file: readseekTool(openCodeToolDescription("write_file"), {
|
|
2819
|
+
path: tool3.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
2820
|
+
content: tool3.schema.string().describe(PARAM_DESCRIPTIONS.content)
|
|
2821
|
+
}, "write_file", async (input, context) => {
|
|
2698
2822
|
const filePath = resolvePath(context.directory, input.path);
|
|
2699
2823
|
await authorizeExternal(context, filePath);
|
|
2700
2824
|
return withFileMutationQueue(filePath, async () => {
|
|
@@ -2707,11 +2831,50 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2707
2831
|
await authorizeEdit(context, [filePath], simpleDiff(filePath, before.content, content), false);
|
|
2708
2832
|
context.abort.throwIfAborted();
|
|
2709
2833
|
await writeText(filePath, before, content);
|
|
2710
|
-
return postMutationPreview(context,
|
|
2834
|
+
return postMutationPreview(context, contentArgs(filePath, { end: DEFAULT_READ_LIMIT }), filePath, !before.exists || before.content !== content);
|
|
2711
2835
|
});
|
|
2712
2836
|
})
|
|
2713
2837
|
};
|
|
2714
2838
|
}
|
|
2839
|
+
function createAccessTools(imagePolicy) {
|
|
2840
|
+
const pathSchema = { path: tool3.schema.string().describe(PARAM_DESCRIPTIONS.projectPath) };
|
|
2841
|
+
const atSchema = { at: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.at) };
|
|
2842
|
+
const languageSchema = { language: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.language) };
|
|
2843
|
+
const levelSchema = { level: tool3.schema.enum(VISION_LEVEL_VALUES).optional().describe(PARAM_DESCRIPTIONS.level) };
|
|
2844
|
+
const definitions = {
|
|
2845
|
+
show_content: { build: buildShowContentArgs, args: { ...pathSchema, ...atSchema, ...languageSchema, end: tool3.schema.number().int().positive().optional(), limit: tool3.schema.number().int().positive().optional(), noAnchors: tool3.schema.boolean().optional() } },
|
|
2846
|
+
show_structure: { build: buildShowStructureArgs, args: { ...pathSchema, ...atSchema, ...languageSchema, depth: tool3.schema.number().int().nonnegative().optional(), kind: tool3.schema.string().optional(), outline: tool3.schema.boolean().optional() } },
|
|
2847
|
+
show_metadata: { build: buildShowMetadataArgs, args: { ...pathSchema, ...languageSchema } },
|
|
2848
|
+
show_assets: { build: buildShowAssetsArgs, args: { ...pathSchema, ...atSchema } },
|
|
2849
|
+
check_syntax: { build: buildCheckSyntaxArgs, args: { ...pathSchema, ...languageSchema } },
|
|
2850
|
+
check_rules: { build: buildCheckRulesArgs, args: { path: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath), rules: tool3.schema.string().min(1), gitScope: tool3.schema.enum(GIT_SCOPES).optional() } },
|
|
2851
|
+
generate_caption: { build: buildGenerateCaptionArgs, args: { ...pathSchema, ...atSchema, ...levelSchema, include: tool3.schema.array(tool3.schema.enum(["text", "objects"])).min(1).optional() } },
|
|
2852
|
+
detect_objects: { build: buildDetectObjectsArgs, args: { ...pathSchema, ...atSchema, ...levelSchema } },
|
|
2853
|
+
recognize_text: { build: buildRecognizeTextArgs, args: { ...pathSchema, ...atSchema, ...levelSchema } }
|
|
2854
|
+
};
|
|
2855
|
+
return Object.fromEntries(Object.entries(definitions).map(([name, definition]) => [
|
|
2856
|
+
`readseek_${name}`,
|
|
2857
|
+
readseekTool(openCodeToolDescription(name), definition.args, name, async (input, context) => {
|
|
2858
|
+
const filePath = resolvePath(context.directory, input.path ?? ".");
|
|
2859
|
+
const buildInput = { ...input, path: filePath, ...name === "show_structure" ? { depth: input.depth ?? DEFAULT_STRUCTURE_DEPTH } : {} };
|
|
2860
|
+
let args = definition.build(buildInput);
|
|
2861
|
+
await authorizeRead(context, filePath);
|
|
2862
|
+
if (name === "check_rules") {
|
|
2863
|
+
if (input.rules === "-")
|
|
2864
|
+
throw new Error("rules stdin is unavailable in OpenCode tools; use inline KDL or @path");
|
|
2865
|
+
await authorizeSearch(context, filePath, input.rules);
|
|
2866
|
+
if (input.rules.startsWith("@"))
|
|
2867
|
+
await authorizeRead(context, resolvePath(context.directory, input.rules.slice(1)));
|
|
2868
|
+
}
|
|
2869
|
+
const inference = ["generate_caption", "detect_objects", "recognize_text"].includes(name);
|
|
2870
|
+
if (name === "show_content" || name === "show_assets" || inference) {
|
|
2871
|
+
const readseekDir = await ensureReadSeekCache(context);
|
|
2872
|
+
args = definition.build({ ...buildInput, readseekDir });
|
|
2873
|
+
}
|
|
2874
|
+
return runReadSeek(context, args, { timeoutMs: inference ? DEFAULT_VISION_TIMEOUT_MS : DEFAULT_READSEEK_TIMEOUT_MS });
|
|
2875
|
+
}, name === "show_content" || name === "show_assets" ? imagePolicy : undefined)
|
|
2876
|
+
]));
|
|
2877
|
+
}
|
|
2715
2878
|
|
|
2716
2879
|
// v2.ts
|
|
2717
2880
|
var GUIDANCE = "For structural code navigation, consult the readseek skill when ReadSeek tools are unavailable and the readseek CLI is installed.";
|
|
@@ -2724,6 +2887,29 @@ var SKILL = {
|
|
|
2724
2887
|
|
|
2725
2888
|
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
2889
|
|
|
2890
|
+
Commands use verb-object pairs, with the target path positional:
|
|
2891
|
+
|
|
2892
|
+
\`\`\`sh
|
|
2893
|
+
readseek show content file.ts --at line:10 --limit 40 --kdl
|
|
2894
|
+
readseek show structure file.ts --depth 1 --kdl
|
|
2895
|
+
readseek show metadata paper.pdf --kdl
|
|
2896
|
+
readseek show assets paper.pdf --at page:2 --kdl
|
|
2897
|
+
readseek check syntax file.ts --kdl
|
|
2898
|
+
readseek check rules . --rules @checks.kdl --kdl
|
|
2899
|
+
readseek find nodes src --pattern 'console.log($$$ARGS)' --kdl
|
|
2900
|
+
readseek find text src --text 'TODO' --kdl
|
|
2901
|
+
readseek find definitions src --name main --kdl
|
|
2902
|
+
readseek find references file.ts --at line:10:3 --kdl
|
|
2903
|
+
readseek edit text file.ts --edits @edits.kdl --kdl
|
|
2904
|
+
readseek rename symbol file.ts --at line:10:3 --to nextName --kdl
|
|
2905
|
+
\`\`\`
|
|
2906
|
+
|
|
2907
|
+
Native \`find text\` is literal, not regex. Reference selection requires exactly one of \`--name\` or \`--at\`; rename requires a line or hash cursor. Preview mutations first, then use \`--apply --plan-hash HASH\` with the returned plan hash. Edit requests are KDL: for example, \`set_line anchor="10:HASH" new_text="replacement"\`.
|
|
2908
|
+
|
|
2909
|
+
Syntax and rule checks return exit 0 when clean, 1 for findings, and 2 on failure. Exit 1 is not a transport failure. Syntax checks are parser checks, not type checking.
|
|
2910
|
+
|
|
2911
|
+
Content and assets return cached paths; assets auto-materialize. Read those paths directly. There is no extract command or asset destination option. Reading does not invoke inference. Request inference explicitly with \`generate caption\` (optionally \`--include text,objects\`), \`detect objects\`, or \`recognize text\`, using \`--level low|medium|high\` and an installed model; models are never downloaded automatically.
|
|
2912
|
+
|
|
2727
2913
|
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
2914
|
};
|
|
2729
2915
|
var ReadSeekV2Plugin = {
|
|
@@ -2779,9 +2965,9 @@ var ReadSeekPlugin = async (_input, options) => {
|
|
|
2779
2965
|
return;
|
|
2780
2966
|
try {
|
|
2781
2967
|
const result = JSON.parse(output.output);
|
|
2782
|
-
if (input.tool === "
|
|
2968
|
+
if (input.tool === "readseek_rename_symbol") {
|
|
2783
2969
|
anchors.planRename(input.sessionID, result);
|
|
2784
|
-
if (
|
|
2970
|
+
if (record2(result).applied === true) {
|
|
2785
2971
|
for (const filePath of renameFiles(result))
|
|
2786
2972
|
anchors.forget(filePath);
|
|
2787
2973
|
}
|
|
@@ -2789,7 +2975,7 @@ var ReadSeekPlugin = async (_input, options) => {
|
|
|
2789
2975
|
const files = new Set;
|
|
2790
2976
|
collectAnchoredFiles(result, files);
|
|
2791
2977
|
for (const filePath of files)
|
|
2792
|
-
anchors.mark(input.sessionID,
|
|
2978
|
+
anchors.mark(input.sessionID, path8.resolve(filePath));
|
|
2793
2979
|
} catch {}
|
|
2794
2980
|
},
|
|
2795
2981
|
"experimental.session.compacting": async (input, output) => {
|