pi-readseek 0.9.16 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/index.ts +1277 -1105
- 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,363 @@ 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
|
-
|
|
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
|
+
function mapNodeShape(child) {
|
|
1418
|
+
return Type.Object({
|
|
1419
|
+
kind: Str,
|
|
1420
|
+
name: Str,
|
|
1421
|
+
start_line: Int,
|
|
1422
|
+
end_line: Int,
|
|
1423
|
+
start_anchor: Str,
|
|
1424
|
+
end_anchor: Str,
|
|
1425
|
+
children: Type.Array(child)
|
|
1426
|
+
});
|
|
1040
1427
|
}
|
|
1428
|
+
function recursiveMapNode() {
|
|
1429
|
+
return Type.Recursive((self) => mapNodeShape(self));
|
|
1430
|
+
}
|
|
1431
|
+
var MapNode = typeof Type.Recursive === "function" ? recursiveMapNode() : Type.Cyclic({ MapNode: mapNodeShape(Type.Ref("MapNode")) }, "MapNode");
|
|
1432
|
+
var MapOutput = Type.Object({
|
|
1433
|
+
file: Str,
|
|
1434
|
+
language: Str,
|
|
1435
|
+
line_count: Int,
|
|
1436
|
+
file_hash: Str,
|
|
1437
|
+
root: Type.Optional(MapNode),
|
|
1438
|
+
symbols: Type.Array(SymbolOutput)
|
|
1439
|
+
});
|
|
1440
|
+
var SearchSpan = {
|
|
1441
|
+
start_byte: OptInt,
|
|
1442
|
+
end_byte: OptInt,
|
|
1443
|
+
text: OptStr,
|
|
1444
|
+
start_line: Int,
|
|
1445
|
+
end_line: Int,
|
|
1446
|
+
start_hash: Str,
|
|
1447
|
+
end_hash: Str,
|
|
1448
|
+
hashlines: Hashlines
|
|
1449
|
+
};
|
|
1450
|
+
var SearchCapture = Type.Object({ name: Str, ...SearchSpan });
|
|
1451
|
+
var SearchMatch = Type.Object({
|
|
1452
|
+
pattern_index: Type.Integer({ default: 0 }),
|
|
1453
|
+
...SearchSpan,
|
|
1454
|
+
captures: Type.Array(SearchCapture)
|
|
1455
|
+
});
|
|
1456
|
+
var SearchFileOutput = Type.Object({
|
|
1457
|
+
file: Str,
|
|
1458
|
+
language: Str,
|
|
1459
|
+
file_hash: Str,
|
|
1460
|
+
matches: Type.Array(SearchMatch)
|
|
1461
|
+
});
|
|
1462
|
+
var SearchEnvelope = Type.Object({
|
|
1463
|
+
results: Type.Array(SearchFileOutput),
|
|
1464
|
+
returned_matches: OptInt,
|
|
1465
|
+
truncated: Type.Optional(Bool),
|
|
1466
|
+
byte_basis: OptStr
|
|
1467
|
+
});
|
|
1468
|
+
var RefsEnvelope = Type.Object({
|
|
1469
|
+
references: Type.Array(Type.Object({
|
|
1470
|
+
file: Str,
|
|
1471
|
+
line: Int,
|
|
1472
|
+
column: Int,
|
|
1473
|
+
line_hash: Str,
|
|
1474
|
+
text: Str,
|
|
1475
|
+
symbol: Type.Optional(Type.Object({ qualified_name: OptStr }))
|
|
1476
|
+
}))
|
|
1477
|
+
});
|
|
1478
|
+
var Diagnostic = Type.Object({
|
|
1479
|
+
kind: Type.Union([Type.Literal("error"), Type.Literal("missing")]),
|
|
1480
|
+
start_line: Int,
|
|
1481
|
+
end_line: Int
|
|
1482
|
+
});
|
|
1483
|
+
var CheckEnvelope = Type.Object({
|
|
1484
|
+
file_hash: OptStr,
|
|
1485
|
+
error_count: Int,
|
|
1486
|
+
missing_count: Int,
|
|
1487
|
+
diagnostics: Type.Array(Diagnostic)
|
|
1488
|
+
});
|
|
1489
|
+
var DetectedObject = Type.Object({
|
|
1490
|
+
label: Str,
|
|
1491
|
+
bbox: Type.Tuple([Int, Int, Int, Int])
|
|
1492
|
+
});
|
|
1493
|
+
var PreparedImage = Type.Object({
|
|
1494
|
+
mime: Str,
|
|
1495
|
+
encoding: Type.Literal("base64"),
|
|
1496
|
+
data: Str
|
|
1497
|
+
});
|
|
1498
|
+
var SourceDetection = Type.Object({
|
|
1499
|
+
kind: Type.Literal("source"),
|
|
1500
|
+
type: Str,
|
|
1501
|
+
file: Str,
|
|
1502
|
+
language: Str,
|
|
1503
|
+
engine: OptStr,
|
|
1504
|
+
supported: Bool,
|
|
1505
|
+
mime: OptStr,
|
|
1506
|
+
syntax: OptStr
|
|
1507
|
+
});
|
|
1508
|
+
var ImageDetection = Type.Object({
|
|
1509
|
+
kind: Type.Literal("image"),
|
|
1510
|
+
type: Str,
|
|
1511
|
+
file: Str,
|
|
1512
|
+
mime: OptStr,
|
|
1513
|
+
format: Str,
|
|
1514
|
+
width: Int,
|
|
1515
|
+
height: Int,
|
|
1516
|
+
animated: Bool,
|
|
1517
|
+
encoding: Type.Optional(Type.Literal("base64")),
|
|
1518
|
+
data: OptStr,
|
|
1519
|
+
ocr: OptStr,
|
|
1520
|
+
caption: OptStr,
|
|
1521
|
+
objects: Type.Optional(Type.Array(DetectedObject))
|
|
1522
|
+
});
|
|
1523
|
+
var TextDetection = Type.Object({
|
|
1524
|
+
kind: Type.Literal("text"),
|
|
1525
|
+
type: Str,
|
|
1526
|
+
file: Str,
|
|
1527
|
+
mime: OptStr
|
|
1528
|
+
});
|
|
1529
|
+
var PdfDetection = Type.Object({
|
|
1530
|
+
kind: Type.Literal("pdf"),
|
|
1531
|
+
type: Type.Literal("application/pdf"),
|
|
1532
|
+
file: Str,
|
|
1533
|
+
mime: OptStr,
|
|
1534
|
+
format: Type.Literal("pdf"),
|
|
1535
|
+
pages: Int
|
|
1536
|
+
});
|
|
1537
|
+
var Detection = Type.Union([SourceDetection, ImageDetection, TextDetection, PdfDetection]);
|
|
1538
|
+
var SourceDetectionInput = Type.Omit(SourceDetection, ["kind"]);
|
|
1539
|
+
var ImageDetectionInput = Type.Omit(ImageDetection, ["kind"]);
|
|
1540
|
+
var TextDetectionInput = Type.Omit(TextDetection, ["kind"]);
|
|
1541
|
+
var PdfDetectionInput = Type.Omit(PdfDetection, ["kind"]);
|
|
1542
|
+
var UnspannedContent = Type.Omit(ReadOutput, ["start_line", "end_line"]);
|
|
1543
|
+
var PdfInspectMetadata = Type.Object({ source: Str, format: Type.Literal("pdf"), pages: Int });
|
|
1544
|
+
var DocumentCache = Type.Object({
|
|
1545
|
+
content_hash: Str,
|
|
1546
|
+
state: Type.Union([Type.Literal("built"), Type.Literal("reused")])
|
|
1547
|
+
});
|
|
1548
|
+
var Identifier = Type.Object({
|
|
1549
|
+
text: Str,
|
|
1550
|
+
start_column: Int,
|
|
1551
|
+
end_column: Int,
|
|
1552
|
+
start_byte: Int,
|
|
1553
|
+
end_byte: Int
|
|
1554
|
+
});
|
|
1555
|
+
var IdentifyEnvelope = Type.Object({
|
|
1556
|
+
file: Str,
|
|
1557
|
+
language: Str,
|
|
1558
|
+
engine: OptStr,
|
|
1559
|
+
line_count: Int,
|
|
1560
|
+
file_hash: Str,
|
|
1561
|
+
line: Int,
|
|
1562
|
+
column: Int,
|
|
1563
|
+
line_hash: Str,
|
|
1564
|
+
identifier: Type.Optional(Identifier),
|
|
1565
|
+
symbol: Type.Optional(Type.Object({
|
|
1566
|
+
name: Str,
|
|
1567
|
+
kind: Str,
|
|
1568
|
+
qualified_name: Str,
|
|
1569
|
+
start_line: Int,
|
|
1570
|
+
end_line: Int
|
|
1571
|
+
}))
|
|
1572
|
+
});
|
|
1573
|
+
var EditOutputSchema = Type.Object({
|
|
1574
|
+
file: Str,
|
|
1575
|
+
before_hash: Str,
|
|
1576
|
+
file_hash: Str,
|
|
1577
|
+
plan_hash: Str,
|
|
1578
|
+
applied: Bool,
|
|
1579
|
+
changed: Bool,
|
|
1580
|
+
edit_count: Int,
|
|
1581
|
+
before_content: OptStr,
|
|
1582
|
+
content: OptStr
|
|
1583
|
+
});
|
|
1584
|
+
var RenameConflictSchema = Type.Object({ line: Int, column: Int, reason: Str });
|
|
1585
|
+
var RenameEditSchema = Type.Object({
|
|
1586
|
+
line: Int,
|
|
1587
|
+
start_column: Int,
|
|
1588
|
+
end_column: Int,
|
|
1589
|
+
start_byte: Int,
|
|
1590
|
+
end_byte: Int,
|
|
1591
|
+
occurrence: Str,
|
|
1592
|
+
line_hash: Str,
|
|
1593
|
+
text: Str
|
|
1594
|
+
});
|
|
1595
|
+
var Conflicts = Type.Array(RenameConflictSchema, { default: [] });
|
|
1596
|
+
var Edits = Type.Array(RenameEditSchema, { default: [] });
|
|
1597
|
+
var RenameFileOutputSchema = Type.Object({
|
|
1598
|
+
file: Str,
|
|
1599
|
+
language: Str,
|
|
1600
|
+
engine: OptStr,
|
|
1601
|
+
file_hash: Str,
|
|
1602
|
+
conflicts: Conflicts,
|
|
1603
|
+
edits: Edits
|
|
1604
|
+
});
|
|
1605
|
+
var RenameOutputSchema = Type.Object({
|
|
1606
|
+
file: Str,
|
|
1607
|
+
language: Str,
|
|
1608
|
+
engine: OptStr,
|
|
1609
|
+
file_hash: Str,
|
|
1610
|
+
old_name: Str,
|
|
1611
|
+
new_name: Str,
|
|
1612
|
+
plan_hash: Str,
|
|
1613
|
+
applied: Bool,
|
|
1614
|
+
conflicts: Conflicts,
|
|
1615
|
+
edits: Edits,
|
|
1616
|
+
others: Type.Array(RenameFileOutputSchema, { default: [] })
|
|
1617
|
+
});
|
|
1618
|
+
var DefEnvelope = Type.Object({
|
|
1619
|
+
locations: Type.Array(Type.Object({
|
|
1620
|
+
file: Str,
|
|
1621
|
+
line: Int,
|
|
1622
|
+
column: Int,
|
|
1623
|
+
line_hash: Str,
|
|
1624
|
+
text: Str,
|
|
1625
|
+
kind: OptStr,
|
|
1626
|
+
name: OptStr,
|
|
1627
|
+
qualified_name: OptStr
|
|
1628
|
+
}))
|
|
1629
|
+
});
|
|
1041
1630
|
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
|
-
};
|
|
1631
|
+
return parse2(ReadOutput, value, "read output");
|
|
1073
1632
|
}
|
|
1074
1633
|
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
|
-
};
|
|
1634
|
+
return parse2(MapOutput, value, "map output");
|
|
1102
1635
|
}
|
|
1103
1636
|
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
|
-
});
|
|
1637
|
+
const envelope = parse2(SearchEnvelope, value, "search output");
|
|
1638
|
+
const results = envelope.results;
|
|
1639
|
+
if (envelope.returned_matches !== undefined)
|
|
1640
|
+
results.returned_matches = envelope.returned_matches;
|
|
1641
|
+
if (envelope.truncated !== undefined)
|
|
1642
|
+
results.truncated = envelope.truncated;
|
|
1643
|
+
if (envelope.byte_basis !== undefined)
|
|
1644
|
+
results.byte_basis = envelope.byte_basis;
|
|
1645
|
+
return results;
|
|
1149
1646
|
}
|
|
1150
1647
|
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");
|
|
1648
|
+
return parse2(RefsEnvelope, value, "refs output").references.map(({ symbol, ...reference }) => ({
|
|
1649
|
+
...reference,
|
|
1650
|
+
enclosingSymbol: symbol?.qualified_name
|
|
1651
|
+
}));
|
|
1176
1652
|
}
|
|
1177
1653
|
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");
|
|
1654
|
+
const output = parse2(CheckEnvelope, value, "check output");
|
|
1183
1655
|
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
|
-
})
|
|
1656
|
+
file_hash: output.file_hash,
|
|
1657
|
+
errorCount: output.error_count,
|
|
1658
|
+
missingCount: output.missing_count,
|
|
1659
|
+
diagnostics: output.diagnostics
|
|
1197
1660
|
};
|
|
1198
1661
|
}
|
|
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
1662
|
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
|
-
};
|
|
1663
|
+
const output = value ?? {};
|
|
1664
|
+
if (output.type === "application/pdf") {
|
|
1665
|
+
return { kind: "pdf", ...parse2(PdfDetectionInput, value, "detect output") };
|
|
1240
1666
|
}
|
|
1241
1667
|
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
|
-
};
|
|
1668
|
+
return { kind: "image", ...parse2(ImageDetectionInput, value, "detect output") };
|
|
1257
1669
|
}
|
|
1258
1670
|
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
|
-
};
|
|
1671
|
+
return { kind: "source", ...parse2(SourceDetectionInput, value, "detect output") };
|
|
1269
1672
|
}
|
|
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
|
-
};
|
|
1673
|
+
return { kind: "text", ...parse2(TextDetectionInput, value, "detect output") };
|
|
1314
1674
|
}
|
|
1315
1675
|
function parseImageContent(value) {
|
|
1316
1676
|
const detection = parseDetectOutput(value);
|
|
1317
1677
|
if (detection.kind !== "image")
|
|
1318
|
-
throw new Error("invalid readseek image content");
|
|
1678
|
+
throw new Error("invalid readseek image content: expected an image");
|
|
1319
1679
|
return detection;
|
|
1320
1680
|
}
|
|
1321
1681
|
function parseTextContent(value) {
|
|
1682
|
+
const output = value ?? {};
|
|
1683
|
+
if (output.hashlines === undefined || output.start_line !== undefined)
|
|
1684
|
+
return parseReadOutput(value);
|
|
1685
|
+
const spanned = parse2(UnspannedContent, value, "content");
|
|
1686
|
+
return {
|
|
1687
|
+
...spanned,
|
|
1688
|
+
start_line: spanned.hashlines[0]?.line ?? 1,
|
|
1689
|
+
end_line: spanned.hashlines[spanned.hashlines.length - 1]?.line ?? 0
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
function parseDocumentCache(value) {
|
|
1693
|
+
return parse2(DocumentCache, value, "document cache");
|
|
1694
|
+
}
|
|
1695
|
+
function parseIdentifyOutput(value) {
|
|
1696
|
+
return parse2(IdentifyEnvelope, value, "identify output");
|
|
1697
|
+
}
|
|
1698
|
+
function parsePdfInspectMetadata(value) {
|
|
1322
1699
|
if (!value || typeof value !== "object")
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1700
|
+
return;
|
|
1701
|
+
if (value.format !== "pdf")
|
|
1702
|
+
return;
|
|
1703
|
+
const document = parse2(PdfInspectMetadata, value, "inspect metadata");
|
|
1704
|
+
return { kind: "pdf", type: "application/pdf", file: document.source, format: "pdf", pages: document.pages };
|
|
1705
|
+
}
|
|
1706
|
+
function parseEditOutput(value) {
|
|
1707
|
+
return parse2(EditOutputSchema, value, "edit output");
|
|
1708
|
+
}
|
|
1709
|
+
function parseRenameOutput(value) {
|
|
1710
|
+
return parse2(RenameOutputSchema, value, "rename output");
|
|
1711
|
+
}
|
|
1712
|
+
function parseDefOutput(value) {
|
|
1713
|
+
return parse2(DefEnvelope, value, "def output").locations;
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
// ../readseek-api/src/client.ts
|
|
1717
|
+
function classifyReadSeekFailure(err) {
|
|
1718
|
+
const failure = err;
|
|
1719
|
+
const message = String(failure?.message ?? err);
|
|
1720
|
+
const missing = failure?.code === "ENOENT" || /Cannot find package|Cannot find module|no such file/i.test(message);
|
|
1721
|
+
if (missing) {
|
|
1722
|
+
return { code: "readseek-not-installed", message, hint: "Run npm install to install @jarkkojs/readseek." };
|
|
1338
1723
|
}
|
|
1339
|
-
return
|
|
1724
|
+
return { code: "readseek-execution-error", message };
|
|
1340
1725
|
}
|
|
1341
|
-
function
|
|
1726
|
+
function parseSourceInspectEnvelope(value) {
|
|
1342
1727
|
if (!value || typeof value !== "object")
|
|
1343
|
-
throw new Error("invalid readseek
|
|
1728
|
+
throw new Error("invalid readseek inspect output");
|
|
1344
1729
|
const output = value;
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
metadata: parseDetectOutput(output.metadata)
|
|
1349
|
-
};
|
|
1730
|
+
const result = {};
|
|
1731
|
+
if (output.metadata !== undefined)
|
|
1732
|
+
result.metadata = parseDetectOutput(output.metadata);
|
|
1350
1733
|
if (output.cache !== undefined)
|
|
1351
1734
|
result.cache = parseDocumentCache(output.cache);
|
|
1352
1735
|
if (output.content !== undefined)
|
|
@@ -1359,224 +1742,104 @@ function parseDigestEnvelope(value) {
|
|
|
1359
1742
|
result.identity = parseIdentifyOutput(output.identity);
|
|
1360
1743
|
return result;
|
|
1361
1744
|
}
|
|
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
1745
|
function createReadSeekClient(runner) {
|
|
1471
|
-
function
|
|
1472
|
-
return
|
|
1746
|
+
function inspectArgs(target, options) {
|
|
1747
|
+
return buildInspectArgs({
|
|
1473
1748
|
path: target,
|
|
1749
|
+
selector: options.selector,
|
|
1474
1750
|
select: options.select ?? "content",
|
|
1475
|
-
|
|
1751
|
+
query: options.query,
|
|
1476
1752
|
end: options.end,
|
|
1477
1753
|
limit: options.limit,
|
|
1478
1754
|
depth: options.depth,
|
|
1479
1755
|
language: options.language,
|
|
1480
1756
|
visionMode: options.visionMode,
|
|
1481
1757
|
visionLevel: options.visionLevel,
|
|
1482
|
-
stdinName: options.
|
|
1758
|
+
stdinName: options.stdinName
|
|
1483
1759
|
});
|
|
1484
1760
|
}
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
const runOptions = {
|
|
1761
|
+
function inspectRunOptions(options) {
|
|
1762
|
+
return {
|
|
1488
1763
|
signal: options.signal,
|
|
1489
1764
|
stdin: options.stdin,
|
|
1490
1765
|
timeoutMs: options.timeoutMs,
|
|
1491
1766
|
vision: options.vision
|
|
1492
1767
|
};
|
|
1493
|
-
return runner.json(args, runOptions);
|
|
1494
1768
|
}
|
|
1495
|
-
async function
|
|
1496
|
-
return
|
|
1769
|
+
async function inspect(target, options = {}) {
|
|
1770
|
+
return runner.kdl(inspectArgs(target, options), inspectRunOptions(options));
|
|
1771
|
+
}
|
|
1772
|
+
async function inspectText(target, options = {}) {
|
|
1773
|
+
return runner.text(inspectArgs(target, options), inspectRunOptions(options));
|
|
1774
|
+
}
|
|
1775
|
+
async function inspectSource(target, options = {}) {
|
|
1776
|
+
return parseSourceInspectEnvelope(await inspect(target, options));
|
|
1497
1777
|
}
|
|
1498
|
-
function
|
|
1778
|
+
function requireInspectMetadata(envelope, field) {
|
|
1779
|
+
if (envelope.metadata === undefined)
|
|
1780
|
+
throw new Error(`invalid readseek ${field}: missing metadata`);
|
|
1781
|
+
return envelope.metadata;
|
|
1782
|
+
}
|
|
1783
|
+
function requireInspectContent(envelope, field) {
|
|
1499
1784
|
if (envelope.content === undefined)
|
|
1500
1785
|
throw new Error(`invalid readseek ${field}: missing content`);
|
|
1501
1786
|
return envelope.content;
|
|
1502
1787
|
}
|
|
1503
|
-
async function
|
|
1504
|
-
const
|
|
1505
|
-
select: "content",
|
|
1506
|
-
|
|
1788
|
+
async function inspectContent(filePath, options = {}) {
|
|
1789
|
+
const inspected = await inspect(filePath, {
|
|
1790
|
+
select: ["metadata", "content"],
|
|
1791
|
+
selector: options.startLine !== undefined ? buildLineLocation(options.startLine) : undefined,
|
|
1507
1792
|
end: options.endLine,
|
|
1508
1793
|
visionMode: options.visionMode,
|
|
1509
1794
|
signal: options.signal,
|
|
1510
1795
|
vision: options.vision
|
|
1511
1796
|
});
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
const
|
|
1516
|
-
|
|
1517
|
-
|
|
1797
|
+
const pdf = parsePdfInspectMetadata(inspected);
|
|
1798
|
+
if (pdf)
|
|
1799
|
+
return { metadata: pdf };
|
|
1800
|
+
const envelope = parseSourceInspectEnvelope(inspected);
|
|
1801
|
+
const metadata = requireInspectMetadata(envelope, "content");
|
|
1802
|
+
const content = requireInspectContent(envelope, "content");
|
|
1803
|
+
if (metadata.kind === "image") {
|
|
1804
|
+
return { metadata, image: parseImageContent(content) };
|
|
1518
1805
|
}
|
|
1519
|
-
return { metadata
|
|
1806
|
+
return { metadata, text: parseTextContent(content) };
|
|
1520
1807
|
}
|
|
1521
1808
|
return {
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
});
|
|
1533
|
-
return runner.text(args, {
|
|
1809
|
+
inspect,
|
|
1810
|
+
inspectText,
|
|
1811
|
+
inspectContent,
|
|
1812
|
+
async extract(target, selector, output, options = {}) {
|
|
1813
|
+
await runner.text(buildExtractArgs({
|
|
1814
|
+
path: target,
|
|
1815
|
+
selector,
|
|
1816
|
+
output,
|
|
1817
|
+
stdinName: options.stdinName
|
|
1818
|
+
}), {
|
|
1534
1819
|
signal: options.signal,
|
|
1535
|
-
|
|
1820
|
+
stdin: options.stdin,
|
|
1821
|
+
timeoutMs: options.timeoutMs,
|
|
1822
|
+
cancelable: options.cancelable
|
|
1536
1823
|
});
|
|
1537
1824
|
},
|
|
1538
|
-
digestRaw(target, options = {}) {
|
|
1539
|
-
return runDigestRaw(target, options);
|
|
1540
|
-
},
|
|
1541
|
-
digest,
|
|
1542
|
-
digestContent,
|
|
1543
1825
|
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}`);
|
|
1826
|
+
if (modes.length === 0)
|
|
1827
|
+
throw new Error(`readseek returned no image analysis for ${filePath}`);
|
|
1828
|
+
const inspected = await inspectContent(filePath, {
|
|
1829
|
+
visionMode: modes.length > 1 ? "all" : modes[0],
|
|
1830
|
+
signal: options.signal,
|
|
1831
|
+
vision: true
|
|
1832
|
+
});
|
|
1833
|
+
if (!inspected.image)
|
|
1834
|
+
throw new Error(`readseek returned no image analysis for ${filePath}`);
|
|
1835
|
+
return inspected.image;
|
|
1573
1836
|
},
|
|
1574
1837
|
async preparedImage(filePath, options = {}) {
|
|
1575
|
-
const
|
|
1838
|
+
const inspected = await inspectContent(filePath, {
|
|
1576
1839
|
visionMode: "none",
|
|
1577
1840
|
signal: options.signal
|
|
1578
1841
|
});
|
|
1579
|
-
const output =
|
|
1842
|
+
const output = inspected.image;
|
|
1580
1843
|
if (!output || output.encoding !== "base64" || output.data === undefined || output.mime === undefined) {
|
|
1581
1844
|
throw new Error(`readseek returned no prepared image for ${filePath}`);
|
|
1582
1845
|
}
|
|
@@ -1588,11 +1851,9 @@ function createReadSeekClient(runner) {
|
|
|
1588
1851
|
pattern,
|
|
1589
1852
|
language: options.language,
|
|
1590
1853
|
limit: options.limit,
|
|
1591
|
-
|
|
1592
|
-
others: options.others,
|
|
1593
|
-
ignored: options.ignored
|
|
1854
|
+
gitScope: options.gitScope
|
|
1594
1855
|
});
|
|
1595
|
-
return parseSearchOutput(await runner.
|
|
1856
|
+
return parseSearchOutput(await runner.kdl(args, { signal: options.signal }));
|
|
1596
1857
|
},
|
|
1597
1858
|
async refs(target, options = {}) {
|
|
1598
1859
|
const args = buildRefsArgs({
|
|
@@ -1602,26 +1863,21 @@ function createReadSeekClient(runner) {
|
|
|
1602
1863
|
line: options.line,
|
|
1603
1864
|
column: options.column,
|
|
1604
1865
|
language: options.language,
|
|
1605
|
-
|
|
1606
|
-
others: options.others,
|
|
1607
|
-
ignored: options.ignored
|
|
1866
|
+
gitScope: options.gitScope
|
|
1608
1867
|
});
|
|
1609
|
-
return parseRefsOutput(await runner.
|
|
1868
|
+
return parseRefsOutput(await runner.kdl(args, { signal: options.signal }));
|
|
1610
1869
|
},
|
|
1611
1870
|
async def(target, options) {
|
|
1612
1871
|
const args = buildDefArgs({
|
|
1613
1872
|
path: target,
|
|
1614
1873
|
name: options.name,
|
|
1615
|
-
format: "plain",
|
|
1616
1874
|
language: options.language,
|
|
1617
|
-
|
|
1618
|
-
others: options.others,
|
|
1619
|
-
ignored: options.ignored
|
|
1875
|
+
gitScope: options.gitScope
|
|
1620
1876
|
});
|
|
1621
|
-
return
|
|
1877
|
+
return parseDefOutput(await runner.kdl(args, { signal: options.signal }));
|
|
1622
1878
|
},
|
|
1623
1879
|
async check(filePath, content, options = {}) {
|
|
1624
|
-
const envelope = await
|
|
1880
|
+
const envelope = await inspectSource("-", {
|
|
1625
1881
|
select: "diagnostics",
|
|
1626
1882
|
stdin: content,
|
|
1627
1883
|
stdinName: filePath,
|
|
@@ -1631,12 +1887,12 @@ function createReadSeekClient(runner) {
|
|
|
1631
1887
|
throw new Error("invalid readseek diagnostics: missing diagnostics facet");
|
|
1632
1888
|
return envelope.diagnostics;
|
|
1633
1889
|
},
|
|
1634
|
-
async identify(filePath, content, options
|
|
1635
|
-
const envelope = await
|
|
1890
|
+
async identify(filePath, content, options) {
|
|
1891
|
+
const envelope = await inspectSource("-", {
|
|
1636
1892
|
select: "identity",
|
|
1637
1893
|
stdin: content,
|
|
1638
1894
|
stdinName: filePath,
|
|
1639
|
-
|
|
1895
|
+
selector: buildLineLocation(options.line, options.column),
|
|
1640
1896
|
language: options.language,
|
|
1641
1897
|
signal: options.signal
|
|
1642
1898
|
});
|
|
@@ -1645,8 +1901,11 @@ function createReadSeekClient(runner) {
|
|
|
1645
1901
|
return envelope.identity;
|
|
1646
1902
|
},
|
|
1647
1903
|
async detect(filePath, options = {}) {
|
|
1648
|
-
const
|
|
1649
|
-
|
|
1904
|
+
const inspected = await inspect(filePath, { select: "metadata", signal: options.signal });
|
|
1905
|
+
const pdf = parsePdfInspectMetadata(inspected);
|
|
1906
|
+
if (pdf)
|
|
1907
|
+
return pdf;
|
|
1908
|
+
return requireInspectMetadata(parseSourceInspectEnvelope(inspected), "metadata");
|
|
1650
1909
|
},
|
|
1651
1910
|
async edit(filePath, edits, options = {}) {
|
|
1652
1911
|
const args = buildEditArgs({
|
|
@@ -1656,9 +1915,9 @@ function createReadSeekClient(runner) {
|
|
|
1656
1915
|
language: options.language,
|
|
1657
1916
|
requestFromStdin: true
|
|
1658
1917
|
});
|
|
1659
|
-
return parseEditOutput(await runner.
|
|
1918
|
+
return parseEditOutput(await runner.kdl(args, {
|
|
1660
1919
|
signal: options.signal,
|
|
1661
|
-
stdin:
|
|
1920
|
+
stdin: serializeEditPlan(edits),
|
|
1662
1921
|
timeoutMs: options.timeoutMs,
|
|
1663
1922
|
cancelable: options.cancelable
|
|
1664
1923
|
}));
|
|
@@ -1671,12 +1930,11 @@ function createReadSeekClient(runner) {
|
|
|
1671
1930
|
column: options.column,
|
|
1672
1931
|
workspace: options.workspace,
|
|
1673
1932
|
apply: options.apply,
|
|
1933
|
+
planHash: options.planHash,
|
|
1674
1934
|
language: options.language,
|
|
1675
|
-
|
|
1676
|
-
others: options.others,
|
|
1677
|
-
ignored: options.ignored
|
|
1935
|
+
gitScope: options.gitScope
|
|
1678
1936
|
});
|
|
1679
|
-
return parseRenameOutput(await runner.
|
|
1937
|
+
return parseRenameOutput(await runner.kdl(args, { signal: options.signal }));
|
|
1680
1938
|
}
|
|
1681
1939
|
};
|
|
1682
1940
|
}
|
|
@@ -2157,7 +2415,7 @@ function formatReplaceHint(edits, noopEdits) {
|
|
|
2157
2415
|
return;
|
|
2158
2416
|
if (counts.replace !== counts.total)
|
|
2159
2417
|
return;
|
|
2160
|
-
return "[info: this edit used replace (unverified). For safer future edits, prefer set_line/replace_lines with an anchor from
|
|
2418
|
+
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
2419
|
}
|
|
2162
2420
|
function buildEditOutput(input) {
|
|
2163
2421
|
const summary = `Updated ${input.displayPath}`;
|
|
@@ -2204,20 +2462,20 @@ function classifyEdit(oldContent, newContent) {
|
|
|
2204
2462
|
const newLines = newContent.split(`
|
|
2205
2463
|
`);
|
|
2206
2464
|
if (oldLines.length === newLines.length) {
|
|
2207
|
-
let
|
|
2208
|
-
let
|
|
2465
|
+
let hasWhitespaceChange = false;
|
|
2466
|
+
let hasSemanticChange = false;
|
|
2209
2467
|
for (let i = 0;i < oldLines.length; i++) {
|
|
2210
2468
|
if (oldLines[i] === newLines[i])
|
|
2211
2469
|
continue;
|
|
2212
2470
|
if (oldLines[i].trim() === newLines[i].trim()) {
|
|
2213
|
-
|
|
2471
|
+
hasWhitespaceChange = true;
|
|
2214
2472
|
} else {
|
|
2215
|
-
|
|
2473
|
+
hasSemanticChange = true;
|
|
2216
2474
|
}
|
|
2217
2475
|
}
|
|
2218
|
-
if (
|
|
2476
|
+
if (hasSemanticChange && hasWhitespaceChange)
|
|
2219
2477
|
return { classification: "mixed" };
|
|
2220
|
-
if (
|
|
2478
|
+
if (hasWhitespaceChange)
|
|
2221
2479
|
return { classification: "whitespace-only" };
|
|
2222
2480
|
return { classification: "semantic" };
|
|
2223
2481
|
}
|
|
@@ -2257,7 +2515,7 @@ import { readFileSync, statSync as statSync2 } from "node:fs";
|
|
|
2257
2515
|
import { homedir } from "node:os";
|
|
2258
2516
|
import { join } from "node:path";
|
|
2259
2517
|
var READSEEK_TOOL_REPLACEMENTS = {
|
|
2260
|
-
read: "
|
|
2518
|
+
read: "readSeek_inspect",
|
|
2261
2519
|
edit: "readSeek_edit",
|
|
2262
2520
|
grep: "readSeek_grep",
|
|
2263
2521
|
write: "readSeek_write"
|
|
@@ -2270,42 +2528,36 @@ var DEFAULT_DISPLAY_MODES = {
|
|
|
2270
2528
|
edit: "expanded",
|
|
2271
2529
|
write: "expanded"
|
|
2272
2530
|
};
|
|
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
2531
|
function isRecord(value) {
|
|
2280
2532
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2281
2533
|
}
|
|
2282
|
-
function invalid(source,
|
|
2283
|
-
return { source, path
|
|
2534
|
+
function invalid(source, path) {
|
|
2535
|
+
return { source, path, message: `Invalid readseek setting at ${path}` };
|
|
2284
2536
|
}
|
|
2285
2537
|
function warnUnknownKeys(raw, known, prefix, source, warnings) {
|
|
2286
2538
|
for (const key of Object.keys(raw)) {
|
|
2287
2539
|
if (known.includes(key))
|
|
2288
2540
|
continue;
|
|
2289
|
-
const
|
|
2290
|
-
warnings.push({ source, path
|
|
2541
|
+
const path = `${prefix}.${key}`;
|
|
2542
|
+
warnings.push({ source, path, message: `Unknown readseek setting at ${path}` });
|
|
2291
2543
|
}
|
|
2292
2544
|
}
|
|
2293
|
-
function readPositive(raw, key,
|
|
2545
|
+
function readPositive(raw, key, path, source, warnings) {
|
|
2294
2546
|
if (!(key in raw))
|
|
2295
2547
|
return;
|
|
2296
2548
|
const val = raw[key];
|
|
2297
2549
|
if (typeof val === "number" && Number.isSafeInteger(val) && val > 0)
|
|
2298
2550
|
return val;
|
|
2299
|
-
warnings.push(invalid(source,
|
|
2551
|
+
warnings.push(invalid(source, path));
|
|
2300
2552
|
return;
|
|
2301
2553
|
}
|
|
2302
|
-
function readEnum(raw, key, values,
|
|
2554
|
+
function readEnum(raw, key, values, path, source, warnings) {
|
|
2303
2555
|
if (!(key in raw))
|
|
2304
2556
|
return;
|
|
2305
2557
|
const val = raw[key];
|
|
2306
2558
|
if (typeof val === "string" && values.includes(val))
|
|
2307
2559
|
return val;
|
|
2308
|
-
warnings.push(invalid(source,
|
|
2560
|
+
warnings.push(invalid(source, path));
|
|
2309
2561
|
return;
|
|
2310
2562
|
}
|
|
2311
2563
|
function readImageMode(raw, source, warnings) {
|
|
@@ -2409,31 +2661,31 @@ function validateSettings(raw, source) {
|
|
|
2409
2661
|
return { settings, warnings };
|
|
2410
2662
|
}
|
|
2411
2663
|
var settingsFileCache = new Map;
|
|
2412
|
-
function statFileOrNull(
|
|
2664
|
+
function statFileOrNull(path) {
|
|
2413
2665
|
try {
|
|
2414
|
-
return statSync2(
|
|
2666
|
+
return statSync2(path);
|
|
2415
2667
|
} catch {
|
|
2416
2668
|
return null;
|
|
2417
2669
|
}
|
|
2418
2670
|
}
|
|
2419
|
-
function readSettingsFile(
|
|
2420
|
-
const stats = statFileOrNull(
|
|
2671
|
+
function readSettingsFile(path) {
|
|
2672
|
+
const stats = statFileOrNull(path);
|
|
2421
2673
|
if (!stats) {
|
|
2422
|
-
settingsFileCache.delete(
|
|
2674
|
+
settingsFileCache.delete(path);
|
|
2423
2675
|
return { settings: {}, warnings: [] };
|
|
2424
2676
|
}
|
|
2425
|
-
const cached = settingsFileCache.get(
|
|
2677
|
+
const cached = settingsFileCache.get(path);
|
|
2426
2678
|
if (cached && cached.mtimeMs === stats.mtimeMs && cached.size === stats.size)
|
|
2427
2679
|
return cached.result;
|
|
2428
2680
|
let result;
|
|
2429
2681
|
try {
|
|
2430
|
-
const text = readFileSync(
|
|
2431
|
-
result = validateSettings(JSON.parse(text),
|
|
2682
|
+
const text = readFileSync(path, "utf8");
|
|
2683
|
+
result = validateSettings(JSON.parse(text), path);
|
|
2432
2684
|
} catch (error) {
|
|
2433
2685
|
const message = error instanceof Error ? error.message : String(error);
|
|
2434
|
-
result = { settings: {}, warnings: [{ source:
|
|
2686
|
+
result = { settings: {}, warnings: [{ source: path, message: `Invalid JSON: ${message}` }] };
|
|
2435
2687
|
}
|
|
2436
|
-
settingsFileCache.set(
|
|
2688
|
+
settingsFileCache.set(path, { mtimeMs: stats.mtimeMs, size: stats.size, result });
|
|
2437
2689
|
return result;
|
|
2438
2690
|
}
|
|
2439
2691
|
function mergeSettings(base, override) {
|
|
@@ -2447,8 +2699,8 @@ function mergeSettings(base, override) {
|
|
|
2447
2699
|
return settings;
|
|
2448
2700
|
}
|
|
2449
2701
|
function resolveReadSeekJsonSettings() {
|
|
2450
|
-
const globalResult = readSettingsFile(
|
|
2451
|
-
const projectResult = readSettingsFile(
|
|
2702
|
+
const globalResult = readSettingsFile(join(homedir(), ".pi/agent/settings.json"));
|
|
2703
|
+
const projectResult = readSettingsFile(join(process.cwd(), ".pi/settings.json"));
|
|
2452
2704
|
return {
|
|
2453
2705
|
settings: mergeSettings(globalResult.settings, projectResult.settings),
|
|
2454
2706
|
warnings: [...globalResult.warnings, ...projectResult.warnings]
|
|
@@ -2496,21 +2748,13 @@ var DEFAULT_READSEEK_VISION_TIMEOUT_MS = 30 * 60000;
|
|
|
2496
2748
|
function readSeekTimeoutMs() {
|
|
2497
2749
|
return resolveReadSeekTimeoutMs() ?? DEFAULT_READSEEK_TIMEOUT_MS;
|
|
2498
2750
|
}
|
|
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
2751
|
async function ensureDefaultReadSeekDir() {
|
|
2508
2752
|
const dir = defaultReadSeekDir();
|
|
2509
2753
|
if (!dir)
|
|
2510
2754
|
return null;
|
|
2511
2755
|
if (directoryExists(dir))
|
|
2512
2756
|
return dir;
|
|
2513
|
-
defaultReadSeekDirInit ??=
|
|
2757
|
+
defaultReadSeekDirInit ??= runReadSeekNodeText(readSeekBinaryPath(), ["--readseek-dir", dir, "init"], { timeoutMs: readSeekTimeoutMs() }).then(() => directoryExists(dir) ? dir : null).catch(() => null).finally(() => {
|
|
2514
2758
|
defaultReadSeekDirInit = undefined;
|
|
2515
2759
|
});
|
|
2516
2760
|
return defaultReadSeekDirInit;
|
|
@@ -2532,17 +2776,6 @@ async function readSeekInvocationArgs(args) {
|
|
|
2532
2776
|
const readSeekDir = await ensureDefaultReadSeekDir();
|
|
2533
2777
|
return readSeekDir ? ["--readseek-dir", readSeekDir, ...args] : [...args];
|
|
2534
2778
|
}
|
|
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
2779
|
var visionInvocationTail = Promise.resolve();
|
|
2547
2780
|
async function runReadSeekVisionRaw(args, options = {}) {
|
|
2548
2781
|
let release;
|
|
@@ -2573,40 +2806,38 @@ async function runReadSeekVisionRaw(args, options = {}) {
|
|
|
2573
2806
|
await predecessor;
|
|
2574
2807
|
}
|
|
2575
2808
|
signal?.throwIfAborted();
|
|
2576
|
-
return await
|
|
2577
|
-
|
|
2578
|
-
|
|
2809
|
+
return await runReadSeekNodeText(readSeekBinaryPath(), await readSeekInvocationArgs(args), {
|
|
2810
|
+
signal: options.signal,
|
|
2811
|
+
stdin: options.stdin,
|
|
2812
|
+
timeoutMs: options.timeoutMs ?? resolveReadSeekTimeoutMs() ?? DEFAULT_READSEEK_VISION_TIMEOUT_MS,
|
|
2813
|
+
cancelable: options.cancelable
|
|
2579
2814
|
});
|
|
2580
2815
|
} finally {
|
|
2581
2816
|
release();
|
|
2582
2817
|
}
|
|
2583
2818
|
}
|
|
2584
|
-
async function runReadSeekVision(args, options = {}) {
|
|
2585
|
-
return parseReadSeekJson(await runReadSeekVisionRaw(args, options));
|
|
2586
|
-
}
|
|
2587
2819
|
var runner = {
|
|
2588
|
-
text: (args, options = {}) => options.vision ? runReadSeekVisionRaw(args, options) :
|
|
2589
|
-
|
|
2820
|
+
text: async (args, options = {}) => options.vision ? runReadSeekVisionRaw(args, options) : runReadSeekNodeText(readSeekBinaryPath(), await readSeekInvocationArgs(args), {
|
|
2821
|
+
signal: options.signal,
|
|
2822
|
+
stdin: options.stdin,
|
|
2823
|
+
timeoutMs: options.timeoutMs ?? readSeekTimeoutMs(),
|
|
2824
|
+
cancelable: options.cancelable
|
|
2825
|
+
}),
|
|
2826
|
+
kdl: async (args, options = {}) => parseReadSeekKdl(await runner.text(args, options))
|
|
2590
2827
|
};
|
|
2591
2828
|
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;
|
|
2829
|
+
var readSeekInspect = client.inspect;
|
|
2830
|
+
var readSeekExtract = client.extract;
|
|
2598
2831
|
var readSeekSearch = client.search;
|
|
2599
2832
|
var readSeekRefs = client.refs;
|
|
2600
2833
|
var readSeekDef = client.def;
|
|
2601
2834
|
var readSeekCheck = client.check;
|
|
2602
|
-
var readSeekIdentify = client.identify;
|
|
2603
|
-
var readSeekDetect = client.detect;
|
|
2604
2835
|
var readSeekEdit = client.edit;
|
|
2605
2836
|
var readSeekRename = client.rename;
|
|
2606
2837
|
async function readSeekMap(filePath, options = {}) {
|
|
2607
|
-
const envelope = await client.
|
|
2838
|
+
const envelope = await client.inspect(filePath, {
|
|
2608
2839
|
select: "map",
|
|
2609
|
-
depth:
|
|
2840
|
+
depth: DEFAULT_INSPECT_MAP_DEPTH,
|
|
2610
2841
|
signal: options.signal
|
|
2611
2842
|
});
|
|
2612
2843
|
const map = envelope.map;
|
|
@@ -2672,8 +2903,8 @@ var LANGUAGE_BY_EXTENSION = new Map([
|
|
|
2672
2903
|
[".rs", "rust"],
|
|
2673
2904
|
[".java", "java"]
|
|
2674
2905
|
]);
|
|
2675
|
-
function inferLanguage(
|
|
2676
|
-
const extensionMatch =
|
|
2906
|
+
function inferLanguage(path) {
|
|
2907
|
+
const extensionMatch = path.match(/\.[^.\/\\]+$/);
|
|
2677
2908
|
if (!extensionMatch)
|
|
2678
2909
|
return;
|
|
2679
2910
|
return LANGUAGE_BY_EXTENSION.get(extensionMatch[0].toLowerCase());
|
|
@@ -2719,10 +2950,10 @@ function parseCompactDiffEntries(diff, oldContent, newContent) {
|
|
|
2719
2950
|
`);
|
|
2720
2951
|
const compactDelete = diff.match(/^(\d+):[0-9a-f]{6}\|(.*) → \[deleted\]$/);
|
|
2721
2952
|
if (compactDelete) {
|
|
2722
|
-
const
|
|
2953
|
+
const oldLine = Number(compactDelete[1]);
|
|
2723
2954
|
const text = compactDelete[2] ?? "";
|
|
2724
|
-
if (oldLines[
|
|
2725
|
-
return [{ kind: "remove", oldLine
|
|
2955
|
+
if (oldLines[oldLine - 1] === text)
|
|
2956
|
+
return [{ kind: "remove", oldLine, text }];
|
|
2726
2957
|
}
|
|
2727
2958
|
const compactPrefix = diff.match(/^(\d+):[0-9a-f]{6}\|/);
|
|
2728
2959
|
if (!compactPrefix)
|
|
@@ -2762,9 +2993,9 @@ function longestCommonSubsequence(a, b) {
|
|
|
2762
2993
|
const rows = a.length + 1;
|
|
2763
2994
|
const cols = b.length + 1;
|
|
2764
2995
|
const table = Array.from({ length: rows }, () => Array(cols).fill(0));
|
|
2765
|
-
for (let
|
|
2766
|
-
for (let
|
|
2767
|
-
table[
|
|
2996
|
+
for (let i = a.length - 1;i >= 0; i--) {
|
|
2997
|
+
for (let j = b.length - 1;j >= 0; j--) {
|
|
2998
|
+
table[i][j] = a[i] === b[j] ? table[i + 1][j + 1] + 1 : Math.max(table[i + 1][j], table[i][j + 1]);
|
|
2768
2999
|
}
|
|
2769
3000
|
}
|
|
2770
3001
|
const pairs = [];
|
|
@@ -2882,8 +3113,8 @@ function buildInlineDiffs(entries) {
|
|
|
2882
3113
|
}
|
|
2883
3114
|
return inlineDiffs.length ? inlineDiffs : undefined;
|
|
2884
3115
|
}
|
|
2885
|
-
function finalizeDiffData(
|
|
2886
|
-
const language = inferLanguage(
|
|
3116
|
+
function finalizeDiffData(path, entries, blockRanges) {
|
|
3117
|
+
const language = inferLanguage(path);
|
|
2887
3118
|
const inlineDiffs = buildInlineDiffs(entries);
|
|
2888
3119
|
return {
|
|
2889
3120
|
version: 1,
|
|
@@ -2976,7 +3207,6 @@ function wrapWithHangingIndent(prefix, content, width, options = {}) {
|
|
|
2976
3207
|
const indent = " ".repeat(prefixWidth);
|
|
2977
3208
|
return wrapped.map((line, index) => tint(clampLineToWidth(index === 0 ? prefix + line : indent + line, normalized)));
|
|
2978
3209
|
}
|
|
2979
|
-
var wrappedHashlinesCache = new WeakMap;
|
|
2980
3210
|
function renderPendingResult(pendingLabel, width, theme) {
|
|
2981
3211
|
return new Text(clampLinesToWidth([summaryLine(pendingLabel, { theme, style: "muted" })], width).join(`
|
|
2982
3212
|
`), 0, 0);
|
|
@@ -3107,8 +3337,8 @@ function padRightVisual(line, width) {
|
|
|
3107
3337
|
function tint(theme, entry, text) {
|
|
3108
3338
|
return entry.kind === "add" ? theme.fg("success", text) : entry.kind === "remove" ? theme.fg("error", text) : theme.fg("toolOutput", text);
|
|
3109
3339
|
}
|
|
3110
|
-
function spans(theme,
|
|
3111
|
-
return
|
|
3340
|
+
function spans(theme, spans, fallback) {
|
|
3341
|
+
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
3342
|
}
|
|
3113
3343
|
function inlineText(input, index, entry) {
|
|
3114
3344
|
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 +3467,12 @@ function upsertTextComponent(lastComponent, text) {
|
|
|
3237
3467
|
}
|
|
3238
3468
|
|
|
3239
3469
|
// src/register-tool.ts
|
|
3240
|
-
import { Type } from "@sinclair/typebox";
|
|
3470
|
+
import { Type as Type2 } from "@sinclair/typebox";
|
|
3241
3471
|
function optionalIntOrString(description) {
|
|
3242
|
-
return
|
|
3472
|
+
return Type2.Optional(Type2.Union([Type2.Number({ description }), Type2.String({ description })]));
|
|
3243
3473
|
}
|
|
3244
3474
|
function filePathParam() {
|
|
3245
|
-
return
|
|
3475
|
+
return Type2.String({ description: PARAM_DESCRIPTIONS.path });
|
|
3246
3476
|
}
|
|
3247
3477
|
function registerReadSeekTool(pi, tool) {
|
|
3248
3478
|
pi.registerTool(tool);
|
|
@@ -3250,28 +3480,28 @@ function registerReadSeekTool(pi, tool) {
|
|
|
3250
3480
|
}
|
|
3251
3481
|
|
|
3252
3482
|
// src/edit.ts
|
|
3253
|
-
var hashlineEditItemSchema =
|
|
3254
|
-
set_line:
|
|
3255
|
-
anchor:
|
|
3256
|
-
new_text:
|
|
3483
|
+
var hashlineEditItemSchema = Type3.Object({
|
|
3484
|
+
set_line: Type3.Optional(Type3.Object({
|
|
3485
|
+
anchor: Type3.String({ description: PARAM_DESCRIPTIONS.anchor }),
|
|
3486
|
+
new_text: Type3.String({ description: PARAM_DESCRIPTIONS.newTextLine })
|
|
3257
3487
|
}, { additionalProperties: false, description: PARAM_DESCRIPTIONS.setLine })),
|
|
3258
|
-
replace_lines:
|
|
3259
|
-
start_anchor:
|
|
3260
|
-
end_anchor:
|
|
3261
|
-
new_text:
|
|
3488
|
+
replace_lines: Type3.Optional(Type3.Object({
|
|
3489
|
+
start_anchor: Type3.String({ description: PARAM_DESCRIPTIONS.startAnchor }),
|
|
3490
|
+
end_anchor: Type3.String({ description: PARAM_DESCRIPTIONS.endAnchor }),
|
|
3491
|
+
new_text: Type3.String({ description: PARAM_DESCRIPTIONS.newTextRange })
|
|
3262
3492
|
}, { additionalProperties: false, description: PARAM_DESCRIPTIONS.replaceLines })),
|
|
3263
|
-
insert_after:
|
|
3264
|
-
anchor:
|
|
3265
|
-
new_text:
|
|
3493
|
+
insert_after: Type3.Optional(Type3.Object({
|
|
3494
|
+
anchor: Type3.String({ description: PARAM_DESCRIPTIONS.insertAnchor }),
|
|
3495
|
+
new_text: Type3.String({ description: PARAM_DESCRIPTIONS.newTextInsert })
|
|
3266
3496
|
}, { additionalProperties: false, description: PARAM_DESCRIPTIONS.insertAfter })),
|
|
3267
|
-
replace:
|
|
3268
|
-
old_text:
|
|
3269
|
-
new_text:
|
|
3270
|
-
all:
|
|
3497
|
+
replace: Type3.Optional(Type3.Object({
|
|
3498
|
+
old_text: Type3.String({ description: PARAM_DESCRIPTIONS.oldText }),
|
|
3499
|
+
new_text: Type3.String({ description: PARAM_DESCRIPTIONS.newText }),
|
|
3500
|
+
all: Type3.Optional(Type3.Boolean({ description: PARAM_DESCRIPTIONS.all }))
|
|
3271
3501
|
}, { additionalProperties: false, description: PARAM_DESCRIPTIONS.replace })),
|
|
3272
|
-
replace_symbol:
|
|
3273
|
-
symbol:
|
|
3274
|
-
new_body:
|
|
3502
|
+
replace_symbol: Type3.Optional(Type3.Object({
|
|
3503
|
+
symbol: Type3.String({ description: PARAM_DESCRIPTIONS.symbol }),
|
|
3504
|
+
new_body: Type3.String({ description: PARAM_DESCRIPTIONS.newBody })
|
|
3275
3505
|
}, { additionalProperties: false, description: PARAM_DESCRIPTIONS.replaceSymbol }))
|
|
3276
3506
|
}, {
|
|
3277
3507
|
additionalProperties: false,
|
|
@@ -3279,18 +3509,18 @@ var hashlineEditItemSchema = Type2.Object({
|
|
|
3279
3509
|
maxProperties: 1,
|
|
3280
3510
|
description: PARAM_DESCRIPTIONS.editVariant
|
|
3281
3511
|
});
|
|
3282
|
-
var hashlineEditSchema =
|
|
3512
|
+
var hashlineEditSchema = Type3.Object({
|
|
3283
3513
|
path: filePathParam(),
|
|
3284
|
-
language:
|
|
3285
|
-
edits:
|
|
3514
|
+
language: Type3.Optional(Type3.String({ description: PARAM_DESCRIPTIONS.languageSymbol })),
|
|
3515
|
+
edits: Type3.Array(hashlineEditItemSchema, {
|
|
3286
3516
|
minItems: 1,
|
|
3287
3517
|
description: PARAM_DESCRIPTIONS.edits
|
|
3288
3518
|
}),
|
|
3289
|
-
postEditVerify:
|
|
3519
|
+
postEditVerify: Type3.Optional(Type3.Boolean({
|
|
3290
3520
|
description: PARAM_DESCRIPTIONS.postEditVerify
|
|
3291
3521
|
}))
|
|
3292
3522
|
}, { additionalProperties: false });
|
|
3293
|
-
function buildEditError(
|
|
3523
|
+
function buildEditError(path, code, message, hint, errorDetails) {
|
|
3294
3524
|
return {
|
|
3295
3525
|
content: [{ type: "text", text: message }],
|
|
3296
3526
|
isError: true,
|
|
@@ -3301,7 +3531,7 @@ function buildEditError(path3, code, message, hint, errorDetails) {
|
|
|
3301
3531
|
readSeekValue: {
|
|
3302
3532
|
tool: "edit",
|
|
3303
3533
|
ok: false,
|
|
3304
|
-
path
|
|
3534
|
+
path,
|
|
3305
3535
|
error: buildReadSeekError(code, message, hint, errorDetails)
|
|
3306
3536
|
}
|
|
3307
3537
|
}
|
|
@@ -3315,14 +3545,14 @@ async function executeEdit(opts) {
|
|
|
3315
3545
|
const { params, signal, cwd, wasReadInSession, onFileMutated, syntaxValidate } = opts;
|
|
3316
3546
|
const parsed = params;
|
|
3317
3547
|
const rawPath = parsed.path;
|
|
3318
|
-
const
|
|
3319
|
-
const absolutePath = resolveToCwd2(
|
|
3548
|
+
const path = rawPath.replace(/^@/, "");
|
|
3549
|
+
const absolutePath = resolveToCwd2(path, cwd);
|
|
3320
3550
|
throwIfAborted(signal);
|
|
3321
3551
|
try {
|
|
3322
3552
|
return await withFileMutationQueue(absolutePath, async () => {
|
|
3323
3553
|
throwIfAborted(signal);
|
|
3324
3554
|
if (wasReadInSession && !wasReadInSession(absolutePath)) {
|
|
3325
|
-
const hint = `Call
|
|
3555
|
+
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
3556
|
return buildEditError(absolutePath, "file-not-read", `You must get fresh anchors for ${absolutePath} before editing it. ${hint}`, hint);
|
|
3327
3557
|
}
|
|
3328
3558
|
const parsedEdits = validateExactlyOneEditVariant(parsed.edits ?? []);
|
|
@@ -3347,7 +3577,7 @@ async function executeEdit(opts) {
|
|
|
3347
3577
|
const originalForSyntax = normalizeToLF(stripBom(original).text);
|
|
3348
3578
|
const resultForSyntax = normalizeToLF(stripBom(result).text);
|
|
3349
3579
|
if (!plan.changed) {
|
|
3350
|
-
return buildEditError(absolutePath, "no-op", `No changes made to ${
|
|
3580
|
+
return buildEditError(absolutePath, "no-op", `No changes made to ${path}. The edits produced identical content.`);
|
|
3351
3581
|
}
|
|
3352
3582
|
const syntaxMode = resolveSyntaxValidateMode({ syntaxValidate });
|
|
3353
3583
|
let syntaxWarning;
|
|
@@ -3388,14 +3618,14 @@ async function executeEdit(opts) {
|
|
|
3388
3618
|
try {
|
|
3389
3619
|
verifiedContent = await fsReadFile(absolutePath, "utf-8");
|
|
3390
3620
|
} catch (err) {
|
|
3391
|
-
return buildEditError(absolutePath, "post-edit-verification-read-failed", `Edit write completed but post-edit verification failed: could not read ${
|
|
3621
|
+
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
3622
|
}
|
|
3393
3623
|
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 ${
|
|
3624
|
+
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
3625
|
}
|
|
3396
3626
|
}
|
|
3397
3627
|
const diffResult = generateCompactOrFullDiff(original, result);
|
|
3398
|
-
const patch = createPatch(
|
|
3628
|
+
const patch = createPatch(path, original, result);
|
|
3399
3629
|
const diffData = buildDiffData({
|
|
3400
3630
|
path: absolutePath,
|
|
3401
3631
|
oldContent: original,
|
|
@@ -3407,7 +3637,7 @@ async function executeEdit(opts) {
|
|
|
3407
3637
|
const semanticSummary = { classification: classification.classification };
|
|
3408
3638
|
const builtOutput = buildEditOutput({
|
|
3409
3639
|
path: absolutePath,
|
|
3410
|
-
displayPath:
|
|
3640
|
+
displayPath: path,
|
|
3411
3641
|
diff: diffResult.diff,
|
|
3412
3642
|
patch,
|
|
3413
3643
|
diffData,
|
|
@@ -3430,7 +3660,7 @@ async function executeEdit(opts) {
|
|
|
3430
3660
|
});
|
|
3431
3661
|
} catch (err) {
|
|
3432
3662
|
if (typeof err?.code === "string") {
|
|
3433
|
-
return mapEditFileError(err, absolutePath,
|
|
3663
|
+
return mapEditFileError(err, absolutePath, path, "read");
|
|
3434
3664
|
}
|
|
3435
3665
|
throw err;
|
|
3436
3666
|
}
|
|
@@ -3480,8 +3710,8 @@ function registerEditTool(pi, options = {}) {
|
|
|
3480
3710
|
text = clampLineToWidth(text, context.width);
|
|
3481
3711
|
return upsertTextComponent(context.lastComponent, text);
|
|
3482
3712
|
},
|
|
3483
|
-
renderResult(result,
|
|
3484
|
-
const { isPartial, isError, expanded, width, context } = resolveRenderResultContext(
|
|
3713
|
+
renderResult(result, options, theme, ...rest) {
|
|
3714
|
+
const { isPartial, isError, expanded, width, context } = resolveRenderResultContext(options, rest, resolveReadSeekToolDisplayMode("edit") === "expanded");
|
|
3485
3715
|
if (isPartial) {
|
|
3486
3716
|
return renderPendingResult("pending edit", width, theme);
|
|
3487
3717
|
}
|
|
@@ -3537,7 +3767,7 @@ ${theme.fg("error", info.errorText)}`;
|
|
|
3537
3767
|
import { readFile as fsReadFile2, stat as fsStat } from "node:fs/promises";
|
|
3538
3768
|
import path3 from "node:path";
|
|
3539
3769
|
import { createGrepTool } from "@earendil-works/pi-coding-agent";
|
|
3540
|
-
import { Type as
|
|
3770
|
+
import { Type as Type5 } from "@sinclair/typebox";
|
|
3541
3771
|
import { Text as Text4 } from "@earendil-works/pi-tui";
|
|
3542
3772
|
|
|
3543
3773
|
// src/binary-detect.ts
|
|
@@ -3609,14 +3839,14 @@ function buildScopeMetadata(groups, warnings) {
|
|
|
3609
3839
|
}
|
|
3610
3840
|
function buildGrepOutput(input) {
|
|
3611
3841
|
const fileCount = new Set(input.groups.map((group) => group.absolutePath)).size;
|
|
3612
|
-
const
|
|
3842
|
+
const header = `[${input.totalMatches} matches in ${fileCount} files]`;
|
|
3613
3843
|
let text;
|
|
3614
3844
|
if (input.summary) {
|
|
3615
3845
|
const fileLines = [...input.groups].sort((a, b) => b.matchCount - a.matchCount).map((group) => `${group.absolutePath}: ${group.matchCount} matches`);
|
|
3616
|
-
text = [
|
|
3846
|
+
text = [header, ...fileLines].join(`
|
|
3617
3847
|
`);
|
|
3618
3848
|
} else {
|
|
3619
|
-
const blocks = [
|
|
3849
|
+
const blocks = [header];
|
|
3620
3850
|
for (const group of input.groups) {
|
|
3621
3851
|
blocks.push(renderGroupHeader(group));
|
|
3622
3852
|
for (const entry of group.entries) {
|
|
@@ -3721,12 +3951,12 @@ function firstLineNumber(group) {
|
|
|
3721
3951
|
}
|
|
3722
3952
|
function buildSymbolEntries(fileLines, symbol, matchLines, scopeContext) {
|
|
3723
3953
|
if (scopeContext === undefined) {
|
|
3724
|
-
const
|
|
3954
|
+
const entries = [];
|
|
3725
3955
|
for (let lineNumber = symbol.startLine;lineNumber <= symbol.endLine; lineNumber++) {
|
|
3726
3956
|
const built = buildReadSeekLine(lineNumber, fileLines[lineNumber - 1] ?? "");
|
|
3727
|
-
|
|
3957
|
+
entries.push({ kind: matchLines.has(lineNumber) ? "match" : "context", line: built });
|
|
3728
3958
|
}
|
|
3729
|
-
return
|
|
3959
|
+
return entries;
|
|
3730
3960
|
}
|
|
3731
3961
|
const ranges = [...matchLines].sort((a, b) => a - b).map((ln) => ({
|
|
3732
3962
|
startLine: Math.max(symbol.startLine, ln - scopeContext),
|
|
@@ -3909,11 +4139,8 @@ import {
|
|
|
3909
4139
|
} from "@jarkkojs/readseek-api";
|
|
3910
4140
|
|
|
3911
4141
|
// 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
4142
|
var GREP_ANCHOR_RE = /^(.*?):(>>| )(\d+):([0-9a-zA-Z]{1,16})\|(.*)$/;
|
|
3915
|
-
|
|
3916
|
-
function renderGrepSourceForDisplay(text, anchors, renderPath = (path3) => path3) {
|
|
4143
|
+
function renderGrepSourceForDisplay(text, anchors, renderPath = (path) => path) {
|
|
3917
4144
|
return text.split(`
|
|
3918
4145
|
`).map((line) => {
|
|
3919
4146
|
const match = line.match(GREP_ANCHOR_RE);
|
|
@@ -3929,39 +4156,30 @@ function renderGrepSourceForDisplay(text, anchors, renderPath = (path3) => path3
|
|
|
3929
4156
|
}
|
|
3930
4157
|
|
|
3931
4158
|
// src/readseek-params.ts
|
|
3932
|
-
import { Type as
|
|
4159
|
+
import { Type as Type4 } from "@sinclair/typebox";
|
|
3933
4160
|
function searchPathParam() {
|
|
3934
|
-
return
|
|
4161
|
+
return Type4.Optional(Type4.String({ description: PARAM_DESCRIPTIONS.searchPath }));
|
|
3935
4162
|
}
|
|
3936
4163
|
function languageParam() {
|
|
3937
|
-
return
|
|
4164
|
+
return Type4.Optional(Type4.String({ description: PARAM_DESCRIPTIONS.language }));
|
|
3938
4165
|
}
|
|
3939
4166
|
function readSeekGitSearchParams() {
|
|
3940
4167
|
return {
|
|
3941
|
-
|
|
3942
|
-
others: Type3.Optional(Type3.Boolean({ description: PARAM_DESCRIPTIONS.others })),
|
|
3943
|
-
ignored: Type3.Optional(Type3.Boolean({ description: PARAM_DESCRIPTIONS.ignored }))
|
|
4168
|
+
gitScope: Type4.Optional(Type4.Union(GIT_SCOPES.map((scope) => Type4.Literal(scope)), { description: PARAM_DESCRIPTIONS.gitScope }))
|
|
3944
4169
|
};
|
|
3945
4170
|
}
|
|
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
4171
|
|
|
3954
4172
|
// src/grep.ts
|
|
3955
|
-
var grepSchema =
|
|
3956
|
-
pattern:
|
|
4173
|
+
var grepSchema = Type5.Object({
|
|
4174
|
+
pattern: Type5.String({ description: PARAM_DESCRIPTIONS.grepPattern }),
|
|
3957
4175
|
path: searchPathParam(),
|
|
3958
|
-
glob:
|
|
3959
|
-
ignoreCase:
|
|
3960
|
-
literal:
|
|
4176
|
+
glob: Type5.Optional(Type5.String({ description: PARAM_DESCRIPTIONS.glob })),
|
|
4177
|
+
ignoreCase: Type5.Optional(Type5.Boolean({ description: PARAM_DESCRIPTIONS.ignoreCase })),
|
|
4178
|
+
literal: Type5.Optional(Type5.Boolean({ description: PARAM_DESCRIPTIONS.literal })),
|
|
3961
4179
|
context: optionalIntOrString(PARAM_DESCRIPTIONS.context),
|
|
3962
4180
|
limit: optionalIntOrString(PARAM_DESCRIPTIONS.matchLimit),
|
|
3963
|
-
summary:
|
|
3964
|
-
scope:
|
|
4181
|
+
summary: Type5.Optional(Type5.Boolean({ description: PARAM_DESCRIPTIONS.summary })),
|
|
4182
|
+
scope: Type5.Optional(Type5.Literal("symbol", {
|
|
3965
4183
|
description: PARAM_DESCRIPTIONS.grepScope
|
|
3966
4184
|
})),
|
|
3967
4185
|
scopeContext: optionalIntOrString(PARAM_DESCRIPTIONS.scopeContext)
|
|
@@ -4033,12 +4251,6 @@ function truncateGroupEntries(group) {
|
|
|
4033
4251
|
}
|
|
4034
4252
|
return { ...group, entries: kept };
|
|
4035
4253
|
}
|
|
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
4254
|
async function executeGrep(opts) {
|
|
4043
4255
|
const { toolCallId, params, signal, onUpdate, cwd, onFileAnchored } = opts;
|
|
4044
4256
|
await ensureHashInit();
|
|
@@ -4047,10 +4259,16 @@ async function executeGrep(opts) {
|
|
|
4047
4259
|
if (!context.ok) {
|
|
4048
4260
|
return buildToolErrorResult("grep", "invalid-params-combo", context.message);
|
|
4049
4261
|
}
|
|
4262
|
+
if (context.value !== undefined && context.value < 0) {
|
|
4263
|
+
return buildToolErrorResult("grep", "invalid-params-combo", `Invalid context: expected a non-negative integer, received ${context.value}.`);
|
|
4264
|
+
}
|
|
4050
4265
|
const limit = coerceObviousBase10Int2(rawParams.limit, "limit");
|
|
4051
4266
|
if (!limit.ok) {
|
|
4052
4267
|
return buildToolErrorResult("grep", "invalid-limit", limit.message);
|
|
4053
4268
|
}
|
|
4269
|
+
if (limit.value !== undefined && limit.value < 1) {
|
|
4270
|
+
return buildToolErrorResult("grep", "invalid-limit", `Invalid limit: expected a positive integer, received ${limit.value}.`);
|
|
4271
|
+
}
|
|
4054
4272
|
const scopeContext = coerceObviousBase10Int2(rawParams.scopeContext, "scopeContext");
|
|
4055
4273
|
if (!scopeContext.ok) {
|
|
4056
4274
|
return buildToolErrorResult("grep", "invalid-params-combo", scopeContext.message);
|
|
@@ -4155,7 +4373,7 @@ async function executeGrep(opts) {
|
|
|
4155
4373
|
const CONCURRENCY = 8;
|
|
4156
4374
|
const pathList = [...pathsToRead];
|
|
4157
4375
|
for (let i = 0;i < pathList.length; i += CONCURRENCY) {
|
|
4158
|
-
await Promise.all(pathList.slice(i, i + CONCURRENCY).map((
|
|
4376
|
+
await Promise.all(pathList.slice(i, i + CONCURRENCY).map((p) => getFileLines(p)));
|
|
4159
4377
|
}
|
|
4160
4378
|
}
|
|
4161
4379
|
const addSummaryMatch = (displayPath, absolutePath) => {
|
|
@@ -4208,7 +4426,7 @@ async function executeGrep(opts) {
|
|
|
4208
4426
|
const flags = p.ignoreCase ? "i" : "";
|
|
4209
4427
|
let patternRe = null;
|
|
4210
4428
|
try {
|
|
4211
|
-
patternRe = p.literal ? new RegExp(
|
|
4429
|
+
patternRe = p.literal ? new RegExp(p.pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), flags) : new RegExp(p.pattern, flags);
|
|
4212
4430
|
} catch {}
|
|
4213
4431
|
if (patternRe !== null) {
|
|
4214
4432
|
let emitted = false;
|
|
@@ -4292,7 +4510,7 @@ ${warning}` } : item),
|
|
|
4292
4510
|
renderedGroups = scoped.groups;
|
|
4293
4511
|
scopeWarnings = scoped.warnings;
|
|
4294
4512
|
}
|
|
4295
|
-
const readSeekRecords =
|
|
4513
|
+
const readSeekRecords = renderedGroups.flatMap((group) => group.entries.flatMap((entry) => entry.kind === "separator" ? [] : [{ path: group.absolutePath, ...entry.line, kind: entry.kind }]));
|
|
4296
4514
|
const builtOutput = buildGrepOutput({
|
|
4297
4515
|
summary: !!summary,
|
|
4298
4516
|
totalMatches,
|
|
@@ -4363,8 +4581,8 @@ function registerGrepTool(pi, options = {}) {
|
|
|
4363
4581
|
}
|
|
4364
4582
|
return new Text4(clampLineToWidth(text, context.width), 0, 0);
|
|
4365
4583
|
},
|
|
4366
|
-
renderResult(result,
|
|
4367
|
-
const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(
|
|
4584
|
+
renderResult(result, options, theme, ...rest) {
|
|
4585
|
+
const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(options, rest, resolveReadSeekToolDisplayMode("grep") === "expanded");
|
|
4368
4586
|
if (isPartial)
|
|
4369
4587
|
return renderPendingResult("pending grep", width, theme);
|
|
4370
4588
|
const content = result.content?.[0];
|
|
@@ -4411,7 +4629,7 @@ function registerGrepTool(pi, options = {}) {
|
|
|
4411
4629
|
|
|
4412
4630
|
// src/search.ts
|
|
4413
4631
|
import path4 from "node:path";
|
|
4414
|
-
import { Type as
|
|
4632
|
+
import { Type as Type6 } from "@sinclair/typebox";
|
|
4415
4633
|
|
|
4416
4634
|
// src/stat-search-path.ts
|
|
4417
4635
|
import { stat as fsStat2 } from "node:fs/promises";
|
|
@@ -4419,9 +4637,9 @@ async function statSearchPathOrError(tool, rawPath, searchPath) {
|
|
|
4419
4637
|
try {
|
|
4420
4638
|
return { ok: true, stats: await fsStat2(searchPath) };
|
|
4421
4639
|
} catch (err) {
|
|
4422
|
-
const
|
|
4640
|
+
const path = rawPath ?? searchPath;
|
|
4423
4641
|
const { code, message } = formatFsError(err, "stat-error");
|
|
4424
|
-
return { ok: false, error: buildToolErrorResult(tool, code, message, { path
|
|
4642
|
+
return { ok: false, error: buildToolErrorResult(tool, code, message, { path }) };
|
|
4425
4643
|
}
|
|
4426
4644
|
}
|
|
4427
4645
|
|
|
@@ -4502,9 +4720,6 @@ function readSeekLanguageForPath(language, searchPath, isFile) {
|
|
|
4502
4720
|
async function executeSearch(opts) {
|
|
4503
4721
|
const { params, signal, cwd, onFileAnchored } = opts;
|
|
4504
4722
|
const p = params;
|
|
4505
|
-
const ignoredError = validateIgnoredRequiresOthers("search", p);
|
|
4506
|
-
if (ignoredError)
|
|
4507
|
-
return ignoredError;
|
|
4508
4723
|
const searchPath = resolveToCwd2(p.path ?? ".", cwd);
|
|
4509
4724
|
const statResult = await statSearchPathOrError("search", p.path, searchPath);
|
|
4510
4725
|
if (!statResult.ok)
|
|
@@ -4514,9 +4729,7 @@ async function executeSearch(opts) {
|
|
|
4514
4729
|
const effectiveLang = readSeekLanguageForPath(p.language, searchPath, searchPathIsFile);
|
|
4515
4730
|
const results = await readSeekSearch(searchPath, p.pattern, {
|
|
4516
4731
|
language: effectiveLang,
|
|
4517
|
-
|
|
4518
|
-
others: p.others,
|
|
4519
|
-
ignored: p.ignored,
|
|
4732
|
+
gitScope: p.gitScope,
|
|
4520
4733
|
signal
|
|
4521
4734
|
});
|
|
4522
4735
|
if (results.length === 0) {
|
|
@@ -4578,8 +4791,8 @@ function registerSearchTool(pi, options = {}) {
|
|
|
4578
4791
|
description: SEARCH_PROMPT_METADATA.description,
|
|
4579
4792
|
promptSnippet: SEARCH_PROMPT_METADATA.promptSnippet,
|
|
4580
4793
|
promptGuidelines: SEARCH_PROMPT_METADATA.promptGuidelines,
|
|
4581
|
-
parameters:
|
|
4582
|
-
pattern:
|
|
4794
|
+
parameters: Type6.Object({
|
|
4795
|
+
pattern: Type6.String({ description: PARAM_DESCRIPTIONS.pattern }),
|
|
4583
4796
|
language: languageParam(),
|
|
4584
4797
|
path: searchPathParam(),
|
|
4585
4798
|
...readSeekGitSearchParams()
|
|
@@ -4591,11 +4804,11 @@ function registerSearchTool(pi, options = {}) {
|
|
|
4591
4804
|
return renderReadSeekSearchCall(args, theme, rest, {
|
|
4592
4805
|
label: "search",
|
|
4593
4806
|
accent: `/${args.pattern}/`,
|
|
4594
|
-
flags: [args.
|
|
4807
|
+
flags: [args.gitScope && `git-scope=${args.gitScope}`]
|
|
4595
4808
|
});
|
|
4596
4809
|
},
|
|
4597
|
-
renderResult(result,
|
|
4598
|
-
return renderAnchoredFilesResult(result,
|
|
4810
|
+
renderResult(result, options, theme, ...rest) {
|
|
4811
|
+
return renderAnchoredFilesResult(result, options, theme, rest, {
|
|
4599
4812
|
pendingLabel: "pending search",
|
|
4600
4813
|
emptyLabel: "no matches",
|
|
4601
4814
|
unitSingular: "match",
|
|
@@ -4607,7 +4820,7 @@ function registerSearchTool(pi, options = {}) {
|
|
|
4607
4820
|
}
|
|
4608
4821
|
|
|
4609
4822
|
// src/refs.ts
|
|
4610
|
-
import { Type as
|
|
4823
|
+
import { Type as Type7 } from "@sinclair/typebox";
|
|
4611
4824
|
import path5 from "node:path";
|
|
4612
4825
|
|
|
4613
4826
|
// src/refs-output.ts
|
|
@@ -4673,9 +4886,6 @@ function isReadSeekCursorValidationFailure(message) {
|
|
|
4673
4886
|
async function executeRefs(opts) {
|
|
4674
4887
|
const { params, signal, cwd, onFileAnchored } = opts;
|
|
4675
4888
|
const p = params;
|
|
4676
|
-
const ignoredError = validateIgnoredRequiresOthers("refs", p);
|
|
4677
|
-
if (ignoredError)
|
|
4678
|
-
return ignoredError;
|
|
4679
4889
|
const scopeError = validateRefsNameVsScope(p);
|
|
4680
4890
|
if (!scopeError.ok) {
|
|
4681
4891
|
return buildToolErrorResult("refs", "invalid-parameter", `refs parameter: ${scopeError.error}`);
|
|
@@ -4691,9 +4901,7 @@ async function executeRefs(opts) {
|
|
|
4691
4901
|
line: p.line,
|
|
4692
4902
|
column: p.column,
|
|
4693
4903
|
language: p.language,
|
|
4694
|
-
|
|
4695
|
-
others: p.others,
|
|
4696
|
-
ignored: p.ignored,
|
|
4904
|
+
gitScope: p.gitScope,
|
|
4697
4905
|
signal
|
|
4698
4906
|
});
|
|
4699
4907
|
const files = groupReferences(references, cwd);
|
|
@@ -4726,13 +4934,13 @@ function registerRefsTool(pi, options = {}) {
|
|
|
4726
4934
|
description: REFS_PROMPT_METADATA.description,
|
|
4727
4935
|
promptSnippet: REFS_PROMPT_METADATA.promptSnippet,
|
|
4728
4936
|
promptGuidelines: REFS_PROMPT_METADATA.promptGuidelines,
|
|
4729
|
-
parameters:
|
|
4730
|
-
name:
|
|
4937
|
+
parameters: Type7.Object({
|
|
4938
|
+
name: Type7.Optional(Type7.String({ description: PARAM_DESCRIPTIONS.refsName })),
|
|
4731
4939
|
path: searchPathParam(),
|
|
4732
4940
|
language: languageParam(),
|
|
4733
|
-
scope:
|
|
4734
|
-
line:
|
|
4735
|
-
column:
|
|
4941
|
+
scope: Type7.Optional(Type7.Boolean({ description: PARAM_DESCRIPTIONS.scope })),
|
|
4942
|
+
line: Type7.Optional(Type7.Number({ description: PARAM_DESCRIPTIONS.line })),
|
|
4943
|
+
column: Type7.Optional(Type7.Number({ description: PARAM_DESCRIPTIONS.column })),
|
|
4736
4944
|
...readSeekGitSearchParams()
|
|
4737
4945
|
}),
|
|
4738
4946
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
@@ -4743,11 +4951,11 @@ function registerRefsTool(pi, options = {}) {
|
|
|
4743
4951
|
return renderReadSeekSearchCall(args, theme, rest, {
|
|
4744
4952
|
label: "refs",
|
|
4745
4953
|
accent,
|
|
4746
|
-
flags: [args.scope && "scope", args.
|
|
4954
|
+
flags: [args.scope && "scope", args.gitScope && `git-scope=${args.gitScope}`]
|
|
4747
4955
|
});
|
|
4748
4956
|
},
|
|
4749
|
-
renderResult(result,
|
|
4750
|
-
return renderAnchoredFilesResult(result,
|
|
4957
|
+
renderResult(result, options, theme, ...rest) {
|
|
4958
|
+
return renderAnchoredFilesResult(result, options, theme, rest, {
|
|
4751
4959
|
pendingLabel: "pending refs",
|
|
4752
4960
|
emptyLabel: "no references",
|
|
4753
4961
|
unitSingular: "reference",
|
|
@@ -4759,20 +4967,20 @@ function registerRefsTool(pi, options = {}) {
|
|
|
4759
4967
|
}
|
|
4760
4968
|
|
|
4761
4969
|
// src/rename.ts
|
|
4762
|
-
import { Type as
|
|
4970
|
+
import { Type as Type8 } from "@sinclair/typebox";
|
|
4763
4971
|
import path6 from "node:path";
|
|
4764
4972
|
import { Text as Text5 } from "@earendil-works/pi-tui";
|
|
4765
4973
|
var RENAME_PROMPT_METADATA = defineToolPromptMetadata({
|
|
4766
4974
|
promptUrl: new URL("../prompts/rename.md", import.meta.url),
|
|
4767
4975
|
promptSnippet: "Rename a symbol at a source location with verified edits"
|
|
4768
4976
|
});
|
|
4769
|
-
var renameSchema =
|
|
4977
|
+
var renameSchema = Type8.Object({
|
|
4770
4978
|
path: filePathParam(),
|
|
4771
|
-
line:
|
|
4772
|
-
column:
|
|
4773
|
-
to:
|
|
4774
|
-
workspace:
|
|
4775
|
-
apply:
|
|
4979
|
+
line: Type8.Integer({ minimum: 1, description: PARAM_DESCRIPTIONS.renameLine }),
|
|
4980
|
+
column: Type8.Optional(Type8.Integer({ minimum: 1, description: PARAM_DESCRIPTIONS.column })),
|
|
4981
|
+
to: Type8.String({ description: PARAM_DESCRIPTIONS.to }),
|
|
4982
|
+
workspace: Type8.Optional(Type8.Boolean({ description: PARAM_DESCRIPTIONS.workspace })),
|
|
4983
|
+
apply: Type8.Optional(Type8.Boolean({ description: PARAM_DESCRIPTIONS.applyDefaultTrue }))
|
|
4776
4984
|
});
|
|
4777
4985
|
async function executeRename(opts) {
|
|
4778
4986
|
const { params, signal, cwd, onFileMutated } = opts;
|
|
@@ -4804,7 +5012,8 @@ async function executeRename(opts) {
|
|
|
4804
5012
|
onFileMutated?.(absoluteFile);
|
|
4805
5013
|
}
|
|
4806
5014
|
}
|
|
4807
|
-
const
|
|
5015
|
+
const primaryAbs = path6.isAbsolute(output.file) ? output.file : path6.resolve(cwd, output.file);
|
|
5016
|
+
const files = [primaryAbs];
|
|
4808
5017
|
for (const other of output.others) {
|
|
4809
5018
|
const abs = path6.isAbsolute(other.file) ? other.file : path6.resolve(cwd, other.file);
|
|
4810
5019
|
if (!files.includes(abs))
|
|
@@ -4864,8 +5073,8 @@ function registerRenameTool(pi, options = {}) {
|
|
|
4864
5073
|
text += theme.fg("dim", ` → ${args.to}`);
|
|
4865
5074
|
return new Text5(clampLineToWidth(text, context.width), 0, 0);
|
|
4866
5075
|
},
|
|
4867
|
-
renderResult(result,
|
|
4868
|
-
const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(
|
|
5076
|
+
renderResult(result, options, theme, ...rest) {
|
|
5077
|
+
const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(options, rest);
|
|
4869
5078
|
if (isPartial)
|
|
4870
5079
|
return renderPendingResult("pending rename", width, theme);
|
|
4871
5080
|
const content = result.content?.[0];
|
|
@@ -4906,7 +5115,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
4906
5115
|
import { dirname as dirname2, relative as relative3 } from "node:path";
|
|
4907
5116
|
import { withFileMutationQueue as withFileMutationQueue2, truncateHead as truncateHead2 } from "@earendil-works/pi-coding-agent";
|
|
4908
5117
|
import { Text as Text6 } from "@earendil-works/pi-tui";
|
|
4909
|
-
import { Type as
|
|
5118
|
+
import { Type as Type9 } from "@sinclair/typebox";
|
|
4910
5119
|
|
|
4911
5120
|
// src/pending-diff-preview.ts
|
|
4912
5121
|
import { existsSync as existsSync3, readFileSync as readFileSync2, realpathSync, statSync as statSync4 } from "node:fs";
|
|
@@ -5053,9 +5262,9 @@ function pendingWritePreviewParts(summary, preview, expanded, theme) {
|
|
|
5053
5262
|
if (!preview || preview.type !== "ok")
|
|
5054
5263
|
return { lines: summary.split(`
|
|
5055
5264
|
`) };
|
|
5056
|
-
const
|
|
5265
|
+
const hasOldSide = preview.data.fileExistedBeforeWrite;
|
|
5057
5266
|
const headerLine = summaryLine(preview.data.headerLabel, { hidden: !expanded });
|
|
5058
|
-
if (!
|
|
5267
|
+
if (!hasOldSide) {
|
|
5059
5268
|
const lines = [summary, headerLine];
|
|
5060
5269
|
if (expanded)
|
|
5061
5270
|
lines.push(...formatContentPreviewLines(preview.data.nextContent, theme));
|
|
@@ -5111,7 +5320,7 @@ async function executeWrite(opts) {
|
|
|
5111
5320
|
const warnings = [];
|
|
5112
5321
|
const readSeekWarnings = [];
|
|
5113
5322
|
if (hasBareCarriageReturn(content)) {
|
|
5114
|
-
const message = "File content contains bare CR (\\r) line endings; readSeek_write refuses to emit anchors that
|
|
5323
|
+
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
5324
|
warnings.push(message);
|
|
5116
5325
|
readSeekWarnings.push(buildReadSeekWarning("bare-cr", message));
|
|
5117
5326
|
return buildToolErrorResult("write", "bare-cr", `Cannot write ${filePath}
|
|
@@ -5202,9 +5411,9 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5202
5411
|
description: promptMetadata.description,
|
|
5203
5412
|
promptSnippet: promptMetadata.promptSnippet,
|
|
5204
5413
|
promptGuidelines: promptMetadata.promptGuidelines,
|
|
5205
|
-
parameters:
|
|
5414
|
+
parameters: Type9.Object({
|
|
5206
5415
|
path: filePathParam(),
|
|
5207
|
-
content:
|
|
5416
|
+
content: Type9.String({ description: PARAM_DESCRIPTIONS.content })
|
|
5208
5417
|
}),
|
|
5209
5418
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
5210
5419
|
const cwd = ctx?.cwd ?? process.cwd();
|
|
@@ -5242,13 +5451,13 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5242
5451
|
}
|
|
5243
5452
|
},
|
|
5244
5453
|
renderCall(args, theme, context = {}) {
|
|
5245
|
-
const { path
|
|
5454
|
+
const { path, content } = args;
|
|
5246
5455
|
const cwd = context.cwd ?? process.cwd();
|
|
5247
5456
|
const label = renderToolLabel(theme, "write");
|
|
5248
5457
|
const lineCount = typeof content === "string" ? content.split(`
|
|
5249
5458
|
`).length : 0;
|
|
5250
5459
|
const bytes = typeof content === "string" ? Buffer.byteLength(content, "utf8") : 0;
|
|
5251
|
-
const renderedPath = typeof
|
|
5460
|
+
const renderedPath = typeof path === "string" ? linkToolPath(theme.fg("muted", path), path, cwd) : theme.fg("toolOutput", "...");
|
|
5252
5461
|
let text = clampLineToWidth(`${label} ${renderedPath}${typeof content === "string" ? ` (${lineCount} ${lineCount === 1 ? "line" : "lines"} • ${bytes} B)` : ""}`, context.width);
|
|
5253
5462
|
if (context.executionStarted) {
|
|
5254
5463
|
return upsertTextComponent(context.lastComponent, text);
|
|
@@ -5263,8 +5472,8 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5263
5472
|
return upsertTextComponent(context.lastComponent, clampLinesToWidth(parts.lines, context.width).join(`
|
|
5264
5473
|
`));
|
|
5265
5474
|
},
|
|
5266
|
-
renderResult(result,
|
|
5267
|
-
const { isPartial, expanded, width, context } = resolveRenderResultContext(
|
|
5475
|
+
renderResult(result, options, theme, ...rest) {
|
|
5476
|
+
const { isPartial, expanded, width, context } = resolveRenderResultContext(options, rest, resolveReadSeekToolDisplayMode("write") === "expanded");
|
|
5268
5477
|
if (isPartial)
|
|
5269
5478
|
return renderPendingResult("pending write", width, theme);
|
|
5270
5479
|
const details = result.details ?? {};
|
|
@@ -5277,8 +5486,8 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5277
5486
|
if (state === "created") {
|
|
5278
5487
|
const readSeekLines = details.readSeekValue?.lines ?? [];
|
|
5279
5488
|
const hasContent = readSeekLines.length > 0;
|
|
5280
|
-
const
|
|
5281
|
-
const lines =
|
|
5489
|
+
const header = summaryLine(state, { hidden: hasContent && !expanded, theme, style: "success" });
|
|
5490
|
+
const lines = header.split(`
|
|
5282
5491
|
`);
|
|
5283
5492
|
if (expanded && hasContent) {
|
|
5284
5493
|
const content = readSeekLines.map((l) => l.raw).join(`
|
|
@@ -5303,7 +5512,7 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5303
5512
|
}
|
|
5304
5513
|
|
|
5305
5514
|
// src/def.ts
|
|
5306
|
-
import { Type as
|
|
5515
|
+
import { Type as Type10 } from "@sinclair/typebox";
|
|
5307
5516
|
import path7 from "node:path";
|
|
5308
5517
|
var DEF_PROMPT_METADATA = defineToolPromptMetadata({
|
|
5309
5518
|
promptUrl: new URL("../prompts/def.md", import.meta.url),
|
|
@@ -5312,9 +5521,6 @@ var DEF_PROMPT_METADATA = defineToolPromptMetadata({
|
|
|
5312
5521
|
async function executeDef(opts) {
|
|
5313
5522
|
const { params, signal, cwd, onFileAnchored } = opts;
|
|
5314
5523
|
const p = params;
|
|
5315
|
-
const ignoredError = validateIgnoredRequiresOthers("def", p);
|
|
5316
|
-
if (ignoredError)
|
|
5317
|
-
return ignoredError;
|
|
5318
5524
|
if (!p.name || !p.name.trim()) {
|
|
5319
5525
|
return buildToolErrorResult("def", "invalid-parameter", "readSeek_def requires 'name'");
|
|
5320
5526
|
}
|
|
@@ -5326,9 +5532,7 @@ async function executeDef(opts) {
|
|
|
5326
5532
|
const definitions = await readSeekDef(searchPath, {
|
|
5327
5533
|
name: p.name,
|
|
5328
5534
|
language: p.language,
|
|
5329
|
-
|
|
5330
|
-
others: p.others,
|
|
5331
|
-
ignored: p.ignored,
|
|
5535
|
+
gitScope: p.gitScope,
|
|
5332
5536
|
signal
|
|
5333
5537
|
});
|
|
5334
5538
|
if (definitions.length === 0) {
|
|
@@ -5379,8 +5583,8 @@ function registerDefTool(pi, options = {}) {
|
|
|
5379
5583
|
description: DEF_PROMPT_METADATA.description,
|
|
5380
5584
|
promptSnippet: DEF_PROMPT_METADATA.promptSnippet,
|
|
5381
5585
|
promptGuidelines: DEF_PROMPT_METADATA.promptGuidelines,
|
|
5382
|
-
parameters:
|
|
5383
|
-
name:
|
|
5586
|
+
parameters: Type10.Object({
|
|
5587
|
+
name: Type10.String({ description: PARAM_DESCRIPTIONS.name }),
|
|
5384
5588
|
path: searchPathParam(),
|
|
5385
5589
|
language: languageParam(),
|
|
5386
5590
|
...readSeekGitSearchParams()
|
|
@@ -5392,11 +5596,11 @@ function registerDefTool(pi, options = {}) {
|
|
|
5392
5596
|
return renderReadSeekSearchCall(args, theme, rest, {
|
|
5393
5597
|
label: "def",
|
|
5394
5598
|
accent: args.name,
|
|
5395
|
-
flags: [args.
|
|
5599
|
+
flags: [args.gitScope && `git-scope=${args.gitScope}`]
|
|
5396
5600
|
});
|
|
5397
5601
|
},
|
|
5398
|
-
renderResult(result,
|
|
5399
|
-
return renderAnchoredFilesResult(result,
|
|
5602
|
+
renderResult(result, options, theme, ...rest) {
|
|
5603
|
+
return renderAnchoredFilesResult(result, options, theme, rest, {
|
|
5400
5604
|
pendingLabel: "pending def",
|
|
5401
5605
|
emptyLabel: "no definitions",
|
|
5402
5606
|
unitSingular: "definition",
|
|
@@ -5406,39 +5610,33 @@ function registerDefTool(pi, options = {}) {
|
|
|
5406
5610
|
});
|
|
5407
5611
|
}
|
|
5408
5612
|
|
|
5409
|
-
// src/
|
|
5613
|
+
// src/inspect.ts
|
|
5410
5614
|
import { Text as Text7 } from "@earendil-works/pi-tui";
|
|
5411
|
-
import { Type as
|
|
5412
|
-
var
|
|
5413
|
-
var
|
|
5414
|
-
var digestSchema = Type10.Object({
|
|
5615
|
+
import { Type as Type11 } from "@sinclair/typebox";
|
|
5616
|
+
var facetSchema = Type11.Union(INSPECT_FACETS.map((facet) => Type11.Literal(facet)));
|
|
5617
|
+
var inspectSchema = Type11.Object({
|
|
5415
5618
|
path: filePathParam(),
|
|
5416
|
-
|
|
5619
|
+
selector: Type11.Optional(Type11.String({ description: PARAM_DESCRIPTIONS.selector })),
|
|
5620
|
+
select: Type11.Optional(Type11.Union([
|
|
5417
5621
|
facetSchema,
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
], {
|
|
5423
|
-
description: PARAM_DESCRIPTIONS.select
|
|
5424
|
-
})),
|
|
5425
|
-
at: Type10.Optional(Type10.String({
|
|
5426
|
-
description: PARAM_DESCRIPTIONS.at
|
|
5427
|
-
})),
|
|
5622
|
+
Type11.Array(facetSchema, { minItems: 1 }),
|
|
5623
|
+
Type11.String({ description: PARAM_DESCRIPTIONS.selectComma })
|
|
5624
|
+
], { description: PARAM_DESCRIPTIONS.select })),
|
|
5625
|
+
query: Type11.Optional(Type11.String({ description: PARAM_DESCRIPTIONS.query })),
|
|
5428
5626
|
end: optionalIntOrString(PARAM_DESCRIPTIONS.end),
|
|
5429
5627
|
limit: optionalIntOrString(PARAM_DESCRIPTIONS.limit),
|
|
5430
|
-
depth: optionalIntOrString(PARAM_DESCRIPTIONS.
|
|
5431
|
-
language:
|
|
5432
|
-
visionMode:
|
|
5433
|
-
visionLevel:
|
|
5628
|
+
depth: optionalIntOrString(PARAM_DESCRIPTIONS.inspectDepth),
|
|
5629
|
+
language: Type11.Optional(Type11.String({ description: PARAM_DESCRIPTIONS.languageOverride })),
|
|
5630
|
+
visionMode: Type11.Optional(Type11.Union(VISION_MODES.map((mode) => Type11.Literal(mode)), { description: PARAM_DESCRIPTIONS.visionModeInspect })),
|
|
5631
|
+
visionLevel: Type11.Optional(Type11.Union(VISION_LEVELS.map((level) => Type11.Literal(level)), { description: PARAM_DESCRIPTIONS.visionLevel }))
|
|
5434
5632
|
});
|
|
5435
|
-
function
|
|
5633
|
+
function hasInspectAnchors(value) {
|
|
5436
5634
|
if (!value || typeof value !== "object")
|
|
5437
5635
|
return false;
|
|
5438
5636
|
const record = value;
|
|
5439
5637
|
if (Array.isArray(record.hashlines) && record.hashlines.length > 0)
|
|
5440
5638
|
return true;
|
|
5441
|
-
return Object.values(record).some(
|
|
5639
|
+
return Object.values(record).some(hasInspectAnchors);
|
|
5442
5640
|
}
|
|
5443
5641
|
function parseSelect(select) {
|
|
5444
5642
|
if (select === undefined)
|
|
@@ -5448,8 +5646,8 @@ function parseSelect(select) {
|
|
|
5448
5646
|
if (facets.length === 0)
|
|
5449
5647
|
throw new Error("select must include at least one facet");
|
|
5450
5648
|
for (const facet of facets) {
|
|
5451
|
-
if (!
|
|
5452
|
-
throw new Error(`unknown
|
|
5649
|
+
if (!INSPECT_FACETS.includes(facet)) {
|
|
5650
|
+
throw new Error(`unknown inspect facet \`${facet}\`; expected ${INSPECT_FACETS.join(", ")}`);
|
|
5453
5651
|
}
|
|
5454
5652
|
}
|
|
5455
5653
|
return facets;
|
|
@@ -5457,15 +5655,15 @@ function parseSelect(select) {
|
|
|
5457
5655
|
if (typeof select !== "string") {
|
|
5458
5656
|
throw new Error("select must be a facet, facet list, or comma-separated facets");
|
|
5459
5657
|
}
|
|
5460
|
-
if (
|
|
5658
|
+
if (INSPECT_FACETS.includes(select)) {
|
|
5461
5659
|
return select;
|
|
5462
5660
|
}
|
|
5463
5661
|
const parts = select.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
|
|
5464
5662
|
if (parts.length === 0)
|
|
5465
5663
|
throw new Error("select must include at least one facet");
|
|
5466
5664
|
for (const part of parts) {
|
|
5467
|
-
if (!
|
|
5468
|
-
throw new Error(`unknown
|
|
5665
|
+
if (!INSPECT_FACETS.includes(part)) {
|
|
5666
|
+
throw new Error(`unknown inspect facet \`${part}\`; expected ${INSPECT_FACETS.join(", ")}`);
|
|
5469
5667
|
}
|
|
5470
5668
|
}
|
|
5471
5669
|
return parts;
|
|
@@ -5477,7 +5675,7 @@ function selectedFacets(select) {
|
|
|
5477
5675
|
}
|
|
5478
5676
|
function summarizeEnvelope(envelope, facets) {
|
|
5479
5677
|
if (!envelope || typeof envelope !== "object")
|
|
5480
|
-
return `
|
|
5678
|
+
return `inspected ${facets.join(",")}`;
|
|
5481
5679
|
const record = envelope;
|
|
5482
5680
|
const parts = [];
|
|
5483
5681
|
for (const facet of facets) {
|
|
@@ -5486,7 +5684,7 @@ function summarizeEnvelope(envelope, facets) {
|
|
|
5486
5684
|
}
|
|
5487
5685
|
if (record.metadata !== undefined && !parts.includes("metadata"))
|
|
5488
5686
|
parts.unshift("metadata");
|
|
5489
|
-
return `
|
|
5687
|
+
return `inspected ${parts.join(",") || "metadata"}`;
|
|
5490
5688
|
}
|
|
5491
5689
|
function parseImageEnvelope(envelope) {
|
|
5492
5690
|
if (!envelope || typeof envelope !== "object")
|
|
@@ -5504,12 +5702,12 @@ function parseImageEnvelope(envelope) {
|
|
|
5504
5702
|
const prepared = content.encoding === "base64" && typeof content.data === "string" && typeof mime === "string" ? { data: content.data, mimeType: mime } : undefined;
|
|
5505
5703
|
return { metadata, prepared };
|
|
5506
5704
|
}
|
|
5507
|
-
function
|
|
5705
|
+
function buildInspectSuccess(filePath, facets, envelope, content = [{ type: "text", text: JSON.stringify(envelope) }]) {
|
|
5508
5706
|
return {
|
|
5509
5707
|
content,
|
|
5510
5708
|
details: {
|
|
5511
5709
|
readSeekValue: {
|
|
5512
|
-
tool: "
|
|
5710
|
+
tool: "inspect",
|
|
5513
5711
|
ok: true,
|
|
5514
5712
|
path: filePath,
|
|
5515
5713
|
select: facets,
|
|
@@ -5518,14 +5716,14 @@ function buildDigestSuccess(filePath, facets, envelope, content = [{ type: "text
|
|
|
5518
5716
|
}
|
|
5519
5717
|
};
|
|
5520
5718
|
}
|
|
5521
|
-
async function
|
|
5719
|
+
async function executeInspect(opts) {
|
|
5522
5720
|
const { params, signal, cwd, onFileAnchored } = opts;
|
|
5523
5721
|
const p = params;
|
|
5524
5722
|
let select;
|
|
5525
5723
|
try {
|
|
5526
5724
|
select = parseSelect(p.select);
|
|
5527
5725
|
} catch (err) {
|
|
5528
|
-
return buildToolErrorResult("
|
|
5726
|
+
return buildToolErrorResult("inspect", "invalid-parameter", err?.message ?? String(err), {
|
|
5529
5727
|
path: p.path
|
|
5530
5728
|
});
|
|
5531
5729
|
}
|
|
@@ -5534,52 +5732,66 @@ async function executeDigest(opts) {
|
|
|
5534
5732
|
const end = coerceObviousBase10Int2(p.end, "end");
|
|
5535
5733
|
if (!end.ok || end.value !== undefined && end.value < 1) {
|
|
5536
5734
|
const message = end.ok ? `Invalid end: expected a positive integer, received ${end.value}.` : end.message;
|
|
5537
|
-
return buildToolErrorResult("
|
|
5735
|
+
return buildToolErrorResult("inspect", "invalid-parameter", message, { path: p.path });
|
|
5538
5736
|
}
|
|
5539
5737
|
const limit = coerceObviousBase10Int2(p.limit, "limit");
|
|
5540
5738
|
if (!limit.ok || limit.value !== undefined && limit.value < 1) {
|
|
5541
5739
|
const message = limit.ok ? `Invalid limit: expected a positive integer, received ${limit.value}.` : limit.message;
|
|
5542
|
-
return buildToolErrorResult("
|
|
5740
|
+
return buildToolErrorResult("inspect", "invalid-parameter", message, { path: p.path });
|
|
5741
|
+
}
|
|
5742
|
+
if (end.value !== undefined && limit.value !== undefined) {
|
|
5743
|
+
return buildToolErrorResult("inspect", "invalid-params-combo", "end and limit cannot be combined.", { path: p.path });
|
|
5543
5744
|
}
|
|
5544
5745
|
const depth = coerceObviousBase10Int2(p.depth, "depth");
|
|
5545
5746
|
if (!depth.ok || depth.value !== undefined && depth.value < 0) {
|
|
5546
5747
|
const message = depth.ok ? `Invalid depth: expected a non-negative integer, received ${depth.value}.` : depth.message;
|
|
5547
|
-
return buildToolErrorResult("
|
|
5748
|
+
return buildToolErrorResult("inspect", "invalid-parameter", message, { path: p.path });
|
|
5548
5749
|
}
|
|
5549
5750
|
if (depth.value !== undefined && !facets.includes("map")) {
|
|
5550
|
-
return buildToolErrorResult("
|
|
5751
|
+
return buildToolErrorResult("inspect", "invalid-parameter", "depth requires the map facet.", { path: p.path });
|
|
5551
5752
|
}
|
|
5552
|
-
const
|
|
5553
|
-
if (p.
|
|
5554
|
-
return buildToolErrorResult("
|
|
5555
|
-
path: p.path
|
|
5556
|
-
});
|
|
5753
|
+
const selector = typeof p.selector === "string" ? p.selector.trim() : undefined;
|
|
5754
|
+
if (p.selector !== undefined && !selector) {
|
|
5755
|
+
return buildToolErrorResult("inspect", "invalid-parameter", "Invalid selector: expected a non-empty selector.", { path: p.path });
|
|
5557
5756
|
}
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5757
|
+
const query = typeof p.query === "string" ? p.query.trim() : undefined;
|
|
5758
|
+
if (p.query !== undefined && !query) {
|
|
5759
|
+
return buildToolErrorResult("inspect", "invalid-parameter", "Invalid query: expected a non-empty query.", { path: p.path });
|
|
5760
|
+
}
|
|
5761
|
+
if (selector !== undefined && query !== undefined) {
|
|
5762
|
+
return buildToolErrorResult("inspect", "invalid-params-combo", "selector and query cannot be combined.", { path: p.path });
|
|
5763
|
+
}
|
|
5764
|
+
if (facets.includes("identity") && selector === undefined) {
|
|
5765
|
+
return buildToolErrorResult("inspect", "invalid-params-combo", "the identity facet requires a selector.", { path: p.path });
|
|
5766
|
+
}
|
|
5767
|
+
if (p.visionLevel !== undefined && (p.visionMode === undefined || p.visionMode === "none") && !appliesImagePolicy) {
|
|
5768
|
+
return buildToolErrorResult("inspect", "invalid-params-combo", "visionLevel requires an analysis visionMode.", { path: p.path });
|
|
5562
5769
|
}
|
|
5563
5770
|
const filePath = resolveToCwd2(p.path, cwd);
|
|
5564
5771
|
const commonOptions = {
|
|
5565
5772
|
select,
|
|
5566
5773
|
signal,
|
|
5567
|
-
depth: depth.value ?? (facets.includes("map") ?
|
|
5774
|
+
depth: depth.value ?? (facets.includes("map") ? DEFAULT_INSPECT_MAP_DEPTH : undefined)
|
|
5568
5775
|
};
|
|
5569
|
-
const
|
|
5776
|
+
const selectionOptions = {
|
|
5570
5777
|
...commonOptions,
|
|
5571
|
-
|
|
5778
|
+
selector,
|
|
5779
|
+
query,
|
|
5572
5780
|
end: end.value,
|
|
5573
5781
|
limit: limit.value,
|
|
5574
5782
|
language: p.language
|
|
5575
5783
|
};
|
|
5576
|
-
const
|
|
5784
|
+
const probeOptions = {
|
|
5785
|
+
...commonOptions,
|
|
5786
|
+
select: [...new Set(["metadata", ...facets])].filter((facet) => facet !== "identity")
|
|
5787
|
+
};
|
|
5788
|
+
const hasSelectionOptions = selector !== undefined || query !== undefined || end.value !== undefined || limit.value !== undefined || p.language !== undefined;
|
|
5577
5789
|
const hasVisionOptions = p.visionMode !== undefined || p.visionLevel !== undefined;
|
|
5578
5790
|
const initialVisionMode = appliesImagePolicy ? undefined : p.visionMode;
|
|
5579
5791
|
const initialVisionLevel = appliesImagePolicy ? undefined : p.visionLevel;
|
|
5580
5792
|
try {
|
|
5581
|
-
const envelope = await
|
|
5582
|
-
...appliesImagePolicy ?
|
|
5793
|
+
const envelope = await readSeekInspect(filePath, {
|
|
5794
|
+
...appliesImagePolicy ? probeOptions : selectionOptions,
|
|
5583
5795
|
visionMode: initialVisionMode,
|
|
5584
5796
|
visionLevel: initialVisionLevel,
|
|
5585
5797
|
vision: initialVisionMode !== undefined && initialVisionMode !== "none"
|
|
@@ -5588,60 +5800,60 @@ async function executeDigest(opts) {
|
|
|
5588
5800
|
if (image) {
|
|
5589
5801
|
const imageDetails = { metadata: image.metadata };
|
|
5590
5802
|
if (opts.imageMode === "off") {
|
|
5591
|
-
return
|
|
5803
|
+
return buildInspectSuccess(filePath, facets, imageDetails, [
|
|
5592
5804
|
{ type: "text", text: `[Skipped standalone image: ${p.path}; readseek.imageMode is off]` }
|
|
5593
5805
|
]);
|
|
5594
5806
|
}
|
|
5595
5807
|
if (opts.imageMode === "auto" && opts.modelSupportsImages && image.prepared) {
|
|
5596
|
-
return
|
|
5808
|
+
return buildInspectSuccess(filePath, facets, imageDetails, [
|
|
5597
5809
|
{ type: "image", data: image.prepared.data, mimeType: image.prepared.mimeType }
|
|
5598
5810
|
]);
|
|
5599
5811
|
}
|
|
5600
5812
|
const visionMode = p.visionMode && p.visionMode !== "none" ? p.visionMode : "all";
|
|
5601
|
-
const analyzedEnvelope = await
|
|
5813
|
+
const analyzedEnvelope = await readSeekInspect(filePath, {
|
|
5602
5814
|
...commonOptions,
|
|
5603
5815
|
visionMode,
|
|
5604
5816
|
visionLevel: p.visionLevel,
|
|
5605
5817
|
vision: true
|
|
5606
5818
|
});
|
|
5607
|
-
return
|
|
5819
|
+
return buildInspectSuccess(filePath, facets, analyzedEnvelope);
|
|
5608
5820
|
}
|
|
5609
|
-
if (p.visionLevel !== undefined && p.visionMode === undefined) {
|
|
5610
|
-
return buildToolErrorResult("
|
|
5821
|
+
if (p.visionLevel !== undefined && (p.visionMode === undefined || p.visionMode === "none")) {
|
|
5822
|
+
return buildToolErrorResult("inspect", "invalid-params-combo", "visionLevel requires an analysis visionMode.", {
|
|
5611
5823
|
path: p.path
|
|
5612
5824
|
});
|
|
5613
5825
|
}
|
|
5614
|
-
const
|
|
5615
|
-
...
|
|
5826
|
+
const selectedEnvelope = appliesImagePolicy && (hasSelectionOptions || hasVisionOptions) ? await readSeekInspect(filePath, {
|
|
5827
|
+
...selectionOptions,
|
|
5616
5828
|
visionMode: p.visionMode,
|
|
5617
5829
|
visionLevel: p.visionLevel,
|
|
5618
5830
|
vision: p.visionMode !== undefined && p.visionMode !== "none"
|
|
5619
5831
|
}) : envelope;
|
|
5620
|
-
if (
|
|
5832
|
+
if (hasInspectAnchors(selectedEnvelope))
|
|
5621
5833
|
onFileAnchored?.(filePath);
|
|
5622
|
-
return
|
|
5834
|
+
return buildInspectSuccess(filePath, facets, selectedEnvelope);
|
|
5623
5835
|
} catch (err) {
|
|
5624
5836
|
const failure = classifyReadSeekFailure(err);
|
|
5625
|
-
return buildToolErrorResult("
|
|
5837
|
+
return buildToolErrorResult("inspect", failure.code, failure.message, failure.hint ? { hint: failure.hint } : {});
|
|
5626
5838
|
}
|
|
5627
5839
|
}
|
|
5628
|
-
function
|
|
5840
|
+
function registerInspectTool(pi, options = {}) {
|
|
5629
5841
|
const promptMetadata = defineToolPromptMetadata({
|
|
5630
|
-
promptUrl: new URL("../prompts/
|
|
5631
|
-
promptSnippet: "
|
|
5632
|
-
registeredName: options.name ?? "
|
|
5842
|
+
promptUrl: new URL("../prompts/inspect.md", import.meta.url),
|
|
5843
|
+
promptSnippet: "Inspect source, images, and documents",
|
|
5844
|
+
registeredName: options.name ?? "readSeek_inspect",
|
|
5633
5845
|
toolAliases: options.toolAliases
|
|
5634
5846
|
});
|
|
5635
5847
|
return registerReadSeekTool(pi, {
|
|
5636
|
-
name: options.name ?? "
|
|
5637
|
-
label: "
|
|
5848
|
+
name: options.name ?? "readSeek_inspect",
|
|
5849
|
+
label: "Inspect",
|
|
5638
5850
|
description: promptMetadata.description,
|
|
5639
5851
|
promptSnippet: promptMetadata.promptSnippet,
|
|
5640
5852
|
toolAliases: options.toolAliases,
|
|
5641
5853
|
promptGuidelines: promptMetadata.promptGuidelines,
|
|
5642
|
-
parameters:
|
|
5854
|
+
parameters: inspectSchema,
|
|
5643
5855
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
5644
|
-
return
|
|
5856
|
+
return executeInspect({
|
|
5645
5857
|
params,
|
|
5646
5858
|
signal,
|
|
5647
5859
|
cwd: ctx.cwd,
|
|
@@ -5654,23 +5866,23 @@ function registerDigestTool(pi, options = {}) {
|
|
|
5654
5866
|
const context = rest[0] ?? {};
|
|
5655
5867
|
const cwd = context.cwd ?? process.cwd();
|
|
5656
5868
|
const displayPath = typeof args?.path === "string" ? args.path : "?";
|
|
5657
|
-
let text = theme.fg("toolTitle", theme.bold("
|
|
5869
|
+
let text = theme.fg("toolTitle", theme.bold("inspect"));
|
|
5658
5870
|
text += ` ${linkToolPath(theme.fg("accent", displayPath), displayPath, cwd)}`;
|
|
5659
5871
|
if (args?.select !== undefined) {
|
|
5660
5872
|
const select = Array.isArray(args.select) ? args.select.join(",") : String(args.select);
|
|
5661
5873
|
text += theme.fg("dim", ` [${select}]`);
|
|
5662
5874
|
}
|
|
5663
|
-
if (typeof args?.
|
|
5664
|
-
text += theme.fg("dim", ` @ ${args.
|
|
5875
|
+
if (typeof args?.selector === "string" && args.selector)
|
|
5876
|
+
text += theme.fg("dim", ` @ ${args.selector}`);
|
|
5665
5877
|
return new Text7(clampLineToWidth(text, context.width), 0, 0);
|
|
5666
5878
|
},
|
|
5667
|
-
renderResult(result,
|
|
5668
|
-
const { isPartial, isError, expanded, width } = resolveRenderResultContext(
|
|
5879
|
+
renderResult(result, options, theme, ...rest) {
|
|
5880
|
+
const { isPartial, isError, expanded, width } = resolveRenderResultContext(options, rest);
|
|
5669
5881
|
if (isPartial)
|
|
5670
|
-
return renderPendingResult("pending
|
|
5882
|
+
return renderPendingResult("pending inspect", width, theme);
|
|
5671
5883
|
const textContent = result.content?.[0]?.type === "text" ? result.content[0].text : "";
|
|
5672
5884
|
if (isError || result.isError) {
|
|
5673
|
-
return renderErrorResult(textContent, { expanded, width, fallback: "
|
|
5885
|
+
return renderErrorResult(textContent, { expanded, width, fallback: "inspect failed", theme });
|
|
5674
5886
|
}
|
|
5675
5887
|
const value = result.details?.readSeekValue;
|
|
5676
5888
|
const facets = Array.isArray(value?.select) ? value.select : ["content"];
|
|
@@ -5686,131 +5898,88 @@ ${textContent}`;
|
|
|
5686
5898
|
});
|
|
5687
5899
|
}
|
|
5688
5900
|
|
|
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";
|
|
5901
|
+
// src/extract.ts
|
|
5695
5902
|
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"
|
|
5903
|
+
import { Type as Type12 } from "@sinclair/typebox";
|
|
5904
|
+
var EXTRACT_PROMPT_METADATA = defineToolPromptMetadata({
|
|
5905
|
+
promptUrl: new URL("../prompts/extract.md", import.meta.url),
|
|
5906
|
+
promptSnippet: "Extract a selected document asset to a file"
|
|
5701
5907
|
});
|
|
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.", {
|
|
5908
|
+
async function executeExtract(opts) {
|
|
5909
|
+
const { signal, cwd, onFileMutated } = opts;
|
|
5910
|
+
const input = opts.params;
|
|
5911
|
+
const selector = typeof input.selector === "string" ? input.selector.trim() : "";
|
|
5912
|
+
if (!selector.startsWith("asset:") || selector === "asset:") {
|
|
5913
|
+
return buildToolErrorResult("extract", "invalid-selector", "selector must identify an asset as asset:ID.", {
|
|
5718
5914
|
path: input.path
|
|
5719
5915
|
});
|
|
5720
5916
|
}
|
|
5721
|
-
const
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
visionLevel: input.visionLevel
|
|
5725
|
-
});
|
|
5726
|
-
if (!visionValidation.ok) {
|
|
5727
|
-
return buildToolErrorResult("view", "invalid-params-combo", `${visionValidation.error}.`, {
|
|
5917
|
+
const output = typeof input.output === "string" ? input.output.trim() : "";
|
|
5918
|
+
if (!output || output === "-") {
|
|
5919
|
+
return buildToolErrorResult("extract", "invalid-output", "output must be a file path, not stdout.", {
|
|
5728
5920
|
path: input.path
|
|
5729
5921
|
});
|
|
5730
5922
|
}
|
|
5731
5923
|
const filePath = resolveToCwd2(input.path, cwd);
|
|
5924
|
+
const outputPath = resolveToCwd2(output, cwd);
|
|
5732
5925
|
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]` : "";
|
|
5926
|
+
await readSeekExtract(filePath, selector, outputPath, { signal, cancelable: false });
|
|
5927
|
+
onFileMutated?.(outputPath);
|
|
5749
5928
|
return {
|
|
5750
|
-
content: [{ type: "text", text:
|
|
5929
|
+
content: [{ type: "text", text: `Extracted ${selector} to ${outputPath}` }],
|
|
5751
5930
|
details: {
|
|
5752
5931
|
readSeekValue: {
|
|
5753
|
-
tool: "
|
|
5932
|
+
tool: "extract",
|
|
5933
|
+
ok: true,
|
|
5754
5934
|
path: filePath,
|
|
5755
|
-
|
|
5935
|
+
selector,
|
|
5936
|
+
output: outputPath
|
|
5756
5937
|
}
|
|
5757
5938
|
}
|
|
5758
5939
|
};
|
|
5759
5940
|
} catch (error) {
|
|
5760
5941
|
const failure = classifyReadSeekFailure(error);
|
|
5761
|
-
return buildToolErrorResult("
|
|
5942
|
+
return buildToolErrorResult("extract", failure.code, failure.message, failure.hint ? { hint: failure.hint } : {});
|
|
5762
5943
|
}
|
|
5763
5944
|
}
|
|
5764
|
-
function
|
|
5945
|
+
function registerExtractTool(pi, options = {}) {
|
|
5765
5946
|
return registerReadSeekTool(pi, {
|
|
5766
|
-
name: "
|
|
5767
|
-
label: "
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
promptGuidelines: VIEW_PROMPT_METADATA.promptGuidelines,
|
|
5771
|
-
parameters: Type11.Object({
|
|
5947
|
+
name: "readSeek_extract",
|
|
5948
|
+
label: "Extract",
|
|
5949
|
+
...EXTRACT_PROMPT_METADATA,
|
|
5950
|
+
parameters: Type12.Object({
|
|
5772
5951
|
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 }))
|
|
5952
|
+
selector: Type12.String({ pattern: "^asset:.+", description: PARAM_DESCRIPTIONS.assetSelector }),
|
|
5953
|
+
output: Type12.String({ minLength: 1, pattern: "^(?!-$).+", description: PARAM_DESCRIPTIONS.output })
|
|
5782
5954
|
}),
|
|
5783
5955
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
5784
|
-
return
|
|
5956
|
+
return executeExtract({
|
|
5957
|
+
params,
|
|
5958
|
+
signal,
|
|
5959
|
+
cwd: ctx.cwd,
|
|
5960
|
+
onFileMutated: options.onFileMutated
|
|
5961
|
+
});
|
|
5785
5962
|
},
|
|
5786
5963
|
renderCall(args, theme, ...rest) {
|
|
5787
5964
|
const context = rest[0] ?? {};
|
|
5788
5965
|
const cwd = context.cwd ?? process.cwd();
|
|
5789
5966
|
const displayPath = typeof args?.path === "string" ? args.path : "?";
|
|
5790
|
-
const
|
|
5967
|
+
const output = typeof args?.output === "string" ? args.output : "?";
|
|
5968
|
+
let text = `${theme.fg("toolTitle", theme.bold("extract"))} ${linkToolPath(theme.fg("accent", displayPath), displayPath, cwd)}`;
|
|
5969
|
+
if (typeof args?.selector === "string")
|
|
5970
|
+
text += theme.fg("dim", ` ${args.selector}`);
|
|
5971
|
+
text += theme.fg("dim", ` → ${output}`);
|
|
5791
5972
|
return new Text8(clampLineToWidth(text, context.width), 0, 0);
|
|
5792
5973
|
},
|
|
5793
5974
|
renderResult(result, options, theme, ...rest) {
|
|
5794
|
-
const { isPartial, isError,
|
|
5975
|
+
const { isPartial, isError, width } = resolveRenderResultContext(options, rest);
|
|
5795
5976
|
if (isPartial)
|
|
5796
|
-
return renderPendingResult("pending
|
|
5977
|
+
return renderPendingResult("pending extract", width, theme);
|
|
5797
5978
|
const textContent = result.content?.[0]?.type === "text" ? result.content[0].text : "";
|
|
5798
5979
|
if (isError || result.isError) {
|
|
5799
|
-
return renderErrorResult(textContent, { expanded, width, fallback: "
|
|
5980
|
+
return renderErrorResult(textContent, { expanded: false, width, fallback: "extract failed", theme });
|
|
5800
5981
|
}
|
|
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);
|
|
5982
|
+
return new Text8(clampLineToWidth(summaryLine(textContent || "asset extracted", { theme, style: "success" }), width), 0, 0);
|
|
5814
5983
|
}
|
|
5815
5984
|
});
|
|
5816
5985
|
}
|
|
@@ -5841,32 +6010,20 @@ var READSEEK_TOOL_ENTRIES = [
|
|
|
5841
6010
|
{ builtIn: null, readSeekName: "readSeek_rename" },
|
|
5842
6011
|
{ builtIn: "write", readSeekName: "readSeek_write" },
|
|
5843
6012
|
{ builtIn: null, readSeekName: "readSeek_def" },
|
|
5844
|
-
{ builtIn: "read", readSeekName: "
|
|
5845
|
-
{ builtIn: null, readSeekName: "
|
|
6013
|
+
{ builtIn: "read", readSeekName: "readSeek_inspect" },
|
|
6014
|
+
{ builtIn: null, readSeekName: "readSeek_extract" }
|
|
5846
6015
|
];
|
|
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
6016
|
function piReadSeekExtension(pi) {
|
|
5857
6017
|
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
6018
|
const { settings } = resolveReadSeekJsonSettings();
|
|
5862
6019
|
const replacedBuiltIns = new Set(settings.overrideTools ?? []);
|
|
5863
6020
|
const swap = (builtIn, readSeekName) => replacedBuiltIns.has(builtIn) ? builtIn : readSeekName;
|
|
5864
|
-
const readName = swap("read", "
|
|
6021
|
+
const readName = swap("read", "readSeek_inspect");
|
|
5865
6022
|
const editName = swap("edit", "readSeek_edit");
|
|
5866
6023
|
const writeName = swap("write", "readSeek_write");
|
|
5867
6024
|
const grepName = swap("grep", "readSeek_grep");
|
|
5868
6025
|
const toolAliases = {
|
|
5869
|
-
|
|
6026
|
+
readSeek_inspect: readName,
|
|
5870
6027
|
readSeek_edit: editName,
|
|
5871
6028
|
readSeek_grep: grepName,
|
|
5872
6029
|
readSeek_write: writeName
|
|
@@ -5875,20 +6032,33 @@ function piReadSeekExtension(pi) {
|
|
|
5875
6032
|
const readSeekName = `readSeek_${name}`;
|
|
5876
6033
|
return [name, toolAliases[readSeekName] ?? readSeekName];
|
|
5877
6034
|
}));
|
|
5878
|
-
registerEditTool(pi, {
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
onFileAnchored: markAnchored,
|
|
6035
|
+
registerEditTool(pi, {
|
|
6036
|
+
wasReadInSession: (absolutePath) => sessionAnchors.hasFreshAnchors(absolutePath),
|
|
6037
|
+
onFileMutated: (absolutePath) => sessionAnchors.forget(absolutePath),
|
|
6038
|
+
name: editName,
|
|
6039
|
+
toolAliases
|
|
6040
|
+
});
|
|
6041
|
+
registerGrepTool(pi, {
|
|
6042
|
+
onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath),
|
|
6043
|
+
name: grepName
|
|
6044
|
+
});
|
|
6045
|
+
registerSearchTool(pi, { onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath) });
|
|
6046
|
+
registerRefsTool(pi, { onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath) });
|
|
6047
|
+
registerRenameTool(pi, { onFileMutated: (absolutePath) => sessionAnchors.forget(absolutePath) });
|
|
6048
|
+
registerDefTool(pi, { onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath) });
|
|
6049
|
+
registerInspectTool(pi, {
|
|
6050
|
+
onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath),
|
|
5886
6051
|
name: readName,
|
|
5887
6052
|
toolAliases,
|
|
5888
6053
|
imageMode: replacedBuiltIns.has("read") ? resolveReadSeekImageMode() : undefined
|
|
5889
6054
|
});
|
|
5890
|
-
|
|
5891
|
-
|
|
6055
|
+
registerExtractTool(pi, {
|
|
6056
|
+
onFileMutated: (absolutePath) => sessionAnchors.forget(absolutePath)
|
|
6057
|
+
});
|
|
6058
|
+
registerWriteTool(pi, {
|
|
6059
|
+
onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath),
|
|
6060
|
+
name: writeName
|
|
6061
|
+
});
|
|
5892
6062
|
const activeReadSeekNames = READSEEK_TOOL_ENTRIES.map((entry) => entry.builtIn && replacedBuiltIns.has(entry.builtIn) ? entry.builtIn : entry.readSeekName);
|
|
5893
6063
|
const inactiveReadSeekNames = new Set(READSEEK_TOOL_ENTRIES.filter((entry) => entry.builtIn !== null && replacedBuiltIns.has(entry.builtIn)).map((entry) => entry.readSeekName));
|
|
5894
6064
|
const activateReadSeekTools = () => {
|
|
@@ -5908,14 +6078,16 @@ function piReadSeekExtension(pi) {
|
|
|
5908
6078
|
pi.on("before_agent_start", (event) => {
|
|
5909
6079
|
if (!activateReadSeekTools())
|
|
5910
6080
|
return;
|
|
5911
|
-
return {
|
|
6081
|
+
return {
|
|
6082
|
+
systemPrompt: `${event.systemPrompt}
|
|
5912
6083
|
|
|
5913
|
-
${
|
|
6084
|
+
${renderToolRoutingPolicy({ aliases: policyAliases, title: "ReadSeek tool policy:" })}`
|
|
6085
|
+
};
|
|
5914
6086
|
});
|
|
5915
6087
|
pi.on("session_start", (_event, ctx) => {
|
|
5916
6088
|
sessionAnchors.clear();
|
|
5917
6089
|
const { warnings } = resolveReadSeekJsonSettings();
|
|
5918
|
-
const problems = warnings.map(
|
|
6090
|
+
const problems = warnings.map((warning) => `${warning.message} (${warning.source})`);
|
|
5919
6091
|
const availability = readSeekBinaryAvailability();
|
|
5920
6092
|
if (!availability.available) {
|
|
5921
6093
|
problems.push(`readseek installation is broken: ${availability.reason}`);
|