pi-readseek 0.10.7 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -4
- package/dist/index.ts +1807 -1960
- package/package.json +3 -3
- package/prompts/{edit.md → edit_text.md} +2 -2
- package/prompts/{def.md → find_definitions.md} +1 -1
- package/prompts/{search.md → find_nodes.md} +12 -1
- package/prompts/{extract.md → show_assets.md} +1 -1
- package/prompts/{inspect.md → show_content.md} +1 -1
- /package/prompts/{refs.md → find_references.md} +0 -0
- /package/prompts/{grep.md → find_text.md} +0 -0
- /package/prompts/{rename.md → rename_symbol.md} +0 -0
- /package/prompts/{write.md → write_file.md} +0 -0
package/dist/index.ts
CHANGED
|
@@ -6,14 +6,6 @@ import { Type as Type3 } from "@sinclair/typebox";
|
|
|
6
6
|
import { Text as Text3 } from "@earendil-works/pi-tui";
|
|
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,244 +193,84 @@ 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
|
];
|
|
227
|
+
function getToolContract(name) {
|
|
228
|
+
const contract = TOOL_CONTRACTS.find((entry) => entry.name === name);
|
|
229
|
+
if (!contract)
|
|
230
|
+
throw new Error(`missing tool contract: ${name}`);
|
|
231
|
+
return contract;
|
|
232
|
+
}
|
|
388
233
|
// ../readseek-api/src/policy.ts
|
|
389
234
|
var REPLACEABLE_TOOLS = [
|
|
390
235
|
{
|
|
391
|
-
tool: "
|
|
236
|
+
tool: "show_content",
|
|
392
237
|
builtIn: "read",
|
|
393
238
|
benefit: "it returns LINE:HASH anchors for safe edits."
|
|
394
239
|
},
|
|
395
240
|
{
|
|
396
|
-
tool: "
|
|
241
|
+
tool: "edit_text",
|
|
397
242
|
builtIn: "edit",
|
|
398
243
|
benefit: "it verifies fresh LINE:HASH anchors."
|
|
399
244
|
},
|
|
400
245
|
{
|
|
401
|
-
tool: "
|
|
246
|
+
tool: "find_text",
|
|
402
247
|
builtIn: "grep",
|
|
403
248
|
benefit: "it returns LINE:HASH anchors."
|
|
404
249
|
},
|
|
405
250
|
{
|
|
406
|
-
tool: "
|
|
251
|
+
tool: "write_file",
|
|
407
252
|
builtIn: "write",
|
|
408
253
|
benefit: "it returns LINE:HASH anchors."
|
|
409
254
|
}
|
|
410
255
|
];
|
|
411
256
|
var TOOL_COMPACT_GUIDELINES = {
|
|
412
|
-
|
|
413
|
-
"Use `
|
|
257
|
+
show_content: [
|
|
258
|
+
"Use `show_structure` for shallow overviews and `check_syntax` for diagnostics; use `find_definitions`, `find_references`, or `find_nodes` for targeted symbol questions."
|
|
414
259
|
],
|
|
415
|
-
|
|
260
|
+
edit_text: [
|
|
416
261
|
"Prefer set_line, replace_lines, and insert_after; use replace only when anchors are impractical."
|
|
417
262
|
],
|
|
418
|
-
|
|
419
|
-
"Use `
|
|
263
|
+
find_text: [
|
|
264
|
+
"Use `find_text` with summary first for broad searches, then narrow by path, glob, or pattern."
|
|
420
265
|
],
|
|
421
|
-
|
|
422
|
-
"Use anchored edits rather than `
|
|
266
|
+
write_file: [
|
|
267
|
+
"Use anchored edits rather than `write_file` for small changes or appends."
|
|
423
268
|
],
|
|
424
|
-
|
|
425
|
-
"Use `
|
|
269
|
+
find_nodes: [
|
|
270
|
+
"Use `find_nodes` for AST patterns; use `find_text` for plain text."
|
|
426
271
|
],
|
|
427
|
-
|
|
428
|
-
"Use `
|
|
272
|
+
find_references: [
|
|
273
|
+
"Use `find_references` before changing a symbol; use at with line:N:C to follow one binding."
|
|
429
274
|
]
|
|
430
275
|
};
|
|
431
276
|
function resolveToolName(name, aliases = {}) {
|
|
@@ -439,20 +284,20 @@ function renderCompactGuidelines(name, aliases = {}) {
|
|
|
439
284
|
}
|
|
440
285
|
function renderToolRoutingPolicy(options = {}) {
|
|
441
286
|
const aliases = options.aliases ?? {};
|
|
442
|
-
const
|
|
443
|
-
const
|
|
444
|
-
const
|
|
445
|
-
const
|
|
446
|
-
const
|
|
447
|
-
const
|
|
448
|
-
const
|
|
449
|
-
const
|
|
450
|
-
const
|
|
287
|
+
const show_content = resolveToolName("show_content", aliases);
|
|
288
|
+
const edit_text = resolveToolName("edit_text", aliases);
|
|
289
|
+
const write_file = resolveToolName("write_file", aliases);
|
|
290
|
+
const find_text = resolveToolName("find_text", aliases);
|
|
291
|
+
const find_nodes = resolveToolName("find_nodes", aliases);
|
|
292
|
+
const find_definitions = resolveToolName("find_definitions", aliases);
|
|
293
|
+
const find_references = resolveToolName("find_references", aliases);
|
|
294
|
+
const rename_symbol = resolveToolName("rename_symbol", aliases);
|
|
295
|
+
const show_assets = resolveToolName("show_assets", aliases);
|
|
451
296
|
if (options.style === "server") {
|
|
452
297
|
return [
|
|
453
298
|
"Structural code navigation, document inspection, and hash-verified edits with readseek.",
|
|
454
|
-
"For structural questions, prefer these tools to text Grep/Bash: targeted definitions, references, and AST
|
|
455
|
-
`Use ${
|
|
299
|
+
"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.",
|
|
300
|
+
`Use ${edit_text} for LINE:HASH-anchored mutations (preview with apply=false; apply defaults to true and re-checks plan_hash).`,
|
|
456
301
|
"Paths resolve against CLAUDE_PROJECT_DIR. Native Read/Grep/Edit hooks provide passive context;",
|
|
457
302
|
"call MCP tools for active navigation, document questions, or anchored edits."
|
|
458
303
|
].join(" ");
|
|
@@ -462,7 +307,7 @@ function renderToolRoutingPolicy(options = {}) {
|
|
|
462
307
|
if (options.preferOverBuiltIns) {
|
|
463
308
|
bullets.push("- Prefer ReadSeek tools over built-ins when they can do the job.");
|
|
464
309
|
}
|
|
465
|
-
bullets.push(`- Read with ${
|
|
310
|
+
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.`);
|
|
466
311
|
if (options.demoteBuiltIns && options.demoteBuiltIns.length > 0) {
|
|
467
312
|
bullets.push(`- Do not use built-in ${options.demoteBuiltIns.join(", ")} when a ReadSeek tool can do the job.`);
|
|
468
313
|
}
|
|
@@ -489,25 +334,9 @@ function tryParseLineAnchor(anchor) {
|
|
|
489
334
|
}
|
|
490
335
|
|
|
491
336
|
// ../readseek-api/src/validation.ts
|
|
492
|
-
function
|
|
493
|
-
if (input.
|
|
494
|
-
|
|
495
|
-
return { ok: false, error: "scope requires line" };
|
|
496
|
-
}
|
|
497
|
-
return { ok: true };
|
|
498
|
-
}
|
|
499
|
-
if (input.line !== undefined || input.column !== undefined) {
|
|
500
|
-
return { ok: false, error: "line/column require scope=true" };
|
|
501
|
-
}
|
|
502
|
-
if (typeof input.name !== "string" || input.name.length === 0) {
|
|
503
|
-
return { ok: false, error: "name is required when scope is not set" };
|
|
504
|
-
}
|
|
505
|
-
return { ok: true };
|
|
506
|
-
}
|
|
507
|
-
function validateVisionLevelVsMode(input) {
|
|
508
|
-
if (input.visionLevel !== undefined && !["all", "ocr", "caption", "objects"].includes(input.visionMode ?? "")) {
|
|
509
|
-
return { ok: false, error: "visionLevel requires an analysis visionMode" };
|
|
510
|
-
}
|
|
337
|
+
function validateReferencesSelection(input) {
|
|
338
|
+
if (Boolean(input.name) === Boolean(input.at))
|
|
339
|
+
return { ok: false, error: "exactly one of name or at is required" };
|
|
511
340
|
return { ok: true };
|
|
512
341
|
}
|
|
513
342
|
var VARIANT_FIELDS = {
|
|
@@ -577,51 +406,8 @@ function validateExactlyOneEditVariant(edits) {
|
|
|
577
406
|
}
|
|
578
407
|
var MIN_SAFE_INTEGER = BigInt(Number.MIN_SAFE_INTEGER);
|
|
579
408
|
var MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
|
|
580
|
-
// ../readseek-api/src/edit-plan.ts
|
|
581
|
-
import { Document, Node, format } from "@bgotink/kdl";
|
|
582
|
-
function node(name, properties, children = []) {
|
|
583
|
-
const result = Node.create(name);
|
|
584
|
-
for (const [key, value] of Object.entries(properties))
|
|
585
|
-
result.setProperty(key, value);
|
|
586
|
-
if (children.length > 0)
|
|
587
|
-
result.children = new Document(children);
|
|
588
|
-
return result;
|
|
589
|
-
}
|
|
590
|
-
function editNode(edit) {
|
|
591
|
-
if ("set_line" in edit) {
|
|
592
|
-
const value = edit.set_line;
|
|
593
|
-
return node("set-line", { anchor: value.anchor, text: value.new_text });
|
|
594
|
-
}
|
|
595
|
-
if ("replace_lines" in edit) {
|
|
596
|
-
const value = edit.replace_lines;
|
|
597
|
-
return node("replace-lines", { start: value.start_anchor, end: value.end_anchor, text: value.new_text });
|
|
598
|
-
}
|
|
599
|
-
if ("insert_after" in edit) {
|
|
600
|
-
const value = edit.insert_after;
|
|
601
|
-
return node("insert-after", { anchor: value.anchor, text: value.new_text });
|
|
602
|
-
}
|
|
603
|
-
if ("replace_symbol" in edit) {
|
|
604
|
-
const value = edit.replace_symbol;
|
|
605
|
-
return node("replace-symbol", { symbol: value.symbol, body: value.new_body });
|
|
606
|
-
}
|
|
607
|
-
const value = edit.replace;
|
|
608
|
-
return node("replace", {
|
|
609
|
-
old: value.old_text,
|
|
610
|
-
new: value.new_text,
|
|
611
|
-
...value.all === undefined ? {} : { all: value.all }
|
|
612
|
-
});
|
|
613
|
-
}
|
|
614
|
-
function serializeEditPlan(edits) {
|
|
615
|
-
const validation = validateExactlyOneEditVariant(edits);
|
|
616
|
-
if (!validation.ok)
|
|
617
|
-
throw new Error(validation.error);
|
|
618
|
-
return format(new Document([node("plan", {}, validation.edits.map(editNode))]));
|
|
619
|
-
}
|
|
620
409
|
// ../readseek-api/src/argv.ts
|
|
621
|
-
var
|
|
622
|
-
function buildLineLocation(line, column) {
|
|
623
|
-
return column === undefined ? `line:${line}` : `line:${line}:${column}`;
|
|
624
|
-
}
|
|
410
|
+
var DEFAULT_STRUCTURE_DEPTH = 1;
|
|
625
411
|
function appendGitScopeArgs(args, input) {
|
|
626
412
|
if (input.gitScope !== undefined)
|
|
627
413
|
args.push("--git-scope", input.gitScope);
|
|
@@ -648,30 +434,6 @@ function appendApplyAndPlanHash(args, input) {
|
|
|
648
434
|
pushOptionalString(args, "--plan-hash", input.planHash);
|
|
649
435
|
return args;
|
|
650
436
|
}
|
|
651
|
-
function selectArg(select) {
|
|
652
|
-
if (select === undefined)
|
|
653
|
-
return;
|
|
654
|
-
return Array.isArray(select) ? select.join(",") : select;
|
|
655
|
-
}
|
|
656
|
-
function selectedInspectFacets(select) {
|
|
657
|
-
const facets = (selectArg(select) ?? "content").split(",").map((facet) => facet.trim());
|
|
658
|
-
for (const facet of facets) {
|
|
659
|
-
if (!INSPECT_FACETS.includes(facet)) {
|
|
660
|
-
throw new Error(`unknown inspect facet: ${facet}`);
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
return facets;
|
|
664
|
-
}
|
|
665
|
-
function hasInspectFacet(facets, facet) {
|
|
666
|
-
return facets.includes(facet);
|
|
667
|
-
}
|
|
668
|
-
function validateTarget(command, target, stdinName) {
|
|
669
|
-
if (target.length === 0)
|
|
670
|
-
throw new Error(`${command} requires a target file path or '-'`);
|
|
671
|
-
if (stdinName !== undefined && stdinName.length > 0 && target !== "-") {
|
|
672
|
-
throw new Error("stdinName requires '-' as the target");
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
437
|
function validatePositiveInteger(value, name) {
|
|
676
438
|
if (!Number.isSafeInteger(value) || value < 1) {
|
|
677
439
|
throw new Error(`${name} must be a positive integer`);
|
|
@@ -717,1226 +479,1515 @@ function parseSelector(text) {
|
|
|
717
479
|
}
|
|
718
480
|
return { kind, value };
|
|
719
481
|
}
|
|
720
|
-
function
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
482
|
+
function start(verb, object, input) {
|
|
483
|
+
if (!input.path)
|
|
484
|
+
throw new Error(`${verb} ${object} requires a path`);
|
|
485
|
+
if (input.stdinName !== undefined && input.path !== "-")
|
|
486
|
+
throw new Error("stdinName requires '-' as the target");
|
|
487
|
+
const args = [];
|
|
488
|
+
pushOptionalString(args, "--readseek-dir", input.readseekDir);
|
|
489
|
+
args.push(verb, object, input.path);
|
|
490
|
+
pushOptionalString(args, "--stdin-name", input.stdinName);
|
|
491
|
+
return args;
|
|
492
|
+
}
|
|
493
|
+
function at2(args, value) {
|
|
494
|
+
if (value !== undefined) {
|
|
495
|
+
parseSelector(value);
|
|
496
|
+
args.push("--at", value);
|
|
724
497
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
498
|
+
}
|
|
499
|
+
function finish(args) {
|
|
500
|
+
args.push("--kdl");
|
|
501
|
+
return args;
|
|
502
|
+
}
|
|
503
|
+
function language(args, input) {
|
|
504
|
+
pushOptionalString(args, "--language", input.language);
|
|
505
|
+
}
|
|
506
|
+
function limit2(args, value) {
|
|
507
|
+
if (value !== undefined)
|
|
508
|
+
validatePositiveInteger(value, "limit");
|
|
509
|
+
pushOptionalNumber(args, "--limit", value);
|
|
510
|
+
}
|
|
511
|
+
function anchors(args, input) {
|
|
512
|
+
if (input.noAnchors)
|
|
513
|
+
args.push("--no-anchors");
|
|
514
|
+
}
|
|
515
|
+
function buildShowContentArgs(input) {
|
|
736
516
|
if (input.end !== undefined)
|
|
737
517
|
validatePositiveInteger(input.end, "end");
|
|
738
|
-
if (input.limit !== undefined)
|
|
739
|
-
validatePositiveInteger(input.limit, "limit");
|
|
740
|
-
if (input.end !== undefined && input.limit !== undefined) {
|
|
518
|
+
if (input.end !== undefined && input.limit !== undefined)
|
|
741
519
|
throw new Error("cannot combine end with limit");
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
if (!Number.isSafeInteger(input.depth) || input.depth < 0) {
|
|
745
|
-
throw new Error("depth must be a non-negative integer");
|
|
746
|
-
}
|
|
747
|
-
if (!hasMap)
|
|
748
|
-
throw new Error("depth requires the map facet");
|
|
749
|
-
}
|
|
750
|
-
if (input.selector !== undefined && input.query !== undefined) {
|
|
751
|
-
throw new Error("cannot combine selector with query");
|
|
752
|
-
}
|
|
753
|
-
if (!hasContent && (input.end !== undefined || input.limit !== undefined || input.query !== undefined)) {
|
|
754
|
-
throw new Error("ranges and queries require the content facet");
|
|
755
|
-
}
|
|
756
|
-
if (hasIdentity && input.selector === undefined) {
|
|
757
|
-
throw new Error("the identity facet requires a selector");
|
|
758
|
-
}
|
|
759
|
-
if (input.selector !== undefined && !hasContent && !hasIdentity) {
|
|
760
|
-
throw new Error("selectors require the content or identity facet");
|
|
761
|
-
}
|
|
762
|
-
if (input.selector !== undefined) {
|
|
763
|
-
const selector = parseSelector(input.selector);
|
|
764
|
-
if (selector.kind === "symbol" && hasIdentity) {
|
|
765
|
-
throw new Error("the identity facet does not accept symbol selectors");
|
|
766
|
-
}
|
|
767
|
-
if (selector.column !== undefined && !hasIdentity) {
|
|
768
|
-
throw new Error("selector columns require the identity facet");
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
const hasVision = input.visionMode !== undefined || input.visionLevel !== undefined;
|
|
772
|
-
if (hasVision && !hasContent) {
|
|
773
|
-
throw new Error("vision options require the content facet");
|
|
774
|
-
}
|
|
775
|
-
if (hasVision && hasSourceFacet) {
|
|
776
|
-
throw new Error("vision options cannot be combined with source facets");
|
|
777
|
-
}
|
|
778
|
-
const args = [];
|
|
779
|
-
if (input.readseekDir !== undefined)
|
|
780
|
-
args.push("--readseek-dir", input.readseekDir);
|
|
781
|
-
args.push("inspect", input.path);
|
|
782
|
-
if (input.selector !== undefined)
|
|
783
|
-
args.push(input.selector);
|
|
784
|
-
const select = selectArg(input.select);
|
|
785
|
-
if (select !== undefined)
|
|
786
|
-
args.push("--select", select);
|
|
787
|
-
pushOptionalString(args, "--query", input.query);
|
|
520
|
+
const args = start("show", "content", input);
|
|
521
|
+
at2(args, input.at);
|
|
788
522
|
pushOptionalNumber(args, "--end", input.end);
|
|
789
|
-
|
|
523
|
+
limit2(args, input.limit);
|
|
524
|
+
language(args, input);
|
|
525
|
+
anchors(args, input);
|
|
526
|
+
return finish(args);
|
|
527
|
+
}
|
|
528
|
+
function buildShowStructureArgs(input) {
|
|
529
|
+
if (input.depth !== undefined && (!Number.isSafeInteger(input.depth) || input.depth < 0))
|
|
530
|
+
throw new Error("depth must be a non-negative integer");
|
|
531
|
+
const args = start("show", "structure", input);
|
|
532
|
+
at2(args, input.at);
|
|
790
533
|
pushOptionalNumber(args, "--depth", input.depth);
|
|
791
|
-
pushOptionalString(args, "--
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
args
|
|
534
|
+
pushOptionalString(args, "--kind", input.kind);
|
|
535
|
+
language(args, input);
|
|
536
|
+
if (input.outline)
|
|
537
|
+
args.push("--outline");
|
|
538
|
+
return finish(args);
|
|
539
|
+
}
|
|
540
|
+
function buildShowMetadataArgs(input) {
|
|
541
|
+
const args = start("show", "metadata", input);
|
|
542
|
+
language(args, input);
|
|
543
|
+
return finish(args);
|
|
544
|
+
}
|
|
545
|
+
function buildShowAssetsArgs(input) {
|
|
546
|
+
if (input.at !== undefined && !["page", "node", "asset"].includes(parseSelector(input.at).kind))
|
|
547
|
+
throw new Error("assets requires a page, node, or asset selector");
|
|
548
|
+
const args = start("show", "assets", input);
|
|
549
|
+
at2(args, input.at);
|
|
550
|
+
return finish(args);
|
|
551
|
+
}
|
|
552
|
+
function buildCheckSyntaxArgs(input) {
|
|
553
|
+
const args = start("check", "syntax", input);
|
|
554
|
+
language(args, input);
|
|
555
|
+
return finish(args);
|
|
556
|
+
}
|
|
557
|
+
function visionArgs(verb, object, input) {
|
|
558
|
+
if (input.level !== undefined && !VISION_LEVELS.includes(input.level))
|
|
559
|
+
throw new Error("invalid vision level");
|
|
560
|
+
if (input.at !== undefined && !["page", "node", "asset"].includes(parseSelector(input.at).kind))
|
|
561
|
+
throw new Error("vision requires a document selector");
|
|
562
|
+
const args = start(verb, object, input);
|
|
563
|
+
at2(args, input.at);
|
|
564
|
+
pushOptionalString(args, "--level", input.level);
|
|
796
565
|
return args;
|
|
797
566
|
}
|
|
798
|
-
function
|
|
799
|
-
|
|
800
|
-
if (input.
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
567
|
+
function buildGenerateCaptionArgs(input) {
|
|
568
|
+
const args = visionArgs("generate", "caption", input);
|
|
569
|
+
if (input.include !== undefined) {
|
|
570
|
+
if (input.include.length === 0 || input.include.some((task) => task !== "text" && task !== "objects"))
|
|
571
|
+
throw new Error("include requires text or objects");
|
|
572
|
+
args.push("--include", input.include.join(","));
|
|
573
|
+
}
|
|
574
|
+
return finish(args);
|
|
575
|
+
}
|
|
576
|
+
function buildDetectObjectsArgs(input) {
|
|
577
|
+
return finish(visionArgs("detect", "objects", input));
|
|
578
|
+
}
|
|
579
|
+
function buildRecognizeTextArgs(input) {
|
|
580
|
+
return finish(visionArgs("recognize", "text", input));
|
|
581
|
+
}
|
|
582
|
+
function buildFindNodesArgs(input) {
|
|
583
|
+
const hasPattern = Boolean(input.pattern);
|
|
584
|
+
const hasRule = Boolean(input.rule);
|
|
585
|
+
const hasContext = Boolean(input.context);
|
|
586
|
+
if (hasRule) {
|
|
587
|
+
if (hasPattern || hasContext || input.selector)
|
|
588
|
+
throw new Error("rule cannot be combined with pattern, context, or selector");
|
|
589
|
+
} else if (!hasPattern && !hasContext) {
|
|
590
|
+
throw new Error("find nodes requires pattern, context, or rule");
|
|
591
|
+
}
|
|
592
|
+
if (hasContext && !input.selector) {
|
|
593
|
+
throw new Error("context requires selector");
|
|
594
|
+
}
|
|
595
|
+
if (hasPattern && hasContext) {
|
|
596
|
+
throw new Error("pattern and context cannot be combined");
|
|
597
|
+
}
|
|
598
|
+
const args = start("find", "nodes", input);
|
|
599
|
+
pushOptionalString(args, "--pattern", input.pattern);
|
|
600
|
+
pushOptionalString(args, "--context", input.context);
|
|
601
|
+
pushOptionalString(args, "--selector", input.selector);
|
|
602
|
+
pushOptionalString(args, "--rule", input.rule);
|
|
603
|
+
language(args, input);
|
|
604
|
+
limit2(args, input.limit);
|
|
605
|
+
appendGitScopeArgs(args, input);
|
|
606
|
+
anchors(args, input);
|
|
607
|
+
return finish(args);
|
|
814
608
|
}
|
|
815
|
-
function
|
|
816
|
-
const args =
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
pushOptionalNumber(args, "--limit", input.limit);
|
|
609
|
+
function buildFindTextArgs(input) {
|
|
610
|
+
const args = start("find", "text", input);
|
|
611
|
+
args.push("--text", input.text);
|
|
612
|
+
limit2(args, input.limit);
|
|
820
613
|
appendGitScopeArgs(args, input);
|
|
821
|
-
args
|
|
822
|
-
return args;
|
|
614
|
+
return finish(args);
|
|
823
615
|
}
|
|
824
|
-
function
|
|
825
|
-
const args =
|
|
826
|
-
|
|
827
|
-
|
|
616
|
+
function buildFindDefinitionsArgs(input) {
|
|
617
|
+
const args = start("find", "definitions", input);
|
|
618
|
+
args.push("--name", input.name);
|
|
619
|
+
language(args, input);
|
|
828
620
|
appendGitScopeArgs(args, input);
|
|
829
|
-
args
|
|
830
|
-
return args;
|
|
621
|
+
anchors(args, input);
|
|
622
|
+
return finish(args);
|
|
831
623
|
}
|
|
832
|
-
function
|
|
833
|
-
const validated =
|
|
624
|
+
function buildFindReferencesArgs(input) {
|
|
625
|
+
const validated = validateReferencesSelection(input);
|
|
834
626
|
if (!validated.ok)
|
|
835
627
|
throw new Error(validated.error);
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
pushOptionalString(args, "--
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
pushOptionalNumber(args, "--line", input.line);
|
|
843
|
-
pushOptionalNumber(args, "--column", input.column);
|
|
844
|
-
}
|
|
845
|
-
pushOptionalString(args, "--language", input.language);
|
|
628
|
+
if (input.at !== undefined && !["line", "hash"].includes(parseSelector(input.at).kind))
|
|
629
|
+
throw new Error("references requires a line or hash selector");
|
|
630
|
+
const args = start("find", "references", input);
|
|
631
|
+
pushOptionalString(args, "--name", input.name);
|
|
632
|
+
at2(args, input.at);
|
|
633
|
+
language(args, input);
|
|
846
634
|
appendGitScopeArgs(args, input);
|
|
847
|
-
args
|
|
848
|
-
return args;
|
|
635
|
+
anchors(args, input);
|
|
636
|
+
return finish(args);
|
|
849
637
|
}
|
|
850
|
-
function
|
|
851
|
-
const args =
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
638
|
+
function buildCheckRulesArgs(input) {
|
|
639
|
+
const args = start("check", "rules", input);
|
|
640
|
+
args.push("--rules", input.rules);
|
|
641
|
+
appendGitScopeArgs(args, input);
|
|
642
|
+
return finish(args);
|
|
643
|
+
}
|
|
644
|
+
function buildEditTextArgs(input) {
|
|
645
|
+
const args = start("edit", "text", input);
|
|
646
|
+
appendApplyAndPlanHash(args, input);
|
|
647
|
+
language(args, input);
|
|
648
|
+
appendGitScopeArgs(args, input);
|
|
858
649
|
if (input.requestFromStdin)
|
|
859
650
|
args.push("--edits", "-");
|
|
860
|
-
args
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
651
|
+
return finish(args);
|
|
652
|
+
}
|
|
653
|
+
function buildRenameSymbolArgs(input) {
|
|
654
|
+
if (!["line", "hash"].includes(parseSelector(input.at).kind))
|
|
655
|
+
throw new Error("rename requires a line or hash selector");
|
|
656
|
+
const args = start("rename", "symbol", input);
|
|
657
|
+
at2(args, input.at);
|
|
658
|
+
args.push("--to", input.to);
|
|
865
659
|
if (input.workspace !== undefined)
|
|
866
660
|
args.push(`--workspace=${input.workspace}`);
|
|
867
|
-
appendApplyAndPlanHash(args, {
|
|
868
|
-
|
|
869
|
-
planHash: input.planHash,
|
|
870
|
-
flagOrder: input.flagOrder ?? "plan-hash-first"
|
|
871
|
-
});
|
|
872
|
-
pushOptionalString(args, "--language", input.language);
|
|
661
|
+
appendApplyAndPlanHash(args, { ...input, flagOrder: input.flagOrder ?? "plan-hash-first" });
|
|
662
|
+
language(args, input);
|
|
873
663
|
appendGitScopeArgs(args, input);
|
|
874
|
-
args
|
|
875
|
-
return args;
|
|
876
|
-
}
|
|
877
|
-
// ../readseek-api/src/platform.ts
|
|
878
|
-
import { accessSync, constants as fsConstants, existsSync, statSync } from "node:fs";
|
|
879
|
-
import { createRequire } from "node:module";
|
|
880
|
-
import path from "node:path";
|
|
881
|
-
var READSEEK_BINARY_ENV = "READSEEK_BINARY";
|
|
882
|
-
var READSEEK_PLATFORM_PACKAGES = {
|
|
883
|
-
"darwin-arm64": "@jarkkojs/readseek-darwin-arm64",
|
|
884
|
-
"linux-arm64": "@jarkkojs/readseek-linux-arm64",
|
|
885
|
-
"linux-x64": "@jarkkojs/readseek-linux-x64",
|
|
886
|
-
"win32-x64": "@jarkkojs/readseek-win32-x64"
|
|
887
|
-
};
|
|
888
|
-
var resolveFromPackage = createRequire(import.meta.url).resolve;
|
|
889
|
-
function readSeekBinaryName(platform = process.platform) {
|
|
890
|
-
if (platform === "linux")
|
|
891
|
-
return "readseek.AppImage";
|
|
892
|
-
return platform === "win32" ? "readseek.exe" : "readseek";
|
|
664
|
+
return finish(args);
|
|
893
665
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
if (binaryPath === undefined)
|
|
900
|
-
return null;
|
|
901
|
-
if (!path.isAbsolute(binaryPath)) {
|
|
902
|
-
throw new Error(`${READSEEK_BINARY_ENV} must be an absolute path to the readseek executable`);
|
|
903
|
-
}
|
|
666
|
+
// ../readseek-api/src/client-output.ts
|
|
667
|
+
import { Type } from "@sinclair/typebox";
|
|
668
|
+
import { AssertError, Value } from "@sinclair/typebox/value";
|
|
669
|
+
var OPERATIONS = ["Clone", "Clean", "Default", "Assert"];
|
|
670
|
+
function parse(schema, value, what) {
|
|
904
671
|
try {
|
|
905
|
-
|
|
906
|
-
throw new Error("not a regular file");
|
|
907
|
-
accessSync(binaryPath, fsConstants.X_OK);
|
|
672
|
+
return Value.Parse(OPERATIONS, schema, value);
|
|
908
673
|
} catch (error) {
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
return binaryPath;
|
|
914
|
-
}
|
|
915
|
-
function resolveReadSeekBinaryFromPackages(resolver = resolveFromPackage, options = {}) {
|
|
916
|
-
const resolve = typeof resolver === "function" ? resolver : resolver.resolve.bind(resolver);
|
|
917
|
-
const platform = options.platform ?? process.platform;
|
|
918
|
-
const arch = options.arch ?? process.arch;
|
|
919
|
-
const key = readSeekPlatformKey(platform, arch);
|
|
920
|
-
const platformPackage = READSEEK_PLATFORM_PACKAGES[key];
|
|
921
|
-
if (!platformPackage) {
|
|
922
|
-
const supported = Object.keys(READSEEK_PLATFORM_PACKAGES).join(", ");
|
|
923
|
-
throw new Error(`@jarkkojs/readseek ships no binary for ${key}; it supports ${supported}`);
|
|
924
|
-
}
|
|
925
|
-
const metaPackage = options.metaPackage ?? "@jarkkojs/readseek";
|
|
926
|
-
let packageJsonPath;
|
|
927
|
-
try {
|
|
928
|
-
const metaPackageJson = resolve(`${metaPackage}/package.json`);
|
|
929
|
-
const metaPackageDir = path.dirname(metaPackageJson);
|
|
930
|
-
packageJsonPath = resolve(`${platformPackage}/package.json`, {
|
|
931
|
-
paths: [metaPackageDir]
|
|
932
|
-
});
|
|
933
|
-
} catch {
|
|
934
|
-
packageJsonPath = resolve(`${platformPackage}/package.json`);
|
|
674
|
+
if (!(error instanceof AssertError))
|
|
675
|
+
throw error;
|
|
676
|
+
const detail = error.error;
|
|
677
|
+
throw new Error(`invalid readseek ${what}${detail?.path ?? ""}: ${detail?.message ?? "invalid value"}`);
|
|
935
678
|
}
|
|
936
|
-
const binaryPath = path.join(path.dirname(packageJsonPath), "bin", readSeekBinaryName(platform));
|
|
937
|
-
accessSync(binaryPath, fsConstants.X_OK);
|
|
938
|
-
return binaryPath;
|
|
939
679
|
}
|
|
940
|
-
|
|
941
|
-
var
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
680
|
+
var Int = Type.Integer({ minimum: Number.MIN_SAFE_INTEGER, maximum: Number.MAX_SAFE_INTEGER });
|
|
681
|
+
var Str = Type.String();
|
|
682
|
+
var Bool = Type.Boolean();
|
|
683
|
+
var OptStr = Type.Optional(Str);
|
|
684
|
+
var OptInt = Type.Optional(Int);
|
|
685
|
+
var Hashline = Type.Object({ line: Int, hash: Str, text: Str });
|
|
686
|
+
var Hashlines = Type.Array(Hashline);
|
|
687
|
+
var ReadOutput = Type.Object({
|
|
688
|
+
file: Str,
|
|
689
|
+
language: Str,
|
|
690
|
+
line_count: Int,
|
|
691
|
+
file_hash: Str,
|
|
692
|
+
start_line: Int,
|
|
693
|
+
end_line: Int,
|
|
694
|
+
hashlines: Hashlines,
|
|
695
|
+
truncated: Type.Optional(Bool)
|
|
696
|
+
});
|
|
697
|
+
var SymbolOutput = Type.Object({
|
|
698
|
+
kind: Str,
|
|
699
|
+
name: Str,
|
|
700
|
+
qualified_name: OptStr,
|
|
701
|
+
start_line: Int,
|
|
702
|
+
end_line: Int,
|
|
703
|
+
start_hash: OptStr,
|
|
704
|
+
end_hash: OptStr,
|
|
705
|
+
start_anchor: OptStr,
|
|
706
|
+
end_anchor: OptStr
|
|
707
|
+
});
|
|
708
|
+
function mapNodeShape(child) {
|
|
709
|
+
return Type.Object({
|
|
710
|
+
kind: Str,
|
|
711
|
+
name: Str,
|
|
712
|
+
start_line: Int,
|
|
713
|
+
end_line: Int,
|
|
714
|
+
start_anchor: Str,
|
|
715
|
+
end_anchor: Str,
|
|
716
|
+
children: Type.Array(child)
|
|
717
|
+
});
|
|
978
718
|
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
function properties(node) {
|
|
982
|
-
return Object.fromEntries(node.properties);
|
|
719
|
+
function recursiveStructureNode() {
|
|
720
|
+
return Type.Recursive((self) => mapNodeShape(self));
|
|
983
721
|
}
|
|
984
|
-
function
|
|
985
|
-
|
|
722
|
+
var StructureNode = typeof Type.Recursive === "function" ? recursiveStructureNode() : Type.Cyclic({ StructureNode: mapNodeShape(Type.Ref("StructureNode")) }, "StructureNode");
|
|
723
|
+
var StructureOutput = Type.Object({
|
|
724
|
+
file: Str,
|
|
725
|
+
language: Str,
|
|
726
|
+
line_count: Int,
|
|
727
|
+
file_hash: Str,
|
|
728
|
+
root: Type.Optional(StructureNode),
|
|
729
|
+
symbols: Type.Array(SymbolOutput)
|
|
730
|
+
});
|
|
731
|
+
var SearchSpan = {
|
|
732
|
+
start_byte: OptInt,
|
|
733
|
+
end_byte: OptInt,
|
|
734
|
+
text: OptStr,
|
|
735
|
+
start_line: Int,
|
|
736
|
+
end_line: Int,
|
|
737
|
+
start_hash: Str,
|
|
738
|
+
end_hash: Str,
|
|
739
|
+
hashlines: Hashlines
|
|
740
|
+
};
|
|
741
|
+
var SearchCapture = Type.Object({ name: Str, ...SearchSpan });
|
|
742
|
+
var SearchMatch = Type.Object({
|
|
743
|
+
pattern_index: Type.Integer({ default: 0 }),
|
|
744
|
+
...SearchSpan,
|
|
745
|
+
captures: Type.Array(SearchCapture)
|
|
746
|
+
});
|
|
747
|
+
var SearchFileOutput = Type.Object({
|
|
748
|
+
file: Str,
|
|
749
|
+
language: Str,
|
|
750
|
+
file_hash: Str,
|
|
751
|
+
matches: Type.Array(SearchMatch)
|
|
752
|
+
});
|
|
753
|
+
var SearchEnvelope = Type.Object({
|
|
754
|
+
results: Type.Array(SearchFileOutput),
|
|
755
|
+
returned_matches: OptInt,
|
|
756
|
+
truncated: Type.Optional(Bool),
|
|
757
|
+
byte_basis: OptStr
|
|
758
|
+
});
|
|
759
|
+
var RefsEnvelope = Type.Object({
|
|
760
|
+
references: Type.Array(Type.Object({
|
|
761
|
+
file: Str,
|
|
762
|
+
line: Int,
|
|
763
|
+
column: Int,
|
|
764
|
+
line_hash: Str,
|
|
765
|
+
text: Str,
|
|
766
|
+
symbol: Type.Optional(Type.Object({ qualified_name: OptStr }))
|
|
767
|
+
}))
|
|
768
|
+
});
|
|
769
|
+
var Diagnostic = Type.Object({
|
|
770
|
+
kind: Type.Union([Type.Literal("error"), Type.Literal("missing")]),
|
|
771
|
+
start_line: Int,
|
|
772
|
+
end_line: Int
|
|
773
|
+
});
|
|
774
|
+
var CheckEnvelope = Type.Object({
|
|
775
|
+
file_hash: OptStr,
|
|
776
|
+
error_count: Int,
|
|
777
|
+
missing_count: Int,
|
|
778
|
+
diagnostics: Type.Array(Diagnostic)
|
|
779
|
+
});
|
|
780
|
+
var DetectedObject = Type.Object({
|
|
781
|
+
label: Str,
|
|
782
|
+
bbox: Type.Tuple([Int, Int, Int, Int])
|
|
783
|
+
});
|
|
784
|
+
var SourceDetection = Type.Object({
|
|
785
|
+
kind: Type.Literal("source"),
|
|
786
|
+
type: Str,
|
|
787
|
+
file: Str,
|
|
788
|
+
language: Str,
|
|
789
|
+
engine: OptStr,
|
|
790
|
+
supported: Bool,
|
|
791
|
+
mime: OptStr,
|
|
792
|
+
syntax: OptStr
|
|
793
|
+
});
|
|
794
|
+
var ImageDetection = Type.Object({
|
|
795
|
+
kind: Type.Literal("image"),
|
|
796
|
+
type: Str,
|
|
797
|
+
file: Str,
|
|
798
|
+
mime: OptStr,
|
|
799
|
+
format: Str,
|
|
800
|
+
width: Int,
|
|
801
|
+
height: Int,
|
|
802
|
+
animated: Bool,
|
|
803
|
+
ocr: OptStr,
|
|
804
|
+
caption: OptStr,
|
|
805
|
+
objects: Type.Optional(Type.Array(DetectedObject))
|
|
806
|
+
});
|
|
807
|
+
var TextDetection = Type.Object({
|
|
808
|
+
kind: Type.Literal("text"),
|
|
809
|
+
type: Str,
|
|
810
|
+
file: Str,
|
|
811
|
+
mime: OptStr
|
|
812
|
+
});
|
|
813
|
+
var PdfDetection = Type.Object({
|
|
814
|
+
kind: Type.Literal("pdf"),
|
|
815
|
+
type: Type.Literal("application/pdf"),
|
|
816
|
+
file: Str,
|
|
817
|
+
mime: OptStr,
|
|
818
|
+
format: Type.Literal("pdf"),
|
|
819
|
+
pages: Int
|
|
820
|
+
});
|
|
821
|
+
var Detection = Type.Union([SourceDetection, ImageDetection, TextDetection, PdfDetection]);
|
|
822
|
+
var SourceDetectionInput = Type.Omit(SourceDetection, ["kind"]);
|
|
823
|
+
var ImageDetectionInput = Type.Omit(ImageDetection, ["kind"]);
|
|
824
|
+
var TextDetectionInput = Type.Omit(TextDetection, ["kind"]);
|
|
825
|
+
var PdfDetectionInput = Type.Omit(PdfDetection, ["kind"]);
|
|
826
|
+
var UnspannedContent = Type.Omit(ReadOutput, ["start_line", "end_line"]);
|
|
827
|
+
var PdfMetadata = Type.Object({ source: Str, format: Type.Literal("pdf"), pages: Int });
|
|
828
|
+
var DocumentCache = Type.Object({
|
|
829
|
+
content_hash: Str,
|
|
830
|
+
state: Type.Union([Type.Literal("built"), Type.Literal("reused")])
|
|
831
|
+
});
|
|
832
|
+
var Identifier = Type.Object({
|
|
833
|
+
text: Str,
|
|
834
|
+
start_column: Int,
|
|
835
|
+
end_column: Int,
|
|
836
|
+
start_byte: Int,
|
|
837
|
+
end_byte: Int
|
|
838
|
+
});
|
|
839
|
+
var CursorEnvelope = Type.Object({
|
|
840
|
+
file: Str,
|
|
841
|
+
language: Str,
|
|
842
|
+
engine: OptStr,
|
|
843
|
+
line_count: Int,
|
|
844
|
+
file_hash: Str,
|
|
845
|
+
line: Int,
|
|
846
|
+
column: Int,
|
|
847
|
+
line_hash: Str,
|
|
848
|
+
identifier: Type.Optional(Identifier),
|
|
849
|
+
symbol: Type.Optional(Type.Object({
|
|
850
|
+
name: Str,
|
|
851
|
+
kind: Str,
|
|
852
|
+
qualified_name: Str,
|
|
853
|
+
start_line: Int,
|
|
854
|
+
end_line: Int
|
|
855
|
+
}))
|
|
856
|
+
});
|
|
857
|
+
var EditOutputSchema = Type.Object({
|
|
858
|
+
file: Str,
|
|
859
|
+
before_hash: Str,
|
|
860
|
+
file_hash: Str,
|
|
861
|
+
plan_hash: Str,
|
|
862
|
+
applied: Bool,
|
|
863
|
+
changed: Bool,
|
|
864
|
+
edit_count: Int,
|
|
865
|
+
before_content: OptStr,
|
|
866
|
+
content: OptStr
|
|
867
|
+
});
|
|
868
|
+
var RenameConflictSchema = Type.Object({ line: Int, column: Int, reason: Str });
|
|
869
|
+
var RenameEditSchema = Type.Object({
|
|
870
|
+
line: Int,
|
|
871
|
+
start_column: Int,
|
|
872
|
+
end_column: Int,
|
|
873
|
+
start_byte: Int,
|
|
874
|
+
end_byte: Int,
|
|
875
|
+
occurrence: Str,
|
|
876
|
+
line_hash: Str,
|
|
877
|
+
text: Str
|
|
878
|
+
});
|
|
879
|
+
var Conflicts = Type.Array(RenameConflictSchema, { default: [] });
|
|
880
|
+
var Edits = Type.Array(RenameEditSchema, { default: [] });
|
|
881
|
+
var RenameFileOutputSchema = Type.Object({
|
|
882
|
+
file: Str,
|
|
883
|
+
language: Str,
|
|
884
|
+
engine: OptStr,
|
|
885
|
+
file_hash: Str,
|
|
886
|
+
conflicts: Conflicts,
|
|
887
|
+
edits: Edits
|
|
888
|
+
});
|
|
889
|
+
var RenameOutputSchema = Type.Object({
|
|
890
|
+
file: Str,
|
|
891
|
+
language: Str,
|
|
892
|
+
engine: OptStr,
|
|
893
|
+
file_hash: Str,
|
|
894
|
+
old_name: Str,
|
|
895
|
+
new_name: Str,
|
|
896
|
+
plan_hash: Str,
|
|
897
|
+
applied: Bool,
|
|
898
|
+
conflicts: Conflicts,
|
|
899
|
+
edits: Edits,
|
|
900
|
+
others: Type.Array(RenameFileOutputSchema, { default: [] })
|
|
901
|
+
});
|
|
902
|
+
var DefEnvelope = Type.Object({
|
|
903
|
+
locations: Type.Array(Type.Object({
|
|
904
|
+
file: Str,
|
|
905
|
+
line: Int,
|
|
906
|
+
column: Int,
|
|
907
|
+
line_hash: Str,
|
|
908
|
+
text: Str,
|
|
909
|
+
kind: OptStr,
|
|
910
|
+
name: OptStr,
|
|
911
|
+
qualified_name: OptStr
|
|
912
|
+
}))
|
|
913
|
+
});
|
|
914
|
+
function parseReadOutput(value) {
|
|
915
|
+
return parse(ReadOutput, value, "read output");
|
|
986
916
|
}
|
|
987
|
-
function
|
|
988
|
-
return
|
|
917
|
+
function parseStructureOutput(value) {
|
|
918
|
+
return parse(StructureOutput, value, "structure output");
|
|
989
919
|
}
|
|
990
|
-
function
|
|
991
|
-
const
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
920
|
+
function parseSearchOutput(value) {
|
|
921
|
+
const envelope = parse(SearchEnvelope, value, "search output");
|
|
922
|
+
const results = envelope.results;
|
|
923
|
+
if (envelope.returned_matches !== undefined)
|
|
924
|
+
results.returned_matches = envelope.returned_matches;
|
|
925
|
+
if (envelope.truncated !== undefined)
|
|
926
|
+
results.truncated = envelope.truncated;
|
|
927
|
+
if (envelope.byte_basis !== undefined)
|
|
928
|
+
results.byte_basis = envelope.byte_basis;
|
|
929
|
+
return results;
|
|
995
930
|
}
|
|
996
|
-
function
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
}
|
|
1002
|
-
const match = /^(\d+):([0-9a-f]+)$/.exec(anchor);
|
|
1003
|
-
if (!match)
|
|
1004
|
-
throw new Error(`invalid readseek KDL line anchor: ${anchor}`);
|
|
1005
|
-
return {
|
|
1006
|
-
line: Number(match[1]),
|
|
1007
|
-
hash: match[2],
|
|
1008
|
-
text: content
|
|
1009
|
-
};
|
|
931
|
+
function parseRefsOutput(value) {
|
|
932
|
+
return parse(RefsEnvelope, value, "refs output").references.map(({ symbol, ...reference }) => ({
|
|
933
|
+
...reference,
|
|
934
|
+
enclosingSymbol: symbol?.qualified_name
|
|
935
|
+
}));
|
|
1010
936
|
}
|
|
1011
|
-
function
|
|
937
|
+
function parseCheckOutput(value) {
|
|
938
|
+
const output = parse(CheckEnvelope, value, "check output");
|
|
1012
939
|
return {
|
|
1013
|
-
|
|
1014
|
-
|
|
940
|
+
file_hash: output.file_hash,
|
|
941
|
+
errorCount: output.error_count,
|
|
942
|
+
missingCount: output.missing_count,
|
|
943
|
+
diagnostics: output.diagnostics
|
|
1015
944
|
};
|
|
1016
945
|
}
|
|
1017
|
-
function
|
|
1018
|
-
const
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
if (objects.length > 0) {
|
|
1022
|
-
result.objects = objects.map((object) => ({
|
|
1023
|
-
label: requireProperty(object, "label"),
|
|
1024
|
-
bbox: [
|
|
1025
|
-
requireProperty(object, "xmin"),
|
|
1026
|
-
requireProperty(object, "ymin"),
|
|
1027
|
-
requireProperty(object, "xmax"),
|
|
1028
|
-
requireProperty(object, "ymax")
|
|
1029
|
-
]
|
|
1030
|
-
}));
|
|
946
|
+
function parseDetectOutput(value) {
|
|
947
|
+
const output = value ?? {};
|
|
948
|
+
if (output.type === "application/pdf") {
|
|
949
|
+
return { kind: "pdf", ...parse(PdfDetectionInput, value, "detect output") };
|
|
1031
950
|
}
|
|
1032
|
-
|
|
951
|
+
if (output.width !== undefined || output.height !== undefined) {
|
|
952
|
+
return { kind: "image", ...parse(ImageDetectionInput, value, "detect output") };
|
|
953
|
+
}
|
|
954
|
+
if (output.language !== undefined) {
|
|
955
|
+
return { kind: "source", ...parse(SourceDetectionInput, value, "detect output") };
|
|
956
|
+
}
|
|
957
|
+
return { kind: "text", ...parse(TextDetectionInput, value, "detect output") };
|
|
1033
958
|
}
|
|
1034
|
-
function
|
|
1035
|
-
const
|
|
959
|
+
function parseTextContent(value) {
|
|
960
|
+
const output = value ?? {};
|
|
961
|
+
if (output.hashlines === undefined || output.start_line !== undefined)
|
|
962
|
+
return parseReadOutput(value);
|
|
963
|
+
const spanned = parse(UnspannedContent, value, "content");
|
|
1036
964
|
return {
|
|
1037
|
-
...
|
|
1038
|
-
|
|
1039
|
-
|
|
965
|
+
...spanned,
|
|
966
|
+
start_line: spanned.hashlines[0]?.line ?? 1,
|
|
967
|
+
end_line: spanned.hashlines[spanned.hashlines.length - 1]?.line ?? 0
|
|
1040
968
|
};
|
|
1041
969
|
}
|
|
1042
|
-
function
|
|
970
|
+
function parseCursorOutput(value) {
|
|
971
|
+
return parse(CursorEnvelope, value, "cursor output");
|
|
972
|
+
}
|
|
973
|
+
function parsePdfMetadata(value) {
|
|
974
|
+
if (!value || typeof value !== "object")
|
|
975
|
+
return;
|
|
976
|
+
if (value.format !== "pdf")
|
|
977
|
+
return;
|
|
978
|
+
const document = parse(PdfMetadata, value, "inspect metadata");
|
|
979
|
+
return { kind: "pdf", type: "application/pdf", file: document.source, format: "pdf", pages: document.pages };
|
|
980
|
+
}
|
|
981
|
+
function parseEditOutput(value) {
|
|
982
|
+
return parse(EditOutputSchema, value, "edit output");
|
|
983
|
+
}
|
|
984
|
+
function parseRenameOutput(value) {
|
|
985
|
+
return parse(RenameOutputSchema, value, "rename output");
|
|
986
|
+
}
|
|
987
|
+
function parseDefOutput(value) {
|
|
988
|
+
return parse(DefEnvelope, value, "def output").locations;
|
|
989
|
+
}
|
|
990
|
+
var Bbox = Type.Object({ x: Type.Number(), y: Type.Number(), width: Type.Number(), height: Type.Number() });
|
|
991
|
+
var Position = { page: OptInt, destination: OptStr, bbox: Type.Optional(Bbox) };
|
|
992
|
+
var Provenance = { inferred: Type.Optional(Bool), model: OptStr, level: OptStr, task: OptStr };
|
|
993
|
+
var ImageDescriptor = Type.Object({
|
|
994
|
+
id: OptStr,
|
|
995
|
+
kind: Type.Optional(Type.Literal("image")),
|
|
996
|
+
content_identity: OptStr,
|
|
997
|
+
path: Str,
|
|
998
|
+
mime: Str,
|
|
999
|
+
width: Int,
|
|
1000
|
+
height: Int,
|
|
1001
|
+
caption: OptStr,
|
|
1002
|
+
ocr: OptStr,
|
|
1003
|
+
objects: Type.Optional(Type.Array(DetectedObject)),
|
|
1004
|
+
...Provenance
|
|
1005
|
+
});
|
|
1006
|
+
var DocumentNode = Type.Object({
|
|
1007
|
+
id: Str,
|
|
1008
|
+
kind: Str,
|
|
1009
|
+
parent_id: OptStr,
|
|
1010
|
+
title: OptStr,
|
|
1011
|
+
level: OptInt,
|
|
1012
|
+
column: OptInt,
|
|
1013
|
+
text: OptStr,
|
|
1014
|
+
...Position
|
|
1015
|
+
});
|
|
1016
|
+
var DocumentAsset = Type.Object({
|
|
1017
|
+
id: Str,
|
|
1018
|
+
kind: Str,
|
|
1019
|
+
mime: Str,
|
|
1020
|
+
path: OptStr,
|
|
1021
|
+
content_identity: OptStr,
|
|
1022
|
+
bytes: OptInt,
|
|
1023
|
+
...Position
|
|
1024
|
+
});
|
|
1025
|
+
var DocumentVision = Type.Object({
|
|
1026
|
+
asset_id: Str,
|
|
1027
|
+
mode: OptStr,
|
|
1028
|
+
caption: OptStr,
|
|
1029
|
+
ocr: OptStr,
|
|
1030
|
+
objects: Type.Optional(Type.Array(DetectedObject)),
|
|
1031
|
+
caption_requested: Type.Optional(Bool),
|
|
1032
|
+
objects_requested: Type.Optional(Bool),
|
|
1033
|
+
ocr_requested: Type.Optional(Bool),
|
|
1034
|
+
...Provenance
|
|
1035
|
+
});
|
|
1036
|
+
var DocumentOutput = Type.Object({
|
|
1037
|
+
id: OptStr,
|
|
1038
|
+
format: Str,
|
|
1039
|
+
source: Str,
|
|
1040
|
+
title: OptStr,
|
|
1041
|
+
pages: Int,
|
|
1042
|
+
nodes: Type.Array(DocumentNode, { default: [] }),
|
|
1043
|
+
assets: Type.Array(DocumentAsset, { default: [] }),
|
|
1044
|
+
vision: Type.Array(DocumentVision, { default: [] })
|
|
1045
|
+
});
|
|
1046
|
+
var TextHit = Type.Object({
|
|
1047
|
+
line: Int,
|
|
1048
|
+
column: Int,
|
|
1049
|
+
text: Str,
|
|
1050
|
+
start_line: Int,
|
|
1051
|
+
end_line: Int,
|
|
1052
|
+
start_byte: Int,
|
|
1053
|
+
end_byte: Int,
|
|
1054
|
+
hashlines: Hashlines
|
|
1055
|
+
});
|
|
1056
|
+
var DocumentHit = Type.Object({ node_id: Str, kind: Str, snippet: Str, page: OptInt });
|
|
1057
|
+
var TextSearchOutput = Type.Object({
|
|
1058
|
+
text: Str,
|
|
1059
|
+
mode: Type.Literal("literal"),
|
|
1060
|
+
total_matches: Int,
|
|
1061
|
+
returned_matches: Int,
|
|
1062
|
+
truncated: Bool,
|
|
1063
|
+
results: Type.Array(Type.Object({ file: Str, file_hash: Str, matches: Type.Array(TextHit) })),
|
|
1064
|
+
documents: Type.Array(Type.Object({ file: Str, content_hash: Str, hits: Type.Array(DocumentHit) }))
|
|
1065
|
+
});
|
|
1066
|
+
var RuleFinding = Type.Object({
|
|
1067
|
+
name: Str,
|
|
1068
|
+
message: Str,
|
|
1069
|
+
path: Str,
|
|
1070
|
+
language: Str,
|
|
1071
|
+
start_byte: Int,
|
|
1072
|
+
end_byte: Int,
|
|
1073
|
+
line: Int,
|
|
1074
|
+
column: Int,
|
|
1075
|
+
end_line: Int,
|
|
1076
|
+
end_column: Int
|
|
1077
|
+
});
|
|
1078
|
+
var RulesOutput = Type.Object({
|
|
1079
|
+
complete: Bool,
|
|
1080
|
+
check_count: Int,
|
|
1081
|
+
file_count: Int,
|
|
1082
|
+
violation_count: Int,
|
|
1083
|
+
diagnostics: Type.Array(RuleFinding)
|
|
1084
|
+
});
|
|
1085
|
+
function parseDocumentOutput(value) {
|
|
1086
|
+
return parse(DocumentOutput, value, "document output");
|
|
1087
|
+
}
|
|
1088
|
+
function parseContentOutput(value) {
|
|
1089
|
+
const output = value;
|
|
1090
|
+
if (output?.format !== undefined)
|
|
1091
|
+
return parseDocumentOutput(value);
|
|
1092
|
+
if (!output || output.metadata === undefined || output.content === undefined)
|
|
1093
|
+
throw new Error("invalid readseek content: missing metadata or content");
|
|
1094
|
+
const metadata = parseDetectOutput(output.metadata);
|
|
1043
1095
|
return {
|
|
1044
|
-
|
|
1045
|
-
|
|
1096
|
+
metadata,
|
|
1097
|
+
content: metadata.kind === "image" ? parse(ImageDescriptor, output.content, "image descriptor") : parseTextContent(output.content),
|
|
1098
|
+
...output.cursor !== undefined ? { cursor: parseCursorOutput(output.cursor) } : {}
|
|
1046
1099
|
};
|
|
1047
1100
|
}
|
|
1048
|
-
function
|
|
1049
|
-
const
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
if (
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1101
|
+
function parseStructureEnvelope(value) {
|
|
1102
|
+
const output = value;
|
|
1103
|
+
if (output?.format !== undefined)
|
|
1104
|
+
return parseDocumentOutput(value);
|
|
1105
|
+
if (!output || output.structure === undefined)
|
|
1106
|
+
throw new Error("invalid readseek structure: missing structure");
|
|
1107
|
+
return { ...output.metadata !== undefined ? { metadata: parseDetectOutput(output.metadata) } : {}, structure: parseStructureOutput(output.structure) };
|
|
1108
|
+
}
|
|
1109
|
+
function parseTextSearchOutput(value) {
|
|
1110
|
+
return parse(TextSearchOutput, value, "text search output");
|
|
1111
|
+
}
|
|
1112
|
+
function parseRulesOutput(value) {
|
|
1113
|
+
return parse(RulesOutput, value, "rules output");
|
|
1114
|
+
}
|
|
1115
|
+
var SourceAssetsOutput = Type.Object({ file: OptStr, metadata: Type.Optional(Detection), assets: Type.Array(DocumentAsset, { default: [] }) });
|
|
1116
|
+
function parseAssetsOutput(value) {
|
|
1117
|
+
const raw = value;
|
|
1118
|
+
if (raw?.format !== undefined)
|
|
1119
|
+
return parseDocumentOutput(value);
|
|
1120
|
+
if (!raw || raw.file === undefined && raw.metadata === undefined)
|
|
1121
|
+
throw new Error("invalid readseek assets output: missing file or metadata");
|
|
1122
|
+
return parse(SourceAssetsOutput, { ...raw, ...raw.metadata !== undefined ? { metadata: parseDetectOutput(raw.metadata) } : {} }, "assets output");
|
|
1057
1123
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
const
|
|
1063
|
-
const
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
result.metadata = properties(metadata);
|
|
1068
|
-
if (cache)
|
|
1069
|
-
result.cache = properties(cache);
|
|
1070
|
-
if (content)
|
|
1071
|
-
result.content = sourceContent(content);
|
|
1072
|
-
if (map)
|
|
1073
|
-
result.map = sourceMap(map);
|
|
1074
|
-
if (diagnosticOutput)
|
|
1075
|
-
result.diagnostics = diagnostics(diagnosticOutput);
|
|
1076
|
-
if (identityOutput)
|
|
1077
|
-
result.identity = identity(identityOutput);
|
|
1124
|
+
|
|
1125
|
+
// ../readseek-api/src/edit-plan.ts
|
|
1126
|
+
import { Document, Node, format } from "@bgotink/kdl";
|
|
1127
|
+
function node(name, properties, children = []) {
|
|
1128
|
+
const result = Node.create(name);
|
|
1129
|
+
for (const [key, value] of Object.entries(properties))
|
|
1130
|
+
result.setProperty(key, value);
|
|
1131
|
+
if (children.length > 0)
|
|
1132
|
+
result.children = new Document(children);
|
|
1078
1133
|
return result;
|
|
1079
1134
|
}
|
|
1080
|
-
function
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
})
|
|
1088
|
-
}
|
|
1135
|
+
function editNode(edit) {
|
|
1136
|
+
if ("set_line" in edit) {
|
|
1137
|
+
const value = edit.set_line;
|
|
1138
|
+
return node("set-line", { anchor: value.anchor, text: value.new_text });
|
|
1139
|
+
}
|
|
1140
|
+
if ("replace_lines" in edit) {
|
|
1141
|
+
const value = edit.replace_lines;
|
|
1142
|
+
return node("replace-lines", { start: value.start_anchor, end: value.end_anchor, text: value.new_text });
|
|
1143
|
+
}
|
|
1144
|
+
if ("insert_after" in edit) {
|
|
1145
|
+
const value = edit.insert_after;
|
|
1146
|
+
return node("insert-after", { anchor: value.anchor, text: value.new_text });
|
|
1147
|
+
}
|
|
1148
|
+
if ("replace_symbol" in edit) {
|
|
1149
|
+
const value = edit.replace_symbol;
|
|
1150
|
+
return node("replace-symbol", { symbol: value.symbol, body: value.new_body });
|
|
1151
|
+
}
|
|
1152
|
+
const value = edit.replace;
|
|
1153
|
+
return node("replace", {
|
|
1154
|
+
old: value.old_text,
|
|
1155
|
+
new: value.new_text,
|
|
1156
|
+
...value.all === undefined ? {} : { all: value.all }
|
|
1157
|
+
});
|
|
1089
1158
|
}
|
|
1090
|
-
function
|
|
1159
|
+
function serializeEditPlan(edits) {
|
|
1160
|
+
const validation = validateExactlyOneEditVariant(edits);
|
|
1161
|
+
if (!validation.ok)
|
|
1162
|
+
throw new Error(validation.error);
|
|
1163
|
+
return format(new Document([node("plan", {}, validation.edits.map(editNode))]));
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
// ../readseek-api/src/client.ts
|
|
1167
|
+
function classifyReadSeekFailure(err) {
|
|
1168
|
+
const failure = err;
|
|
1169
|
+
const message = String(failure?.message ?? err);
|
|
1170
|
+
const missing = failure?.code === "ENOENT" || /Cannot find package|Cannot find module|no such file/i.test(message);
|
|
1171
|
+
if (missing) {
|
|
1172
|
+
return { code: "readseek-not-installed", message, hint: "Run npm install to install @jarkkojs/readseek." };
|
|
1173
|
+
}
|
|
1174
|
+
return { code: "readseek-execution-error", message };
|
|
1175
|
+
}
|
|
1176
|
+
function runOptions(options, vision = false) {
|
|
1091
1177
|
return {
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
}
|
|
1097
|
-
documents: children(root, "document").map((documentNode) => ({
|
|
1098
|
-
...properties(documentNode),
|
|
1099
|
-
hits: children(documentNode, "hit").map(properties)
|
|
1100
|
-
}))
|
|
1178
|
+
signal: options.signal,
|
|
1179
|
+
stdin: options.stdin,
|
|
1180
|
+
timeoutMs: options.timeoutMs,
|
|
1181
|
+
cancelable: options.cancelable,
|
|
1182
|
+
...vision ? { vision: true } : {}
|
|
1101
1183
|
};
|
|
1102
1184
|
}
|
|
1103
|
-
function
|
|
1104
|
-
const parsed = children(root, "reference").map((reference) => {
|
|
1105
|
-
const sourceLine = child(reference, "line");
|
|
1106
|
-
if (!sourceLine)
|
|
1107
|
-
throw new Error("invalid readseek KDL reference: missing line");
|
|
1108
|
-
const parsedLine = line(sourceLine);
|
|
1109
|
-
const symbol = child(reference, "symbol");
|
|
1110
|
-
return {
|
|
1111
|
-
...properties(reference),
|
|
1112
|
-
line_hash: parsedLine.hash,
|
|
1113
|
-
text: parsedLine.text,
|
|
1114
|
-
...symbol ? { symbol: properties(symbol) } : {}
|
|
1115
|
-
};
|
|
1116
|
-
});
|
|
1185
|
+
function createReadSeekClient(runner) {
|
|
1117
1186
|
return {
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
})
|
|
1187
|
+
async showContent(path, options = {}) {
|
|
1188
|
+
const value = await runner.kdl(buildShowContentArgs({ ...options, path }), runOptions(options));
|
|
1189
|
+
return parseContentOutput(value);
|
|
1190
|
+
},
|
|
1191
|
+
async showStructure(path, options = {}) {
|
|
1192
|
+
const value = await runner.kdl(buildShowStructureArgs({ ...options, path }), runOptions(options));
|
|
1193
|
+
return parseStructureEnvelope(value);
|
|
1194
|
+
},
|
|
1195
|
+
async showMetadata(path, options = {}) {
|
|
1196
|
+
const value = await runner.kdl(buildShowMetadataArgs({ ...options, path }), runOptions(options));
|
|
1197
|
+
return parsePdfMetadata(value) ?? parseDetectOutput(value?.metadata);
|
|
1198
|
+
},
|
|
1199
|
+
async showAssets(path, options = {}) {
|
|
1200
|
+
const value = await runner.kdl(buildShowAssetsArgs({ ...options, path }), runOptions(options));
|
|
1201
|
+
return parseAssetsOutput(value);
|
|
1202
|
+
},
|
|
1203
|
+
async checkSyntax(path, options = {}) {
|
|
1204
|
+
const value = await runner.kdl(buildCheckSyntaxArgs({ ...options, path }), runOptions(options));
|
|
1205
|
+
return parseCheckOutput(value?.diagnostics ?? value);
|
|
1206
|
+
},
|
|
1207
|
+
async checkRules(path, options) {
|
|
1208
|
+
const value = await runner.kdl(buildCheckRulesArgs({ ...options, path }), runOptions(options));
|
|
1209
|
+
return parseRulesOutput(value);
|
|
1210
|
+
},
|
|
1211
|
+
async generateCaption(path, options = {}) {
|
|
1212
|
+
const value = await runner.kdl(buildGenerateCaptionArgs({ ...options, path }), runOptions(options, true));
|
|
1213
|
+
return parseContentOutput(value);
|
|
1214
|
+
},
|
|
1215
|
+
async detectObjects(path, options = {}) {
|
|
1216
|
+
const value = await runner.kdl(buildDetectObjectsArgs({ ...options, path }), runOptions(options, true));
|
|
1217
|
+
return parseContentOutput(value);
|
|
1218
|
+
},
|
|
1219
|
+
async recognizeText(path, options = {}) {
|
|
1220
|
+
const value = await runner.kdl(buildRecognizeTextArgs({ ...options, path }), runOptions(options, true));
|
|
1221
|
+
return parseContentOutput(value);
|
|
1222
|
+
},
|
|
1223
|
+
async findNodes(path, options) {
|
|
1224
|
+
const value = await runner.kdl(buildFindNodesArgs({ ...options, path }), runOptions(options));
|
|
1225
|
+
return parseSearchOutput(value);
|
|
1226
|
+
},
|
|
1227
|
+
async findText(path, options) {
|
|
1228
|
+
const value = await runner.kdl(buildFindTextArgs({ ...options, path }), runOptions(options));
|
|
1229
|
+
return parseTextSearchOutput(value);
|
|
1230
|
+
},
|
|
1231
|
+
async findDefinitions(path, options) {
|
|
1232
|
+
const value = await runner.kdl(buildFindDefinitionsArgs({ ...options, path }), runOptions(options));
|
|
1233
|
+
return parseDefOutput(value);
|
|
1234
|
+
},
|
|
1235
|
+
async findReferences(path, options) {
|
|
1236
|
+
const value = await runner.kdl(buildFindReferencesArgs({ ...options, path }), runOptions(options));
|
|
1237
|
+
return parseRefsOutput(value);
|
|
1238
|
+
},
|
|
1239
|
+
async editText(path, edits, options = {}) {
|
|
1240
|
+
const stdin = serializeEditPlan(edits);
|
|
1241
|
+
return parseEditOutput(await runner.kdl(buildEditTextArgs({ ...options, path, requestFromStdin: true }), runOptions({ ...options, stdin })));
|
|
1242
|
+
},
|
|
1243
|
+
async renameSymbol(path, options) {
|
|
1244
|
+
const value = await runner.kdl(buildRenameSymbolArgs({ ...options, path }), runOptions(options));
|
|
1245
|
+
return parseRenameOutput(value);
|
|
1246
|
+
}
|
|
1123
1247
|
};
|
|
1124
1248
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
const symbol = child(definition, "symbol");
|
|
1129
|
-
const sourceLine = child(definition, "line");
|
|
1130
|
-
if (!symbol || !sourceLine) {
|
|
1131
|
-
throw new Error("invalid readseek KDL definition: missing symbol or line");
|
|
1132
|
-
}
|
|
1133
|
-
const symbolProperties = properties(symbol);
|
|
1134
|
-
const parsedLine = line(sourceLine);
|
|
1135
|
-
const definitionColumn = definition.properties.get("column");
|
|
1136
|
-
const nameByte = symbol.properties.get("name_byte");
|
|
1137
|
-
const startByte = symbol.properties.get("start_byte");
|
|
1138
|
-
let column = 1;
|
|
1139
|
-
if (typeof definitionColumn === "number") {
|
|
1140
|
-
column = definitionColumn;
|
|
1141
|
-
} else if (typeof nameByte === "number" && typeof startByte === "number") {
|
|
1142
|
-
column = nameByte - startByte + 1;
|
|
1143
|
-
}
|
|
1144
|
-
return {
|
|
1145
|
-
file: requireProperty(definition, "file"),
|
|
1146
|
-
line: parsedLine.line,
|
|
1147
|
-
column,
|
|
1148
|
-
line_hash: parsedLine.hash,
|
|
1149
|
-
text: parsedLine.text,
|
|
1150
|
-
kind: symbolProperties.kind,
|
|
1151
|
-
name: symbolProperties.name,
|
|
1152
|
-
qualified_name: symbolProperties.qualified_name
|
|
1153
|
-
};
|
|
1154
|
-
})
|
|
1155
|
-
};
|
|
1156
|
-
}
|
|
1157
|
-
function edit(root) {
|
|
1158
|
-
const result = properties(root);
|
|
1159
|
-
for (const name of ["before_content", "content"]) {
|
|
1160
|
-
const contentNode = child(root, name);
|
|
1161
|
-
if (contentNode)
|
|
1162
|
-
result[name] = requireProperty(contentNode, "content");
|
|
1163
|
-
}
|
|
1164
|
-
return result;
|
|
1249
|
+
// ../readseek-api/src/show-presentation.ts
|
|
1250
|
+
function isShowTool(name) {
|
|
1251
|
+
return name === "show_content" || name === "show_structure" || name === "show_metadata" || name === "show_assets";
|
|
1165
1252
|
}
|
|
1166
|
-
function
|
|
1167
|
-
|
|
1168
|
-
if (!sourceLine)
|
|
1169
|
-
throw new Error("invalid readseek KDL rename edit: missing line");
|
|
1170
|
-
return {
|
|
1171
|
-
...properties(node),
|
|
1172
|
-
text: requireProperty(sourceLine, "content")
|
|
1173
|
-
};
|
|
1253
|
+
function record(value) {
|
|
1254
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1174
1255
|
}
|
|
1175
|
-
function
|
|
1176
|
-
return
|
|
1177
|
-
...properties(node),
|
|
1178
|
-
edits: children(node, "edit").map(renameEdit),
|
|
1179
|
-
conflicts: children(node, "conflict").map(properties)
|
|
1180
|
-
};
|
|
1256
|
+
function records(value) {
|
|
1257
|
+
return Array.isArray(value) ? value.map(record) : [];
|
|
1181
1258
|
}
|
|
1182
|
-
function
|
|
1183
|
-
return
|
|
1184
|
-
...renameFile(root),
|
|
1185
|
-
others: children(root, "other").map(renameFile)
|
|
1186
|
-
};
|
|
1259
|
+
function text(value) {
|
|
1260
|
+
return typeof value === "string" ? value : "";
|
|
1187
1261
|
}
|
|
1188
|
-
function
|
|
1189
|
-
|
|
1190
|
-
const textNode = child(node, "text");
|
|
1191
|
-
const bbox = child(node, "bbox");
|
|
1192
|
-
if (textNode)
|
|
1193
|
-
result.text = requireProperty(textNode, "content");
|
|
1194
|
-
if (bbox)
|
|
1195
|
-
result.bbox = properties(bbox);
|
|
1196
|
-
const objects = children(node, "object");
|
|
1197
|
-
if (objects.length > 0)
|
|
1198
|
-
result.objects = objects.map(genericNode);
|
|
1199
|
-
return result;
|
|
1262
|
+
function count(value) {
|
|
1263
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : undefined;
|
|
1200
1264
|
}
|
|
1201
|
-
function
|
|
1202
|
-
return {
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1265
|
+
function safe(value) {
|
|
1266
|
+
return value.replace(/[\x00-\x09\x0b-\x1f\x7f-\x9f]/g, (char) => {
|
|
1267
|
+
if (char === "\t")
|
|
1268
|
+
return "\\t";
|
|
1269
|
+
if (char === "\r")
|
|
1270
|
+
return "\\r";
|
|
1271
|
+
return `\\x${char.charCodeAt(0).toString(16).padStart(2, "0")}`;
|
|
1272
|
+
});
|
|
1208
1273
|
}
|
|
1209
|
-
function
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
case "document":
|
|
1213
|
-
return root.properties.has("format") ? pdfDocument(root) : sourceDocument(root);
|
|
1214
|
-
case "search":
|
|
1215
|
-
return search(root);
|
|
1216
|
-
case "check":
|
|
1217
|
-
return diagnostics(root);
|
|
1218
|
-
case "refs":
|
|
1219
|
-
return references(root);
|
|
1220
|
-
case "def":
|
|
1221
|
-
return definitions(root);
|
|
1222
|
-
case "edit":
|
|
1223
|
-
return edit(root);
|
|
1224
|
-
case "edit-batch":
|
|
1225
|
-
return { ...properties(root), files: children(root, "file").map(edit) };
|
|
1226
|
-
case "rename":
|
|
1227
|
-
return rename(root);
|
|
1228
|
-
default:
|
|
1229
|
-
throw new Error(`unsupported readseek KDL root: ${root.name}`);
|
|
1230
|
-
}
|
|
1274
|
+
function inline(value) {
|
|
1275
|
+
return safe(text(value)).replaceAll(`
|
|
1276
|
+
`, "\\n");
|
|
1231
1277
|
}
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
var DEFAULT_MAX_OUTPUT_BYTES = 32 * 1024 * 1024;
|
|
1235
|
-
function readSeekProcessEnvironment() {
|
|
1236
|
-
return process.platform === "linux" ? { ...process.env, APPIMAGE_EXTRACT_AND_RUN: "1" } : process.env;
|
|
1278
|
+
function plural(n, unit) {
|
|
1279
|
+
return `${n} ${unit}${n === 1 ? "" : "s"}`;
|
|
1237
1280
|
}
|
|
1238
|
-
function
|
|
1239
|
-
return {
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1281
|
+
function fields(value, omit = []) {
|
|
1282
|
+
return Object.entries(value).flatMap(([key, item]) => {
|
|
1283
|
+
if (omit.includes(key) || item === undefined || item === null || typeof item === "object")
|
|
1284
|
+
return [];
|
|
1285
|
+
const label = key.replaceAll("_", " ");
|
|
1286
|
+
return [`${label[0]?.toUpperCase() ?? ""}${label.slice(1)}: ${safe(String(item))}`];
|
|
1287
|
+
}).join(`
|
|
1288
|
+
`);
|
|
1244
1289
|
}
|
|
1245
|
-
function
|
|
1246
|
-
const
|
|
1247
|
-
|
|
1290
|
+
function location(value) {
|
|
1291
|
+
const start = text(value.start_anchor) || (count(value.start_line) !== undefined ? `${value.start_line}${text(value.start_hash) ? `:${value.start_hash}` : ""}` : "");
|
|
1292
|
+
const end = text(value.end_anchor) || (count(value.end_line) !== undefined ? `${value.end_line}${text(value.end_hash) ? `:${value.end_hash}` : ""}` : "");
|
|
1293
|
+
return start ? ` ${inline(start)}${end && end !== start ? ` - ${inline(end)}` : ""}` : "";
|
|
1248
1294
|
}
|
|
1249
|
-
function
|
|
1250
|
-
|
|
1251
|
-
|
|
1295
|
+
function sourceStructure(structure) {
|
|
1296
|
+
const sections = [];
|
|
1297
|
+
if (structure.root) {
|
|
1298
|
+
const lines = [];
|
|
1299
|
+
const stack = [{ node: record(structure.root), depth: 0 }];
|
|
1300
|
+
while (stack.length > 0) {
|
|
1301
|
+
const { node, depth } = stack.pop();
|
|
1302
|
+
lines.push(`${" ".repeat(Math.min(depth, 12))}${inline(node.kind) || "node"}${text(node.name) ? ` ${inline(node.name)}` : ""}${location(node)}`);
|
|
1303
|
+
const children = records(node.children);
|
|
1304
|
+
for (let i = children.length - 1;i >= 0; i--)
|
|
1305
|
+
stack.push({ node: children[i], depth: depth + 1 });
|
|
1306
|
+
}
|
|
1307
|
+
sections.push(`Tree
|
|
1308
|
+
${lines.join(`
|
|
1309
|
+
`)}`);
|
|
1252
1310
|
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1311
|
+
const symbols = records(structure.symbols);
|
|
1312
|
+
if (symbols.length > 0) {
|
|
1313
|
+
sections.push(`Symbols
|
|
1314
|
+
${symbols.map((symbol) => `${inline(symbol.kind) || "symbol"} ${inline(symbol.qualified_name || symbol.name)}${location(symbol)}`).join(`
|
|
1315
|
+
`)}`);
|
|
1255
1316
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1317
|
+
return sections.join(`
|
|
1318
|
+
|
|
1319
|
+
`) || "No structure found in this selection.";
|
|
1320
|
+
}
|
|
1321
|
+
function documentNodes(nodes, content) {
|
|
1322
|
+
const depths = new Map;
|
|
1323
|
+
return nodes.map((node) => {
|
|
1324
|
+
const depth = Math.min((depths.get(text(node.parent_id)) ?? -1) + 1, 12);
|
|
1325
|
+
depths.set(text(node.id), depth);
|
|
1326
|
+
const indent = " ".repeat(depth);
|
|
1327
|
+
const label = inline(node.title) || inline(node.kind) || "node";
|
|
1328
|
+
const page = count(node.page);
|
|
1329
|
+
const selector = text(node.id) ? `node:${inline(node.id)}` : "";
|
|
1330
|
+
const position = [selector, page !== undefined ? `page:${page}` : ""].filter(Boolean).join(" · ");
|
|
1331
|
+
const heading = `${indent}${label}${position ? ` [${position}]` : ""}`;
|
|
1332
|
+
const body = text(node.text);
|
|
1333
|
+
return content && body ? `${heading}
|
|
1334
|
+
${safe(body).split(`
|
|
1335
|
+
`).map((line) => indent + line).join(`
|
|
1336
|
+
`)}` : heading;
|
|
1337
|
+
}).join(content ? `
|
|
1338
|
+
|
|
1339
|
+
` : `
|
|
1340
|
+
`);
|
|
1262
1341
|
}
|
|
1263
|
-
function
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1342
|
+
function assetList(assets) {
|
|
1343
|
+
return assets.map((asset) => {
|
|
1344
|
+
const id = text(asset.id);
|
|
1345
|
+
const page = count(asset.page);
|
|
1346
|
+
const heading = [id ? `asset:${inline(id)}` : inline(asset.kind) || "image", inline(asset.mime), page !== undefined ? `page:${page}` : ""].filter(Boolean).join(" · ");
|
|
1347
|
+
const details = fields(asset, ["id", "kind", "mime", "page", "bbox", "content_identity"]);
|
|
1348
|
+
return `${heading}${details ? `
|
|
1349
|
+
${details.split(`
|
|
1350
|
+
`).map((line) => ` ${line}`).join(`
|
|
1351
|
+
`)}` : ""}`;
|
|
1352
|
+
}).join(`
|
|
1353
|
+
|
|
1354
|
+
`);
|
|
1267
1355
|
}
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
const
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
const
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1356
|
+
function presentShowResult(tool, value, options = {}) {
|
|
1357
|
+
const output = record(value);
|
|
1358
|
+
const metadata = record(output.metadata);
|
|
1359
|
+
const content = record(output.content);
|
|
1360
|
+
const cursor = record(output.cursor);
|
|
1361
|
+
const structure = record(output.structure);
|
|
1362
|
+
const document = typeof output.format === "string" && (Array.isArray(output.nodes) || typeof output.source === "string");
|
|
1363
|
+
const path = inline(options.path || output.source || output.file || content.file || structure.file || metadata.file || cursor.file) || "(unknown path)";
|
|
1364
|
+
const summary = [];
|
|
1365
|
+
const result = { summary: "", body: "", notices: [], metadata: {} };
|
|
1366
|
+
if (tool === "show_metadata") {
|
|
1367
|
+
const data = Object.keys(metadata).length > 0 ? metadata : output;
|
|
1368
|
+
if (text(data.format || data.language || data.type))
|
|
1369
|
+
summary.push(inline(data.format || data.language || data.type));
|
|
1370
|
+
if (count(data.pages) !== undefined) {
|
|
1371
|
+
result.metadata.pages = data.pages;
|
|
1372
|
+
summary.push(plural(result.metadata.pages, "page"));
|
|
1285
1373
|
}
|
|
1286
|
-
|
|
1287
|
-
})
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
const
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
clearTimeout(timer);
|
|
1307
|
-
options.signal?.removeEventListener("abort", onAbort);
|
|
1308
|
-
resolve(result);
|
|
1309
|
-
};
|
|
1310
|
-
const fail = (error) => {
|
|
1311
|
-
if (settled)
|
|
1312
|
-
return;
|
|
1313
|
-
settled = true;
|
|
1314
|
-
if (timer !== undefined)
|
|
1315
|
-
clearTimeout(timer);
|
|
1316
|
-
options.signal?.removeEventListener("abort", onAbort);
|
|
1317
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
1318
|
-
};
|
|
1319
|
-
const terminate = (error) => {
|
|
1320
|
-
if (settled)
|
|
1321
|
-
return;
|
|
1322
|
-
fail(error);
|
|
1323
|
-
try {
|
|
1324
|
-
child.kill("SIGKILL");
|
|
1325
|
-
} catch {}
|
|
1326
|
-
};
|
|
1327
|
-
const onAbort = () => {
|
|
1328
|
-
terminate(readSeekCancellationError(options.signal));
|
|
1329
|
-
};
|
|
1330
|
-
try {
|
|
1331
|
-
child = spawn(binaryPath, [...args], {
|
|
1332
|
-
cwd: options.cwd,
|
|
1333
|
-
env: readSeekProcessEnvironment(),
|
|
1334
|
-
shell: false,
|
|
1335
|
-
stdio: [options.stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"],
|
|
1336
|
-
windowsHide: true
|
|
1337
|
-
});
|
|
1338
|
-
} catch (error) {
|
|
1339
|
-
fail(error);
|
|
1340
|
-
return;
|
|
1374
|
+
result.body = fields(data) || "No metadata available.";
|
|
1375
|
+
} else if (tool === "show_assets") {
|
|
1376
|
+
const assets = records(output.assets);
|
|
1377
|
+
result.metadata.assets = assets.length;
|
|
1378
|
+
summary.push(plural(assets.length, "asset"));
|
|
1379
|
+
result.body = assetList(assets) || "No assets in this selection.";
|
|
1380
|
+
} else if (document) {
|
|
1381
|
+
const nodes = records(output.nodes);
|
|
1382
|
+
const assets = records(output.assets);
|
|
1383
|
+
result.metadata.nodes = nodes.length;
|
|
1384
|
+
if (count(output.pages) !== undefined)
|
|
1385
|
+
result.metadata.pages = output.pages;
|
|
1386
|
+
summary.push(plural(nodes.length, "node"));
|
|
1387
|
+
if (result.metadata.pages !== undefined)
|
|
1388
|
+
summary.push(plural(result.metadata.pages, "page"));
|
|
1389
|
+
const sections = [text(output.title) ? safe(text(output.title)) : "", documentNodes(nodes, tool === "show_content")];
|
|
1390
|
+
if (assets.length > 0) {
|
|
1391
|
+
result.metadata.assets = assets.length;
|
|
1392
|
+
sections.push(`Assets
|
|
1393
|
+
${assetList(assets)}`);
|
|
1341
1394
|
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
if (
|
|
1353
|
-
|
|
1395
|
+
result.body = sections.filter(Boolean).join(`
|
|
1396
|
+
|
|
1397
|
+
`) || "No content in this selection.";
|
|
1398
|
+
} else if (tool === "show_structure") {
|
|
1399
|
+
const symbols = records(structure.symbols);
|
|
1400
|
+
result.metadata.symbols = symbols.length;
|
|
1401
|
+
summary.push(plural(symbols.length, "symbol"));
|
|
1402
|
+
result.body = sourceStructure(structure);
|
|
1403
|
+
} else if (content.kind === "image" || count(content.width) !== undefined && count(content.height) !== undefined || count(metadata.width) !== undefined && count(metadata.height) !== undefined || text(content.mime).startsWith("image/") || text(metadata.mime).startsWith("image/") || text(metadata.type).startsWith("image/")) {
|
|
1404
|
+
const description = Object.keys(content).length > 0 ? content : Object.keys(metadata).length > 0 ? metadata : output;
|
|
1405
|
+
if (count(description.width) !== undefined && count(description.height) !== undefined) {
|
|
1406
|
+
summary.push(`${description.width} × ${description.height}`);
|
|
1407
|
+
} else if (count(metadata.width) !== undefined && count(metadata.height) !== undefined) {
|
|
1408
|
+
summary.push(`${metadata.width} × ${metadata.height}`);
|
|
1354
1409
|
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1410
|
+
const mime = text(description.mime || metadata.mime || metadata.type);
|
|
1411
|
+
if (mime)
|
|
1412
|
+
summary.push(inline(mime));
|
|
1413
|
+
result.body = fields(description, ["hashlines"]) || fields(metadata) || "No content in this selection.";
|
|
1414
|
+
} else if (Array.isArray(content.hashlines) || Array.isArray(cursor.hashlines)) {
|
|
1415
|
+
const lines = records(content.hashlines ?? cursor.hashlines);
|
|
1416
|
+
const start = count(content.start_line) ?? count(lines[0]?.line);
|
|
1417
|
+
const end = count(content.end_line) ?? count(lines.at(-1)?.line);
|
|
1418
|
+
const total = count(content.line_count) ?? count(cursor.line_count);
|
|
1419
|
+
if (start !== undefined && end !== undefined && start <= end) {
|
|
1420
|
+
result.metadata.start_line = start;
|
|
1421
|
+
result.metadata.end_line = end;
|
|
1422
|
+
summary.push(`lines ${start}-${end}${total !== undefined ? ` of ${total}` : ""}`);
|
|
1423
|
+
} else
|
|
1424
|
+
summary.push(plural(lines.length, "line"));
|
|
1425
|
+
const lang = text(metadata.language || content.language);
|
|
1426
|
+
if (lang)
|
|
1427
|
+
summary.push(inline(lang));
|
|
1428
|
+
const rows = lines.map((line) => `${line.line}${options.noAnchors ? "" : `:${text(line.hash)}`} | ${safe(text(line.text)).replaceAll("\\n", "\\\\n")}`);
|
|
1429
|
+
const identifier = record(cursor.identifier);
|
|
1430
|
+
if (count(cursor.line) !== undefined)
|
|
1431
|
+
result.metadata.line = cursor.line;
|
|
1432
|
+
if (count(cursor.column) !== undefined)
|
|
1433
|
+
result.metadata.column = cursor.column;
|
|
1434
|
+
if (Object.keys(cursor).length > 0) {
|
|
1435
|
+
const symbol = record(cursor.symbol);
|
|
1436
|
+
const description = [text(identifier.text), text(symbol.qualified_name || symbol.name), text(symbol.kind)].filter(Boolean);
|
|
1437
|
+
rows.unshift(`Cursor: ${cursor.line ?? "?"}:${cursor.column ?? "?"}${description.length > 0 ? ` · ${[...new Set(description)].map(inline).join(" · ")}` : ""}`, "");
|
|
1361
1438
|
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
});
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1439
|
+
result.body = rows.join(`
|
|
1440
|
+
`) || "Empty source selection.";
|
|
1441
|
+
if (start !== undefined && start > 1)
|
|
1442
|
+
result.notices.push(`Earlier lines omitted. Read preceding content with at: line:1 and limit: ${start - 1}.`);
|
|
1443
|
+
if (end !== undefined && total !== undefined && end < total) {
|
|
1444
|
+
result.notices.push(`More lines available. Continue with at: line:${end + 1}.`);
|
|
1445
|
+
} else if (content.truncated === true && (end === undefined || total === undefined)) {
|
|
1446
|
+
result.notices.push(`Content is truncated.${end !== undefined ? ` Continue with at: line:${end + 1}.` : " Select a smaller range with at and limit."}`);
|
|
1447
|
+
}
|
|
1448
|
+
} else {
|
|
1449
|
+
const description = Object.keys(content).length > 0 ? content : output;
|
|
1450
|
+
result.body = fields(description) || "No content in this selection.";
|
|
1451
|
+
}
|
|
1452
|
+
if (tool === "show_structure")
|
|
1453
|
+
result.notices.push("Structure is limited to the requested depth, kind, and outline. Use a deeper depth or focus with at to inspect further.");
|
|
1454
|
+
if (text(output.warning))
|
|
1455
|
+
result.notices.push(safe(text(output.warning)));
|
|
1456
|
+
result.summary = summary.join(" · ") || "complete";
|
|
1457
|
+
return result;
|
|
1375
1458
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
function parse2(schema, value, what) {
|
|
1381
|
-
try {
|
|
1382
|
-
return Value.Parse(OPERATIONS, schema, value);
|
|
1383
|
-
} catch (error) {
|
|
1384
|
-
if (!(error instanceof AssertError))
|
|
1385
|
-
throw error;
|
|
1386
|
-
const detail = error.error;
|
|
1387
|
-
throw new Error(`invalid readseek ${what}${detail?.path ?? ""}: ${detail?.message ?? "invalid value"}`);
|
|
1388
|
-
}
|
|
1459
|
+
function formatShowResult(presentation) {
|
|
1460
|
+
return [presentation.summary, presentation.body, ...presentation.notices.map((notice) => `Note: ${notice}`)].filter(Boolean).join(`
|
|
1461
|
+
|
|
1462
|
+
`);
|
|
1389
1463
|
}
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
var
|
|
1395
|
-
var
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
var SymbolOutput = Type.Object({
|
|
1407
|
-
kind: Str,
|
|
1408
|
-
name: Str,
|
|
1409
|
-
qualified_name: OptStr,
|
|
1410
|
-
start_line: Int,
|
|
1411
|
-
end_line: Int,
|
|
1412
|
-
start_hash: OptStr,
|
|
1413
|
-
end_hash: OptStr,
|
|
1414
|
-
start_anchor: OptStr,
|
|
1415
|
-
end_anchor: OptStr
|
|
1416
|
-
});
|
|
1417
|
-
function mapNodeShape(child) {
|
|
1418
|
-
return Type.Object({
|
|
1419
|
-
kind: Str,
|
|
1420
|
-
name: Str,
|
|
1421
|
-
start_line: Int,
|
|
1422
|
-
end_line: Int,
|
|
1423
|
-
start_anchor: Str,
|
|
1424
|
-
end_anchor: Str,
|
|
1425
|
-
children: Type.Array(child)
|
|
1426
|
-
});
|
|
1464
|
+
// ../readseek-api/src/platform.ts
|
|
1465
|
+
import { accessSync, constants as fsConstants, existsSync, statSync } from "node:fs";
|
|
1466
|
+
import { createRequire } from "node:module";
|
|
1467
|
+
import path2 from "node:path";
|
|
1468
|
+
var READSEEK_BINARY_ENV = "READSEEK_BINARY";
|
|
1469
|
+
var READSEEK_PLATFORM_PACKAGES = {
|
|
1470
|
+
"darwin-arm64": "@jarkkojs/readseek-darwin-arm64",
|
|
1471
|
+
"linux-arm64": "@jarkkojs/readseek-linux-arm64",
|
|
1472
|
+
"linux-x64": "@jarkkojs/readseek-linux-x64",
|
|
1473
|
+
"win32-x64": "@jarkkojs/readseek-win32-x64"
|
|
1474
|
+
};
|
|
1475
|
+
var resolveFromPackage = createRequire(import.meta.url).resolve;
|
|
1476
|
+
function readSeekBinaryName(platform = process.platform) {
|
|
1477
|
+
if (platform === "linux")
|
|
1478
|
+
return "readseek.AppImage";
|
|
1479
|
+
return platform === "win32" ? "readseek.exe" : "readseek";
|
|
1427
1480
|
}
|
|
1428
|
-
function
|
|
1429
|
-
return
|
|
1481
|
+
function readSeekPlatformKey(platform = process.platform, arch = process.arch) {
|
|
1482
|
+
return `${platform}-${arch}`;
|
|
1430
1483
|
}
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
mime: OptStr,
|
|
1513
|
-
format: Str,
|
|
1514
|
-
width: Int,
|
|
1515
|
-
height: Int,
|
|
1516
|
-
animated: Bool,
|
|
1517
|
-
encoding: Type.Optional(Type.Literal("base64")),
|
|
1518
|
-
data: OptStr,
|
|
1519
|
-
ocr: OptStr,
|
|
1520
|
-
caption: OptStr,
|
|
1521
|
-
objects: Type.Optional(Type.Array(DetectedObject))
|
|
1522
|
-
});
|
|
1523
|
-
var TextDetection = Type.Object({
|
|
1524
|
-
kind: Type.Literal("text"),
|
|
1525
|
-
type: Str,
|
|
1526
|
-
file: Str,
|
|
1527
|
-
mime: OptStr
|
|
1528
|
-
});
|
|
1529
|
-
var PdfDetection = Type.Object({
|
|
1530
|
-
kind: Type.Literal("pdf"),
|
|
1531
|
-
type: Type.Literal("application/pdf"),
|
|
1532
|
-
file: Str,
|
|
1533
|
-
mime: OptStr,
|
|
1534
|
-
format: Type.Literal("pdf"),
|
|
1535
|
-
pages: Int
|
|
1536
|
-
});
|
|
1537
|
-
var Detection = Type.Union([SourceDetection, ImageDetection, TextDetection, PdfDetection]);
|
|
1538
|
-
var SourceDetectionInput = Type.Omit(SourceDetection, ["kind"]);
|
|
1539
|
-
var ImageDetectionInput = Type.Omit(ImageDetection, ["kind"]);
|
|
1540
|
-
var TextDetectionInput = Type.Omit(TextDetection, ["kind"]);
|
|
1541
|
-
var PdfDetectionInput = Type.Omit(PdfDetection, ["kind"]);
|
|
1542
|
-
var UnspannedContent = Type.Omit(ReadOutput, ["start_line", "end_line"]);
|
|
1543
|
-
var PdfInspectMetadata = Type.Object({ source: Str, format: Type.Literal("pdf"), pages: Int });
|
|
1544
|
-
var DocumentCache = Type.Object({
|
|
1545
|
-
content_hash: Str,
|
|
1546
|
-
state: Type.Union([Type.Literal("built"), Type.Literal("reused")])
|
|
1547
|
-
});
|
|
1548
|
-
var Identifier = Type.Object({
|
|
1549
|
-
text: Str,
|
|
1550
|
-
start_column: Int,
|
|
1551
|
-
end_column: Int,
|
|
1552
|
-
start_byte: Int,
|
|
1553
|
-
end_byte: Int
|
|
1554
|
-
});
|
|
1555
|
-
var IdentifyEnvelope = Type.Object({
|
|
1556
|
-
file: Str,
|
|
1557
|
-
language: Str,
|
|
1558
|
-
engine: OptStr,
|
|
1559
|
-
line_count: Int,
|
|
1560
|
-
file_hash: Str,
|
|
1561
|
-
line: Int,
|
|
1562
|
-
column: Int,
|
|
1563
|
-
line_hash: Str,
|
|
1564
|
-
identifier: Type.Optional(Identifier),
|
|
1565
|
-
symbol: Type.Optional(Type.Object({
|
|
1566
|
-
name: Str,
|
|
1567
|
-
kind: Str,
|
|
1568
|
-
qualified_name: Str,
|
|
1569
|
-
start_line: Int,
|
|
1570
|
-
end_line: Int
|
|
1571
|
-
}))
|
|
1572
|
-
});
|
|
1573
|
-
var EditOutputSchema = Type.Object({
|
|
1574
|
-
file: Str,
|
|
1575
|
-
before_hash: Str,
|
|
1576
|
-
file_hash: Str,
|
|
1577
|
-
plan_hash: Str,
|
|
1578
|
-
applied: Bool,
|
|
1579
|
-
changed: Bool,
|
|
1580
|
-
edit_count: Int,
|
|
1581
|
-
before_content: OptStr,
|
|
1582
|
-
content: OptStr
|
|
1583
|
-
});
|
|
1584
|
-
var RenameConflictSchema = Type.Object({ line: Int, column: Int, reason: Str });
|
|
1585
|
-
var RenameEditSchema = Type.Object({
|
|
1586
|
-
line: Int,
|
|
1587
|
-
start_column: Int,
|
|
1588
|
-
end_column: Int,
|
|
1589
|
-
start_byte: Int,
|
|
1590
|
-
end_byte: Int,
|
|
1591
|
-
occurrence: Str,
|
|
1592
|
-
line_hash: Str,
|
|
1593
|
-
text: Str
|
|
1594
|
-
});
|
|
1595
|
-
var Conflicts = Type.Array(RenameConflictSchema, { default: [] });
|
|
1596
|
-
var Edits = Type.Array(RenameEditSchema, { default: [] });
|
|
1597
|
-
var RenameFileOutputSchema = Type.Object({
|
|
1598
|
-
file: Str,
|
|
1599
|
-
language: Str,
|
|
1600
|
-
engine: OptStr,
|
|
1601
|
-
file_hash: Str,
|
|
1602
|
-
conflicts: Conflicts,
|
|
1603
|
-
edits: Edits
|
|
1604
|
-
});
|
|
1605
|
-
var RenameOutputSchema = Type.Object({
|
|
1606
|
-
file: Str,
|
|
1607
|
-
language: Str,
|
|
1608
|
-
engine: OptStr,
|
|
1609
|
-
file_hash: Str,
|
|
1610
|
-
old_name: Str,
|
|
1611
|
-
new_name: Str,
|
|
1612
|
-
plan_hash: Str,
|
|
1613
|
-
applied: Bool,
|
|
1614
|
-
conflicts: Conflicts,
|
|
1615
|
-
edits: Edits,
|
|
1616
|
-
others: Type.Array(RenameFileOutputSchema, { default: [] })
|
|
1617
|
-
});
|
|
1618
|
-
var DefEnvelope = Type.Object({
|
|
1619
|
-
locations: Type.Array(Type.Object({
|
|
1620
|
-
file: Str,
|
|
1621
|
-
line: Int,
|
|
1622
|
-
column: Int,
|
|
1623
|
-
line_hash: Str,
|
|
1624
|
-
text: Str,
|
|
1625
|
-
kind: OptStr,
|
|
1626
|
-
name: OptStr,
|
|
1627
|
-
qualified_name: OptStr
|
|
1628
|
-
}))
|
|
1629
|
-
});
|
|
1630
|
-
function parseReadOutput(value) {
|
|
1631
|
-
return parse2(ReadOutput, value, "read output");
|
|
1484
|
+
function resolveReadSeekBinaryOverride(environment = process.env) {
|
|
1485
|
+
const binaryPath = environment[READSEEK_BINARY_ENV];
|
|
1486
|
+
if (binaryPath === undefined)
|
|
1487
|
+
return null;
|
|
1488
|
+
if (!path2.isAbsolute(binaryPath)) {
|
|
1489
|
+
throw new Error(`${READSEEK_BINARY_ENV} must be an absolute path to the readseek executable`);
|
|
1490
|
+
}
|
|
1491
|
+
try {
|
|
1492
|
+
if (!statSync(binaryPath).isFile())
|
|
1493
|
+
throw new Error("not a regular file");
|
|
1494
|
+
accessSync(binaryPath, fsConstants.X_OK);
|
|
1495
|
+
} catch (error) {
|
|
1496
|
+
throw new Error(`${READSEEK_BINARY_ENV} does not point to an executable file: ${binaryPath}`, {
|
|
1497
|
+
cause: error
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1500
|
+
return binaryPath;
|
|
1501
|
+
}
|
|
1502
|
+
function resolveReadSeekBinaryFromPackages(resolver = resolveFromPackage, options = {}) {
|
|
1503
|
+
const resolve = typeof resolver === "function" ? resolver : resolver.resolve.bind(resolver);
|
|
1504
|
+
const platform = options.platform ?? process.platform;
|
|
1505
|
+
const arch = options.arch ?? process.arch;
|
|
1506
|
+
const key = readSeekPlatformKey(platform, arch);
|
|
1507
|
+
const platformPackage = READSEEK_PLATFORM_PACKAGES[key];
|
|
1508
|
+
if (!platformPackage) {
|
|
1509
|
+
const supported = Object.keys(READSEEK_PLATFORM_PACKAGES).join(", ");
|
|
1510
|
+
throw new Error(`@jarkkojs/readseek ships no binary for ${key}; it supports ${supported}`);
|
|
1511
|
+
}
|
|
1512
|
+
const metaPackage = options.metaPackage ?? "@jarkkojs/readseek";
|
|
1513
|
+
let packageJsonPath;
|
|
1514
|
+
try {
|
|
1515
|
+
const metaPackageJson = resolve(`${metaPackage}/package.json`);
|
|
1516
|
+
const metaPackageDir = path2.dirname(metaPackageJson);
|
|
1517
|
+
packageJsonPath = resolve(`${platformPackage}/package.json`, {
|
|
1518
|
+
paths: [metaPackageDir]
|
|
1519
|
+
});
|
|
1520
|
+
} catch {
|
|
1521
|
+
packageJsonPath = resolve(`${platformPackage}/package.json`);
|
|
1522
|
+
}
|
|
1523
|
+
const binaryPath = path2.join(path2.dirname(packageJsonPath), "bin", readSeekBinaryName(platform));
|
|
1524
|
+
accessSync(binaryPath, fsConstants.X_OK);
|
|
1525
|
+
return binaryPath;
|
|
1526
|
+
}
|
|
1527
|
+
// ../readseek-api/src/error.ts
|
|
1528
|
+
var READSEEK_USAGE_HINT = /(?:^|\n)\s*Run readseek(?: [\w-]+)* --help for more information\.?\s*$/;
|
|
1529
|
+
function cleanReadSeekErrorText(stderr) {
|
|
1530
|
+
return stderr.replace(READSEEK_USAGE_HINT, "").replace(/^error:\s*/i, "").replace(/\s*\n\s*/g, " ").trim();
|
|
1531
|
+
}
|
|
1532
|
+
// ../readseek-api/src/kdl.ts
|
|
1533
|
+
import { parse as parse2 } from "@bgotink/kdl";
|
|
1534
|
+
function convertNode(node) {
|
|
1535
|
+
if (node.getTag() !== null)
|
|
1536
|
+
throw new Error("KDL node annotations are not supported");
|
|
1537
|
+
const properties = new Map;
|
|
1538
|
+
for (const entry of node.entries) {
|
|
1539
|
+
const name = entry.getName();
|
|
1540
|
+
if (name === null)
|
|
1541
|
+
throw new Error("KDL positional arguments are not supported");
|
|
1542
|
+
if (properties.has(name))
|
|
1543
|
+
throw new Error(`KDL duplicate property '${name}'`);
|
|
1544
|
+
if (entry.getTag() !== null)
|
|
1545
|
+
throw new Error("KDL value annotations are not supported");
|
|
1546
|
+
const value = entry.getValue();
|
|
1547
|
+
if (value === null)
|
|
1548
|
+
throw new Error("KDL null values are not supported");
|
|
1549
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
1550
|
+
throw new Error("KDL number out of range");
|
|
1551
|
+
}
|
|
1552
|
+
properties.set(name, value);
|
|
1553
|
+
}
|
|
1554
|
+
return {
|
|
1555
|
+
name: node.getName(),
|
|
1556
|
+
properties,
|
|
1557
|
+
children: node.children?.nodes.map(convertNode) ?? []
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
function parseKdlDocument(text) {
|
|
1561
|
+
const document = parse2(text);
|
|
1562
|
+
if (document.nodes.length !== 1)
|
|
1563
|
+
throw new Error("KDL document must contain exactly one root node");
|
|
1564
|
+
return convertNode(document.nodes[0]);
|
|
1632
1565
|
}
|
|
1633
|
-
|
|
1634
|
-
|
|
1566
|
+
|
|
1567
|
+
// ../readseek-api/src/output-kdl.ts
|
|
1568
|
+
function properties(node) {
|
|
1569
|
+
return Object.fromEntries(node.properties);
|
|
1570
|
+
}
|
|
1571
|
+
function children(node, name) {
|
|
1572
|
+
return node.children.filter((child) => child.name === name);
|
|
1573
|
+
}
|
|
1574
|
+
function child(node, name) {
|
|
1575
|
+
return node.children.find((candidate) => candidate.name === name);
|
|
1576
|
+
}
|
|
1577
|
+
function requireProperty(node, name) {
|
|
1578
|
+
const value = node.properties.get(name);
|
|
1579
|
+
if (value === undefined)
|
|
1580
|
+
throw new Error(`invalid readseek KDL: ${node.name} is missing '${name}'`);
|
|
1581
|
+
return value;
|
|
1582
|
+
}
|
|
1583
|
+
function line(node) {
|
|
1584
|
+
const anchor = requireProperty(node, "anchor");
|
|
1585
|
+
const content = requireProperty(node, "content");
|
|
1586
|
+
if (typeof anchor !== "string" || typeof content !== "string") {
|
|
1587
|
+
throw new Error("invalid readseek KDL line");
|
|
1588
|
+
}
|
|
1589
|
+
const match = /^(\d+):([0-9a-f]+)$/.exec(anchor);
|
|
1590
|
+
if (!match)
|
|
1591
|
+
throw new Error(`invalid readseek KDL line anchor: ${anchor}`);
|
|
1592
|
+
return {
|
|
1593
|
+
line: Number(match[1]),
|
|
1594
|
+
hash: match[2],
|
|
1595
|
+
text: content
|
|
1596
|
+
};
|
|
1597
|
+
}
|
|
1598
|
+
function mapNode(node) {
|
|
1599
|
+
return {
|
|
1600
|
+
...properties(node),
|
|
1601
|
+
children: children(node, "node").map(mapNode)
|
|
1602
|
+
};
|
|
1603
|
+
}
|
|
1604
|
+
function sourceContent(node) {
|
|
1605
|
+
const result = properties(node);
|
|
1606
|
+
result.hashlines = children(node, "line").map(line);
|
|
1607
|
+
const objects = children(node, "object");
|
|
1608
|
+
if (objects.length > 0) {
|
|
1609
|
+
result.objects = objects.map((object) => ({
|
|
1610
|
+
label: requireProperty(object, "label"),
|
|
1611
|
+
bbox: [
|
|
1612
|
+
requireProperty(object, "xmin"),
|
|
1613
|
+
requireProperty(object, "ymin"),
|
|
1614
|
+
requireProperty(object, "xmax"),
|
|
1615
|
+
requireProperty(object, "ymax")
|
|
1616
|
+
]
|
|
1617
|
+
}));
|
|
1618
|
+
}
|
|
1619
|
+
return result;
|
|
1620
|
+
}
|
|
1621
|
+
function sourceMap(node) {
|
|
1622
|
+
const root = child(node, "node");
|
|
1623
|
+
return {
|
|
1624
|
+
...properties(node),
|
|
1625
|
+
...root ? { root: mapNode(root) } : {},
|
|
1626
|
+
symbols: children(node, "symbol").map(properties)
|
|
1627
|
+
};
|
|
1628
|
+
}
|
|
1629
|
+
function diagnostics(node) {
|
|
1630
|
+
return {
|
|
1631
|
+
...properties(node),
|
|
1632
|
+
diagnostics: children(node, "diagnostic").map(properties)
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
function identity(node) {
|
|
1636
|
+
const result = properties(node);
|
|
1637
|
+
const identifier = child(node, "identifier");
|
|
1638
|
+
const symbol = child(node, "symbol");
|
|
1639
|
+
if (identifier)
|
|
1640
|
+
result.identifier = properties(identifier);
|
|
1641
|
+
if (symbol)
|
|
1642
|
+
result.symbol = properties(symbol);
|
|
1643
|
+
return result;
|
|
1644
|
+
}
|
|
1645
|
+
function sourceDocument(root) {
|
|
1646
|
+
const result = {};
|
|
1647
|
+
const metadata = child(root, "metadata");
|
|
1648
|
+
const cache = child(root, "cache");
|
|
1649
|
+
const content = child(root, "content");
|
|
1650
|
+
const structure = child(root, "structure");
|
|
1651
|
+
const diagnosticOutput = child(root, "diagnostics");
|
|
1652
|
+
const identityOutput = child(root, "cursor");
|
|
1653
|
+
if (metadata)
|
|
1654
|
+
result.metadata = properties(metadata);
|
|
1655
|
+
if (!metadata && !content && !structure && !diagnosticOutput && !identityOutput && root.properties.has("file"))
|
|
1656
|
+
result.file = root.properties.get("file");
|
|
1657
|
+
const assets = children(root, "asset");
|
|
1658
|
+
if (assets.length > 0)
|
|
1659
|
+
result.assets = assets.map(genericNode);
|
|
1660
|
+
if (cache)
|
|
1661
|
+
result.cache = properties(cache);
|
|
1662
|
+
if (content)
|
|
1663
|
+
result.content = sourceContent(content);
|
|
1664
|
+
if (structure)
|
|
1665
|
+
result.structure = sourceMap(structure);
|
|
1666
|
+
if (diagnosticOutput)
|
|
1667
|
+
result.diagnostics = diagnostics(diagnosticOutput);
|
|
1668
|
+
if (identityOutput)
|
|
1669
|
+
result.cursor = identity(identityOutput);
|
|
1670
|
+
return result;
|
|
1671
|
+
}
|
|
1672
|
+
function hit(node) {
|
|
1673
|
+
return {
|
|
1674
|
+
...properties(node),
|
|
1675
|
+
hashlines: children(node, "line").map(line),
|
|
1676
|
+
captures: children(node, "capture").map((capture) => ({
|
|
1677
|
+
...properties(capture),
|
|
1678
|
+
hashlines: children(capture, "line").map(line)
|
|
1679
|
+
}))
|
|
1680
|
+
};
|
|
1635
1681
|
}
|
|
1636
|
-
function
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1682
|
+
function search(root) {
|
|
1683
|
+
return {
|
|
1684
|
+
...properties(root),
|
|
1685
|
+
results: children(root, "match").map((match) => ({
|
|
1686
|
+
...properties(match),
|
|
1687
|
+
matches: children(match, "hit").map(hit)
|
|
1688
|
+
})),
|
|
1689
|
+
documents: children(root, "document").map((documentNode) => ({
|
|
1690
|
+
...properties(documentNode),
|
|
1691
|
+
hits: children(documentNode, "hit").map(properties)
|
|
1692
|
+
}))
|
|
1693
|
+
};
|
|
1646
1694
|
}
|
|
1647
|
-
function
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1695
|
+
function references(root) {
|
|
1696
|
+
const parsed = children(root, "reference").map((reference) => {
|
|
1697
|
+
const sourceLine = child(reference, "line");
|
|
1698
|
+
if (!sourceLine)
|
|
1699
|
+
throw new Error("invalid readseek KDL reference: missing line");
|
|
1700
|
+
const parsedLine = line(sourceLine);
|
|
1701
|
+
const symbol = child(reference, "symbol");
|
|
1702
|
+
return {
|
|
1703
|
+
...properties(reference),
|
|
1704
|
+
line_hash: parsedLine.hash,
|
|
1705
|
+
text: parsedLine.text,
|
|
1706
|
+
...symbol ? { symbol: properties(symbol) } : {}
|
|
1707
|
+
};
|
|
1708
|
+
});
|
|
1709
|
+
return {
|
|
1710
|
+
references: parsed,
|
|
1711
|
+
locations: parsed.map((reference) => {
|
|
1712
|
+
const { symbol, ...location } = reference;
|
|
1713
|
+
return symbol && typeof symbol === "object" ? { ...location, ...symbol } : location;
|
|
1714
|
+
})
|
|
1715
|
+
};
|
|
1652
1716
|
}
|
|
1653
|
-
function
|
|
1654
|
-
const output = parse2(CheckEnvelope, value, "check output");
|
|
1717
|
+
function definitions(root) {
|
|
1655
1718
|
return {
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1719
|
+
locations: children(root, "definition").map((definition) => {
|
|
1720
|
+
const symbol = child(definition, "symbol");
|
|
1721
|
+
const sourceLine = child(definition, "line");
|
|
1722
|
+
if (!symbol || !sourceLine) {
|
|
1723
|
+
throw new Error("invalid readseek KDL definition: missing symbol or line");
|
|
1724
|
+
}
|
|
1725
|
+
const symbolProperties = properties(symbol);
|
|
1726
|
+
const parsedLine = line(sourceLine);
|
|
1727
|
+
const definitionColumn = definition.properties.get("column");
|
|
1728
|
+
const nameByte = symbol.properties.get("name_byte");
|
|
1729
|
+
const startByte = symbol.properties.get("start_byte");
|
|
1730
|
+
let column = 1;
|
|
1731
|
+
if (typeof definitionColumn === "number") {
|
|
1732
|
+
column = definitionColumn;
|
|
1733
|
+
} else if (typeof nameByte === "number" && typeof startByte === "number") {
|
|
1734
|
+
column = nameByte - startByte + 1;
|
|
1735
|
+
}
|
|
1736
|
+
return {
|
|
1737
|
+
file: requireProperty(definition, "file"),
|
|
1738
|
+
line: parsedLine.line,
|
|
1739
|
+
column,
|
|
1740
|
+
line_hash: parsedLine.hash,
|
|
1741
|
+
text: parsedLine.text,
|
|
1742
|
+
kind: symbolProperties.kind,
|
|
1743
|
+
name: symbolProperties.name,
|
|
1744
|
+
qualified_name: symbolProperties.qualified_name
|
|
1745
|
+
};
|
|
1746
|
+
})
|
|
1660
1747
|
};
|
|
1661
1748
|
}
|
|
1662
|
-
function
|
|
1663
|
-
const
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
return { kind: "image", ...parse2(ImageDetectionInput, value, "detect output") };
|
|
1669
|
-
}
|
|
1670
|
-
if (output.language !== undefined) {
|
|
1671
|
-
return { kind: "source", ...parse2(SourceDetectionInput, value, "detect output") };
|
|
1749
|
+
function edit(root) {
|
|
1750
|
+
const result = properties(root);
|
|
1751
|
+
for (const name of ["before_content", "content"]) {
|
|
1752
|
+
const contentNode = child(root, name);
|
|
1753
|
+
if (contentNode)
|
|
1754
|
+
result[name] = requireProperty(contentNode, "content");
|
|
1672
1755
|
}
|
|
1673
|
-
return
|
|
1756
|
+
return result;
|
|
1674
1757
|
}
|
|
1675
|
-
function
|
|
1676
|
-
const
|
|
1677
|
-
if (
|
|
1678
|
-
throw new Error("invalid readseek
|
|
1679
|
-
return
|
|
1758
|
+
function renameEdit(node) {
|
|
1759
|
+
const sourceLine = child(node, "line");
|
|
1760
|
+
if (!sourceLine)
|
|
1761
|
+
throw new Error("invalid readseek KDL rename edit: missing line");
|
|
1762
|
+
return {
|
|
1763
|
+
...properties(node),
|
|
1764
|
+
text: requireProperty(sourceLine, "content")
|
|
1765
|
+
};
|
|
1680
1766
|
}
|
|
1681
|
-
function
|
|
1682
|
-
const output = value ?? {};
|
|
1683
|
-
if (output.hashlines === undefined || output.start_line !== undefined)
|
|
1684
|
-
return parseReadOutput(value);
|
|
1685
|
-
const spanned = parse2(UnspannedContent, value, "content");
|
|
1767
|
+
function renameFile(node) {
|
|
1686
1768
|
return {
|
|
1687
|
-
...
|
|
1688
|
-
|
|
1689
|
-
|
|
1769
|
+
...properties(node),
|
|
1770
|
+
edits: children(node, "edit").map(renameEdit),
|
|
1771
|
+
conflicts: children(node, "conflict").map(properties)
|
|
1690
1772
|
};
|
|
1691
1773
|
}
|
|
1692
|
-
function
|
|
1693
|
-
return
|
|
1774
|
+
function rename(root) {
|
|
1775
|
+
return {
|
|
1776
|
+
...renameFile(root),
|
|
1777
|
+
others: children(root, "other").map(renameFile)
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
function genericNode(node) {
|
|
1781
|
+
const result = properties(node);
|
|
1782
|
+
const textNode = child(node, "text");
|
|
1783
|
+
const bbox = child(node, "bbox");
|
|
1784
|
+
if (textNode)
|
|
1785
|
+
result.text = requireProperty(textNode, "content");
|
|
1786
|
+
if (bbox)
|
|
1787
|
+
result.bbox = properties(bbox);
|
|
1788
|
+
const objects = children(node, "object");
|
|
1789
|
+
if (objects.length > 0)
|
|
1790
|
+
result.objects = objects.map((object) => ({
|
|
1791
|
+
label: requireProperty(object, "label"),
|
|
1792
|
+
bbox: [requireProperty(object, "x0"), requireProperty(object, "y0"), requireProperty(object, "x1"), requireProperty(object, "y1")]
|
|
1793
|
+
}));
|
|
1794
|
+
return result;
|
|
1795
|
+
}
|
|
1796
|
+
function pdfDocument(root) {
|
|
1797
|
+
return {
|
|
1798
|
+
...properties(root),
|
|
1799
|
+
nodes: children(root, "node").map(genericNode),
|
|
1800
|
+
assets: children(root, "asset").map(genericNode),
|
|
1801
|
+
vision: children(root, "vision").map(genericNode)
|
|
1802
|
+
};
|
|
1694
1803
|
}
|
|
1695
|
-
function
|
|
1696
|
-
|
|
1804
|
+
function parseReadSeekKdl(text) {
|
|
1805
|
+
const root = parseKdlDocument(text);
|
|
1806
|
+
switch (root.name) {
|
|
1807
|
+
case "document":
|
|
1808
|
+
return root.properties.has("format") ? pdfDocument(root) : sourceDocument(root);
|
|
1809
|
+
case "search":
|
|
1810
|
+
return search(root);
|
|
1811
|
+
case "check":
|
|
1812
|
+
return diagnostics(root);
|
|
1813
|
+
case "refs":
|
|
1814
|
+
return references(root);
|
|
1815
|
+
case "def":
|
|
1816
|
+
return definitions(root);
|
|
1817
|
+
case "edit":
|
|
1818
|
+
return edit(root);
|
|
1819
|
+
case "edit-batch":
|
|
1820
|
+
return { ...properties(root), files: children(root, "file").map(edit) };
|
|
1821
|
+
case "rename":
|
|
1822
|
+
return rename(root);
|
|
1823
|
+
default:
|
|
1824
|
+
throw new Error(`unsupported readseek KDL root: ${root.name}`);
|
|
1825
|
+
}
|
|
1697
1826
|
}
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
const document = parse2(PdfInspectMetadata, value, "inspect metadata");
|
|
1704
|
-
return { kind: "pdf", type: "application/pdf", file: document.source, format: "pdf", pages: document.pages };
|
|
1827
|
+
// ../readseek-api/src/process.ts
|
|
1828
|
+
var DEFAULT_READSEEK_TIMEOUT_MS = 120000;
|
|
1829
|
+
var DEFAULT_MAX_OUTPUT_BYTES = 32 * 1024 * 1024;
|
|
1830
|
+
function readSeekProcessEnvironment() {
|
|
1831
|
+
return process.platform === "linux" ? { ...process.env, APPIMAGE_EXTRACT_AND_RUN: "1" } : process.env;
|
|
1705
1832
|
}
|
|
1706
|
-
function
|
|
1707
|
-
return
|
|
1833
|
+
function resolveProcessLimits(options = {}) {
|
|
1834
|
+
return {
|
|
1835
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_READSEEK_TIMEOUT_MS,
|
|
1836
|
+
maxOutputBytes: options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES,
|
|
1837
|
+
cancelable: options.cancelable !== false
|
|
1838
|
+
};
|
|
1708
1839
|
}
|
|
1709
|
-
function
|
|
1710
|
-
|
|
1840
|
+
function readSeekCancellationError(signal) {
|
|
1841
|
+
const reason = signal?.reason;
|
|
1842
|
+
return reason instanceof Error && reason.name !== "AbortError" ? reason : new Error("readseek cancelled");
|
|
1711
1843
|
}
|
|
1712
|
-
function
|
|
1713
|
-
|
|
1844
|
+
function failureFromProcessResult(result, options = {}) {
|
|
1845
|
+
if (options.overflow) {
|
|
1846
|
+
return new Error(`readseek ${options.overflow} exceeded the ${options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES}-byte output limit`);
|
|
1847
|
+
}
|
|
1848
|
+
if (options.timedOut) {
|
|
1849
|
+
return new Error(`readseek timed out after ${options.timeoutMs ?? DEFAULT_READSEEK_TIMEOUT_MS} ms`);
|
|
1850
|
+
}
|
|
1851
|
+
const cleaned = cleanReadSeekErrorText(result.stderr);
|
|
1852
|
+
if (cleaned)
|
|
1853
|
+
return new Error(cleaned);
|
|
1854
|
+
if (result.signal)
|
|
1855
|
+
return new Error(`readseek killed by signal ${result.signal}`);
|
|
1856
|
+
return new Error(`readseek exited with status ${result.exitCode}`);
|
|
1714
1857
|
}
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1858
|
+
function acceptsReadSeekExitCode(args, exitCode) {
|
|
1859
|
+
if (exitCode === 0)
|
|
1860
|
+
return true;
|
|
1861
|
+
if (exitCode !== 1)
|
|
1862
|
+
return false;
|
|
1863
|
+
let index = 0;
|
|
1864
|
+
const valued = new Set(["-C", "--directory", "--output", "--readseek-dir"]);
|
|
1865
|
+
while (index < args.length) {
|
|
1866
|
+
const arg = args[index];
|
|
1867
|
+
if (valued.has(arg)) {
|
|
1868
|
+
index += 2;
|
|
1869
|
+
continue;
|
|
1870
|
+
}
|
|
1871
|
+
if ([...valued].some((flag) => arg.startsWith(`${flag}=`)) || arg === "--kdl") {
|
|
1872
|
+
index++;
|
|
1873
|
+
continue;
|
|
1874
|
+
}
|
|
1875
|
+
return arg === "find" || arg === "check";
|
|
1723
1876
|
}
|
|
1724
|
-
return
|
|
1877
|
+
return false;
|
|
1725
1878
|
}
|
|
1726
|
-
function
|
|
1727
|
-
if (!
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
const result = {};
|
|
1731
|
-
if (output.metadata !== undefined)
|
|
1732
|
-
result.metadata = parseDetectOutput(output.metadata);
|
|
1733
|
-
if (output.cache !== undefined)
|
|
1734
|
-
result.cache = parseDocumentCache(output.cache);
|
|
1735
|
-
if (output.content !== undefined)
|
|
1736
|
-
result.content = output.content;
|
|
1737
|
-
if (output.map !== undefined)
|
|
1738
|
-
result.map = parseMapOutput(output.map);
|
|
1739
|
-
if (output.diagnostics !== undefined)
|
|
1740
|
-
result.diagnostics = parseCheckOutput(output.diagnostics);
|
|
1741
|
-
if (output.identity !== undefined)
|
|
1742
|
-
result.identity = parseIdentifyOutput(output.identity);
|
|
1743
|
-
return result;
|
|
1879
|
+
function ensureSuccessfulStdout(result, args = []) {
|
|
1880
|
+
if (!result.signal && acceptsReadSeekExitCode(args, result.exitCode))
|
|
1881
|
+
return result.stdout;
|
|
1882
|
+
throw failureFromProcessResult(result);
|
|
1744
1883
|
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1884
|
+
// ../readseek-api/src/process-node.ts
|
|
1885
|
+
import { spawn } from "node:child_process";
|
|
1886
|
+
import { StringDecoder } from "node:string_decoder";
|
|
1887
|
+
function attachBoundedStream(stream, maxOutputBytes, onOverflow) {
|
|
1888
|
+
const decoder = new StringDecoder("utf8");
|
|
1889
|
+
let text = "";
|
|
1890
|
+
let bytes = 0;
|
|
1891
|
+
let overflowed = false;
|
|
1892
|
+
stream.on("data", (chunk) => {
|
|
1893
|
+
if (overflowed)
|
|
1894
|
+
return;
|
|
1895
|
+
const buffer = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
1896
|
+
bytes += buffer.length;
|
|
1897
|
+
if (bytes > maxOutputBytes) {
|
|
1898
|
+
overflowed = true;
|
|
1899
|
+
onOverflow();
|
|
1900
|
+
return;
|
|
1901
|
+
}
|
|
1902
|
+
text += decoder.write(buffer);
|
|
1903
|
+
});
|
|
1904
|
+
return {
|
|
1905
|
+
text: () => text + decoder.end()
|
|
1906
|
+
};
|
|
1907
|
+
}
|
|
1908
|
+
async function runReadSeekNode(binaryPath, args, options = {}) {
|
|
1909
|
+
const limits = resolveProcessLimits(options);
|
|
1910
|
+
if (limits.cancelable && options.signal?.aborted) {
|
|
1911
|
+
throw readSeekCancellationError(options.signal);
|
|
1760
1912
|
}
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1913
|
+
return await new Promise((resolve, reject) => {
|
|
1914
|
+
let settled = false;
|
|
1915
|
+
let child;
|
|
1916
|
+
let timer;
|
|
1917
|
+
const finish = (result) => {
|
|
1918
|
+
if (settled)
|
|
1919
|
+
return;
|
|
1920
|
+
settled = true;
|
|
1921
|
+
if (timer !== undefined)
|
|
1922
|
+
clearTimeout(timer);
|
|
1923
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
1924
|
+
resolve(result);
|
|
1767
1925
|
};
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1926
|
+
const fail = (error) => {
|
|
1927
|
+
if (settled)
|
|
1928
|
+
return;
|
|
1929
|
+
settled = true;
|
|
1930
|
+
if (timer !== undefined)
|
|
1931
|
+
clearTimeout(timer);
|
|
1932
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
1933
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
1934
|
+
};
|
|
1935
|
+
const terminate = (error) => {
|
|
1936
|
+
if (settled)
|
|
1937
|
+
return;
|
|
1938
|
+
fail(error);
|
|
1939
|
+
try {
|
|
1940
|
+
child.kill("SIGKILL");
|
|
1941
|
+
} catch {}
|
|
1942
|
+
};
|
|
1943
|
+
const onAbort = () => {
|
|
1944
|
+
terminate(readSeekCancellationError(options.signal));
|
|
1945
|
+
};
|
|
1946
|
+
try {
|
|
1947
|
+
child = spawn(binaryPath, [...args], {
|
|
1948
|
+
cwd: options.cwd,
|
|
1949
|
+
env: readSeekProcessEnvironment(),
|
|
1950
|
+
shell: false,
|
|
1951
|
+
stdio: [options.stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"],
|
|
1952
|
+
windowsHide: true
|
|
1953
|
+
});
|
|
1954
|
+
} catch (error) {
|
|
1955
|
+
fail(error);
|
|
1956
|
+
return;
|
|
1957
|
+
}
|
|
1958
|
+
const stdout = attachBoundedStream(child.stdout, limits.maxOutputBytes, () => {
|
|
1959
|
+
terminate(failureFromProcessResult({ stdout: "", stderr: "", exitCode: null, signal: null }, { overflow: "stdout", maxOutputBytes: limits.maxOutputBytes }));
|
|
1960
|
+
});
|
|
1961
|
+
const stderr = attachBoundedStream(child.stderr, limits.maxOutputBytes, () => {
|
|
1962
|
+
terminate(failureFromProcessResult({ stdout: "", stderr: "", exitCode: null, signal: null }, { overflow: "stderr", maxOutputBytes: limits.maxOutputBytes }));
|
|
1796
1963
|
});
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
if (metadata.kind === "image") {
|
|
1804
|
-
return { metadata, image: parseImageContent(content) };
|
|
1964
|
+
timer = setTimeout(() => {
|
|
1965
|
+
terminate(failureFromProcessResult({ stdout: "", stderr: "", exitCode: null, signal: null }, { timedOut: true, timeoutMs: limits.timeoutMs }));
|
|
1966
|
+
}, limits.timeoutMs);
|
|
1967
|
+
timer.unref?.();
|
|
1968
|
+
if (limits.cancelable && options.signal) {
|
|
1969
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
1805
1970
|
}
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
inspectText,
|
|
1811
|
-
inspectContent,
|
|
1812
|
-
async extract(target, selector, output, options = {}) {
|
|
1813
|
-
await runner.text(buildExtractArgs({
|
|
1814
|
-
path: target,
|
|
1815
|
-
selector,
|
|
1816
|
-
output,
|
|
1817
|
-
stdinName: options.stdinName
|
|
1818
|
-
}), {
|
|
1819
|
-
signal: options.signal,
|
|
1820
|
-
stdin: options.stdin,
|
|
1821
|
-
timeoutMs: options.timeoutMs,
|
|
1822
|
-
cancelable: options.cancelable
|
|
1823
|
-
});
|
|
1824
|
-
},
|
|
1825
|
-
async image(filePath, modes, options = {}) {
|
|
1826
|
-
if (modes.length === 0)
|
|
1827
|
-
throw new Error(`readseek returned no image analysis for ${filePath}`);
|
|
1828
|
-
const inspected = await inspectContent(filePath, {
|
|
1829
|
-
visionMode: modes.length > 1 ? "all" : modes[0],
|
|
1830
|
-
signal: options.signal,
|
|
1831
|
-
vision: true
|
|
1832
|
-
});
|
|
1833
|
-
if (!inspected.image)
|
|
1834
|
-
throw new Error(`readseek returned no image analysis for ${filePath}`);
|
|
1835
|
-
return inspected.image;
|
|
1836
|
-
},
|
|
1837
|
-
async preparedImage(filePath, options = {}) {
|
|
1838
|
-
const inspected = await inspectContent(filePath, {
|
|
1839
|
-
visionMode: "none",
|
|
1840
|
-
signal: options.signal
|
|
1841
|
-
});
|
|
1842
|
-
const output = inspected.image;
|
|
1843
|
-
if (!output || output.encoding !== "base64" || output.data === undefined || output.mime === undefined) {
|
|
1844
|
-
throw new Error(`readseek returned no prepared image for ${filePath}`);
|
|
1845
|
-
}
|
|
1846
|
-
return { mime: output.mime, encoding: output.encoding, data: output.data };
|
|
1847
|
-
},
|
|
1848
|
-
async search(target, pattern, options = {}) {
|
|
1849
|
-
const args = buildSearchArgs({
|
|
1850
|
-
path: target,
|
|
1851
|
-
pattern,
|
|
1852
|
-
language: options.language,
|
|
1853
|
-
limit: options.limit,
|
|
1854
|
-
gitScope: options.gitScope
|
|
1855
|
-
});
|
|
1856
|
-
return parseSearchOutput(await runner.kdl(args, { signal: options.signal }));
|
|
1857
|
-
},
|
|
1858
|
-
async refs(target, options = {}) {
|
|
1859
|
-
const args = buildRefsArgs({
|
|
1860
|
-
path: target,
|
|
1861
|
-
name: options.name,
|
|
1862
|
-
scope: options.scope,
|
|
1863
|
-
line: options.line,
|
|
1864
|
-
column: options.column,
|
|
1865
|
-
language: options.language,
|
|
1866
|
-
gitScope: options.gitScope
|
|
1867
|
-
});
|
|
1868
|
-
return parseRefsOutput(await runner.kdl(args, { signal: options.signal }));
|
|
1869
|
-
},
|
|
1870
|
-
async def(target, options) {
|
|
1871
|
-
const args = buildDefArgs({
|
|
1872
|
-
path: target,
|
|
1873
|
-
name: options.name,
|
|
1874
|
-
language: options.language,
|
|
1875
|
-
gitScope: options.gitScope
|
|
1876
|
-
});
|
|
1877
|
-
return parseDefOutput(await runner.kdl(args, { signal: options.signal }));
|
|
1878
|
-
},
|
|
1879
|
-
async check(filePath, content, options = {}) {
|
|
1880
|
-
const envelope = await inspectSource("-", {
|
|
1881
|
-
select: "diagnostics",
|
|
1882
|
-
stdin: content,
|
|
1883
|
-
stdinName: filePath,
|
|
1884
|
-
signal: options.signal
|
|
1885
|
-
});
|
|
1886
|
-
if (!envelope.diagnostics)
|
|
1887
|
-
throw new Error("invalid readseek diagnostics: missing diagnostics facet");
|
|
1888
|
-
return envelope.diagnostics;
|
|
1889
|
-
},
|
|
1890
|
-
async identify(filePath, content, options) {
|
|
1891
|
-
const envelope = await inspectSource("-", {
|
|
1892
|
-
select: "identity",
|
|
1893
|
-
stdin: content,
|
|
1894
|
-
stdinName: filePath,
|
|
1895
|
-
selector: buildLineLocation(options.line, options.column),
|
|
1896
|
-
language: options.language,
|
|
1897
|
-
signal: options.signal
|
|
1898
|
-
});
|
|
1899
|
-
if (!envelope.identity)
|
|
1900
|
-
throw new Error("invalid readseek identity: missing identity facet");
|
|
1901
|
-
return envelope.identity;
|
|
1902
|
-
},
|
|
1903
|
-
async detect(filePath, options = {}) {
|
|
1904
|
-
const inspected = await inspect(filePath, { select: "metadata", signal: options.signal });
|
|
1905
|
-
const pdf = parsePdfInspectMetadata(inspected);
|
|
1906
|
-
if (pdf)
|
|
1907
|
-
return pdf;
|
|
1908
|
-
return requireInspectMetadata(parseSourceInspectEnvelope(inspected), "metadata");
|
|
1909
|
-
},
|
|
1910
|
-
async edit(filePath, edits, options = {}) {
|
|
1911
|
-
const args = buildEditArgs({
|
|
1912
|
-
path: filePath,
|
|
1913
|
-
apply: options.apply,
|
|
1914
|
-
planHash: options.planHash,
|
|
1915
|
-
language: options.language,
|
|
1916
|
-
requestFromStdin: true
|
|
1917
|
-
});
|
|
1918
|
-
return parseEditOutput(await runner.kdl(args, {
|
|
1919
|
-
signal: options.signal,
|
|
1920
|
-
stdin: serializeEditPlan(edits),
|
|
1921
|
-
timeoutMs: options.timeoutMs,
|
|
1922
|
-
cancelable: options.cancelable
|
|
1923
|
-
}));
|
|
1924
|
-
},
|
|
1925
|
-
async rename(filePath, options) {
|
|
1926
|
-
const args = buildRenameArgs({
|
|
1927
|
-
path: filePath,
|
|
1928
|
-
to: options.to,
|
|
1929
|
-
line: options.line,
|
|
1930
|
-
column: options.column,
|
|
1931
|
-
workspace: options.workspace,
|
|
1932
|
-
apply: options.apply,
|
|
1933
|
-
planHash: options.planHash,
|
|
1934
|
-
language: options.language,
|
|
1935
|
-
gitScope: options.gitScope
|
|
1971
|
+
if (options.stdin !== undefined) {
|
|
1972
|
+
child.stdin.on("error", (error) => {
|
|
1973
|
+
if (error.code !== "EPIPE")
|
|
1974
|
+
fail(error);
|
|
1936
1975
|
});
|
|
1937
|
-
|
|
1976
|
+
child.stdin.end(options.stdin, "utf8");
|
|
1938
1977
|
}
|
|
1939
|
-
|
|
1978
|
+
child.on("error", fail);
|
|
1979
|
+
child.on("close", (code, signal) => {
|
|
1980
|
+
finish({
|
|
1981
|
+
stdout: stdout.text(),
|
|
1982
|
+
stderr: stderr.text(),
|
|
1983
|
+
exitCode: code,
|
|
1984
|
+
signal
|
|
1985
|
+
});
|
|
1986
|
+
});
|
|
1987
|
+
});
|
|
1988
|
+
}
|
|
1989
|
+
async function runReadSeekNodeText(binaryPath, args, options = {}) {
|
|
1990
|
+
return ensureSuccessfulStdout(await runReadSeekNode(binaryPath, args, options), args);
|
|
1940
1991
|
}
|
|
1941
1992
|
// src/tool-prompt-metadata.ts
|
|
1942
1993
|
var REPLACEABLE_BY_TOOL = new Map(REPLACEABLE_TOOLS.map((entry) => [entry.tool, entry]));
|
|
@@ -2415,7 +2466,7 @@ function formatReplaceHint(edits, noopEdits) {
|
|
|
2415
2466
|
return;
|
|
2416
2467
|
if (counts.replace !== counts.total)
|
|
2417
2468
|
return;
|
|
2418
|
-
return "[info: this edit used replace (unverified). For safer future edits, prefer set_line/replace_lines with an anchor from
|
|
2469
|
+
return "[info: this edit used replace (unverified). For safer future edits, prefer set_line/replace_lines with an anchor from readSeek_show_content/readSeek_find_text/readSeek_find_nodes.]";
|
|
2419
2470
|
}
|
|
2420
2471
|
function buildEditOutput(input) {
|
|
2421
2472
|
const summary = `Updated ${input.displayPath}`;
|
|
@@ -2508,17 +2559,17 @@ function classifyEdit(oldContent, newContent) {
|
|
|
2508
2559
|
import { existsSync as existsSync2, statSync as statSync3 } from "node:fs";
|
|
2509
2560
|
import { createRequire as createRequire2 } from "node:module";
|
|
2510
2561
|
import { homedir as homedir2 } from "node:os";
|
|
2511
|
-
import
|
|
2562
|
+
import path3 from "node:path";
|
|
2512
2563
|
|
|
2513
2564
|
// src/readseek-settings.ts
|
|
2514
2565
|
import { readFileSync, statSync as statSync2 } from "node:fs";
|
|
2515
2566
|
import { homedir } from "node:os";
|
|
2516
2567
|
import { join } from "node:path";
|
|
2517
2568
|
var READSEEK_TOOL_REPLACEMENTS = {
|
|
2518
|
-
read: "
|
|
2519
|
-
edit: "
|
|
2520
|
-
grep: "
|
|
2521
|
-
write: "
|
|
2569
|
+
read: "readSeek_show_content",
|
|
2570
|
+
edit: "readSeek_edit_text",
|
|
2571
|
+
grep: "readSeek_find_text",
|
|
2572
|
+
write: "readSeek_write_file"
|
|
2522
2573
|
};
|
|
2523
2574
|
var READSEEK_KEYS = ["overrideTools", "imageMode", "syntaxValidation", "timeoutMs", "grep", "display"];
|
|
2524
2575
|
var READSEEK_GREP_KEYS = ["maxLines", "maxBytes"];
|
|
@@ -2735,7 +2786,7 @@ function readSeekBinaryAvailability() {
|
|
|
2735
2786
|
var defaultReadSeekDirInit;
|
|
2736
2787
|
function defaultReadSeekDir() {
|
|
2737
2788
|
const home = homedir2();
|
|
2738
|
-
return home ?
|
|
2789
|
+
return home ? path3.join(home, ".pi", "readseek") : null;
|
|
2739
2790
|
}
|
|
2740
2791
|
function directoryExists(dir) {
|
|
2741
2792
|
try {
|
|
@@ -2754,24 +2805,39 @@ async function ensureDefaultReadSeekDir() {
|
|
|
2754
2805
|
return null;
|
|
2755
2806
|
if (directoryExists(dir))
|
|
2756
2807
|
return dir;
|
|
2757
|
-
defaultReadSeekDirInit ??= runReadSeekNodeText(readSeekBinaryPath(), ["--readseek-dir", dir, "
|
|
2808
|
+
defaultReadSeekDirInit ??= runReadSeekNodeText(readSeekBinaryPath(), ["--readseek-dir", dir, "initialize", "index"], { timeoutMs: readSeekTimeoutMs() }).then(() => directoryExists(dir) ? dir : null).catch(() => null).finally(() => {
|
|
2758
2809
|
defaultReadSeekDirInit = undefined;
|
|
2759
2810
|
});
|
|
2760
2811
|
return defaultReadSeekDirInit;
|
|
2761
2812
|
}
|
|
2762
2813
|
function hasProjectReadSeekDir(cwd = process.cwd()) {
|
|
2763
|
-
let dir =
|
|
2814
|
+
let dir = path3.resolve(cwd);
|
|
2764
2815
|
while (true) {
|
|
2765
|
-
if (existsSync2(
|
|
2816
|
+
if (existsSync2(path3.join(dir, ".readseek")))
|
|
2766
2817
|
return true;
|
|
2767
|
-
const parent =
|
|
2818
|
+
const parent = path3.dirname(dir);
|
|
2768
2819
|
if (parent === dir)
|
|
2769
2820
|
return false;
|
|
2770
2821
|
dir = parent;
|
|
2771
2822
|
}
|
|
2772
2823
|
}
|
|
2824
|
+
function supportsReadSeekDir(args) {
|
|
2825
|
+
const first = args[0];
|
|
2826
|
+
const second = args[1];
|
|
2827
|
+
if (first === "initialize" && second === "index")
|
|
2828
|
+
return true;
|
|
2829
|
+
if (first === "show" && (second === "content" || second === "assets"))
|
|
2830
|
+
return true;
|
|
2831
|
+
if (first === "generate" && second === "caption")
|
|
2832
|
+
return true;
|
|
2833
|
+
if (first === "detect" && second === "objects")
|
|
2834
|
+
return true;
|
|
2835
|
+
if (first === "recognize" && second === "text")
|
|
2836
|
+
return true;
|
|
2837
|
+
return false;
|
|
2838
|
+
}
|
|
2773
2839
|
async function readSeekInvocationArgs(args) {
|
|
2774
|
-
if (hasProjectReadSeekDir())
|
|
2840
|
+
if (!supportsReadSeekDir(args) || hasProjectReadSeekDir())
|
|
2775
2841
|
return [...args];
|
|
2776
2842
|
const readSeekDir = await ensureDefaultReadSeekDir();
|
|
2777
2843
|
return readSeekDir ? ["--readseek-dir", readSeekDir, ...args] : [...args];
|
|
@@ -2825,22 +2891,24 @@ var runner = {
|
|
|
2825
2891
|
}),
|
|
2826
2892
|
kdl: async (args, options = {}) => parseReadSeekKdl(await runner.text(args, options))
|
|
2827
2893
|
};
|
|
2828
|
-
var
|
|
2829
|
-
var
|
|
2830
|
-
var
|
|
2831
|
-
var
|
|
2832
|
-
var
|
|
2833
|
-
var
|
|
2834
|
-
var
|
|
2835
|
-
var
|
|
2836
|
-
var
|
|
2894
|
+
var client2 = createReadSeekClient(runner);
|
|
2895
|
+
var readSeekShowContent = client2.showContent;
|
|
2896
|
+
var readSeekShowStructure = client2.showStructure;
|
|
2897
|
+
var readSeekShowMetadata = client2.showMetadata;
|
|
2898
|
+
var readSeekShowAssets = client2.showAssets;
|
|
2899
|
+
var readSeekGenerateCaption = client2.generateCaption;
|
|
2900
|
+
var readSeekDetectObjects = client2.detectObjects;
|
|
2901
|
+
var readSeekRecognizeText = client2.recognizeText;
|
|
2902
|
+
var readSeekFindNodes = client2.findNodes;
|
|
2903
|
+
var readSeekFindReferences = client2.findReferences;
|
|
2904
|
+
var readSeekFindDefinitions = client2.findDefinitions;
|
|
2905
|
+
var readSeekCheckSyntax = client2.checkSyntax;
|
|
2906
|
+
var readSeekCheckRules = client2.checkRules;
|
|
2907
|
+
var readSeekEditText = client2.editText;
|
|
2908
|
+
var readSeekRenameSymbol = client2.renameSymbol;
|
|
2837
2909
|
async function readSeekMap(filePath, options = {}) {
|
|
2838
|
-
const envelope = await
|
|
2839
|
-
|
|
2840
|
-
depth: DEFAULT_INSPECT_MAP_DEPTH,
|
|
2841
|
-
signal: options.signal
|
|
2842
|
-
});
|
|
2843
|
-
const map = envelope.map;
|
|
2910
|
+
const envelope = await client2.showStructure(filePath, { depth: DEFAULT_STRUCTURE_DEPTH, signal: options.signal });
|
|
2911
|
+
const map = "structure" in envelope ? envelope.structure : undefined;
|
|
2844
2912
|
if (!map || map.language === "unknown" && map.symbols.length === 0)
|
|
2845
2913
|
return null;
|
|
2846
2914
|
return map;
|
|
@@ -2865,8 +2933,8 @@ async function validateSyntaxRegression(input, options = {}) {
|
|
|
2865
2933
|
let before;
|
|
2866
2934
|
let after;
|
|
2867
2935
|
try {
|
|
2868
|
-
before = input.before === undefined ? EMPTY : await
|
|
2869
|
-
after = await
|
|
2936
|
+
before = input.before === undefined ? EMPTY : await readSeekCheckSyntax("-", { stdinName: input.filePath, stdin: input.before, signal: options.signal });
|
|
2937
|
+
after = await readSeekCheckSyntax("-", { stdinName: input.filePath, stdin: input.after, signal: options.signal });
|
|
2870
2938
|
} catch {
|
|
2871
2939
|
return null;
|
|
2872
2940
|
}
|
|
@@ -3552,7 +3620,7 @@ async function executeEdit(opts) {
|
|
|
3552
3620
|
return await withFileMutationQueue(absolutePath, async () => {
|
|
3553
3621
|
throwIfAborted(signal);
|
|
3554
3622
|
if (wasReadInSession && !wasReadInSession(absolutePath)) {
|
|
3555
|
-
const hint = `Call
|
|
3623
|
+
const hint = `Call readSeek_show_content(${JSON.stringify(rawPath)}) first, or use readSeek_find_text, readSeek_find_nodes, or readSeek_write_file to produce fresh anchors for this file.`;
|
|
3556
3624
|
return buildEditError(absolutePath, "file-not-read", `You must get fresh anchors for ${absolutePath} before editing it. ${hint}`, hint);
|
|
3557
3625
|
}
|
|
3558
3626
|
const parsedEdits = validateExactlyOneEditVariant(parsed.edits ?? []);
|
|
@@ -3562,7 +3630,7 @@ async function executeEdit(opts) {
|
|
|
3562
3630
|
const edits = parsedEdits.edits;
|
|
3563
3631
|
let plan;
|
|
3564
3632
|
try {
|
|
3565
|
-
plan = await
|
|
3633
|
+
plan = await readSeekEditText(absolutePath, edits, { signal, language: parsed.language });
|
|
3566
3634
|
} catch (err) {
|
|
3567
3635
|
throwIfAborted(signal);
|
|
3568
3636
|
const failure = classifyReadSeekFailure(err);
|
|
@@ -3598,7 +3666,7 @@ async function executeEdit(opts) {
|
|
|
3598
3666
|
throwIfAborted(signal);
|
|
3599
3667
|
let applied;
|
|
3600
3668
|
try {
|
|
3601
|
-
applied = await
|
|
3669
|
+
applied = await readSeekEditText(absolutePath, edits, {
|
|
3602
3670
|
apply: true,
|
|
3603
3671
|
planHash: plan.plan_hash,
|
|
3604
3672
|
language: parsed.language,
|
|
@@ -3666,9 +3734,9 @@ async function executeEdit(opts) {
|
|
|
3666
3734
|
}
|
|
3667
3735
|
}
|
|
3668
3736
|
function registerEditTool(pi, options = {}) {
|
|
3669
|
-
const name = options.name ?? "
|
|
3737
|
+
const name = options.name ?? "readSeek_edit_text";
|
|
3670
3738
|
const promptMetadata = defineToolPromptMetadata({
|
|
3671
|
-
promptUrl: new URL("../prompts/
|
|
3739
|
+
promptUrl: new URL("../prompts/edit_text.md", import.meta.url),
|
|
3672
3740
|
promptSnippet: "Edit existing files with fresh LINE:HASH anchors",
|
|
3673
3741
|
registeredName: name,
|
|
3674
3742
|
toolAliases: options.toolAliases
|
|
@@ -3696,7 +3764,7 @@ function registerEditTool(pi, options = {}) {
|
|
|
3696
3764
|
const cwd = context.cwd ?? process.cwd();
|
|
3697
3765
|
const argsComplete = context.argsComplete ?? false;
|
|
3698
3766
|
const { path: filePath, suffix } = formatEditCallText(args, argsComplete);
|
|
3699
|
-
let text = theme.fg("toolTitle", theme.bold(
|
|
3767
|
+
let text = theme.fg("toolTitle", theme.bold(name));
|
|
3700
3768
|
if (filePath)
|
|
3701
3769
|
text += ` ${linkToolPath(theme.fg("accent", filePath), filePath, cwd)}`;
|
|
3702
3770
|
else
|
|
@@ -3713,7 +3781,7 @@ function registerEditTool(pi, options = {}) {
|
|
|
3713
3781
|
renderResult(result, options, theme, ...rest) {
|
|
3714
3782
|
const { isPartial, isError, expanded, width, context } = resolveRenderResultContext(options, rest, resolveReadSeekToolDisplayMode("edit") === "expanded");
|
|
3715
3783
|
if (isPartial) {
|
|
3716
|
-
return renderPendingResult(
|
|
3784
|
+
return renderPendingResult(`pending ${name}`, width, theme);
|
|
3717
3785
|
}
|
|
3718
3786
|
const textContent = result.content?.filter((c) => c.type === "text").map((c) => c.text || "").join(`
|
|
3719
3787
|
`) ?? "";
|
|
@@ -3765,7 +3833,7 @@ ${theme.fg("error", info.errorText)}`;
|
|
|
3765
3833
|
|
|
3766
3834
|
// src/grep.ts
|
|
3767
3835
|
import { readFile as fsReadFile2, stat as fsStat } from "node:fs/promises";
|
|
3768
|
-
import
|
|
3836
|
+
import path4 from "node:path";
|
|
3769
3837
|
import { createGrepTool } from "@earendil-works/pi-coding-agent";
|
|
3770
3838
|
import { Type as Type5 } from "@sinclair/typebox";
|
|
3771
3839
|
import { Text as Text4 } from "@earendil-works/pi-tui";
|
|
@@ -4135,7 +4203,7 @@ function formatGrepResultText(input) {
|
|
|
4135
4203
|
|
|
4136
4204
|
// src/coerce-obvious-int.ts
|
|
4137
4205
|
import {
|
|
4138
|
-
coerceObviousBase10Int
|
|
4206
|
+
coerceObviousBase10Int
|
|
4139
4207
|
} from "@jarkkojs/readseek-api";
|
|
4140
4208
|
|
|
4141
4209
|
// src/tui-source-render.ts
|
|
@@ -4255,21 +4323,21 @@ async function executeGrep(opts) {
|
|
|
4255
4323
|
const { toolCallId, params, signal, onUpdate, cwd, onFileAnchored } = opts;
|
|
4256
4324
|
await ensureHashInit();
|
|
4257
4325
|
const rawParams = params;
|
|
4258
|
-
const context =
|
|
4326
|
+
const context = coerceObviousBase10Int(rawParams.context, "context");
|
|
4259
4327
|
if (!context.ok) {
|
|
4260
4328
|
return buildToolErrorResult("grep", "invalid-params-combo", context.message);
|
|
4261
4329
|
}
|
|
4262
4330
|
if (context.value !== undefined && context.value < 0) {
|
|
4263
4331
|
return buildToolErrorResult("grep", "invalid-params-combo", `Invalid context: expected a non-negative integer, received ${context.value}.`);
|
|
4264
4332
|
}
|
|
4265
|
-
const limit =
|
|
4333
|
+
const limit = coerceObviousBase10Int(rawParams.limit, "limit");
|
|
4266
4334
|
if (!limit.ok) {
|
|
4267
4335
|
return buildToolErrorResult("grep", "invalid-limit", limit.message);
|
|
4268
4336
|
}
|
|
4269
4337
|
if (limit.value !== undefined && limit.value < 1) {
|
|
4270
4338
|
return buildToolErrorResult("grep", "invalid-limit", `Invalid limit: expected a positive integer, received ${limit.value}.`);
|
|
4271
4339
|
}
|
|
4272
|
-
const scopeContext =
|
|
4340
|
+
const scopeContext = coerceObviousBase10Int(rawParams.scopeContext, "scopeContext");
|
|
4273
4341
|
if (!scopeContext.ok) {
|
|
4274
4342
|
return buildToolErrorResult("grep", "invalid-params-combo", scopeContext.message);
|
|
4275
4343
|
}
|
|
@@ -4351,7 +4419,7 @@ async function executeGrep(opts) {
|
|
|
4351
4419
|
};
|
|
4352
4420
|
const toAbsolutePath = (displayPath) => {
|
|
4353
4421
|
if (searchPathIsDirectory)
|
|
4354
|
-
return
|
|
4422
|
+
return path4.resolve(searchPath, displayPath);
|
|
4355
4423
|
return searchPath;
|
|
4356
4424
|
};
|
|
4357
4425
|
const groupsByPath = new Map;
|
|
@@ -4539,9 +4607,9 @@ ${warning}` } : item),
|
|
|
4539
4607
|
};
|
|
4540
4608
|
}
|
|
4541
4609
|
function registerGrepTool(pi, options = {}) {
|
|
4542
|
-
const name = options.name ?? "
|
|
4610
|
+
const name = options.name ?? "readSeek_find_text";
|
|
4543
4611
|
const promptMetadata = defineToolPromptMetadata({
|
|
4544
|
-
promptUrl: new URL("../prompts/
|
|
4612
|
+
promptUrl: new URL("../prompts/find_text.md", import.meta.url),
|
|
4545
4613
|
promptSnippet: "Search text or regex with edit-ready LINE:HASH anchors",
|
|
4546
4614
|
registeredName: name
|
|
4547
4615
|
});
|
|
@@ -4568,7 +4636,7 @@ function registerGrepTool(pi, options = {}) {
|
|
|
4568
4636
|
const { pattern, suffix } = formatGrepCallText(args);
|
|
4569
4637
|
const rawPath = typeof args?.path === "string" && args.path !== "." ? args.path : undefined;
|
|
4570
4638
|
const glob = typeof args?.glob === "string" ? args.glob : undefined;
|
|
4571
|
-
let text = `${renderToolLabel(theme,
|
|
4639
|
+
let text = `${renderToolLabel(theme, name)} ${theme.fg("accent", `/${pattern}/`)}`;
|
|
4572
4640
|
if (suffix) {
|
|
4573
4641
|
if (rawPath) {
|
|
4574
4642
|
text += theme.fg("dim", " in ");
|
|
@@ -4584,7 +4652,7 @@ function registerGrepTool(pi, options = {}) {
|
|
|
4584
4652
|
renderResult(result, options, theme, ...rest) {
|
|
4585
4653
|
const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(options, rest, resolveReadSeekToolDisplayMode("grep") === "expanded");
|
|
4586
4654
|
if (isPartial)
|
|
4587
|
-
return renderPendingResult(
|
|
4655
|
+
return renderPendingResult(`pending ${name}`, width, theme);
|
|
4588
4656
|
const content = result.content?.[0];
|
|
4589
4657
|
const textContent = content?.type === "text" ? content.text : "";
|
|
4590
4658
|
if (isError || result.isError)
|
|
@@ -4628,7 +4696,7 @@ function registerGrepTool(pi, options = {}) {
|
|
|
4628
4696
|
}
|
|
4629
4697
|
|
|
4630
4698
|
// src/search.ts
|
|
4631
|
-
import
|
|
4699
|
+
import path5 from "node:path";
|
|
4632
4700
|
import { Type as Type6 } from "@sinclair/typebox";
|
|
4633
4701
|
|
|
4634
4702
|
// src/stat-search-path.ts
|
|
@@ -4687,7 +4755,7 @@ function mergeRanges(ranges) {
|
|
|
4687
4755
|
return merged;
|
|
4688
4756
|
}
|
|
4689
4757
|
var SEARCH_PROMPT_METADATA = defineToolPromptMetadata({
|
|
4690
|
-
promptUrl: new URL("../prompts/
|
|
4758
|
+
promptUrl: new URL("../prompts/find_nodes.md", import.meta.url),
|
|
4691
4759
|
promptSnippet: "Search code by AST structure using ast-grep-style patterns"
|
|
4692
4760
|
});
|
|
4693
4761
|
function linesFromSearchResult(result, ranges) {
|
|
@@ -4713,7 +4781,7 @@ function linesFromSearchResult(result, ranges) {
|
|
|
4713
4781
|
return lines;
|
|
4714
4782
|
}
|
|
4715
4783
|
function readSeekLanguageForPath(language, searchPath, isFile) {
|
|
4716
|
-
if (language === "typescript" && isFile &&
|
|
4784
|
+
if (language === "typescript" && isFile && path5.extname(searchPath).toLowerCase() === ".tsx")
|
|
4717
4785
|
return "tsx";
|
|
4718
4786
|
return language;
|
|
4719
4787
|
}
|
|
@@ -4727,13 +4795,18 @@ async function executeSearch(opts) {
|
|
|
4727
4795
|
const searchPathIsFile = statResult.stats.isFile();
|
|
4728
4796
|
try {
|
|
4729
4797
|
const effectiveLang = readSeekLanguageForPath(p.language, searchPath, searchPathIsFile);
|
|
4730
|
-
const results = await
|
|
4798
|
+
const results = await readSeekFindNodes(searchPath, {
|
|
4799
|
+
pattern: p.pattern,
|
|
4800
|
+
context: p.context,
|
|
4801
|
+
selector: p.selector,
|
|
4802
|
+
rule: p.rule,
|
|
4803
|
+
limit: p.limit,
|
|
4731
4804
|
language: effectiveLang,
|
|
4732
4805
|
gitScope: p.gitScope,
|
|
4733
4806
|
signal
|
|
4734
4807
|
});
|
|
4735
4808
|
if (results.length === 0) {
|
|
4736
|
-
const emptyOutput = buildSearchOutput({ pattern: p.pattern, files: [] });
|
|
4809
|
+
const emptyOutput = buildSearchOutput({ pattern: p.pattern ?? p.context ?? p.rule ?? "", files: [] });
|
|
4737
4810
|
return {
|
|
4738
4811
|
content: [{ type: "text", text: emptyOutput.text }],
|
|
4739
4812
|
details: {
|
|
@@ -4743,8 +4816,8 @@ async function executeSearch(opts) {
|
|
|
4743
4816
|
}
|
|
4744
4817
|
const readSeekFiles = [];
|
|
4745
4818
|
for (const result of results) {
|
|
4746
|
-
const abs =
|
|
4747
|
-
const display =
|
|
4819
|
+
const abs = path5.isAbsolute(result.file) ? result.file : path5.resolve(cwd, result.file);
|
|
4820
|
+
const display = path5.relative(cwd, abs) || abs;
|
|
4748
4821
|
const ranges = result.matches.map((match) => ({ startLine: match.start_line, endLine: match.end_line }));
|
|
4749
4822
|
const mergedRanges = mergeRanges(ranges);
|
|
4750
4823
|
const lines = linesFromSearchResult(result, mergedRanges);
|
|
@@ -4758,7 +4831,7 @@ async function executeSearch(opts) {
|
|
|
4758
4831
|
});
|
|
4759
4832
|
}
|
|
4760
4833
|
if (readSeekFiles.length === 0) {
|
|
4761
|
-
const emptyOutput = buildSearchOutput({ pattern: p.pattern, files: [] });
|
|
4834
|
+
const emptyOutput = buildSearchOutput({ pattern: p.pattern ?? p.rule ?? "", files: [] });
|
|
4762
4835
|
return {
|
|
4763
4836
|
content: [{ type: "text", text: emptyOutput.text }],
|
|
4764
4837
|
details: {
|
|
@@ -4767,7 +4840,7 @@ async function executeSearch(opts) {
|
|
|
4767
4840
|
};
|
|
4768
4841
|
}
|
|
4769
4842
|
const builtOutput = buildSearchOutput({
|
|
4770
|
-
pattern: p.pattern,
|
|
4843
|
+
pattern: p.pattern ?? p.rule ?? "",
|
|
4771
4844
|
files: readSeekFiles
|
|
4772
4845
|
});
|
|
4773
4846
|
for (const readSeekFile of readSeekFiles) {
|
|
@@ -4786,14 +4859,18 @@ async function executeSearch(opts) {
|
|
|
4786
4859
|
}
|
|
4787
4860
|
function registerSearchTool(pi, options = {}) {
|
|
4788
4861
|
const tool = registerReadSeekTool(pi, {
|
|
4789
|
-
name: "
|
|
4862
|
+
name: "readSeek_find_nodes",
|
|
4790
4863
|
label: "Structural Search",
|
|
4791
4864
|
description: SEARCH_PROMPT_METADATA.description,
|
|
4792
4865
|
promptSnippet: SEARCH_PROMPT_METADATA.promptSnippet,
|
|
4793
4866
|
promptGuidelines: SEARCH_PROMPT_METADATA.promptGuidelines,
|
|
4794
4867
|
parameters: Type6.Object({
|
|
4795
|
-
pattern: Type6.String({ description: PARAM_DESCRIPTIONS.pattern }),
|
|
4868
|
+
pattern: Type6.Optional(Type6.String({ description: PARAM_DESCRIPTIONS.pattern })),
|
|
4869
|
+
context: Type6.Optional(Type6.String({ description: PARAM_DESCRIPTIONS.patternContext })),
|
|
4870
|
+
selector: Type6.Optional(Type6.String({ description: PARAM_DESCRIPTIONS.selector })),
|
|
4796
4871
|
language: languageParam(),
|
|
4872
|
+
rule: Type6.Optional(Type6.String({ description: PARAM_DESCRIPTIONS.rule })),
|
|
4873
|
+
limit: Type6.Optional(Type6.Integer({ minimum: 1 })),
|
|
4797
4874
|
path: searchPathParam(),
|
|
4798
4875
|
...readSeekGitSearchParams()
|
|
4799
4876
|
}),
|
|
@@ -4802,14 +4879,14 @@ function registerSearchTool(pi, options = {}) {
|
|
|
4802
4879
|
},
|
|
4803
4880
|
renderCall(args, theme, ...rest) {
|
|
4804
4881
|
return renderReadSeekSearchCall(args, theme, rest, {
|
|
4805
|
-
label: "
|
|
4806
|
-
accent: `/${args.pattern}/`,
|
|
4882
|
+
label: "readSeek_find_nodes",
|
|
4883
|
+
accent: `/${args.pattern ?? args.context ?? args.rule}/`,
|
|
4807
4884
|
flags: [args.gitScope && `git-scope=${args.gitScope}`]
|
|
4808
4885
|
});
|
|
4809
4886
|
},
|
|
4810
4887
|
renderResult(result, options, theme, ...rest) {
|
|
4811
4888
|
return renderAnchoredFilesResult(result, options, theme, rest, {
|
|
4812
|
-
pendingLabel: "pending
|
|
4889
|
+
pendingLabel: "pending readSeek_find_nodes",
|
|
4813
4890
|
emptyLabel: "no matches",
|
|
4814
4891
|
unitSingular: "match",
|
|
4815
4892
|
unitPlural: "matches"
|
|
@@ -4821,15 +4898,14 @@ function registerSearchTool(pi, options = {}) {
|
|
|
4821
4898
|
|
|
4822
4899
|
// src/refs.ts
|
|
4823
4900
|
import { Type as Type7 } from "@sinclair/typebox";
|
|
4824
|
-
import
|
|
4901
|
+
import path6 from "node:path";
|
|
4825
4902
|
|
|
4826
4903
|
// src/refs-output.ts
|
|
4827
4904
|
function refsQueryLabel(input) {
|
|
4828
4905
|
if (input.name)
|
|
4829
4906
|
return input.name;
|
|
4830
|
-
if (input.
|
|
4831
|
-
return input.
|
|
4832
|
-
}
|
|
4907
|
+
if (input.at)
|
|
4908
|
+
return input.at;
|
|
4833
4909
|
return "?";
|
|
4834
4910
|
}
|
|
4835
4911
|
function buildRefsOutput(input) {
|
|
@@ -4853,7 +4929,7 @@ function buildRefsOutput(input) {
|
|
|
4853
4929
|
|
|
4854
4930
|
// src/refs.ts
|
|
4855
4931
|
var REFS_PROMPT_METADATA = defineToolPromptMetadata({
|
|
4856
|
-
promptUrl: new URL("../prompts/
|
|
4932
|
+
promptUrl: new URL("../prompts/find_references.md", import.meta.url),
|
|
4857
4933
|
promptSnippet: "Find references by identifier name or cursor binding"
|
|
4858
4934
|
});
|
|
4859
4935
|
function refsLine(reference) {
|
|
@@ -4866,14 +4942,14 @@ function groupReferences(references, cwd) {
|
|
|
4866
4942
|
const files = new Map;
|
|
4867
4943
|
const seen = new Set;
|
|
4868
4944
|
for (const reference of references) {
|
|
4869
|
-
const abs =
|
|
4945
|
+
const abs = path6.isAbsolute(reference.file) ? reference.file : path6.resolve(cwd, reference.file);
|
|
4870
4946
|
const dedupeKey = `${abs}:${reference.line}:${reference.column}`;
|
|
4871
4947
|
if (seen.has(dedupeKey))
|
|
4872
4948
|
continue;
|
|
4873
4949
|
seen.add(dedupeKey);
|
|
4874
4950
|
let file = files.get(abs);
|
|
4875
4951
|
if (!file) {
|
|
4876
|
-
file = { displayPath:
|
|
4952
|
+
file = { displayPath: path6.relative(cwd, abs) || abs, path: abs, lines: [] };
|
|
4877
4953
|
files.set(abs, file);
|
|
4878
4954
|
}
|
|
4879
4955
|
file.lines.push(refsLine(reference));
|
|
@@ -4886,7 +4962,7 @@ function isReadSeekCursorValidationFailure(message) {
|
|
|
4886
4962
|
async function executeRefs(opts) {
|
|
4887
4963
|
const { params, signal, cwd, onFileAnchored } = opts;
|
|
4888
4964
|
const p = params;
|
|
4889
|
-
const scopeError =
|
|
4965
|
+
const scopeError = validateReferencesSelection(p);
|
|
4890
4966
|
if (!scopeError.ok) {
|
|
4891
4967
|
return buildToolErrorResult("refs", "invalid-parameter", `refs parameter: ${scopeError.error}`);
|
|
4892
4968
|
}
|
|
@@ -4895,11 +4971,9 @@ async function executeRefs(opts) {
|
|
|
4895
4971
|
if (!statResult.ok)
|
|
4896
4972
|
return statResult.error;
|
|
4897
4973
|
try {
|
|
4898
|
-
const references = await
|
|
4974
|
+
const references = await readSeekFindReferences(searchPath, {
|
|
4899
4975
|
name: p.name,
|
|
4900
|
-
|
|
4901
|
-
line: p.line,
|
|
4902
|
-
column: p.column,
|
|
4976
|
+
at: p.at,
|
|
4903
4977
|
language: p.language,
|
|
4904
4978
|
gitScope: p.gitScope,
|
|
4905
4979
|
signal
|
|
@@ -4907,9 +4981,7 @@ async function executeRefs(opts) {
|
|
|
4907
4981
|
const files = groupReferences(references, cwd);
|
|
4908
4982
|
const builtOutput = buildRefsOutput({
|
|
4909
4983
|
name: p.name,
|
|
4910
|
-
|
|
4911
|
-
line: p.line,
|
|
4912
|
-
column: p.column,
|
|
4984
|
+
at: p.at,
|
|
4913
4985
|
files
|
|
4914
4986
|
});
|
|
4915
4987
|
for (const file of files) {
|
|
@@ -4921,7 +4993,7 @@ async function executeRefs(opts) {
|
|
|
4921
4993
|
};
|
|
4922
4994
|
} catch (err) {
|
|
4923
4995
|
const failure = classifyReadSeekFailure(err);
|
|
4924
|
-
if (p.
|
|
4996
|
+
if (p.at && isReadSeekCursorValidationFailure(failure.message)) {
|
|
4925
4997
|
return buildToolErrorResult("refs", "invalid-parameter", failure.message);
|
|
4926
4998
|
}
|
|
4927
4999
|
return buildToolErrorResult("refs", failure.code, failure.message, failure.hint ? { hint: failure.hint } : {});
|
|
@@ -4929,7 +5001,7 @@ async function executeRefs(opts) {
|
|
|
4929
5001
|
}
|
|
4930
5002
|
function registerRefsTool(pi, options = {}) {
|
|
4931
5003
|
const tool = registerReadSeekTool(pi, {
|
|
4932
|
-
name: "
|
|
5004
|
+
name: "readSeek_find_references",
|
|
4933
5005
|
label: "References",
|
|
4934
5006
|
description: REFS_PROMPT_METADATA.description,
|
|
4935
5007
|
promptSnippet: REFS_PROMPT_METADATA.promptSnippet,
|
|
@@ -4938,25 +5010,23 @@ function registerRefsTool(pi, options = {}) {
|
|
|
4938
5010
|
name: Type7.Optional(Type7.String({ description: PARAM_DESCRIPTIONS.refsName })),
|
|
4939
5011
|
path: searchPathParam(),
|
|
4940
5012
|
language: languageParam(),
|
|
4941
|
-
|
|
4942
|
-
line: Type7.Optional(Type7.Number({ description: PARAM_DESCRIPTIONS.line })),
|
|
4943
|
-
column: Type7.Optional(Type7.Number({ description: PARAM_DESCRIPTIONS.column })),
|
|
5013
|
+
at: Type7.Optional(Type7.String({ description: PARAM_DESCRIPTIONS.at })),
|
|
4944
5014
|
...readSeekGitSearchParams()
|
|
4945
5015
|
}),
|
|
4946
5016
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
4947
5017
|
return executeRefs({ params, signal, cwd: ctx.cwd, onFileAnchored: options.onFileAnchored });
|
|
4948
5018
|
},
|
|
4949
5019
|
renderCall(args, theme, ...rest) {
|
|
4950
|
-
const accent = args.name ??
|
|
5020
|
+
const accent = args.name ?? args.at ?? "?";
|
|
4951
5021
|
return renderReadSeekSearchCall(args, theme, rest, {
|
|
4952
|
-
label: "
|
|
5022
|
+
label: "readSeek_find_references",
|
|
4953
5023
|
accent,
|
|
4954
|
-
flags: [args.
|
|
5024
|
+
flags: [args.at && "binding", args.gitScope && `git-scope=${args.gitScope}`]
|
|
4955
5025
|
});
|
|
4956
5026
|
},
|
|
4957
5027
|
renderResult(result, options, theme, ...rest) {
|
|
4958
5028
|
return renderAnchoredFilesResult(result, options, theme, rest, {
|
|
4959
|
-
pendingLabel: "pending
|
|
5029
|
+
pendingLabel: "pending readSeek_find_references",
|
|
4960
5030
|
emptyLabel: "no references",
|
|
4961
5031
|
unitSingular: "reference",
|
|
4962
5032
|
unitPlural: "references"
|
|
@@ -4968,16 +5038,15 @@ function registerRefsTool(pi, options = {}) {
|
|
|
4968
5038
|
|
|
4969
5039
|
// src/rename.ts
|
|
4970
5040
|
import { Type as Type8 } from "@sinclair/typebox";
|
|
4971
|
-
import
|
|
5041
|
+
import path7 from "node:path";
|
|
4972
5042
|
import { Text as Text5 } from "@earendil-works/pi-tui";
|
|
4973
5043
|
var RENAME_PROMPT_METADATA = defineToolPromptMetadata({
|
|
4974
|
-
promptUrl: new URL("../prompts/
|
|
5044
|
+
promptUrl: new URL("../prompts/rename_symbol.md", import.meta.url),
|
|
4975
5045
|
promptSnippet: "Rename a symbol at a source location with verified edits"
|
|
4976
5046
|
});
|
|
4977
5047
|
var renameSchema = Type8.Object({
|
|
4978
5048
|
path: filePathParam(),
|
|
4979
|
-
|
|
4980
|
-
column: Type8.Optional(Type8.Integer({ minimum: 1, description: PARAM_DESCRIPTIONS.column })),
|
|
5049
|
+
at: Type8.String({ description: PARAM_DESCRIPTIONS.at }),
|
|
4981
5050
|
to: Type8.String({ description: PARAM_DESCRIPTIONS.to }),
|
|
4982
5051
|
workspace: Type8.Optional(Type8.Boolean({ description: PARAM_DESCRIPTIONS.workspace })),
|
|
4983
5052
|
apply: Type8.Optional(Type8.Boolean({ description: PARAM_DESCRIPTIONS.applyDefaultTrue }))
|
|
@@ -4988,18 +5057,14 @@ async function executeRename(opts) {
|
|
|
4988
5057
|
if (!p.to.trim()) {
|
|
4989
5058
|
return buildToolErrorResult("rename", "invalid-parameter", "rename parameter 'to' must not be empty");
|
|
4990
5059
|
}
|
|
4991
|
-
if (
|
|
4992
|
-
return buildToolErrorResult("
|
|
4993
|
-
}
|
|
4994
|
-
if (p.column !== undefined && (!Number.isSafeInteger(p.column) || p.column < 1)) {
|
|
4995
|
-
return buildToolErrorResult("rename", "invalid-parameter", "rename parameter 'column' must be a positive integer");
|
|
5060
|
+
if (typeof p.at !== "string" || !/^(line:[1-9][0-9]*|hash:[0-9a-fA-F]+)(:[1-9][0-9]*)?$/.test(p.at)) {
|
|
5061
|
+
return buildToolErrorResult("rename_symbol", "invalid-parameter", "at must be a line or hash location with an optional positive column");
|
|
4996
5062
|
}
|
|
4997
5063
|
const filePath = resolveToCwd2(p.path, cwd);
|
|
4998
5064
|
try {
|
|
4999
|
-
const output = await
|
|
5065
|
+
const output = await readSeekRenameSymbol(filePath, {
|
|
5000
5066
|
to: p.to,
|
|
5001
|
-
|
|
5002
|
-
column: p.column,
|
|
5067
|
+
at: p.at,
|
|
5003
5068
|
workspace: p.workspace ? cwd : undefined,
|
|
5004
5069
|
apply: p.apply ?? true,
|
|
5005
5070
|
signal
|
|
@@ -5008,14 +5073,14 @@ async function executeRename(opts) {
|
|
|
5008
5073
|
for (const file of [output, ...output.others]) {
|
|
5009
5074
|
if (file.edits.length === 0)
|
|
5010
5075
|
continue;
|
|
5011
|
-
const absoluteFile =
|
|
5076
|
+
const absoluteFile = path7.isAbsolute(file.file) ? file.file : path7.resolve(cwd, file.file);
|
|
5012
5077
|
onFileMutated?.(absoluteFile);
|
|
5013
5078
|
}
|
|
5014
5079
|
}
|
|
5015
|
-
const primaryAbs =
|
|
5080
|
+
const primaryAbs = path7.isAbsolute(output.file) ? output.file : path7.resolve(cwd, output.file);
|
|
5016
5081
|
const files = [primaryAbs];
|
|
5017
5082
|
for (const other of output.others) {
|
|
5018
|
-
const abs =
|
|
5083
|
+
const abs = path7.isAbsolute(other.file) ? other.file : path7.resolve(cwd, other.file);
|
|
5019
5084
|
if (!files.includes(abs))
|
|
5020
5085
|
files.push(abs);
|
|
5021
5086
|
}
|
|
@@ -5054,7 +5119,7 @@ First conflict at ${firstConflict.line}:${firstConflict.column}: ${firstConflict
|
|
|
5054
5119
|
}
|
|
5055
5120
|
function registerRenameTool(pi, options = {}) {
|
|
5056
5121
|
registerReadSeekTool(pi, {
|
|
5057
|
-
name: "
|
|
5122
|
+
name: "readSeek_rename_symbol",
|
|
5058
5123
|
label: "Rename",
|
|
5059
5124
|
description: RENAME_PROMPT_METADATA.description,
|
|
5060
5125
|
promptSnippet: RENAME_PROMPT_METADATA.promptSnippet,
|
|
@@ -5067,7 +5132,7 @@ function registerRenameTool(pi, options = {}) {
|
|
|
5067
5132
|
const context = rest[0] ?? {};
|
|
5068
5133
|
const cwd = context.cwd ?? process.cwd();
|
|
5069
5134
|
const displayPath = typeof args?.path === "string" ? args.path : "?";
|
|
5070
|
-
let text = theme.fg("toolTitle", theme.bold("
|
|
5135
|
+
let text = theme.fg("toolTitle", theme.bold("readSeek_rename_symbol"));
|
|
5071
5136
|
text += ` ${linkToolPath(theme.fg("accent", displayPath), displayPath, cwd)}`;
|
|
5072
5137
|
if (args?.to)
|
|
5073
5138
|
text += theme.fg("dim", ` → ${args.to}`);
|
|
@@ -5076,13 +5141,13 @@ function registerRenameTool(pi, options = {}) {
|
|
|
5076
5141
|
renderResult(result, options, theme, ...rest) {
|
|
5077
5142
|
const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(options, rest);
|
|
5078
5143
|
if (isPartial)
|
|
5079
|
-
return renderPendingResult("pending
|
|
5144
|
+
return renderPendingResult("pending readSeek_rename_symbol", width, theme);
|
|
5080
5145
|
const content = result.content?.[0];
|
|
5081
5146
|
const textContent = content?.type === "text" ? content.text : "";
|
|
5082
5147
|
const readSeekValue = result.details?.readSeekValue;
|
|
5083
5148
|
const output = readSeekValue?.output;
|
|
5084
5149
|
if (isError || result.isError) {
|
|
5085
|
-
return renderErrorResult(textContent, { expanded, width, fallback: "
|
|
5150
|
+
return renderErrorResult(textContent, { expanded, width, fallback: "readSeek_rename_symbol failed", theme });
|
|
5086
5151
|
}
|
|
5087
5152
|
const outputs = output ? [output, ...output.others] : [];
|
|
5088
5153
|
const editCount = outputs.reduce((total, file) => total + file.edits.length, 0);
|
|
@@ -5097,7 +5162,7 @@ function registerRenameTool(pi, options = {}) {
|
|
|
5097
5162
|
});
|
|
5098
5163
|
if (expanded && output) {
|
|
5099
5164
|
for (const file of outputs) {
|
|
5100
|
-
const display =
|
|
5165
|
+
const display = path7.relative(cwd, file.file) || file.file;
|
|
5101
5166
|
text += `
|
|
5102
5167
|
${linkedPathLine(theme, file.file, display, cwd, ` (${file.edits.length} edits)`, width)}`;
|
|
5103
5168
|
}
|
|
@@ -5320,7 +5385,7 @@ async function executeWrite(opts) {
|
|
|
5320
5385
|
const warnings = [];
|
|
5321
5386
|
const readSeekWarnings = [];
|
|
5322
5387
|
if (hasBareCarriageReturn(content)) {
|
|
5323
|
-
const message = "File content contains bare CR (\\r) line endings;
|
|
5388
|
+
const message = "File content contains bare CR (\\r) line endings; readSeek_write_file refuses to emit anchors that readSeek_show_content/readSeek_edit_text would normalize differently.";
|
|
5324
5389
|
warnings.push(message);
|
|
5325
5390
|
readSeekWarnings.push(buildReadSeekWarning("bare-cr", message));
|
|
5326
5391
|
return buildToolErrorResult("write", "bare-cr", `Cannot write ${filePath}
|
|
@@ -5399,9 +5464,9 @@ async function executeWrite(opts) {
|
|
|
5399
5464
|
};
|
|
5400
5465
|
}
|
|
5401
5466
|
function registerWriteTool(pi, options = {}) {
|
|
5402
|
-
const name = options.name ?? "
|
|
5467
|
+
const name = options.name ?? "readSeek_write_file";
|
|
5403
5468
|
const promptMetadata = defineToolPromptMetadata({
|
|
5404
|
-
promptUrl: new URL("../prompts/
|
|
5469
|
+
promptUrl: new URL("../prompts/write_file.md", import.meta.url),
|
|
5405
5470
|
promptSnippet: "Create or replace a complete file with LINE:HASH anchors",
|
|
5406
5471
|
registeredName: name
|
|
5407
5472
|
});
|
|
@@ -5453,7 +5518,7 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5453
5518
|
renderCall(args, theme, context = {}) {
|
|
5454
5519
|
const { path, content } = args;
|
|
5455
5520
|
const cwd = context.cwd ?? process.cwd();
|
|
5456
|
-
const label = renderToolLabel(theme,
|
|
5521
|
+
const label = renderToolLabel(theme, name);
|
|
5457
5522
|
const lineCount = typeof content === "string" ? content.split(`
|
|
5458
5523
|
`).length : 0;
|
|
5459
5524
|
const bytes = typeof content === "string" ? Buffer.byteLength(content, "utf8") : 0;
|
|
@@ -5475,11 +5540,11 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5475
5540
|
renderResult(result, options, theme, ...rest) {
|
|
5476
5541
|
const { isPartial, expanded, width, context } = resolveRenderResultContext(options, rest, resolveReadSeekToolDisplayMode("write") === "expanded");
|
|
5477
5542
|
if (isPartial)
|
|
5478
|
-
return renderPendingResult(
|
|
5543
|
+
return renderPendingResult(`pending ${name}`, width, theme);
|
|
5479
5544
|
const details = result.details ?? {};
|
|
5480
5545
|
const output = result.content?.[0]?.type === "text" ? result.content[0].text : "";
|
|
5481
5546
|
if (result.isError || details.readSeekValue?.ok === false) {
|
|
5482
|
-
return renderErrorResult(output, { expanded, width, fallback:
|
|
5547
|
+
return renderErrorResult(output, { expanded, width, fallback: `${name} failed`, theme });
|
|
5483
5548
|
}
|
|
5484
5549
|
const diffData = details.diffData;
|
|
5485
5550
|
const state = details.writeState === "overwritten" ? "overwritten" : "created";
|
|
@@ -5513,23 +5578,23 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5513
5578
|
|
|
5514
5579
|
// src/def.ts
|
|
5515
5580
|
import { Type as Type10 } from "@sinclair/typebox";
|
|
5516
|
-
import
|
|
5581
|
+
import path8 from "node:path";
|
|
5517
5582
|
var DEF_PROMPT_METADATA = defineToolPromptMetadata({
|
|
5518
|
-
promptUrl: new URL("../prompts/
|
|
5583
|
+
promptUrl: new URL("../prompts/find_definitions.md", import.meta.url),
|
|
5519
5584
|
promptSnippet: "Go to a symbol definition or declaration by name"
|
|
5520
5585
|
});
|
|
5521
5586
|
async function executeDef(opts) {
|
|
5522
5587
|
const { params, signal, cwd, onFileAnchored } = opts;
|
|
5523
5588
|
const p = params;
|
|
5524
5589
|
if (!p.name || !p.name.trim()) {
|
|
5525
|
-
return buildToolErrorResult("def", "invalid-parameter", "
|
|
5590
|
+
return buildToolErrorResult("def", "invalid-parameter", "readSeek_find_definitions requires 'name'");
|
|
5526
5591
|
}
|
|
5527
5592
|
const searchPath = resolveToCwd2(p.path ?? ".", cwd);
|
|
5528
5593
|
const statResult = await statSearchPathOrError("def", p.path, searchPath);
|
|
5529
5594
|
if (!statResult.ok)
|
|
5530
5595
|
return statResult.error;
|
|
5531
5596
|
try {
|
|
5532
|
-
const definitions = await
|
|
5597
|
+
const definitions = await readSeekFindDefinitions(searchPath, {
|
|
5533
5598
|
name: p.name,
|
|
5534
5599
|
language: p.language,
|
|
5535
5600
|
gitScope: p.gitScope,
|
|
@@ -5545,10 +5610,10 @@ async function executeDef(opts) {
|
|
|
5545
5610
|
}
|
|
5546
5611
|
const files = new Map;
|
|
5547
5612
|
for (const def of definitions) {
|
|
5548
|
-
const abs =
|
|
5613
|
+
const abs = path8.isAbsolute(def.file) ? def.file : path8.resolve(cwd, def.file);
|
|
5549
5614
|
let file = files.get(abs);
|
|
5550
5615
|
if (!file) {
|
|
5551
|
-
file = { displayPath:
|
|
5616
|
+
file = { displayPath: path8.relative(cwd, abs) || abs, path: abs, lines: [] };
|
|
5552
5617
|
files.set(abs, file);
|
|
5553
5618
|
}
|
|
5554
5619
|
file.lines.push(buildReadSeekLineWithHash(def.line, def.line_hash, def.text));
|
|
@@ -5578,7 +5643,7 @@ async function executeDef(opts) {
|
|
|
5578
5643
|
}
|
|
5579
5644
|
function registerDefTool(pi, options = {}) {
|
|
5580
5645
|
registerReadSeekTool(pi, {
|
|
5581
|
-
name: "
|
|
5646
|
+
name: "readSeek_find_definitions",
|
|
5582
5647
|
label: "Definition",
|
|
5583
5648
|
description: DEF_PROMPT_METADATA.description,
|
|
5584
5649
|
promptSnippet: DEF_PROMPT_METADATA.promptSnippet,
|
|
@@ -5594,14 +5659,14 @@ function registerDefTool(pi, options = {}) {
|
|
|
5594
5659
|
},
|
|
5595
5660
|
renderCall(args, theme, ...rest) {
|
|
5596
5661
|
return renderReadSeekSearchCall(args, theme, rest, {
|
|
5597
|
-
label: "
|
|
5662
|
+
label: "readSeek_find_definitions",
|
|
5598
5663
|
accent: args.name,
|
|
5599
5664
|
flags: [args.gitScope && `git-scope=${args.gitScope}`]
|
|
5600
5665
|
});
|
|
5601
5666
|
},
|
|
5602
5667
|
renderResult(result, options, theme, ...rest) {
|
|
5603
5668
|
return renderAnchoredFilesResult(result, options, theme, rest, {
|
|
5604
|
-
pendingLabel: "pending
|
|
5669
|
+
pendingLabel: "pending readSeek_find_definitions",
|
|
5605
5670
|
emptyLabel: "no definitions",
|
|
5606
5671
|
unitSingular: "definition",
|
|
5607
5672
|
unitPlural: "definitions"
|
|
@@ -5610,379 +5675,157 @@ function registerDefTool(pi, options = {}) {
|
|
|
5610
5675
|
});
|
|
5611
5676
|
}
|
|
5612
5677
|
|
|
5613
|
-
// src/
|
|
5678
|
+
// src/access.ts
|
|
5614
5679
|
import { Text as Text7 } from "@earendil-works/pi-tui";
|
|
5615
5680
|
import { Type as Type11 } from "@sinclair/typebox";
|
|
5616
|
-
|
|
5617
|
-
var
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
}
|
|
5633
|
-
|
|
5681
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
5682
|
+
var operations = {
|
|
5683
|
+
show_content: readSeekShowContent,
|
|
5684
|
+
show_structure: readSeekShowStructure,
|
|
5685
|
+
show_metadata: readSeekShowMetadata,
|
|
5686
|
+
show_assets: readSeekShowAssets,
|
|
5687
|
+
check_syntax: readSeekCheckSyntax,
|
|
5688
|
+
check_rules: readSeekCheckRules,
|
|
5689
|
+
generate_caption: readSeekGenerateCaption,
|
|
5690
|
+
detect_objects: readSeekDetectObjects,
|
|
5691
|
+
recognize_text: readSeekRecognizeText
|
|
5692
|
+
};
|
|
5693
|
+
function resolveNonShowSummary(tool, envelope) {
|
|
5694
|
+
if (tool === "check_syntax") {
|
|
5695
|
+
const errorCount = envelope?.diagnostics?.error_count ?? 0;
|
|
5696
|
+
return errorCount === 0 ? "clean" : `${errorCount} syntax error${errorCount === 1 ? "" : "s"}`;
|
|
5697
|
+
}
|
|
5698
|
+
if (tool === "check_rules") {
|
|
5699
|
+
const violations = Array.isArray(envelope?.violations) ? envelope.violations.length : 0;
|
|
5700
|
+
return violations === 0 ? "clean" : `${violations} rule violation${violations === 1 ? "" : "s"}`;
|
|
5701
|
+
}
|
|
5702
|
+
if (tool === "detect_objects") {
|
|
5703
|
+
const objects = Array.isArray(envelope?.objects) ? envelope.objects.length : 0;
|
|
5704
|
+
return `${objects} object${objects === 1 ? "" : "s"}`;
|
|
5705
|
+
}
|
|
5706
|
+
if (tool === "generate_caption")
|
|
5707
|
+
return "caption generated";
|
|
5708
|
+
if (tool === "recognize_text")
|
|
5709
|
+
return "text recognized";
|
|
5710
|
+
return "complete";
|
|
5711
|
+
}
|
|
5712
|
+
function hasAnchors(value) {
|
|
5634
5713
|
if (!value || typeof value !== "object")
|
|
5635
5714
|
return false;
|
|
5636
5715
|
const record = value;
|
|
5637
5716
|
if (Array.isArray(record.hashlines) && record.hashlines.length > 0)
|
|
5638
5717
|
return true;
|
|
5639
|
-
return Object.values(record).some(
|
|
5640
|
-
}
|
|
5641
|
-
function parseSelect(select) {
|
|
5642
|
-
if (select === undefined)
|
|
5643
|
-
return;
|
|
5644
|
-
if (Array.isArray(select)) {
|
|
5645
|
-
const facets = select;
|
|
5646
|
-
if (facets.length === 0)
|
|
5647
|
-
throw new Error("select must include at least one facet");
|
|
5648
|
-
for (const facet of facets) {
|
|
5649
|
-
if (!INSPECT_FACETS.includes(facet)) {
|
|
5650
|
-
throw new Error(`unknown inspect facet \`${facet}\`; expected ${INSPECT_FACETS.join(", ")}`);
|
|
5651
|
-
}
|
|
5652
|
-
}
|
|
5653
|
-
return facets;
|
|
5654
|
-
}
|
|
5655
|
-
if (typeof select !== "string") {
|
|
5656
|
-
throw new Error("select must be a facet, facet list, or comma-separated facets");
|
|
5657
|
-
}
|
|
5658
|
-
if (INSPECT_FACETS.includes(select)) {
|
|
5659
|
-
return select;
|
|
5660
|
-
}
|
|
5661
|
-
const parts = select.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
|
|
5662
|
-
if (parts.length === 0)
|
|
5663
|
-
throw new Error("select must include at least one facet");
|
|
5664
|
-
for (const part of parts) {
|
|
5665
|
-
if (!INSPECT_FACETS.includes(part)) {
|
|
5666
|
-
throw new Error(`unknown inspect facet \`${part}\`; expected ${INSPECT_FACETS.join(", ")}`);
|
|
5667
|
-
}
|
|
5668
|
-
}
|
|
5669
|
-
return parts;
|
|
5670
|
-
}
|
|
5671
|
-
function selectedFacets(select) {
|
|
5672
|
-
if (select === undefined)
|
|
5673
|
-
return ["content"];
|
|
5674
|
-
return Array.isArray(select) ? select : [select];
|
|
5675
|
-
}
|
|
5676
|
-
function summarizeEnvelope(envelope, facets) {
|
|
5677
|
-
if (!envelope || typeof envelope !== "object")
|
|
5678
|
-
return `inspected ${facets.join(",")}`;
|
|
5679
|
-
const record = envelope;
|
|
5680
|
-
const parts = [];
|
|
5681
|
-
for (const facet of facets) {
|
|
5682
|
-
if (facet === "metadata" || record[facet] !== undefined)
|
|
5683
|
-
parts.push(facet);
|
|
5684
|
-
}
|
|
5685
|
-
if (record.metadata !== undefined && !parts.includes("metadata"))
|
|
5686
|
-
parts.unshift("metadata");
|
|
5687
|
-
return `inspected ${parts.join(",") || "metadata"}`;
|
|
5688
|
-
}
|
|
5689
|
-
function parseImageEnvelope(envelope) {
|
|
5690
|
-
if (!envelope || typeof envelope !== "object")
|
|
5691
|
-
return;
|
|
5692
|
-
const record = envelope;
|
|
5693
|
-
if (!record.metadata || typeof record.metadata !== "object")
|
|
5694
|
-
return;
|
|
5695
|
-
const metadata = record.metadata;
|
|
5696
|
-
if (typeof metadata.type !== "string" || !metadata.type.startsWith("image/"))
|
|
5697
|
-
return;
|
|
5698
|
-
if (!record.content || typeof record.content !== "object")
|
|
5699
|
-
return { metadata };
|
|
5700
|
-
const content = record.content;
|
|
5701
|
-
const mime = content.mime ?? content.type ?? metadata.mime ?? metadata.type;
|
|
5702
|
-
const prepared = content.encoding === "base64" && typeof content.data === "string" && typeof mime === "string" ? { data: content.data, mimeType: mime } : undefined;
|
|
5703
|
-
return { metadata, prepared };
|
|
5704
|
-
}
|
|
5705
|
-
function buildInspectSuccess(filePath, facets, envelope, content = [{ type: "text", text: JSON.stringify(envelope) }]) {
|
|
5706
|
-
return {
|
|
5707
|
-
content,
|
|
5708
|
-
details: {
|
|
5709
|
-
readSeekValue: {
|
|
5710
|
-
tool: "inspect",
|
|
5711
|
-
ok: true,
|
|
5712
|
-
path: filePath,
|
|
5713
|
-
select: facets,
|
|
5714
|
-
envelope
|
|
5715
|
-
}
|
|
5716
|
-
}
|
|
5717
|
-
};
|
|
5718
|
+
return Object.values(record).some(hasAnchors);
|
|
5718
5719
|
}
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
}
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
const
|
|
5746
|
-
if (!depth.ok || depth.value !== undefined && depth.value < 0) {
|
|
5747
|
-
const message = depth.ok ? `Invalid depth: expected a non-negative integer, received ${depth.value}.` : depth.message;
|
|
5748
|
-
return buildToolErrorResult("inspect", "invalid-parameter", message, { path: p.path });
|
|
5749
|
-
}
|
|
5750
|
-
if (depth.value !== undefined && !facets.includes("map")) {
|
|
5751
|
-
return buildToolErrorResult("inspect", "invalid-parameter", "depth requires the map facet.", { path: p.path });
|
|
5752
|
-
}
|
|
5753
|
-
const selector = typeof p.selector === "string" ? p.selector.trim() : undefined;
|
|
5754
|
-
if (p.selector !== undefined && !selector) {
|
|
5755
|
-
return buildToolErrorResult("inspect", "invalid-parameter", "Invalid selector: expected a non-empty selector.", { path: p.path });
|
|
5756
|
-
}
|
|
5757
|
-
const query = typeof p.query === "string" ? p.query.trim() : undefined;
|
|
5758
|
-
if (p.query !== undefined && !query) {
|
|
5759
|
-
return buildToolErrorResult("inspect", "invalid-parameter", "Invalid query: expected a non-empty query.", { path: p.path });
|
|
5760
|
-
}
|
|
5761
|
-
if (selector !== undefined && query !== undefined) {
|
|
5762
|
-
return buildToolErrorResult("inspect", "invalid-params-combo", "selector and query cannot be combined.", { path: p.path });
|
|
5763
|
-
}
|
|
5764
|
-
if (facets.includes("identity") && selector === undefined) {
|
|
5765
|
-
return buildToolErrorResult("inspect", "invalid-params-combo", "the identity facet requires a selector.", { path: p.path });
|
|
5766
|
-
}
|
|
5767
|
-
if (p.visionLevel !== undefined && (p.visionMode === undefined || p.visionMode === "none") && !appliesImagePolicy) {
|
|
5768
|
-
return buildToolErrorResult("inspect", "invalid-params-combo", "visionLevel requires an analysis visionMode.", { path: p.path });
|
|
5769
|
-
}
|
|
5770
|
-
const filePath = resolveToCwd2(p.path, cwd);
|
|
5771
|
-
const commonOptions = {
|
|
5772
|
-
select,
|
|
5773
|
-
signal,
|
|
5774
|
-
depth: depth.value ?? (facets.includes("map") ? DEFAULT_INSPECT_MAP_DEPTH : undefined)
|
|
5775
|
-
};
|
|
5776
|
-
const selectionOptions = {
|
|
5777
|
-
...commonOptions,
|
|
5778
|
-
selector,
|
|
5779
|
-
query,
|
|
5780
|
-
end: end.value,
|
|
5781
|
-
limit: limit.value,
|
|
5782
|
-
language: p.language
|
|
5783
|
-
};
|
|
5784
|
-
const probeOptions = {
|
|
5785
|
-
...commonOptions,
|
|
5786
|
-
select: [...new Set(["metadata", ...facets])].filter((facet) => facet !== "identity")
|
|
5787
|
-
};
|
|
5788
|
-
const hasSelectionOptions = selector !== undefined || query !== undefined || end.value !== undefined || limit.value !== undefined || p.language !== undefined;
|
|
5789
|
-
const hasVisionOptions = p.visionMode !== undefined || p.visionLevel !== undefined;
|
|
5790
|
-
const initialVisionMode = appliesImagePolicy ? undefined : p.visionMode;
|
|
5791
|
-
const initialVisionLevel = appliesImagePolicy ? undefined : p.visionLevel;
|
|
5720
|
+
function imagePaths(value, paths = new Map) {
|
|
5721
|
+
if (!value || typeof value !== "object")
|
|
5722
|
+
return paths;
|
|
5723
|
+
const record = value;
|
|
5724
|
+
if (typeof record.path === "string" && typeof record.mime === "string" && record.mime.startsWith("image/")) {
|
|
5725
|
+
paths.set(record.path, record.mime);
|
|
5726
|
+
}
|
|
5727
|
+
for (const child of Object.values(record))
|
|
5728
|
+
imagePaths(child, paths);
|
|
5729
|
+
return paths;
|
|
5730
|
+
}
|
|
5731
|
+
async function executeAccess(opts) {
|
|
5732
|
+
if (!opts.params || typeof opts.params !== "object" || Array.isArray(opts.params)) {
|
|
5733
|
+
return buildToolErrorResult(opts.tool, "invalid-parameter", "Expected an object");
|
|
5734
|
+
}
|
|
5735
|
+
const p = opts.params;
|
|
5736
|
+
const contract = getToolContract(opts.tool);
|
|
5737
|
+
for (const key of Object.keys(p)) {
|
|
5738
|
+
if (key === "stdinName" || !Object.hasOwn(contract.inputSchema.properties, key))
|
|
5739
|
+
return buildToolErrorResult(opts.tool, "invalid-parameter", `Unknown parameter: ${key}`);
|
|
5740
|
+
}
|
|
5741
|
+
const path = p.path === undefined && opts.tool === "check_rules" ? "." : p.path;
|
|
5742
|
+
if (typeof path !== "string" || !path.trim())
|
|
5743
|
+
return buildToolErrorResult(opts.tool, "invalid-parameter", "path is required");
|
|
5744
|
+
if (path === "-")
|
|
5745
|
+
return buildToolErrorResult(opts.tool, "invalid-parameter", "Agent tools require a file path; stdin is available through the CLI or API");
|
|
5746
|
+
const filePath = resolveToCwd2(path, opts.cwd);
|
|
5792
5747
|
try {
|
|
5793
|
-
const
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
{ type: "text", text: `
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
{ type: "image", data: image.prepared.data, mimeType: image.prepared.mimeType }
|
|
5810
|
-
]);
|
|
5748
|
+
const { path: _path, ...options } = p;
|
|
5749
|
+
const envelope = await operations[opts.tool](filePath, { ...options, signal: opts.signal });
|
|
5750
|
+
if (hasAnchors(envelope))
|
|
5751
|
+
opts.onFileAnchored?.(filePath);
|
|
5752
|
+
const presentation = isShowTool(opts.tool) ? presentShowResult(opts.tool, envelope, p) : undefined;
|
|
5753
|
+
const content = [
|
|
5754
|
+
{ type: "text", text: presentation ? formatShowResult(presentation) : JSON.stringify(envelope) }
|
|
5755
|
+
];
|
|
5756
|
+
if (opts.tool === "show_content" || opts.tool === "show_assets") {
|
|
5757
|
+
for (const [imagePath, mimeType] of imagePaths(envelope)) {
|
|
5758
|
+
if (opts.imageMode === "off")
|
|
5759
|
+
content.push({ type: "text", text: `Image available at ${imagePath}; image attachments are disabled by readseek.imageMode.` });
|
|
5760
|
+
else if (opts.modelSupportsImages)
|
|
5761
|
+
content.push({ type: "image", data: (await readFile2(imagePath)).toString("base64"), mimeType });
|
|
5762
|
+
else
|
|
5763
|
+
content.push({ type: "text", text: `Image available at ${imagePath}; this host model does not support image attachments.` });
|
|
5811
5764
|
}
|
|
5812
|
-
const visionMode = p.visionMode && p.visionMode !== "none" ? p.visionMode : "all";
|
|
5813
|
-
const analyzedEnvelope = await readSeekInspect(filePath, {
|
|
5814
|
-
...commonOptions,
|
|
5815
|
-
visionMode,
|
|
5816
|
-
visionLevel: p.visionLevel,
|
|
5817
|
-
vision: true
|
|
5818
|
-
});
|
|
5819
|
-
return buildInspectSuccess(filePath, facets, analyzedEnvelope);
|
|
5820
|
-
}
|
|
5821
|
-
if (p.visionLevel !== undefined && (p.visionMode === undefined || p.visionMode === "none")) {
|
|
5822
|
-
return buildToolErrorResult("inspect", "invalid-params-combo", "visionLevel requires an analysis visionMode.", {
|
|
5823
|
-
path: p.path
|
|
5824
|
-
});
|
|
5825
5765
|
}
|
|
5826
|
-
|
|
5827
|
-
...selectionOptions,
|
|
5828
|
-
visionMode: p.visionMode,
|
|
5829
|
-
visionLevel: p.visionLevel,
|
|
5830
|
-
vision: p.visionMode !== undefined && p.visionMode !== "none"
|
|
5831
|
-
}) : envelope;
|
|
5832
|
-
if (hasInspectAnchors(selectedEnvelope))
|
|
5833
|
-
onFileAnchored?.(filePath);
|
|
5834
|
-
return buildInspectSuccess(filePath, facets, selectedEnvelope);
|
|
5766
|
+
return { content, details: { readSeekValue: { tool: opts.tool, ok: true, path: filePath, envelope, presentation } } };
|
|
5835
5767
|
} catch (err) {
|
|
5836
5768
|
const failure = classifyReadSeekFailure(err);
|
|
5837
|
-
return buildToolErrorResult(
|
|
5838
|
-
}
|
|
5839
|
-
}
|
|
5840
|
-
function
|
|
5841
|
-
const
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
label: "Inspect",
|
|
5850
|
-
description: promptMetadata.description,
|
|
5851
|
-
promptSnippet: promptMetadata.promptSnippet,
|
|
5852
|
-
toolAliases: options.toolAliases,
|
|
5853
|
-
promptGuidelines: promptMetadata.promptGuidelines,
|
|
5854
|
-
parameters: inspectSchema,
|
|
5855
|
-
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
5856
|
-
return executeInspect({
|
|
5857
|
-
params,
|
|
5858
|
-
signal,
|
|
5859
|
-
cwd: ctx.cwd,
|
|
5860
|
-
onFileAnchored: options.onFileAnchored,
|
|
5861
|
-
imageMode: options.imageMode,
|
|
5862
|
-
modelSupportsImages: ctx.model?.input.includes("image") ?? false
|
|
5863
|
-
});
|
|
5864
|
-
},
|
|
5865
|
-
renderCall(args, theme, ...rest) {
|
|
5866
|
-
const context = rest[0] ?? {};
|
|
5867
|
-
const cwd = context.cwd ?? process.cwd();
|
|
5868
|
-
const displayPath = typeof args?.path === "string" ? args.path : "?";
|
|
5869
|
-
let text = theme.fg("toolTitle", theme.bold("inspect"));
|
|
5870
|
-
text += ` ${linkToolPath(theme.fg("accent", displayPath), displayPath, cwd)}`;
|
|
5871
|
-
if (args?.select !== undefined) {
|
|
5872
|
-
const select = Array.isArray(args.select) ? args.select.join(",") : String(args.select);
|
|
5873
|
-
text += theme.fg("dim", ` [${select}]`);
|
|
5874
|
-
}
|
|
5875
|
-
if (typeof args?.selector === "string" && args.selector)
|
|
5876
|
-
text += theme.fg("dim", ` @ ${args.selector}`);
|
|
5877
|
-
return new Text7(clampLineToWidth(text, context.width), 0, 0);
|
|
5878
|
-
},
|
|
5879
|
-
renderResult(result, options, theme, ...rest) {
|
|
5880
|
-
const { isPartial, isError, expanded, width } = resolveRenderResultContext(options, rest);
|
|
5881
|
-
if (isPartial)
|
|
5882
|
-
return renderPendingResult("pending inspect", width, theme);
|
|
5883
|
-
const textContent = result.content?.[0]?.type === "text" ? result.content[0].text : "";
|
|
5884
|
-
if (isError || result.isError) {
|
|
5885
|
-
return renderErrorResult(textContent, { expanded, width, fallback: "inspect failed", theme });
|
|
5886
|
-
}
|
|
5887
|
-
const value = result.details?.readSeekValue;
|
|
5888
|
-
const facets = Array.isArray(value?.select) ? value.select : ["content"];
|
|
5889
|
-
const label = summarizeEnvelope(value?.envelope, facets);
|
|
5890
|
-
let text = summaryLine(label, { hidden: !!textContent && !expanded, theme, style: "success" });
|
|
5891
|
-
if (expanded && textContent)
|
|
5892
|
-
text += `
|
|
5893
|
-
${textContent}`;
|
|
5894
|
-
return new Text7(clampLinesToWidth(text.split(`
|
|
5895
|
-
`), width).join(`
|
|
5896
|
-
`), 0, 0);
|
|
5897
|
-
}
|
|
5898
|
-
});
|
|
5899
|
-
}
|
|
5900
|
-
|
|
5901
|
-
// src/extract.ts
|
|
5902
|
-
import { Text as Text8 } from "@earendil-works/pi-tui";
|
|
5903
|
-
import { Type as Type12 } from "@sinclair/typebox";
|
|
5904
|
-
var EXTRACT_PROMPT_METADATA = defineToolPromptMetadata({
|
|
5905
|
-
promptUrl: new URL("../prompts/extract.md", import.meta.url),
|
|
5906
|
-
promptSnippet: "Extract a selected document asset to a file"
|
|
5907
|
-
});
|
|
5908
|
-
async function executeExtract(opts) {
|
|
5909
|
-
const { signal, cwd, onFileMutated } = opts;
|
|
5910
|
-
const input = opts.params;
|
|
5911
|
-
const selector = typeof input.selector === "string" ? input.selector.trim() : "";
|
|
5912
|
-
if (!selector.startsWith("asset:") || selector === "asset:") {
|
|
5913
|
-
return buildToolErrorResult("extract", "invalid-selector", "selector must identify an asset as asset:ID.", {
|
|
5914
|
-
path: input.path
|
|
5769
|
+
return buildToolErrorResult(opts.tool, failure.code, failure.message, failure.hint ? { hint: failure.hint } : {});
|
|
5770
|
+
}
|
|
5771
|
+
}
|
|
5772
|
+
function registerAccessTools(pi, options = {}) {
|
|
5773
|
+
for (const name of Object.keys(operations)) {
|
|
5774
|
+
const registeredName = name === "show_content" ? options.name ?? `readSeek_${name}` : `readSeek_${name}`;
|
|
5775
|
+
const contract = getToolContract(name);
|
|
5776
|
+
const metadata = defineToolPromptMetadata({
|
|
5777
|
+
promptUrl: new URL(`../prompts/${name}.md`, import.meta.url),
|
|
5778
|
+
promptSnippet: contract.title,
|
|
5779
|
+
registeredName,
|
|
5780
|
+
toolAliases: options.toolAliases
|
|
5915
5781
|
});
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5782
|
+
registerReadSeekTool(pi, {
|
|
5783
|
+
name: registeredName,
|
|
5784
|
+
label: contract.title,
|
|
5785
|
+
...metadata,
|
|
5786
|
+
parameters: Type11.Unsafe({
|
|
5787
|
+
...contract.inputSchema,
|
|
5788
|
+
properties: Object.fromEntries(Object.entries(contract.inputSchema.properties).filter(([key]) => key !== "stdinName"))
|
|
5789
|
+
}),
|
|
5790
|
+
async execute(_id, params, signal, _update, ctx) {
|
|
5791
|
+
return executeAccess({ tool: name, params, signal, cwd: ctx.cwd, onFileAnchored: options.onFileAnchored, imageMode: options.imageMode, modelSupportsImages: ctx.model?.input.includes("image") ?? false });
|
|
5792
|
+
},
|
|
5793
|
+
renderCall(args, theme, ...rest) {
|
|
5794
|
+
const context = rest[0] ?? {};
|
|
5795
|
+
const cwd = context.cwd ?? process.cwd();
|
|
5796
|
+
const label = renderToolLabel(theme, registeredName);
|
|
5797
|
+
const filePath = typeof args?.path === "string" ? args.path : args?.path ? String(args.path) : "";
|
|
5798
|
+
const renderedPath = filePath ? linkToolPath(theme?.fg ? theme.fg("accent", filePath) : filePath, filePath, cwd) : "";
|
|
5799
|
+
const atBadge = args?.at ? theme?.fg ? theme.fg("dim", ` @ ${args.at}`) : ` @ ${args.at}` : "";
|
|
5800
|
+
const text = clampLineToWidth([label, renderedPath, atBadge].filter(Boolean).join(" "), context.width);
|
|
5801
|
+
return upsertTextComponent(context.lastComponent, text);
|
|
5802
|
+
},
|
|
5803
|
+
renderResult(result, renderOptions, theme, ...rest) {
|
|
5804
|
+
const { isPartial, isError, expanded, width } = resolveRenderResultContext(renderOptions, rest);
|
|
5805
|
+
if (isPartial)
|
|
5806
|
+
return renderPendingResult(`pending ${registeredName}`, width, theme);
|
|
5807
|
+
const text = result.content?.find((item) => item.type === "text")?.text ?? "";
|
|
5808
|
+
if (isError || result.isError)
|
|
5809
|
+
return renderErrorResult(text, { expanded, width, fallback: `${registeredName} failed`, theme });
|
|
5810
|
+
const presentation = result.details?.readSeekValue?.presentation;
|
|
5811
|
+
if (presentation) {
|
|
5812
|
+
const summary = summaryLine(presentation.summary, { hidden: !!presentation.body && !expanded, theme, style: presentation.notices.length > 0 ? "warning" : "success" });
|
|
5813
|
+
const notes = presentation.notices.map((notice) => `Note: ${notice}`);
|
|
5814
|
+
const attachments = result.content?.filter((item) => item.type === "text").slice(1).map((item) => item.text) ?? [];
|
|
5815
|
+
return new Text7([summary, ...expanded ? [presentation.body] : [], ...notes, ...attachments].filter(Boolean).join(`
|
|
5816
|
+
|
|
5817
|
+
`), 0, 0);
|
|
5937
5818
|
}
|
|
5819
|
+
const envelope = result.details?.readSeekValue?.envelope;
|
|
5820
|
+
const nonShowSummary = resolveNonShowSummary(name, envelope);
|
|
5821
|
+
const summary = summaryLine(nonShowSummary, { hidden: !!text && !expanded, theme, style: "success" });
|
|
5822
|
+
return new Text7(clampLinesToWidth((expanded ? `${summary}\\n${text}` : summary).split(`
|
|
5823
|
+
`), width).join(`
|
|
5824
|
+
`), 0, 0);
|
|
5938
5825
|
}
|
|
5939
|
-
};
|
|
5940
|
-
} catch (error) {
|
|
5941
|
-
const failure = classifyReadSeekFailure(error);
|
|
5942
|
-
return buildToolErrorResult("extract", failure.code, failure.message, failure.hint ? { hint: failure.hint } : {});
|
|
5826
|
+
});
|
|
5943
5827
|
}
|
|
5944
5828
|
}
|
|
5945
|
-
function registerExtractTool(pi, options = {}) {
|
|
5946
|
-
return registerReadSeekTool(pi, {
|
|
5947
|
-
name: "readSeek_extract",
|
|
5948
|
-
label: "Extract",
|
|
5949
|
-
...EXTRACT_PROMPT_METADATA,
|
|
5950
|
-
parameters: Type12.Object({
|
|
5951
|
-
path: filePathParam(),
|
|
5952
|
-
selector: Type12.String({ pattern: "^asset:.+", description: PARAM_DESCRIPTIONS.assetSelector }),
|
|
5953
|
-
output: Type12.String({ minLength: 1, pattern: "^(?!-$).+", description: PARAM_DESCRIPTIONS.output })
|
|
5954
|
-
}),
|
|
5955
|
-
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
5956
|
-
return executeExtract({
|
|
5957
|
-
params,
|
|
5958
|
-
signal,
|
|
5959
|
-
cwd: ctx.cwd,
|
|
5960
|
-
onFileMutated: options.onFileMutated
|
|
5961
|
-
});
|
|
5962
|
-
},
|
|
5963
|
-
renderCall(args, theme, ...rest) {
|
|
5964
|
-
const context = rest[0] ?? {};
|
|
5965
|
-
const cwd = context.cwd ?? process.cwd();
|
|
5966
|
-
const displayPath = typeof args?.path === "string" ? args.path : "?";
|
|
5967
|
-
const output = typeof args?.output === "string" ? args.output : "?";
|
|
5968
|
-
let text = `${theme.fg("toolTitle", theme.bold("extract"))} ${linkToolPath(theme.fg("accent", displayPath), displayPath, cwd)}`;
|
|
5969
|
-
if (typeof args?.selector === "string")
|
|
5970
|
-
text += theme.fg("dim", ` ${args.selector}`);
|
|
5971
|
-
text += theme.fg("dim", ` → ${output}`);
|
|
5972
|
-
return new Text8(clampLineToWidth(text, context.width), 0, 0);
|
|
5973
|
-
},
|
|
5974
|
-
renderResult(result, options, theme, ...rest) {
|
|
5975
|
-
const { isPartial, isError, width } = resolveRenderResultContext(options, rest);
|
|
5976
|
-
if (isPartial)
|
|
5977
|
-
return renderPendingResult("pending extract", width, theme);
|
|
5978
|
-
const textContent = result.content?.[0]?.type === "text" ? result.content[0].text : "";
|
|
5979
|
-
if (isError || result.isError) {
|
|
5980
|
-
return renderErrorResult(textContent, { expanded: false, width, fallback: "extract failed", theme });
|
|
5981
|
-
}
|
|
5982
|
-
return new Text8(clampLineToWidth(summaryLine(textContent || "asset extracted", { theme, style: "success" }), width), 0, 0);
|
|
5983
|
-
}
|
|
5984
|
-
});
|
|
5985
|
-
}
|
|
5986
5829
|
|
|
5987
5830
|
// src/session-anchors.ts
|
|
5988
5831
|
class SessionAnchors {
|
|
@@ -6003,30 +5846,37 @@ class SessionAnchors {
|
|
|
6003
5846
|
|
|
6004
5847
|
// index.ts
|
|
6005
5848
|
var READSEEK_TOOL_ENTRIES = [
|
|
6006
|
-
{ builtIn: "edit", readSeekName: "
|
|
6007
|
-
{ builtIn: "grep", readSeekName: "
|
|
6008
|
-
{ builtIn: null, readSeekName: "
|
|
6009
|
-
{ builtIn: null, readSeekName: "
|
|
6010
|
-
{ builtIn: null, readSeekName: "
|
|
6011
|
-
{ builtIn: "write", readSeekName: "
|
|
6012
|
-
{ builtIn: null, readSeekName: "
|
|
6013
|
-
{ builtIn: "read", readSeekName: "
|
|
6014
|
-
{ builtIn: null, readSeekName: "
|
|
5849
|
+
{ builtIn: "edit", readSeekName: "readSeek_edit_text" },
|
|
5850
|
+
{ builtIn: "grep", readSeekName: "readSeek_find_text" },
|
|
5851
|
+
{ builtIn: null, readSeekName: "readSeek_find_nodes" },
|
|
5852
|
+
{ builtIn: null, readSeekName: "readSeek_find_references" },
|
|
5853
|
+
{ builtIn: null, readSeekName: "readSeek_rename_symbol" },
|
|
5854
|
+
{ builtIn: "write", readSeekName: "readSeek_write_file" },
|
|
5855
|
+
{ builtIn: null, readSeekName: "readSeek_find_definitions" },
|
|
5856
|
+
{ builtIn: "read", readSeekName: "readSeek_show_content" },
|
|
5857
|
+
{ builtIn: null, readSeekName: "readSeek_show_structure" },
|
|
5858
|
+
{ builtIn: null, readSeekName: "readSeek_show_metadata" },
|
|
5859
|
+
{ builtIn: null, readSeekName: "readSeek_show_assets" },
|
|
5860
|
+
{ builtIn: null, readSeekName: "readSeek_check_syntax" },
|
|
5861
|
+
{ builtIn: null, readSeekName: "readSeek_check_rules" },
|
|
5862
|
+
{ builtIn: null, readSeekName: "readSeek_generate_caption" },
|
|
5863
|
+
{ builtIn: null, readSeekName: "readSeek_detect_objects" },
|
|
5864
|
+
{ builtIn: null, readSeekName: "readSeek_recognize_text" }
|
|
6015
5865
|
];
|
|
6016
5866
|
function piReadSeekExtension(pi) {
|
|
6017
5867
|
const sessionAnchors = new SessionAnchors;
|
|
6018
5868
|
const { settings } = resolveReadSeekJsonSettings();
|
|
6019
5869
|
const replacedBuiltIns = new Set(settings.overrideTools ?? []);
|
|
6020
5870
|
const swap = (builtIn, readSeekName) => replacedBuiltIns.has(builtIn) ? builtIn : readSeekName;
|
|
6021
|
-
const readName = swap("read", "
|
|
6022
|
-
const editName = swap("edit", "
|
|
6023
|
-
const writeName = swap("write", "
|
|
6024
|
-
const grepName = swap("grep", "
|
|
5871
|
+
const readName = swap("read", "readSeek_show_content");
|
|
5872
|
+
const editName = swap("edit", "readSeek_edit_text");
|
|
5873
|
+
const writeName = swap("write", "readSeek_write_file");
|
|
5874
|
+
const grepName = swap("grep", "readSeek_find_text");
|
|
6025
5875
|
const toolAliases = {
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
5876
|
+
readSeek_show_content: readName,
|
|
5877
|
+
readSeek_edit_text: editName,
|
|
5878
|
+
readSeek_find_text: grepName,
|
|
5879
|
+
readSeek_write_file: writeName
|
|
6030
5880
|
};
|
|
6031
5881
|
const policyAliases = Object.fromEntries(TOOL_NAMES.map((name) => {
|
|
6032
5882
|
const readSeekName = `readSeek_${name}`;
|
|
@@ -6046,15 +5896,12 @@ function piReadSeekExtension(pi) {
|
|
|
6046
5896
|
registerRefsTool(pi, { onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath) });
|
|
6047
5897
|
registerRenameTool(pi, { onFileMutated: (absolutePath) => sessionAnchors.forget(absolutePath) });
|
|
6048
5898
|
registerDefTool(pi, { onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath) });
|
|
6049
|
-
|
|
5899
|
+
registerAccessTools(pi, {
|
|
6050
5900
|
onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath),
|
|
6051
5901
|
name: readName,
|
|
6052
5902
|
toolAliases,
|
|
6053
5903
|
imageMode: replacedBuiltIns.has("read") ? resolveReadSeekImageMode() : undefined
|
|
6054
5904
|
});
|
|
6055
|
-
registerExtractTool(pi, {
|
|
6056
|
-
onFileMutated: (absolutePath) => sessionAnchors.forget(absolutePath)
|
|
6057
|
-
});
|
|
6058
5905
|
registerWriteTool(pi, {
|
|
6059
5906
|
onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath),
|
|
6060
5907
|
name: writeName
|