pi-readseek 0.9.16 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/index.ts +1272 -1106
- package/package.json +3 -2
- package/prompts/def.md +3 -3
- package/prompts/edit.md +1 -1
- package/prompts/extract.md +11 -0
- package/prompts/inspect.md +34 -0
- package/prompts/refs.md +2 -8
- package/prompts/search.md +2 -7
- package/prompts/digest.md +0 -38
- package/prompts/view.md +0 -18
package/dist/index.ts
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import { readFile as fsReadFile } from "node:fs/promises";
|
|
3
3
|
import { createPatch } from "diff";
|
|
4
4
|
import { withFileMutationQueue } from "@earendil-works/pi-coding-agent";
|
|
5
|
-
import { Type as
|
|
5
|
+
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
|
|
9
|
+
var INSPECT_FACETS = [
|
|
10
10
|
"metadata",
|
|
11
11
|
"content",
|
|
12
12
|
"map",
|
|
@@ -14,20 +14,7 @@ var DIGEST_FACETS = [
|
|
|
14
14
|
"identity"
|
|
15
15
|
];
|
|
16
16
|
var VISION_MODES = ["none", "all", "ocr", "caption", "objects"];
|
|
17
|
-
var VISION_ANALYSIS_MODES = ["all", "ocr", "caption", "objects"];
|
|
18
17
|
var VISION_LEVELS = ["low", "medium", "high"];
|
|
19
|
-
var DOCUMENT_NODE_KINDS = [
|
|
20
|
-
"artifact",
|
|
21
|
-
"footer",
|
|
22
|
-
"header",
|
|
23
|
-
"heading",
|
|
24
|
-
"marginal_label",
|
|
25
|
-
"page",
|
|
26
|
-
"page_number",
|
|
27
|
-
"paragraph",
|
|
28
|
-
"section",
|
|
29
|
-
"structural_section"
|
|
30
|
-
];
|
|
31
18
|
var EDIT_VARIANTS = [
|
|
32
19
|
"set_line",
|
|
33
20
|
"replace_lines",
|
|
@@ -35,12 +22,13 @@ var EDIT_VARIANTS = [
|
|
|
35
22
|
"replace",
|
|
36
23
|
"replace_symbol"
|
|
37
24
|
];
|
|
25
|
+
var GIT_SCOPES = ["tracked", "untracked", "ignored", "all"];
|
|
38
26
|
var TOOL_NAMES = [
|
|
39
|
-
"
|
|
27
|
+
"inspect",
|
|
40
28
|
"edit",
|
|
41
29
|
"write",
|
|
42
30
|
"grep",
|
|
43
|
-
"
|
|
31
|
+
"extract",
|
|
44
32
|
"search",
|
|
45
33
|
"def",
|
|
46
34
|
"refs",
|
|
@@ -62,28 +50,24 @@ var PARAM_DESCRIPTIONS = {
|
|
|
62
50
|
line: "One-based cursor line; required with scope",
|
|
63
51
|
renameLine: "One-based cursor line of the symbol to rename",
|
|
64
52
|
column: "One-based cursor byte column for disambiguation",
|
|
65
|
-
|
|
66
|
-
others: "In a Git repository, search untracked files",
|
|
67
|
-
ignored: "With others=true, include ignored untracked files",
|
|
53
|
+
gitScope: "In a Git repository, which files to search: tracked, untracked, ignored (untracked plus ignored), or all. Defaults to tracked plus untracked.",
|
|
68
54
|
select: "Facets: metadata, content, map, diagnostics, identity (default content)",
|
|
69
55
|
selectComma: "Comma-separated facets: metadata, content, map, diagnostics, identity",
|
|
70
|
-
|
|
56
|
+
selector: "Positional selector: line:N[:COLUMN], hash:H[:COLUMN], symbol:NAME, page:N, node:ID, or asset:ID. Columns require the identity facet, which in turn rejects symbol selectors",
|
|
57
|
+
assetSelector: "Document asset selector in the form asset:ID",
|
|
58
|
+
query: "KDL selection query, @path, or '-' for standard input",
|
|
59
|
+
stdinName: "File name hint when the target is '-'",
|
|
71
60
|
end: "Last line to include",
|
|
72
61
|
limit: "Maximum number of lines to include",
|
|
73
|
-
|
|
62
|
+
inspectDepth: "Maximum structural depth for map output; defaults to 1 in agent tools",
|
|
74
63
|
offset: "One-based starting line",
|
|
75
64
|
readLimit: "Maximum lines to return",
|
|
76
65
|
symbolRead: "Symbol name to read",
|
|
77
66
|
bundle: "Include related symbols from the same file; requires symbol",
|
|
78
67
|
image: "Standalone image mode: none, all, ocr, caption, objects. Required for standalone images.",
|
|
79
|
-
|
|
80
|
-
visionModeView: "Analyze selected assets: all, ocr, caption, or objects",
|
|
68
|
+
visionModeInspect: "Standalone image mode: none, all, caption, objects, or ocr",
|
|
81
69
|
visionLevel: "Vision inference level: low (default), medium, or high",
|
|
82
|
-
|
|
83
|
-
page: "One-based source page",
|
|
84
|
-
kind: "Node kind filter",
|
|
85
|
-
depth: "Maximum depth below selected roots",
|
|
86
|
-
outline: "Return outline nodes only",
|
|
70
|
+
output: "Destination file for extracted binary data; stdout is not supported",
|
|
87
71
|
edits: "Use set_line, replace_lines, insert_after, replace_symbol, or replace",
|
|
88
72
|
editVariant: "Exactly one nested edit variant",
|
|
89
73
|
setLine: 'Replace one line: {"set_line":{"anchor":"LINE:HASH","new_text":"..."}}',
|
|
@@ -120,11 +104,11 @@ var PARAM_DESCRIPTIONS = {
|
|
|
120
104
|
scopeContext: "Context lines within each symbol"
|
|
121
105
|
};
|
|
122
106
|
var TOOL_DESCRIPTIONS = {
|
|
123
|
-
|
|
107
|
+
inspect: "Inspect source content, images, and documents through the native CLI envelope. Use it before edits to obtain fresh `LINE:HASH` anchors; use shallow maps for structural overviews, diagnostics for syntax checks, and identity for cursor context.",
|
|
124
108
|
edit: 'Edit existing text files with fresh `LINE:HASH` anchors. Read or search first when no fresh anchors are available. Each `edits[]` item must use exactly one nested object: `{"set_line":{"anchor":"LINE:HASH","new_text":"..."}}`, `{"replace_lines":{"start_anchor":"LINE:HASH","end_anchor":"LINE:HASH","new_text":"..."}}`, `{"insert_after":{"anchor":"LINE:HASH","new_text":"..."}}`, `{"replace_symbol":{"symbol":"name","new_body":"..."}}`, or `{"replace":{"old_text":"...","new_text":"..."}}`.',
|
|
125
109
|
write: "Create or replace a complete text file and return `LINE:HASH` anchors. Use `edit` for small changes to an existing file.",
|
|
126
110
|
grep: "Search text or regex in files and return edit-ready `LINE:HASH` anchors. Use for identifiers, strings, configuration, errors, comments, and documentation.",
|
|
127
|
-
|
|
111
|
+
extract: "Extract a selected document asset into a destination file. Use an `asset:ID` selector discovered through `inspect`; extracted binary data is never returned on stdout.",
|
|
128
112
|
search: "Search code by AST structure using ast-grep-style patterns. Results include line hashes usable as `LINE:HASH` anchors. Use `grep` for identifiers, strings, comments, configuration, documentation, or other plain text and regex.",
|
|
129
113
|
def: "Go to a symbol definition or declaration by name. Accepts qualified or unqualified names; use this instead of text search for functions, methods, classes, types, and other declarations.",
|
|
130
114
|
refs: "Find references or usages of an identifier by name or cursor binding. Results include enclosing symbols and line hashes usable as `LINE:HASH` anchors. Without cursor scope, results are identifier-name matches; with scope, they follow one binding and exclude shadows. Use text search for literals, and use this before renaming or deleting a symbol.",
|
|
@@ -136,18 +120,11 @@ function renderToolDescription(name, aliases = {}) {
|
|
|
136
120
|
throw new Error(`Missing TOOL_DESCRIPTIONS for ${name}`);
|
|
137
121
|
return TOOL_NAMES.reduce((description, toolName) => description.replaceAll(`\`${toolName}\``, `\`${aliases[toolName] ?? toolName}\``), desc);
|
|
138
122
|
}
|
|
139
|
-
var
|
|
140
|
-
|
|
141
|
-
type: "
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
others: {
|
|
145
|
-
type: "boolean",
|
|
146
|
-
description: PARAM_DESCRIPTIONS.others
|
|
147
|
-
},
|
|
148
|
-
ignored: {
|
|
149
|
-
type: "boolean",
|
|
150
|
-
description: PARAM_DESCRIPTIONS.ignored
|
|
123
|
+
var gitScopeSchema = {
|
|
124
|
+
gitScope: {
|
|
125
|
+
type: "string",
|
|
126
|
+
enum: GIT_SCOPES,
|
|
127
|
+
description: PARAM_DESCRIPTIONS.gitScope
|
|
151
128
|
}
|
|
152
129
|
};
|
|
153
130
|
var editItemSchema = {
|
|
@@ -213,29 +190,31 @@ var editItemSchema = {
|
|
|
213
190
|
};
|
|
214
191
|
var TOOL_CONTRACTS = [
|
|
215
192
|
{
|
|
216
|
-
name: "
|
|
217
|
-
title: "
|
|
218
|
-
description: TOOL_DESCRIPTIONS.
|
|
193
|
+
name: "inspect",
|
|
194
|
+
title: "Inspect input",
|
|
195
|
+
description: TOOL_DESCRIPTIONS.inspect,
|
|
219
196
|
inputSchema: {
|
|
220
197
|
type: "object",
|
|
221
198
|
properties: {
|
|
222
199
|
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
200
|
+
selector: { type: "string", description: PARAM_DESCRIPTIONS.selector },
|
|
223
201
|
select: { type: "string", description: PARAM_DESCRIPTIONS.select },
|
|
224
|
-
|
|
202
|
+
query: { type: "string", description: PARAM_DESCRIPTIONS.query },
|
|
225
203
|
end: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.end },
|
|
226
204
|
limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.limit },
|
|
227
|
-
depth: { type: "integer", minimum: 0, description: PARAM_DESCRIPTIONS.
|
|
205
|
+
depth: { type: "integer", minimum: 0, description: PARAM_DESCRIPTIONS.inspectDepth },
|
|
228
206
|
language: { type: "string", description: PARAM_DESCRIPTIONS.languageOverride },
|
|
229
207
|
visionMode: {
|
|
230
208
|
type: "string",
|
|
231
209
|
enum: [...VISION_MODES],
|
|
232
|
-
description: PARAM_DESCRIPTIONS.
|
|
210
|
+
description: PARAM_DESCRIPTIONS.visionModeInspect
|
|
233
211
|
},
|
|
234
212
|
visionLevel: {
|
|
235
213
|
type: "string",
|
|
236
214
|
enum: [...VISION_LEVELS],
|
|
237
215
|
description: PARAM_DESCRIPTIONS.visionLevel
|
|
238
|
-
}
|
|
216
|
+
},
|
|
217
|
+
stdinName: { type: "string", description: PARAM_DESCRIPTIONS.stdinName }
|
|
239
218
|
},
|
|
240
219
|
required: ["path"],
|
|
241
220
|
additionalProperties: false
|
|
@@ -309,34 +288,27 @@ var TOOL_CONTRACTS = [
|
|
|
309
288
|
}
|
|
310
289
|
},
|
|
311
290
|
{
|
|
312
|
-
name: "
|
|
313
|
-
title: "
|
|
314
|
-
description: TOOL_DESCRIPTIONS.
|
|
291
|
+
name: "extract",
|
|
292
|
+
title: "Extract asset",
|
|
293
|
+
description: TOOL_DESCRIPTIONS.extract,
|
|
315
294
|
inputSchema: {
|
|
316
295
|
type: "object",
|
|
317
296
|
properties: {
|
|
318
297
|
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
319
|
-
|
|
320
|
-
page: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.page },
|
|
321
|
-
kind: {
|
|
298
|
+
selector: {
|
|
322
299
|
type: "string",
|
|
323
|
-
|
|
324
|
-
description: PARAM_DESCRIPTIONS.
|
|
300
|
+
pattern: "^asset:.+",
|
|
301
|
+
description: PARAM_DESCRIPTIONS.assetSelector
|
|
325
302
|
},
|
|
326
|
-
|
|
327
|
-
outline: { type: "boolean", description: PARAM_DESCRIPTIONS.outline },
|
|
328
|
-
visionMode: {
|
|
303
|
+
output: {
|
|
329
304
|
type: "string",
|
|
330
|
-
|
|
331
|
-
|
|
305
|
+
minLength: 1,
|
|
306
|
+
pattern: "^(?!-$).+",
|
|
307
|
+
description: PARAM_DESCRIPTIONS.output
|
|
332
308
|
},
|
|
333
|
-
|
|
334
|
-
type: "string",
|
|
335
|
-
enum: [...VISION_LEVELS],
|
|
336
|
-
description: PARAM_DESCRIPTIONS.visionLevel
|
|
337
|
-
}
|
|
309
|
+
stdinName: { type: "string", description: PARAM_DESCRIPTIONS.stdinName }
|
|
338
310
|
},
|
|
339
|
-
required: ["path"],
|
|
311
|
+
required: ["path", "selector", "output"],
|
|
340
312
|
additionalProperties: false
|
|
341
313
|
}
|
|
342
314
|
},
|
|
@@ -350,7 +322,8 @@ var TOOL_CONTRACTS = [
|
|
|
350
322
|
pattern: { type: "string", description: PARAM_DESCRIPTIONS.pattern },
|
|
351
323
|
path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
|
|
352
324
|
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
353
|
-
|
|
325
|
+
limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.matchLimit },
|
|
326
|
+
...gitScopeSchema
|
|
354
327
|
},
|
|
355
328
|
required: ["pattern"],
|
|
356
329
|
additionalProperties: false
|
|
@@ -366,7 +339,7 @@ var TOOL_CONTRACTS = [
|
|
|
366
339
|
name: { type: "string", description: PARAM_DESCRIPTIONS.name },
|
|
367
340
|
path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
|
|
368
341
|
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
369
|
-
...
|
|
342
|
+
...gitScopeSchema
|
|
370
343
|
},
|
|
371
344
|
required: ["name"],
|
|
372
345
|
additionalProperties: false
|
|
@@ -385,7 +358,7 @@ var TOOL_CONTRACTS = [
|
|
|
385
358
|
scope: { type: "boolean", description: PARAM_DESCRIPTIONS.scope },
|
|
386
359
|
line: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.line },
|
|
387
360
|
column: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.column },
|
|
388
|
-
...
|
|
361
|
+
...gitScopeSchema
|
|
389
362
|
},
|
|
390
363
|
additionalProperties: false
|
|
391
364
|
}
|
|
@@ -403,7 +376,9 @@ var TOOL_CONTRACTS = [
|
|
|
403
376
|
to: { type: "string", description: PARAM_DESCRIPTIONS.to },
|
|
404
377
|
workspace: { type: "boolean", description: PARAM_DESCRIPTIONS.workspace },
|
|
405
378
|
apply: { type: "boolean", description: PARAM_DESCRIPTIONS.apply },
|
|
406
|
-
planHash: { type: "string", description: PARAM_DESCRIPTIONS.planHash }
|
|
379
|
+
planHash: { type: "string", description: PARAM_DESCRIPTIONS.planHash },
|
|
380
|
+
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
381
|
+
...gitScopeSchema
|
|
407
382
|
},
|
|
408
383
|
required: ["path", "line", "to"],
|
|
409
384
|
additionalProperties: false
|
|
@@ -413,7 +388,7 @@ var TOOL_CONTRACTS = [
|
|
|
413
388
|
// ../readseek-api/src/policy.ts
|
|
414
389
|
var REPLACEABLE_TOOLS = [
|
|
415
390
|
{
|
|
416
|
-
tool: "
|
|
391
|
+
tool: "inspect",
|
|
417
392
|
builtIn: "read",
|
|
418
393
|
benefit: "it returns LINE:HASH anchors for safe edits."
|
|
419
394
|
},
|
|
@@ -434,8 +409,8 @@ var REPLACEABLE_TOOLS = [
|
|
|
434
409
|
}
|
|
435
410
|
];
|
|
436
411
|
var TOOL_COMPACT_GUIDELINES = {
|
|
437
|
-
|
|
438
|
-
"Use `
|
|
412
|
+
inspect: [
|
|
413
|
+
"Use `inspect` for shallow maps, diagnostics, and identity; use `def`, `refs`, or `search` for targeted symbol questions."
|
|
439
414
|
],
|
|
440
415
|
edit: [
|
|
441
416
|
"Prefer set_line, replace_lines, and insert_after; use replace only when anchors are impractical."
|
|
@@ -464,7 +439,7 @@ function renderCompactGuidelines(name, aliases = {}) {
|
|
|
464
439
|
}
|
|
465
440
|
function renderToolRoutingPolicy(options = {}) {
|
|
466
441
|
const aliases = options.aliases ?? {};
|
|
467
|
-
const
|
|
442
|
+
const inspect = resolveToolName("inspect", aliases);
|
|
468
443
|
const edit = resolveToolName("edit", aliases);
|
|
469
444
|
const write = resolveToolName("write", aliases);
|
|
470
445
|
const grep = resolveToolName("grep", aliases);
|
|
@@ -472,11 +447,11 @@ function renderToolRoutingPolicy(options = {}) {
|
|
|
472
447
|
const def = resolveToolName("def", aliases);
|
|
473
448
|
const refs = resolveToolName("refs", aliases);
|
|
474
449
|
const rename = resolveToolName("rename", aliases);
|
|
475
|
-
const
|
|
450
|
+
const extract = resolveToolName("extract", aliases);
|
|
476
451
|
if (options.style === "server") {
|
|
477
452
|
return [
|
|
478
|
-
"Structural code navigation, document
|
|
479
|
-
"For structural questions, prefer these tools to text Grep/Bash: targeted definitions, references, and AST search; shallow
|
|
453
|
+
"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 search; shallow inspect maps; diagnostics and identity; and document inspection.",
|
|
480
455
|
`Use ${edit} for LINE:HASH-anchored mutations (preview with apply=false; apply defaults to true and re-checks plan_hash).`,
|
|
481
456
|
"Paths resolve against CLAUDE_PROJECT_DIR. Native Read/Grep/Edit hooks provide passive context;",
|
|
482
457
|
"call MCP tools for active navigation, document questions, or anchored edits."
|
|
@@ -487,7 +462,7 @@ function renderToolRoutingPolicy(options = {}) {
|
|
|
487
462
|
if (options.preferOverBuiltIns) {
|
|
488
463
|
bullets.push("- Prefer ReadSeek tools over built-ins when they can do the job.");
|
|
489
464
|
}
|
|
490
|
-
bullets.push(`- Read with ${
|
|
465
|
+
bullets.push(`- Read with ${inspect} first; ${edit} needs fresh LINE:HASH anchors.`, `- Use ${grep} for text/regex, ${search} for AST patterns, ${def}/${refs} for targeted symbols, and shallow ${inspect} maps only for file overviews.`, `- Use ${edit} for existing files, ${write} for whole-file creation or replacement, and ${rename} for symbol renames.`, `- Use ${inspect} for document structure; run ${inspect} with select diagnostics after source edits for a quick syntax check.`, `- Use ${extract} to save a selected document asset.`);
|
|
491
466
|
if (options.demoteBuiltIns && options.demoteBuiltIns.length > 0) {
|
|
492
467
|
bullets.push(`- Do not use built-in ${options.demoteBuiltIns.join(", ")} when a ReadSeek tool can do the job.`);
|
|
493
468
|
}
|
|
@@ -514,12 +489,6 @@ function tryParseLineAnchor(anchor) {
|
|
|
514
489
|
}
|
|
515
490
|
|
|
516
491
|
// ../readseek-api/src/validation.ts
|
|
517
|
-
function validateGitSelection(input) {
|
|
518
|
-
if (input.ignored && !input.others) {
|
|
519
|
-
return { ok: false, error: "ignored requires others=true" };
|
|
520
|
-
}
|
|
521
|
-
return { ok: true };
|
|
522
|
-
}
|
|
523
492
|
function validateRefsNameVsScope(input) {
|
|
524
493
|
if (input.scope) {
|
|
525
494
|
if (input.line === undefined) {
|
|
@@ -536,11 +505,8 @@ function validateRefsNameVsScope(input) {
|
|
|
536
505
|
return { ok: true };
|
|
537
506
|
}
|
|
538
507
|
function validateVisionLevelVsMode(input) {
|
|
539
|
-
if (input.
|
|
540
|
-
return { ok: false, error: "
|
|
541
|
-
}
|
|
542
|
-
if (input.visionLevel !== undefined && input.visionMode === undefined) {
|
|
543
|
-
return { ok: false, error: "visionLevel requires visionMode" };
|
|
508
|
+
if (input.visionLevel !== undefined && !["all", "ocr", "caption", "objects"].includes(input.visionMode ?? "")) {
|
|
509
|
+
return { ok: false, error: "visionLevel requires an analysis visionMode" };
|
|
544
510
|
}
|
|
545
511
|
return { ok: true };
|
|
546
512
|
}
|
|
@@ -611,22 +577,54 @@ function validateExactlyOneEditVariant(edits) {
|
|
|
611
577
|
}
|
|
612
578
|
var MIN_SAFE_INTEGER = BigInt(Number.MIN_SAFE_INTEGER);
|
|
613
579
|
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
|
+
}
|
|
614
620
|
// ../readseek-api/src/argv.ts
|
|
615
|
-
var
|
|
621
|
+
var DEFAULT_INSPECT_MAP_DEPTH = 1;
|
|
616
622
|
function buildLineLocation(line, column) {
|
|
617
623
|
return column === undefined ? `line:${line}` : `line:${line}:${column}`;
|
|
618
624
|
}
|
|
619
|
-
function
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
throw new Error(validated.error);
|
|
623
|
-
}
|
|
624
|
-
if (input.cached)
|
|
625
|
-
args.push("--cached");
|
|
626
|
-
if (input.others)
|
|
627
|
-
args.push("--others");
|
|
628
|
-
if (input.ignored)
|
|
629
|
-
args.push("--ignored");
|
|
625
|
+
function appendGitScopeArgs(args, input) {
|
|
626
|
+
if (input.gitScope !== undefined)
|
|
627
|
+
args.push("--git-scope", input.gitScope);
|
|
630
628
|
return args;
|
|
631
629
|
}
|
|
632
630
|
function pushOptionalString(args, flag, value) {
|
|
@@ -655,33 +653,138 @@ function selectArg(select) {
|
|
|
655
653
|
return;
|
|
656
654
|
return Array.isArray(select) ? select.join(",") : select;
|
|
657
655
|
}
|
|
658
|
-
function
|
|
659
|
-
const
|
|
660
|
-
|
|
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
|
+
function validatePositiveInteger(value, name) {
|
|
676
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
677
|
+
throw new Error(`${name} must be a positive integer`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
var SELECTOR_KINDS = ["line", "hash", "symbol", "page", "node", "asset"];
|
|
681
|
+
function isSelectorKind(value) {
|
|
682
|
+
return SELECTOR_KINDS.includes(value);
|
|
683
|
+
}
|
|
684
|
+
function parseSelector(text) {
|
|
685
|
+
const separator = text.indexOf(":");
|
|
686
|
+
if (separator < 0) {
|
|
687
|
+
throw new Error("selector must start with line:, hash:, symbol:, page:, node:, or asset:");
|
|
688
|
+
}
|
|
689
|
+
const kind = text.slice(0, separator);
|
|
690
|
+
const value = text.slice(separator + 1);
|
|
691
|
+
if (value.length === 0)
|
|
692
|
+
throw new Error(`${kind} selector must not be empty`);
|
|
693
|
+
if (!isSelectorKind(kind)) {
|
|
694
|
+
throw new Error(`unknown selector kind \`${kind}\`; expected line, hash, symbol, page, node, or asset`);
|
|
695
|
+
}
|
|
696
|
+
if (kind === "line" || kind === "hash") {
|
|
697
|
+
const parts = value.split(":");
|
|
698
|
+
if (parts.length > 2)
|
|
699
|
+
throw new Error("selector has too many components");
|
|
700
|
+
if (parts.length === 2) {
|
|
701
|
+
const column = Number(parts[1]);
|
|
702
|
+
if (!Number.isSafeInteger(column))
|
|
703
|
+
throw new Error(`invalid column \`${parts[1]}\``);
|
|
704
|
+
if (column < 1)
|
|
705
|
+
throw new Error("column must be greater than zero");
|
|
706
|
+
return { kind, value: parts[0], column };
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
if (kind === "page") {
|
|
710
|
+
if (value.includes(":"))
|
|
711
|
+
throw new Error("page selector has too many components");
|
|
712
|
+
const page = Number(value);
|
|
713
|
+
if (!Number.isSafeInteger(page))
|
|
714
|
+
throw new Error(`invalid page \`${value}\``);
|
|
715
|
+
if (page < 1)
|
|
716
|
+
throw new Error("page must be greater than zero");
|
|
717
|
+
}
|
|
718
|
+
return { kind, value };
|
|
661
719
|
}
|
|
662
|
-
function
|
|
720
|
+
function buildInspectArgs(input) {
|
|
721
|
+
validateTarget("inspect", input.path, input.stdinName);
|
|
722
|
+
if (input.selector !== undefined && input.selector.length === 0) {
|
|
723
|
+
throw new Error("selector must not be empty");
|
|
724
|
+
}
|
|
663
725
|
const vision = validateVisionLevelVsMode({
|
|
664
726
|
visionMode: input.visionMode,
|
|
665
727
|
visionLevel: input.visionLevel
|
|
666
728
|
});
|
|
667
729
|
if (!vision.ok)
|
|
668
730
|
throw new Error(vision.error);
|
|
669
|
-
const
|
|
731
|
+
const facets = selectedInspectFacets(input.select);
|
|
732
|
+
const hasContent = hasInspectFacet(facets, "content");
|
|
733
|
+
const hasIdentity = hasInspectFacet(facets, "identity");
|
|
734
|
+
const hasMap = hasInspectFacet(facets, "map");
|
|
735
|
+
const hasSourceFacet = hasMap || hasInspectFacet(facets, "diagnostics") || hasIdentity;
|
|
736
|
+
if (input.end !== undefined)
|
|
737
|
+
validatePositiveInteger(input.end, "end");
|
|
738
|
+
if (input.limit !== undefined)
|
|
739
|
+
validatePositiveInteger(input.limit, "limit");
|
|
740
|
+
if (input.end !== undefined && input.limit !== undefined) {
|
|
741
|
+
throw new Error("cannot combine end with limit");
|
|
742
|
+
}
|
|
670
743
|
if (input.depth !== undefined) {
|
|
671
|
-
if (!Number.
|
|
744
|
+
if (!Number.isSafeInteger(input.depth) || input.depth < 0) {
|
|
672
745
|
throw new Error("depth must be a non-negative integer");
|
|
673
746
|
}
|
|
674
|
-
if (!
|
|
747
|
+
if (!hasMap)
|
|
675
748
|
throw new Error("depth requires the map facet");
|
|
676
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
|
+
}
|
|
677
778
|
const args = [];
|
|
678
779
|
if (input.readseekDir !== undefined)
|
|
679
780
|
args.push("--readseek-dir", input.readseekDir);
|
|
680
|
-
args.push("
|
|
781
|
+
args.push("inspect", input.path);
|
|
782
|
+
if (input.selector !== undefined)
|
|
783
|
+
args.push(input.selector);
|
|
681
784
|
const select = selectArg(input.select);
|
|
682
785
|
if (select !== undefined)
|
|
683
786
|
args.push("--select", select);
|
|
684
|
-
pushOptionalString(args, "--
|
|
787
|
+
pushOptionalString(args, "--query", input.query);
|
|
685
788
|
pushOptionalNumber(args, "--end", input.end);
|
|
686
789
|
pushOptionalNumber(args, "--limit", input.limit);
|
|
687
790
|
pushOptionalNumber(args, "--depth", input.depth);
|
|
@@ -689,30 +792,24 @@ function buildDigestArgs(input) {
|
|
|
689
792
|
pushOptionalString(args, "--vision-mode", input.visionMode);
|
|
690
793
|
pushOptionalString(args, "--vision-level", input.visionLevel);
|
|
691
794
|
pushOptionalString(args, "--stdin-name", input.stdinName);
|
|
795
|
+
args.push("--kdl");
|
|
692
796
|
return args;
|
|
693
797
|
}
|
|
694
|
-
function
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
798
|
+
function buildExtractArgs(input) {
|
|
799
|
+
validateTarget("extract", input.path, input.stdinName);
|
|
800
|
+
if (input.selector.length === 0)
|
|
801
|
+
throw new Error("extract requires an asset selector");
|
|
802
|
+
if (!input.selector.startsWith("asset:") || input.selector === "asset:") {
|
|
803
|
+
throw new Error("extract requires an asset: selector");
|
|
804
|
+
}
|
|
805
|
+
if (input.output.length === 0 || input.output === "-") {
|
|
806
|
+
throw new Error("extract output must be a file path");
|
|
807
|
+
}
|
|
702
808
|
const args = [];
|
|
703
809
|
if (input.readseekDir !== undefined)
|
|
704
810
|
args.push("--readseek-dir", input.readseekDir);
|
|
705
|
-
args.push("
|
|
706
|
-
|
|
707
|
-
args.push("--format", input.format);
|
|
708
|
-
pushOptionalString(args, "--node", input.node);
|
|
709
|
-
pushOptionalNumber(args, "--page", input.page);
|
|
710
|
-
pushOptionalString(args, "--kind", input.kind);
|
|
711
|
-
pushOptionalNumber(args, "--depth", input.depth);
|
|
712
|
-
if (input.outline)
|
|
713
|
-
args.push("--outline");
|
|
714
|
-
pushOptionalString(args, "--vision-mode", input.visionMode);
|
|
715
|
-
pushOptionalString(args, "--vision-level", input.visionLevel);
|
|
811
|
+
args.push("--output", input.output, "extract", input.path, input.selector);
|
|
812
|
+
pushOptionalString(args, "--stdin-name", input.stdinName);
|
|
716
813
|
return args;
|
|
717
814
|
}
|
|
718
815
|
function buildSearchArgs(input) {
|
|
@@ -720,15 +817,17 @@ function buildSearchArgs(input) {
|
|
|
720
817
|
pushOptionalString(args, "--path", input.path);
|
|
721
818
|
pushOptionalString(args, "--language", input.language);
|
|
722
819
|
pushOptionalNumber(args, "--limit", input.limit);
|
|
723
|
-
|
|
820
|
+
appendGitScopeArgs(args, input);
|
|
821
|
+
args.push("--kdl");
|
|
822
|
+
return args;
|
|
724
823
|
}
|
|
725
824
|
function buildDefArgs(input) {
|
|
726
825
|
const args = ["def", input.name];
|
|
727
826
|
pushOptionalString(args, "--path", input.path);
|
|
728
|
-
if (input.format !== undefined)
|
|
729
|
-
args.push("--format", input.format);
|
|
730
827
|
pushOptionalString(args, "--language", input.language);
|
|
731
|
-
|
|
828
|
+
appendGitScopeArgs(args, input);
|
|
829
|
+
args.push("--kdl");
|
|
830
|
+
return args;
|
|
732
831
|
}
|
|
733
832
|
function buildRefsArgs(input) {
|
|
734
833
|
const validated = validateRefsNameVsScope(input);
|
|
@@ -743,10 +842,10 @@ function buildRefsArgs(input) {
|
|
|
743
842
|
pushOptionalNumber(args, "--line", input.line);
|
|
744
843
|
pushOptionalNumber(args, "--column", input.column);
|
|
745
844
|
}
|
|
746
|
-
if (input.format !== undefined)
|
|
747
|
-
args.push("--format", input.format);
|
|
748
845
|
pushOptionalString(args, "--language", input.language);
|
|
749
|
-
|
|
846
|
+
appendGitScopeArgs(args, input);
|
|
847
|
+
args.push("--kdl");
|
|
848
|
+
return args;
|
|
750
849
|
}
|
|
751
850
|
function buildEditArgs(input) {
|
|
752
851
|
const args = ["edit", input.path];
|
|
@@ -758,6 +857,7 @@ function buildEditArgs(input) {
|
|
|
758
857
|
pushOptionalString(args, "--language", input.language);
|
|
759
858
|
if (input.requestFromStdin)
|
|
760
859
|
args.push("--edits", "-");
|
|
860
|
+
args.push("--kdl");
|
|
761
861
|
return args;
|
|
762
862
|
}
|
|
763
863
|
function buildRenameArgs(input) {
|
|
@@ -770,7 +870,9 @@ function buildRenameArgs(input) {
|
|
|
770
870
|
flagOrder: input.flagOrder ?? "plan-hash-first"
|
|
771
871
|
});
|
|
772
872
|
pushOptionalString(args, "--language", input.language);
|
|
773
|
-
|
|
873
|
+
appendGitScopeArgs(args, input);
|
|
874
|
+
args.push("--kdl");
|
|
875
|
+
return args;
|
|
774
876
|
}
|
|
775
877
|
// ../readseek-api/src/platform.ts
|
|
776
878
|
import { accessSync, constants as fsConstants, existsSync, statSync } from "node:fs";
|
|
@@ -785,6 +887,8 @@ var READSEEK_PLATFORM_PACKAGES = {
|
|
|
785
887
|
};
|
|
786
888
|
var resolveFromPackage = createRequire(import.meta.url).resolve;
|
|
787
889
|
function readSeekBinaryName(platform = process.platform) {
|
|
890
|
+
if (platform === "linux")
|
|
891
|
+
return "readseek.AppImage";
|
|
788
892
|
return platform === "win32" ? "readseek.exe" : "readseek";
|
|
789
893
|
}
|
|
790
894
|
function readSeekPlatformKey(platform = process.platform, arch = process.arch) {
|
|
@@ -833,22 +937,304 @@ function resolveReadSeekBinaryFromPackages(resolver = resolveFromPackage, option
|
|
|
833
937
|
accessSync(binaryPath, fsConstants.X_OK);
|
|
834
938
|
return binaryPath;
|
|
835
939
|
}
|
|
836
|
-
// ../readseek-api/src/
|
|
837
|
-
var READSEEK_USAGE_HINT =
|
|
940
|
+
// ../readseek-api/src/error.ts
|
|
941
|
+
var READSEEK_USAGE_HINT = /(?:^|\n)\s*Run readseek(?: [\w-]+)* --help for more information\.?\s*$/;
|
|
838
942
|
function cleanReadSeekErrorText(stderr) {
|
|
839
943
|
return stderr.replace(READSEEK_USAGE_HINT, "").replace(/^error:\s*/i, "").replace(/\s*\n\s*/g, " ").trim();
|
|
840
944
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
945
|
+
// ../readseek-api/src/kdl.ts
|
|
946
|
+
import { parse } from "@bgotink/kdl";
|
|
947
|
+
function convertNode(node) {
|
|
948
|
+
if (node.getTag() !== null)
|
|
949
|
+
throw new Error("KDL node annotations are not supported");
|
|
950
|
+
const properties = new Map;
|
|
951
|
+
for (const entry of node.entries) {
|
|
952
|
+
const name = entry.getName();
|
|
953
|
+
if (name === null)
|
|
954
|
+
throw new Error("KDL positional arguments are not supported");
|
|
955
|
+
if (properties.has(name))
|
|
956
|
+
throw new Error(`KDL duplicate property '${name}'`);
|
|
957
|
+
if (entry.getTag() !== null)
|
|
958
|
+
throw new Error("KDL value annotations are not supported");
|
|
959
|
+
const value = entry.getValue();
|
|
960
|
+
if (value === null)
|
|
961
|
+
throw new Error("KDL null values are not supported");
|
|
962
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
963
|
+
throw new Error("KDL number out of range");
|
|
964
|
+
}
|
|
965
|
+
properties.set(name, value);
|
|
966
|
+
}
|
|
967
|
+
return {
|
|
968
|
+
name: node.getName(),
|
|
969
|
+
properties,
|
|
970
|
+
children: node.children?.nodes.map(convertNode) ?? []
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
function parseKdlDocument(text) {
|
|
974
|
+
const document = parse(text);
|
|
975
|
+
if (document.nodes.length !== 1)
|
|
976
|
+
throw new Error("KDL document must contain exactly one root node");
|
|
977
|
+
return convertNode(document.nodes[0]);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// ../readseek-api/src/output-kdl.ts
|
|
981
|
+
function properties(node) {
|
|
982
|
+
return Object.fromEntries(node.properties);
|
|
983
|
+
}
|
|
984
|
+
function children(node, name) {
|
|
985
|
+
return node.children.filter((child) => child.name === name);
|
|
986
|
+
}
|
|
987
|
+
function child(node, name) {
|
|
988
|
+
return node.children.find((candidate) => candidate.name === name);
|
|
989
|
+
}
|
|
990
|
+
function requireProperty(node, name) {
|
|
991
|
+
const value = node.properties.get(name);
|
|
992
|
+
if (value === undefined)
|
|
993
|
+
throw new Error(`invalid readseek KDL: ${node.name} is missing '${name}'`);
|
|
994
|
+
return value;
|
|
995
|
+
}
|
|
996
|
+
function line(node) {
|
|
997
|
+
const anchor = requireProperty(node, "anchor");
|
|
998
|
+
const content = requireProperty(node, "content");
|
|
999
|
+
if (typeof anchor !== "string" || typeof content !== "string") {
|
|
1000
|
+
throw new Error("invalid readseek KDL line");
|
|
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
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
function mapNode(node) {
|
|
1012
|
+
return {
|
|
1013
|
+
...properties(node),
|
|
1014
|
+
children: children(node, "node").map(mapNode)
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
function sourceContent(node) {
|
|
1018
|
+
const result = properties(node);
|
|
1019
|
+
result.hashlines = children(node, "line").map(line);
|
|
1020
|
+
const objects = children(node, "object");
|
|
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
|
+
}));
|
|
1031
|
+
}
|
|
1032
|
+
return result;
|
|
1033
|
+
}
|
|
1034
|
+
function sourceMap(node) {
|
|
1035
|
+
const root = child(node, "node");
|
|
1036
|
+
return {
|
|
1037
|
+
...properties(node),
|
|
1038
|
+
...root ? { root: mapNode(root) } : {},
|
|
1039
|
+
symbols: children(node, "symbol").map(properties)
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
function diagnostics(node) {
|
|
1043
|
+
return {
|
|
1044
|
+
...properties(node),
|
|
1045
|
+
diagnostics: children(node, "diagnostic").map(properties)
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
function identity(node) {
|
|
1049
|
+
const result = properties(node);
|
|
1050
|
+
const identifier = child(node, "identifier");
|
|
1051
|
+
const symbol = child(node, "symbol");
|
|
1052
|
+
if (identifier)
|
|
1053
|
+
result.identifier = properties(identifier);
|
|
1054
|
+
if (symbol)
|
|
1055
|
+
result.symbol = properties(symbol);
|
|
1056
|
+
return result;
|
|
1057
|
+
}
|
|
1058
|
+
function sourceDocument(root) {
|
|
1059
|
+
const result = {};
|
|
1060
|
+
const metadata = child(root, "metadata");
|
|
1061
|
+
const cache = child(root, "cache");
|
|
1062
|
+
const content = child(root, "content");
|
|
1063
|
+
const map = child(root, "map");
|
|
1064
|
+
const diagnosticOutput = child(root, "diagnostics");
|
|
1065
|
+
const identityOutput = child(root, "identity");
|
|
1066
|
+
if (metadata)
|
|
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);
|
|
1078
|
+
return result;
|
|
1079
|
+
}
|
|
1080
|
+
function hit(node) {
|
|
1081
|
+
return {
|
|
1082
|
+
...properties(node),
|
|
1083
|
+
hashlines: children(node, "line").map(line),
|
|
1084
|
+
captures: children(node, "capture").map((capture) => ({
|
|
1085
|
+
...properties(capture),
|
|
1086
|
+
hashlines: children(capture, "line").map(line)
|
|
1087
|
+
}))
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
function search(root) {
|
|
1091
|
+
return {
|
|
1092
|
+
...properties(root),
|
|
1093
|
+
results: children(root, "match").map((match) => ({
|
|
1094
|
+
...properties(match),
|
|
1095
|
+
matches: children(match, "hit").map(hit)
|
|
1096
|
+
})),
|
|
1097
|
+
documents: children(root, "document").map((documentNode) => ({
|
|
1098
|
+
...properties(documentNode),
|
|
1099
|
+
hits: children(documentNode, "hit").map(properties)
|
|
1100
|
+
}))
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
function references(root) {
|
|
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
|
+
});
|
|
1117
|
+
return {
|
|
1118
|
+
references: parsed,
|
|
1119
|
+
locations: parsed.map((reference) => {
|
|
1120
|
+
const { symbol, ...location } = reference;
|
|
1121
|
+
return symbol && typeof symbol === "object" ? { ...location, ...symbol } : location;
|
|
1122
|
+
})
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
function definitions(root) {
|
|
1126
|
+
return {
|
|
1127
|
+
locations: children(root, "definition").map((definition) => {
|
|
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;
|
|
1165
|
+
}
|
|
1166
|
+
function renameEdit(node) {
|
|
1167
|
+
const sourceLine = child(node, "line");
|
|
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
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
function renameFile(node) {
|
|
1176
|
+
return {
|
|
1177
|
+
...properties(node),
|
|
1178
|
+
edits: children(node, "edit").map(renameEdit),
|
|
1179
|
+
conflicts: children(node, "conflict").map(properties)
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
function rename(root) {
|
|
1183
|
+
return {
|
|
1184
|
+
...renameFile(root),
|
|
1185
|
+
others: children(root, "other").map(renameFile)
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
function genericNode(node) {
|
|
1189
|
+
const result = properties(node);
|
|
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;
|
|
1200
|
+
}
|
|
1201
|
+
function pdfDocument(root) {
|
|
1202
|
+
return {
|
|
1203
|
+
...properties(root),
|
|
1204
|
+
nodes: children(root, "node").map(genericNode),
|
|
1205
|
+
assets: children(root, "asset").map(genericNode),
|
|
1206
|
+
vision: children(root, "vision").map(genericNode)
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
function parseReadSeekKdl(text) {
|
|
1210
|
+
const root = parseKdlDocument(text);
|
|
1211
|
+
switch (root.name) {
|
|
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}`);
|
|
847
1230
|
}
|
|
848
1231
|
}
|
|
849
1232
|
// ../readseek-api/src/process.ts
|
|
850
1233
|
var DEFAULT_READSEEK_TIMEOUT_MS = 120000;
|
|
851
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;
|
|
1237
|
+
}
|
|
852
1238
|
function resolveProcessLimits(options = {}) {
|
|
853
1239
|
return {
|
|
854
1240
|
timeoutMs: options.timeoutMs ?? DEFAULT_READSEEK_TIMEOUT_MS,
|
|
@@ -879,9 +1265,6 @@ function ensureSuccessfulStdout(result) {
|
|
|
879
1265
|
return result.stdout;
|
|
880
1266
|
throw failureFromProcessResult(result);
|
|
881
1267
|
}
|
|
882
|
-
function parseSuccessfulJson(result) {
|
|
883
|
-
return parseReadSeekJson(ensureSuccessfulStdout(result));
|
|
884
|
-
}
|
|
885
1268
|
// ../readseek-api/src/process-node.ts
|
|
886
1269
|
import { spawn } from "node:child_process";
|
|
887
1270
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -947,7 +1330,7 @@ async function runReadSeekNode(binaryPath, args, options = {}) {
|
|
|
947
1330
|
try {
|
|
948
1331
|
child = spawn(binaryPath, [...args], {
|
|
949
1332
|
cwd: options.cwd,
|
|
950
|
-
env:
|
|
1333
|
+
env: readSeekProcessEnvironment(),
|
|
951
1334
|
shell: false,
|
|
952
1335
|
stdio: [options.stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"],
|
|
953
1336
|
windowsHide: true
|
|
@@ -990,363 +1373,357 @@ async function runReadSeekNode(binaryPath, args, options = {}) {
|
|
|
990
1373
|
async function runReadSeekNodeText(binaryPath, args, options = {}) {
|
|
991
1374
|
return ensureSuccessfulStdout(await runReadSeekNode(binaryPath, args, options));
|
|
992
1375
|
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
function
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1376
|
+
// ../readseek-api/src/client-output.ts
|
|
1377
|
+
import { Type } from "@sinclair/typebox";
|
|
1378
|
+
import { AssertError, Value } from "@sinclair/typebox/value";
|
|
1379
|
+
var OPERATIONS = ["Clone", "Clean", "Default", "Assert"];
|
|
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
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
var Int = Type.Integer({ minimum: Number.MIN_SAFE_INTEGER, maximum: Number.MAX_SAFE_INTEGER });
|
|
1391
|
+
var Str = Type.String();
|
|
1392
|
+
var Bool = Type.Boolean();
|
|
1393
|
+
var OptStr = Type.Optional(Str);
|
|
1394
|
+
var OptInt = Type.Optional(Int);
|
|
1395
|
+
var Hashline = Type.Object({ line: Int, hash: Str, text: Str });
|
|
1396
|
+
var Hashlines = Type.Array(Hashline);
|
|
1397
|
+
var ReadOutput = Type.Object({
|
|
1398
|
+
file: Str,
|
|
1399
|
+
language: Str,
|
|
1400
|
+
line_count: Int,
|
|
1401
|
+
file_hash: Str,
|
|
1402
|
+
start_line: Int,
|
|
1403
|
+
end_line: Int,
|
|
1404
|
+
hashlines: Hashlines
|
|
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
|
+
var MapNode = Type.Recursive((Self) => Type.Object({
|
|
1418
|
+
kind: Str,
|
|
1419
|
+
name: Str,
|
|
1420
|
+
start_line: Int,
|
|
1421
|
+
end_line: Int,
|
|
1422
|
+
start_anchor: Str,
|
|
1423
|
+
end_anchor: Str,
|
|
1424
|
+
children: Type.Array(Self)
|
|
1425
|
+
}));
|
|
1426
|
+
var MapOutput = Type.Object({
|
|
1427
|
+
file: Str,
|
|
1428
|
+
language: Str,
|
|
1429
|
+
line_count: Int,
|
|
1430
|
+
file_hash: Str,
|
|
1431
|
+
root: Type.Optional(MapNode),
|
|
1432
|
+
symbols: Type.Array(SymbolOutput)
|
|
1433
|
+
});
|
|
1434
|
+
var SearchSpan = {
|
|
1435
|
+
start_byte: OptInt,
|
|
1436
|
+
end_byte: OptInt,
|
|
1437
|
+
text: OptStr,
|
|
1438
|
+
start_line: Int,
|
|
1439
|
+
end_line: Int,
|
|
1440
|
+
start_hash: Str,
|
|
1441
|
+
end_hash: Str,
|
|
1442
|
+
hashlines: Hashlines
|
|
1443
|
+
};
|
|
1444
|
+
var SearchCapture = Type.Object({ name: Str, ...SearchSpan });
|
|
1445
|
+
var SearchMatch = Type.Object({
|
|
1446
|
+
pattern_index: Type.Integer({ default: 0 }),
|
|
1447
|
+
...SearchSpan,
|
|
1448
|
+
captures: Type.Array(SearchCapture)
|
|
1449
|
+
});
|
|
1450
|
+
var SearchFileOutput = Type.Object({
|
|
1451
|
+
file: Str,
|
|
1452
|
+
language: Str,
|
|
1453
|
+
file_hash: Str,
|
|
1454
|
+
matches: Type.Array(SearchMatch)
|
|
1455
|
+
});
|
|
1456
|
+
var SearchEnvelope = Type.Object({
|
|
1457
|
+
results: Type.Array(SearchFileOutput),
|
|
1458
|
+
returned_matches: OptInt,
|
|
1459
|
+
truncated: Type.Optional(Bool),
|
|
1460
|
+
byte_basis: OptStr
|
|
1461
|
+
});
|
|
1462
|
+
var RefsEnvelope = Type.Object({
|
|
1463
|
+
references: Type.Array(Type.Object({
|
|
1464
|
+
file: Str,
|
|
1465
|
+
line: Int,
|
|
1466
|
+
column: Int,
|
|
1467
|
+
line_hash: Str,
|
|
1468
|
+
text: Str,
|
|
1469
|
+
symbol: Type.Optional(Type.Object({ qualified_name: OptStr }))
|
|
1470
|
+
}))
|
|
1471
|
+
});
|
|
1472
|
+
var Diagnostic = Type.Object({
|
|
1473
|
+
kind: Type.Union([Type.Literal("error"), Type.Literal("missing")]),
|
|
1474
|
+
start_line: Int,
|
|
1475
|
+
end_line: Int
|
|
1476
|
+
});
|
|
1477
|
+
var CheckEnvelope = Type.Object({
|
|
1478
|
+
file_hash: OptStr,
|
|
1479
|
+
error_count: Int,
|
|
1480
|
+
missing_count: Int,
|
|
1481
|
+
diagnostics: Type.Array(Diagnostic)
|
|
1482
|
+
});
|
|
1483
|
+
var DetectedObject = Type.Object({
|
|
1484
|
+
label: Str,
|
|
1485
|
+
bbox: Type.Tuple([Int, Int, Int, Int])
|
|
1486
|
+
});
|
|
1487
|
+
var PreparedImage = Type.Object({
|
|
1488
|
+
mime: Str,
|
|
1489
|
+
encoding: Type.Literal("base64"),
|
|
1490
|
+
data: Str
|
|
1491
|
+
});
|
|
1492
|
+
var SourceDetection = Type.Object({
|
|
1493
|
+
kind: Type.Literal("source"),
|
|
1494
|
+
type: Str,
|
|
1495
|
+
file: Str,
|
|
1496
|
+
language: Str,
|
|
1497
|
+
engine: OptStr,
|
|
1498
|
+
supported: Bool,
|
|
1499
|
+
mime: OptStr,
|
|
1500
|
+
syntax: OptStr
|
|
1501
|
+
});
|
|
1502
|
+
var ImageDetection = Type.Object({
|
|
1503
|
+
kind: Type.Literal("image"),
|
|
1504
|
+
type: Str,
|
|
1505
|
+
file: Str,
|
|
1506
|
+
mime: OptStr,
|
|
1507
|
+
format: Str,
|
|
1508
|
+
width: Int,
|
|
1509
|
+
height: Int,
|
|
1510
|
+
animated: Bool,
|
|
1511
|
+
encoding: Type.Optional(Type.Literal("base64")),
|
|
1512
|
+
data: OptStr,
|
|
1513
|
+
ocr: OptStr,
|
|
1514
|
+
caption: OptStr,
|
|
1515
|
+
objects: Type.Optional(Type.Array(DetectedObject))
|
|
1516
|
+
});
|
|
1517
|
+
var TextDetection = Type.Object({
|
|
1518
|
+
kind: Type.Literal("text"),
|
|
1519
|
+
type: Str,
|
|
1520
|
+
file: Str,
|
|
1521
|
+
mime: OptStr
|
|
1522
|
+
});
|
|
1523
|
+
var PdfDetection = Type.Object({
|
|
1524
|
+
kind: Type.Literal("pdf"),
|
|
1525
|
+
type: Type.Literal("application/pdf"),
|
|
1526
|
+
file: Str,
|
|
1527
|
+
mime: OptStr,
|
|
1528
|
+
format: Type.Literal("pdf"),
|
|
1529
|
+
pages: Int
|
|
1530
|
+
});
|
|
1531
|
+
var Detection = Type.Union([SourceDetection, ImageDetection, TextDetection, PdfDetection]);
|
|
1532
|
+
var SourceDetectionInput = Type.Omit(SourceDetection, ["kind"]);
|
|
1533
|
+
var ImageDetectionInput = Type.Omit(ImageDetection, ["kind"]);
|
|
1534
|
+
var TextDetectionInput = Type.Omit(TextDetection, ["kind"]);
|
|
1535
|
+
var PdfDetectionInput = Type.Omit(PdfDetection, ["kind"]);
|
|
1536
|
+
var UnspannedContent = Type.Omit(ReadOutput, ["start_line", "end_line"]);
|
|
1537
|
+
var PdfInspectMetadata = Type.Object({ source: Str, format: Type.Literal("pdf"), pages: Int });
|
|
1538
|
+
var DocumentCache = Type.Object({
|
|
1539
|
+
content_hash: Str,
|
|
1540
|
+
state: Type.Union([Type.Literal("built"), Type.Literal("reused")])
|
|
1541
|
+
});
|
|
1542
|
+
var Identifier = Type.Object({
|
|
1543
|
+
text: Str,
|
|
1544
|
+
start_column: Int,
|
|
1545
|
+
end_column: Int,
|
|
1546
|
+
start_byte: Int,
|
|
1547
|
+
end_byte: Int
|
|
1548
|
+
});
|
|
1549
|
+
var IdentifyEnvelope = Type.Object({
|
|
1550
|
+
file: Str,
|
|
1551
|
+
language: Str,
|
|
1552
|
+
engine: OptStr,
|
|
1553
|
+
line_count: Int,
|
|
1554
|
+
file_hash: Str,
|
|
1555
|
+
line: Int,
|
|
1556
|
+
column: Int,
|
|
1557
|
+
line_hash: Str,
|
|
1558
|
+
identifier: Type.Optional(Identifier),
|
|
1559
|
+
symbol: Type.Optional(Type.Object({
|
|
1560
|
+
name: Str,
|
|
1561
|
+
kind: Str,
|
|
1562
|
+
qualified_name: Str,
|
|
1563
|
+
start_line: Int,
|
|
1564
|
+
end_line: Int
|
|
1565
|
+
}))
|
|
1566
|
+
});
|
|
1567
|
+
var EditOutputSchema = Type.Object({
|
|
1568
|
+
file: Str,
|
|
1569
|
+
before_hash: Str,
|
|
1570
|
+
file_hash: Str,
|
|
1571
|
+
plan_hash: Str,
|
|
1572
|
+
applied: Bool,
|
|
1573
|
+
changed: Bool,
|
|
1574
|
+
edit_count: Int,
|
|
1575
|
+
before_content: OptStr,
|
|
1576
|
+
content: OptStr
|
|
1577
|
+
});
|
|
1578
|
+
var RenameConflictSchema = Type.Object({ line: Int, column: Int, reason: Str });
|
|
1579
|
+
var RenameEditSchema = Type.Object({
|
|
1580
|
+
line: Int,
|
|
1581
|
+
start_column: Int,
|
|
1582
|
+
end_column: Int,
|
|
1583
|
+
start_byte: Int,
|
|
1584
|
+
end_byte: Int,
|
|
1585
|
+
occurrence: Str,
|
|
1586
|
+
line_hash: Str,
|
|
1587
|
+
text: Str
|
|
1588
|
+
});
|
|
1589
|
+
var Conflicts = Type.Array(RenameConflictSchema, { default: [] });
|
|
1590
|
+
var Edits = Type.Array(RenameEditSchema, { default: [] });
|
|
1591
|
+
var RenameFileOutputSchema = Type.Object({
|
|
1592
|
+
file: Str,
|
|
1593
|
+
language: Str,
|
|
1594
|
+
engine: OptStr,
|
|
1595
|
+
file_hash: Str,
|
|
1596
|
+
conflicts: Conflicts,
|
|
1597
|
+
edits: Edits
|
|
1598
|
+
});
|
|
1599
|
+
var RenameOutputSchema = Type.Object({
|
|
1600
|
+
file: Str,
|
|
1601
|
+
language: Str,
|
|
1602
|
+
engine: OptStr,
|
|
1603
|
+
file_hash: Str,
|
|
1604
|
+
old_name: Str,
|
|
1605
|
+
new_name: Str,
|
|
1606
|
+
plan_hash: Str,
|
|
1607
|
+
applied: Bool,
|
|
1608
|
+
conflicts: Conflicts,
|
|
1609
|
+
edits: Edits,
|
|
1610
|
+
others: Type.Array(RenameFileOutputSchema, { default: [] })
|
|
1611
|
+
});
|
|
1612
|
+
var DefEnvelope = Type.Object({
|
|
1613
|
+
locations: Type.Array(Type.Object({
|
|
1614
|
+
file: Str,
|
|
1615
|
+
line: Int,
|
|
1616
|
+
column: Int,
|
|
1617
|
+
line_hash: Str,
|
|
1618
|
+
text: Str,
|
|
1619
|
+
kind: OptStr,
|
|
1620
|
+
name: OptStr,
|
|
1621
|
+
qualified_name: OptStr
|
|
1622
|
+
}))
|
|
1623
|
+
});
|
|
1041
1624
|
function parseReadOutput(value) {
|
|
1042
|
-
|
|
1043
|
-
throw new Error("invalid readseek read output");
|
|
1044
|
-
const output = value;
|
|
1045
|
-
const hashlines = output.hashlines;
|
|
1046
|
-
if (!Array.isArray(hashlines))
|
|
1047
|
-
throw new Error("invalid readseek hashlines");
|
|
1048
|
-
return {
|
|
1049
|
-
file: requireString(output.file, "file"),
|
|
1050
|
-
language: requireString(output.language, "language"),
|
|
1051
|
-
line_count: requireNumber(output.line_count, "line_count"),
|
|
1052
|
-
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1053
|
-
start_line: requireNumber(output.start_line, "start_line"),
|
|
1054
|
-
end_line: requireNumber(output.end_line, "end_line"),
|
|
1055
|
-
hashlines: hashlines.map((line) => parseHashline(line, "hashline"))
|
|
1056
|
-
};
|
|
1057
|
-
}
|
|
1058
|
-
function parseMapNode(value) {
|
|
1059
|
-
if (!value || typeof value !== "object")
|
|
1060
|
-
throw new Error("invalid readseek map node");
|
|
1061
|
-
const node = value;
|
|
1062
|
-
if (!Array.isArray(node.children))
|
|
1063
|
-
throw new Error("invalid readseek map node children");
|
|
1064
|
-
return {
|
|
1065
|
-
kind: requireString(node.kind, "map node.kind"),
|
|
1066
|
-
name: requireString(node.name, "map node.name"),
|
|
1067
|
-
start_line: requireNumber(node.start_line, "map node.start_line"),
|
|
1068
|
-
end_line: requireNumber(node.end_line, "map node.end_line"),
|
|
1069
|
-
start_anchor: requireString(node.start_anchor, "map node.start_anchor"),
|
|
1070
|
-
end_anchor: requireString(node.end_anchor, "map node.end_anchor"),
|
|
1071
|
-
children: node.children.map(parseMapNode)
|
|
1072
|
-
};
|
|
1625
|
+
return parse2(ReadOutput, value, "read output");
|
|
1073
1626
|
}
|
|
1074
1627
|
function parseMapOutput(value) {
|
|
1075
|
-
|
|
1076
|
-
throw new Error("invalid readseek map output");
|
|
1077
|
-
const output = value;
|
|
1078
|
-
const symbols = output.symbols;
|
|
1079
|
-
if (!Array.isArray(symbols))
|
|
1080
|
-
throw new Error("invalid readseek symbols");
|
|
1081
|
-
return {
|
|
1082
|
-
file: requireString(output.file, "file"),
|
|
1083
|
-
language: requireString(output.language, "language"),
|
|
1084
|
-
line_count: requireNumber(output.line_count, "line_count"),
|
|
1085
|
-
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1086
|
-
root: parseMapNode(output.root),
|
|
1087
|
-
symbols: symbols.map((symbol) => {
|
|
1088
|
-
if (!symbol || typeof symbol !== "object")
|
|
1089
|
-
throw new Error("invalid readseek symbol");
|
|
1090
|
-
const item = symbol;
|
|
1091
|
-
return {
|
|
1092
|
-
kind: requireString(item.kind, "symbol.kind"),
|
|
1093
|
-
name: requireString(item.name, "symbol.name"),
|
|
1094
|
-
qualified_name: requireString(item.qualified_name, "symbol.qualified_name"),
|
|
1095
|
-
start_line: requireNumber(item.start_line, "symbol.start_line"),
|
|
1096
|
-
end_line: requireNumber(item.end_line, "symbol.end_line"),
|
|
1097
|
-
start_hash: requireString(item.start_hash, "symbol.start_hash"),
|
|
1098
|
-
end_hash: requireString(item.end_hash, "symbol.end_hash")
|
|
1099
|
-
};
|
|
1100
|
-
})
|
|
1101
|
-
};
|
|
1628
|
+
return parse2(MapOutput, value, "map output");
|
|
1102
1629
|
}
|
|
1103
1630
|
function parseSearchOutput(value) {
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
if (!Array.isArray(file.matches))
|
|
1114
|
-
throw new Error("invalid readseek search matches");
|
|
1115
|
-
return {
|
|
1116
|
-
file: requireString(file.file, "search.file"),
|
|
1117
|
-
language: requireString(file.language, "search.language"),
|
|
1118
|
-
file_hash: requireString(file.file_hash, "search.file_hash"),
|
|
1119
|
-
matches: file.matches.map((match) => {
|
|
1120
|
-
if (!match || typeof match !== "object")
|
|
1121
|
-
throw new Error("invalid readseek search match");
|
|
1122
|
-
const item = match;
|
|
1123
|
-
if (!Array.isArray(item.captures))
|
|
1124
|
-
throw new Error("invalid readseek search captures");
|
|
1125
|
-
return {
|
|
1126
|
-
pattern_index: item.pattern_index === undefined ? 0 : requireNumber(item.pattern_index, "search.match.pattern_index"),
|
|
1127
|
-
start_line: requireNumber(item.start_line, "search.match.start_line"),
|
|
1128
|
-
end_line: requireNumber(item.end_line, "search.match.end_line"),
|
|
1129
|
-
start_hash: requireString(item.start_hash, "search.match.start_hash"),
|
|
1130
|
-
end_hash: requireString(item.end_hash, "search.match.end_hash"),
|
|
1131
|
-
hashlines: parseSearchHashlines(item.hashlines, "search.match.hashlines"),
|
|
1132
|
-
captures: item.captures.map((capture) => {
|
|
1133
|
-
if (!capture || typeof capture !== "object")
|
|
1134
|
-
throw new Error("invalid readseek search capture");
|
|
1135
|
-
const captureItem = capture;
|
|
1136
|
-
return {
|
|
1137
|
-
name: requireString(captureItem.name, "search.capture.name"),
|
|
1138
|
-
start_line: requireNumber(captureItem.start_line, "search.capture.start_line"),
|
|
1139
|
-
end_line: requireNumber(captureItem.end_line, "search.capture.end_line"),
|
|
1140
|
-
start_hash: requireString(captureItem.start_hash, "search.capture.start_hash"),
|
|
1141
|
-
end_hash: requireString(captureItem.end_hash, "search.capture.end_hash"),
|
|
1142
|
-
hashlines: parseSearchHashlines(captureItem.hashlines, "search.capture.hashlines")
|
|
1143
|
-
};
|
|
1144
|
-
})
|
|
1145
|
-
};
|
|
1146
|
-
})
|
|
1147
|
-
};
|
|
1148
|
-
});
|
|
1631
|
+
const envelope = parse2(SearchEnvelope, value, "search output");
|
|
1632
|
+
const results = envelope.results;
|
|
1633
|
+
if (envelope.returned_matches !== undefined)
|
|
1634
|
+
results.returned_matches = envelope.returned_matches;
|
|
1635
|
+
if (envelope.truncated !== undefined)
|
|
1636
|
+
results.truncated = envelope.truncated;
|
|
1637
|
+
if (envelope.byte_basis !== undefined)
|
|
1638
|
+
results.byte_basis = envelope.byte_basis;
|
|
1639
|
+
return results;
|
|
1149
1640
|
}
|
|
1150
1641
|
function parseRefsOutput(value) {
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
throw new Error("invalid readseek references");
|
|
1156
|
-
return output.references.map((reference) => {
|
|
1157
|
-
if (!reference || typeof reference !== "object")
|
|
1158
|
-
throw new Error("invalid readseek reference");
|
|
1159
|
-
const item = reference;
|
|
1160
|
-
const symbol = item.symbol;
|
|
1161
|
-
const enclosing = symbol && typeof symbol === "object" ? optionalString(symbol.qualified_name, "reference.symbol.qualified_name") : undefined;
|
|
1162
|
-
return {
|
|
1163
|
-
file: requireString(item.file, "reference.file"),
|
|
1164
|
-
line: requireNumber(item.line, "reference.line"),
|
|
1165
|
-
column: requireNumber(item.column, "reference.column"),
|
|
1166
|
-
line_hash: requireString(item.line_hash, "reference.line_hash"),
|
|
1167
|
-
text: requireString(item.text, "reference.text"),
|
|
1168
|
-
enclosingSymbol: enclosing
|
|
1169
|
-
};
|
|
1170
|
-
});
|
|
1171
|
-
}
|
|
1172
|
-
function parseDiagnosticKind(value) {
|
|
1173
|
-
if (value === "error" || value === "missing")
|
|
1174
|
-
return value;
|
|
1175
|
-
throw new Error("invalid readseek diagnostic.kind");
|
|
1642
|
+
return parse2(RefsEnvelope, value, "refs output").references.map(({ symbol, ...reference }) => ({
|
|
1643
|
+
...reference,
|
|
1644
|
+
enclosingSymbol: symbol?.qualified_name
|
|
1645
|
+
}));
|
|
1176
1646
|
}
|
|
1177
1647
|
function parseCheckOutput(value) {
|
|
1178
|
-
|
|
1179
|
-
throw new Error("invalid readseek check output");
|
|
1180
|
-
const output = value;
|
|
1181
|
-
if (!Array.isArray(output.diagnostics))
|
|
1182
|
-
throw new Error("invalid readseek diagnostics");
|
|
1648
|
+
const output = parse2(CheckEnvelope, value, "check output");
|
|
1183
1649
|
return {
|
|
1184
|
-
file_hash:
|
|
1185
|
-
errorCount:
|
|
1186
|
-
missingCount:
|
|
1187
|
-
diagnostics: output.diagnostics
|
|
1188
|
-
if (!diagnostic || typeof diagnostic !== "object")
|
|
1189
|
-
throw new Error("invalid readseek diagnostic");
|
|
1190
|
-
const item = diagnostic;
|
|
1191
|
-
return {
|
|
1192
|
-
kind: parseDiagnosticKind(item.kind),
|
|
1193
|
-
start_line: requireNumber(item.start_line, "diagnostic.start_line"),
|
|
1194
|
-
end_line: requireNumber(item.end_line, "diagnostic.end_line")
|
|
1195
|
-
};
|
|
1196
|
-
})
|
|
1650
|
+
file_hash: output.file_hash,
|
|
1651
|
+
errorCount: output.error_count,
|
|
1652
|
+
missingCount: output.missing_count,
|
|
1653
|
+
diagnostics: output.diagnostics
|
|
1197
1654
|
};
|
|
1198
1655
|
}
|
|
1199
|
-
function parseDetectedObjects(value) {
|
|
1200
|
-
if (value === undefined || value === null)
|
|
1201
|
-
return;
|
|
1202
|
-
if (!Array.isArray(value))
|
|
1203
|
-
throw new Error("invalid readseek detect objects");
|
|
1204
|
-
return value.map((object) => {
|
|
1205
|
-
if (!object || typeof object !== "object")
|
|
1206
|
-
throw new Error("invalid readseek detect object");
|
|
1207
|
-
const item = object;
|
|
1208
|
-
const bbox = item.bbox;
|
|
1209
|
-
if (!Array.isArray(bbox) || bbox.length !== 4)
|
|
1210
|
-
throw new Error("invalid readseek detect object.bbox");
|
|
1211
|
-
return {
|
|
1212
|
-
label: requireString(item.label, "object.label"),
|
|
1213
|
-
bbox: bbox.map((n, i) => requireNumber(n, `object.bbox[${i}]`))
|
|
1214
|
-
};
|
|
1215
|
-
});
|
|
1216
|
-
}
|
|
1217
|
-
function parseImageEncoding(value) {
|
|
1218
|
-
if (value === "base64")
|
|
1219
|
-
return value;
|
|
1220
|
-
throw new Error("invalid image encoding");
|
|
1221
|
-
}
|
|
1222
1656
|
function parseDetectOutput(value) {
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
const type = requireString(output.type, "type");
|
|
1227
|
-
const file = requireString(output.file, "file");
|
|
1228
|
-
const mime = optionalString(output.mime, "mime");
|
|
1229
|
-
if (type === "application/pdf") {
|
|
1230
|
-
if (output.format !== "pdf")
|
|
1231
|
-
throw new Error("invalid readseek PDF format");
|
|
1232
|
-
return {
|
|
1233
|
-
kind: "pdf",
|
|
1234
|
-
type,
|
|
1235
|
-
file,
|
|
1236
|
-
mime,
|
|
1237
|
-
format: output.format,
|
|
1238
|
-
pages: requireNumber(output.pages, "pages")
|
|
1239
|
-
};
|
|
1657
|
+
const output = value ?? {};
|
|
1658
|
+
if (output.type === "application/pdf") {
|
|
1659
|
+
return { kind: "pdf", ...parse2(PdfDetectionInput, value, "detect output") };
|
|
1240
1660
|
}
|
|
1241
1661
|
if (output.width !== undefined || output.height !== undefined) {
|
|
1242
|
-
return {
|
|
1243
|
-
kind: "image",
|
|
1244
|
-
type,
|
|
1245
|
-
file,
|
|
1246
|
-
mime,
|
|
1247
|
-
format: requireString(output.format, "format"),
|
|
1248
|
-
width: requireNumber(output.width, "width"),
|
|
1249
|
-
height: requireNumber(output.height, "height"),
|
|
1250
|
-
animated: requireBoolean(output.animated, "animated"),
|
|
1251
|
-
encoding: output.encoding === undefined ? undefined : parseImageEncoding(output.encoding),
|
|
1252
|
-
data: output.data === undefined ? undefined : requireString(output.data, "data"),
|
|
1253
|
-
ocr: optionalString(output.ocr, "ocr"),
|
|
1254
|
-
caption: optionalString(output.caption, "caption"),
|
|
1255
|
-
objects: parseDetectedObjects(output.objects)
|
|
1256
|
-
};
|
|
1662
|
+
return { kind: "image", ...parse2(ImageDetectionInput, value, "detect output") };
|
|
1257
1663
|
}
|
|
1258
1664
|
if (output.language !== undefined) {
|
|
1259
|
-
return {
|
|
1260
|
-
kind: "source",
|
|
1261
|
-
type,
|
|
1262
|
-
file,
|
|
1263
|
-
language: requireString(output.language, "language"),
|
|
1264
|
-
engine: optionalString(output.engine, "engine"),
|
|
1265
|
-
supported: requireBoolean(output.supported, "supported"),
|
|
1266
|
-
mime,
|
|
1267
|
-
syntax: optionalString(output.syntax, "syntax")
|
|
1268
|
-
};
|
|
1665
|
+
return { kind: "source", ...parse2(SourceDetectionInput, value, "detect output") };
|
|
1269
1666
|
}
|
|
1270
|
-
return { kind: "text",
|
|
1271
|
-
}
|
|
1272
|
-
function parseDocumentCache(value) {
|
|
1273
|
-
if (!value || typeof value !== "object")
|
|
1274
|
-
throw new Error("invalid readseek document cache");
|
|
1275
|
-
const cache = value;
|
|
1276
|
-
const state = requireString(cache.state, "cache.state");
|
|
1277
|
-
if (state !== "built" && state !== "reused")
|
|
1278
|
-
throw new Error("invalid readseek cache.state");
|
|
1279
|
-
return {
|
|
1280
|
-
content_hash: requireString(cache.content_hash, "cache.content_hash"),
|
|
1281
|
-
state
|
|
1282
|
-
};
|
|
1283
|
-
}
|
|
1284
|
-
function parseIdentifyOutput(value) {
|
|
1285
|
-
if (!value || typeof value !== "object")
|
|
1286
|
-
throw new Error("invalid readseek identify output");
|
|
1287
|
-
const output = value;
|
|
1288
|
-
const identifier = output.identifier;
|
|
1289
|
-
const symbol = output.symbol;
|
|
1290
|
-
return {
|
|
1291
|
-
file: requireString(output.file, "file"),
|
|
1292
|
-
language: requireString(output.language, "language"),
|
|
1293
|
-
engine: optionalString(output.engine, "engine"),
|
|
1294
|
-
line_count: requireNumber(output.line_count, "line_count"),
|
|
1295
|
-
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1296
|
-
line: requireNumber(output.line, "line"),
|
|
1297
|
-
column: requireNumber(output.column, "column"),
|
|
1298
|
-
line_hash: requireString(output.line_hash, "line_hash"),
|
|
1299
|
-
identifier: identifier && typeof identifier === "object" ? {
|
|
1300
|
-
text: requireString(identifier.text, "identifier.text"),
|
|
1301
|
-
start_column: requireNumber(identifier.start_column, "identifier.start_column"),
|
|
1302
|
-
end_column: requireNumber(identifier.end_column, "identifier.end_column"),
|
|
1303
|
-
start_byte: requireNumber(identifier.start_byte, "identifier.start_byte"),
|
|
1304
|
-
end_byte: requireNumber(identifier.end_byte, "identifier.end_byte")
|
|
1305
|
-
} : undefined,
|
|
1306
|
-
symbol: symbol && typeof symbol === "object" ? {
|
|
1307
|
-
name: requireString(symbol.name, "symbol.name"),
|
|
1308
|
-
kind: requireString(symbol.kind, "symbol.kind"),
|
|
1309
|
-
qualified_name: requireString(symbol.qualified_name, "symbol.qualified_name"),
|
|
1310
|
-
start_line: requireNumber(symbol.start_line, "symbol.start_line"),
|
|
1311
|
-
end_line: requireNumber(symbol.end_line, "symbol.end_line")
|
|
1312
|
-
} : undefined
|
|
1313
|
-
};
|
|
1667
|
+
return { kind: "text", ...parse2(TextDetectionInput, value, "detect output") };
|
|
1314
1668
|
}
|
|
1315
1669
|
function parseImageContent(value) {
|
|
1316
1670
|
const detection = parseDetectOutput(value);
|
|
1317
1671
|
if (detection.kind !== "image")
|
|
1318
|
-
throw new Error("invalid readseek image content");
|
|
1672
|
+
throw new Error("invalid readseek image content: expected an image");
|
|
1319
1673
|
return detection;
|
|
1320
1674
|
}
|
|
1321
1675
|
function parseTextContent(value) {
|
|
1676
|
+
const output = value ?? {};
|
|
1677
|
+
if (output.hashlines === undefined || output.start_line !== undefined)
|
|
1678
|
+
return parseReadOutput(value);
|
|
1679
|
+
const spanned = parse2(UnspannedContent, value, "content");
|
|
1680
|
+
return {
|
|
1681
|
+
...spanned,
|
|
1682
|
+
start_line: spanned.hashlines[0]?.line ?? 1,
|
|
1683
|
+
end_line: spanned.hashlines[spanned.hashlines.length - 1]?.line ?? 0
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1686
|
+
function parseDocumentCache(value) {
|
|
1687
|
+
return parse2(DocumentCache, value, "document cache");
|
|
1688
|
+
}
|
|
1689
|
+
function parseIdentifyOutput(value) {
|
|
1690
|
+
return parse2(IdentifyEnvelope, value, "identify output");
|
|
1691
|
+
}
|
|
1692
|
+
function parsePdfInspectMetadata(value) {
|
|
1322
1693
|
if (!value || typeof value !== "object")
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1694
|
+
return;
|
|
1695
|
+
if (value.format !== "pdf")
|
|
1696
|
+
return;
|
|
1697
|
+
const document = parse2(PdfInspectMetadata, value, "inspect metadata");
|
|
1698
|
+
return { kind: "pdf", type: "application/pdf", file: document.source, format: "pdf", pages: document.pages };
|
|
1699
|
+
}
|
|
1700
|
+
function parseEditOutput(value) {
|
|
1701
|
+
return parse2(EditOutputSchema, value, "edit output");
|
|
1702
|
+
}
|
|
1703
|
+
function parseRenameOutput(value) {
|
|
1704
|
+
return parse2(RenameOutputSchema, value, "rename output");
|
|
1705
|
+
}
|
|
1706
|
+
function parseDefOutput(value) {
|
|
1707
|
+
return parse2(DefEnvelope, value, "def output").locations;
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
// ../readseek-api/src/client.ts
|
|
1711
|
+
function classifyReadSeekFailure(err) {
|
|
1712
|
+
const failure = err;
|
|
1713
|
+
const message = String(failure?.message ?? err);
|
|
1714
|
+
const missing = failure?.code === "ENOENT" || /Cannot find package|Cannot find module|no such file/i.test(message);
|
|
1715
|
+
if (missing) {
|
|
1716
|
+
return { code: "readseek-not-installed", message, hint: "Run npm install to install @jarkkojs/readseek." };
|
|
1338
1717
|
}
|
|
1339
|
-
return
|
|
1718
|
+
return { code: "readseek-execution-error", message };
|
|
1340
1719
|
}
|
|
1341
|
-
function
|
|
1720
|
+
function parseSourceInspectEnvelope(value) {
|
|
1342
1721
|
if (!value || typeof value !== "object")
|
|
1343
|
-
throw new Error("invalid readseek
|
|
1722
|
+
throw new Error("invalid readseek inspect output");
|
|
1344
1723
|
const output = value;
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
metadata: parseDetectOutput(output.metadata)
|
|
1349
|
-
};
|
|
1724
|
+
const result = {};
|
|
1725
|
+
if (output.metadata !== undefined)
|
|
1726
|
+
result.metadata = parseDetectOutput(output.metadata);
|
|
1350
1727
|
if (output.cache !== undefined)
|
|
1351
1728
|
result.cache = parseDocumentCache(output.cache);
|
|
1352
1729
|
if (output.content !== undefined)
|
|
@@ -1359,224 +1736,104 @@ function parseDigestEnvelope(value) {
|
|
|
1359
1736
|
result.identity = parseIdentifyOutput(output.identity);
|
|
1360
1737
|
return result;
|
|
1361
1738
|
}
|
|
1362
|
-
function parseEditOutput(value) {
|
|
1363
|
-
if (!value || typeof value !== "object")
|
|
1364
|
-
throw new Error("invalid readseek edit output");
|
|
1365
|
-
const output = value;
|
|
1366
|
-
return {
|
|
1367
|
-
file: requireString(output.file, "file"),
|
|
1368
|
-
before_hash: requireString(output.before_hash, "before_hash"),
|
|
1369
|
-
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1370
|
-
plan_hash: requireString(output.plan_hash, "plan_hash"),
|
|
1371
|
-
applied: requireBoolean(output.applied, "applied"),
|
|
1372
|
-
changed: requireBoolean(output.changed, "changed"),
|
|
1373
|
-
edit_count: requireNumber(output.edit_count, "edit_count"),
|
|
1374
|
-
before_content: optionalString(output.before_content, "before_content"),
|
|
1375
|
-
content: optionalString(output.content, "content")
|
|
1376
|
-
};
|
|
1377
|
-
}
|
|
1378
|
-
function parseRenameConflicts(value, field) {
|
|
1379
|
-
if (value === undefined)
|
|
1380
|
-
return [];
|
|
1381
|
-
if (!Array.isArray(value))
|
|
1382
|
-
throw new Error(`invalid readseek ${field}`);
|
|
1383
|
-
return value.map((item) => {
|
|
1384
|
-
if (!item || typeof item !== "object")
|
|
1385
|
-
throw new Error(`invalid readseek ${field} entry`);
|
|
1386
|
-
const c = item;
|
|
1387
|
-
return {
|
|
1388
|
-
line: requireNumber(c.line, `${field}.line`),
|
|
1389
|
-
column: requireNumber(c.column, `${field}.column`),
|
|
1390
|
-
reason: requireString(c.reason, `${field}.reason`)
|
|
1391
|
-
};
|
|
1392
|
-
});
|
|
1393
|
-
}
|
|
1394
|
-
function parseRenameEdits(value, field) {
|
|
1395
|
-
if (value === undefined)
|
|
1396
|
-
return [];
|
|
1397
|
-
if (!Array.isArray(value))
|
|
1398
|
-
throw new Error(`invalid readseek ${field} entry`);
|
|
1399
|
-
return value.map((item) => {
|
|
1400
|
-
if (!item || typeof item !== "object")
|
|
1401
|
-
throw new Error(`invalid readseek ${field} entry`);
|
|
1402
|
-
const e = item;
|
|
1403
|
-
return {
|
|
1404
|
-
line: requireNumber(e.line, `${field}.line`),
|
|
1405
|
-
start_column: requireNumber(e.start_column, `${field}.start_column`),
|
|
1406
|
-
end_column: requireNumber(e.end_column, `${field}.end_column`),
|
|
1407
|
-
start_byte: requireNumber(e.start_byte, `${field}.start_byte`),
|
|
1408
|
-
end_byte: requireNumber(e.end_byte, `${field}.end_byte`),
|
|
1409
|
-
occurrence: requireString(e.occurrence, `${field}.occurrence`),
|
|
1410
|
-
line_hash: requireString(e.line_hash, `${field}.line_hash`),
|
|
1411
|
-
text: requireString(e.text, `${field}.text`)
|
|
1412
|
-
};
|
|
1413
|
-
});
|
|
1414
|
-
}
|
|
1415
|
-
function parseRenameOutput(value) {
|
|
1416
|
-
if (!value || typeof value !== "object")
|
|
1417
|
-
throw new Error("invalid readseek rename output");
|
|
1418
|
-
const output = value;
|
|
1419
|
-
const others = output.others;
|
|
1420
|
-
if (others !== undefined && !Array.isArray(others))
|
|
1421
|
-
throw new Error("invalid readseek others");
|
|
1422
|
-
return {
|
|
1423
|
-
file: requireString(output.file, "file"),
|
|
1424
|
-
language: requireString(output.language, "language"),
|
|
1425
|
-
engine: optionalString(output.engine, "engine"),
|
|
1426
|
-
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1427
|
-
old_name: requireString(output.old_name, "old_name"),
|
|
1428
|
-
new_name: requireString(output.new_name, "new_name"),
|
|
1429
|
-
applied: requireBoolean(output.applied, "applied"),
|
|
1430
|
-
conflicts: parseRenameConflicts(output.conflicts, "conflicts"),
|
|
1431
|
-
edits: parseRenameEdits(output.edits, "edits"),
|
|
1432
|
-
others: others?.map((entry) => {
|
|
1433
|
-
if (!entry || typeof entry !== "object")
|
|
1434
|
-
throw new Error("invalid readseek other");
|
|
1435
|
-
const o = entry;
|
|
1436
|
-
return {
|
|
1437
|
-
file: requireString(o.file, "other.file"),
|
|
1438
|
-
language: requireString(o.language, "other.language"),
|
|
1439
|
-
engine: optionalString(o.engine, "other.engine"),
|
|
1440
|
-
file_hash: requireString(o.file_hash, "other.file_hash"),
|
|
1441
|
-
conflicts: parseRenameConflicts(o.conflicts, "other.conflicts"),
|
|
1442
|
-
edits: parseRenameEdits(o.edits, "other.edits")
|
|
1443
|
-
};
|
|
1444
|
-
}) ?? []
|
|
1445
|
-
};
|
|
1446
|
-
}
|
|
1447
|
-
function parseDefCompact(value) {
|
|
1448
|
-
if (!value || typeof value !== "object")
|
|
1449
|
-
throw new Error("invalid readseek def output");
|
|
1450
|
-
const output = value;
|
|
1451
|
-
const locations = output.locations;
|
|
1452
|
-
if (!Array.isArray(locations))
|
|
1453
|
-
throw new Error("invalid readseek def locations");
|
|
1454
|
-
return locations.map((loc) => {
|
|
1455
|
-
if (!loc || typeof loc !== "object")
|
|
1456
|
-
throw new Error("invalid readseek def location");
|
|
1457
|
-
const item = loc;
|
|
1458
|
-
return {
|
|
1459
|
-
file: requireString(item.file, "location.file"),
|
|
1460
|
-
line: requireNumber(item.line, "location.line"),
|
|
1461
|
-
column: requireNumber(item.column, "location.column"),
|
|
1462
|
-
line_hash: requireString(item.line_hash, "location.line_hash"),
|
|
1463
|
-
text: requireString(item.text, "location.text"),
|
|
1464
|
-
kind: optionalString(item.kind, "location.kind"),
|
|
1465
|
-
name: optionalString(item.name, "location.name"),
|
|
1466
|
-
qualified_name: optionalString(item.qualified_name, "location.qualified_name")
|
|
1467
|
-
};
|
|
1468
|
-
});
|
|
1469
|
-
}
|
|
1470
1739
|
function createReadSeekClient(runner) {
|
|
1471
|
-
function
|
|
1472
|
-
return
|
|
1740
|
+
function inspectArgs(target, options) {
|
|
1741
|
+
return buildInspectArgs({
|
|
1473
1742
|
path: target,
|
|
1743
|
+
selector: options.selector,
|
|
1474
1744
|
select: options.select ?? "content",
|
|
1475
|
-
|
|
1745
|
+
query: options.query,
|
|
1476
1746
|
end: options.end,
|
|
1477
1747
|
limit: options.limit,
|
|
1478
1748
|
depth: options.depth,
|
|
1479
1749
|
language: options.language,
|
|
1480
1750
|
visionMode: options.visionMode,
|
|
1481
1751
|
visionLevel: options.visionLevel,
|
|
1482
|
-
stdinName: options.
|
|
1752
|
+
stdinName: options.stdinName
|
|
1483
1753
|
});
|
|
1484
1754
|
}
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
const runOptions = {
|
|
1755
|
+
function inspectRunOptions(options) {
|
|
1756
|
+
return {
|
|
1488
1757
|
signal: options.signal,
|
|
1489
1758
|
stdin: options.stdin,
|
|
1490
1759
|
timeoutMs: options.timeoutMs,
|
|
1491
1760
|
vision: options.vision
|
|
1492
1761
|
};
|
|
1493
|
-
return runner.json(args, runOptions);
|
|
1494
1762
|
}
|
|
1495
|
-
async function
|
|
1496
|
-
return
|
|
1763
|
+
async function inspect(target, options = {}) {
|
|
1764
|
+
return runner.kdl(inspectArgs(target, options), inspectRunOptions(options));
|
|
1765
|
+
}
|
|
1766
|
+
async function inspectText(target, options = {}) {
|
|
1767
|
+
return runner.text(inspectArgs(target, options), inspectRunOptions(options));
|
|
1768
|
+
}
|
|
1769
|
+
async function inspectSource(target, options = {}) {
|
|
1770
|
+
return parseSourceInspectEnvelope(await inspect(target, options));
|
|
1497
1771
|
}
|
|
1498
|
-
function
|
|
1772
|
+
function requireInspectMetadata(envelope, field) {
|
|
1773
|
+
if (envelope.metadata === undefined)
|
|
1774
|
+
throw new Error(`invalid readseek ${field}: missing metadata`);
|
|
1775
|
+
return envelope.metadata;
|
|
1776
|
+
}
|
|
1777
|
+
function requireInspectContent(envelope, field) {
|
|
1499
1778
|
if (envelope.content === undefined)
|
|
1500
1779
|
throw new Error(`invalid readseek ${field}: missing content`);
|
|
1501
1780
|
return envelope.content;
|
|
1502
1781
|
}
|
|
1503
|
-
async function
|
|
1504
|
-
const
|
|
1505
|
-
select: "content",
|
|
1506
|
-
|
|
1782
|
+
async function inspectContent(filePath, options = {}) {
|
|
1783
|
+
const inspected = await inspect(filePath, {
|
|
1784
|
+
select: ["metadata", "content"],
|
|
1785
|
+
selector: options.startLine !== undefined ? buildLineLocation(options.startLine) : undefined,
|
|
1507
1786
|
end: options.endLine,
|
|
1508
1787
|
visionMode: options.visionMode,
|
|
1509
1788
|
signal: options.signal,
|
|
1510
1789
|
vision: options.vision
|
|
1511
1790
|
});
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
const
|
|
1516
|
-
|
|
1517
|
-
|
|
1791
|
+
const pdf = parsePdfInspectMetadata(inspected);
|
|
1792
|
+
if (pdf)
|
|
1793
|
+
return { metadata: pdf };
|
|
1794
|
+
const envelope = parseSourceInspectEnvelope(inspected);
|
|
1795
|
+
const metadata = requireInspectMetadata(envelope, "content");
|
|
1796
|
+
const content = requireInspectContent(envelope, "content");
|
|
1797
|
+
if (metadata.kind === "image") {
|
|
1798
|
+
return { metadata, image: parseImageContent(content) };
|
|
1518
1799
|
}
|
|
1519
|
-
return { metadata
|
|
1800
|
+
return { metadata, text: parseTextContent(content) };
|
|
1520
1801
|
}
|
|
1521
1802
|
return {
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
});
|
|
1533
|
-
return runner.text(args, {
|
|
1803
|
+
inspect,
|
|
1804
|
+
inspectText,
|
|
1805
|
+
inspectContent,
|
|
1806
|
+
async extract(target, selector, output, options = {}) {
|
|
1807
|
+
await runner.text(buildExtractArgs({
|
|
1808
|
+
path: target,
|
|
1809
|
+
selector,
|
|
1810
|
+
output,
|
|
1811
|
+
stdinName: options.stdinName
|
|
1812
|
+
}), {
|
|
1534
1813
|
signal: options.signal,
|
|
1535
|
-
|
|
1814
|
+
stdin: options.stdin,
|
|
1815
|
+
timeoutMs: options.timeoutMs,
|
|
1816
|
+
cancelable: options.cancelable
|
|
1536
1817
|
});
|
|
1537
1818
|
},
|
|
1538
|
-
digestRaw(target, options = {}) {
|
|
1539
|
-
return runDigestRaw(target, options);
|
|
1540
|
-
},
|
|
1541
|
-
digest,
|
|
1542
|
-
digestContent,
|
|
1543
1819
|
async image(filePath, modes, options = {}) {
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
}));
|
|
1555
|
-
let merged;
|
|
1556
|
-
for (const result of results) {
|
|
1557
|
-
if (result.status !== "fulfilled")
|
|
1558
|
-
continue;
|
|
1559
|
-
const detection = result.value;
|
|
1560
|
-
merged = merged === undefined ? detection : {
|
|
1561
|
-
...merged,
|
|
1562
|
-
ocr: detection.ocr ?? merged.ocr,
|
|
1563
|
-
caption: detection.caption ?? merged.caption,
|
|
1564
|
-
objects: detection.objects ?? merged.objects
|
|
1565
|
-
};
|
|
1566
|
-
}
|
|
1567
|
-
if (merged !== undefined)
|
|
1568
|
-
return merged;
|
|
1569
|
-
const failure = results.find((result) => result.status === "rejected");
|
|
1570
|
-
if (failure?.status === "rejected")
|
|
1571
|
-
throw failure.reason;
|
|
1572
|
-
throw new Error(`readseek returned no image analysis for ${filePath}`);
|
|
1820
|
+
if (modes.length === 0)
|
|
1821
|
+
throw new Error(`readseek returned no image analysis for ${filePath}`);
|
|
1822
|
+
const inspected = await inspectContent(filePath, {
|
|
1823
|
+
visionMode: modes.length > 1 ? "all" : modes[0],
|
|
1824
|
+
signal: options.signal,
|
|
1825
|
+
vision: true
|
|
1826
|
+
});
|
|
1827
|
+
if (!inspected.image)
|
|
1828
|
+
throw new Error(`readseek returned no image analysis for ${filePath}`);
|
|
1829
|
+
return inspected.image;
|
|
1573
1830
|
},
|
|
1574
1831
|
async preparedImage(filePath, options = {}) {
|
|
1575
|
-
const
|
|
1832
|
+
const inspected = await inspectContent(filePath, {
|
|
1576
1833
|
visionMode: "none",
|
|
1577
1834
|
signal: options.signal
|
|
1578
1835
|
});
|
|
1579
|
-
const output =
|
|
1836
|
+
const output = inspected.image;
|
|
1580
1837
|
if (!output || output.encoding !== "base64" || output.data === undefined || output.mime === undefined) {
|
|
1581
1838
|
throw new Error(`readseek returned no prepared image for ${filePath}`);
|
|
1582
1839
|
}
|
|
@@ -1588,11 +1845,9 @@ function createReadSeekClient(runner) {
|
|
|
1588
1845
|
pattern,
|
|
1589
1846
|
language: options.language,
|
|
1590
1847
|
limit: options.limit,
|
|
1591
|
-
|
|
1592
|
-
others: options.others,
|
|
1593
|
-
ignored: options.ignored
|
|
1848
|
+
gitScope: options.gitScope
|
|
1594
1849
|
});
|
|
1595
|
-
return parseSearchOutput(await runner.
|
|
1850
|
+
return parseSearchOutput(await runner.kdl(args, { signal: options.signal }));
|
|
1596
1851
|
},
|
|
1597
1852
|
async refs(target, options = {}) {
|
|
1598
1853
|
const args = buildRefsArgs({
|
|
@@ -1602,26 +1857,21 @@ function createReadSeekClient(runner) {
|
|
|
1602
1857
|
line: options.line,
|
|
1603
1858
|
column: options.column,
|
|
1604
1859
|
language: options.language,
|
|
1605
|
-
|
|
1606
|
-
others: options.others,
|
|
1607
|
-
ignored: options.ignored
|
|
1860
|
+
gitScope: options.gitScope
|
|
1608
1861
|
});
|
|
1609
|
-
return parseRefsOutput(await runner.
|
|
1862
|
+
return parseRefsOutput(await runner.kdl(args, { signal: options.signal }));
|
|
1610
1863
|
},
|
|
1611
1864
|
async def(target, options) {
|
|
1612
1865
|
const args = buildDefArgs({
|
|
1613
1866
|
path: target,
|
|
1614
1867
|
name: options.name,
|
|
1615
|
-
format: "plain",
|
|
1616
1868
|
language: options.language,
|
|
1617
|
-
|
|
1618
|
-
others: options.others,
|
|
1619
|
-
ignored: options.ignored
|
|
1869
|
+
gitScope: options.gitScope
|
|
1620
1870
|
});
|
|
1621
|
-
return
|
|
1871
|
+
return parseDefOutput(await runner.kdl(args, { signal: options.signal }));
|
|
1622
1872
|
},
|
|
1623
1873
|
async check(filePath, content, options = {}) {
|
|
1624
|
-
const envelope = await
|
|
1874
|
+
const envelope = await inspectSource("-", {
|
|
1625
1875
|
select: "diagnostics",
|
|
1626
1876
|
stdin: content,
|
|
1627
1877
|
stdinName: filePath,
|
|
@@ -1631,12 +1881,12 @@ function createReadSeekClient(runner) {
|
|
|
1631
1881
|
throw new Error("invalid readseek diagnostics: missing diagnostics facet");
|
|
1632
1882
|
return envelope.diagnostics;
|
|
1633
1883
|
},
|
|
1634
|
-
async identify(filePath, content, options
|
|
1635
|
-
const envelope = await
|
|
1884
|
+
async identify(filePath, content, options) {
|
|
1885
|
+
const envelope = await inspectSource("-", {
|
|
1636
1886
|
select: "identity",
|
|
1637
1887
|
stdin: content,
|
|
1638
1888
|
stdinName: filePath,
|
|
1639
|
-
|
|
1889
|
+
selector: buildLineLocation(options.line, options.column),
|
|
1640
1890
|
language: options.language,
|
|
1641
1891
|
signal: options.signal
|
|
1642
1892
|
});
|
|
@@ -1645,8 +1895,11 @@ function createReadSeekClient(runner) {
|
|
|
1645
1895
|
return envelope.identity;
|
|
1646
1896
|
},
|
|
1647
1897
|
async detect(filePath, options = {}) {
|
|
1648
|
-
const
|
|
1649
|
-
|
|
1898
|
+
const inspected = await inspect(filePath, { select: "metadata", signal: options.signal });
|
|
1899
|
+
const pdf = parsePdfInspectMetadata(inspected);
|
|
1900
|
+
if (pdf)
|
|
1901
|
+
return pdf;
|
|
1902
|
+
return requireInspectMetadata(parseSourceInspectEnvelope(inspected), "metadata");
|
|
1650
1903
|
},
|
|
1651
1904
|
async edit(filePath, edits, options = {}) {
|
|
1652
1905
|
const args = buildEditArgs({
|
|
@@ -1656,9 +1909,9 @@ function createReadSeekClient(runner) {
|
|
|
1656
1909
|
language: options.language,
|
|
1657
1910
|
requestFromStdin: true
|
|
1658
1911
|
});
|
|
1659
|
-
return parseEditOutput(await runner.
|
|
1912
|
+
return parseEditOutput(await runner.kdl(args, {
|
|
1660
1913
|
signal: options.signal,
|
|
1661
|
-
stdin:
|
|
1914
|
+
stdin: serializeEditPlan(edits),
|
|
1662
1915
|
timeoutMs: options.timeoutMs,
|
|
1663
1916
|
cancelable: options.cancelable
|
|
1664
1917
|
}));
|
|
@@ -1671,12 +1924,11 @@ function createReadSeekClient(runner) {
|
|
|
1671
1924
|
column: options.column,
|
|
1672
1925
|
workspace: options.workspace,
|
|
1673
1926
|
apply: options.apply,
|
|
1927
|
+
planHash: options.planHash,
|
|
1674
1928
|
language: options.language,
|
|
1675
|
-
|
|
1676
|
-
others: options.others,
|
|
1677
|
-
ignored: options.ignored
|
|
1929
|
+
gitScope: options.gitScope
|
|
1678
1930
|
});
|
|
1679
|
-
return parseRenameOutput(await runner.
|
|
1931
|
+
return parseRenameOutput(await runner.kdl(args, { signal: options.signal }));
|
|
1680
1932
|
}
|
|
1681
1933
|
};
|
|
1682
1934
|
}
|
|
@@ -2157,7 +2409,7 @@ function formatReplaceHint(edits, noopEdits) {
|
|
|
2157
2409
|
return;
|
|
2158
2410
|
if (counts.replace !== counts.total)
|
|
2159
2411
|
return;
|
|
2160
|
-
return "[info: this edit used replace (unverified). For safer future edits, prefer set_line/replace_lines with an anchor from
|
|
2412
|
+
return "[info: this edit used replace (unverified). For safer future edits, prefer set_line/replace_lines with an anchor from readSeek_inspect/readSeek_grep/readSeek_search.]";
|
|
2161
2413
|
}
|
|
2162
2414
|
function buildEditOutput(input) {
|
|
2163
2415
|
const summary = `Updated ${input.displayPath}`;
|
|
@@ -2204,20 +2456,20 @@ function classifyEdit(oldContent, newContent) {
|
|
|
2204
2456
|
const newLines = newContent.split(`
|
|
2205
2457
|
`);
|
|
2206
2458
|
if (oldLines.length === newLines.length) {
|
|
2207
|
-
let
|
|
2208
|
-
let
|
|
2459
|
+
let hasWhitespaceChange = false;
|
|
2460
|
+
let hasSemanticChange = false;
|
|
2209
2461
|
for (let i = 0;i < oldLines.length; i++) {
|
|
2210
2462
|
if (oldLines[i] === newLines[i])
|
|
2211
2463
|
continue;
|
|
2212
2464
|
if (oldLines[i].trim() === newLines[i].trim()) {
|
|
2213
|
-
|
|
2465
|
+
hasWhitespaceChange = true;
|
|
2214
2466
|
} else {
|
|
2215
|
-
|
|
2467
|
+
hasSemanticChange = true;
|
|
2216
2468
|
}
|
|
2217
2469
|
}
|
|
2218
|
-
if (
|
|
2470
|
+
if (hasSemanticChange && hasWhitespaceChange)
|
|
2219
2471
|
return { classification: "mixed" };
|
|
2220
|
-
if (
|
|
2472
|
+
if (hasWhitespaceChange)
|
|
2221
2473
|
return { classification: "whitespace-only" };
|
|
2222
2474
|
return { classification: "semantic" };
|
|
2223
2475
|
}
|
|
@@ -2257,7 +2509,7 @@ import { readFileSync, statSync as statSync2 } from "node:fs";
|
|
|
2257
2509
|
import { homedir } from "node:os";
|
|
2258
2510
|
import { join } from "node:path";
|
|
2259
2511
|
var READSEEK_TOOL_REPLACEMENTS = {
|
|
2260
|
-
read: "
|
|
2512
|
+
read: "readSeek_inspect",
|
|
2261
2513
|
edit: "readSeek_edit",
|
|
2262
2514
|
grep: "readSeek_grep",
|
|
2263
2515
|
write: "readSeek_write"
|
|
@@ -2270,42 +2522,36 @@ var DEFAULT_DISPLAY_MODES = {
|
|
|
2270
2522
|
edit: "expanded",
|
|
2271
2523
|
write: "expanded"
|
|
2272
2524
|
};
|
|
2273
|
-
function globalSettingsPath() {
|
|
2274
|
-
return join(homedir(), ".pi/agent/settings.json");
|
|
2275
|
-
}
|
|
2276
|
-
function projectSettingsPath() {
|
|
2277
|
-
return join(process.cwd(), ".pi/settings.json");
|
|
2278
|
-
}
|
|
2279
2525
|
function isRecord(value) {
|
|
2280
2526
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2281
2527
|
}
|
|
2282
|
-
function invalid(source,
|
|
2283
|
-
return { source, path
|
|
2528
|
+
function invalid(source, path) {
|
|
2529
|
+
return { source, path, message: `Invalid readseek setting at ${path}` };
|
|
2284
2530
|
}
|
|
2285
2531
|
function warnUnknownKeys(raw, known, prefix, source, warnings) {
|
|
2286
2532
|
for (const key of Object.keys(raw)) {
|
|
2287
2533
|
if (known.includes(key))
|
|
2288
2534
|
continue;
|
|
2289
|
-
const
|
|
2290
|
-
warnings.push({ source, path
|
|
2535
|
+
const path = `${prefix}.${key}`;
|
|
2536
|
+
warnings.push({ source, path, message: `Unknown readseek setting at ${path}` });
|
|
2291
2537
|
}
|
|
2292
2538
|
}
|
|
2293
|
-
function readPositive(raw, key,
|
|
2539
|
+
function readPositive(raw, key, path, source, warnings) {
|
|
2294
2540
|
if (!(key in raw))
|
|
2295
2541
|
return;
|
|
2296
2542
|
const val = raw[key];
|
|
2297
2543
|
if (typeof val === "number" && Number.isSafeInteger(val) && val > 0)
|
|
2298
2544
|
return val;
|
|
2299
|
-
warnings.push(invalid(source,
|
|
2545
|
+
warnings.push(invalid(source, path));
|
|
2300
2546
|
return;
|
|
2301
2547
|
}
|
|
2302
|
-
function readEnum(raw, key, values,
|
|
2548
|
+
function readEnum(raw, key, values, path, source, warnings) {
|
|
2303
2549
|
if (!(key in raw))
|
|
2304
2550
|
return;
|
|
2305
2551
|
const val = raw[key];
|
|
2306
2552
|
if (typeof val === "string" && values.includes(val))
|
|
2307
2553
|
return val;
|
|
2308
|
-
warnings.push(invalid(source,
|
|
2554
|
+
warnings.push(invalid(source, path));
|
|
2309
2555
|
return;
|
|
2310
2556
|
}
|
|
2311
2557
|
function readImageMode(raw, source, warnings) {
|
|
@@ -2409,31 +2655,31 @@ function validateSettings(raw, source) {
|
|
|
2409
2655
|
return { settings, warnings };
|
|
2410
2656
|
}
|
|
2411
2657
|
var settingsFileCache = new Map;
|
|
2412
|
-
function statFileOrNull(
|
|
2658
|
+
function statFileOrNull(path) {
|
|
2413
2659
|
try {
|
|
2414
|
-
return statSync2(
|
|
2660
|
+
return statSync2(path);
|
|
2415
2661
|
} catch {
|
|
2416
2662
|
return null;
|
|
2417
2663
|
}
|
|
2418
2664
|
}
|
|
2419
|
-
function readSettingsFile(
|
|
2420
|
-
const stats = statFileOrNull(
|
|
2665
|
+
function readSettingsFile(path) {
|
|
2666
|
+
const stats = statFileOrNull(path);
|
|
2421
2667
|
if (!stats) {
|
|
2422
|
-
settingsFileCache.delete(
|
|
2668
|
+
settingsFileCache.delete(path);
|
|
2423
2669
|
return { settings: {}, warnings: [] };
|
|
2424
2670
|
}
|
|
2425
|
-
const cached = settingsFileCache.get(
|
|
2671
|
+
const cached = settingsFileCache.get(path);
|
|
2426
2672
|
if (cached && cached.mtimeMs === stats.mtimeMs && cached.size === stats.size)
|
|
2427
2673
|
return cached.result;
|
|
2428
2674
|
let result;
|
|
2429
2675
|
try {
|
|
2430
|
-
const text = readFileSync(
|
|
2431
|
-
result = validateSettings(JSON.parse(text),
|
|
2676
|
+
const text = readFileSync(path, "utf8");
|
|
2677
|
+
result = validateSettings(JSON.parse(text), path);
|
|
2432
2678
|
} catch (error) {
|
|
2433
2679
|
const message = error instanceof Error ? error.message : String(error);
|
|
2434
|
-
result = { settings: {}, warnings: [{ source:
|
|
2680
|
+
result = { settings: {}, warnings: [{ source: path, message: `Invalid JSON: ${message}` }] };
|
|
2435
2681
|
}
|
|
2436
|
-
settingsFileCache.set(
|
|
2682
|
+
settingsFileCache.set(path, { mtimeMs: stats.mtimeMs, size: stats.size, result });
|
|
2437
2683
|
return result;
|
|
2438
2684
|
}
|
|
2439
2685
|
function mergeSettings(base, override) {
|
|
@@ -2447,8 +2693,8 @@ function mergeSettings(base, override) {
|
|
|
2447
2693
|
return settings;
|
|
2448
2694
|
}
|
|
2449
2695
|
function resolveReadSeekJsonSettings() {
|
|
2450
|
-
const globalResult = readSettingsFile(
|
|
2451
|
-
const projectResult = readSettingsFile(
|
|
2696
|
+
const globalResult = readSettingsFile(join(homedir(), ".pi/agent/settings.json"));
|
|
2697
|
+
const projectResult = readSettingsFile(join(process.cwd(), ".pi/settings.json"));
|
|
2452
2698
|
return {
|
|
2453
2699
|
settings: mergeSettings(globalResult.settings, projectResult.settings),
|
|
2454
2700
|
warnings: [...globalResult.warnings, ...projectResult.warnings]
|
|
@@ -2496,21 +2742,13 @@ var DEFAULT_READSEEK_VISION_TIMEOUT_MS = 30 * 60000;
|
|
|
2496
2742
|
function readSeekTimeoutMs() {
|
|
2497
2743
|
return resolveReadSeekTimeoutMs() ?? DEFAULT_READSEEK_TIMEOUT_MS;
|
|
2498
2744
|
}
|
|
2499
|
-
async function spawnReadSeekRaw(args, options = {}) {
|
|
2500
|
-
return runReadSeekNodeText(readSeekBinaryPath(), args, {
|
|
2501
|
-
signal: options.signal,
|
|
2502
|
-
stdin: options.stdin,
|
|
2503
|
-
timeoutMs: options.timeoutMs ?? readSeekTimeoutMs(),
|
|
2504
|
-
cancelable: options.cancelable
|
|
2505
|
-
});
|
|
2506
|
-
}
|
|
2507
2745
|
async function ensureDefaultReadSeekDir() {
|
|
2508
2746
|
const dir = defaultReadSeekDir();
|
|
2509
2747
|
if (!dir)
|
|
2510
2748
|
return null;
|
|
2511
2749
|
if (directoryExists(dir))
|
|
2512
2750
|
return dir;
|
|
2513
|
-
defaultReadSeekDirInit ??=
|
|
2751
|
+
defaultReadSeekDirInit ??= runReadSeekNodeText(readSeekBinaryPath(), ["--readseek-dir", dir, "init"], { timeoutMs: readSeekTimeoutMs() }).then(() => directoryExists(dir) ? dir : null).catch(() => null).finally(() => {
|
|
2514
2752
|
defaultReadSeekDirInit = undefined;
|
|
2515
2753
|
});
|
|
2516
2754
|
return defaultReadSeekDirInit;
|
|
@@ -2532,17 +2770,6 @@ async function readSeekInvocationArgs(args) {
|
|
|
2532
2770
|
const readSeekDir = await ensureDefaultReadSeekDir();
|
|
2533
2771
|
return readSeekDir ? ["--readseek-dir", readSeekDir, ...args] : [...args];
|
|
2534
2772
|
}
|
|
2535
|
-
async function runReadSeekRaw(args, options = {}) {
|
|
2536
|
-
return spawnReadSeekRaw(await readSeekInvocationArgs(args), options);
|
|
2537
|
-
}
|
|
2538
|
-
async function runReadSeek(args, options = {}) {
|
|
2539
|
-
return runReadSeekNodeJson(readSeekBinaryPath(), await readSeekInvocationArgs(args), {
|
|
2540
|
-
signal: options.signal,
|
|
2541
|
-
stdin: options.stdin,
|
|
2542
|
-
timeoutMs: options.timeoutMs ?? readSeekTimeoutMs(),
|
|
2543
|
-
cancelable: options.cancelable
|
|
2544
|
-
});
|
|
2545
|
-
}
|
|
2546
2773
|
var visionInvocationTail = Promise.resolve();
|
|
2547
2774
|
async function runReadSeekVisionRaw(args, options = {}) {
|
|
2548
2775
|
let release;
|
|
@@ -2573,40 +2800,38 @@ async function runReadSeekVisionRaw(args, options = {}) {
|
|
|
2573
2800
|
await predecessor;
|
|
2574
2801
|
}
|
|
2575
2802
|
signal?.throwIfAborted();
|
|
2576
|
-
return await
|
|
2577
|
-
|
|
2578
|
-
|
|
2803
|
+
return await runReadSeekNodeText(readSeekBinaryPath(), await readSeekInvocationArgs(args), {
|
|
2804
|
+
signal: options.signal,
|
|
2805
|
+
stdin: options.stdin,
|
|
2806
|
+
timeoutMs: options.timeoutMs ?? resolveReadSeekTimeoutMs() ?? DEFAULT_READSEEK_VISION_TIMEOUT_MS,
|
|
2807
|
+
cancelable: options.cancelable
|
|
2579
2808
|
});
|
|
2580
2809
|
} finally {
|
|
2581
2810
|
release();
|
|
2582
2811
|
}
|
|
2583
2812
|
}
|
|
2584
|
-
async function runReadSeekVision(args, options = {}) {
|
|
2585
|
-
return parseReadSeekJson(await runReadSeekVisionRaw(args, options));
|
|
2586
|
-
}
|
|
2587
2813
|
var runner = {
|
|
2588
|
-
text: (args, options = {}) => options.vision ? runReadSeekVisionRaw(args, options) :
|
|
2589
|
-
|
|
2814
|
+
text: async (args, options = {}) => options.vision ? runReadSeekVisionRaw(args, options) : runReadSeekNodeText(readSeekBinaryPath(), await readSeekInvocationArgs(args), {
|
|
2815
|
+
signal: options.signal,
|
|
2816
|
+
stdin: options.stdin,
|
|
2817
|
+
timeoutMs: options.timeoutMs ?? readSeekTimeoutMs(),
|
|
2818
|
+
cancelable: options.cancelable
|
|
2819
|
+
}),
|
|
2820
|
+
kdl: async (args, options = {}) => parseReadSeekKdl(await runner.text(args, options))
|
|
2590
2821
|
};
|
|
2591
2822
|
var client = createReadSeekClient(runner);
|
|
2592
|
-
var
|
|
2593
|
-
var
|
|
2594
|
-
var readSeekDigest = client.digest;
|
|
2595
|
-
var readSeekDigestContent = client.digestContent;
|
|
2596
|
-
var readSeekImage = client.image;
|
|
2597
|
-
var readSeekPreparedImage = client.preparedImage;
|
|
2823
|
+
var readSeekInspect = client.inspect;
|
|
2824
|
+
var readSeekExtract = client.extract;
|
|
2598
2825
|
var readSeekSearch = client.search;
|
|
2599
2826
|
var readSeekRefs = client.refs;
|
|
2600
2827
|
var readSeekDef = client.def;
|
|
2601
2828
|
var readSeekCheck = client.check;
|
|
2602
|
-
var readSeekIdentify = client.identify;
|
|
2603
|
-
var readSeekDetect = client.detect;
|
|
2604
2829
|
var readSeekEdit = client.edit;
|
|
2605
2830
|
var readSeekRename = client.rename;
|
|
2606
2831
|
async function readSeekMap(filePath, options = {}) {
|
|
2607
|
-
const envelope = await client.
|
|
2832
|
+
const envelope = await client.inspect(filePath, {
|
|
2608
2833
|
select: "map",
|
|
2609
|
-
depth:
|
|
2834
|
+
depth: DEFAULT_INSPECT_MAP_DEPTH,
|
|
2610
2835
|
signal: options.signal
|
|
2611
2836
|
});
|
|
2612
2837
|
const map = envelope.map;
|
|
@@ -2672,8 +2897,8 @@ var LANGUAGE_BY_EXTENSION = new Map([
|
|
|
2672
2897
|
[".rs", "rust"],
|
|
2673
2898
|
[".java", "java"]
|
|
2674
2899
|
]);
|
|
2675
|
-
function inferLanguage(
|
|
2676
|
-
const extensionMatch =
|
|
2900
|
+
function inferLanguage(path) {
|
|
2901
|
+
const extensionMatch = path.match(/\.[^.\/\\]+$/);
|
|
2677
2902
|
if (!extensionMatch)
|
|
2678
2903
|
return;
|
|
2679
2904
|
return LANGUAGE_BY_EXTENSION.get(extensionMatch[0].toLowerCase());
|
|
@@ -2719,10 +2944,10 @@ function parseCompactDiffEntries(diff, oldContent, newContent) {
|
|
|
2719
2944
|
`);
|
|
2720
2945
|
const compactDelete = diff.match(/^(\d+):[0-9a-f]{6}\|(.*) → \[deleted\]$/);
|
|
2721
2946
|
if (compactDelete) {
|
|
2722
|
-
const
|
|
2947
|
+
const oldLine = Number(compactDelete[1]);
|
|
2723
2948
|
const text = compactDelete[2] ?? "";
|
|
2724
|
-
if (oldLines[
|
|
2725
|
-
return [{ kind: "remove", oldLine
|
|
2949
|
+
if (oldLines[oldLine - 1] === text)
|
|
2950
|
+
return [{ kind: "remove", oldLine, text }];
|
|
2726
2951
|
}
|
|
2727
2952
|
const compactPrefix = diff.match(/^(\d+):[0-9a-f]{6}\|/);
|
|
2728
2953
|
if (!compactPrefix)
|
|
@@ -2762,9 +2987,9 @@ function longestCommonSubsequence(a, b) {
|
|
|
2762
2987
|
const rows = a.length + 1;
|
|
2763
2988
|
const cols = b.length + 1;
|
|
2764
2989
|
const table = Array.from({ length: rows }, () => Array(cols).fill(0));
|
|
2765
|
-
for (let
|
|
2766
|
-
for (let
|
|
2767
|
-
table[
|
|
2990
|
+
for (let i = a.length - 1;i >= 0; i--) {
|
|
2991
|
+
for (let j = b.length - 1;j >= 0; j--) {
|
|
2992
|
+
table[i][j] = a[i] === b[j] ? table[i + 1][j + 1] + 1 : Math.max(table[i + 1][j], table[i][j + 1]);
|
|
2768
2993
|
}
|
|
2769
2994
|
}
|
|
2770
2995
|
const pairs = [];
|
|
@@ -2882,8 +3107,8 @@ function buildInlineDiffs(entries) {
|
|
|
2882
3107
|
}
|
|
2883
3108
|
return inlineDiffs.length ? inlineDiffs : undefined;
|
|
2884
3109
|
}
|
|
2885
|
-
function finalizeDiffData(
|
|
2886
|
-
const language = inferLanguage(
|
|
3110
|
+
function finalizeDiffData(path, entries, blockRanges) {
|
|
3111
|
+
const language = inferLanguage(path);
|
|
2887
3112
|
const inlineDiffs = buildInlineDiffs(entries);
|
|
2888
3113
|
return {
|
|
2889
3114
|
version: 1,
|
|
@@ -2976,7 +3201,6 @@ function wrapWithHangingIndent(prefix, content, width, options = {}) {
|
|
|
2976
3201
|
const indent = " ".repeat(prefixWidth);
|
|
2977
3202
|
return wrapped.map((line, index) => tint(clampLineToWidth(index === 0 ? prefix + line : indent + line, normalized)));
|
|
2978
3203
|
}
|
|
2979
|
-
var wrappedHashlinesCache = new WeakMap;
|
|
2980
3204
|
function renderPendingResult(pendingLabel, width, theme) {
|
|
2981
3205
|
return new Text(clampLinesToWidth([summaryLine(pendingLabel, { theme, style: "muted" })], width).join(`
|
|
2982
3206
|
`), 0, 0);
|
|
@@ -3107,8 +3331,8 @@ function padRightVisual(line, width) {
|
|
|
3107
3331
|
function tint(theme, entry, text) {
|
|
3108
3332
|
return entry.kind === "add" ? theme.fg("success", text) : entry.kind === "remove" ? theme.fg("error", text) : theme.fg("toolOutput", text);
|
|
3109
3333
|
}
|
|
3110
|
-
function spans(theme,
|
|
3111
|
-
return
|
|
3334
|
+
function spans(theme, spans, fallback) {
|
|
3335
|
+
return spans?.map((s) => s.kind === "add" ? theme.fg("success", s.text) : s.kind === "remove" ? theme.fg("error", s.text) : s.text).join("") ?? fallback;
|
|
3112
3336
|
}
|
|
3113
3337
|
function inlineText(input, index, entry) {
|
|
3114
3338
|
const pair = Array.isArray(input.diffData.inlineDiffs) ? input.diffData.inlineDiffs.find((d) => entry.kind === "remove" ? d.removeLineIndex === index : entry.kind === "add" ? d.addLineIndex === index : false) : undefined;
|
|
@@ -3237,12 +3461,12 @@ function upsertTextComponent(lastComponent, text) {
|
|
|
3237
3461
|
}
|
|
3238
3462
|
|
|
3239
3463
|
// src/register-tool.ts
|
|
3240
|
-
import { Type } from "@sinclair/typebox";
|
|
3464
|
+
import { Type as Type2 } from "@sinclair/typebox";
|
|
3241
3465
|
function optionalIntOrString(description) {
|
|
3242
|
-
return
|
|
3466
|
+
return Type2.Optional(Type2.Union([Type2.Number({ description }), Type2.String({ description })]));
|
|
3243
3467
|
}
|
|
3244
3468
|
function filePathParam() {
|
|
3245
|
-
return
|
|
3469
|
+
return Type2.String({ description: PARAM_DESCRIPTIONS.path });
|
|
3246
3470
|
}
|
|
3247
3471
|
function registerReadSeekTool(pi, tool) {
|
|
3248
3472
|
pi.registerTool(tool);
|
|
@@ -3250,28 +3474,28 @@ function registerReadSeekTool(pi, tool) {
|
|
|
3250
3474
|
}
|
|
3251
3475
|
|
|
3252
3476
|
// src/edit.ts
|
|
3253
|
-
var hashlineEditItemSchema =
|
|
3254
|
-
set_line:
|
|
3255
|
-
anchor:
|
|
3256
|
-
new_text:
|
|
3477
|
+
var hashlineEditItemSchema = Type3.Object({
|
|
3478
|
+
set_line: Type3.Optional(Type3.Object({
|
|
3479
|
+
anchor: Type3.String({ description: PARAM_DESCRIPTIONS.anchor }),
|
|
3480
|
+
new_text: Type3.String({ description: PARAM_DESCRIPTIONS.newTextLine })
|
|
3257
3481
|
}, { additionalProperties: false, description: PARAM_DESCRIPTIONS.setLine })),
|
|
3258
|
-
replace_lines:
|
|
3259
|
-
start_anchor:
|
|
3260
|
-
end_anchor:
|
|
3261
|
-
new_text:
|
|
3482
|
+
replace_lines: Type3.Optional(Type3.Object({
|
|
3483
|
+
start_anchor: Type3.String({ description: PARAM_DESCRIPTIONS.startAnchor }),
|
|
3484
|
+
end_anchor: Type3.String({ description: PARAM_DESCRIPTIONS.endAnchor }),
|
|
3485
|
+
new_text: Type3.String({ description: PARAM_DESCRIPTIONS.newTextRange })
|
|
3262
3486
|
}, { additionalProperties: false, description: PARAM_DESCRIPTIONS.replaceLines })),
|
|
3263
|
-
insert_after:
|
|
3264
|
-
anchor:
|
|
3265
|
-
new_text:
|
|
3487
|
+
insert_after: Type3.Optional(Type3.Object({
|
|
3488
|
+
anchor: Type3.String({ description: PARAM_DESCRIPTIONS.insertAnchor }),
|
|
3489
|
+
new_text: Type3.String({ description: PARAM_DESCRIPTIONS.newTextInsert })
|
|
3266
3490
|
}, { additionalProperties: false, description: PARAM_DESCRIPTIONS.insertAfter })),
|
|
3267
|
-
replace:
|
|
3268
|
-
old_text:
|
|
3269
|
-
new_text:
|
|
3270
|
-
all:
|
|
3491
|
+
replace: Type3.Optional(Type3.Object({
|
|
3492
|
+
old_text: Type3.String({ description: PARAM_DESCRIPTIONS.oldText }),
|
|
3493
|
+
new_text: Type3.String({ description: PARAM_DESCRIPTIONS.newText }),
|
|
3494
|
+
all: Type3.Optional(Type3.Boolean({ description: PARAM_DESCRIPTIONS.all }))
|
|
3271
3495
|
}, { additionalProperties: false, description: PARAM_DESCRIPTIONS.replace })),
|
|
3272
|
-
replace_symbol:
|
|
3273
|
-
symbol:
|
|
3274
|
-
new_body:
|
|
3496
|
+
replace_symbol: Type3.Optional(Type3.Object({
|
|
3497
|
+
symbol: Type3.String({ description: PARAM_DESCRIPTIONS.symbol }),
|
|
3498
|
+
new_body: Type3.String({ description: PARAM_DESCRIPTIONS.newBody })
|
|
3275
3499
|
}, { additionalProperties: false, description: PARAM_DESCRIPTIONS.replaceSymbol }))
|
|
3276
3500
|
}, {
|
|
3277
3501
|
additionalProperties: false,
|
|
@@ -3279,18 +3503,18 @@ var hashlineEditItemSchema = Type2.Object({
|
|
|
3279
3503
|
maxProperties: 1,
|
|
3280
3504
|
description: PARAM_DESCRIPTIONS.editVariant
|
|
3281
3505
|
});
|
|
3282
|
-
var hashlineEditSchema =
|
|
3506
|
+
var hashlineEditSchema = Type3.Object({
|
|
3283
3507
|
path: filePathParam(),
|
|
3284
|
-
language:
|
|
3285
|
-
edits:
|
|
3508
|
+
language: Type3.Optional(Type3.String({ description: PARAM_DESCRIPTIONS.languageSymbol })),
|
|
3509
|
+
edits: Type3.Array(hashlineEditItemSchema, {
|
|
3286
3510
|
minItems: 1,
|
|
3287
3511
|
description: PARAM_DESCRIPTIONS.edits
|
|
3288
3512
|
}),
|
|
3289
|
-
postEditVerify:
|
|
3513
|
+
postEditVerify: Type3.Optional(Type3.Boolean({
|
|
3290
3514
|
description: PARAM_DESCRIPTIONS.postEditVerify
|
|
3291
3515
|
}))
|
|
3292
3516
|
}, { additionalProperties: false });
|
|
3293
|
-
function buildEditError(
|
|
3517
|
+
function buildEditError(path, code, message, hint, errorDetails) {
|
|
3294
3518
|
return {
|
|
3295
3519
|
content: [{ type: "text", text: message }],
|
|
3296
3520
|
isError: true,
|
|
@@ -3301,7 +3525,7 @@ function buildEditError(path3, code, message, hint, errorDetails) {
|
|
|
3301
3525
|
readSeekValue: {
|
|
3302
3526
|
tool: "edit",
|
|
3303
3527
|
ok: false,
|
|
3304
|
-
path
|
|
3528
|
+
path,
|
|
3305
3529
|
error: buildReadSeekError(code, message, hint, errorDetails)
|
|
3306
3530
|
}
|
|
3307
3531
|
}
|
|
@@ -3315,14 +3539,14 @@ async function executeEdit(opts) {
|
|
|
3315
3539
|
const { params, signal, cwd, wasReadInSession, onFileMutated, syntaxValidate } = opts;
|
|
3316
3540
|
const parsed = params;
|
|
3317
3541
|
const rawPath = parsed.path;
|
|
3318
|
-
const
|
|
3319
|
-
const absolutePath = resolveToCwd2(
|
|
3542
|
+
const path = rawPath.replace(/^@/, "");
|
|
3543
|
+
const absolutePath = resolveToCwd2(path, cwd);
|
|
3320
3544
|
throwIfAborted(signal);
|
|
3321
3545
|
try {
|
|
3322
3546
|
return await withFileMutationQueue(absolutePath, async () => {
|
|
3323
3547
|
throwIfAborted(signal);
|
|
3324
3548
|
if (wasReadInSession && !wasReadInSession(absolutePath)) {
|
|
3325
|
-
const hint = `Call
|
|
3549
|
+
const hint = `Call readSeek_inspect(${JSON.stringify(rawPath)}) first, or use readSeek_grep, readSeek_search, or readSeek_write to produce fresh anchors for this file.`;
|
|
3326
3550
|
return buildEditError(absolutePath, "file-not-read", `You must get fresh anchors for ${absolutePath} before editing it. ${hint}`, hint);
|
|
3327
3551
|
}
|
|
3328
3552
|
const parsedEdits = validateExactlyOneEditVariant(parsed.edits ?? []);
|
|
@@ -3347,7 +3571,7 @@ async function executeEdit(opts) {
|
|
|
3347
3571
|
const originalForSyntax = normalizeToLF(stripBom(original).text);
|
|
3348
3572
|
const resultForSyntax = normalizeToLF(stripBom(result).text);
|
|
3349
3573
|
if (!plan.changed) {
|
|
3350
|
-
return buildEditError(absolutePath, "no-op", `No changes made to ${
|
|
3574
|
+
return buildEditError(absolutePath, "no-op", `No changes made to ${path}. The edits produced identical content.`);
|
|
3351
3575
|
}
|
|
3352
3576
|
const syntaxMode = resolveSyntaxValidateMode({ syntaxValidate });
|
|
3353
3577
|
let syntaxWarning;
|
|
@@ -3388,14 +3612,14 @@ async function executeEdit(opts) {
|
|
|
3388
3612
|
try {
|
|
3389
3613
|
verifiedContent = await fsReadFile(absolutePath, "utf-8");
|
|
3390
3614
|
} catch (err) {
|
|
3391
|
-
return buildEditError(absolutePath, "post-edit-verification-read-failed", `Edit write completed but post-edit verification failed: could not read ${
|
|
3615
|
+
return buildEditError(absolutePath, "post-edit-verification-read-failed", `Edit write completed but post-edit verification failed: could not read ${path} after writing.`, undefined, { fsCode: err?.code, fsMessage: err?.message });
|
|
3392
3616
|
}
|
|
3393
3617
|
if (verifiedContent !== plan.content) {
|
|
3394
|
-
return buildEditError(absolutePath, "post-edit-verification-mismatch", `Edit write completed but post-edit verification did not confirm the intended content for ${
|
|
3618
|
+
return buildEditError(absolutePath, "post-edit-verification-mismatch", `Edit write completed but post-edit verification did not confirm the intended content for ${path}. Re-read the file before making follow-up edits.`, undefined, { expectedLength: plan.content.length, actualLength: verifiedContent.length });
|
|
3395
3619
|
}
|
|
3396
3620
|
}
|
|
3397
3621
|
const diffResult = generateCompactOrFullDiff(original, result);
|
|
3398
|
-
const patch = createPatch(
|
|
3622
|
+
const patch = createPatch(path, original, result);
|
|
3399
3623
|
const diffData = buildDiffData({
|
|
3400
3624
|
path: absolutePath,
|
|
3401
3625
|
oldContent: original,
|
|
@@ -3407,7 +3631,7 @@ async function executeEdit(opts) {
|
|
|
3407
3631
|
const semanticSummary = { classification: classification.classification };
|
|
3408
3632
|
const builtOutput = buildEditOutput({
|
|
3409
3633
|
path: absolutePath,
|
|
3410
|
-
displayPath:
|
|
3634
|
+
displayPath: path,
|
|
3411
3635
|
diff: diffResult.diff,
|
|
3412
3636
|
patch,
|
|
3413
3637
|
diffData,
|
|
@@ -3430,7 +3654,7 @@ async function executeEdit(opts) {
|
|
|
3430
3654
|
});
|
|
3431
3655
|
} catch (err) {
|
|
3432
3656
|
if (typeof err?.code === "string") {
|
|
3433
|
-
return mapEditFileError(err, absolutePath,
|
|
3657
|
+
return mapEditFileError(err, absolutePath, path, "read");
|
|
3434
3658
|
}
|
|
3435
3659
|
throw err;
|
|
3436
3660
|
}
|
|
@@ -3480,8 +3704,8 @@ function registerEditTool(pi, options = {}) {
|
|
|
3480
3704
|
text = clampLineToWidth(text, context.width);
|
|
3481
3705
|
return upsertTextComponent(context.lastComponent, text);
|
|
3482
3706
|
},
|
|
3483
|
-
renderResult(result,
|
|
3484
|
-
const { isPartial, isError, expanded, width, context } = resolveRenderResultContext(
|
|
3707
|
+
renderResult(result, options, theme, ...rest) {
|
|
3708
|
+
const { isPartial, isError, expanded, width, context } = resolveRenderResultContext(options, rest, resolveReadSeekToolDisplayMode("edit") === "expanded");
|
|
3485
3709
|
if (isPartial) {
|
|
3486
3710
|
return renderPendingResult("pending edit", width, theme);
|
|
3487
3711
|
}
|
|
@@ -3537,7 +3761,7 @@ ${theme.fg("error", info.errorText)}`;
|
|
|
3537
3761
|
import { readFile as fsReadFile2, stat as fsStat } from "node:fs/promises";
|
|
3538
3762
|
import path3 from "node:path";
|
|
3539
3763
|
import { createGrepTool } from "@earendil-works/pi-coding-agent";
|
|
3540
|
-
import { Type as
|
|
3764
|
+
import { Type as Type5 } from "@sinclair/typebox";
|
|
3541
3765
|
import { Text as Text4 } from "@earendil-works/pi-tui";
|
|
3542
3766
|
|
|
3543
3767
|
// src/binary-detect.ts
|
|
@@ -3609,14 +3833,14 @@ function buildScopeMetadata(groups, warnings) {
|
|
|
3609
3833
|
}
|
|
3610
3834
|
function buildGrepOutput(input) {
|
|
3611
3835
|
const fileCount = new Set(input.groups.map((group) => group.absolutePath)).size;
|
|
3612
|
-
const
|
|
3836
|
+
const header = `[${input.totalMatches} matches in ${fileCount} files]`;
|
|
3613
3837
|
let text;
|
|
3614
3838
|
if (input.summary) {
|
|
3615
3839
|
const fileLines = [...input.groups].sort((a, b) => b.matchCount - a.matchCount).map((group) => `${group.absolutePath}: ${group.matchCount} matches`);
|
|
3616
|
-
text = [
|
|
3840
|
+
text = [header, ...fileLines].join(`
|
|
3617
3841
|
`);
|
|
3618
3842
|
} else {
|
|
3619
|
-
const blocks = [
|
|
3843
|
+
const blocks = [header];
|
|
3620
3844
|
for (const group of input.groups) {
|
|
3621
3845
|
blocks.push(renderGroupHeader(group));
|
|
3622
3846
|
for (const entry of group.entries) {
|
|
@@ -3721,12 +3945,12 @@ function firstLineNumber(group) {
|
|
|
3721
3945
|
}
|
|
3722
3946
|
function buildSymbolEntries(fileLines, symbol, matchLines, scopeContext) {
|
|
3723
3947
|
if (scopeContext === undefined) {
|
|
3724
|
-
const
|
|
3948
|
+
const entries = [];
|
|
3725
3949
|
for (let lineNumber = symbol.startLine;lineNumber <= symbol.endLine; lineNumber++) {
|
|
3726
3950
|
const built = buildReadSeekLine(lineNumber, fileLines[lineNumber - 1] ?? "");
|
|
3727
|
-
|
|
3951
|
+
entries.push({ kind: matchLines.has(lineNumber) ? "match" : "context", line: built });
|
|
3728
3952
|
}
|
|
3729
|
-
return
|
|
3953
|
+
return entries;
|
|
3730
3954
|
}
|
|
3731
3955
|
const ranges = [...matchLines].sort((a, b) => a - b).map((ln) => ({
|
|
3732
3956
|
startLine: Math.max(symbol.startLine, ln - scopeContext),
|
|
@@ -3909,11 +4133,8 @@ import {
|
|
|
3909
4133
|
} from "@jarkkojs/readseek-api";
|
|
3910
4134
|
|
|
3911
4135
|
// src/tui-source-render.ts
|
|
3912
|
-
import { getLanguageFromPath, highlightCode } from "@earendil-works/pi-coding-agent";
|
|
3913
|
-
import { wrapTextWithAnsi as wrapTextWithAnsi2 } from "@earendil-works/pi-tui";
|
|
3914
4136
|
var GREP_ANCHOR_RE = /^(.*?):(>>| )(\d+):([0-9a-zA-Z]{1,16})\|(.*)$/;
|
|
3915
|
-
|
|
3916
|
-
function renderGrepSourceForDisplay(text, anchors, renderPath = (path3) => path3) {
|
|
4137
|
+
function renderGrepSourceForDisplay(text, anchors, renderPath = (path) => path) {
|
|
3917
4138
|
return text.split(`
|
|
3918
4139
|
`).map((line) => {
|
|
3919
4140
|
const match = line.match(GREP_ANCHOR_RE);
|
|
@@ -3929,39 +4150,30 @@ function renderGrepSourceForDisplay(text, anchors, renderPath = (path3) => path3
|
|
|
3929
4150
|
}
|
|
3930
4151
|
|
|
3931
4152
|
// src/readseek-params.ts
|
|
3932
|
-
import { Type as
|
|
4153
|
+
import { Type as Type4 } from "@sinclair/typebox";
|
|
3933
4154
|
function searchPathParam() {
|
|
3934
|
-
return
|
|
4155
|
+
return Type4.Optional(Type4.String({ description: PARAM_DESCRIPTIONS.searchPath }));
|
|
3935
4156
|
}
|
|
3936
4157
|
function languageParam() {
|
|
3937
|
-
return
|
|
4158
|
+
return Type4.Optional(Type4.String({ description: PARAM_DESCRIPTIONS.language }));
|
|
3938
4159
|
}
|
|
3939
4160
|
function readSeekGitSearchParams() {
|
|
3940
4161
|
return {
|
|
3941
|
-
|
|
3942
|
-
others: Type3.Optional(Type3.Boolean({ description: PARAM_DESCRIPTIONS.others })),
|
|
3943
|
-
ignored: Type3.Optional(Type3.Boolean({ description: PARAM_DESCRIPTIONS.ignored }))
|
|
4162
|
+
gitScope: Type4.Optional(Type4.Union(GIT_SCOPES.map((scope) => Type4.Literal(scope)), { description: PARAM_DESCRIPTIONS.gitScope }))
|
|
3944
4163
|
};
|
|
3945
4164
|
}
|
|
3946
|
-
function validateIgnoredRequiresOthers(tool, params) {
|
|
3947
|
-
const validated = validateGitSelection(params);
|
|
3948
|
-
if (!validated.ok) {
|
|
3949
|
-
return buildToolErrorResult(tool, "invalid-parameter", `${tool} parameter 'ignored' requires 'others'`);
|
|
3950
|
-
}
|
|
3951
|
-
return null;
|
|
3952
|
-
}
|
|
3953
4165
|
|
|
3954
4166
|
// src/grep.ts
|
|
3955
|
-
var grepSchema =
|
|
3956
|
-
pattern:
|
|
4167
|
+
var grepSchema = Type5.Object({
|
|
4168
|
+
pattern: Type5.String({ description: PARAM_DESCRIPTIONS.grepPattern }),
|
|
3957
4169
|
path: searchPathParam(),
|
|
3958
|
-
glob:
|
|
3959
|
-
ignoreCase:
|
|
3960
|
-
literal:
|
|
4170
|
+
glob: Type5.Optional(Type5.String({ description: PARAM_DESCRIPTIONS.glob })),
|
|
4171
|
+
ignoreCase: Type5.Optional(Type5.Boolean({ description: PARAM_DESCRIPTIONS.ignoreCase })),
|
|
4172
|
+
literal: Type5.Optional(Type5.Boolean({ description: PARAM_DESCRIPTIONS.literal })),
|
|
3961
4173
|
context: optionalIntOrString(PARAM_DESCRIPTIONS.context),
|
|
3962
4174
|
limit: optionalIntOrString(PARAM_DESCRIPTIONS.matchLimit),
|
|
3963
|
-
summary:
|
|
3964
|
-
scope:
|
|
4175
|
+
summary: Type5.Optional(Type5.Boolean({ description: PARAM_DESCRIPTIONS.summary })),
|
|
4176
|
+
scope: Type5.Optional(Type5.Literal("symbol", {
|
|
3965
4177
|
description: PARAM_DESCRIPTIONS.grepScope
|
|
3966
4178
|
})),
|
|
3967
4179
|
scopeContext: optionalIntOrString(PARAM_DESCRIPTIONS.scopeContext)
|
|
@@ -4033,12 +4245,6 @@ function truncateGroupEntries(group) {
|
|
|
4033
4245
|
}
|
|
4034
4246
|
return { ...group, entries: kept };
|
|
4035
4247
|
}
|
|
4036
|
-
function recordsFromGroups(groups) {
|
|
4037
|
-
return groups.flatMap((group) => group.entries.flatMap((entry) => entry.kind === "separator" ? [] : [{ path: group.absolutePath, ...entry.line, kind: entry.kind }]));
|
|
4038
|
-
}
|
|
4039
|
-
function escapeForRegex(s) {
|
|
4040
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4041
|
-
}
|
|
4042
4248
|
async function executeGrep(opts) {
|
|
4043
4249
|
const { toolCallId, params, signal, onUpdate, cwd, onFileAnchored } = opts;
|
|
4044
4250
|
await ensureHashInit();
|
|
@@ -4047,10 +4253,16 @@ async function executeGrep(opts) {
|
|
|
4047
4253
|
if (!context.ok) {
|
|
4048
4254
|
return buildToolErrorResult("grep", "invalid-params-combo", context.message);
|
|
4049
4255
|
}
|
|
4256
|
+
if (context.value !== undefined && context.value < 0) {
|
|
4257
|
+
return buildToolErrorResult("grep", "invalid-params-combo", `Invalid context: expected a non-negative integer, received ${context.value}.`);
|
|
4258
|
+
}
|
|
4050
4259
|
const limit = coerceObviousBase10Int2(rawParams.limit, "limit");
|
|
4051
4260
|
if (!limit.ok) {
|
|
4052
4261
|
return buildToolErrorResult("grep", "invalid-limit", limit.message);
|
|
4053
4262
|
}
|
|
4263
|
+
if (limit.value !== undefined && limit.value < 1) {
|
|
4264
|
+
return buildToolErrorResult("grep", "invalid-limit", `Invalid limit: expected a positive integer, received ${limit.value}.`);
|
|
4265
|
+
}
|
|
4054
4266
|
const scopeContext = coerceObviousBase10Int2(rawParams.scopeContext, "scopeContext");
|
|
4055
4267
|
if (!scopeContext.ok) {
|
|
4056
4268
|
return buildToolErrorResult("grep", "invalid-params-combo", scopeContext.message);
|
|
@@ -4155,7 +4367,7 @@ async function executeGrep(opts) {
|
|
|
4155
4367
|
const CONCURRENCY = 8;
|
|
4156
4368
|
const pathList = [...pathsToRead];
|
|
4157
4369
|
for (let i = 0;i < pathList.length; i += CONCURRENCY) {
|
|
4158
|
-
await Promise.all(pathList.slice(i, i + CONCURRENCY).map((
|
|
4370
|
+
await Promise.all(pathList.slice(i, i + CONCURRENCY).map((p) => getFileLines(p)));
|
|
4159
4371
|
}
|
|
4160
4372
|
}
|
|
4161
4373
|
const addSummaryMatch = (displayPath, absolutePath) => {
|
|
@@ -4208,7 +4420,7 @@ async function executeGrep(opts) {
|
|
|
4208
4420
|
const flags = p.ignoreCase ? "i" : "";
|
|
4209
4421
|
let patternRe = null;
|
|
4210
4422
|
try {
|
|
4211
|
-
patternRe = p.literal ? new RegExp(
|
|
4423
|
+
patternRe = p.literal ? new RegExp(p.pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), flags) : new RegExp(p.pattern, flags);
|
|
4212
4424
|
} catch {}
|
|
4213
4425
|
if (patternRe !== null) {
|
|
4214
4426
|
let emitted = false;
|
|
@@ -4292,7 +4504,7 @@ ${warning}` } : item),
|
|
|
4292
4504
|
renderedGroups = scoped.groups;
|
|
4293
4505
|
scopeWarnings = scoped.warnings;
|
|
4294
4506
|
}
|
|
4295
|
-
const readSeekRecords =
|
|
4507
|
+
const readSeekRecords = renderedGroups.flatMap((group) => group.entries.flatMap((entry) => entry.kind === "separator" ? [] : [{ path: group.absolutePath, ...entry.line, kind: entry.kind }]));
|
|
4296
4508
|
const builtOutput = buildGrepOutput({
|
|
4297
4509
|
summary: !!summary,
|
|
4298
4510
|
totalMatches,
|
|
@@ -4363,8 +4575,8 @@ function registerGrepTool(pi, options = {}) {
|
|
|
4363
4575
|
}
|
|
4364
4576
|
return new Text4(clampLineToWidth(text, context.width), 0, 0);
|
|
4365
4577
|
},
|
|
4366
|
-
renderResult(result,
|
|
4367
|
-
const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(
|
|
4578
|
+
renderResult(result, options, theme, ...rest) {
|
|
4579
|
+
const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(options, rest, resolveReadSeekToolDisplayMode("grep") === "expanded");
|
|
4368
4580
|
if (isPartial)
|
|
4369
4581
|
return renderPendingResult("pending grep", width, theme);
|
|
4370
4582
|
const content = result.content?.[0];
|
|
@@ -4411,7 +4623,7 @@ function registerGrepTool(pi, options = {}) {
|
|
|
4411
4623
|
|
|
4412
4624
|
// src/search.ts
|
|
4413
4625
|
import path4 from "node:path";
|
|
4414
|
-
import { Type as
|
|
4626
|
+
import { Type as Type6 } from "@sinclair/typebox";
|
|
4415
4627
|
|
|
4416
4628
|
// src/stat-search-path.ts
|
|
4417
4629
|
import { stat as fsStat2 } from "node:fs/promises";
|
|
@@ -4419,9 +4631,9 @@ async function statSearchPathOrError(tool, rawPath, searchPath) {
|
|
|
4419
4631
|
try {
|
|
4420
4632
|
return { ok: true, stats: await fsStat2(searchPath) };
|
|
4421
4633
|
} catch (err) {
|
|
4422
|
-
const
|
|
4634
|
+
const path = rawPath ?? searchPath;
|
|
4423
4635
|
const { code, message } = formatFsError(err, "stat-error");
|
|
4424
|
-
return { ok: false, error: buildToolErrorResult(tool, code, message, { path
|
|
4636
|
+
return { ok: false, error: buildToolErrorResult(tool, code, message, { path }) };
|
|
4425
4637
|
}
|
|
4426
4638
|
}
|
|
4427
4639
|
|
|
@@ -4502,9 +4714,6 @@ function readSeekLanguageForPath(language, searchPath, isFile) {
|
|
|
4502
4714
|
async function executeSearch(opts) {
|
|
4503
4715
|
const { params, signal, cwd, onFileAnchored } = opts;
|
|
4504
4716
|
const p = params;
|
|
4505
|
-
const ignoredError = validateIgnoredRequiresOthers("search", p);
|
|
4506
|
-
if (ignoredError)
|
|
4507
|
-
return ignoredError;
|
|
4508
4717
|
const searchPath = resolveToCwd2(p.path ?? ".", cwd);
|
|
4509
4718
|
const statResult = await statSearchPathOrError("search", p.path, searchPath);
|
|
4510
4719
|
if (!statResult.ok)
|
|
@@ -4514,9 +4723,7 @@ async function executeSearch(opts) {
|
|
|
4514
4723
|
const effectiveLang = readSeekLanguageForPath(p.language, searchPath, searchPathIsFile);
|
|
4515
4724
|
const results = await readSeekSearch(searchPath, p.pattern, {
|
|
4516
4725
|
language: effectiveLang,
|
|
4517
|
-
|
|
4518
|
-
others: p.others,
|
|
4519
|
-
ignored: p.ignored,
|
|
4726
|
+
gitScope: p.gitScope,
|
|
4520
4727
|
signal
|
|
4521
4728
|
});
|
|
4522
4729
|
if (results.length === 0) {
|
|
@@ -4578,8 +4785,8 @@ function registerSearchTool(pi, options = {}) {
|
|
|
4578
4785
|
description: SEARCH_PROMPT_METADATA.description,
|
|
4579
4786
|
promptSnippet: SEARCH_PROMPT_METADATA.promptSnippet,
|
|
4580
4787
|
promptGuidelines: SEARCH_PROMPT_METADATA.promptGuidelines,
|
|
4581
|
-
parameters:
|
|
4582
|
-
pattern:
|
|
4788
|
+
parameters: Type6.Object({
|
|
4789
|
+
pattern: Type6.String({ description: PARAM_DESCRIPTIONS.pattern }),
|
|
4583
4790
|
language: languageParam(),
|
|
4584
4791
|
path: searchPathParam(),
|
|
4585
4792
|
...readSeekGitSearchParams()
|
|
@@ -4591,11 +4798,11 @@ function registerSearchTool(pi, options = {}) {
|
|
|
4591
4798
|
return renderReadSeekSearchCall(args, theme, rest, {
|
|
4592
4799
|
label: "search",
|
|
4593
4800
|
accent: `/${args.pattern}/`,
|
|
4594
|
-
flags: [args.
|
|
4801
|
+
flags: [args.gitScope && `git-scope=${args.gitScope}`]
|
|
4595
4802
|
});
|
|
4596
4803
|
},
|
|
4597
|
-
renderResult(result,
|
|
4598
|
-
return renderAnchoredFilesResult(result,
|
|
4804
|
+
renderResult(result, options, theme, ...rest) {
|
|
4805
|
+
return renderAnchoredFilesResult(result, options, theme, rest, {
|
|
4599
4806
|
pendingLabel: "pending search",
|
|
4600
4807
|
emptyLabel: "no matches",
|
|
4601
4808
|
unitSingular: "match",
|
|
@@ -4607,7 +4814,7 @@ function registerSearchTool(pi, options = {}) {
|
|
|
4607
4814
|
}
|
|
4608
4815
|
|
|
4609
4816
|
// src/refs.ts
|
|
4610
|
-
import { Type as
|
|
4817
|
+
import { Type as Type7 } from "@sinclair/typebox";
|
|
4611
4818
|
import path5 from "node:path";
|
|
4612
4819
|
|
|
4613
4820
|
// src/refs-output.ts
|
|
@@ -4673,9 +4880,6 @@ function isReadSeekCursorValidationFailure(message) {
|
|
|
4673
4880
|
async function executeRefs(opts) {
|
|
4674
4881
|
const { params, signal, cwd, onFileAnchored } = opts;
|
|
4675
4882
|
const p = params;
|
|
4676
|
-
const ignoredError = validateIgnoredRequiresOthers("refs", p);
|
|
4677
|
-
if (ignoredError)
|
|
4678
|
-
return ignoredError;
|
|
4679
4883
|
const scopeError = validateRefsNameVsScope(p);
|
|
4680
4884
|
if (!scopeError.ok) {
|
|
4681
4885
|
return buildToolErrorResult("refs", "invalid-parameter", `refs parameter: ${scopeError.error}`);
|
|
@@ -4691,9 +4895,7 @@ async function executeRefs(opts) {
|
|
|
4691
4895
|
line: p.line,
|
|
4692
4896
|
column: p.column,
|
|
4693
4897
|
language: p.language,
|
|
4694
|
-
|
|
4695
|
-
others: p.others,
|
|
4696
|
-
ignored: p.ignored,
|
|
4898
|
+
gitScope: p.gitScope,
|
|
4697
4899
|
signal
|
|
4698
4900
|
});
|
|
4699
4901
|
const files = groupReferences(references, cwd);
|
|
@@ -4726,13 +4928,13 @@ function registerRefsTool(pi, options = {}) {
|
|
|
4726
4928
|
description: REFS_PROMPT_METADATA.description,
|
|
4727
4929
|
promptSnippet: REFS_PROMPT_METADATA.promptSnippet,
|
|
4728
4930
|
promptGuidelines: REFS_PROMPT_METADATA.promptGuidelines,
|
|
4729
|
-
parameters:
|
|
4730
|
-
name:
|
|
4931
|
+
parameters: Type7.Object({
|
|
4932
|
+
name: Type7.Optional(Type7.String({ description: PARAM_DESCRIPTIONS.refsName })),
|
|
4731
4933
|
path: searchPathParam(),
|
|
4732
4934
|
language: languageParam(),
|
|
4733
|
-
scope:
|
|
4734
|
-
line:
|
|
4735
|
-
column:
|
|
4935
|
+
scope: Type7.Optional(Type7.Boolean({ description: PARAM_DESCRIPTIONS.scope })),
|
|
4936
|
+
line: Type7.Optional(Type7.Number({ description: PARAM_DESCRIPTIONS.line })),
|
|
4937
|
+
column: Type7.Optional(Type7.Number({ description: PARAM_DESCRIPTIONS.column })),
|
|
4736
4938
|
...readSeekGitSearchParams()
|
|
4737
4939
|
}),
|
|
4738
4940
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
@@ -4743,11 +4945,11 @@ function registerRefsTool(pi, options = {}) {
|
|
|
4743
4945
|
return renderReadSeekSearchCall(args, theme, rest, {
|
|
4744
4946
|
label: "refs",
|
|
4745
4947
|
accent,
|
|
4746
|
-
flags: [args.scope && "scope", args.
|
|
4948
|
+
flags: [args.scope && "scope", args.gitScope && `git-scope=${args.gitScope}`]
|
|
4747
4949
|
});
|
|
4748
4950
|
},
|
|
4749
|
-
renderResult(result,
|
|
4750
|
-
return renderAnchoredFilesResult(result,
|
|
4951
|
+
renderResult(result, options, theme, ...rest) {
|
|
4952
|
+
return renderAnchoredFilesResult(result, options, theme, rest, {
|
|
4751
4953
|
pendingLabel: "pending refs",
|
|
4752
4954
|
emptyLabel: "no references",
|
|
4753
4955
|
unitSingular: "reference",
|
|
@@ -4759,20 +4961,20 @@ function registerRefsTool(pi, options = {}) {
|
|
|
4759
4961
|
}
|
|
4760
4962
|
|
|
4761
4963
|
// src/rename.ts
|
|
4762
|
-
import { Type as
|
|
4964
|
+
import { Type as Type8 } from "@sinclair/typebox";
|
|
4763
4965
|
import path6 from "node:path";
|
|
4764
4966
|
import { Text as Text5 } from "@earendil-works/pi-tui";
|
|
4765
4967
|
var RENAME_PROMPT_METADATA = defineToolPromptMetadata({
|
|
4766
4968
|
promptUrl: new URL("../prompts/rename.md", import.meta.url),
|
|
4767
4969
|
promptSnippet: "Rename a symbol at a source location with verified edits"
|
|
4768
4970
|
});
|
|
4769
|
-
var renameSchema =
|
|
4971
|
+
var renameSchema = Type8.Object({
|
|
4770
4972
|
path: filePathParam(),
|
|
4771
|
-
line:
|
|
4772
|
-
column:
|
|
4773
|
-
to:
|
|
4774
|
-
workspace:
|
|
4775
|
-
apply:
|
|
4973
|
+
line: Type8.Integer({ minimum: 1, description: PARAM_DESCRIPTIONS.renameLine }),
|
|
4974
|
+
column: Type8.Optional(Type8.Integer({ minimum: 1, description: PARAM_DESCRIPTIONS.column })),
|
|
4975
|
+
to: Type8.String({ description: PARAM_DESCRIPTIONS.to }),
|
|
4976
|
+
workspace: Type8.Optional(Type8.Boolean({ description: PARAM_DESCRIPTIONS.workspace })),
|
|
4977
|
+
apply: Type8.Optional(Type8.Boolean({ description: PARAM_DESCRIPTIONS.applyDefaultTrue }))
|
|
4776
4978
|
});
|
|
4777
4979
|
async function executeRename(opts) {
|
|
4778
4980
|
const { params, signal, cwd, onFileMutated } = opts;
|
|
@@ -4804,7 +5006,8 @@ async function executeRename(opts) {
|
|
|
4804
5006
|
onFileMutated?.(absoluteFile);
|
|
4805
5007
|
}
|
|
4806
5008
|
}
|
|
4807
|
-
const
|
|
5009
|
+
const primaryAbs = path6.isAbsolute(output.file) ? output.file : path6.resolve(cwd, output.file);
|
|
5010
|
+
const files = [primaryAbs];
|
|
4808
5011
|
for (const other of output.others) {
|
|
4809
5012
|
const abs = path6.isAbsolute(other.file) ? other.file : path6.resolve(cwd, other.file);
|
|
4810
5013
|
if (!files.includes(abs))
|
|
@@ -4864,8 +5067,8 @@ function registerRenameTool(pi, options = {}) {
|
|
|
4864
5067
|
text += theme.fg("dim", ` → ${args.to}`);
|
|
4865
5068
|
return new Text5(clampLineToWidth(text, context.width), 0, 0);
|
|
4866
5069
|
},
|
|
4867
|
-
renderResult(result,
|
|
4868
|
-
const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(
|
|
5070
|
+
renderResult(result, options, theme, ...rest) {
|
|
5071
|
+
const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(options, rest);
|
|
4869
5072
|
if (isPartial)
|
|
4870
5073
|
return renderPendingResult("pending rename", width, theme);
|
|
4871
5074
|
const content = result.content?.[0];
|
|
@@ -4906,7 +5109,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
4906
5109
|
import { dirname as dirname2, relative as relative3 } from "node:path";
|
|
4907
5110
|
import { withFileMutationQueue as withFileMutationQueue2, truncateHead as truncateHead2 } from "@earendil-works/pi-coding-agent";
|
|
4908
5111
|
import { Text as Text6 } from "@earendil-works/pi-tui";
|
|
4909
|
-
import { Type as
|
|
5112
|
+
import { Type as Type9 } from "@sinclair/typebox";
|
|
4910
5113
|
|
|
4911
5114
|
// src/pending-diff-preview.ts
|
|
4912
5115
|
import { existsSync as existsSync3, readFileSync as readFileSync2, realpathSync, statSync as statSync4 } from "node:fs";
|
|
@@ -5053,9 +5256,9 @@ function pendingWritePreviewParts(summary, preview, expanded, theme) {
|
|
|
5053
5256
|
if (!preview || preview.type !== "ok")
|
|
5054
5257
|
return { lines: summary.split(`
|
|
5055
5258
|
`) };
|
|
5056
|
-
const
|
|
5259
|
+
const hasOldSide = preview.data.fileExistedBeforeWrite;
|
|
5057
5260
|
const headerLine = summaryLine(preview.data.headerLabel, { hidden: !expanded });
|
|
5058
|
-
if (!
|
|
5261
|
+
if (!hasOldSide) {
|
|
5059
5262
|
const lines = [summary, headerLine];
|
|
5060
5263
|
if (expanded)
|
|
5061
5264
|
lines.push(...formatContentPreviewLines(preview.data.nextContent, theme));
|
|
@@ -5111,7 +5314,7 @@ async function executeWrite(opts) {
|
|
|
5111
5314
|
const warnings = [];
|
|
5112
5315
|
const readSeekWarnings = [];
|
|
5113
5316
|
if (hasBareCarriageReturn(content)) {
|
|
5114
|
-
const message = "File content contains bare CR (\\r) line endings; readSeek_write refuses to emit anchors that
|
|
5317
|
+
const message = "File content contains bare CR (\\r) line endings; readSeek_write refuses to emit anchors that readSeek_inspect/readSeek_edit would normalize differently.";
|
|
5115
5318
|
warnings.push(message);
|
|
5116
5319
|
readSeekWarnings.push(buildReadSeekWarning("bare-cr", message));
|
|
5117
5320
|
return buildToolErrorResult("write", "bare-cr", `Cannot write ${filePath}
|
|
@@ -5202,9 +5405,9 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5202
5405
|
description: promptMetadata.description,
|
|
5203
5406
|
promptSnippet: promptMetadata.promptSnippet,
|
|
5204
5407
|
promptGuidelines: promptMetadata.promptGuidelines,
|
|
5205
|
-
parameters:
|
|
5408
|
+
parameters: Type9.Object({
|
|
5206
5409
|
path: filePathParam(),
|
|
5207
|
-
content:
|
|
5410
|
+
content: Type9.String({ description: PARAM_DESCRIPTIONS.content })
|
|
5208
5411
|
}),
|
|
5209
5412
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
5210
5413
|
const cwd = ctx?.cwd ?? process.cwd();
|
|
@@ -5242,13 +5445,13 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5242
5445
|
}
|
|
5243
5446
|
},
|
|
5244
5447
|
renderCall(args, theme, context = {}) {
|
|
5245
|
-
const { path
|
|
5448
|
+
const { path, content } = args;
|
|
5246
5449
|
const cwd = context.cwd ?? process.cwd();
|
|
5247
5450
|
const label = renderToolLabel(theme, "write");
|
|
5248
5451
|
const lineCount = typeof content === "string" ? content.split(`
|
|
5249
5452
|
`).length : 0;
|
|
5250
5453
|
const bytes = typeof content === "string" ? Buffer.byteLength(content, "utf8") : 0;
|
|
5251
|
-
const renderedPath = typeof
|
|
5454
|
+
const renderedPath = typeof path === "string" ? linkToolPath(theme.fg("muted", path), path, cwd) : theme.fg("toolOutput", "...");
|
|
5252
5455
|
let text = clampLineToWidth(`${label} ${renderedPath}${typeof content === "string" ? ` (${lineCount} ${lineCount === 1 ? "line" : "lines"} • ${bytes} B)` : ""}`, context.width);
|
|
5253
5456
|
if (context.executionStarted) {
|
|
5254
5457
|
return upsertTextComponent(context.lastComponent, text);
|
|
@@ -5263,8 +5466,8 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5263
5466
|
return upsertTextComponent(context.lastComponent, clampLinesToWidth(parts.lines, context.width).join(`
|
|
5264
5467
|
`));
|
|
5265
5468
|
},
|
|
5266
|
-
renderResult(result,
|
|
5267
|
-
const { isPartial, expanded, width, context } = resolveRenderResultContext(
|
|
5469
|
+
renderResult(result, options, theme, ...rest) {
|
|
5470
|
+
const { isPartial, expanded, width, context } = resolveRenderResultContext(options, rest, resolveReadSeekToolDisplayMode("write") === "expanded");
|
|
5268
5471
|
if (isPartial)
|
|
5269
5472
|
return renderPendingResult("pending write", width, theme);
|
|
5270
5473
|
const details = result.details ?? {};
|
|
@@ -5277,8 +5480,8 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5277
5480
|
if (state === "created") {
|
|
5278
5481
|
const readSeekLines = details.readSeekValue?.lines ?? [];
|
|
5279
5482
|
const hasContent = readSeekLines.length > 0;
|
|
5280
|
-
const
|
|
5281
|
-
const lines =
|
|
5483
|
+
const header = summaryLine(state, { hidden: hasContent && !expanded, theme, style: "success" });
|
|
5484
|
+
const lines = header.split(`
|
|
5282
5485
|
`);
|
|
5283
5486
|
if (expanded && hasContent) {
|
|
5284
5487
|
const content = readSeekLines.map((l) => l.raw).join(`
|
|
@@ -5303,7 +5506,7 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5303
5506
|
}
|
|
5304
5507
|
|
|
5305
5508
|
// src/def.ts
|
|
5306
|
-
import { Type as
|
|
5509
|
+
import { Type as Type10 } from "@sinclair/typebox";
|
|
5307
5510
|
import path7 from "node:path";
|
|
5308
5511
|
var DEF_PROMPT_METADATA = defineToolPromptMetadata({
|
|
5309
5512
|
promptUrl: new URL("../prompts/def.md", import.meta.url),
|
|
@@ -5312,9 +5515,6 @@ var DEF_PROMPT_METADATA = defineToolPromptMetadata({
|
|
|
5312
5515
|
async function executeDef(opts) {
|
|
5313
5516
|
const { params, signal, cwd, onFileAnchored } = opts;
|
|
5314
5517
|
const p = params;
|
|
5315
|
-
const ignoredError = validateIgnoredRequiresOthers("def", p);
|
|
5316
|
-
if (ignoredError)
|
|
5317
|
-
return ignoredError;
|
|
5318
5518
|
if (!p.name || !p.name.trim()) {
|
|
5319
5519
|
return buildToolErrorResult("def", "invalid-parameter", "readSeek_def requires 'name'");
|
|
5320
5520
|
}
|
|
@@ -5326,9 +5526,7 @@ async function executeDef(opts) {
|
|
|
5326
5526
|
const definitions = await readSeekDef(searchPath, {
|
|
5327
5527
|
name: p.name,
|
|
5328
5528
|
language: p.language,
|
|
5329
|
-
|
|
5330
|
-
others: p.others,
|
|
5331
|
-
ignored: p.ignored,
|
|
5529
|
+
gitScope: p.gitScope,
|
|
5332
5530
|
signal
|
|
5333
5531
|
});
|
|
5334
5532
|
if (definitions.length === 0) {
|
|
@@ -5379,8 +5577,8 @@ function registerDefTool(pi, options = {}) {
|
|
|
5379
5577
|
description: DEF_PROMPT_METADATA.description,
|
|
5380
5578
|
promptSnippet: DEF_PROMPT_METADATA.promptSnippet,
|
|
5381
5579
|
promptGuidelines: DEF_PROMPT_METADATA.promptGuidelines,
|
|
5382
|
-
parameters:
|
|
5383
|
-
name:
|
|
5580
|
+
parameters: Type10.Object({
|
|
5581
|
+
name: Type10.String({ description: PARAM_DESCRIPTIONS.name }),
|
|
5384
5582
|
path: searchPathParam(),
|
|
5385
5583
|
language: languageParam(),
|
|
5386
5584
|
...readSeekGitSearchParams()
|
|
@@ -5392,11 +5590,11 @@ function registerDefTool(pi, options = {}) {
|
|
|
5392
5590
|
return renderReadSeekSearchCall(args, theme, rest, {
|
|
5393
5591
|
label: "def",
|
|
5394
5592
|
accent: args.name,
|
|
5395
|
-
flags: [args.
|
|
5593
|
+
flags: [args.gitScope && `git-scope=${args.gitScope}`]
|
|
5396
5594
|
});
|
|
5397
5595
|
},
|
|
5398
|
-
renderResult(result,
|
|
5399
|
-
return renderAnchoredFilesResult(result,
|
|
5596
|
+
renderResult(result, options, theme, ...rest) {
|
|
5597
|
+
return renderAnchoredFilesResult(result, options, theme, rest, {
|
|
5400
5598
|
pendingLabel: "pending def",
|
|
5401
5599
|
emptyLabel: "no definitions",
|
|
5402
5600
|
unitSingular: "definition",
|
|
@@ -5406,39 +5604,33 @@ function registerDefTool(pi, options = {}) {
|
|
|
5406
5604
|
});
|
|
5407
5605
|
}
|
|
5408
5606
|
|
|
5409
|
-
// src/
|
|
5607
|
+
// src/inspect.ts
|
|
5410
5608
|
import { Text as Text7 } from "@earendil-works/pi-tui";
|
|
5411
|
-
import { Type as
|
|
5412
|
-
var
|
|
5413
|
-
var
|
|
5414
|
-
var digestSchema = Type10.Object({
|
|
5609
|
+
import { Type as Type11 } from "@sinclair/typebox";
|
|
5610
|
+
var facetSchema = Type11.Union(INSPECT_FACETS.map((facet) => Type11.Literal(facet)));
|
|
5611
|
+
var inspectSchema = Type11.Object({
|
|
5415
5612
|
path: filePathParam(),
|
|
5416
|
-
|
|
5613
|
+
selector: Type11.Optional(Type11.String({ description: PARAM_DESCRIPTIONS.selector })),
|
|
5614
|
+
select: Type11.Optional(Type11.Union([
|
|
5417
5615
|
facetSchema,
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
], {
|
|
5423
|
-
description: PARAM_DESCRIPTIONS.select
|
|
5424
|
-
})),
|
|
5425
|
-
at: Type10.Optional(Type10.String({
|
|
5426
|
-
description: PARAM_DESCRIPTIONS.at
|
|
5427
|
-
})),
|
|
5616
|
+
Type11.Array(facetSchema, { minItems: 1 }),
|
|
5617
|
+
Type11.String({ description: PARAM_DESCRIPTIONS.selectComma })
|
|
5618
|
+
], { description: PARAM_DESCRIPTIONS.select })),
|
|
5619
|
+
query: Type11.Optional(Type11.String({ description: PARAM_DESCRIPTIONS.query })),
|
|
5428
5620
|
end: optionalIntOrString(PARAM_DESCRIPTIONS.end),
|
|
5429
5621
|
limit: optionalIntOrString(PARAM_DESCRIPTIONS.limit),
|
|
5430
|
-
depth: optionalIntOrString(PARAM_DESCRIPTIONS.
|
|
5431
|
-
language:
|
|
5432
|
-
visionMode:
|
|
5433
|
-
visionLevel:
|
|
5622
|
+
depth: optionalIntOrString(PARAM_DESCRIPTIONS.inspectDepth),
|
|
5623
|
+
language: Type11.Optional(Type11.String({ description: PARAM_DESCRIPTIONS.languageOverride })),
|
|
5624
|
+
visionMode: Type11.Optional(Type11.Union(VISION_MODES.map((mode) => Type11.Literal(mode)), { description: PARAM_DESCRIPTIONS.visionModeInspect })),
|
|
5625
|
+
visionLevel: Type11.Optional(Type11.Union(VISION_LEVELS.map((level) => Type11.Literal(level)), { description: PARAM_DESCRIPTIONS.visionLevel }))
|
|
5434
5626
|
});
|
|
5435
|
-
function
|
|
5627
|
+
function hasInspectAnchors(value) {
|
|
5436
5628
|
if (!value || typeof value !== "object")
|
|
5437
5629
|
return false;
|
|
5438
5630
|
const record = value;
|
|
5439
5631
|
if (Array.isArray(record.hashlines) && record.hashlines.length > 0)
|
|
5440
5632
|
return true;
|
|
5441
|
-
return Object.values(record).some(
|
|
5633
|
+
return Object.values(record).some(hasInspectAnchors);
|
|
5442
5634
|
}
|
|
5443
5635
|
function parseSelect(select) {
|
|
5444
5636
|
if (select === undefined)
|
|
@@ -5448,8 +5640,8 @@ function parseSelect(select) {
|
|
|
5448
5640
|
if (facets.length === 0)
|
|
5449
5641
|
throw new Error("select must include at least one facet");
|
|
5450
5642
|
for (const facet of facets) {
|
|
5451
|
-
if (!
|
|
5452
|
-
throw new Error(`unknown
|
|
5643
|
+
if (!INSPECT_FACETS.includes(facet)) {
|
|
5644
|
+
throw new Error(`unknown inspect facet \`${facet}\`; expected ${INSPECT_FACETS.join(", ")}`);
|
|
5453
5645
|
}
|
|
5454
5646
|
}
|
|
5455
5647
|
return facets;
|
|
@@ -5457,15 +5649,15 @@ function parseSelect(select) {
|
|
|
5457
5649
|
if (typeof select !== "string") {
|
|
5458
5650
|
throw new Error("select must be a facet, facet list, or comma-separated facets");
|
|
5459
5651
|
}
|
|
5460
|
-
if (
|
|
5652
|
+
if (INSPECT_FACETS.includes(select)) {
|
|
5461
5653
|
return select;
|
|
5462
5654
|
}
|
|
5463
5655
|
const parts = select.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
|
|
5464
5656
|
if (parts.length === 0)
|
|
5465
5657
|
throw new Error("select must include at least one facet");
|
|
5466
5658
|
for (const part of parts) {
|
|
5467
|
-
if (!
|
|
5468
|
-
throw new Error(`unknown
|
|
5659
|
+
if (!INSPECT_FACETS.includes(part)) {
|
|
5660
|
+
throw new Error(`unknown inspect facet \`${part}\`; expected ${INSPECT_FACETS.join(", ")}`);
|
|
5469
5661
|
}
|
|
5470
5662
|
}
|
|
5471
5663
|
return parts;
|
|
@@ -5477,7 +5669,7 @@ function selectedFacets(select) {
|
|
|
5477
5669
|
}
|
|
5478
5670
|
function summarizeEnvelope(envelope, facets) {
|
|
5479
5671
|
if (!envelope || typeof envelope !== "object")
|
|
5480
|
-
return `
|
|
5672
|
+
return `inspected ${facets.join(",")}`;
|
|
5481
5673
|
const record = envelope;
|
|
5482
5674
|
const parts = [];
|
|
5483
5675
|
for (const facet of facets) {
|
|
@@ -5486,7 +5678,7 @@ function summarizeEnvelope(envelope, facets) {
|
|
|
5486
5678
|
}
|
|
5487
5679
|
if (record.metadata !== undefined && !parts.includes("metadata"))
|
|
5488
5680
|
parts.unshift("metadata");
|
|
5489
|
-
return `
|
|
5681
|
+
return `inspected ${parts.join(",") || "metadata"}`;
|
|
5490
5682
|
}
|
|
5491
5683
|
function parseImageEnvelope(envelope) {
|
|
5492
5684
|
if (!envelope || typeof envelope !== "object")
|
|
@@ -5504,12 +5696,12 @@ function parseImageEnvelope(envelope) {
|
|
|
5504
5696
|
const prepared = content.encoding === "base64" && typeof content.data === "string" && typeof mime === "string" ? { data: content.data, mimeType: mime } : undefined;
|
|
5505
5697
|
return { metadata, prepared };
|
|
5506
5698
|
}
|
|
5507
|
-
function
|
|
5699
|
+
function buildInspectSuccess(filePath, facets, envelope, content = [{ type: "text", text: JSON.stringify(envelope) }]) {
|
|
5508
5700
|
return {
|
|
5509
5701
|
content,
|
|
5510
5702
|
details: {
|
|
5511
5703
|
readSeekValue: {
|
|
5512
|
-
tool: "
|
|
5704
|
+
tool: "inspect",
|
|
5513
5705
|
ok: true,
|
|
5514
5706
|
path: filePath,
|
|
5515
5707
|
select: facets,
|
|
@@ -5518,14 +5710,14 @@ function buildDigestSuccess(filePath, facets, envelope, content = [{ type: "text
|
|
|
5518
5710
|
}
|
|
5519
5711
|
};
|
|
5520
5712
|
}
|
|
5521
|
-
async function
|
|
5713
|
+
async function executeInspect(opts) {
|
|
5522
5714
|
const { params, signal, cwd, onFileAnchored } = opts;
|
|
5523
5715
|
const p = params;
|
|
5524
5716
|
let select;
|
|
5525
5717
|
try {
|
|
5526
5718
|
select = parseSelect(p.select);
|
|
5527
5719
|
} catch (err) {
|
|
5528
|
-
return buildToolErrorResult("
|
|
5720
|
+
return buildToolErrorResult("inspect", "invalid-parameter", err?.message ?? String(err), {
|
|
5529
5721
|
path: p.path
|
|
5530
5722
|
});
|
|
5531
5723
|
}
|
|
@@ -5534,52 +5726,66 @@ async function executeDigest(opts) {
|
|
|
5534
5726
|
const end = coerceObviousBase10Int2(p.end, "end");
|
|
5535
5727
|
if (!end.ok || end.value !== undefined && end.value < 1) {
|
|
5536
5728
|
const message = end.ok ? `Invalid end: expected a positive integer, received ${end.value}.` : end.message;
|
|
5537
|
-
return buildToolErrorResult("
|
|
5729
|
+
return buildToolErrorResult("inspect", "invalid-parameter", message, { path: p.path });
|
|
5538
5730
|
}
|
|
5539
5731
|
const limit = coerceObviousBase10Int2(p.limit, "limit");
|
|
5540
5732
|
if (!limit.ok || limit.value !== undefined && limit.value < 1) {
|
|
5541
5733
|
const message = limit.ok ? `Invalid limit: expected a positive integer, received ${limit.value}.` : limit.message;
|
|
5542
|
-
return buildToolErrorResult("
|
|
5734
|
+
return buildToolErrorResult("inspect", "invalid-parameter", message, { path: p.path });
|
|
5735
|
+
}
|
|
5736
|
+
if (end.value !== undefined && limit.value !== undefined) {
|
|
5737
|
+
return buildToolErrorResult("inspect", "invalid-params-combo", "end and limit cannot be combined.", { path: p.path });
|
|
5543
5738
|
}
|
|
5544
5739
|
const depth = coerceObviousBase10Int2(p.depth, "depth");
|
|
5545
5740
|
if (!depth.ok || depth.value !== undefined && depth.value < 0) {
|
|
5546
5741
|
const message = depth.ok ? `Invalid depth: expected a non-negative integer, received ${depth.value}.` : depth.message;
|
|
5547
|
-
return buildToolErrorResult("
|
|
5742
|
+
return buildToolErrorResult("inspect", "invalid-parameter", message, { path: p.path });
|
|
5548
5743
|
}
|
|
5549
5744
|
if (depth.value !== undefined && !facets.includes("map")) {
|
|
5550
|
-
return buildToolErrorResult("
|
|
5745
|
+
return buildToolErrorResult("inspect", "invalid-parameter", "depth requires the map facet.", { path: p.path });
|
|
5551
5746
|
}
|
|
5552
|
-
const
|
|
5553
|
-
if (p.
|
|
5554
|
-
return buildToolErrorResult("
|
|
5555
|
-
path: p.path
|
|
5556
|
-
});
|
|
5747
|
+
const selector = typeof p.selector === "string" ? p.selector.trim() : undefined;
|
|
5748
|
+
if (p.selector !== undefined && !selector) {
|
|
5749
|
+
return buildToolErrorResult("inspect", "invalid-parameter", "Invalid selector: expected a non-empty selector.", { path: p.path });
|
|
5557
5750
|
}
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5751
|
+
const query = typeof p.query === "string" ? p.query.trim() : undefined;
|
|
5752
|
+
if (p.query !== undefined && !query) {
|
|
5753
|
+
return buildToolErrorResult("inspect", "invalid-parameter", "Invalid query: expected a non-empty query.", { path: p.path });
|
|
5754
|
+
}
|
|
5755
|
+
if (selector !== undefined && query !== undefined) {
|
|
5756
|
+
return buildToolErrorResult("inspect", "invalid-params-combo", "selector and query cannot be combined.", { path: p.path });
|
|
5757
|
+
}
|
|
5758
|
+
if (facets.includes("identity") && selector === undefined) {
|
|
5759
|
+
return buildToolErrorResult("inspect", "invalid-params-combo", "the identity facet requires a selector.", { path: p.path });
|
|
5760
|
+
}
|
|
5761
|
+
if (p.visionLevel !== undefined && (p.visionMode === undefined || p.visionMode === "none") && !appliesImagePolicy) {
|
|
5762
|
+
return buildToolErrorResult("inspect", "invalid-params-combo", "visionLevel requires an analysis visionMode.", { path: p.path });
|
|
5562
5763
|
}
|
|
5563
5764
|
const filePath = resolveToCwd2(p.path, cwd);
|
|
5564
5765
|
const commonOptions = {
|
|
5565
5766
|
select,
|
|
5566
5767
|
signal,
|
|
5567
|
-
depth: depth.value ?? (facets.includes("map") ?
|
|
5768
|
+
depth: depth.value ?? (facets.includes("map") ? DEFAULT_INSPECT_MAP_DEPTH : undefined)
|
|
5568
5769
|
};
|
|
5569
|
-
const
|
|
5770
|
+
const selectionOptions = {
|
|
5570
5771
|
...commonOptions,
|
|
5571
|
-
|
|
5772
|
+
selector,
|
|
5773
|
+
query,
|
|
5572
5774
|
end: end.value,
|
|
5573
5775
|
limit: limit.value,
|
|
5574
5776
|
language: p.language
|
|
5575
5777
|
};
|
|
5576
|
-
const
|
|
5778
|
+
const probeOptions = {
|
|
5779
|
+
...commonOptions,
|
|
5780
|
+
select: [...new Set(["metadata", ...facets])].filter((facet) => facet !== "identity")
|
|
5781
|
+
};
|
|
5782
|
+
const hasSelectionOptions = selector !== undefined || query !== undefined || end.value !== undefined || limit.value !== undefined || p.language !== undefined;
|
|
5577
5783
|
const hasVisionOptions = p.visionMode !== undefined || p.visionLevel !== undefined;
|
|
5578
5784
|
const initialVisionMode = appliesImagePolicy ? undefined : p.visionMode;
|
|
5579
5785
|
const initialVisionLevel = appliesImagePolicy ? undefined : p.visionLevel;
|
|
5580
5786
|
try {
|
|
5581
|
-
const envelope = await
|
|
5582
|
-
...appliesImagePolicy ?
|
|
5787
|
+
const envelope = await readSeekInspect(filePath, {
|
|
5788
|
+
...appliesImagePolicy ? probeOptions : selectionOptions,
|
|
5583
5789
|
visionMode: initialVisionMode,
|
|
5584
5790
|
visionLevel: initialVisionLevel,
|
|
5585
5791
|
vision: initialVisionMode !== undefined && initialVisionMode !== "none"
|
|
@@ -5588,60 +5794,60 @@ async function executeDigest(opts) {
|
|
|
5588
5794
|
if (image) {
|
|
5589
5795
|
const imageDetails = { metadata: image.metadata };
|
|
5590
5796
|
if (opts.imageMode === "off") {
|
|
5591
|
-
return
|
|
5797
|
+
return buildInspectSuccess(filePath, facets, imageDetails, [
|
|
5592
5798
|
{ type: "text", text: `[Skipped standalone image: ${p.path}; readseek.imageMode is off]` }
|
|
5593
5799
|
]);
|
|
5594
5800
|
}
|
|
5595
5801
|
if (opts.imageMode === "auto" && opts.modelSupportsImages && image.prepared) {
|
|
5596
|
-
return
|
|
5802
|
+
return buildInspectSuccess(filePath, facets, imageDetails, [
|
|
5597
5803
|
{ type: "image", data: image.prepared.data, mimeType: image.prepared.mimeType }
|
|
5598
5804
|
]);
|
|
5599
5805
|
}
|
|
5600
5806
|
const visionMode = p.visionMode && p.visionMode !== "none" ? p.visionMode : "all";
|
|
5601
|
-
const analyzedEnvelope = await
|
|
5807
|
+
const analyzedEnvelope = await readSeekInspect(filePath, {
|
|
5602
5808
|
...commonOptions,
|
|
5603
5809
|
visionMode,
|
|
5604
5810
|
visionLevel: p.visionLevel,
|
|
5605
5811
|
vision: true
|
|
5606
5812
|
});
|
|
5607
|
-
return
|
|
5813
|
+
return buildInspectSuccess(filePath, facets, analyzedEnvelope);
|
|
5608
5814
|
}
|
|
5609
|
-
if (p.visionLevel !== undefined && p.visionMode === undefined) {
|
|
5610
|
-
return buildToolErrorResult("
|
|
5815
|
+
if (p.visionLevel !== undefined && (p.visionMode === undefined || p.visionMode === "none")) {
|
|
5816
|
+
return buildToolErrorResult("inspect", "invalid-params-combo", "visionLevel requires an analysis visionMode.", {
|
|
5611
5817
|
path: p.path
|
|
5612
5818
|
});
|
|
5613
5819
|
}
|
|
5614
|
-
const
|
|
5615
|
-
...
|
|
5820
|
+
const selectedEnvelope = appliesImagePolicy && (hasSelectionOptions || hasVisionOptions) ? await readSeekInspect(filePath, {
|
|
5821
|
+
...selectionOptions,
|
|
5616
5822
|
visionMode: p.visionMode,
|
|
5617
5823
|
visionLevel: p.visionLevel,
|
|
5618
5824
|
vision: p.visionMode !== undefined && p.visionMode !== "none"
|
|
5619
5825
|
}) : envelope;
|
|
5620
|
-
if (
|
|
5826
|
+
if (hasInspectAnchors(selectedEnvelope))
|
|
5621
5827
|
onFileAnchored?.(filePath);
|
|
5622
|
-
return
|
|
5828
|
+
return buildInspectSuccess(filePath, facets, selectedEnvelope);
|
|
5623
5829
|
} catch (err) {
|
|
5624
5830
|
const failure = classifyReadSeekFailure(err);
|
|
5625
|
-
return buildToolErrorResult("
|
|
5831
|
+
return buildToolErrorResult("inspect", failure.code, failure.message, failure.hint ? { hint: failure.hint } : {});
|
|
5626
5832
|
}
|
|
5627
5833
|
}
|
|
5628
|
-
function
|
|
5834
|
+
function registerInspectTool(pi, options = {}) {
|
|
5629
5835
|
const promptMetadata = defineToolPromptMetadata({
|
|
5630
|
-
promptUrl: new URL("../prompts/
|
|
5631
|
-
promptSnippet: "
|
|
5632
|
-
registeredName: options.name ?? "
|
|
5836
|
+
promptUrl: new URL("../prompts/inspect.md", import.meta.url),
|
|
5837
|
+
promptSnippet: "Inspect source, images, and documents",
|
|
5838
|
+
registeredName: options.name ?? "readSeek_inspect",
|
|
5633
5839
|
toolAliases: options.toolAliases
|
|
5634
5840
|
});
|
|
5635
5841
|
return registerReadSeekTool(pi, {
|
|
5636
|
-
name: options.name ?? "
|
|
5637
|
-
label: "
|
|
5842
|
+
name: options.name ?? "readSeek_inspect",
|
|
5843
|
+
label: "Inspect",
|
|
5638
5844
|
description: promptMetadata.description,
|
|
5639
5845
|
promptSnippet: promptMetadata.promptSnippet,
|
|
5640
5846
|
toolAliases: options.toolAliases,
|
|
5641
5847
|
promptGuidelines: promptMetadata.promptGuidelines,
|
|
5642
|
-
parameters:
|
|
5848
|
+
parameters: inspectSchema,
|
|
5643
5849
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
5644
|
-
return
|
|
5850
|
+
return executeInspect({
|
|
5645
5851
|
params,
|
|
5646
5852
|
signal,
|
|
5647
5853
|
cwd: ctx.cwd,
|
|
@@ -5654,23 +5860,23 @@ function registerDigestTool(pi, options = {}) {
|
|
|
5654
5860
|
const context = rest[0] ?? {};
|
|
5655
5861
|
const cwd = context.cwd ?? process.cwd();
|
|
5656
5862
|
const displayPath = typeof args?.path === "string" ? args.path : "?";
|
|
5657
|
-
let text = theme.fg("toolTitle", theme.bold("
|
|
5863
|
+
let text = theme.fg("toolTitle", theme.bold("inspect"));
|
|
5658
5864
|
text += ` ${linkToolPath(theme.fg("accent", displayPath), displayPath, cwd)}`;
|
|
5659
5865
|
if (args?.select !== undefined) {
|
|
5660
5866
|
const select = Array.isArray(args.select) ? args.select.join(",") : String(args.select);
|
|
5661
5867
|
text += theme.fg("dim", ` [${select}]`);
|
|
5662
5868
|
}
|
|
5663
|
-
if (typeof args?.
|
|
5664
|
-
text += theme.fg("dim", ` @ ${args.
|
|
5869
|
+
if (typeof args?.selector === "string" && args.selector)
|
|
5870
|
+
text += theme.fg("dim", ` @ ${args.selector}`);
|
|
5665
5871
|
return new Text7(clampLineToWidth(text, context.width), 0, 0);
|
|
5666
5872
|
},
|
|
5667
|
-
renderResult(result,
|
|
5668
|
-
const { isPartial, isError, expanded, width } = resolveRenderResultContext(
|
|
5873
|
+
renderResult(result, options, theme, ...rest) {
|
|
5874
|
+
const { isPartial, isError, expanded, width } = resolveRenderResultContext(options, rest);
|
|
5669
5875
|
if (isPartial)
|
|
5670
|
-
return renderPendingResult("pending
|
|
5876
|
+
return renderPendingResult("pending inspect", width, theme);
|
|
5671
5877
|
const textContent = result.content?.[0]?.type === "text" ? result.content[0].text : "";
|
|
5672
5878
|
if (isError || result.isError) {
|
|
5673
|
-
return renderErrorResult(textContent, { expanded, width, fallback: "
|
|
5879
|
+
return renderErrorResult(textContent, { expanded, width, fallback: "inspect failed", theme });
|
|
5674
5880
|
}
|
|
5675
5881
|
const value = result.details?.readSeekValue;
|
|
5676
5882
|
const facets = Array.isArray(value?.select) ? value.select : ["content"];
|
|
@@ -5686,131 +5892,88 @@ ${textContent}`;
|
|
|
5686
5892
|
});
|
|
5687
5893
|
}
|
|
5688
5894
|
|
|
5689
|
-
// src/
|
|
5690
|
-
import {
|
|
5691
|
-
DEFAULT_MAX_BYTES as DEFAULT_MAX_BYTES2,
|
|
5692
|
-
DEFAULT_MAX_LINES as DEFAULT_MAX_LINES2,
|
|
5693
|
-
truncateHead as truncateHead3
|
|
5694
|
-
} from "@earendil-works/pi-coding-agent";
|
|
5895
|
+
// src/extract.ts
|
|
5695
5896
|
import { Text as Text8 } from "@earendil-works/pi-tui";
|
|
5696
|
-
import { Type as
|
|
5697
|
-
var
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
promptSnippet: "Digest a document's structure or selected content"
|
|
5897
|
+
import { Type as Type12 } from "@sinclair/typebox";
|
|
5898
|
+
var EXTRACT_PROMPT_METADATA = defineToolPromptMetadata({
|
|
5899
|
+
promptUrl: new URL("../prompts/extract.md", import.meta.url),
|
|
5900
|
+
promptSnippet: "Extract a selected document asset to a file"
|
|
5701
5901
|
});
|
|
5702
|
-
async function
|
|
5703
|
-
const {
|
|
5704
|
-
const input = params;
|
|
5705
|
-
const
|
|
5706
|
-
if (!
|
|
5707
|
-
|
|
5708
|
-
return buildToolErrorResult("view", "invalid-page", message, { path: input.path });
|
|
5709
|
-
}
|
|
5710
|
-
const depth = coerceObviousBase10Int2(input.depth, "depth");
|
|
5711
|
-
if (!depth.ok || depth.value !== undefined && depth.value < 0) {
|
|
5712
|
-
const message = depth.ok ? `Invalid depth: expected a non-negative integer, received ${depth.value}.` : depth.message;
|
|
5713
|
-
return buildToolErrorResult("view", "invalid-depth", message, { path: input.path });
|
|
5714
|
-
}
|
|
5715
|
-
const node = input.node?.trim();
|
|
5716
|
-
if (input.node !== undefined && !node) {
|
|
5717
|
-
return buildToolErrorResult("view", "invalid-node", "Invalid node: expected a non-empty ID.", {
|
|
5902
|
+
async function executeExtract(opts) {
|
|
5903
|
+
const { signal, cwd, onFileMutated } = opts;
|
|
5904
|
+
const input = opts.params;
|
|
5905
|
+
const selector = typeof input.selector === "string" ? input.selector.trim() : "";
|
|
5906
|
+
if (!selector.startsWith("asset:") || selector === "asset:") {
|
|
5907
|
+
return buildToolErrorResult("extract", "invalid-selector", "selector must identify an asset as asset:ID.", {
|
|
5718
5908
|
path: input.path
|
|
5719
5909
|
});
|
|
5720
5910
|
}
|
|
5721
|
-
const
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
visionLevel: input.visionLevel
|
|
5725
|
-
});
|
|
5726
|
-
if (!visionValidation.ok) {
|
|
5727
|
-
return buildToolErrorResult("view", "invalid-params-combo", `${visionValidation.error}.`, {
|
|
5911
|
+
const output = typeof input.output === "string" ? input.output.trim() : "";
|
|
5912
|
+
if (!output || output === "-") {
|
|
5913
|
+
return buildToolErrorResult("extract", "invalid-output", "output must be a file path, not stdout.", {
|
|
5728
5914
|
path: input.path
|
|
5729
5915
|
});
|
|
5730
5916
|
}
|
|
5731
5917
|
const filePath = resolveToCwd2(input.path, cwd);
|
|
5918
|
+
const outputPath = resolveToCwd2(output, cwd);
|
|
5732
5919
|
try {
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
page: page.value,
|
|
5736
|
-
kind: input.kind,
|
|
5737
|
-
depth: depth.value,
|
|
5738
|
-
outline: input.outline,
|
|
5739
|
-
visionMode: input.visionMode,
|
|
5740
|
-
visionLevel: input.visionLevel,
|
|
5741
|
-
signal
|
|
5742
|
-
});
|
|
5743
|
-
const truncation = truncateHead3(output, {
|
|
5744
|
-
maxLines: DEFAULT_MAX_LINES2,
|
|
5745
|
-
maxBytes: DEFAULT_MAX_BYTES2
|
|
5746
|
-
});
|
|
5747
|
-
const notice = truncation.truncated ? `
|
|
5748
|
-
[… document view truncated; narrow it with page, node, kind, or depth]` : "";
|
|
5920
|
+
await readSeekExtract(filePath, selector, outputPath, { signal, cancelable: false });
|
|
5921
|
+
onFileMutated?.(outputPath);
|
|
5749
5922
|
return {
|
|
5750
|
-
content: [{ type: "text", text:
|
|
5923
|
+
content: [{ type: "text", text: `Extracted ${selector} to ${outputPath}` }],
|
|
5751
5924
|
details: {
|
|
5752
5925
|
readSeekValue: {
|
|
5753
|
-
tool: "
|
|
5926
|
+
tool: "extract",
|
|
5927
|
+
ok: true,
|
|
5754
5928
|
path: filePath,
|
|
5755
|
-
|
|
5929
|
+
selector,
|
|
5930
|
+
output: outputPath
|
|
5756
5931
|
}
|
|
5757
5932
|
}
|
|
5758
5933
|
};
|
|
5759
5934
|
} catch (error) {
|
|
5760
5935
|
const failure = classifyReadSeekFailure(error);
|
|
5761
|
-
return buildToolErrorResult("
|
|
5936
|
+
return buildToolErrorResult("extract", failure.code, failure.message, failure.hint ? { hint: failure.hint } : {});
|
|
5762
5937
|
}
|
|
5763
5938
|
}
|
|
5764
|
-
function
|
|
5939
|
+
function registerExtractTool(pi, options = {}) {
|
|
5765
5940
|
return registerReadSeekTool(pi, {
|
|
5766
|
-
name: "
|
|
5767
|
-
label: "
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
promptGuidelines: VIEW_PROMPT_METADATA.promptGuidelines,
|
|
5771
|
-
parameters: Type11.Object({
|
|
5941
|
+
name: "readSeek_extract",
|
|
5942
|
+
label: "Extract",
|
|
5943
|
+
...EXTRACT_PROMPT_METADATA,
|
|
5944
|
+
parameters: Type12.Object({
|
|
5772
5945
|
path: filePathParam(),
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
kind: Type11.Optional(Type11.Union(NODE_KINDS.map((kind) => Type11.Literal(kind)), {
|
|
5776
|
-
description: PARAM_DESCRIPTIONS.kind
|
|
5777
|
-
})),
|
|
5778
|
-
depth: optionalIntOrString(PARAM_DESCRIPTIONS.depth),
|
|
5779
|
-
visionMode: Type11.Optional(Type11.Union(VISION_ANALYSIS_MODES.map((mode) => Type11.Literal(mode)), { description: PARAM_DESCRIPTIONS.visionModeView })),
|
|
5780
|
-
visionLevel: Type11.Optional(Type11.Union(VISION_LEVELS.map((level) => Type11.Literal(level)), { description: PARAM_DESCRIPTIONS.visionLevel })),
|
|
5781
|
-
outline: Type11.Optional(Type11.Boolean({ description: PARAM_DESCRIPTIONS.outline }))
|
|
5946
|
+
selector: Type12.String({ pattern: "^asset:.+", description: PARAM_DESCRIPTIONS.assetSelector }),
|
|
5947
|
+
output: Type12.String({ minLength: 1, pattern: "^(?!-$).+", description: PARAM_DESCRIPTIONS.output })
|
|
5782
5948
|
}),
|
|
5783
5949
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
5784
|
-
return
|
|
5950
|
+
return executeExtract({
|
|
5951
|
+
params,
|
|
5952
|
+
signal,
|
|
5953
|
+
cwd: ctx.cwd,
|
|
5954
|
+
onFileMutated: options.onFileMutated
|
|
5955
|
+
});
|
|
5785
5956
|
},
|
|
5786
5957
|
renderCall(args, theme, ...rest) {
|
|
5787
5958
|
const context = rest[0] ?? {};
|
|
5788
5959
|
const cwd = context.cwd ?? process.cwd();
|
|
5789
5960
|
const displayPath = typeof args?.path === "string" ? args.path : "?";
|
|
5790
|
-
const
|
|
5961
|
+
const output = typeof args?.output === "string" ? args.output : "?";
|
|
5962
|
+
let text = `${theme.fg("toolTitle", theme.bold("extract"))} ${linkToolPath(theme.fg("accent", displayPath), displayPath, cwd)}`;
|
|
5963
|
+
if (typeof args?.selector === "string")
|
|
5964
|
+
text += theme.fg("dim", ` ${args.selector}`);
|
|
5965
|
+
text += theme.fg("dim", ` → ${output}`);
|
|
5791
5966
|
return new Text8(clampLineToWidth(text, context.width), 0, 0);
|
|
5792
5967
|
},
|
|
5793
5968
|
renderResult(result, options, theme, ...rest) {
|
|
5794
|
-
const { isPartial, isError,
|
|
5969
|
+
const { isPartial, isError, width } = resolveRenderResultContext(options, rest);
|
|
5795
5970
|
if (isPartial)
|
|
5796
|
-
return renderPendingResult("pending
|
|
5971
|
+
return renderPendingResult("pending extract", width, theme);
|
|
5797
5972
|
const textContent = result.content?.[0]?.type === "text" ? result.content[0].text : "";
|
|
5798
5973
|
if (isError || result.isError) {
|
|
5799
|
-
return renderErrorResult(textContent, { expanded, width, fallback: "
|
|
5974
|
+
return renderErrorResult(textContent, { expanded: false, width, fallback: "extract failed", theme });
|
|
5800
5975
|
}
|
|
5801
|
-
|
|
5802
|
-
`).filter(Boolean).length;
|
|
5803
|
-
let text = summaryLine(`loaded ${lines} document ${lines === 1 ? "line" : "lines"}`, {
|
|
5804
|
-
hidden: !!textContent && !expanded,
|
|
5805
|
-
theme,
|
|
5806
|
-
style: "success"
|
|
5807
|
-
});
|
|
5808
|
-
if (expanded && textContent)
|
|
5809
|
-
text += `
|
|
5810
|
-
${textContent}`;
|
|
5811
|
-
return new Text8(clampLinesToWidth(text.split(`
|
|
5812
|
-
`), width).join(`
|
|
5813
|
-
`), 0, 0);
|
|
5976
|
+
return new Text8(clampLineToWidth(summaryLine(textContent || "asset extracted", { theme, style: "success" }), width), 0, 0);
|
|
5814
5977
|
}
|
|
5815
5978
|
});
|
|
5816
5979
|
}
|
|
@@ -5841,32 +6004,20 @@ var READSEEK_TOOL_ENTRIES = [
|
|
|
5841
6004
|
{ builtIn: null, readSeekName: "readSeek_rename" },
|
|
5842
6005
|
{ builtIn: "write", readSeekName: "readSeek_write" },
|
|
5843
6006
|
{ builtIn: null, readSeekName: "readSeek_def" },
|
|
5844
|
-
{ builtIn: "read", readSeekName: "
|
|
5845
|
-
{ builtIn: null, readSeekName: "
|
|
6007
|
+
{ builtIn: "read", readSeekName: "readSeek_inspect" },
|
|
6008
|
+
{ builtIn: null, readSeekName: "readSeek_extract" }
|
|
5846
6009
|
];
|
|
5847
|
-
function formatSettingsWarning(warning) {
|
|
5848
|
-
return `${warning.message} (${warning.source})`;
|
|
5849
|
-
}
|
|
5850
|
-
function toolRoutingPolicy(aliases) {
|
|
5851
|
-
return renderToolRoutingPolicy({
|
|
5852
|
-
aliases,
|
|
5853
|
-
title: "ReadSeek tool policy:"
|
|
5854
|
-
});
|
|
5855
|
-
}
|
|
5856
6010
|
function piReadSeekExtension(pi) {
|
|
5857
6011
|
const sessionAnchors = new SessionAnchors;
|
|
5858
|
-
const markAnchored = (absolutePath) => sessionAnchors.markAnchored(absolutePath);
|
|
5859
|
-
const hasFreshAnchors = (absolutePath) => sessionAnchors.hasFreshAnchors(absolutePath);
|
|
5860
|
-
const forgetAnchors = (absolutePath) => sessionAnchors.forget(absolutePath);
|
|
5861
6012
|
const { settings } = resolveReadSeekJsonSettings();
|
|
5862
6013
|
const replacedBuiltIns = new Set(settings.overrideTools ?? []);
|
|
5863
6014
|
const swap = (builtIn, readSeekName) => replacedBuiltIns.has(builtIn) ? builtIn : readSeekName;
|
|
5864
|
-
const readName = swap("read", "
|
|
6015
|
+
const readName = swap("read", "readSeek_inspect");
|
|
5865
6016
|
const editName = swap("edit", "readSeek_edit");
|
|
5866
6017
|
const writeName = swap("write", "readSeek_write");
|
|
5867
6018
|
const grepName = swap("grep", "readSeek_grep");
|
|
5868
6019
|
const toolAliases = {
|
|
5869
|
-
|
|
6020
|
+
readSeek_inspect: readName,
|
|
5870
6021
|
readSeek_edit: editName,
|
|
5871
6022
|
readSeek_grep: grepName,
|
|
5872
6023
|
readSeek_write: writeName
|
|
@@ -5875,20 +6026,33 @@ function piReadSeekExtension(pi) {
|
|
|
5875
6026
|
const readSeekName = `readSeek_${name}`;
|
|
5876
6027
|
return [name, toolAliases[readSeekName] ?? readSeekName];
|
|
5877
6028
|
}));
|
|
5878
|
-
registerEditTool(pi, {
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
onFileAnchored: markAnchored,
|
|
6029
|
+
registerEditTool(pi, {
|
|
6030
|
+
wasReadInSession: (absolutePath) => sessionAnchors.hasFreshAnchors(absolutePath),
|
|
6031
|
+
onFileMutated: (absolutePath) => sessionAnchors.forget(absolutePath),
|
|
6032
|
+
name: editName,
|
|
6033
|
+
toolAliases
|
|
6034
|
+
});
|
|
6035
|
+
registerGrepTool(pi, {
|
|
6036
|
+
onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath),
|
|
6037
|
+
name: grepName
|
|
6038
|
+
});
|
|
6039
|
+
registerSearchTool(pi, { onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath) });
|
|
6040
|
+
registerRefsTool(pi, { onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath) });
|
|
6041
|
+
registerRenameTool(pi, { onFileMutated: (absolutePath) => sessionAnchors.forget(absolutePath) });
|
|
6042
|
+
registerDefTool(pi, { onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath) });
|
|
6043
|
+
registerInspectTool(pi, {
|
|
6044
|
+
onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath),
|
|
5886
6045
|
name: readName,
|
|
5887
6046
|
toolAliases,
|
|
5888
6047
|
imageMode: replacedBuiltIns.has("read") ? resolveReadSeekImageMode() : undefined
|
|
5889
6048
|
});
|
|
5890
|
-
|
|
5891
|
-
|
|
6049
|
+
registerExtractTool(pi, {
|
|
6050
|
+
onFileMutated: (absolutePath) => sessionAnchors.forget(absolutePath)
|
|
6051
|
+
});
|
|
6052
|
+
registerWriteTool(pi, {
|
|
6053
|
+
onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath),
|
|
6054
|
+
name: writeName
|
|
6055
|
+
});
|
|
5892
6056
|
const activeReadSeekNames = READSEEK_TOOL_ENTRIES.map((entry) => entry.builtIn && replacedBuiltIns.has(entry.builtIn) ? entry.builtIn : entry.readSeekName);
|
|
5893
6057
|
const inactiveReadSeekNames = new Set(READSEEK_TOOL_ENTRIES.filter((entry) => entry.builtIn !== null && replacedBuiltIns.has(entry.builtIn)).map((entry) => entry.readSeekName));
|
|
5894
6058
|
const activateReadSeekTools = () => {
|
|
@@ -5908,14 +6072,16 @@ function piReadSeekExtension(pi) {
|
|
|
5908
6072
|
pi.on("before_agent_start", (event) => {
|
|
5909
6073
|
if (!activateReadSeekTools())
|
|
5910
6074
|
return;
|
|
5911
|
-
return {
|
|
6075
|
+
return {
|
|
6076
|
+
systemPrompt: `${event.systemPrompt}
|
|
5912
6077
|
|
|
5913
|
-
${
|
|
6078
|
+
${renderToolRoutingPolicy({ aliases: policyAliases, title: "ReadSeek tool policy:" })}`
|
|
6079
|
+
};
|
|
5914
6080
|
});
|
|
5915
6081
|
pi.on("session_start", (_event, ctx) => {
|
|
5916
6082
|
sessionAnchors.clear();
|
|
5917
6083
|
const { warnings } = resolveReadSeekJsonSettings();
|
|
5918
|
-
const problems = warnings.map(
|
|
6084
|
+
const problems = warnings.map((warning) => `${warning.message} (${warning.source})`);
|
|
5919
6085
|
const availability = readSeekBinaryAvailability();
|
|
5920
6086
|
if (!availability.available) {
|
|
5921
6087
|
problems.push(`readseek installation is broken: ${availability.reason}`);
|