opencode-readseek 0.10.7 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -5
- package/dist/index.js +1057 -1062
- 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,6 +397,597 @@ 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);
|
|
400
|
+
// ../readseek-api/src/argv.ts
|
|
401
|
+
var DEFAULT_STRUCTURE_DEPTH = 1;
|
|
402
|
+
function appendGitScopeArgs(args, input) {
|
|
403
|
+
if (input.gitScope !== undefined)
|
|
404
|
+
args.push("--git-scope", input.gitScope);
|
|
405
|
+
return args;
|
|
406
|
+
}
|
|
407
|
+
function pushOptionalString(args, flag, value) {
|
|
408
|
+
if (value !== undefined)
|
|
409
|
+
args.push(flag, value);
|
|
410
|
+
}
|
|
411
|
+
function pushOptionalNumber(args, flag, value) {
|
|
412
|
+
if (value !== undefined)
|
|
413
|
+
args.push(flag, String(value));
|
|
414
|
+
}
|
|
415
|
+
function appendApplyAndPlanHash(args, input) {
|
|
416
|
+
const order = input.flagOrder ?? "apply-first";
|
|
417
|
+
if (order === "plan-hash-first") {
|
|
418
|
+
pushOptionalString(args, "--plan-hash", input.planHash);
|
|
419
|
+
if (input.apply)
|
|
420
|
+
args.push("--apply");
|
|
421
|
+
return args;
|
|
422
|
+
}
|
|
423
|
+
if (input.apply)
|
|
424
|
+
args.push("--apply");
|
|
425
|
+
pushOptionalString(args, "--plan-hash", input.planHash);
|
|
426
|
+
return args;
|
|
427
|
+
}
|
|
428
|
+
function validatePositiveInteger(value, name) {
|
|
429
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
430
|
+
throw new Error(`${name} must be a positive integer`);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
var SELECTOR_KINDS = ["line", "hash", "symbol", "page", "node", "asset"];
|
|
434
|
+
function isSelectorKind(value) {
|
|
435
|
+
return SELECTOR_KINDS.includes(value);
|
|
436
|
+
}
|
|
437
|
+
function parseSelector(text) {
|
|
438
|
+
const separator = text.indexOf(":");
|
|
439
|
+
if (separator < 0) {
|
|
440
|
+
throw new Error("selector must start with line:, hash:, symbol:, page:, node:, or asset:");
|
|
441
|
+
}
|
|
442
|
+
const kind = text.slice(0, separator);
|
|
443
|
+
const value = text.slice(separator + 1);
|
|
444
|
+
if (value.length === 0)
|
|
445
|
+
throw new Error(`${kind} selector must not be empty`);
|
|
446
|
+
if (!isSelectorKind(kind)) {
|
|
447
|
+
throw new Error(`unknown selector kind \`${kind}\`; expected line, hash, symbol, page, node, or asset`);
|
|
448
|
+
}
|
|
449
|
+
if (kind === "line" || kind === "hash") {
|
|
450
|
+
const parts = value.split(":");
|
|
451
|
+
if (parts.length > 2)
|
|
452
|
+
throw new Error("selector has too many components");
|
|
453
|
+
if (parts.length === 2) {
|
|
454
|
+
const column = Number(parts[1]);
|
|
455
|
+
if (!Number.isSafeInteger(column))
|
|
456
|
+
throw new Error(`invalid column \`${parts[1]}\``);
|
|
457
|
+
if (column < 1)
|
|
458
|
+
throw new Error("column must be greater than zero");
|
|
459
|
+
return { kind, value: parts[0], column };
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if (kind === "page") {
|
|
463
|
+
if (value.includes(":"))
|
|
464
|
+
throw new Error("page selector has too many components");
|
|
465
|
+
const page = Number(value);
|
|
466
|
+
if (!Number.isSafeInteger(page))
|
|
467
|
+
throw new Error(`invalid page \`${value}\``);
|
|
468
|
+
if (page < 1)
|
|
469
|
+
throw new Error("page must be greater than zero");
|
|
470
|
+
}
|
|
471
|
+
return { kind, value };
|
|
472
|
+
}
|
|
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);
|
|
488
|
+
}
|
|
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) {
|
|
507
|
+
if (input.end !== undefined)
|
|
508
|
+
validatePositiveInteger(input.end, "end");
|
|
509
|
+
if (input.end !== undefined && input.limit !== undefined)
|
|
510
|
+
throw new Error("cannot combine end with limit");
|
|
511
|
+
const args = start("show", "content", input);
|
|
512
|
+
at2(args, input.at);
|
|
513
|
+
pushOptionalNumber(args, "--end", input.end);
|
|
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);
|
|
524
|
+
pushOptionalNumber(args, "--depth", input.depth);
|
|
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);
|
|
556
|
+
return args;
|
|
557
|
+
}
|
|
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);
|
|
599
|
+
}
|
|
600
|
+
function buildFindTextArgs(input) {
|
|
601
|
+
const args = start("find", "text", input);
|
|
602
|
+
args.push("--text", input.text);
|
|
603
|
+
limit2(args, input.limit);
|
|
604
|
+
appendGitScopeArgs(args, input);
|
|
605
|
+
return finish(args);
|
|
606
|
+
}
|
|
607
|
+
function buildFindDefinitionsArgs(input) {
|
|
608
|
+
const args = start("find", "definitions", input);
|
|
609
|
+
args.push("--name", input.name);
|
|
610
|
+
language(args, input);
|
|
611
|
+
appendGitScopeArgs(args, input);
|
|
612
|
+
anchors(args, input);
|
|
613
|
+
return finish(args);
|
|
614
|
+
}
|
|
615
|
+
function buildFindReferencesArgs(input) {
|
|
616
|
+
const validated = validateReferencesSelection(input);
|
|
617
|
+
if (!validated.ok)
|
|
618
|
+
throw new Error(validated.error);
|
|
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);
|
|
625
|
+
appendGitScopeArgs(args, input);
|
|
626
|
+
anchors(args, input);
|
|
627
|
+
return finish(args);
|
|
628
|
+
}
|
|
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);
|
|
640
|
+
if (input.requestFromStdin)
|
|
641
|
+
args.push("--edits", "-");
|
|
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);
|
|
650
|
+
if (input.workspace !== undefined)
|
|
651
|
+
args.push(`--workspace=${input.workspace}`);
|
|
652
|
+
appendApplyAndPlanHash(args, { ...input, flagOrder: input.flagOrder ?? "plan-hash-first" });
|
|
653
|
+
language(args, input);
|
|
654
|
+
appendGitScopeArgs(args, input);
|
|
655
|
+
return finish(args);
|
|
656
|
+
}
|
|
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
|
+
});
|
|
698
|
+
}
|
|
699
|
+
function recursiveStructureNode() {
|
|
700
|
+
return Type.Recursive((self) => mapNodeShape(self));
|
|
701
|
+
}
|
|
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
|
+
|
|
577
991
|
// ../readseek-api/src/edit-plan.ts
|
|
578
992
|
import { Document, Node, format } from "@bgotink/kdl";
|
|
579
993
|
function node(name, properties, children = []) {
|
|
@@ -614,268 +1028,11 @@ function serializeEditPlan(edits) {
|
|
|
614
1028
|
throw new Error(validation.error);
|
|
615
1029
|
return format(new Document([node("plan", {}, validation.edits.map(editNode))]));
|
|
616
1030
|
}
|
|
617
|
-
// ../readseek-api/src/argv.ts
|
|
618
|
-
var DEFAULT_INSPECT_MAP_DEPTH = 1;
|
|
619
|
-
function buildLineLocation(line, column) {
|
|
620
|
-
return column === undefined ? `line:${line}` : `line:${line}:${column}`;
|
|
621
|
-
}
|
|
622
|
-
function appendGitScopeArgs(args, input) {
|
|
623
|
-
if (input.gitScope !== undefined)
|
|
624
|
-
args.push("--git-scope", input.gitScope);
|
|
625
|
-
return args;
|
|
626
|
-
}
|
|
627
|
-
function pushOptionalString(args, flag, value) {
|
|
628
|
-
if (value !== undefined)
|
|
629
|
-
args.push(flag, value);
|
|
630
|
-
}
|
|
631
|
-
function pushOptionalNumber(args, flag, value) {
|
|
632
|
-
if (value !== undefined)
|
|
633
|
-
args.push(flag, String(value));
|
|
634
|
-
}
|
|
635
|
-
function appendApplyAndPlanHash(args, input) {
|
|
636
|
-
const order = input.flagOrder ?? "apply-first";
|
|
637
|
-
if (order === "plan-hash-first") {
|
|
638
|
-
pushOptionalString(args, "--plan-hash", input.planHash);
|
|
639
|
-
if (input.apply)
|
|
640
|
-
args.push("--apply");
|
|
641
|
-
return args;
|
|
642
|
-
}
|
|
643
|
-
if (input.apply)
|
|
644
|
-
args.push("--apply");
|
|
645
|
-
pushOptionalString(args, "--plan-hash", input.planHash);
|
|
646
|
-
return args;
|
|
647
|
-
}
|
|
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
|
-
function validatePositiveInteger(value, name) {
|
|
673
|
-
if (!Number.isSafeInteger(value) || value < 1) {
|
|
674
|
-
throw new Error(`${name} must be a positive integer`);
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
var SELECTOR_KINDS = ["line", "hash", "symbol", "page", "node", "asset"];
|
|
678
|
-
function isSelectorKind(value) {
|
|
679
|
-
return SELECTOR_KINDS.includes(value);
|
|
680
|
-
}
|
|
681
|
-
function parseSelector(text) {
|
|
682
|
-
const separator = text.indexOf(":");
|
|
683
|
-
if (separator < 0) {
|
|
684
|
-
throw new Error("selector must start with line:, hash:, symbol:, page:, node:, or asset:");
|
|
685
|
-
}
|
|
686
|
-
const kind = text.slice(0, separator);
|
|
687
|
-
const value = text.slice(separator + 1);
|
|
688
|
-
if (value.length === 0)
|
|
689
|
-
throw new Error(`${kind} selector must not be empty`);
|
|
690
|
-
if (!isSelectorKind(kind)) {
|
|
691
|
-
throw new Error(`unknown selector kind \`${kind}\`; expected line, hash, symbol, page, node, or asset`);
|
|
692
|
-
}
|
|
693
|
-
if (kind === "line" || kind === "hash") {
|
|
694
|
-
const parts = value.split(":");
|
|
695
|
-
if (parts.length > 2)
|
|
696
|
-
throw new Error("selector has too many components");
|
|
697
|
-
if (parts.length === 2) {
|
|
698
|
-
const column = Number(parts[1]);
|
|
699
|
-
if (!Number.isSafeInteger(column))
|
|
700
|
-
throw new Error(`invalid column \`${parts[1]}\``);
|
|
701
|
-
if (column < 1)
|
|
702
|
-
throw new Error("column must be greater than zero");
|
|
703
|
-
return { kind, value: parts[0], column };
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
if (kind === "page") {
|
|
707
|
-
if (value.includes(":"))
|
|
708
|
-
throw new Error("page selector has too many components");
|
|
709
|
-
const page = Number(value);
|
|
710
|
-
if (!Number.isSafeInteger(page))
|
|
711
|
-
throw new Error(`invalid page \`${value}\``);
|
|
712
|
-
if (page < 1)
|
|
713
|
-
throw new Error("page must be greater than zero");
|
|
714
|
-
}
|
|
715
|
-
return { kind, value };
|
|
716
|
-
}
|
|
717
|
-
function buildInspectArgs(input) {
|
|
718
|
-
validateTarget("inspect", input.path, input.stdinName);
|
|
719
|
-
if (input.selector !== undefined && input.selector.length === 0) {
|
|
720
|
-
throw new Error("selector must not be empty");
|
|
721
|
-
}
|
|
722
|
-
const vision = validateVisionLevelVsMode({
|
|
723
|
-
visionMode: input.visionMode,
|
|
724
|
-
visionLevel: input.visionLevel
|
|
725
|
-
});
|
|
726
|
-
if (!vision.ok)
|
|
727
|
-
throw new Error(vision.error);
|
|
728
|
-
const facets = selectedInspectFacets(input.select);
|
|
729
|
-
const hasContent = hasInspectFacet(facets, "content");
|
|
730
|
-
const hasIdentity = hasInspectFacet(facets, "identity");
|
|
731
|
-
const hasMap = hasInspectFacet(facets, "map");
|
|
732
|
-
const hasSourceFacet = hasMap || hasInspectFacet(facets, "diagnostics") || hasIdentity;
|
|
733
|
-
if (input.end !== undefined)
|
|
734
|
-
validatePositiveInteger(input.end, "end");
|
|
735
|
-
if (input.limit !== undefined)
|
|
736
|
-
validatePositiveInteger(input.limit, "limit");
|
|
737
|
-
if (input.end !== undefined && input.limit !== undefined) {
|
|
738
|
-
throw new Error("cannot combine end with limit");
|
|
739
|
-
}
|
|
740
|
-
if (input.depth !== undefined) {
|
|
741
|
-
if (!Number.isSafeInteger(input.depth) || input.depth < 0) {
|
|
742
|
-
throw new Error("depth must be a non-negative integer");
|
|
743
|
-
}
|
|
744
|
-
if (!hasMap)
|
|
745
|
-
throw new Error("depth requires the map facet");
|
|
746
|
-
}
|
|
747
|
-
if (input.selector !== undefined && input.query !== undefined) {
|
|
748
|
-
throw new Error("cannot combine selector with query");
|
|
749
|
-
}
|
|
750
|
-
if (!hasContent && (input.end !== undefined || input.limit !== undefined || input.query !== undefined)) {
|
|
751
|
-
throw new Error("ranges and queries require the content facet");
|
|
752
|
-
}
|
|
753
|
-
if (hasIdentity && input.selector === undefined) {
|
|
754
|
-
throw new Error("the identity facet requires a selector");
|
|
755
|
-
}
|
|
756
|
-
if (input.selector !== undefined && !hasContent && !hasIdentity) {
|
|
757
|
-
throw new Error("selectors require the content or identity facet");
|
|
758
|
-
}
|
|
759
|
-
if (input.selector !== undefined) {
|
|
760
|
-
const selector = parseSelector(input.selector);
|
|
761
|
-
if (selector.kind === "symbol" && hasIdentity) {
|
|
762
|
-
throw new Error("the identity facet does not accept symbol selectors");
|
|
763
|
-
}
|
|
764
|
-
if (selector.column !== undefined && !hasIdentity) {
|
|
765
|
-
throw new Error("selector columns require the identity facet");
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
const hasVision = input.visionMode !== undefined || input.visionLevel !== undefined;
|
|
769
|
-
if (hasVision && !hasContent) {
|
|
770
|
-
throw new Error("vision options require the content facet");
|
|
771
|
-
}
|
|
772
|
-
if (hasVision && hasSourceFacet) {
|
|
773
|
-
throw new Error("vision options cannot be combined with source facets");
|
|
774
|
-
}
|
|
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);
|
|
785
|
-
pushOptionalNumber(args, "--end", input.end);
|
|
786
|
-
pushOptionalNumber(args, "--limit", input.limit);
|
|
787
|
-
pushOptionalNumber(args, "--depth", input.depth);
|
|
788
|
-
pushOptionalString(args, "--language", input.language);
|
|
789
|
-
pushOptionalString(args, "--vision-mode", input.visionMode);
|
|
790
|
-
pushOptionalString(args, "--vision-level", input.visionLevel);
|
|
791
|
-
pushOptionalString(args, "--stdin-name", input.stdinName);
|
|
792
|
-
args.push("--kdl");
|
|
793
|
-
return args;
|
|
794
|
-
}
|
|
795
|
-
function buildExtractArgs(input) {
|
|
796
|
-
validateTarget("extract", input.path, input.stdinName);
|
|
797
|
-
if (input.selector.length === 0)
|
|
798
|
-
throw new Error("extract requires an asset selector");
|
|
799
|
-
if (!input.selector.startsWith("asset:") || input.selector === "asset:") {
|
|
800
|
-
throw new Error("extract requires an asset: selector");
|
|
801
|
-
}
|
|
802
|
-
if (input.output.length === 0 || input.output === "-") {
|
|
803
|
-
throw new Error("extract output must be a file path");
|
|
804
|
-
}
|
|
805
|
-
const args = [];
|
|
806
|
-
if (input.readseekDir !== undefined)
|
|
807
|
-
args.push("--readseek-dir", input.readseekDir);
|
|
808
|
-
args.push("--output", input.output, "extract", input.path, input.selector);
|
|
809
|
-
pushOptionalString(args, "--stdin-name", input.stdinName);
|
|
810
|
-
return args;
|
|
811
|
-
}
|
|
812
|
-
function buildSearchArgs(input) {
|
|
813
|
-
const args = ["search", input.pattern];
|
|
814
|
-
pushOptionalString(args, "--path", input.path);
|
|
815
|
-
pushOptionalString(args, "--language", input.language);
|
|
816
|
-
pushOptionalNumber(args, "--limit", input.limit);
|
|
817
|
-
appendGitScopeArgs(args, input);
|
|
818
|
-
args.push("--kdl");
|
|
819
|
-
return args;
|
|
820
|
-
}
|
|
821
|
-
function buildDefArgs(input) {
|
|
822
|
-
const args = ["def", input.name];
|
|
823
|
-
pushOptionalString(args, "--path", input.path);
|
|
824
|
-
pushOptionalString(args, "--language", input.language);
|
|
825
|
-
appendGitScopeArgs(args, input);
|
|
826
|
-
args.push("--kdl");
|
|
827
|
-
return args;
|
|
828
|
-
}
|
|
829
|
-
function buildRefsArgs(input) {
|
|
830
|
-
const validated = validateRefsNameVsScope(input);
|
|
831
|
-
if (!validated.ok)
|
|
832
|
-
throw new Error(validated.error);
|
|
833
|
-
const args = ["refs"];
|
|
834
|
-
if (input.name !== undefined)
|
|
835
|
-
args.push(input.name);
|
|
836
|
-
pushOptionalString(args, "--path", input.path);
|
|
837
|
-
if (input.scope) {
|
|
838
|
-
args.push("--scope");
|
|
839
|
-
pushOptionalNumber(args, "--line", input.line);
|
|
840
|
-
pushOptionalNumber(args, "--column", input.column);
|
|
841
|
-
}
|
|
842
|
-
pushOptionalString(args, "--language", input.language);
|
|
843
|
-
appendGitScopeArgs(args, input);
|
|
844
|
-
args.push("--kdl");
|
|
845
|
-
return args;
|
|
846
|
-
}
|
|
847
|
-
function buildEditArgs(input) {
|
|
848
|
-
const args = ["edit", input.path];
|
|
849
|
-
appendApplyAndPlanHash(args, {
|
|
850
|
-
apply: input.apply,
|
|
851
|
-
planHash: input.planHash,
|
|
852
|
-
flagOrder: input.flagOrder ?? "apply-first"
|
|
853
|
-
});
|
|
854
|
-
pushOptionalString(args, "--language", input.language);
|
|
855
|
-
if (input.requestFromStdin)
|
|
856
|
-
args.push("--edits", "-");
|
|
857
|
-
args.push("--kdl");
|
|
858
|
-
return args;
|
|
859
|
-
}
|
|
860
|
-
function buildRenameArgs(input) {
|
|
861
|
-
const args = ["rename", input.path, "--at", buildLineLocation(input.line, input.column), "--to", input.to];
|
|
862
|
-
if (input.workspace !== undefined)
|
|
863
|
-
args.push(`--workspace=${input.workspace}`);
|
|
864
|
-
appendApplyAndPlanHash(args, {
|
|
865
|
-
apply: input.apply,
|
|
866
|
-
planHash: input.planHash,
|
|
867
|
-
flagOrder: input.flagOrder ?? "plan-hash-first"
|
|
868
|
-
});
|
|
869
|
-
pushOptionalString(args, "--language", input.language);
|
|
870
|
-
appendGitScopeArgs(args, input);
|
|
871
|
-
args.push("--kdl");
|
|
872
|
-
return args;
|
|
873
|
-
}
|
|
874
1031
|
// ../readseek-api/src/platform.ts
|
|
875
1032
|
import { accessSync, constants as fsConstants, existsSync, statSync } from "node:fs";
|
|
876
1033
|
import { createRequire } from "node:module";
|
|
877
1034
|
import { homedir } from "node:os";
|
|
878
|
-
import
|
|
1035
|
+
import path2 from "node:path";
|
|
879
1036
|
var READSEEK_BINARY_ENV = "READSEEK_BINARY";
|
|
880
1037
|
var READSEEK_PLATFORM_PACKAGES = {
|
|
881
1038
|
"darwin-arm64": "@jarkkojs/readseek-darwin-arm64",
|
|
@@ -896,7 +1053,7 @@ function resolveReadSeekBinaryOverride(environment = process.env) {
|
|
|
896
1053
|
const binaryPath = environment[READSEEK_BINARY_ENV];
|
|
897
1054
|
if (binaryPath === undefined)
|
|
898
1055
|
return null;
|
|
899
|
-
if (!
|
|
1056
|
+
if (!path2.isAbsolute(binaryPath)) {
|
|
900
1057
|
throw new Error(`${READSEEK_BINARY_ENV} must be an absolute path to the readseek executable`);
|
|
901
1058
|
}
|
|
902
1059
|
try {
|
|
@@ -924,14 +1081,14 @@ function resolveReadSeekBinaryFromPackages(resolver = resolveFromPackage, option
|
|
|
924
1081
|
let packageJsonPath;
|
|
925
1082
|
try {
|
|
926
1083
|
const metaPackageJson = resolve(`${metaPackage}/package.json`);
|
|
927
|
-
const metaPackageDir =
|
|
1084
|
+
const metaPackageDir = path2.dirname(metaPackageJson);
|
|
928
1085
|
packageJsonPath = resolve(`${platformPackage}/package.json`, {
|
|
929
1086
|
paths: [metaPackageDir]
|
|
930
1087
|
});
|
|
931
1088
|
} catch {
|
|
932
1089
|
packageJsonPath = resolve(`${platformPackage}/package.json`);
|
|
933
1090
|
}
|
|
934
|
-
const binaryPath =
|
|
1091
|
+
const binaryPath = path2.join(path2.dirname(packageJsonPath), "bin", readSeekBinaryName(platform));
|
|
935
1092
|
accessSync(binaryPath, fsConstants.X_OK);
|
|
936
1093
|
return binaryPath;
|
|
937
1094
|
}
|
|
@@ -942,18 +1099,18 @@ function resolveReadSeekCacheDir(subPath, options = {}) {
|
|
|
942
1099
|
const cwd = options.cwd ?? process.cwd();
|
|
943
1100
|
const localDirName = options.localDirName ?? ".readseek";
|
|
944
1101
|
if (localDirName && localDirName.length > 0) {
|
|
945
|
-
const local =
|
|
1102
|
+
const local = path2.join(cwd, localDirName);
|
|
946
1103
|
if (existsSync(local))
|
|
947
1104
|
return local;
|
|
948
1105
|
}
|
|
949
1106
|
let base;
|
|
950
1107
|
if (platform === "win32") {
|
|
951
|
-
base = env.LOCALAPPDATA ??
|
|
1108
|
+
base = env.LOCALAPPDATA ?? path2.join(home, "AppData", "Local");
|
|
952
1109
|
} else {
|
|
953
1110
|
const xdg = env.XDG_CACHE_HOME?.trim();
|
|
954
|
-
base = xdg &&
|
|
1111
|
+
base = xdg && path2.isAbsolute(xdg) ? xdg : path2.join(home, ".cache");
|
|
955
1112
|
}
|
|
956
|
-
const pathModule = platform === "win32" ?
|
|
1113
|
+
const pathModule = platform === "win32" ? path2.win32 : path2;
|
|
957
1114
|
if (subPath === undefined) {
|
|
958
1115
|
return pathModule.join(base, "readseek");
|
|
959
1116
|
}
|
|
@@ -961,10 +1118,10 @@ function resolveReadSeekCacheDir(subPath, options = {}) {
|
|
|
961
1118
|
return pathModule.join(base, ...parts);
|
|
962
1119
|
}
|
|
963
1120
|
// ../readseek-api/src/path.ts
|
|
964
|
-
import
|
|
1121
|
+
import path3 from "node:path";
|
|
965
1122
|
function isSubPath(parent, candidate) {
|
|
966
|
-
const relative =
|
|
967
|
-
return relative === "" || !relative.startsWith(`..${
|
|
1123
|
+
const relative = path3.relative(parent, candidate);
|
|
1124
|
+
return relative === "" || !relative.startsWith(`..${path3.sep}`) && relative !== ".." && !path3.isAbsolute(relative);
|
|
968
1125
|
}
|
|
969
1126
|
// ../readseek-api/src/error.ts
|
|
970
1127
|
var READSEEK_USAGE_HINT = /(?:^|\n)\s*Run readseek(?: [\w-]+)* --help for more information\.?\s*$/;
|
|
@@ -1089,21 +1246,26 @@ function sourceDocument(root) {
|
|
|
1089
1246
|
const metadata = child(root, "metadata");
|
|
1090
1247
|
const cache = child(root, "cache");
|
|
1091
1248
|
const content = child(root, "content");
|
|
1092
|
-
const
|
|
1249
|
+
const structure = child(root, "structure");
|
|
1093
1250
|
const diagnosticOutput = child(root, "diagnostics");
|
|
1094
|
-
const identityOutput = child(root, "
|
|
1251
|
+
const identityOutput = child(root, "cursor");
|
|
1095
1252
|
if (metadata)
|
|
1096
1253
|
result.metadata = properties(metadata);
|
|
1254
|
+
if (!metadata && !content && !structure && !diagnosticOutput && !identityOutput && root.properties.has("file"))
|
|
1255
|
+
result.file = root.properties.get("file");
|
|
1256
|
+
const assets = children(root, "asset");
|
|
1257
|
+
if (assets.length > 0)
|
|
1258
|
+
result.assets = assets.map(genericNode);
|
|
1097
1259
|
if (cache)
|
|
1098
1260
|
result.cache = properties(cache);
|
|
1099
1261
|
if (content)
|
|
1100
1262
|
result.content = sourceContent(content);
|
|
1101
|
-
if (
|
|
1102
|
-
result.
|
|
1263
|
+
if (structure)
|
|
1264
|
+
result.structure = sourceMap(structure);
|
|
1103
1265
|
if (diagnosticOutput)
|
|
1104
1266
|
result.diagnostics = diagnostics(diagnosticOutput);
|
|
1105
1267
|
if (identityOutput)
|
|
1106
|
-
result.
|
|
1268
|
+
result.cursor = identity(identityOutput);
|
|
1107
1269
|
return result;
|
|
1108
1270
|
}
|
|
1109
1271
|
function hit(node) {
|
|
@@ -1224,7 +1386,10 @@ function genericNode(node) {
|
|
|
1224
1386
|
result.bbox = properties(bbox);
|
|
1225
1387
|
const objects = children(node, "object");
|
|
1226
1388
|
if (objects.length > 0)
|
|
1227
|
-
result.objects = objects.map(
|
|
1389
|
+
result.objects = objects.map((object) => ({
|
|
1390
|
+
label: requireProperty(object, "label"),
|
|
1391
|
+
bbox: [requireProperty(object, "x0"), requireProperty(object, "y0"), requireProperty(object, "x1"), requireProperty(object, "y1")]
|
|
1392
|
+
}));
|
|
1228
1393
|
return result;
|
|
1229
1394
|
}
|
|
1230
1395
|
function pdfDocument(root) {
|
|
@@ -1289,13 +1454,34 @@ function failureFromProcessResult(result, options = {}) {
|
|
|
1289
1454
|
return new Error(`readseek killed by signal ${result.signal}`);
|
|
1290
1455
|
return new Error(`readseek exited with status ${result.exitCode}`);
|
|
1291
1456
|
}
|
|
1292
|
-
function
|
|
1293
|
-
if (
|
|
1457
|
+
function acceptsReadSeekExitCode(args, exitCode) {
|
|
1458
|
+
if (exitCode === 0)
|
|
1459
|
+
return true;
|
|
1460
|
+
if (exitCode !== 1)
|
|
1461
|
+
return false;
|
|
1462
|
+
let index = 0;
|
|
1463
|
+
const valued = new Set(["-C", "--directory", "--output", "--readseek-dir"]);
|
|
1464
|
+
while (index < args.length) {
|
|
1465
|
+
const arg = args[index];
|
|
1466
|
+
if (valued.has(arg)) {
|
|
1467
|
+
index += 2;
|
|
1468
|
+
continue;
|
|
1469
|
+
}
|
|
1470
|
+
if ([...valued].some((flag) => arg.startsWith(`${flag}=`)) || arg === "--kdl") {
|
|
1471
|
+
index++;
|
|
1472
|
+
continue;
|
|
1473
|
+
}
|
|
1474
|
+
return arg === "find" || arg === "check";
|
|
1475
|
+
}
|
|
1476
|
+
return false;
|
|
1477
|
+
}
|
|
1478
|
+
function ensureSuccessfulStdout(result, args = []) {
|
|
1479
|
+
if (!result.signal && acceptsReadSeekExitCode(args, result.exitCode))
|
|
1294
1480
|
return result.stdout;
|
|
1295
1481
|
throw failureFromProcessResult(result);
|
|
1296
1482
|
}
|
|
1297
|
-
function parseSuccessfulKdl(result) {
|
|
1298
|
-
return parseReadSeekKdl(ensureSuccessfulStdout(result));
|
|
1483
|
+
function parseSuccessfulKdl(result, args = []) {
|
|
1484
|
+
return parseReadSeekKdl(ensureSuccessfulStdout(result, args));
|
|
1299
1485
|
}
|
|
1300
1486
|
// ../readseek-api/src/process-bun.ts
|
|
1301
1487
|
function getBunSpawn() {
|
|
@@ -1385,256 +1571,13 @@ async function runReadSeekBun(binaryPath, args, options = {}) {
|
|
|
1385
1571
|
}
|
|
1386
1572
|
}
|
|
1387
1573
|
async function runReadSeekBunText(binaryPath, args, options = {}) {
|
|
1388
|
-
return ensureSuccessfulStdout(await runReadSeekBun(binaryPath, args, options));
|
|
1389
|
-
}
|
|
1390
|
-
async function runReadSeekBunKdl(binaryPath, args, options = {}) {
|
|
1391
|
-
return parseSuccessfulKdl(await runReadSeekBun(binaryPath, args, options));
|
|
1392
|
-
}
|
|
1393
|
-
// ../readseek-api/src/client-output.ts
|
|
1394
|
-
import { Type } from "@sinclair/typebox";
|
|
1395
|
-
import { AssertError, Value } from "@sinclair/typebox/value";
|
|
1396
|
-
var Int = Type.Integer({ minimum: Number.MIN_SAFE_INTEGER, maximum: Number.MAX_SAFE_INTEGER });
|
|
1397
|
-
var Str = Type.String();
|
|
1398
|
-
var Bool = Type.Boolean();
|
|
1399
|
-
var OptStr = Type.Optional(Str);
|
|
1400
|
-
var OptInt = Type.Optional(Int);
|
|
1401
|
-
var Hashline = Type.Object({ line: Int, hash: Str, text: Str });
|
|
1402
|
-
var Hashlines = Type.Array(Hashline);
|
|
1403
|
-
var ReadOutput = Type.Object({
|
|
1404
|
-
file: Str,
|
|
1405
|
-
language: Str,
|
|
1406
|
-
line_count: Int,
|
|
1407
|
-
file_hash: Str,
|
|
1408
|
-
start_line: Int,
|
|
1409
|
-
end_line: Int,
|
|
1410
|
-
hashlines: Hashlines
|
|
1411
|
-
});
|
|
1412
|
-
var SymbolOutput = Type.Object({
|
|
1413
|
-
kind: Str,
|
|
1414
|
-
name: Str,
|
|
1415
|
-
qualified_name: OptStr,
|
|
1416
|
-
start_line: Int,
|
|
1417
|
-
end_line: Int,
|
|
1418
|
-
start_hash: OptStr,
|
|
1419
|
-
end_hash: OptStr,
|
|
1420
|
-
start_anchor: OptStr,
|
|
1421
|
-
end_anchor: OptStr
|
|
1422
|
-
});
|
|
1423
|
-
function mapNodeShape(child) {
|
|
1424
|
-
return Type.Object({
|
|
1425
|
-
kind: Str,
|
|
1426
|
-
name: Str,
|
|
1427
|
-
start_line: Int,
|
|
1428
|
-
end_line: Int,
|
|
1429
|
-
start_anchor: Str,
|
|
1430
|
-
end_anchor: Str,
|
|
1431
|
-
children: Type.Array(child)
|
|
1432
|
-
});
|
|
1574
|
+
return ensureSuccessfulStdout(await runReadSeekBun(binaryPath, args, options), args);
|
|
1433
1575
|
}
|
|
1434
|
-
function
|
|
1435
|
-
return
|
|
1576
|
+
async function runReadSeekBunKdl(binaryPath, args, options = {}) {
|
|
1577
|
+
return parseSuccessfulKdl(await runReadSeekBun(binaryPath, args, options), args);
|
|
1436
1578
|
}
|
|
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
1579
|
// src/util.ts
|
|
1637
|
-
import
|
|
1580
|
+
import path4 from "node:path";
|
|
1638
1581
|
function render(value) {
|
|
1639
1582
|
return JSON.stringify(value, null, 2);
|
|
1640
1583
|
}
|
|
@@ -1672,12 +1615,12 @@ function renameFiles(value, directory = process.cwd()) {
|
|
|
1672
1615
|
const output = record(value);
|
|
1673
1616
|
const files = [];
|
|
1674
1617
|
if (typeof output.file === "string" && items(output.edits).length > 0) {
|
|
1675
|
-
files.push(
|
|
1618
|
+
files.push(path4.resolve(directory, output.file));
|
|
1676
1619
|
}
|
|
1677
1620
|
for (const other of items(output.others)) {
|
|
1678
1621
|
const item = record(other);
|
|
1679
1622
|
if (typeof item.file === "string" && items(item.edits).length > 0) {
|
|
1680
|
-
files.push(
|
|
1623
|
+
files.push(path4.resolve(directory, item.file));
|
|
1681
1624
|
}
|
|
1682
1625
|
}
|
|
1683
1626
|
return files;
|
|
@@ -1728,21 +1671,18 @@ function resolveImagePolicy(options) {
|
|
|
1728
1671
|
return value;
|
|
1729
1672
|
throw new Error('opencode-readseek imageMode must be "on", "auto", or "off"');
|
|
1730
1673
|
}
|
|
1731
|
-
function
|
|
1732
|
-
return
|
|
1674
|
+
function contentArgs(filePath, options = {}) {
|
|
1675
|
+
return buildShowContentArgs({ path: filePath, ...options, at: options.at ?? (options.end === undefined ? undefined : "line:1") });
|
|
1733
1676
|
}
|
|
1734
|
-
function
|
|
1735
|
-
const value = record(envelope)
|
|
1677
|
+
function requireContent(envelope) {
|
|
1678
|
+
const value = record(envelope).content;
|
|
1736
1679
|
if (value === undefined || value === null || typeof value !== "object") {
|
|
1737
|
-
throw new Error(
|
|
1680
|
+
throw new Error("show content returned no source content");
|
|
1738
1681
|
}
|
|
1739
1682
|
return value;
|
|
1740
1683
|
}
|
|
1741
1684
|
function readRangeArgs(filePath, start, end) {
|
|
1742
|
-
return
|
|
1743
|
-
selector: start > 1 ? `line:${start}` : undefined,
|
|
1744
|
-
end
|
|
1745
|
-
});
|
|
1685
|
+
return contentArgs(filePath, { at: `line:${start}`, end });
|
|
1746
1686
|
}
|
|
1747
1687
|
function readSeekBinaryPath() {
|
|
1748
1688
|
return resolveReadSeekBinaryOverride() ?? resolveReadSeekBinaryFromPackages(resolveFromHost);
|
|
@@ -1779,12 +1719,12 @@ async function runReadSeek(context, args, options = {}) {
|
|
|
1779
1719
|
timeoutMs: options.timeoutMs
|
|
1780
1720
|
}));
|
|
1781
1721
|
}
|
|
1782
|
-
async function
|
|
1783
|
-
return
|
|
1722
|
+
async function runContent(context, args, options = {}) {
|
|
1723
|
+
return requireContent(await runReadSeek(context, args, options));
|
|
1784
1724
|
}
|
|
1785
1725
|
async function postMutationPreview(context, args, filePath, changed) {
|
|
1786
1726
|
try {
|
|
1787
|
-
return await
|
|
1727
|
+
return await runContent(context, args, {
|
|
1788
1728
|
cancelable: false,
|
|
1789
1729
|
timeoutMs: DEFAULT_READSEEK_TIMEOUT_MS
|
|
1790
1730
|
});
|
|
@@ -1813,7 +1753,7 @@ async function ensureReadSeekCache(context) {
|
|
|
1813
1753
|
if (error.code !== "ENOENT")
|
|
1814
1754
|
throw error;
|
|
1815
1755
|
}
|
|
1816
|
-
readSeekCacheInit ??= runReadSeekRaw(context, ["--readseek-dir", cacheDir, "
|
|
1756
|
+
readSeekCacheInit ??= runReadSeekRaw(context, ["--readseek-dir", cacheDir, "initialize", "index"], { cancelable: false }).then(() => cacheDir).finally(() => {
|
|
1817
1757
|
readSeekCacheInit = undefined;
|
|
1818
1758
|
});
|
|
1819
1759
|
return readSeekCacheInit;
|
|
@@ -1833,7 +1773,7 @@ function openCodeToolDescription(name) {
|
|
|
1833
1773
|
}
|
|
1834
1774
|
|
|
1835
1775
|
// src/session-anchors.ts
|
|
1836
|
-
import
|
|
1776
|
+
import path5 from "node:path";
|
|
1837
1777
|
|
|
1838
1778
|
class SessionAnchors {
|
|
1839
1779
|
#pathsBySession = new Map;
|
|
@@ -1847,7 +1787,7 @@ class SessionAnchors {
|
|
|
1847
1787
|
paths.add(filePath);
|
|
1848
1788
|
}
|
|
1849
1789
|
forget(filePath) {
|
|
1850
|
-
const absolutePath =
|
|
1790
|
+
const absolutePath = path5.resolve(filePath);
|
|
1851
1791
|
for (const paths of this.#pathsBySession.values())
|
|
1852
1792
|
paths.delete(absolutePath);
|
|
1853
1793
|
for (const [sessionID, plan] of this.#renamePlans) {
|
|
@@ -1870,14 +1810,14 @@ class SessionAnchors {
|
|
|
1870
1810
|
const { file, old_name: oldName, new_name: newName, others } = record;
|
|
1871
1811
|
if (typeof file !== "string" || typeof oldName !== "string" || typeof newName !== "string")
|
|
1872
1812
|
return;
|
|
1873
|
-
const files = new Set([
|
|
1813
|
+
const files = new Set([path5.resolve(file)]);
|
|
1874
1814
|
if (Array.isArray(others)) {
|
|
1875
1815
|
for (const item of others) {
|
|
1876
1816
|
if (!item || typeof item !== "object")
|
|
1877
1817
|
continue;
|
|
1878
1818
|
const otherFile = item.file;
|
|
1879
1819
|
if (typeof otherFile === "string")
|
|
1880
|
-
files.add(
|
|
1820
|
+
files.add(path5.resolve(otherFile));
|
|
1881
1821
|
}
|
|
1882
1822
|
}
|
|
1883
1823
|
this.#renamePlans.set(sessionID, { summary: `${oldName} -> ${newName}`, files });
|
|
@@ -1903,22 +1843,21 @@ ${[...paths].sort().map((filePath) => `- ${filePath}`).join(`
|
|
|
1903
1843
|
|
|
1904
1844
|
// src/tools.ts
|
|
1905
1845
|
import { readFile as readFile2, stat as stat3 } from "node:fs/promises";
|
|
1906
|
-
import { tool as
|
|
1846
|
+
import { tool as tool3 } from "@opencode-ai/plugin";
|
|
1907
1847
|
|
|
1908
1848
|
// src/constants.ts
|
|
1909
1849
|
var DEFAULT_VISION_TIMEOUT_MS = 30 * 60 * 1000;
|
|
1910
1850
|
var DEFAULT_READ_LIMIT = 2000;
|
|
1911
1851
|
var EDIT_RESULT_READ_LIMIT = 40;
|
|
1912
1852
|
var MAX_PERMISSION_DIFF_BYTES = 32 * 1024;
|
|
1913
|
-
var IMAGE_MODES = [...VISION_MODES];
|
|
1914
1853
|
var VISION_LEVEL_VALUES = [...VISION_LEVELS];
|
|
1915
1854
|
|
|
1916
1855
|
// src/edits.ts
|
|
1917
1856
|
import { lstat, mkdir, open, readFile } from "node:fs/promises";
|
|
1918
|
-
import
|
|
1857
|
+
import path6 from "node:path";
|
|
1919
1858
|
var fileMutationTails = new Map;
|
|
1920
1859
|
async function withFileMutationQueue(filePath, operation) {
|
|
1921
|
-
const key =
|
|
1860
|
+
const key = path6.resolve(filePath);
|
|
1922
1861
|
const previous = fileMutationTails.get(key);
|
|
1923
1862
|
let release = () => {};
|
|
1924
1863
|
const current = new Promise((resolve) => {
|
|
@@ -1973,7 +1912,7 @@ async function writeHandle(handle, content) {
|
|
|
1973
1912
|
}
|
|
1974
1913
|
}
|
|
1975
1914
|
async function writeText(filePath, expected, content) {
|
|
1976
|
-
await mkdir(
|
|
1915
|
+
await mkdir(path6.dirname(filePath), { recursive: true });
|
|
1977
1916
|
if (!expected.exists) {
|
|
1978
1917
|
let handle;
|
|
1979
1918
|
try {
|
|
@@ -2105,20 +2044,20 @@ function simpleDiff(filePath, before, after) {
|
|
|
2105
2044
|
|
|
2106
2045
|
// src/paths.ts
|
|
2107
2046
|
import { lstat as lstat2, readlink, realpath, stat as stat2 } from "node:fs/promises";
|
|
2108
|
-
import
|
|
2047
|
+
import path7 from "node:path";
|
|
2109
2048
|
function resolvePath(directory, filePath) {
|
|
2110
|
-
return
|
|
2049
|
+
return path7.resolve(directory, filePath);
|
|
2111
2050
|
}
|
|
2112
2051
|
async function resolveSymlinks(filePath) {
|
|
2113
|
-
let unresolved =
|
|
2052
|
+
let unresolved = path7.resolve(filePath);
|
|
2114
2053
|
let symlinks = 0;
|
|
2115
2054
|
while (true) {
|
|
2116
|
-
const parsed =
|
|
2117
|
-
const parts = unresolved.slice(parsed.root.length).split(
|
|
2055
|
+
const parsed = path7.parse(unresolved);
|
|
2056
|
+
const parts = unresolved.slice(parsed.root.length).split(path7.sep).filter(Boolean);
|
|
2118
2057
|
let resolved = parsed.root;
|
|
2119
2058
|
let restart = false;
|
|
2120
2059
|
for (let index = 0;index < parts.length; index++) {
|
|
2121
|
-
const candidate =
|
|
2060
|
+
const candidate = path7.join(resolved, parts[index]);
|
|
2122
2061
|
const info = await lstat2(candidate);
|
|
2123
2062
|
if (!info.isSymbolicLink()) {
|
|
2124
2063
|
resolved = candidate;
|
|
@@ -2131,7 +2070,7 @@ async function resolveSymlinks(filePath) {
|
|
|
2131
2070
|
throw error;
|
|
2132
2071
|
}
|
|
2133
2072
|
const target = await readlink(candidate);
|
|
2134
|
-
unresolved =
|
|
2073
|
+
unresolved = path7.resolve(path7.dirname(candidate), target, ...parts.slice(index + 1));
|
|
2135
2074
|
restart = true;
|
|
2136
2075
|
break;
|
|
2137
2076
|
}
|
|
@@ -2153,14 +2092,14 @@ async function canonicalPath(filePath) {
|
|
|
2153
2092
|
let existing = filePath;
|
|
2154
2093
|
while (true) {
|
|
2155
2094
|
try {
|
|
2156
|
-
return
|
|
2095
|
+
return path7.join(await portableRealpath(existing), ...missing.reverse());
|
|
2157
2096
|
} catch (error) {
|
|
2158
2097
|
if (error.code !== "ENOENT")
|
|
2159
2098
|
throw error;
|
|
2160
|
-
const parent =
|
|
2099
|
+
const parent = path7.dirname(existing);
|
|
2161
2100
|
if (parent === existing)
|
|
2162
2101
|
throw error;
|
|
2163
|
-
missing.push(
|
|
2102
|
+
missing.push(path7.basename(existing));
|
|
2164
2103
|
existing = parent;
|
|
2165
2104
|
}
|
|
2166
2105
|
}
|
|
@@ -2177,8 +2116,8 @@ async function authorizeExternal(context, filePath) {
|
|
|
2177
2116
|
const info = await stat2(accessPath).catch(() => {
|
|
2178
2117
|
return;
|
|
2179
2118
|
});
|
|
2180
|
-
const parentDir = info?.isDirectory() ? accessPath :
|
|
2181
|
-
const pattern =
|
|
2119
|
+
const parentDir = info?.isDirectory() ? accessPath : path7.dirname(accessPath);
|
|
2120
|
+
const pattern = path7.join(parentDir, "*").replaceAll("\\", "/");
|
|
2182
2121
|
await context.ask({
|
|
2183
2122
|
permission: "external_directory",
|
|
2184
2123
|
patterns: [pattern],
|
|
@@ -2199,7 +2138,7 @@ async function authorizeRead(context, filePath) {
|
|
|
2199
2138
|
await authorizeExternal(context, filePath);
|
|
2200
2139
|
await context.ask({
|
|
2201
2140
|
permission: "read",
|
|
2202
|
-
patterns: [
|
|
2141
|
+
patterns: [path7.relative(context.worktree, filePath).replaceAll("\\", "/")],
|
|
2203
2142
|
always: ["*"],
|
|
2204
2143
|
metadata: {}
|
|
2205
2144
|
});
|
|
@@ -2218,7 +2157,7 @@ async function authorizeEdit(context, filePaths, diff = "", authorizeExternalPat
|
|
|
2218
2157
|
for (const filePath of filePaths)
|
|
2219
2158
|
await authorizeExternal(context, filePath);
|
|
2220
2159
|
}
|
|
2221
|
-
const patterns = filePaths.map((filePath) =>
|
|
2160
|
+
const patterns = filePaths.map((filePath) => path7.relative(context.worktree, filePath).replaceAll("\\", "/"));
|
|
2222
2161
|
await context.ask({
|
|
2223
2162
|
permission: "edit",
|
|
2224
2163
|
patterns,
|
|
@@ -2228,54 +2167,59 @@ async function authorizeEdit(context, filePaths, diff = "", authorizeExternalPat
|
|
|
2228
2167
|
}
|
|
2229
2168
|
|
|
2230
2169
|
// src/presentation.ts
|
|
2231
|
-
import {
|
|
2170
|
+
import { pathToFileURL } from "node:url";
|
|
2171
|
+
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
2232
2172
|
function initialTitle(kind, args) {
|
|
2233
2173
|
switch (kind) {
|
|
2234
|
-
case "
|
|
2235
|
-
|
|
2236
|
-
case "
|
|
2237
|
-
|
|
2238
|
-
case "
|
|
2239
|
-
|
|
2240
|
-
case "
|
|
2241
|
-
return
|
|
2242
|
-
case "
|
|
2174
|
+
case "show_structure":
|
|
2175
|
+
case "show_metadata":
|
|
2176
|
+
case "check_syntax":
|
|
2177
|
+
case "check_rules":
|
|
2178
|
+
case "generate_caption":
|
|
2179
|
+
case "detect_objects":
|
|
2180
|
+
case "recognize_text":
|
|
2181
|
+
return `${kind.replaceAll("_", " ")} ${args.path}`;
|
|
2182
|
+
case "show_content":
|
|
2183
|
+
return `Show content ${args.path}`;
|
|
2184
|
+
case "show_assets":
|
|
2185
|
+
return `Show assets ${args.path}`;
|
|
2186
|
+
case "find_nodes":
|
|
2187
|
+
return `Find nodes ${args.pattern ?? args.context ?? args.rule}`;
|
|
2188
|
+
case "find_text":
|
|
2189
|
+
return `Find text ${args.pattern ?? args.text}`;
|
|
2190
|
+
case "find_definitions":
|
|
2243
2191
|
return `Find definition ${args.name}`;
|
|
2244
|
-
case "
|
|
2192
|
+
case "find_references":
|
|
2245
2193
|
return `Find references to ${args.name}`;
|
|
2246
|
-
case "
|
|
2247
|
-
return `Rename ${args.path}:${args.
|
|
2248
|
-
case "
|
|
2194
|
+
case "rename_symbol":
|
|
2195
|
+
return `Rename ${args.path}:${args.at} to ${args.to}`;
|
|
2196
|
+
case "edit_text":
|
|
2249
2197
|
return `Edit ${args.path}`;
|
|
2250
|
-
case "
|
|
2198
|
+
case "write_file":
|
|
2251
2199
|
return `Write ${args.path}`;
|
|
2252
2200
|
}
|
|
2253
2201
|
}
|
|
2254
2202
|
function resultPresentation(kind, args, value) {
|
|
2255
2203
|
const output = record(value);
|
|
2256
2204
|
switch (kind) {
|
|
2257
|
-
case "
|
|
2258
|
-
return { title: `
|
|
2259
|
-
case "
|
|
2260
|
-
const
|
|
2261
|
-
|
|
2262
|
-
|
|
2205
|
+
case "show_assets":
|
|
2206
|
+
return { title: `Assets ${args.path}`, metadata: {} };
|
|
2207
|
+
case "show_structure": {
|
|
2208
|
+
const symbols = items(record(output.structure).symbols).length;
|
|
2209
|
+
return { title: `Structure ${args.path} (${symbols} symbols)`, metadata: { symbols } };
|
|
2210
|
+
}
|
|
2211
|
+
case "check_syntax": {
|
|
2212
|
+
const diagnostics = record(output.diagnostics ?? output);
|
|
2213
|
+
const errors = typeof diagnostics.error_count === "number" ? diagnostics.error_count : 0;
|
|
2214
|
+
const missing = typeof diagnostics.missing_count === "number" ? diagnostics.missing_count : 0;
|
|
2215
|
+
return { title: `Syntax ${args.path} (${errors} errors, ${missing} missing)`, metadata: { error_count: errors, missing_count: missing } };
|
|
2216
|
+
}
|
|
2217
|
+
case "show_content": {
|
|
2218
|
+
const identity = record(output.cursor);
|
|
2263
2219
|
const content = record(output.content);
|
|
2264
2220
|
const metadata = {};
|
|
2265
2221
|
const summary = [];
|
|
2266
|
-
if (output.
|
|
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) {
|
|
2222
|
+
if (output.cursor !== undefined) {
|
|
2279
2223
|
if (typeof identity.line === "number")
|
|
2280
2224
|
metadata.line = identity.line;
|
|
2281
2225
|
if (typeof identity.column === "number")
|
|
@@ -2289,29 +2233,32 @@ function resultPresentation(kind, args, value) {
|
|
|
2289
2233
|
if (typeof content.end_line === "number")
|
|
2290
2234
|
metadata.end_line = content.end_line;
|
|
2291
2235
|
return {
|
|
2292
|
-
title: summary.length > 0 ? `
|
|
2236
|
+
title: summary.length > 0 ? `Read ${args.path} (${summary.join(", ")})` : `Read ${args.path}`,
|
|
2293
2237
|
metadata
|
|
2294
2238
|
};
|
|
2295
2239
|
}
|
|
2296
|
-
case "
|
|
2240
|
+
case "find_nodes": {
|
|
2297
2241
|
const results = items(output.results);
|
|
2298
2242
|
const matches = results.reduce((total, result) => total + items(record(result).matches).length, 0);
|
|
2299
2243
|
return { title: `Found ${matches} matches`, metadata: { results: results.length, matches } };
|
|
2300
2244
|
}
|
|
2301
|
-
case "
|
|
2245
|
+
case "find_text": {
|
|
2302
2246
|
const results = items(output.results);
|
|
2303
|
-
const matches = results.reduce((total, result) =>
|
|
2247
|
+
const matches = results.reduce((total, result) => {
|
|
2248
|
+
const entry = record(result);
|
|
2249
|
+
return total + (typeof entry.count === "number" ? entry.count : items(entry.matches).length);
|
|
2250
|
+
}, 0);
|
|
2304
2251
|
return { title: `Found ${matches} matches`, metadata: { results: results.length, matches } };
|
|
2305
2252
|
}
|
|
2306
|
-
case "
|
|
2253
|
+
case "find_definitions": {
|
|
2307
2254
|
const locations = items(output.locations).length;
|
|
2308
2255
|
return { title: `Found ${locations} definitions`, metadata: { locations } };
|
|
2309
2256
|
}
|
|
2310
|
-
case "
|
|
2257
|
+
case "find_references": {
|
|
2311
2258
|
const references = items(output.references).length;
|
|
2312
2259
|
return { title: `Found ${references} references`, metadata: { references } };
|
|
2313
2260
|
}
|
|
2314
|
-
case "
|
|
2261
|
+
case "rename_symbol": {
|
|
2315
2262
|
const oldName = output.old_name;
|
|
2316
2263
|
const newName = output.new_name;
|
|
2317
2264
|
const otherOutputs = items(output.others).map(record);
|
|
@@ -2321,16 +2268,16 @@ function resultPresentation(kind, args, value) {
|
|
|
2321
2268
|
const title = typeof oldName === "string" && typeof newName === "string" ? `${output.applied === true ? "Renamed" : "Plan"} ${oldName} -> ${newName}` : initialTitle(kind, args);
|
|
2322
2269
|
return { title, metadata: { edits, conflicts, others } };
|
|
2323
2270
|
}
|
|
2324
|
-
case "
|
|
2325
|
-
case "
|
|
2271
|
+
case "edit_text":
|
|
2272
|
+
case "write_file": {
|
|
2326
2273
|
const preview = typeof output.preview_error === "string" ? " (preview unavailable)" : "";
|
|
2327
|
-
return { title: `${kind === "
|
|
2274
|
+
return { title: `${kind === "edit_text" ? "Edited" : "Wrote"} ${args.path}${preview}`, metadata: {} };
|
|
2328
2275
|
}
|
|
2329
2276
|
}
|
|
2330
2277
|
return { title: initialTitle(kind, args), metadata: {} };
|
|
2331
2278
|
}
|
|
2332
|
-
function readseekTool(description, args, kind, execute) {
|
|
2333
|
-
return
|
|
2279
|
+
function readseekTool(description, args, kind, execute, imagePolicy) {
|
|
2280
|
+
return tool2({
|
|
2334
2281
|
description,
|
|
2335
2282
|
args,
|
|
2336
2283
|
async execute(args, context) {
|
|
@@ -2338,9 +2285,27 @@ function readseekTool(description, args, kind, execute) {
|
|
|
2338
2285
|
context.metadata({ title });
|
|
2339
2286
|
const result = await execute(args, context);
|
|
2340
2287
|
const presentation = resultPresentation(kind, args, result);
|
|
2288
|
+
const attachments = [];
|
|
2289
|
+
const imagePaths = new Map;
|
|
2290
|
+
const visit = (value) => {
|
|
2291
|
+
if (!value || typeof value !== "object")
|
|
2292
|
+
return;
|
|
2293
|
+
const item = value;
|
|
2294
|
+
if (typeof item.path === "string" && typeof item.mime === "string" && item.mime.startsWith("image/"))
|
|
2295
|
+
imagePaths.set(item.path, item.mime);
|
|
2296
|
+
for (const child of Object.values(item))
|
|
2297
|
+
visit(child);
|
|
2298
|
+
};
|
|
2299
|
+
if (imagePolicy !== undefined)
|
|
2300
|
+
visit(result);
|
|
2301
|
+
for (const [path, mime] of imagePaths) {
|
|
2302
|
+
if (imagePolicy !== "off")
|
|
2303
|
+
attachments.push({ type: "file", mime, url: pathToFileURL(path).href });
|
|
2304
|
+
}
|
|
2341
2305
|
return {
|
|
2306
|
+
...attachments.length > 0 ? { attachments } : {},
|
|
2342
2307
|
title: presentation.title,
|
|
2343
|
-
output: render(result),
|
|
2308
|
+
output: render(imagePolicy === "off" && imagePaths.size > 0 ? { ...record(result), warning: "Image attachments are disabled; use the returned paths to view the images." } : result),
|
|
2344
2309
|
metadata: presentation.metadata
|
|
2345
2310
|
};
|
|
2346
2311
|
}
|
|
@@ -2350,88 +2315,30 @@ function readseekTool(description, args, kind, execute) {
|
|
|
2350
2315
|
// src/tools.ts
|
|
2351
2316
|
function createReadSeekTools(imagePolicy) {
|
|
2352
2317
|
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) => {
|
|
2318
|
+
...createAccessTools(imagePolicy),
|
|
2319
|
+
readseek_find_text: readseekTool(openCodeToolDescription("find_text"), {
|
|
2320
|
+
pattern: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.grepPattern),
|
|
2321
|
+
text: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.text),
|
|
2322
|
+
summary: tool3.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.summary),
|
|
2323
|
+
path: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
2324
|
+
glob: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.glob),
|
|
2325
|
+
literal: tool3.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.literal),
|
|
2326
|
+
ignoreCase: tool3.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.ignoreCase),
|
|
2327
|
+
context: tool3.schema.number().int().nonnegative().optional().describe(PARAM_DESCRIPTIONS.context),
|
|
2328
|
+
limit: tool3.schema.number().int().positive().optional().describe(PARAM_DESCRIPTIONS.matchLimit)
|
|
2329
|
+
}, "find_text", async (input, context) => {
|
|
2429
2330
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
2430
|
-
|
|
2331
|
+
if (input.pattern === undefined === (input.text === undefined)) {
|
|
2332
|
+
throw new Error("exactly one of pattern or text is required");
|
|
2333
|
+
}
|
|
2334
|
+
const pattern = input.pattern ?? input.text;
|
|
2431
2335
|
await authorizeSearch(context, target, pattern);
|
|
2336
|
+
if (input.text !== undefined && !input.glob && !input.ignoreCase && !input.context && !input.summary) {
|
|
2337
|
+
return runReadSeek(context, buildFindTextArgs({ path: target, text: pattern, limit: input.limit }));
|
|
2338
|
+
}
|
|
2432
2339
|
let expression;
|
|
2433
2340
|
try {
|
|
2434
|
-
const source = input.literal ? pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") : pattern;
|
|
2341
|
+
const source = input.literal || input.text !== undefined ? pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") : pattern;
|
|
2435
2342
|
expression = new RegExp(source, input.ignoreCase ? "i" : "");
|
|
2436
2343
|
} catch (error) {
|
|
2437
2344
|
throw new Error(`invalid regular expression: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -2479,6 +2386,10 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2479
2386
|
}
|
|
2480
2387
|
if (ranges.length === 0)
|
|
2481
2388
|
continue;
|
|
2389
|
+
if (input.summary) {
|
|
2390
|
+
results.push({ file: filePath, count: matchLines.size });
|
|
2391
|
+
continue;
|
|
2392
|
+
}
|
|
2482
2393
|
const mergedRanges = [];
|
|
2483
2394
|
for (const [start, end] of ranges) {
|
|
2484
2395
|
const previous = mergedRanges.at(-1);
|
|
@@ -2489,7 +2400,7 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2489
2400
|
}
|
|
2490
2401
|
const linesByNumber = new Map;
|
|
2491
2402
|
for (const [start, end] of mergedRanges) {
|
|
2492
|
-
const output = record(await
|
|
2403
|
+
const output = record(await runContent(context, readRangeArgs(filePath, start, end)));
|
|
2493
2404
|
for (const hashline of items(output.hashlines)) {
|
|
2494
2405
|
const line = record(hashline).line;
|
|
2495
2406
|
if (typeof line === "number" && !linesByNumber.has(line))
|
|
@@ -2505,30 +2416,43 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2505
2416
|
}
|
|
2506
2417
|
return { results, truncated };
|
|
2507
2418
|
}),
|
|
2508
|
-
|
|
2509
|
-
pattern:
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2419
|
+
readseek_find_nodes: readseekTool(openCodeToolDescription("find_nodes"), {
|
|
2420
|
+
pattern: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.pattern),
|
|
2421
|
+
context: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.patternContext),
|
|
2422
|
+
selector: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.selector),
|
|
2423
|
+
rule: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.rule),
|
|
2424
|
+
limit: tool3.schema.number().int().positive().optional(),
|
|
2425
|
+
path: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
2426
|
+
language: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
2427
|
+
gitScope: tool3.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
2428
|
+
}, "find_nodes", async (input, context) => {
|
|
2514
2429
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
2515
|
-
const args =
|
|
2430
|
+
const args = buildFindNodesArgs({
|
|
2516
2431
|
path: target,
|
|
2517
2432
|
pattern: input.pattern,
|
|
2433
|
+
context: input.context,
|
|
2434
|
+
selector: input.selector,
|
|
2435
|
+
rule: input.rule,
|
|
2436
|
+
limit: input.limit,
|
|
2518
2437
|
language: input.language,
|
|
2519
2438
|
gitScope: input.gitScope
|
|
2520
2439
|
});
|
|
2521
|
-
await authorizeSearch(context, target, input.pattern);
|
|
2440
|
+
await authorizeSearch(context, target, input.pattern ?? input.context ?? input.rule);
|
|
2441
|
+
if (input.rule?.startsWith("@")) {
|
|
2442
|
+
await authorizeRead(context, resolvePath(context.directory, input.rule.slice(1)));
|
|
2443
|
+
}
|
|
2444
|
+
if (input.rule === "-")
|
|
2445
|
+
throw new Error("rule stdin is unavailable in OpenCode tools; use inline KDL or @path");
|
|
2522
2446
|
return runReadSeek(context, args);
|
|
2523
2447
|
}),
|
|
2524
|
-
|
|
2525
|
-
name:
|
|
2526
|
-
path:
|
|
2527
|
-
language:
|
|
2528
|
-
gitScope:
|
|
2529
|
-
}, "
|
|
2448
|
+
readseek_find_definitions: readseekTool(openCodeToolDescription("find_definitions"), {
|
|
2449
|
+
name: tool3.schema.string().describe(PARAM_DESCRIPTIONS.name),
|
|
2450
|
+
path: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
2451
|
+
language: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
2452
|
+
gitScope: tool3.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
2453
|
+
}, "find_definitions", async (input, context) => {
|
|
2530
2454
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
2531
|
-
const args =
|
|
2455
|
+
const args = buildFindDefinitionsArgs({
|
|
2532
2456
|
path: target,
|
|
2533
2457
|
name: input.name,
|
|
2534
2458
|
language: input.language,
|
|
@@ -2537,58 +2461,56 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2537
2461
|
await authorizeSearch(context, target, input.name);
|
|
2538
2462
|
return runReadSeek(context, args);
|
|
2539
2463
|
}),
|
|
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) => {
|
|
2464
|
+
readseek_find_references: readseekTool(openCodeToolDescription("find_references"), {
|
|
2465
|
+
name: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.refsName),
|
|
2466
|
+
path: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.projectSearchPath),
|
|
2467
|
+
language: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
2468
|
+
at: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.at),
|
|
2469
|
+
gitScope: tool3.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
2470
|
+
}, "find_references", async (input, context) => {
|
|
2549
2471
|
const target = resolvePath(context.directory, input.path ?? ".");
|
|
2550
2472
|
const name = input.name;
|
|
2551
|
-
const args =
|
|
2473
|
+
const args = buildFindReferencesArgs({
|
|
2552
2474
|
path: target,
|
|
2553
2475
|
name,
|
|
2554
|
-
|
|
2555
|
-
line: input.line,
|
|
2556
|
-
column: input.column,
|
|
2476
|
+
at: input.at,
|
|
2557
2477
|
language: input.language,
|
|
2558
2478
|
gitScope: input.gitScope
|
|
2559
2479
|
});
|
|
2560
2480
|
await authorizeSearch(context, target, name ?? "*");
|
|
2561
2481
|
return runReadSeek(context, args);
|
|
2562
2482
|
}),
|
|
2563
|
-
|
|
2564
|
-
path:
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2483
|
+
readseek_rename_symbol: readseekTool(openCodeToolDescription("rename_symbol"), {
|
|
2484
|
+
path: tool3.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
2485
|
+
at: tool3.schema.string().describe(PARAM_DESCRIPTIONS.at),
|
|
2486
|
+
to: tool3.schema.string().min(1).describe(PARAM_DESCRIPTIONS.to),
|
|
2487
|
+
workspace: tool3.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.workspace),
|
|
2488
|
+
apply: tool3.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.applyDefaultTrue),
|
|
2489
|
+
planHash: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.planHash),
|
|
2490
|
+
language: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.language),
|
|
2491
|
+
gitScope: tool3.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
2492
|
+
}, "rename_symbol", async (input, context) => {
|
|
2571
2493
|
const filePath = resolvePath(context.directory, input.path);
|
|
2572
2494
|
await authorizeRead(context, filePath);
|
|
2573
2495
|
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"));
|
|
2496
|
+
const envelope = await runReadSeek(context, contentArgs(filePath, { at: input.at }));
|
|
2497
|
+
const name = identifiedName(record(envelope).cursor);
|
|
2580
2498
|
if (!name)
|
|
2581
2499
|
throw new Error("readseek could not identify a binding at the rename cursor");
|
|
2582
2500
|
await authorizeSearch(context, context.directory, name);
|
|
2583
2501
|
}
|
|
2584
|
-
const args =
|
|
2502
|
+
const args = buildRenameSymbolArgs({
|
|
2585
2503
|
path: filePath,
|
|
2586
2504
|
to: input.to,
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2505
|
+
at: input.at,
|
|
2506
|
+
workspace: input.workspace ? context.directory : undefined,
|
|
2507
|
+
language: input.language,
|
|
2508
|
+
gitScope: input.gitScope
|
|
2590
2509
|
});
|
|
2591
2510
|
const plan = await runReadSeek(context, args);
|
|
2511
|
+
if (input.planHash !== undefined && record(plan).plan_hash !== input.planHash) {
|
|
2512
|
+
throw new Error("readseek rename plan does not match planHash");
|
|
2513
|
+
}
|
|
2592
2514
|
if (input.apply === false)
|
|
2593
2515
|
return plan;
|
|
2594
2516
|
const files = renameFiles(plan, context.directory);
|
|
@@ -2597,57 +2519,61 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2597
2519
|
throw new Error("readseek rename plan did not include a plan hash");
|
|
2598
2520
|
}
|
|
2599
2521
|
await authorizeEdit(context, files);
|
|
2600
|
-
return runReadSeek(context,
|
|
2522
|
+
return runReadSeek(context, buildRenameSymbolArgs({
|
|
2601
2523
|
path: filePath,
|
|
2602
2524
|
to: input.to,
|
|
2603
|
-
|
|
2604
|
-
column: input.column,
|
|
2525
|
+
at: input.at,
|
|
2605
2526
|
workspace: input.workspace ? context.directory : undefined,
|
|
2606
2527
|
apply: true,
|
|
2607
|
-
planHash
|
|
2528
|
+
planHash,
|
|
2529
|
+
language: input.language,
|
|
2530
|
+
gitScope: input.gitScope
|
|
2608
2531
|
}), {
|
|
2609
2532
|
cancelable: false,
|
|
2610
2533
|
timeoutMs: DEFAULT_READSEEK_TIMEOUT_MS
|
|
2611
2534
|
});
|
|
2612
2535
|
}),
|
|
2613
|
-
|
|
2614
|
-
path:
|
|
2615
|
-
edits:
|
|
2616
|
-
|
|
2617
|
-
set_line:
|
|
2618
|
-
anchor:
|
|
2619
|
-
new_text:
|
|
2536
|
+
readseek_edit_text: readseekTool(openCodeToolDescription("edit_text"), {
|
|
2537
|
+
path: tool3.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
2538
|
+
edits: tool3.schema.array(tool3.schema.union([
|
|
2539
|
+
tool3.schema.object({
|
|
2540
|
+
set_line: tool3.schema.object({
|
|
2541
|
+
anchor: tool3.schema.string().describe(PARAM_DESCRIPTIONS.anchor),
|
|
2542
|
+
new_text: tool3.schema.string().describe(PARAM_DESCRIPTIONS.newTextLine)
|
|
2620
2543
|
}).strict()
|
|
2621
2544
|
}).strict(),
|
|
2622
|
-
|
|
2623
|
-
replace_lines:
|
|
2624
|
-
start_anchor:
|
|
2625
|
-
end_anchor:
|
|
2626
|
-
new_text:
|
|
2545
|
+
tool3.schema.object({
|
|
2546
|
+
replace_lines: tool3.schema.object({
|
|
2547
|
+
start_anchor: tool3.schema.string().describe(PARAM_DESCRIPTIONS.startAnchor),
|
|
2548
|
+
end_anchor: tool3.schema.string().describe(PARAM_DESCRIPTIONS.endAnchor),
|
|
2549
|
+
new_text: tool3.schema.string().describe(PARAM_DESCRIPTIONS.newTextRange)
|
|
2627
2550
|
}).strict()
|
|
2628
2551
|
}).strict(),
|
|
2629
|
-
|
|
2630
|
-
insert_after:
|
|
2631
|
-
anchor:
|
|
2632
|
-
new_text:
|
|
2552
|
+
tool3.schema.object({
|
|
2553
|
+
insert_after: tool3.schema.object({
|
|
2554
|
+
anchor: tool3.schema.string().describe(PARAM_DESCRIPTIONS.insertAnchor),
|
|
2555
|
+
new_text: tool3.schema.string().describe(PARAM_DESCRIPTIONS.newTextInsert)
|
|
2633
2556
|
}).strict()
|
|
2634
2557
|
}).strict(),
|
|
2635
|
-
|
|
2636
|
-
replace:
|
|
2637
|
-
old_text:
|
|
2638
|
-
new_text:
|
|
2639
|
-
all:
|
|
2558
|
+
tool3.schema.object({
|
|
2559
|
+
replace: tool3.schema.object({
|
|
2560
|
+
old_text: tool3.schema.string().describe(PARAM_DESCRIPTIONS.oldText),
|
|
2561
|
+
new_text: tool3.schema.string().describe(PARAM_DESCRIPTIONS.newText),
|
|
2562
|
+
all: tool3.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.all)
|
|
2640
2563
|
}).strict()
|
|
2641
2564
|
}).strict(),
|
|
2642
|
-
|
|
2643
|
-
replace_symbol:
|
|
2644
|
-
symbol:
|
|
2645
|
-
new_body:
|
|
2565
|
+
tool3.schema.object({
|
|
2566
|
+
replace_symbol: tool3.schema.object({
|
|
2567
|
+
symbol: tool3.schema.string().describe(PARAM_DESCRIPTIONS.symbol),
|
|
2568
|
+
new_body: tool3.schema.string().describe(PARAM_DESCRIPTIONS.newBody)
|
|
2646
2569
|
}).strict()
|
|
2647
2570
|
}).strict()
|
|
2648
2571
|
])).min(1).describe(PARAM_DESCRIPTIONS.edits),
|
|
2649
|
-
language:
|
|
2650
|
-
|
|
2572
|
+
language: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.languageSymbol),
|
|
2573
|
+
apply: tool3.schema.boolean().optional().describe(PARAM_DESCRIPTIONS.applyDefaultTrue),
|
|
2574
|
+
planHash: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.planHash),
|
|
2575
|
+
gitScope: tool3.schema.enum(GIT_SCOPES).optional().describe(PARAM_DESCRIPTIONS.gitScope)
|
|
2576
|
+
}, "edit_text", async (input, context) => {
|
|
2651
2577
|
const filePath = resolvePath(context.directory, input.path);
|
|
2652
2578
|
validateEdits(input.edits);
|
|
2653
2579
|
return withFileMutationQueue(filePath, async () => {
|
|
@@ -2656,15 +2582,21 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2656
2582
|
await rejectSymlinkMutation(filePath);
|
|
2657
2583
|
const edits = input.edits;
|
|
2658
2584
|
const request = serializeEditPlan(edits);
|
|
2659
|
-
const baseArgs =
|
|
2585
|
+
const baseArgs = buildEditTextArgs({
|
|
2660
2586
|
path: filePath,
|
|
2661
2587
|
language: input.language,
|
|
2588
|
+
gitScope: input.gitScope,
|
|
2662
2589
|
requestFromStdin: true
|
|
2663
2590
|
});
|
|
2664
2591
|
const plan = record(await runReadSeek(context, baseArgs, { stdin: request }));
|
|
2665
2592
|
if (typeof plan.before_content !== "string" || typeof plan.content !== "string" || typeof plan.plan_hash !== "string") {
|
|
2666
2593
|
throw new Error("readseek edit plan is missing before_content, content, or plan_hash");
|
|
2667
2594
|
}
|
|
2595
|
+
if (input.planHash !== undefined && plan.plan_hash !== input.planHash) {
|
|
2596
|
+
throw new Error("readseek edit plan does not match planHash");
|
|
2597
|
+
}
|
|
2598
|
+
if (input.apply === false)
|
|
2599
|
+
return plan;
|
|
2668
2600
|
const before = plan.before_content;
|
|
2669
2601
|
const after = plan.content;
|
|
2670
2602
|
const previewStart = Math.max(1, firstChangedLine(before, after) - 3);
|
|
@@ -2674,9 +2606,10 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2674
2606
|
}
|
|
2675
2607
|
await authorizeEdit(context, [filePath], simpleDiff(filePath, before, after), false);
|
|
2676
2608
|
context.abort.throwIfAborted();
|
|
2677
|
-
const applied = record(await runReadSeek(context,
|
|
2609
|
+
const applied = record(await runReadSeek(context, buildEditTextArgs({
|
|
2678
2610
|
path: filePath,
|
|
2679
2611
|
language: input.language,
|
|
2612
|
+
gitScope: input.gitScope,
|
|
2680
2613
|
requestFromStdin: true,
|
|
2681
2614
|
apply: true,
|
|
2682
2615
|
planHash: plan.plan_hash
|
|
@@ -2691,10 +2624,10 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2691
2624
|
return postMutationPreview(context, previewArgs, filePath, true);
|
|
2692
2625
|
});
|
|
2693
2626
|
}),
|
|
2694
|
-
|
|
2695
|
-
path:
|
|
2696
|
-
content:
|
|
2697
|
-
}, "
|
|
2627
|
+
readseek_write_file: readseekTool(openCodeToolDescription("write_file"), {
|
|
2628
|
+
path: tool3.schema.string().describe(PARAM_DESCRIPTIONS.projectPath),
|
|
2629
|
+
content: tool3.schema.string().describe(PARAM_DESCRIPTIONS.content)
|
|
2630
|
+
}, "write_file", async (input, context) => {
|
|
2698
2631
|
const filePath = resolvePath(context.directory, input.path);
|
|
2699
2632
|
await authorizeExternal(context, filePath);
|
|
2700
2633
|
return withFileMutationQueue(filePath, async () => {
|
|
@@ -2707,11 +2640,50 @@ function createReadSeekTools(imagePolicy) {
|
|
|
2707
2640
|
await authorizeEdit(context, [filePath], simpleDiff(filePath, before.content, content), false);
|
|
2708
2641
|
context.abort.throwIfAborted();
|
|
2709
2642
|
await writeText(filePath, before, content);
|
|
2710
|
-
return postMutationPreview(context,
|
|
2643
|
+
return postMutationPreview(context, contentArgs(filePath, { end: DEFAULT_READ_LIMIT }), filePath, !before.exists || before.content !== content);
|
|
2711
2644
|
});
|
|
2712
2645
|
})
|
|
2713
2646
|
};
|
|
2714
2647
|
}
|
|
2648
|
+
function createAccessTools(imagePolicy) {
|
|
2649
|
+
const pathSchema = { path: tool3.schema.string().describe(PARAM_DESCRIPTIONS.projectPath) };
|
|
2650
|
+
const atSchema = { at: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.at) };
|
|
2651
|
+
const languageSchema = { language: tool3.schema.string().optional().describe(PARAM_DESCRIPTIONS.language) };
|
|
2652
|
+
const levelSchema = { level: tool3.schema.enum(VISION_LEVEL_VALUES).optional().describe(PARAM_DESCRIPTIONS.level) };
|
|
2653
|
+
const definitions = {
|
|
2654
|
+
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() } },
|
|
2655
|
+
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() } },
|
|
2656
|
+
show_metadata: { build: buildShowMetadataArgs, args: { ...pathSchema, ...languageSchema } },
|
|
2657
|
+
show_assets: { build: buildShowAssetsArgs, args: { ...pathSchema, ...atSchema } },
|
|
2658
|
+
check_syntax: { build: buildCheckSyntaxArgs, args: { ...pathSchema, ...languageSchema } },
|
|
2659
|
+
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() } },
|
|
2660
|
+
generate_caption: { build: buildGenerateCaptionArgs, args: { ...pathSchema, ...atSchema, ...levelSchema, include: tool3.schema.array(tool3.schema.enum(["text", "objects"])).min(1).optional() } },
|
|
2661
|
+
detect_objects: { build: buildDetectObjectsArgs, args: { ...pathSchema, ...atSchema, ...levelSchema } },
|
|
2662
|
+
recognize_text: { build: buildRecognizeTextArgs, args: { ...pathSchema, ...atSchema, ...levelSchema } }
|
|
2663
|
+
};
|
|
2664
|
+
return Object.fromEntries(Object.entries(definitions).map(([name, definition]) => [
|
|
2665
|
+
`readseek_${name}`,
|
|
2666
|
+
readseekTool(openCodeToolDescription(name), definition.args, name, async (input, context) => {
|
|
2667
|
+
const filePath = resolvePath(context.directory, input.path ?? ".");
|
|
2668
|
+
const buildInput = { ...input, path: filePath, ...name === "show_structure" ? { depth: input.depth ?? DEFAULT_STRUCTURE_DEPTH } : {} };
|
|
2669
|
+
let args = definition.build(buildInput);
|
|
2670
|
+
await authorizeRead(context, filePath);
|
|
2671
|
+
if (name === "check_rules") {
|
|
2672
|
+
if (input.rules === "-")
|
|
2673
|
+
throw new Error("rules stdin is unavailable in OpenCode tools; use inline KDL or @path");
|
|
2674
|
+
await authorizeSearch(context, filePath, input.rules);
|
|
2675
|
+
if (input.rules.startsWith("@"))
|
|
2676
|
+
await authorizeRead(context, resolvePath(context.directory, input.rules.slice(1)));
|
|
2677
|
+
}
|
|
2678
|
+
const inference = ["generate_caption", "detect_objects", "recognize_text"].includes(name);
|
|
2679
|
+
if (name === "show_content" || name === "show_assets" || inference) {
|
|
2680
|
+
const readseekDir = await ensureReadSeekCache(context);
|
|
2681
|
+
args = definition.build({ ...buildInput, readseekDir });
|
|
2682
|
+
}
|
|
2683
|
+
return runReadSeek(context, args, { timeoutMs: inference ? DEFAULT_VISION_TIMEOUT_MS : DEFAULT_READSEEK_TIMEOUT_MS });
|
|
2684
|
+
}, name === "show_content" || name === "show_assets" ? imagePolicy : undefined)
|
|
2685
|
+
]));
|
|
2686
|
+
}
|
|
2715
2687
|
|
|
2716
2688
|
// v2.ts
|
|
2717
2689
|
var GUIDANCE = "For structural code navigation, consult the readseek skill when ReadSeek tools are unavailable and the readseek CLI is installed.";
|
|
@@ -2724,6 +2696,29 @@ var SKILL = {
|
|
|
2724
2696
|
|
|
2725
2697
|
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
2698
|
|
|
2699
|
+
Commands use verb-object pairs, with the target path positional:
|
|
2700
|
+
|
|
2701
|
+
\`\`\`sh
|
|
2702
|
+
readseek show content file.ts --at line:10 --limit 40 --kdl
|
|
2703
|
+
readseek show structure file.ts --depth 1 --kdl
|
|
2704
|
+
readseek show metadata paper.pdf --kdl
|
|
2705
|
+
readseek show assets paper.pdf --at page:2 --kdl
|
|
2706
|
+
readseek check syntax file.ts --kdl
|
|
2707
|
+
readseek check rules . --rules @checks.kdl --kdl
|
|
2708
|
+
readseek find nodes src --pattern 'console.log($$$ARGS)' --kdl
|
|
2709
|
+
readseek find text src --text 'TODO' --kdl
|
|
2710
|
+
readseek find definitions src --name main --kdl
|
|
2711
|
+
readseek find references file.ts --at line:10:3 --kdl
|
|
2712
|
+
readseek edit text file.ts --edits @edits.kdl --kdl
|
|
2713
|
+
readseek rename symbol file.ts --at line:10:3 --to nextName --kdl
|
|
2714
|
+
\`\`\`
|
|
2715
|
+
|
|
2716
|
+
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"\`.
|
|
2717
|
+
|
|
2718
|
+
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.
|
|
2719
|
+
|
|
2720
|
+
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.
|
|
2721
|
+
|
|
2727
2722
|
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
2723
|
};
|
|
2729
2724
|
var ReadSeekV2Plugin = {
|
|
@@ -2779,7 +2774,7 @@ var ReadSeekPlugin = async (_input, options) => {
|
|
|
2779
2774
|
return;
|
|
2780
2775
|
try {
|
|
2781
2776
|
const result = JSON.parse(output.output);
|
|
2782
|
-
if (input.tool === "
|
|
2777
|
+
if (input.tool === "readseek_rename_symbol") {
|
|
2783
2778
|
anchors.planRename(input.sessionID, result);
|
|
2784
2779
|
if (record(result).applied === true) {
|
|
2785
2780
|
for (const filePath of renameFiles(result))
|
|
@@ -2789,7 +2784,7 @@ var ReadSeekPlugin = async (_input, options) => {
|
|
|
2789
2784
|
const files = new Set;
|
|
2790
2785
|
collectAnchoredFiles(result, files);
|
|
2791
2786
|
for (const filePath of files)
|
|
2792
|
-
anchors.mark(input.sessionID,
|
|
2787
|
+
anchors.mark(input.sessionID, path8.resolve(filePath));
|
|
2793
2788
|
} catch {}
|
|
2794
2789
|
},
|
|
2795
2790
|
"experimental.session.compacting": async (input, output) => {
|