pi-readseek 0.9.15 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +176 -0
- package/README.md +7 -4
- package/dist/index.ts +1344 -1097
- package/package.json +3 -3
- 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 -37
- 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,27 +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",
|
|
62
|
+
inspectDepth: "Maximum structural depth for map output; defaults to 1 in agent tools",
|
|
73
63
|
offset: "One-based starting line",
|
|
74
64
|
readLimit: "Maximum lines to return",
|
|
75
65
|
symbolRead: "Symbol name to read",
|
|
76
66
|
bundle: "Include related symbols from the same file; requires symbol",
|
|
77
67
|
image: "Standalone image mode: none, all, ocr, caption, objects. Required for standalone images.",
|
|
78
|
-
|
|
79
|
-
visionModeView: "Analyze selected assets: all, ocr, caption, or objects",
|
|
68
|
+
visionModeInspect: "Standalone image mode: none, all, caption, objects, or ocr",
|
|
80
69
|
visionLevel: "Vision inference level: low (default), medium, or high",
|
|
81
|
-
|
|
82
|
-
page: "One-based source page",
|
|
83
|
-
kind: "Node kind filter",
|
|
84
|
-
depth: "Maximum depth below selected roots",
|
|
85
|
-
outline: "Return outline nodes only",
|
|
70
|
+
output: "Destination file for extracted binary data; stdout is not supported",
|
|
86
71
|
edits: "Use set_line, replace_lines, insert_after, replace_symbol, or replace",
|
|
87
72
|
editVariant: "Exactly one nested edit variant",
|
|
88
73
|
setLine: 'Replace one line: {"set_line":{"anchor":"LINE:HASH","new_text":"..."}}',
|
|
@@ -119,11 +104,11 @@ var PARAM_DESCRIPTIONS = {
|
|
|
119
104
|
scopeContext: "Context lines within each symbol"
|
|
120
105
|
};
|
|
121
106
|
var TOOL_DESCRIPTIONS = {
|
|
122
|
-
|
|
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.",
|
|
123
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":"..."}}`.',
|
|
124
109
|
write: "Create or replace a complete text file and return `LINE:HASH` anchors. Use `edit` for small changes to an existing file.",
|
|
125
110
|
grep: "Search text or regex in files and return edit-ready `LINE:HASH` anchors. Use for identifiers, strings, configuration, errors, comments, and documentation.",
|
|
126
|
-
|
|
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.",
|
|
127
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.",
|
|
128
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.",
|
|
129
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.",
|
|
@@ -135,18 +120,11 @@ function renderToolDescription(name, aliases = {}) {
|
|
|
135
120
|
throw new Error(`Missing TOOL_DESCRIPTIONS for ${name}`);
|
|
136
121
|
return TOOL_NAMES.reduce((description, toolName) => description.replaceAll(`\`${toolName}\``, `\`${aliases[toolName] ?? toolName}\``), desc);
|
|
137
122
|
}
|
|
138
|
-
var
|
|
139
|
-
|
|
140
|
-
type: "
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
others: {
|
|
144
|
-
type: "boolean",
|
|
145
|
-
description: PARAM_DESCRIPTIONS.others
|
|
146
|
-
},
|
|
147
|
-
ignored: {
|
|
148
|
-
type: "boolean",
|
|
149
|
-
description: PARAM_DESCRIPTIONS.ignored
|
|
123
|
+
var gitScopeSchema = {
|
|
124
|
+
gitScope: {
|
|
125
|
+
type: "string",
|
|
126
|
+
enum: GIT_SCOPES,
|
|
127
|
+
description: PARAM_DESCRIPTIONS.gitScope
|
|
150
128
|
}
|
|
151
129
|
};
|
|
152
130
|
var editItemSchema = {
|
|
@@ -212,28 +190,31 @@ var editItemSchema = {
|
|
|
212
190
|
};
|
|
213
191
|
var TOOL_CONTRACTS = [
|
|
214
192
|
{
|
|
215
|
-
name: "
|
|
216
|
-
title: "
|
|
217
|
-
description: TOOL_DESCRIPTIONS.
|
|
193
|
+
name: "inspect",
|
|
194
|
+
title: "Inspect input",
|
|
195
|
+
description: TOOL_DESCRIPTIONS.inspect,
|
|
218
196
|
inputSchema: {
|
|
219
197
|
type: "object",
|
|
220
198
|
properties: {
|
|
221
199
|
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
200
|
+
selector: { type: "string", description: PARAM_DESCRIPTIONS.selector },
|
|
222
201
|
select: { type: "string", description: PARAM_DESCRIPTIONS.select },
|
|
223
|
-
|
|
202
|
+
query: { type: "string", description: PARAM_DESCRIPTIONS.query },
|
|
224
203
|
end: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.end },
|
|
225
204
|
limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.limit },
|
|
205
|
+
depth: { type: "integer", minimum: 0, description: PARAM_DESCRIPTIONS.inspectDepth },
|
|
226
206
|
language: { type: "string", description: PARAM_DESCRIPTIONS.languageOverride },
|
|
227
207
|
visionMode: {
|
|
228
208
|
type: "string",
|
|
229
209
|
enum: [...VISION_MODES],
|
|
230
|
-
description: PARAM_DESCRIPTIONS.
|
|
210
|
+
description: PARAM_DESCRIPTIONS.visionModeInspect
|
|
231
211
|
},
|
|
232
212
|
visionLevel: {
|
|
233
213
|
type: "string",
|
|
234
214
|
enum: [...VISION_LEVELS],
|
|
235
215
|
description: PARAM_DESCRIPTIONS.visionLevel
|
|
236
|
-
}
|
|
216
|
+
},
|
|
217
|
+
stdinName: { type: "string", description: PARAM_DESCRIPTIONS.stdinName }
|
|
237
218
|
},
|
|
238
219
|
required: ["path"],
|
|
239
220
|
additionalProperties: false
|
|
@@ -307,34 +288,27 @@ var TOOL_CONTRACTS = [
|
|
|
307
288
|
}
|
|
308
289
|
},
|
|
309
290
|
{
|
|
310
|
-
name: "
|
|
311
|
-
title: "
|
|
312
|
-
description: TOOL_DESCRIPTIONS.
|
|
291
|
+
name: "extract",
|
|
292
|
+
title: "Extract asset",
|
|
293
|
+
description: TOOL_DESCRIPTIONS.extract,
|
|
313
294
|
inputSchema: {
|
|
314
295
|
type: "object",
|
|
315
296
|
properties: {
|
|
316
297
|
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
317
|
-
|
|
318
|
-
page: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.page },
|
|
319
|
-
kind: {
|
|
298
|
+
selector: {
|
|
320
299
|
type: "string",
|
|
321
|
-
|
|
322
|
-
description: PARAM_DESCRIPTIONS.
|
|
300
|
+
pattern: "^asset:.+",
|
|
301
|
+
description: PARAM_DESCRIPTIONS.assetSelector
|
|
323
302
|
},
|
|
324
|
-
|
|
325
|
-
outline: { type: "boolean", description: PARAM_DESCRIPTIONS.outline },
|
|
326
|
-
visionMode: {
|
|
303
|
+
output: {
|
|
327
304
|
type: "string",
|
|
328
|
-
|
|
329
|
-
|
|
305
|
+
minLength: 1,
|
|
306
|
+
pattern: "^(?!-$).+",
|
|
307
|
+
description: PARAM_DESCRIPTIONS.output
|
|
330
308
|
},
|
|
331
|
-
|
|
332
|
-
type: "string",
|
|
333
|
-
enum: [...VISION_LEVELS],
|
|
334
|
-
description: PARAM_DESCRIPTIONS.visionLevel
|
|
335
|
-
}
|
|
309
|
+
stdinName: { type: "string", description: PARAM_DESCRIPTIONS.stdinName }
|
|
336
310
|
},
|
|
337
|
-
required: ["path"],
|
|
311
|
+
required: ["path", "selector", "output"],
|
|
338
312
|
additionalProperties: false
|
|
339
313
|
}
|
|
340
314
|
},
|
|
@@ -348,7 +322,8 @@ var TOOL_CONTRACTS = [
|
|
|
348
322
|
pattern: { type: "string", description: PARAM_DESCRIPTIONS.pattern },
|
|
349
323
|
path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
|
|
350
324
|
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
351
|
-
|
|
325
|
+
limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.matchLimit },
|
|
326
|
+
...gitScopeSchema
|
|
352
327
|
},
|
|
353
328
|
required: ["pattern"],
|
|
354
329
|
additionalProperties: false
|
|
@@ -364,7 +339,7 @@ var TOOL_CONTRACTS = [
|
|
|
364
339
|
name: { type: "string", description: PARAM_DESCRIPTIONS.name },
|
|
365
340
|
path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
|
|
366
341
|
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
367
|
-
...
|
|
342
|
+
...gitScopeSchema
|
|
368
343
|
},
|
|
369
344
|
required: ["name"],
|
|
370
345
|
additionalProperties: false
|
|
@@ -383,7 +358,7 @@ var TOOL_CONTRACTS = [
|
|
|
383
358
|
scope: { type: "boolean", description: PARAM_DESCRIPTIONS.scope },
|
|
384
359
|
line: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.line },
|
|
385
360
|
column: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.column },
|
|
386
|
-
...
|
|
361
|
+
...gitScopeSchema
|
|
387
362
|
},
|
|
388
363
|
additionalProperties: false
|
|
389
364
|
}
|
|
@@ -401,7 +376,9 @@ var TOOL_CONTRACTS = [
|
|
|
401
376
|
to: { type: "string", description: PARAM_DESCRIPTIONS.to },
|
|
402
377
|
workspace: { type: "boolean", description: PARAM_DESCRIPTIONS.workspace },
|
|
403
378
|
apply: { type: "boolean", description: PARAM_DESCRIPTIONS.apply },
|
|
404
|
-
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
|
|
405
382
|
},
|
|
406
383
|
required: ["path", "line", "to"],
|
|
407
384
|
additionalProperties: false
|
|
@@ -411,7 +388,7 @@ var TOOL_CONTRACTS = [
|
|
|
411
388
|
// ../readseek-api/src/policy.ts
|
|
412
389
|
var REPLACEABLE_TOOLS = [
|
|
413
390
|
{
|
|
414
|
-
tool: "
|
|
391
|
+
tool: "inspect",
|
|
415
392
|
builtIn: "read",
|
|
416
393
|
benefit: "it returns LINE:HASH anchors for safe edits."
|
|
417
394
|
},
|
|
@@ -432,8 +409,8 @@ var REPLACEABLE_TOOLS = [
|
|
|
432
409
|
}
|
|
433
410
|
];
|
|
434
411
|
var TOOL_COMPACT_GUIDELINES = {
|
|
435
|
-
|
|
436
|
-
"Use `
|
|
412
|
+
inspect: [
|
|
413
|
+
"Use `inspect` for shallow maps, diagnostics, and identity; use `def`, `refs`, or `search` for targeted symbol questions."
|
|
437
414
|
],
|
|
438
415
|
edit: [
|
|
439
416
|
"Prefer set_line, replace_lines, and insert_after; use replace only when anchors are impractical."
|
|
@@ -462,7 +439,7 @@ function renderCompactGuidelines(name, aliases = {}) {
|
|
|
462
439
|
}
|
|
463
440
|
function renderToolRoutingPolicy(options = {}) {
|
|
464
441
|
const aliases = options.aliases ?? {};
|
|
465
|
-
const
|
|
442
|
+
const inspect = resolveToolName("inspect", aliases);
|
|
466
443
|
const edit = resolveToolName("edit", aliases);
|
|
467
444
|
const write = resolveToolName("write", aliases);
|
|
468
445
|
const grep = resolveToolName("grep", aliases);
|
|
@@ -470,11 +447,11 @@ function renderToolRoutingPolicy(options = {}) {
|
|
|
470
447
|
const def = resolveToolName("def", aliases);
|
|
471
448
|
const refs = resolveToolName("refs", aliases);
|
|
472
449
|
const rename = resolveToolName("rename", aliases);
|
|
473
|
-
const
|
|
450
|
+
const extract = resolveToolName("extract", aliases);
|
|
474
451
|
if (options.style === "server") {
|
|
475
452
|
return [
|
|
476
|
-
"Structural code navigation, document
|
|
477
|
-
"For structural questions, prefer these tools to text Grep/Bash: definitions, references, AST search
|
|
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.",
|
|
478
455
|
`Use ${edit} for LINE:HASH-anchored mutations (preview with apply=false; apply defaults to true and re-checks plan_hash).`,
|
|
479
456
|
"Paths resolve against CLAUDE_PROJECT_DIR. Native Read/Grep/Edit hooks provide passive context;",
|
|
480
457
|
"call MCP tools for active navigation, document questions, or anchored edits."
|
|
@@ -485,7 +462,7 @@ function renderToolRoutingPolicy(options = {}) {
|
|
|
485
462
|
if (options.preferOverBuiltIns) {
|
|
486
463
|
bullets.push("- Prefer ReadSeek tools over built-ins when they can do the job.");
|
|
487
464
|
}
|
|
488
|
-
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.`);
|
|
489
466
|
if (options.demoteBuiltIns && options.demoteBuiltIns.length > 0) {
|
|
490
467
|
bullets.push(`- Do not use built-in ${options.demoteBuiltIns.join(", ")} when a ReadSeek tool can do the job.`);
|
|
491
468
|
}
|
|
@@ -512,12 +489,6 @@ function tryParseLineAnchor(anchor) {
|
|
|
512
489
|
}
|
|
513
490
|
|
|
514
491
|
// ../readseek-api/src/validation.ts
|
|
515
|
-
function validateGitSelection(input) {
|
|
516
|
-
if (input.ignored && !input.others) {
|
|
517
|
-
return { ok: false, error: "ignored requires others=true" };
|
|
518
|
-
}
|
|
519
|
-
return { ok: true };
|
|
520
|
-
}
|
|
521
492
|
function validateRefsNameVsScope(input) {
|
|
522
493
|
if (input.scope) {
|
|
523
494
|
if (input.line === undefined) {
|
|
@@ -534,11 +505,8 @@ function validateRefsNameVsScope(input) {
|
|
|
534
505
|
return { ok: true };
|
|
535
506
|
}
|
|
536
507
|
function validateVisionLevelVsMode(input) {
|
|
537
|
-
if (input.
|
|
538
|
-
return { ok: false, error: "
|
|
539
|
-
}
|
|
540
|
-
if (input.visionLevel !== undefined && input.visionMode === undefined) {
|
|
541
|
-
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" };
|
|
542
510
|
}
|
|
543
511
|
return { ok: true };
|
|
544
512
|
}
|
|
@@ -609,21 +577,54 @@ function validateExactlyOneEditVariant(edits) {
|
|
|
609
577
|
}
|
|
610
578
|
var MIN_SAFE_INTEGER = BigInt(Number.MIN_SAFE_INTEGER);
|
|
611
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
|
+
}
|
|
612
620
|
// ../readseek-api/src/argv.ts
|
|
621
|
+
var DEFAULT_INSPECT_MAP_DEPTH = 1;
|
|
613
622
|
function buildLineLocation(line, column) {
|
|
614
623
|
return column === undefined ? `line:${line}` : `line:${line}:${column}`;
|
|
615
624
|
}
|
|
616
|
-
function
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
throw new Error(validated.error);
|
|
620
|
-
}
|
|
621
|
-
if (input.cached)
|
|
622
|
-
args.push("--cached");
|
|
623
|
-
if (input.others)
|
|
624
|
-
args.push("--others");
|
|
625
|
-
if (input.ignored)
|
|
626
|
-
args.push("--ignored");
|
|
625
|
+
function appendGitScopeArgs(args, input) {
|
|
626
|
+
if (input.gitScope !== undefined)
|
|
627
|
+
args.push("--git-scope", input.gitScope);
|
|
627
628
|
return args;
|
|
628
629
|
}
|
|
629
630
|
function pushOptionalString(args, flag, value) {
|
|
@@ -652,51 +653,163 @@ function selectArg(select) {
|
|
|
652
653
|
return;
|
|
653
654
|
return Array.isArray(select) ? select.join(",") : select;
|
|
654
655
|
}
|
|
655
|
-
function
|
|
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 };
|
|
719
|
+
}
|
|
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
|
+
}
|
|
656
725
|
const vision = validateVisionLevelVsMode({
|
|
657
726
|
visionMode: input.visionMode,
|
|
658
727
|
visionLevel: input.visionLevel
|
|
659
728
|
});
|
|
660
729
|
if (!vision.ok)
|
|
661
730
|
throw new Error(vision.error);
|
|
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
|
+
}
|
|
743
|
+
if (input.depth !== undefined) {
|
|
744
|
+
if (!Number.isSafeInteger(input.depth) || input.depth < 0) {
|
|
745
|
+
throw new Error("depth must be a non-negative integer");
|
|
746
|
+
}
|
|
747
|
+
if (!hasMap)
|
|
748
|
+
throw new Error("depth requires the map facet");
|
|
749
|
+
}
|
|
750
|
+
if (input.selector !== undefined && input.query !== undefined) {
|
|
751
|
+
throw new Error("cannot combine selector with query");
|
|
752
|
+
}
|
|
753
|
+
if (!hasContent && (input.end !== undefined || input.limit !== undefined || input.query !== undefined)) {
|
|
754
|
+
throw new Error("ranges and queries require the content facet");
|
|
755
|
+
}
|
|
756
|
+
if (hasIdentity && input.selector === undefined) {
|
|
757
|
+
throw new Error("the identity facet requires a selector");
|
|
758
|
+
}
|
|
759
|
+
if (input.selector !== undefined && !hasContent && !hasIdentity) {
|
|
760
|
+
throw new Error("selectors require the content or identity facet");
|
|
761
|
+
}
|
|
762
|
+
if (input.selector !== undefined) {
|
|
763
|
+
const selector = parseSelector(input.selector);
|
|
764
|
+
if (selector.kind === "symbol" && hasIdentity) {
|
|
765
|
+
throw new Error("the identity facet does not accept symbol selectors");
|
|
766
|
+
}
|
|
767
|
+
if (selector.column !== undefined && !hasIdentity) {
|
|
768
|
+
throw new Error("selector columns require the identity facet");
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
const hasVision = input.visionMode !== undefined || input.visionLevel !== undefined;
|
|
772
|
+
if (hasVision && !hasContent) {
|
|
773
|
+
throw new Error("vision options require the content facet");
|
|
774
|
+
}
|
|
775
|
+
if (hasVision && hasSourceFacet) {
|
|
776
|
+
throw new Error("vision options cannot be combined with source facets");
|
|
777
|
+
}
|
|
662
778
|
const args = [];
|
|
663
779
|
if (input.readseekDir !== undefined)
|
|
664
780
|
args.push("--readseek-dir", input.readseekDir);
|
|
665
|
-
args.push("
|
|
781
|
+
args.push("inspect", input.path);
|
|
782
|
+
if (input.selector !== undefined)
|
|
783
|
+
args.push(input.selector);
|
|
666
784
|
const select = selectArg(input.select);
|
|
667
785
|
if (select !== undefined)
|
|
668
786
|
args.push("--select", select);
|
|
669
|
-
pushOptionalString(args, "--
|
|
787
|
+
pushOptionalString(args, "--query", input.query);
|
|
670
788
|
pushOptionalNumber(args, "--end", input.end);
|
|
671
789
|
pushOptionalNumber(args, "--limit", input.limit);
|
|
790
|
+
pushOptionalNumber(args, "--depth", input.depth);
|
|
672
791
|
pushOptionalString(args, "--language", input.language);
|
|
673
792
|
pushOptionalString(args, "--vision-mode", input.visionMode);
|
|
674
793
|
pushOptionalString(args, "--vision-level", input.visionLevel);
|
|
675
794
|
pushOptionalString(args, "--stdin-name", input.stdinName);
|
|
795
|
+
args.push("--kdl");
|
|
676
796
|
return args;
|
|
677
797
|
}
|
|
678
|
-
function
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
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
|
+
}
|
|
686
808
|
const args = [];
|
|
687
809
|
if (input.readseekDir !== undefined)
|
|
688
810
|
args.push("--readseek-dir", input.readseekDir);
|
|
689
|
-
args.push("
|
|
690
|
-
|
|
691
|
-
args.push("--format", input.format);
|
|
692
|
-
pushOptionalString(args, "--node", input.node);
|
|
693
|
-
pushOptionalNumber(args, "--page", input.page);
|
|
694
|
-
pushOptionalString(args, "--kind", input.kind);
|
|
695
|
-
pushOptionalNumber(args, "--depth", input.depth);
|
|
696
|
-
if (input.outline)
|
|
697
|
-
args.push("--outline");
|
|
698
|
-
pushOptionalString(args, "--vision-mode", input.visionMode);
|
|
699
|
-
pushOptionalString(args, "--vision-level", input.visionLevel);
|
|
811
|
+
args.push("--output", input.output, "extract", input.path, input.selector);
|
|
812
|
+
pushOptionalString(args, "--stdin-name", input.stdinName);
|
|
700
813
|
return args;
|
|
701
814
|
}
|
|
702
815
|
function buildSearchArgs(input) {
|
|
@@ -704,15 +817,17 @@ function buildSearchArgs(input) {
|
|
|
704
817
|
pushOptionalString(args, "--path", input.path);
|
|
705
818
|
pushOptionalString(args, "--language", input.language);
|
|
706
819
|
pushOptionalNumber(args, "--limit", input.limit);
|
|
707
|
-
|
|
820
|
+
appendGitScopeArgs(args, input);
|
|
821
|
+
args.push("--kdl");
|
|
822
|
+
return args;
|
|
708
823
|
}
|
|
709
824
|
function buildDefArgs(input) {
|
|
710
825
|
const args = ["def", input.name];
|
|
711
826
|
pushOptionalString(args, "--path", input.path);
|
|
712
|
-
if (input.format !== undefined)
|
|
713
|
-
args.push("--format", input.format);
|
|
714
827
|
pushOptionalString(args, "--language", input.language);
|
|
715
|
-
|
|
828
|
+
appendGitScopeArgs(args, input);
|
|
829
|
+
args.push("--kdl");
|
|
830
|
+
return args;
|
|
716
831
|
}
|
|
717
832
|
function buildRefsArgs(input) {
|
|
718
833
|
const validated = validateRefsNameVsScope(input);
|
|
@@ -727,10 +842,10 @@ function buildRefsArgs(input) {
|
|
|
727
842
|
pushOptionalNumber(args, "--line", input.line);
|
|
728
843
|
pushOptionalNumber(args, "--column", input.column);
|
|
729
844
|
}
|
|
730
|
-
if (input.format !== undefined)
|
|
731
|
-
args.push("--format", input.format);
|
|
732
845
|
pushOptionalString(args, "--language", input.language);
|
|
733
|
-
|
|
846
|
+
appendGitScopeArgs(args, input);
|
|
847
|
+
args.push("--kdl");
|
|
848
|
+
return args;
|
|
734
849
|
}
|
|
735
850
|
function buildEditArgs(input) {
|
|
736
851
|
const args = ["edit", input.path];
|
|
@@ -742,6 +857,7 @@ function buildEditArgs(input) {
|
|
|
742
857
|
pushOptionalString(args, "--language", input.language);
|
|
743
858
|
if (input.requestFromStdin)
|
|
744
859
|
args.push("--edits", "-");
|
|
860
|
+
args.push("--kdl");
|
|
745
861
|
return args;
|
|
746
862
|
}
|
|
747
863
|
function buildRenameArgs(input) {
|
|
@@ -754,7 +870,9 @@ function buildRenameArgs(input) {
|
|
|
754
870
|
flagOrder: input.flagOrder ?? "plan-hash-first"
|
|
755
871
|
});
|
|
756
872
|
pushOptionalString(args, "--language", input.language);
|
|
757
|
-
|
|
873
|
+
appendGitScopeArgs(args, input);
|
|
874
|
+
args.push("--kdl");
|
|
875
|
+
return args;
|
|
758
876
|
}
|
|
759
877
|
// ../readseek-api/src/platform.ts
|
|
760
878
|
import { accessSync, constants as fsConstants, existsSync, statSync } from "node:fs";
|
|
@@ -769,6 +887,8 @@ var READSEEK_PLATFORM_PACKAGES = {
|
|
|
769
887
|
};
|
|
770
888
|
var resolveFromPackage = createRequire(import.meta.url).resolve;
|
|
771
889
|
function readSeekBinaryName(platform = process.platform) {
|
|
890
|
+
if (platform === "linux")
|
|
891
|
+
return "readseek.AppImage";
|
|
772
892
|
return platform === "win32" ? "readseek.exe" : "readseek";
|
|
773
893
|
}
|
|
774
894
|
function readSeekPlatformKey(platform = process.platform, arch = process.arch) {
|
|
@@ -800,9 +920,9 @@ function resolveReadSeekBinaryFromPackages(resolver = resolveFromPackage, option
|
|
|
800
920
|
const platformPackage = READSEEK_PLATFORM_PACKAGES[key];
|
|
801
921
|
if (!platformPackage) {
|
|
802
922
|
const supported = Object.keys(READSEEK_PLATFORM_PACKAGES).join(", ");
|
|
803
|
-
throw new Error(`@jarkkojs/readseek ships no binary for ${key}; it supports ${supported}`);
|
|
923
|
+
throw new Error(`@jarkkojs/readseek-api ships no binary for ${key}; it supports ${supported}`);
|
|
804
924
|
}
|
|
805
|
-
const metaPackage = options.metaPackage ?? "@jarkkojs/readseek";
|
|
925
|
+
const metaPackage = options.metaPackage ?? "@jarkkojs/readseek-api";
|
|
806
926
|
const metaPackageJson = resolve(`${metaPackage}/package.json`);
|
|
807
927
|
const metaPackageDir = path.dirname(metaPackageJson);
|
|
808
928
|
let packageJsonPath;
|
|
@@ -817,22 +937,304 @@ function resolveReadSeekBinaryFromPackages(resolver = resolveFromPackage, option
|
|
|
817
937
|
accessSync(binaryPath, fsConstants.X_OK);
|
|
818
938
|
return binaryPath;
|
|
819
939
|
}
|
|
820
|
-
// ../readseek-api/src/
|
|
821
|
-
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*$/;
|
|
822
942
|
function cleanReadSeekErrorText(stderr) {
|
|
823
943
|
return stderr.replace(READSEEK_USAGE_HINT, "").replace(/^error:\s*/i, "").replace(/\s*\n\s*/g, " ").trim();
|
|
824
944
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
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}`);
|
|
831
1230
|
}
|
|
832
1231
|
}
|
|
833
1232
|
// ../readseek-api/src/process.ts
|
|
834
1233
|
var DEFAULT_READSEEK_TIMEOUT_MS = 120000;
|
|
835
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
|
+
}
|
|
836
1238
|
function resolveProcessLimits(options = {}) {
|
|
837
1239
|
return {
|
|
838
1240
|
timeoutMs: options.timeoutMs ?? DEFAULT_READSEEK_TIMEOUT_MS,
|
|
@@ -863,9 +1265,6 @@ function ensureSuccessfulStdout(result) {
|
|
|
863
1265
|
return result.stdout;
|
|
864
1266
|
throw failureFromProcessResult(result);
|
|
865
1267
|
}
|
|
866
|
-
function parseSuccessfulJson(result) {
|
|
867
|
-
return parseReadSeekJson(ensureSuccessfulStdout(result));
|
|
868
|
-
}
|
|
869
1268
|
// ../readseek-api/src/process-node.ts
|
|
870
1269
|
import { spawn } from "node:child_process";
|
|
871
1270
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -931,7 +1330,7 @@ async function runReadSeekNode(binaryPath, args, options = {}) {
|
|
|
931
1330
|
try {
|
|
932
1331
|
child = spawn(binaryPath, [...args], {
|
|
933
1332
|
cwd: options.cwd,
|
|
934
|
-
env:
|
|
1333
|
+
env: readSeekProcessEnvironment(),
|
|
935
1334
|
shell: false,
|
|
936
1335
|
stdio: [options.stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"],
|
|
937
1336
|
windowsHide: true
|
|
@@ -974,346 +1373,357 @@ async function runReadSeekNode(binaryPath, args, options = {}) {
|
|
|
974
1373
|
async function runReadSeekNodeText(binaryPath, args, options = {}) {
|
|
975
1374
|
return ensureSuccessfulStdout(await runReadSeekNode(binaryPath, args, options));
|
|
976
1375
|
}
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
function
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
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
|
-
|
|
1376
|
+
// ../readseek-api/src/client-output.ts
|
|
1377
|
+
import { Type } from "@sinclair/typebox";
|
|
1378
|
+
import { AssertError, Value } from "@sinclair/typebox/value";
|
|
1379
|
+
var OPERATIONS = ["Clone", "Clean", "Default", "Assert"];
|
|
1380
|
+
function parse2(schema, value, what) {
|
|
1381
|
+
try {
|
|
1382
|
+
return Value.Parse(OPERATIONS, schema, value);
|
|
1383
|
+
} catch (error) {
|
|
1384
|
+
if (!(error instanceof AssertError))
|
|
1385
|
+
throw error;
|
|
1386
|
+
const detail = error.error;
|
|
1387
|
+
throw new Error(`invalid readseek ${what}${detail?.path ?? ""}: ${detail?.message ?? "invalid value"}`);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
var Int = Type.Integer({ minimum: Number.MIN_SAFE_INTEGER, maximum: Number.MAX_SAFE_INTEGER });
|
|
1391
|
+
var Str = Type.String();
|
|
1392
|
+
var Bool = Type.Boolean();
|
|
1393
|
+
var OptStr = Type.Optional(Str);
|
|
1394
|
+
var OptInt = Type.Optional(Int);
|
|
1395
|
+
var Hashline = Type.Object({ line: Int, hash: Str, text: Str });
|
|
1396
|
+
var Hashlines = Type.Array(Hashline);
|
|
1397
|
+
var ReadOutput = Type.Object({
|
|
1398
|
+
file: Str,
|
|
1399
|
+
language: Str,
|
|
1400
|
+
line_count: Int,
|
|
1401
|
+
file_hash: Str,
|
|
1402
|
+
start_line: Int,
|
|
1403
|
+
end_line: Int,
|
|
1404
|
+
hashlines: Hashlines
|
|
1405
|
+
});
|
|
1406
|
+
var SymbolOutput = Type.Object({
|
|
1407
|
+
kind: Str,
|
|
1408
|
+
name: Str,
|
|
1409
|
+
qualified_name: OptStr,
|
|
1410
|
+
start_line: Int,
|
|
1411
|
+
end_line: Int,
|
|
1412
|
+
start_hash: OptStr,
|
|
1413
|
+
end_hash: OptStr,
|
|
1414
|
+
start_anchor: OptStr,
|
|
1415
|
+
end_anchor: OptStr
|
|
1416
|
+
});
|
|
1417
|
+
var MapNode = Type.Recursive((Self) => Type.Object({
|
|
1418
|
+
kind: Str,
|
|
1419
|
+
name: Str,
|
|
1420
|
+
start_line: Int,
|
|
1421
|
+
end_line: Int,
|
|
1422
|
+
start_anchor: Str,
|
|
1423
|
+
end_anchor: Str,
|
|
1424
|
+
children: Type.Array(Self)
|
|
1425
|
+
}));
|
|
1426
|
+
var MapOutput = Type.Object({
|
|
1427
|
+
file: Str,
|
|
1428
|
+
language: Str,
|
|
1429
|
+
line_count: Int,
|
|
1430
|
+
file_hash: Str,
|
|
1431
|
+
root: Type.Optional(MapNode),
|
|
1432
|
+
symbols: Type.Array(SymbolOutput)
|
|
1433
|
+
});
|
|
1434
|
+
var SearchSpan = {
|
|
1435
|
+
start_byte: OptInt,
|
|
1436
|
+
end_byte: OptInt,
|
|
1437
|
+
text: OptStr,
|
|
1438
|
+
start_line: Int,
|
|
1439
|
+
end_line: Int,
|
|
1440
|
+
start_hash: Str,
|
|
1441
|
+
end_hash: Str,
|
|
1442
|
+
hashlines: Hashlines
|
|
1443
|
+
};
|
|
1444
|
+
var SearchCapture = Type.Object({ name: Str, ...SearchSpan });
|
|
1445
|
+
var SearchMatch = Type.Object({
|
|
1446
|
+
pattern_index: Type.Integer({ default: 0 }),
|
|
1447
|
+
...SearchSpan,
|
|
1448
|
+
captures: Type.Array(SearchCapture)
|
|
1449
|
+
});
|
|
1450
|
+
var SearchFileOutput = Type.Object({
|
|
1451
|
+
file: Str,
|
|
1452
|
+
language: Str,
|
|
1453
|
+
file_hash: Str,
|
|
1454
|
+
matches: Type.Array(SearchMatch)
|
|
1455
|
+
});
|
|
1456
|
+
var SearchEnvelope = Type.Object({
|
|
1457
|
+
results: Type.Array(SearchFileOutput),
|
|
1458
|
+
returned_matches: OptInt,
|
|
1459
|
+
truncated: Type.Optional(Bool),
|
|
1460
|
+
byte_basis: OptStr
|
|
1461
|
+
});
|
|
1462
|
+
var RefsEnvelope = Type.Object({
|
|
1463
|
+
references: Type.Array(Type.Object({
|
|
1464
|
+
file: Str,
|
|
1465
|
+
line: Int,
|
|
1466
|
+
column: Int,
|
|
1467
|
+
line_hash: Str,
|
|
1468
|
+
text: Str,
|
|
1469
|
+
symbol: Type.Optional(Type.Object({ qualified_name: OptStr }))
|
|
1470
|
+
}))
|
|
1471
|
+
});
|
|
1472
|
+
var Diagnostic = Type.Object({
|
|
1473
|
+
kind: Type.Union([Type.Literal("error"), Type.Literal("missing")]),
|
|
1474
|
+
start_line: Int,
|
|
1475
|
+
end_line: Int
|
|
1476
|
+
});
|
|
1477
|
+
var CheckEnvelope = Type.Object({
|
|
1478
|
+
file_hash: OptStr,
|
|
1479
|
+
error_count: Int,
|
|
1480
|
+
missing_count: Int,
|
|
1481
|
+
diagnostics: Type.Array(Diagnostic)
|
|
1482
|
+
});
|
|
1483
|
+
var DetectedObject = Type.Object({
|
|
1484
|
+
label: Str,
|
|
1485
|
+
bbox: Type.Tuple([Int, Int, Int, Int])
|
|
1486
|
+
});
|
|
1487
|
+
var PreparedImage = Type.Object({
|
|
1488
|
+
mime: Str,
|
|
1489
|
+
encoding: Type.Literal("base64"),
|
|
1490
|
+
data: Str
|
|
1491
|
+
});
|
|
1492
|
+
var SourceDetection = Type.Object({
|
|
1493
|
+
kind: Type.Literal("source"),
|
|
1494
|
+
type: Str,
|
|
1495
|
+
file: Str,
|
|
1496
|
+
language: Str,
|
|
1497
|
+
engine: OptStr,
|
|
1498
|
+
supported: Bool,
|
|
1499
|
+
mime: OptStr,
|
|
1500
|
+
syntax: OptStr
|
|
1501
|
+
});
|
|
1502
|
+
var ImageDetection = Type.Object({
|
|
1503
|
+
kind: Type.Literal("image"),
|
|
1504
|
+
type: Str,
|
|
1505
|
+
file: Str,
|
|
1506
|
+
mime: OptStr,
|
|
1507
|
+
format: Str,
|
|
1508
|
+
width: Int,
|
|
1509
|
+
height: Int,
|
|
1510
|
+
animated: Bool,
|
|
1511
|
+
encoding: Type.Optional(Type.Literal("base64")),
|
|
1512
|
+
data: OptStr,
|
|
1513
|
+
ocr: OptStr,
|
|
1514
|
+
caption: OptStr,
|
|
1515
|
+
objects: Type.Optional(Type.Array(DetectedObject))
|
|
1516
|
+
});
|
|
1517
|
+
var TextDetection = Type.Object({
|
|
1518
|
+
kind: Type.Literal("text"),
|
|
1519
|
+
type: Str,
|
|
1520
|
+
file: Str,
|
|
1521
|
+
mime: OptStr
|
|
1522
|
+
});
|
|
1523
|
+
var PdfDetection = Type.Object({
|
|
1524
|
+
kind: Type.Literal("pdf"),
|
|
1525
|
+
type: Type.Literal("application/pdf"),
|
|
1526
|
+
file: Str,
|
|
1527
|
+
mime: OptStr,
|
|
1528
|
+
format: Type.Literal("pdf"),
|
|
1529
|
+
pages: Int
|
|
1530
|
+
});
|
|
1531
|
+
var Detection = Type.Union([SourceDetection, ImageDetection, TextDetection, PdfDetection]);
|
|
1532
|
+
var SourceDetectionInput = Type.Omit(SourceDetection, ["kind"]);
|
|
1533
|
+
var ImageDetectionInput = Type.Omit(ImageDetection, ["kind"]);
|
|
1534
|
+
var TextDetectionInput = Type.Omit(TextDetection, ["kind"]);
|
|
1535
|
+
var PdfDetectionInput = Type.Omit(PdfDetection, ["kind"]);
|
|
1536
|
+
var UnspannedContent = Type.Omit(ReadOutput, ["start_line", "end_line"]);
|
|
1537
|
+
var PdfInspectMetadata = Type.Object({ source: Str, format: Type.Literal("pdf"), pages: Int });
|
|
1538
|
+
var DocumentCache = Type.Object({
|
|
1539
|
+
content_hash: Str,
|
|
1540
|
+
state: Type.Union([Type.Literal("built"), Type.Literal("reused")])
|
|
1541
|
+
});
|
|
1542
|
+
var Identifier = Type.Object({
|
|
1543
|
+
text: Str,
|
|
1544
|
+
start_column: Int,
|
|
1545
|
+
end_column: Int,
|
|
1546
|
+
start_byte: Int,
|
|
1547
|
+
end_byte: Int
|
|
1548
|
+
});
|
|
1549
|
+
var IdentifyEnvelope = Type.Object({
|
|
1550
|
+
file: Str,
|
|
1551
|
+
language: Str,
|
|
1552
|
+
engine: OptStr,
|
|
1553
|
+
line_count: Int,
|
|
1554
|
+
file_hash: Str,
|
|
1555
|
+
line: Int,
|
|
1556
|
+
column: Int,
|
|
1557
|
+
line_hash: Str,
|
|
1558
|
+
identifier: Type.Optional(Identifier),
|
|
1559
|
+
symbol: Type.Optional(Type.Object({
|
|
1560
|
+
name: Str,
|
|
1561
|
+
kind: Str,
|
|
1562
|
+
qualified_name: Str,
|
|
1563
|
+
start_line: Int,
|
|
1564
|
+
end_line: Int
|
|
1565
|
+
}))
|
|
1566
|
+
});
|
|
1567
|
+
var EditOutputSchema = Type.Object({
|
|
1568
|
+
file: Str,
|
|
1569
|
+
before_hash: Str,
|
|
1570
|
+
file_hash: Str,
|
|
1571
|
+
plan_hash: Str,
|
|
1572
|
+
applied: Bool,
|
|
1573
|
+
changed: Bool,
|
|
1574
|
+
edit_count: Int,
|
|
1575
|
+
before_content: OptStr,
|
|
1576
|
+
content: OptStr
|
|
1577
|
+
});
|
|
1578
|
+
var RenameConflictSchema = Type.Object({ line: Int, column: Int, reason: Str });
|
|
1579
|
+
var RenameEditSchema = Type.Object({
|
|
1580
|
+
line: Int,
|
|
1581
|
+
start_column: Int,
|
|
1582
|
+
end_column: Int,
|
|
1583
|
+
start_byte: Int,
|
|
1584
|
+
end_byte: Int,
|
|
1585
|
+
occurrence: Str,
|
|
1586
|
+
line_hash: Str,
|
|
1587
|
+
text: Str
|
|
1588
|
+
});
|
|
1589
|
+
var Conflicts = Type.Array(RenameConflictSchema, { default: [] });
|
|
1590
|
+
var Edits = Type.Array(RenameEditSchema, { default: [] });
|
|
1591
|
+
var RenameFileOutputSchema = Type.Object({
|
|
1592
|
+
file: Str,
|
|
1593
|
+
language: Str,
|
|
1594
|
+
engine: OptStr,
|
|
1595
|
+
file_hash: Str,
|
|
1596
|
+
conflicts: Conflicts,
|
|
1597
|
+
edits: Edits
|
|
1598
|
+
});
|
|
1599
|
+
var RenameOutputSchema = Type.Object({
|
|
1600
|
+
file: Str,
|
|
1601
|
+
language: Str,
|
|
1602
|
+
engine: OptStr,
|
|
1603
|
+
file_hash: Str,
|
|
1604
|
+
old_name: Str,
|
|
1605
|
+
new_name: Str,
|
|
1606
|
+
plan_hash: Str,
|
|
1607
|
+
applied: Bool,
|
|
1608
|
+
conflicts: Conflicts,
|
|
1609
|
+
edits: Edits,
|
|
1610
|
+
others: Type.Array(RenameFileOutputSchema, { default: [] })
|
|
1611
|
+
});
|
|
1612
|
+
var DefEnvelope = Type.Object({
|
|
1613
|
+
locations: Type.Array(Type.Object({
|
|
1614
|
+
file: Str,
|
|
1615
|
+
line: Int,
|
|
1616
|
+
column: Int,
|
|
1617
|
+
line_hash: Str,
|
|
1618
|
+
text: Str,
|
|
1619
|
+
kind: OptStr,
|
|
1620
|
+
name: OptStr,
|
|
1621
|
+
qualified_name: OptStr
|
|
1622
|
+
}))
|
|
1623
|
+
});
|
|
1025
1624
|
function parseReadOutput(value) {
|
|
1026
|
-
|
|
1027
|
-
throw new Error("invalid readseek read output");
|
|
1028
|
-
const output = value;
|
|
1029
|
-
const hashlines = output.hashlines;
|
|
1030
|
-
if (!Array.isArray(hashlines))
|
|
1031
|
-
throw new Error("invalid readseek hashlines");
|
|
1032
|
-
return {
|
|
1033
|
-
file: requireString(output.file, "file"),
|
|
1034
|
-
language: requireString(output.language, "language"),
|
|
1035
|
-
line_count: requireNumber(output.line_count, "line_count"),
|
|
1036
|
-
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1037
|
-
start_line: requireNumber(output.start_line, "start_line"),
|
|
1038
|
-
end_line: requireNumber(output.end_line, "end_line"),
|
|
1039
|
-
hashlines: hashlines.map((line) => parseHashline(line, "hashline"))
|
|
1040
|
-
};
|
|
1625
|
+
return parse2(ReadOutput, value, "read output");
|
|
1041
1626
|
}
|
|
1042
1627
|
function parseMapOutput(value) {
|
|
1043
|
-
|
|
1044
|
-
throw new Error("invalid readseek map output");
|
|
1045
|
-
const output = value;
|
|
1046
|
-
const symbols = output.symbols;
|
|
1047
|
-
if (!Array.isArray(symbols))
|
|
1048
|
-
throw new Error("invalid readseek symbols");
|
|
1049
|
-
return {
|
|
1050
|
-
file: requireString(output.file, "file"),
|
|
1051
|
-
language: requireString(output.language, "language"),
|
|
1052
|
-
line_count: requireNumber(output.line_count, "line_count"),
|
|
1053
|
-
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1054
|
-
symbols: symbols.map((symbol) => {
|
|
1055
|
-
if (!symbol || typeof symbol !== "object")
|
|
1056
|
-
throw new Error("invalid readseek symbol");
|
|
1057
|
-
const item = symbol;
|
|
1058
|
-
return {
|
|
1059
|
-
kind: requireString(item.kind, "symbol.kind"),
|
|
1060
|
-
name: requireString(item.name, "symbol.name"),
|
|
1061
|
-
qualified_name: requireString(item.qualified_name, "symbol.qualified_name"),
|
|
1062
|
-
start_line: requireNumber(item.start_line, "symbol.start_line"),
|
|
1063
|
-
end_line: requireNumber(item.end_line, "symbol.end_line"),
|
|
1064
|
-
start_hash: requireString(item.start_hash, "symbol.start_hash"),
|
|
1065
|
-
end_hash: requireString(item.end_hash, "symbol.end_hash")
|
|
1066
|
-
};
|
|
1067
|
-
})
|
|
1068
|
-
};
|
|
1628
|
+
return parse2(MapOutput, value, "map output");
|
|
1069
1629
|
}
|
|
1070
1630
|
function parseSearchOutput(value) {
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
if (!Array.isArray(file.matches))
|
|
1081
|
-
throw new Error("invalid readseek search matches");
|
|
1082
|
-
return {
|
|
1083
|
-
file: requireString(file.file, "search.file"),
|
|
1084
|
-
language: requireString(file.language, "search.language"),
|
|
1085
|
-
file_hash: requireString(file.file_hash, "search.file_hash"),
|
|
1086
|
-
matches: file.matches.map((match) => {
|
|
1087
|
-
if (!match || typeof match !== "object")
|
|
1088
|
-
throw new Error("invalid readseek search match");
|
|
1089
|
-
const item = match;
|
|
1090
|
-
if (!Array.isArray(item.captures))
|
|
1091
|
-
throw new Error("invalid readseek search captures");
|
|
1092
|
-
return {
|
|
1093
|
-
pattern_index: item.pattern_index === undefined ? 0 : requireNumber(item.pattern_index, "search.match.pattern_index"),
|
|
1094
|
-
start_line: requireNumber(item.start_line, "search.match.start_line"),
|
|
1095
|
-
end_line: requireNumber(item.end_line, "search.match.end_line"),
|
|
1096
|
-
start_hash: requireString(item.start_hash, "search.match.start_hash"),
|
|
1097
|
-
end_hash: requireString(item.end_hash, "search.match.end_hash"),
|
|
1098
|
-
hashlines: parseSearchHashlines(item.hashlines, "search.match.hashlines"),
|
|
1099
|
-
captures: item.captures.map((capture) => {
|
|
1100
|
-
if (!capture || typeof capture !== "object")
|
|
1101
|
-
throw new Error("invalid readseek search capture");
|
|
1102
|
-
const captureItem = capture;
|
|
1103
|
-
return {
|
|
1104
|
-
name: requireString(captureItem.name, "search.capture.name"),
|
|
1105
|
-
start_line: requireNumber(captureItem.start_line, "search.capture.start_line"),
|
|
1106
|
-
end_line: requireNumber(captureItem.end_line, "search.capture.end_line"),
|
|
1107
|
-
start_hash: requireString(captureItem.start_hash, "search.capture.start_hash"),
|
|
1108
|
-
end_hash: requireString(captureItem.end_hash, "search.capture.end_hash"),
|
|
1109
|
-
hashlines: parseSearchHashlines(captureItem.hashlines, "search.capture.hashlines")
|
|
1110
|
-
};
|
|
1111
|
-
})
|
|
1112
|
-
};
|
|
1113
|
-
})
|
|
1114
|
-
};
|
|
1115
|
-
});
|
|
1631
|
+
const envelope = parse2(SearchEnvelope, value, "search output");
|
|
1632
|
+
const results = envelope.results;
|
|
1633
|
+
if (envelope.returned_matches !== undefined)
|
|
1634
|
+
results.returned_matches = envelope.returned_matches;
|
|
1635
|
+
if (envelope.truncated !== undefined)
|
|
1636
|
+
results.truncated = envelope.truncated;
|
|
1637
|
+
if (envelope.byte_basis !== undefined)
|
|
1638
|
+
results.byte_basis = envelope.byte_basis;
|
|
1639
|
+
return results;
|
|
1116
1640
|
}
|
|
1117
1641
|
function parseRefsOutput(value) {
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
throw new Error("invalid readseek references");
|
|
1123
|
-
return output.references.map((reference) => {
|
|
1124
|
-
if (!reference || typeof reference !== "object")
|
|
1125
|
-
throw new Error("invalid readseek reference");
|
|
1126
|
-
const item = reference;
|
|
1127
|
-
const symbol = item.symbol;
|
|
1128
|
-
const enclosing = symbol && typeof symbol === "object" ? optionalString(symbol.qualified_name, "reference.symbol.qualified_name") : undefined;
|
|
1129
|
-
return {
|
|
1130
|
-
file: requireString(item.file, "reference.file"),
|
|
1131
|
-
line: requireNumber(item.line, "reference.line"),
|
|
1132
|
-
column: requireNumber(item.column, "reference.column"),
|
|
1133
|
-
line_hash: requireString(item.line_hash, "reference.line_hash"),
|
|
1134
|
-
text: requireString(item.text, "reference.text"),
|
|
1135
|
-
enclosingSymbol: enclosing
|
|
1136
|
-
};
|
|
1137
|
-
});
|
|
1138
|
-
}
|
|
1139
|
-
function parseDiagnosticKind(value) {
|
|
1140
|
-
if (value === "error" || value === "missing")
|
|
1141
|
-
return value;
|
|
1142
|
-
throw new Error("invalid readseek diagnostic.kind");
|
|
1642
|
+
return parse2(RefsEnvelope, value, "refs output").references.map(({ symbol, ...reference }) => ({
|
|
1643
|
+
...reference,
|
|
1644
|
+
enclosingSymbol: symbol?.qualified_name
|
|
1645
|
+
}));
|
|
1143
1646
|
}
|
|
1144
1647
|
function parseCheckOutput(value) {
|
|
1145
|
-
|
|
1146
|
-
throw new Error("invalid readseek check output");
|
|
1147
|
-
const output = value;
|
|
1148
|
-
if (!Array.isArray(output.diagnostics))
|
|
1149
|
-
throw new Error("invalid readseek diagnostics");
|
|
1648
|
+
const output = parse2(CheckEnvelope, value, "check output");
|
|
1150
1649
|
return {
|
|
1151
|
-
file_hash:
|
|
1152
|
-
errorCount:
|
|
1153
|
-
missingCount:
|
|
1154
|
-
diagnostics: output.diagnostics
|
|
1155
|
-
if (!diagnostic || typeof diagnostic !== "object")
|
|
1156
|
-
throw new Error("invalid readseek diagnostic");
|
|
1157
|
-
const item = diagnostic;
|
|
1158
|
-
return {
|
|
1159
|
-
kind: parseDiagnosticKind(item.kind),
|
|
1160
|
-
start_line: requireNumber(item.start_line, "diagnostic.start_line"),
|
|
1161
|
-
end_line: requireNumber(item.end_line, "diagnostic.end_line")
|
|
1162
|
-
};
|
|
1163
|
-
})
|
|
1650
|
+
file_hash: output.file_hash,
|
|
1651
|
+
errorCount: output.error_count,
|
|
1652
|
+
missingCount: output.missing_count,
|
|
1653
|
+
diagnostics: output.diagnostics
|
|
1164
1654
|
};
|
|
1165
1655
|
}
|
|
1166
|
-
function parseDetectedObjects(value) {
|
|
1167
|
-
if (value === undefined || value === null)
|
|
1168
|
-
return;
|
|
1169
|
-
if (!Array.isArray(value))
|
|
1170
|
-
throw new Error("invalid readseek detect objects");
|
|
1171
|
-
return value.map((object) => {
|
|
1172
|
-
if (!object || typeof object !== "object")
|
|
1173
|
-
throw new Error("invalid readseek detect object");
|
|
1174
|
-
const item = object;
|
|
1175
|
-
const bbox = item.bbox;
|
|
1176
|
-
if (!Array.isArray(bbox) || bbox.length !== 4)
|
|
1177
|
-
throw new Error("invalid readseek detect object.bbox");
|
|
1178
|
-
return {
|
|
1179
|
-
label: requireString(item.label, "object.label"),
|
|
1180
|
-
bbox: bbox.map((n, i) => requireNumber(n, `object.bbox[${i}]`))
|
|
1181
|
-
};
|
|
1182
|
-
});
|
|
1183
|
-
}
|
|
1184
|
-
function parseImageEncoding(value) {
|
|
1185
|
-
if (value === "base64")
|
|
1186
|
-
return value;
|
|
1187
|
-
throw new Error("invalid image encoding");
|
|
1188
|
-
}
|
|
1189
1656
|
function parseDetectOutput(value) {
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
const type = requireString(output.type, "type");
|
|
1194
|
-
const file = requireString(output.file, "file");
|
|
1195
|
-
const mime = optionalString(output.mime, "mime");
|
|
1196
|
-
if (type === "application/pdf") {
|
|
1197
|
-
if (output.format !== "pdf")
|
|
1198
|
-
throw new Error("invalid readseek PDF format");
|
|
1199
|
-
return {
|
|
1200
|
-
kind: "pdf",
|
|
1201
|
-
type,
|
|
1202
|
-
file,
|
|
1203
|
-
mime,
|
|
1204
|
-
format: output.format,
|
|
1205
|
-
pages: requireNumber(output.pages, "pages")
|
|
1206
|
-
};
|
|
1657
|
+
const output = value ?? {};
|
|
1658
|
+
if (output.type === "application/pdf") {
|
|
1659
|
+
return { kind: "pdf", ...parse2(PdfDetectionInput, value, "detect output") };
|
|
1207
1660
|
}
|
|
1208
1661
|
if (output.width !== undefined || output.height !== undefined) {
|
|
1209
|
-
return {
|
|
1210
|
-
kind: "image",
|
|
1211
|
-
type,
|
|
1212
|
-
file,
|
|
1213
|
-
mime,
|
|
1214
|
-
format: requireString(output.format, "format"),
|
|
1215
|
-
width: requireNumber(output.width, "width"),
|
|
1216
|
-
height: requireNumber(output.height, "height"),
|
|
1217
|
-
animated: requireBoolean(output.animated, "animated"),
|
|
1218
|
-
encoding: output.encoding === undefined ? undefined : parseImageEncoding(output.encoding),
|
|
1219
|
-
data: output.data === undefined ? undefined : requireString(output.data, "data"),
|
|
1220
|
-
ocr: optionalString(output.ocr, "ocr"),
|
|
1221
|
-
caption: optionalString(output.caption, "caption"),
|
|
1222
|
-
objects: parseDetectedObjects(output.objects)
|
|
1223
|
-
};
|
|
1662
|
+
return { kind: "image", ...parse2(ImageDetectionInput, value, "detect output") };
|
|
1224
1663
|
}
|
|
1225
1664
|
if (output.language !== undefined) {
|
|
1226
|
-
return {
|
|
1227
|
-
kind: "source",
|
|
1228
|
-
type,
|
|
1229
|
-
file,
|
|
1230
|
-
language: requireString(output.language, "language"),
|
|
1231
|
-
engine: optionalString(output.engine, "engine"),
|
|
1232
|
-
supported: requireBoolean(output.supported, "supported"),
|
|
1233
|
-
mime,
|
|
1234
|
-
syntax: optionalString(output.syntax, "syntax")
|
|
1235
|
-
};
|
|
1665
|
+
return { kind: "source", ...parse2(SourceDetectionInput, value, "detect output") };
|
|
1236
1666
|
}
|
|
1237
|
-
return { kind: "text",
|
|
1238
|
-
}
|
|
1239
|
-
function parseDocumentCache(value) {
|
|
1240
|
-
if (!value || typeof value !== "object")
|
|
1241
|
-
throw new Error("invalid readseek document cache");
|
|
1242
|
-
const cache = value;
|
|
1243
|
-
const state = requireString(cache.state, "cache.state");
|
|
1244
|
-
if (state !== "built" && state !== "reused")
|
|
1245
|
-
throw new Error("invalid readseek cache.state");
|
|
1246
|
-
return {
|
|
1247
|
-
content_hash: requireString(cache.content_hash, "cache.content_hash"),
|
|
1248
|
-
state
|
|
1249
|
-
};
|
|
1250
|
-
}
|
|
1251
|
-
function parseIdentifyOutput(value) {
|
|
1252
|
-
if (!value || typeof value !== "object")
|
|
1253
|
-
throw new Error("invalid readseek identify output");
|
|
1254
|
-
const output = value;
|
|
1255
|
-
const identifier = output.identifier;
|
|
1256
|
-
const symbol = output.symbol;
|
|
1257
|
-
return {
|
|
1258
|
-
file: requireString(output.file, "file"),
|
|
1259
|
-
language: requireString(output.language, "language"),
|
|
1260
|
-
engine: optionalString(output.engine, "engine"),
|
|
1261
|
-
line_count: requireNumber(output.line_count, "line_count"),
|
|
1262
|
-
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1263
|
-
line: requireNumber(output.line, "line"),
|
|
1264
|
-
column: requireNumber(output.column, "column"),
|
|
1265
|
-
line_hash: requireString(output.line_hash, "line_hash"),
|
|
1266
|
-
identifier: identifier && typeof identifier === "object" ? {
|
|
1267
|
-
text: requireString(identifier.text, "identifier.text"),
|
|
1268
|
-
start_column: requireNumber(identifier.start_column, "identifier.start_column"),
|
|
1269
|
-
end_column: requireNumber(identifier.end_column, "identifier.end_column"),
|
|
1270
|
-
start_byte: requireNumber(identifier.start_byte, "identifier.start_byte"),
|
|
1271
|
-
end_byte: requireNumber(identifier.end_byte, "identifier.end_byte")
|
|
1272
|
-
} : undefined,
|
|
1273
|
-
symbol: symbol && typeof symbol === "object" ? {
|
|
1274
|
-
name: requireString(symbol.name, "symbol.name"),
|
|
1275
|
-
kind: requireString(symbol.kind, "symbol.kind"),
|
|
1276
|
-
qualified_name: requireString(symbol.qualified_name, "symbol.qualified_name"),
|
|
1277
|
-
start_line: requireNumber(symbol.start_line, "symbol.start_line"),
|
|
1278
|
-
end_line: requireNumber(symbol.end_line, "symbol.end_line")
|
|
1279
|
-
} : undefined
|
|
1280
|
-
};
|
|
1667
|
+
return { kind: "text", ...parse2(TextDetectionInput, value, "detect output") };
|
|
1281
1668
|
}
|
|
1282
1669
|
function parseImageContent(value) {
|
|
1283
1670
|
const detection = parseDetectOutput(value);
|
|
1284
1671
|
if (detection.kind !== "image")
|
|
1285
|
-
throw new Error("invalid readseek image content");
|
|
1672
|
+
throw new Error("invalid readseek image content: expected an image");
|
|
1286
1673
|
return detection;
|
|
1287
1674
|
}
|
|
1288
1675
|
function parseTextContent(value) {
|
|
1676
|
+
const output = value ?? {};
|
|
1677
|
+
if (output.hashlines === undefined || output.start_line !== undefined)
|
|
1678
|
+
return parseReadOutput(value);
|
|
1679
|
+
const spanned = parse2(UnspannedContent, value, "content");
|
|
1680
|
+
return {
|
|
1681
|
+
...spanned,
|
|
1682
|
+
start_line: spanned.hashlines[0]?.line ?? 1,
|
|
1683
|
+
end_line: spanned.hashlines[spanned.hashlines.length - 1]?.line ?? 0
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1686
|
+
function parseDocumentCache(value) {
|
|
1687
|
+
return parse2(DocumentCache, value, "document cache");
|
|
1688
|
+
}
|
|
1689
|
+
function parseIdentifyOutput(value) {
|
|
1690
|
+
return parse2(IdentifyEnvelope, value, "identify output");
|
|
1691
|
+
}
|
|
1692
|
+
function parsePdfInspectMetadata(value) {
|
|
1289
1693
|
if (!value || typeof value !== "object")
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1694
|
+
return;
|
|
1695
|
+
if (value.format !== "pdf")
|
|
1696
|
+
return;
|
|
1697
|
+
const document = parse2(PdfInspectMetadata, value, "inspect metadata");
|
|
1698
|
+
return { kind: "pdf", type: "application/pdf", file: document.source, format: "pdf", pages: document.pages };
|
|
1699
|
+
}
|
|
1700
|
+
function parseEditOutput(value) {
|
|
1701
|
+
return parse2(EditOutputSchema, value, "edit output");
|
|
1702
|
+
}
|
|
1703
|
+
function parseRenameOutput(value) {
|
|
1704
|
+
return parse2(RenameOutputSchema, value, "rename output");
|
|
1705
|
+
}
|
|
1706
|
+
function parseDefOutput(value) {
|
|
1707
|
+
return parse2(DefEnvelope, value, "def output").locations;
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
// ../readseek-api/src/client.ts
|
|
1711
|
+
function classifyReadSeekFailure(err) {
|
|
1712
|
+
const failure = err;
|
|
1713
|
+
const message = String(failure?.message ?? err);
|
|
1714
|
+
const missing = failure?.code === "ENOENT" || /Cannot find package|Cannot find module|no such file/i.test(message);
|
|
1715
|
+
if (missing) {
|
|
1716
|
+
return { code: "readseek-not-installed", message, hint: "Run npm install to install @jarkkojs/readseek." };
|
|
1305
1717
|
}
|
|
1306
|
-
return
|
|
1718
|
+
return { code: "readseek-execution-error", message };
|
|
1307
1719
|
}
|
|
1308
|
-
function
|
|
1720
|
+
function parseSourceInspectEnvelope(value) {
|
|
1309
1721
|
if (!value || typeof value !== "object")
|
|
1310
|
-
throw new Error("invalid readseek
|
|
1722
|
+
throw new Error("invalid readseek inspect output");
|
|
1311
1723
|
const output = value;
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
metadata: parseDetectOutput(output.metadata)
|
|
1316
|
-
};
|
|
1724
|
+
const result = {};
|
|
1725
|
+
if (output.metadata !== undefined)
|
|
1726
|
+
result.metadata = parseDetectOutput(output.metadata);
|
|
1317
1727
|
if (output.cache !== undefined)
|
|
1318
1728
|
result.cache = parseDocumentCache(output.cache);
|
|
1319
1729
|
if (output.content !== undefined)
|
|
@@ -1326,223 +1736,104 @@ function parseDigestEnvelope(value) {
|
|
|
1326
1736
|
result.identity = parseIdentifyOutput(output.identity);
|
|
1327
1737
|
return result;
|
|
1328
1738
|
}
|
|
1329
|
-
function parseEditOutput(value) {
|
|
1330
|
-
if (!value || typeof value !== "object")
|
|
1331
|
-
throw new Error("invalid readseek edit output");
|
|
1332
|
-
const output = value;
|
|
1333
|
-
return {
|
|
1334
|
-
file: requireString(output.file, "file"),
|
|
1335
|
-
before_hash: requireString(output.before_hash, "before_hash"),
|
|
1336
|
-
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1337
|
-
plan_hash: requireString(output.plan_hash, "plan_hash"),
|
|
1338
|
-
applied: requireBoolean(output.applied, "applied"),
|
|
1339
|
-
changed: requireBoolean(output.changed, "changed"),
|
|
1340
|
-
edit_count: requireNumber(output.edit_count, "edit_count"),
|
|
1341
|
-
before_content: optionalString(output.before_content, "before_content"),
|
|
1342
|
-
content: optionalString(output.content, "content")
|
|
1343
|
-
};
|
|
1344
|
-
}
|
|
1345
|
-
function parseRenameConflicts(value, field) {
|
|
1346
|
-
if (value === undefined)
|
|
1347
|
-
return [];
|
|
1348
|
-
if (!Array.isArray(value))
|
|
1349
|
-
throw new Error(`invalid readseek ${field}`);
|
|
1350
|
-
return value.map((item) => {
|
|
1351
|
-
if (!item || typeof item !== "object")
|
|
1352
|
-
throw new Error(`invalid readseek ${field} entry`);
|
|
1353
|
-
const c = item;
|
|
1354
|
-
return {
|
|
1355
|
-
line: requireNumber(c.line, `${field}.line`),
|
|
1356
|
-
column: requireNumber(c.column, `${field}.column`),
|
|
1357
|
-
reason: requireString(c.reason, `${field}.reason`)
|
|
1358
|
-
};
|
|
1359
|
-
});
|
|
1360
|
-
}
|
|
1361
|
-
function parseRenameEdits(value, field) {
|
|
1362
|
-
if (value === undefined)
|
|
1363
|
-
return [];
|
|
1364
|
-
if (!Array.isArray(value))
|
|
1365
|
-
throw new Error(`invalid readseek ${field} entry`);
|
|
1366
|
-
return value.map((item) => {
|
|
1367
|
-
if (!item || typeof item !== "object")
|
|
1368
|
-
throw new Error(`invalid readseek ${field} entry`);
|
|
1369
|
-
const e = item;
|
|
1370
|
-
return {
|
|
1371
|
-
line: requireNumber(e.line, `${field}.line`),
|
|
1372
|
-
start_column: requireNumber(e.start_column, `${field}.start_column`),
|
|
1373
|
-
end_column: requireNumber(e.end_column, `${field}.end_column`),
|
|
1374
|
-
start_byte: requireNumber(e.start_byte, `${field}.start_byte`),
|
|
1375
|
-
end_byte: requireNumber(e.end_byte, `${field}.end_byte`),
|
|
1376
|
-
occurrence: requireString(e.occurrence, `${field}.occurrence`),
|
|
1377
|
-
line_hash: requireString(e.line_hash, `${field}.line_hash`),
|
|
1378
|
-
text: requireString(e.text, `${field}.text`)
|
|
1379
|
-
};
|
|
1380
|
-
});
|
|
1381
|
-
}
|
|
1382
|
-
function parseRenameOutput(value) {
|
|
1383
|
-
if (!value || typeof value !== "object")
|
|
1384
|
-
throw new Error("invalid readseek rename output");
|
|
1385
|
-
const output = value;
|
|
1386
|
-
const others = output.others;
|
|
1387
|
-
if (others !== undefined && !Array.isArray(others))
|
|
1388
|
-
throw new Error("invalid readseek others");
|
|
1389
|
-
return {
|
|
1390
|
-
file: requireString(output.file, "file"),
|
|
1391
|
-
language: requireString(output.language, "language"),
|
|
1392
|
-
engine: optionalString(output.engine, "engine"),
|
|
1393
|
-
file_hash: requireString(output.file_hash, "file_hash"),
|
|
1394
|
-
old_name: requireString(output.old_name, "old_name"),
|
|
1395
|
-
new_name: requireString(output.new_name, "new_name"),
|
|
1396
|
-
applied: requireBoolean(output.applied, "applied"),
|
|
1397
|
-
conflicts: parseRenameConflicts(output.conflicts, "conflicts"),
|
|
1398
|
-
edits: parseRenameEdits(output.edits, "edits"),
|
|
1399
|
-
others: others?.map((entry) => {
|
|
1400
|
-
if (!entry || typeof entry !== "object")
|
|
1401
|
-
throw new Error("invalid readseek other");
|
|
1402
|
-
const o = entry;
|
|
1403
|
-
return {
|
|
1404
|
-
file: requireString(o.file, "other.file"),
|
|
1405
|
-
language: requireString(o.language, "other.language"),
|
|
1406
|
-
engine: optionalString(o.engine, "other.engine"),
|
|
1407
|
-
file_hash: requireString(o.file_hash, "other.file_hash"),
|
|
1408
|
-
conflicts: parseRenameConflicts(o.conflicts, "other.conflicts"),
|
|
1409
|
-
edits: parseRenameEdits(o.edits, "other.edits")
|
|
1410
|
-
};
|
|
1411
|
-
}) ?? []
|
|
1412
|
-
};
|
|
1413
|
-
}
|
|
1414
|
-
function parseDefCompact(value) {
|
|
1415
|
-
if (!value || typeof value !== "object")
|
|
1416
|
-
throw new Error("invalid readseek def output");
|
|
1417
|
-
const output = value;
|
|
1418
|
-
const locations = output.locations;
|
|
1419
|
-
if (!Array.isArray(locations))
|
|
1420
|
-
throw new Error("invalid readseek def locations");
|
|
1421
|
-
return locations.map((loc) => {
|
|
1422
|
-
if (!loc || typeof loc !== "object")
|
|
1423
|
-
throw new Error("invalid readseek def location");
|
|
1424
|
-
const item = loc;
|
|
1425
|
-
return {
|
|
1426
|
-
file: requireString(item.file, "location.file"),
|
|
1427
|
-
line: requireNumber(item.line, "location.line"),
|
|
1428
|
-
column: requireNumber(item.column, "location.column"),
|
|
1429
|
-
line_hash: requireString(item.line_hash, "location.line_hash"),
|
|
1430
|
-
text: requireString(item.text, "location.text"),
|
|
1431
|
-
kind: optionalString(item.kind, "location.kind"),
|
|
1432
|
-
name: optionalString(item.name, "location.name"),
|
|
1433
|
-
qualified_name: optionalString(item.qualified_name, "location.qualified_name")
|
|
1434
|
-
};
|
|
1435
|
-
});
|
|
1436
|
-
}
|
|
1437
1739
|
function createReadSeekClient(runner) {
|
|
1438
|
-
function
|
|
1439
|
-
return
|
|
1740
|
+
function inspectArgs(target, options) {
|
|
1741
|
+
return buildInspectArgs({
|
|
1440
1742
|
path: target,
|
|
1743
|
+
selector: options.selector,
|
|
1441
1744
|
select: options.select ?? "content",
|
|
1442
|
-
|
|
1745
|
+
query: options.query,
|
|
1443
1746
|
end: options.end,
|
|
1444
1747
|
limit: options.limit,
|
|
1748
|
+
depth: options.depth,
|
|
1445
1749
|
language: options.language,
|
|
1446
1750
|
visionMode: options.visionMode,
|
|
1447
1751
|
visionLevel: options.visionLevel,
|
|
1448
|
-
stdinName: options.
|
|
1752
|
+
stdinName: options.stdinName
|
|
1449
1753
|
});
|
|
1450
1754
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
const runOptions = {
|
|
1755
|
+
function inspectRunOptions(options) {
|
|
1756
|
+
return {
|
|
1454
1757
|
signal: options.signal,
|
|
1455
1758
|
stdin: options.stdin,
|
|
1456
1759
|
timeoutMs: options.timeoutMs,
|
|
1457
1760
|
vision: options.vision
|
|
1458
1761
|
};
|
|
1459
|
-
return runner.json(args, runOptions);
|
|
1460
1762
|
}
|
|
1461
|
-
async function
|
|
1462
|
-
return
|
|
1763
|
+
async function inspect(target, options = {}) {
|
|
1764
|
+
return runner.kdl(inspectArgs(target, options), inspectRunOptions(options));
|
|
1765
|
+
}
|
|
1766
|
+
async function inspectText(target, options = {}) {
|
|
1767
|
+
return runner.text(inspectArgs(target, options), inspectRunOptions(options));
|
|
1768
|
+
}
|
|
1769
|
+
async function inspectSource(target, options = {}) {
|
|
1770
|
+
return parseSourceInspectEnvelope(await inspect(target, options));
|
|
1463
1771
|
}
|
|
1464
|
-
function
|
|
1772
|
+
function requireInspectMetadata(envelope, field) {
|
|
1773
|
+
if (envelope.metadata === undefined)
|
|
1774
|
+
throw new Error(`invalid readseek ${field}: missing metadata`);
|
|
1775
|
+
return envelope.metadata;
|
|
1776
|
+
}
|
|
1777
|
+
function requireInspectContent(envelope, field) {
|
|
1465
1778
|
if (envelope.content === undefined)
|
|
1466
1779
|
throw new Error(`invalid readseek ${field}: missing content`);
|
|
1467
1780
|
return envelope.content;
|
|
1468
1781
|
}
|
|
1469
|
-
async function
|
|
1470
|
-
const
|
|
1471
|
-
select: "content",
|
|
1472
|
-
|
|
1782
|
+
async function inspectContent(filePath, options = {}) {
|
|
1783
|
+
const inspected = await inspect(filePath, {
|
|
1784
|
+
select: ["metadata", "content"],
|
|
1785
|
+
selector: options.startLine !== undefined ? buildLineLocation(options.startLine) : undefined,
|
|
1473
1786
|
end: options.endLine,
|
|
1474
1787
|
visionMode: options.visionMode,
|
|
1475
1788
|
signal: options.signal,
|
|
1476
1789
|
vision: options.vision
|
|
1477
1790
|
});
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
const
|
|
1482
|
-
|
|
1483
|
-
|
|
1791
|
+
const pdf = parsePdfInspectMetadata(inspected);
|
|
1792
|
+
if (pdf)
|
|
1793
|
+
return { metadata: pdf };
|
|
1794
|
+
const envelope = parseSourceInspectEnvelope(inspected);
|
|
1795
|
+
const metadata = requireInspectMetadata(envelope, "content");
|
|
1796
|
+
const content = requireInspectContent(envelope, "content");
|
|
1797
|
+
if (metadata.kind === "image") {
|
|
1798
|
+
return { metadata, image: parseImageContent(content) };
|
|
1484
1799
|
}
|
|
1485
|
-
return { metadata
|
|
1800
|
+
return { metadata, text: parseTextContent(content) };
|
|
1486
1801
|
}
|
|
1487
1802
|
return {
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
});
|
|
1499
|
-
return runner.text(args, {
|
|
1803
|
+
inspect,
|
|
1804
|
+
inspectText,
|
|
1805
|
+
inspectContent,
|
|
1806
|
+
async extract(target, selector, output, options = {}) {
|
|
1807
|
+
await runner.text(buildExtractArgs({
|
|
1808
|
+
path: target,
|
|
1809
|
+
selector,
|
|
1810
|
+
output,
|
|
1811
|
+
stdinName: options.stdinName
|
|
1812
|
+
}), {
|
|
1500
1813
|
signal: options.signal,
|
|
1501
|
-
|
|
1814
|
+
stdin: options.stdin,
|
|
1815
|
+
timeoutMs: options.timeoutMs,
|
|
1816
|
+
cancelable: options.cancelable
|
|
1502
1817
|
});
|
|
1503
1818
|
},
|
|
1504
|
-
digestRaw(target, options = {}) {
|
|
1505
|
-
return runDigestRaw(target, options);
|
|
1506
|
-
},
|
|
1507
|
-
digest,
|
|
1508
|
-
digestContent,
|
|
1509
1819
|
async image(filePath, modes, options = {}) {
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
}));
|
|
1521
|
-
let merged;
|
|
1522
|
-
for (const result of results) {
|
|
1523
|
-
if (result.status !== "fulfilled")
|
|
1524
|
-
continue;
|
|
1525
|
-
const detection = result.value;
|
|
1526
|
-
merged = merged === undefined ? detection : {
|
|
1527
|
-
...merged,
|
|
1528
|
-
ocr: detection.ocr ?? merged.ocr,
|
|
1529
|
-
caption: detection.caption ?? merged.caption,
|
|
1530
|
-
objects: detection.objects ?? merged.objects
|
|
1531
|
-
};
|
|
1532
|
-
}
|
|
1533
|
-
if (merged !== undefined)
|
|
1534
|
-
return merged;
|
|
1535
|
-
const failure = results.find((result) => result.status === "rejected");
|
|
1536
|
-
if (failure?.status === "rejected")
|
|
1537
|
-
throw failure.reason;
|
|
1538
|
-
throw new Error(`readseek returned no image analysis for ${filePath}`);
|
|
1820
|
+
if (modes.length === 0)
|
|
1821
|
+
throw new Error(`readseek returned no image analysis for ${filePath}`);
|
|
1822
|
+
const inspected = await inspectContent(filePath, {
|
|
1823
|
+
visionMode: modes.length > 1 ? "all" : modes[0],
|
|
1824
|
+
signal: options.signal,
|
|
1825
|
+
vision: true
|
|
1826
|
+
});
|
|
1827
|
+
if (!inspected.image)
|
|
1828
|
+
throw new Error(`readseek returned no image analysis for ${filePath}`);
|
|
1829
|
+
return inspected.image;
|
|
1539
1830
|
},
|
|
1540
1831
|
async preparedImage(filePath, options = {}) {
|
|
1541
|
-
const
|
|
1832
|
+
const inspected = await inspectContent(filePath, {
|
|
1542
1833
|
visionMode: "none",
|
|
1543
1834
|
signal: options.signal
|
|
1544
1835
|
});
|
|
1545
|
-
const output =
|
|
1836
|
+
const output = inspected.image;
|
|
1546
1837
|
if (!output || output.encoding !== "base64" || output.data === undefined || output.mime === undefined) {
|
|
1547
1838
|
throw new Error(`readseek returned no prepared image for ${filePath}`);
|
|
1548
1839
|
}
|
|
@@ -1554,11 +1845,9 @@ function createReadSeekClient(runner) {
|
|
|
1554
1845
|
pattern,
|
|
1555
1846
|
language: options.language,
|
|
1556
1847
|
limit: options.limit,
|
|
1557
|
-
|
|
1558
|
-
others: options.others,
|
|
1559
|
-
ignored: options.ignored
|
|
1848
|
+
gitScope: options.gitScope
|
|
1560
1849
|
});
|
|
1561
|
-
return parseSearchOutput(await runner.
|
|
1850
|
+
return parseSearchOutput(await runner.kdl(args, { signal: options.signal }));
|
|
1562
1851
|
},
|
|
1563
1852
|
async refs(target, options = {}) {
|
|
1564
1853
|
const args = buildRefsArgs({
|
|
@@ -1568,26 +1857,21 @@ function createReadSeekClient(runner) {
|
|
|
1568
1857
|
line: options.line,
|
|
1569
1858
|
column: options.column,
|
|
1570
1859
|
language: options.language,
|
|
1571
|
-
|
|
1572
|
-
others: options.others,
|
|
1573
|
-
ignored: options.ignored
|
|
1860
|
+
gitScope: options.gitScope
|
|
1574
1861
|
});
|
|
1575
|
-
return parseRefsOutput(await runner.
|
|
1862
|
+
return parseRefsOutput(await runner.kdl(args, { signal: options.signal }));
|
|
1576
1863
|
},
|
|
1577
1864
|
async def(target, options) {
|
|
1578
1865
|
const args = buildDefArgs({
|
|
1579
1866
|
path: target,
|
|
1580
1867
|
name: options.name,
|
|
1581
|
-
format: "plain",
|
|
1582
1868
|
language: options.language,
|
|
1583
|
-
|
|
1584
|
-
others: options.others,
|
|
1585
|
-
ignored: options.ignored
|
|
1869
|
+
gitScope: options.gitScope
|
|
1586
1870
|
});
|
|
1587
|
-
return
|
|
1871
|
+
return parseDefOutput(await runner.kdl(args, { signal: options.signal }));
|
|
1588
1872
|
},
|
|
1589
1873
|
async check(filePath, content, options = {}) {
|
|
1590
|
-
const envelope = await
|
|
1874
|
+
const envelope = await inspectSource("-", {
|
|
1591
1875
|
select: "diagnostics",
|
|
1592
1876
|
stdin: content,
|
|
1593
1877
|
stdinName: filePath,
|
|
@@ -1597,12 +1881,12 @@ function createReadSeekClient(runner) {
|
|
|
1597
1881
|
throw new Error("invalid readseek diagnostics: missing diagnostics facet");
|
|
1598
1882
|
return envelope.diagnostics;
|
|
1599
1883
|
},
|
|
1600
|
-
async identify(filePath, content, options
|
|
1601
|
-
const envelope = await
|
|
1884
|
+
async identify(filePath, content, options) {
|
|
1885
|
+
const envelope = await inspectSource("-", {
|
|
1602
1886
|
select: "identity",
|
|
1603
1887
|
stdin: content,
|
|
1604
1888
|
stdinName: filePath,
|
|
1605
|
-
|
|
1889
|
+
selector: buildLineLocation(options.line, options.column),
|
|
1606
1890
|
language: options.language,
|
|
1607
1891
|
signal: options.signal
|
|
1608
1892
|
});
|
|
@@ -1611,8 +1895,11 @@ function createReadSeekClient(runner) {
|
|
|
1611
1895
|
return envelope.identity;
|
|
1612
1896
|
},
|
|
1613
1897
|
async detect(filePath, options = {}) {
|
|
1614
|
-
const
|
|
1615
|
-
|
|
1898
|
+
const inspected = await inspect(filePath, { select: "metadata", signal: options.signal });
|
|
1899
|
+
const pdf = parsePdfInspectMetadata(inspected);
|
|
1900
|
+
if (pdf)
|
|
1901
|
+
return pdf;
|
|
1902
|
+
return requireInspectMetadata(parseSourceInspectEnvelope(inspected), "metadata");
|
|
1616
1903
|
},
|
|
1617
1904
|
async edit(filePath, edits, options = {}) {
|
|
1618
1905
|
const args = buildEditArgs({
|
|
@@ -1622,9 +1909,9 @@ function createReadSeekClient(runner) {
|
|
|
1622
1909
|
language: options.language,
|
|
1623
1910
|
requestFromStdin: true
|
|
1624
1911
|
});
|
|
1625
|
-
return parseEditOutput(await runner.
|
|
1912
|
+
return parseEditOutput(await runner.kdl(args, {
|
|
1626
1913
|
signal: options.signal,
|
|
1627
|
-
stdin:
|
|
1914
|
+
stdin: serializeEditPlan(edits),
|
|
1628
1915
|
timeoutMs: options.timeoutMs,
|
|
1629
1916
|
cancelable: options.cancelable
|
|
1630
1917
|
}));
|
|
@@ -1637,12 +1924,11 @@ function createReadSeekClient(runner) {
|
|
|
1637
1924
|
column: options.column,
|
|
1638
1925
|
workspace: options.workspace,
|
|
1639
1926
|
apply: options.apply,
|
|
1927
|
+
planHash: options.planHash,
|
|
1640
1928
|
language: options.language,
|
|
1641
|
-
|
|
1642
|
-
others: options.others,
|
|
1643
|
-
ignored: options.ignored
|
|
1929
|
+
gitScope: options.gitScope
|
|
1644
1930
|
});
|
|
1645
|
-
return parseRenameOutput(await runner.
|
|
1931
|
+
return parseRenameOutput(await runner.kdl(args, { signal: options.signal }));
|
|
1646
1932
|
}
|
|
1647
1933
|
};
|
|
1648
1934
|
}
|
|
@@ -2123,7 +2409,7 @@ function formatReplaceHint(edits, noopEdits) {
|
|
|
2123
2409
|
return;
|
|
2124
2410
|
if (counts.replace !== counts.total)
|
|
2125
2411
|
return;
|
|
2126
|
-
return "[info: this edit used replace (unverified). For safer future edits, prefer set_line/replace_lines with an anchor from
|
|
2412
|
+
return "[info: this edit used replace (unverified). For safer future edits, prefer set_line/replace_lines with an anchor from readSeek_inspect/readSeek_grep/readSeek_search.]";
|
|
2127
2413
|
}
|
|
2128
2414
|
function buildEditOutput(input) {
|
|
2129
2415
|
const summary = `Updated ${input.displayPath}`;
|
|
@@ -2170,20 +2456,20 @@ function classifyEdit(oldContent, newContent) {
|
|
|
2170
2456
|
const newLines = newContent.split(`
|
|
2171
2457
|
`);
|
|
2172
2458
|
if (oldLines.length === newLines.length) {
|
|
2173
|
-
let
|
|
2174
|
-
let
|
|
2459
|
+
let hasWhitespaceChange = false;
|
|
2460
|
+
let hasSemanticChange = false;
|
|
2175
2461
|
for (let i = 0;i < oldLines.length; i++) {
|
|
2176
2462
|
if (oldLines[i] === newLines[i])
|
|
2177
2463
|
continue;
|
|
2178
2464
|
if (oldLines[i].trim() === newLines[i].trim()) {
|
|
2179
|
-
|
|
2465
|
+
hasWhitespaceChange = true;
|
|
2180
2466
|
} else {
|
|
2181
|
-
|
|
2467
|
+
hasSemanticChange = true;
|
|
2182
2468
|
}
|
|
2183
2469
|
}
|
|
2184
|
-
if (
|
|
2470
|
+
if (hasSemanticChange && hasWhitespaceChange)
|
|
2185
2471
|
return { classification: "mixed" };
|
|
2186
|
-
if (
|
|
2472
|
+
if (hasWhitespaceChange)
|
|
2187
2473
|
return { classification: "whitespace-only" };
|
|
2188
2474
|
return { classification: "semantic" };
|
|
2189
2475
|
}
|
|
@@ -2223,7 +2509,7 @@ import { readFileSync, statSync as statSync2 } from "node:fs";
|
|
|
2223
2509
|
import { homedir } from "node:os";
|
|
2224
2510
|
import { join } from "node:path";
|
|
2225
2511
|
var READSEEK_TOOL_REPLACEMENTS = {
|
|
2226
|
-
read: "
|
|
2512
|
+
read: "readSeek_inspect",
|
|
2227
2513
|
edit: "readSeek_edit",
|
|
2228
2514
|
grep: "readSeek_grep",
|
|
2229
2515
|
write: "readSeek_write"
|
|
@@ -2236,42 +2522,36 @@ var DEFAULT_DISPLAY_MODES = {
|
|
|
2236
2522
|
edit: "expanded",
|
|
2237
2523
|
write: "expanded"
|
|
2238
2524
|
};
|
|
2239
|
-
function globalSettingsPath() {
|
|
2240
|
-
return join(homedir(), ".pi/agent/settings.json");
|
|
2241
|
-
}
|
|
2242
|
-
function projectSettingsPath() {
|
|
2243
|
-
return join(process.cwd(), ".pi/settings.json");
|
|
2244
|
-
}
|
|
2245
2525
|
function isRecord(value) {
|
|
2246
2526
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2247
2527
|
}
|
|
2248
|
-
function invalid(source,
|
|
2249
|
-
return { source, path
|
|
2528
|
+
function invalid(source, path) {
|
|
2529
|
+
return { source, path, message: `Invalid readseek setting at ${path}` };
|
|
2250
2530
|
}
|
|
2251
2531
|
function warnUnknownKeys(raw, known, prefix, source, warnings) {
|
|
2252
2532
|
for (const key of Object.keys(raw)) {
|
|
2253
2533
|
if (known.includes(key))
|
|
2254
2534
|
continue;
|
|
2255
|
-
const
|
|
2256
|
-
warnings.push({ source, path
|
|
2535
|
+
const path = `${prefix}.${key}`;
|
|
2536
|
+
warnings.push({ source, path, message: `Unknown readseek setting at ${path}` });
|
|
2257
2537
|
}
|
|
2258
2538
|
}
|
|
2259
|
-
function readPositive(raw, key,
|
|
2539
|
+
function readPositive(raw, key, path, source, warnings) {
|
|
2260
2540
|
if (!(key in raw))
|
|
2261
2541
|
return;
|
|
2262
2542
|
const val = raw[key];
|
|
2263
2543
|
if (typeof val === "number" && Number.isSafeInteger(val) && val > 0)
|
|
2264
2544
|
return val;
|
|
2265
|
-
warnings.push(invalid(source,
|
|
2545
|
+
warnings.push(invalid(source, path));
|
|
2266
2546
|
return;
|
|
2267
2547
|
}
|
|
2268
|
-
function readEnum(raw, key, values,
|
|
2548
|
+
function readEnum(raw, key, values, path, source, warnings) {
|
|
2269
2549
|
if (!(key in raw))
|
|
2270
2550
|
return;
|
|
2271
2551
|
const val = raw[key];
|
|
2272
2552
|
if (typeof val === "string" && values.includes(val))
|
|
2273
2553
|
return val;
|
|
2274
|
-
warnings.push(invalid(source,
|
|
2554
|
+
warnings.push(invalid(source, path));
|
|
2275
2555
|
return;
|
|
2276
2556
|
}
|
|
2277
2557
|
function readImageMode(raw, source, warnings) {
|
|
@@ -2375,31 +2655,31 @@ function validateSettings(raw, source) {
|
|
|
2375
2655
|
return { settings, warnings };
|
|
2376
2656
|
}
|
|
2377
2657
|
var settingsFileCache = new Map;
|
|
2378
|
-
function statFileOrNull(
|
|
2658
|
+
function statFileOrNull(path) {
|
|
2379
2659
|
try {
|
|
2380
|
-
return statSync2(
|
|
2660
|
+
return statSync2(path);
|
|
2381
2661
|
} catch {
|
|
2382
2662
|
return null;
|
|
2383
2663
|
}
|
|
2384
2664
|
}
|
|
2385
|
-
function readSettingsFile(
|
|
2386
|
-
const stats = statFileOrNull(
|
|
2665
|
+
function readSettingsFile(path) {
|
|
2666
|
+
const stats = statFileOrNull(path);
|
|
2387
2667
|
if (!stats) {
|
|
2388
|
-
settingsFileCache.delete(
|
|
2668
|
+
settingsFileCache.delete(path);
|
|
2389
2669
|
return { settings: {}, warnings: [] };
|
|
2390
2670
|
}
|
|
2391
|
-
const cached = settingsFileCache.get(
|
|
2671
|
+
const cached = settingsFileCache.get(path);
|
|
2392
2672
|
if (cached && cached.mtimeMs === stats.mtimeMs && cached.size === stats.size)
|
|
2393
2673
|
return cached.result;
|
|
2394
2674
|
let result;
|
|
2395
2675
|
try {
|
|
2396
|
-
const text = readFileSync(
|
|
2397
|
-
result = validateSettings(JSON.parse(text),
|
|
2676
|
+
const text = readFileSync(path, "utf8");
|
|
2677
|
+
result = validateSettings(JSON.parse(text), path);
|
|
2398
2678
|
} catch (error) {
|
|
2399
2679
|
const message = error instanceof Error ? error.message : String(error);
|
|
2400
|
-
result = { settings: {}, warnings: [{ source:
|
|
2680
|
+
result = { settings: {}, warnings: [{ source: path, message: `Invalid JSON: ${message}` }] };
|
|
2401
2681
|
}
|
|
2402
|
-
settingsFileCache.set(
|
|
2682
|
+
settingsFileCache.set(path, { mtimeMs: stats.mtimeMs, size: stats.size, result });
|
|
2403
2683
|
return result;
|
|
2404
2684
|
}
|
|
2405
2685
|
function mergeSettings(base, override) {
|
|
@@ -2413,8 +2693,8 @@ function mergeSettings(base, override) {
|
|
|
2413
2693
|
return settings;
|
|
2414
2694
|
}
|
|
2415
2695
|
function resolveReadSeekJsonSettings() {
|
|
2416
|
-
const globalResult = readSettingsFile(
|
|
2417
|
-
const projectResult = readSettingsFile(
|
|
2696
|
+
const globalResult = readSettingsFile(join(homedir(), ".pi/agent/settings.json"));
|
|
2697
|
+
const projectResult = readSettingsFile(join(process.cwd(), ".pi/settings.json"));
|
|
2418
2698
|
return {
|
|
2419
2699
|
settings: mergeSettings(globalResult.settings, projectResult.settings),
|
|
2420
2700
|
warnings: [...globalResult.warnings, ...projectResult.warnings]
|
|
@@ -2462,21 +2742,13 @@ var DEFAULT_READSEEK_VISION_TIMEOUT_MS = 30 * 60000;
|
|
|
2462
2742
|
function readSeekTimeoutMs() {
|
|
2463
2743
|
return resolveReadSeekTimeoutMs() ?? DEFAULT_READSEEK_TIMEOUT_MS;
|
|
2464
2744
|
}
|
|
2465
|
-
async function spawnReadSeekRaw(args, options = {}) {
|
|
2466
|
-
return runReadSeekNodeText(readSeekBinaryPath(), args, {
|
|
2467
|
-
signal: options.signal,
|
|
2468
|
-
stdin: options.stdin,
|
|
2469
|
-
timeoutMs: options.timeoutMs ?? readSeekTimeoutMs(),
|
|
2470
|
-
cancelable: options.cancelable
|
|
2471
|
-
});
|
|
2472
|
-
}
|
|
2473
2745
|
async function ensureDefaultReadSeekDir() {
|
|
2474
2746
|
const dir = defaultReadSeekDir();
|
|
2475
2747
|
if (!dir)
|
|
2476
2748
|
return null;
|
|
2477
2749
|
if (directoryExists(dir))
|
|
2478
2750
|
return dir;
|
|
2479
|
-
defaultReadSeekDirInit ??=
|
|
2751
|
+
defaultReadSeekDirInit ??= runReadSeekNodeText(readSeekBinaryPath(), ["--readseek-dir", dir, "init"], { timeoutMs: readSeekTimeoutMs() }).then(() => directoryExists(dir) ? dir : null).catch(() => null).finally(() => {
|
|
2480
2752
|
defaultReadSeekDirInit = undefined;
|
|
2481
2753
|
});
|
|
2482
2754
|
return defaultReadSeekDirInit;
|
|
@@ -2498,17 +2770,6 @@ async function readSeekInvocationArgs(args) {
|
|
|
2498
2770
|
const readSeekDir = await ensureDefaultReadSeekDir();
|
|
2499
2771
|
return readSeekDir ? ["--readseek-dir", readSeekDir, ...args] : [...args];
|
|
2500
2772
|
}
|
|
2501
|
-
async function runReadSeekRaw(args, options = {}) {
|
|
2502
|
-
return spawnReadSeekRaw(await readSeekInvocationArgs(args), options);
|
|
2503
|
-
}
|
|
2504
|
-
async function runReadSeek(args, options = {}) {
|
|
2505
|
-
return runReadSeekNodeJson(readSeekBinaryPath(), await readSeekInvocationArgs(args), {
|
|
2506
|
-
signal: options.signal,
|
|
2507
|
-
stdin: options.stdin,
|
|
2508
|
-
timeoutMs: options.timeoutMs ?? readSeekTimeoutMs(),
|
|
2509
|
-
cancelable: options.cancelable
|
|
2510
|
-
});
|
|
2511
|
-
}
|
|
2512
2773
|
var visionInvocationTail = Promise.resolve();
|
|
2513
2774
|
async function runReadSeekVisionRaw(args, options = {}) {
|
|
2514
2775
|
let release;
|
|
@@ -2539,38 +2800,40 @@ async function runReadSeekVisionRaw(args, options = {}) {
|
|
|
2539
2800
|
await predecessor;
|
|
2540
2801
|
}
|
|
2541
2802
|
signal?.throwIfAborted();
|
|
2542
|
-
return await
|
|
2543
|
-
|
|
2544
|
-
|
|
2803
|
+
return await runReadSeekNodeText(readSeekBinaryPath(), await readSeekInvocationArgs(args), {
|
|
2804
|
+
signal: options.signal,
|
|
2805
|
+
stdin: options.stdin,
|
|
2806
|
+
timeoutMs: options.timeoutMs ?? resolveReadSeekTimeoutMs() ?? DEFAULT_READSEEK_VISION_TIMEOUT_MS,
|
|
2807
|
+
cancelable: options.cancelable
|
|
2545
2808
|
});
|
|
2546
2809
|
} finally {
|
|
2547
2810
|
release();
|
|
2548
2811
|
}
|
|
2549
2812
|
}
|
|
2550
|
-
async function runReadSeekVision(args, options = {}) {
|
|
2551
|
-
return parseReadSeekJson(await runReadSeekVisionRaw(args, options));
|
|
2552
|
-
}
|
|
2553
2813
|
var runner = {
|
|
2554
|
-
text: (args, options = {}) => options.vision ? runReadSeekVisionRaw(args, options) :
|
|
2555
|
-
|
|
2814
|
+
text: async (args, options = {}) => options.vision ? runReadSeekVisionRaw(args, options) : runReadSeekNodeText(readSeekBinaryPath(), await readSeekInvocationArgs(args), {
|
|
2815
|
+
signal: options.signal,
|
|
2816
|
+
stdin: options.stdin,
|
|
2817
|
+
timeoutMs: options.timeoutMs ?? readSeekTimeoutMs(),
|
|
2818
|
+
cancelable: options.cancelable
|
|
2819
|
+
}),
|
|
2820
|
+
kdl: async (args, options = {}) => parseReadSeekKdl(await runner.text(args, options))
|
|
2556
2821
|
};
|
|
2557
2822
|
var client = createReadSeekClient(runner);
|
|
2558
|
-
var
|
|
2559
|
-
var
|
|
2560
|
-
var readSeekDigest = client.digest;
|
|
2561
|
-
var readSeekDigestContent = client.digestContent;
|
|
2562
|
-
var readSeekImage = client.image;
|
|
2563
|
-
var readSeekPreparedImage = client.preparedImage;
|
|
2823
|
+
var readSeekInspect = client.inspect;
|
|
2824
|
+
var readSeekExtract = client.extract;
|
|
2564
2825
|
var readSeekSearch = client.search;
|
|
2565
2826
|
var readSeekRefs = client.refs;
|
|
2566
2827
|
var readSeekDef = client.def;
|
|
2567
2828
|
var readSeekCheck = client.check;
|
|
2568
|
-
var readSeekIdentify = client.identify;
|
|
2569
|
-
var readSeekDetect = client.detect;
|
|
2570
2829
|
var readSeekEdit = client.edit;
|
|
2571
2830
|
var readSeekRename = client.rename;
|
|
2572
2831
|
async function readSeekMap(filePath, options = {}) {
|
|
2573
|
-
const envelope = await client.
|
|
2832
|
+
const envelope = await client.inspect(filePath, {
|
|
2833
|
+
select: "map",
|
|
2834
|
+
depth: DEFAULT_INSPECT_MAP_DEPTH,
|
|
2835
|
+
signal: options.signal
|
|
2836
|
+
});
|
|
2574
2837
|
const map = envelope.map;
|
|
2575
2838
|
if (!map || map.language === "unknown" && map.symbols.length === 0)
|
|
2576
2839
|
return null;
|
|
@@ -2634,8 +2897,8 @@ var LANGUAGE_BY_EXTENSION = new Map([
|
|
|
2634
2897
|
[".rs", "rust"],
|
|
2635
2898
|
[".java", "java"]
|
|
2636
2899
|
]);
|
|
2637
|
-
function inferLanguage(
|
|
2638
|
-
const extensionMatch =
|
|
2900
|
+
function inferLanguage(path) {
|
|
2901
|
+
const extensionMatch = path.match(/\.[^.\/\\]+$/);
|
|
2639
2902
|
if (!extensionMatch)
|
|
2640
2903
|
return;
|
|
2641
2904
|
return LANGUAGE_BY_EXTENSION.get(extensionMatch[0].toLowerCase());
|
|
@@ -2681,10 +2944,10 @@ function parseCompactDiffEntries(diff, oldContent, newContent) {
|
|
|
2681
2944
|
`);
|
|
2682
2945
|
const compactDelete = diff.match(/^(\d+):[0-9a-f]{6}\|(.*) → \[deleted\]$/);
|
|
2683
2946
|
if (compactDelete) {
|
|
2684
|
-
const
|
|
2947
|
+
const oldLine = Number(compactDelete[1]);
|
|
2685
2948
|
const text = compactDelete[2] ?? "";
|
|
2686
|
-
if (oldLines[
|
|
2687
|
-
return [{ kind: "remove", oldLine
|
|
2949
|
+
if (oldLines[oldLine - 1] === text)
|
|
2950
|
+
return [{ kind: "remove", oldLine, text }];
|
|
2688
2951
|
}
|
|
2689
2952
|
const compactPrefix = diff.match(/^(\d+):[0-9a-f]{6}\|/);
|
|
2690
2953
|
if (!compactPrefix)
|
|
@@ -2724,9 +2987,9 @@ function longestCommonSubsequence(a, b) {
|
|
|
2724
2987
|
const rows = a.length + 1;
|
|
2725
2988
|
const cols = b.length + 1;
|
|
2726
2989
|
const table = Array.from({ length: rows }, () => Array(cols).fill(0));
|
|
2727
|
-
for (let
|
|
2728
|
-
for (let
|
|
2729
|
-
table[
|
|
2990
|
+
for (let i = a.length - 1;i >= 0; i--) {
|
|
2991
|
+
for (let j = b.length - 1;j >= 0; j--) {
|
|
2992
|
+
table[i][j] = a[i] === b[j] ? table[i + 1][j + 1] + 1 : Math.max(table[i + 1][j], table[i][j + 1]);
|
|
2730
2993
|
}
|
|
2731
2994
|
}
|
|
2732
2995
|
const pairs = [];
|
|
@@ -2844,8 +3107,8 @@ function buildInlineDiffs(entries) {
|
|
|
2844
3107
|
}
|
|
2845
3108
|
return inlineDiffs.length ? inlineDiffs : undefined;
|
|
2846
3109
|
}
|
|
2847
|
-
function finalizeDiffData(
|
|
2848
|
-
const language = inferLanguage(
|
|
3110
|
+
function finalizeDiffData(path, entries, blockRanges) {
|
|
3111
|
+
const language = inferLanguage(path);
|
|
2849
3112
|
const inlineDiffs = buildInlineDiffs(entries);
|
|
2850
3113
|
return {
|
|
2851
3114
|
version: 1,
|
|
@@ -2938,7 +3201,6 @@ function wrapWithHangingIndent(prefix, content, width, options = {}) {
|
|
|
2938
3201
|
const indent = " ".repeat(prefixWidth);
|
|
2939
3202
|
return wrapped.map((line, index) => tint(clampLineToWidth(index === 0 ? prefix + line : indent + line, normalized)));
|
|
2940
3203
|
}
|
|
2941
|
-
var wrappedHashlinesCache = new WeakMap;
|
|
2942
3204
|
function renderPendingResult(pendingLabel, width, theme) {
|
|
2943
3205
|
return new Text(clampLinesToWidth([summaryLine(pendingLabel, { theme, style: "muted" })], width).join(`
|
|
2944
3206
|
`), 0, 0);
|
|
@@ -3004,28 +3266,53 @@ import { Text as Text2, visibleWidth as visibleWidth3 } from "@earendil-works/pi
|
|
|
3004
3266
|
|
|
3005
3267
|
// src/tui-diff-renderer.ts
|
|
3006
3268
|
import { visibleWidth as visibleWidth2 } from "@earendil-works/pi-tui";
|
|
3007
|
-
function
|
|
3008
|
-
|
|
3269
|
+
function isDiffEntry(value) {
|
|
3270
|
+
if (typeof value !== "object" || value === null)
|
|
3271
|
+
return false;
|
|
3272
|
+
const entry = value;
|
|
3273
|
+
if (typeof entry.text !== "string")
|
|
3274
|
+
return false;
|
|
3275
|
+
if (entry.kind === "meta")
|
|
3276
|
+
return true;
|
|
3277
|
+
if (entry.kind === "add")
|
|
3278
|
+
return Number.isInteger(entry.newLine) && Number(entry.newLine) > 0;
|
|
3279
|
+
if (entry.kind === "remove")
|
|
3280
|
+
return Number.isInteger(entry.oldLine) && Number(entry.oldLine) > 0;
|
|
3281
|
+
return entry.kind === "context" && Number.isInteger(entry.oldLine) && Number(entry.oldLine) > 0 && Number.isInteger(entry.newLine) && Number(entry.newLine) > 0;
|
|
3282
|
+
}
|
|
3283
|
+
function indexedDiffEntries(data) {
|
|
3284
|
+
if (!Array.isArray(data.entries))
|
|
3285
|
+
return [];
|
|
3286
|
+
const entries = [];
|
|
3287
|
+
for (const [index, entry] of data.entries.entries()) {
|
|
3288
|
+
if (isDiffEntry(entry))
|
|
3289
|
+
entries.push([index, entry]);
|
|
3290
|
+
}
|
|
3291
|
+
return entries;
|
|
3292
|
+
}
|
|
3293
|
+
function hasOldSide(entries) {
|
|
3294
|
+
return entries.some(([, entry]) => entry.kind === "remove" || entry.kind === "context");
|
|
3009
3295
|
}
|
|
3010
|
-
function chooseMode(width,
|
|
3296
|
+
function chooseMode(width, entries) {
|
|
3011
3297
|
if (width < 24)
|
|
3012
3298
|
return "summary";
|
|
3013
3299
|
if (width < 50)
|
|
3014
3300
|
return "compact";
|
|
3015
|
-
if (width >= 100 && hasOldSide(
|
|
3301
|
+
if (width >= 100 && hasOldSide(entries))
|
|
3016
3302
|
return "split";
|
|
3017
3303
|
return "unified";
|
|
3018
3304
|
}
|
|
3019
|
-
function hunkCount(data) {
|
|
3020
|
-
|
|
3305
|
+
function hunkCount(data, entries) {
|
|
3306
|
+
const blockRangeCount = Array.isArray(data.blockRanges) ? data.blockRanges.length : undefined;
|
|
3307
|
+
return Math.max(1, blockRangeCount ?? (entries.some(([, entry]) => entry.kind === "add" || entry.kind === "remove") ? 1 : 0));
|
|
3021
3308
|
}
|
|
3022
3309
|
function compactHeader(data) {
|
|
3023
3310
|
return `↳ diff +${data.stats.added} -${data.stats.removed}`;
|
|
3024
3311
|
}
|
|
3025
|
-
function header(data, mode, width) {
|
|
3312
|
+
function header(data, mode, width, entries) {
|
|
3026
3313
|
if (mode === "summary" && width <= 10)
|
|
3027
3314
|
return `↳ diff +${data.stats.added}`;
|
|
3028
|
-
const full = `${compactHeader(data)} • ${hunkCount(data)} hunk • 1 file • ${mode === "split" ? "split" : "unified"}`;
|
|
3315
|
+
const full = `${compactHeader(data)} • ${hunkCount(data, entries)} hunk • 1 file • ${mode === "split" ? "split" : "unified"}`;
|
|
3029
3316
|
return visibleWidth2(full) <= width ? full : clampLineToWidth(compactHeader(data), width);
|
|
3030
3317
|
}
|
|
3031
3318
|
function lineNo(entry) {
|
|
@@ -3044,18 +3331,18 @@ function padRightVisual(line, width) {
|
|
|
3044
3331
|
function tint(theme, entry, text) {
|
|
3045
3332
|
return entry.kind === "add" ? theme.fg("success", text) : entry.kind === "remove" ? theme.fg("error", text) : theme.fg("toolOutput", text);
|
|
3046
3333
|
}
|
|
3047
|
-
function spans(theme,
|
|
3048
|
-
return
|
|
3334
|
+
function spans(theme, spans, fallback) {
|
|
3335
|
+
return spans?.map((s) => s.kind === "add" ? theme.fg("success", s.text) : s.kind === "remove" ? theme.fg("error", s.text) : s.text).join("") ?? fallback;
|
|
3049
3336
|
}
|
|
3050
3337
|
function inlineText(input, index, entry) {
|
|
3051
|
-
const pair = input.diffData.inlineDiffs
|
|
3338
|
+
const pair = Array.isArray(input.diffData.inlineDiffs) ? input.diffData.inlineDiffs.find((d) => entry.kind === "remove" ? d.removeLineIndex === index : entry.kind === "add" ? d.addLineIndex === index : false) : undefined;
|
|
3052
3339
|
if (!pair)
|
|
3053
3340
|
return textOf(entry);
|
|
3054
3341
|
return entry.kind === "remove" ? spans(input.theme, pair.removeSpans, textOf(entry)) : entry.kind === "add" ? spans(input.theme, pair.addSpans, textOf(entry)) : textOf(entry);
|
|
3055
3342
|
}
|
|
3056
|
-
function unifiedRows(input, width) {
|
|
3343
|
+
function unifiedRows(input, width, entries) {
|
|
3057
3344
|
const rows = [];
|
|
3058
|
-
for (const [i, e] of
|
|
3345
|
+
for (const [i, e] of entries) {
|
|
3059
3346
|
if (e.kind === "meta")
|
|
3060
3347
|
continue;
|
|
3061
3348
|
const prefix = `▌${gutterMarker(e)} ${lineNo(e)} │ `;
|
|
@@ -3064,9 +3351,9 @@ function unifiedRows(input, width) {
|
|
|
3064
3351
|
}
|
|
3065
3352
|
return rows;
|
|
3066
3353
|
}
|
|
3067
|
-
function compactRows(input, width) {
|
|
3354
|
+
function compactRows(input, width, entries) {
|
|
3068
3355
|
const rows = [];
|
|
3069
|
-
for (const [i, e] of
|
|
3356
|
+
for (const [i, e] of entries) {
|
|
3070
3357
|
if (e.kind !== "add" && e.kind !== "remove")
|
|
3071
3358
|
continue;
|
|
3072
3359
|
const prefix = `▌${gutterMarker(e)} ${lineNo(e)} `;
|
|
@@ -3075,11 +3362,11 @@ function compactRows(input, width) {
|
|
|
3075
3362
|
}
|
|
3076
3363
|
return rows;
|
|
3077
3364
|
}
|
|
3078
|
-
function splitRows(input, width) {
|
|
3365
|
+
function splitRows(input, width, entries) {
|
|
3079
3366
|
const pane = Math.max(10, Math.floor((width - 3) / 2));
|
|
3080
3367
|
const rows = [`${padRightVisual("old", pane)} │ new`];
|
|
3081
3368
|
const blankPane = " ".repeat(pane);
|
|
3082
|
-
for (const [i, e] of
|
|
3369
|
+
for (const [i, e] of entries) {
|
|
3083
3370
|
if (e.kind === "remove") {
|
|
3084
3371
|
const left = wrapWithHangingIndent(`▌- ${e.oldLine} │ `, inlineText(input, i, e), pane, { tint: (text) => tint(input.theme, e, text) });
|
|
3085
3372
|
for (const line of left)
|
|
@@ -3107,13 +3394,18 @@ function hiddenHint(hiddenLines, hiddenHunks, width) {
|
|
|
3107
3394
|
}
|
|
3108
3395
|
function renderTuiDiff(input) {
|
|
3109
3396
|
const width = normalizeWidth(input.width);
|
|
3110
|
-
const
|
|
3111
|
-
const
|
|
3397
|
+
const entries = indexedDiffEntries(input.diffData);
|
|
3398
|
+
const rawEntryCount = Array.isArray(input.diffData.entries) ? input.diffData.entries.length : input.diffData.stats.added + input.diffData.stats.removed + input.diffData.stats.context;
|
|
3399
|
+
const truncated = !Array.isArray(input.diffData.entries) || entries.length !== rawEntryCount;
|
|
3400
|
+
const mode = chooseMode(width, entries);
|
|
3401
|
+
const lines = [header(input.diffData, mode, width, entries)];
|
|
3112
3402
|
if (!input.expanded)
|
|
3113
|
-
return { mode, width, lines: clampLinesToWidth([...lines, hiddenHint(
|
|
3403
|
+
return { mode, width, lines: clampLinesToWidth([...lines, hiddenHint(rawEntryCount, hunkCount(input.diffData, entries), width)], width) };
|
|
3114
3404
|
if (mode === "summary")
|
|
3115
3405
|
return { mode, width, lines: clampLinesToWidth(lines, width) };
|
|
3116
|
-
const rows = mode === "split" ? splitRows(input, width) : mode === "compact" ? compactRows(input, width) : unifiedRows(input, width);
|
|
3406
|
+
const rows = mode === "split" ? splitRows(input, width, entries) : mode === "compact" ? compactRows(input, width, entries) : unifiedRows(input, width, entries);
|
|
3407
|
+
if (truncated)
|
|
3408
|
+
rows.push(clampLineToWidth("… diff preview truncated", width));
|
|
3117
3409
|
return { mode, width, lines: [...lines, ...rows] };
|
|
3118
3410
|
}
|
|
3119
3411
|
|
|
@@ -3169,12 +3461,12 @@ function upsertTextComponent(lastComponent, text) {
|
|
|
3169
3461
|
}
|
|
3170
3462
|
|
|
3171
3463
|
// src/register-tool.ts
|
|
3172
|
-
import { Type } from "@sinclair/typebox";
|
|
3464
|
+
import { Type as Type2 } from "@sinclair/typebox";
|
|
3173
3465
|
function optionalIntOrString(description) {
|
|
3174
|
-
return
|
|
3466
|
+
return Type2.Optional(Type2.Union([Type2.Number({ description }), Type2.String({ description })]));
|
|
3175
3467
|
}
|
|
3176
3468
|
function filePathParam() {
|
|
3177
|
-
return
|
|
3469
|
+
return Type2.String({ description: PARAM_DESCRIPTIONS.path });
|
|
3178
3470
|
}
|
|
3179
3471
|
function registerReadSeekTool(pi, tool) {
|
|
3180
3472
|
pi.registerTool(tool);
|
|
@@ -3182,28 +3474,28 @@ function registerReadSeekTool(pi, tool) {
|
|
|
3182
3474
|
}
|
|
3183
3475
|
|
|
3184
3476
|
// src/edit.ts
|
|
3185
|
-
var hashlineEditItemSchema =
|
|
3186
|
-
set_line:
|
|
3187
|
-
anchor:
|
|
3188
|
-
new_text:
|
|
3477
|
+
var hashlineEditItemSchema = Type3.Object({
|
|
3478
|
+
set_line: Type3.Optional(Type3.Object({
|
|
3479
|
+
anchor: Type3.String({ description: PARAM_DESCRIPTIONS.anchor }),
|
|
3480
|
+
new_text: Type3.String({ description: PARAM_DESCRIPTIONS.newTextLine })
|
|
3189
3481
|
}, { additionalProperties: false, description: PARAM_DESCRIPTIONS.setLine })),
|
|
3190
|
-
replace_lines:
|
|
3191
|
-
start_anchor:
|
|
3192
|
-
end_anchor:
|
|
3193
|
-
new_text:
|
|
3482
|
+
replace_lines: Type3.Optional(Type3.Object({
|
|
3483
|
+
start_anchor: Type3.String({ description: PARAM_DESCRIPTIONS.startAnchor }),
|
|
3484
|
+
end_anchor: Type3.String({ description: PARAM_DESCRIPTIONS.endAnchor }),
|
|
3485
|
+
new_text: Type3.String({ description: PARAM_DESCRIPTIONS.newTextRange })
|
|
3194
3486
|
}, { additionalProperties: false, description: PARAM_DESCRIPTIONS.replaceLines })),
|
|
3195
|
-
insert_after:
|
|
3196
|
-
anchor:
|
|
3197
|
-
new_text:
|
|
3487
|
+
insert_after: Type3.Optional(Type3.Object({
|
|
3488
|
+
anchor: Type3.String({ description: PARAM_DESCRIPTIONS.insertAnchor }),
|
|
3489
|
+
new_text: Type3.String({ description: PARAM_DESCRIPTIONS.newTextInsert })
|
|
3198
3490
|
}, { additionalProperties: false, description: PARAM_DESCRIPTIONS.insertAfter })),
|
|
3199
|
-
replace:
|
|
3200
|
-
old_text:
|
|
3201
|
-
new_text:
|
|
3202
|
-
all:
|
|
3491
|
+
replace: Type3.Optional(Type3.Object({
|
|
3492
|
+
old_text: Type3.String({ description: PARAM_DESCRIPTIONS.oldText }),
|
|
3493
|
+
new_text: Type3.String({ description: PARAM_DESCRIPTIONS.newText }),
|
|
3494
|
+
all: Type3.Optional(Type3.Boolean({ description: PARAM_DESCRIPTIONS.all }))
|
|
3203
3495
|
}, { additionalProperties: false, description: PARAM_DESCRIPTIONS.replace })),
|
|
3204
|
-
replace_symbol:
|
|
3205
|
-
symbol:
|
|
3206
|
-
new_body:
|
|
3496
|
+
replace_symbol: Type3.Optional(Type3.Object({
|
|
3497
|
+
symbol: Type3.String({ description: PARAM_DESCRIPTIONS.symbol }),
|
|
3498
|
+
new_body: Type3.String({ description: PARAM_DESCRIPTIONS.newBody })
|
|
3207
3499
|
}, { additionalProperties: false, description: PARAM_DESCRIPTIONS.replaceSymbol }))
|
|
3208
3500
|
}, {
|
|
3209
3501
|
additionalProperties: false,
|
|
@@ -3211,18 +3503,18 @@ var hashlineEditItemSchema = Type2.Object({
|
|
|
3211
3503
|
maxProperties: 1,
|
|
3212
3504
|
description: PARAM_DESCRIPTIONS.editVariant
|
|
3213
3505
|
});
|
|
3214
|
-
var hashlineEditSchema =
|
|
3506
|
+
var hashlineEditSchema = Type3.Object({
|
|
3215
3507
|
path: filePathParam(),
|
|
3216
|
-
language:
|
|
3217
|
-
edits:
|
|
3508
|
+
language: Type3.Optional(Type3.String({ description: PARAM_DESCRIPTIONS.languageSymbol })),
|
|
3509
|
+
edits: Type3.Array(hashlineEditItemSchema, {
|
|
3218
3510
|
minItems: 1,
|
|
3219
3511
|
description: PARAM_DESCRIPTIONS.edits
|
|
3220
3512
|
}),
|
|
3221
|
-
postEditVerify:
|
|
3513
|
+
postEditVerify: Type3.Optional(Type3.Boolean({
|
|
3222
3514
|
description: PARAM_DESCRIPTIONS.postEditVerify
|
|
3223
3515
|
}))
|
|
3224
3516
|
}, { additionalProperties: false });
|
|
3225
|
-
function buildEditError(
|
|
3517
|
+
function buildEditError(path, code, message, hint, errorDetails) {
|
|
3226
3518
|
return {
|
|
3227
3519
|
content: [{ type: "text", text: message }],
|
|
3228
3520
|
isError: true,
|
|
@@ -3233,7 +3525,7 @@ function buildEditError(path3, code, message, hint, errorDetails) {
|
|
|
3233
3525
|
readSeekValue: {
|
|
3234
3526
|
tool: "edit",
|
|
3235
3527
|
ok: false,
|
|
3236
|
-
path
|
|
3528
|
+
path,
|
|
3237
3529
|
error: buildReadSeekError(code, message, hint, errorDetails)
|
|
3238
3530
|
}
|
|
3239
3531
|
}
|
|
@@ -3247,14 +3539,14 @@ async function executeEdit(opts) {
|
|
|
3247
3539
|
const { params, signal, cwd, wasReadInSession, onFileMutated, syntaxValidate } = opts;
|
|
3248
3540
|
const parsed = params;
|
|
3249
3541
|
const rawPath = parsed.path;
|
|
3250
|
-
const
|
|
3251
|
-
const absolutePath = resolveToCwd2(
|
|
3542
|
+
const path = rawPath.replace(/^@/, "");
|
|
3543
|
+
const absolutePath = resolveToCwd2(path, cwd);
|
|
3252
3544
|
throwIfAborted(signal);
|
|
3253
3545
|
try {
|
|
3254
3546
|
return await withFileMutationQueue(absolutePath, async () => {
|
|
3255
3547
|
throwIfAborted(signal);
|
|
3256
3548
|
if (wasReadInSession && !wasReadInSession(absolutePath)) {
|
|
3257
|
-
const hint = `Call
|
|
3549
|
+
const hint = `Call readSeek_inspect(${JSON.stringify(rawPath)}) first, or use readSeek_grep, readSeek_search, or readSeek_write to produce fresh anchors for this file.`;
|
|
3258
3550
|
return buildEditError(absolutePath, "file-not-read", `You must get fresh anchors for ${absolutePath} before editing it. ${hint}`, hint);
|
|
3259
3551
|
}
|
|
3260
3552
|
const parsedEdits = validateExactlyOneEditVariant(parsed.edits ?? []);
|
|
@@ -3279,7 +3571,7 @@ async function executeEdit(opts) {
|
|
|
3279
3571
|
const originalForSyntax = normalizeToLF(stripBom(original).text);
|
|
3280
3572
|
const resultForSyntax = normalizeToLF(stripBom(result).text);
|
|
3281
3573
|
if (!plan.changed) {
|
|
3282
|
-
return buildEditError(absolutePath, "no-op", `No changes made to ${
|
|
3574
|
+
return buildEditError(absolutePath, "no-op", `No changes made to ${path}. The edits produced identical content.`);
|
|
3283
3575
|
}
|
|
3284
3576
|
const syntaxMode = resolveSyntaxValidateMode({ syntaxValidate });
|
|
3285
3577
|
let syntaxWarning;
|
|
@@ -3320,14 +3612,14 @@ async function executeEdit(opts) {
|
|
|
3320
3612
|
try {
|
|
3321
3613
|
verifiedContent = await fsReadFile(absolutePath, "utf-8");
|
|
3322
3614
|
} catch (err) {
|
|
3323
|
-
return buildEditError(absolutePath, "post-edit-verification-read-failed", `Edit write completed but post-edit verification failed: could not read ${
|
|
3615
|
+
return buildEditError(absolutePath, "post-edit-verification-read-failed", `Edit write completed but post-edit verification failed: could not read ${path} after writing.`, undefined, { fsCode: err?.code, fsMessage: err?.message });
|
|
3324
3616
|
}
|
|
3325
3617
|
if (verifiedContent !== plan.content) {
|
|
3326
|
-
return buildEditError(absolutePath, "post-edit-verification-mismatch", `Edit write completed but post-edit verification did not confirm the intended content for ${
|
|
3618
|
+
return buildEditError(absolutePath, "post-edit-verification-mismatch", `Edit write completed but post-edit verification did not confirm the intended content for ${path}. Re-read the file before making follow-up edits.`, undefined, { expectedLength: plan.content.length, actualLength: verifiedContent.length });
|
|
3327
3619
|
}
|
|
3328
3620
|
}
|
|
3329
3621
|
const diffResult = generateCompactOrFullDiff(original, result);
|
|
3330
|
-
const patch = createPatch(
|
|
3622
|
+
const patch = createPatch(path, original, result);
|
|
3331
3623
|
const diffData = buildDiffData({
|
|
3332
3624
|
path: absolutePath,
|
|
3333
3625
|
oldContent: original,
|
|
@@ -3339,7 +3631,7 @@ async function executeEdit(opts) {
|
|
|
3339
3631
|
const semanticSummary = { classification: classification.classification };
|
|
3340
3632
|
const builtOutput = buildEditOutput({
|
|
3341
3633
|
path: absolutePath,
|
|
3342
|
-
displayPath:
|
|
3634
|
+
displayPath: path,
|
|
3343
3635
|
diff: diffResult.diff,
|
|
3344
3636
|
patch,
|
|
3345
3637
|
diffData,
|
|
@@ -3362,7 +3654,7 @@ async function executeEdit(opts) {
|
|
|
3362
3654
|
});
|
|
3363
3655
|
} catch (err) {
|
|
3364
3656
|
if (typeof err?.code === "string") {
|
|
3365
|
-
return mapEditFileError(err, absolutePath,
|
|
3657
|
+
return mapEditFileError(err, absolutePath, path, "read");
|
|
3366
3658
|
}
|
|
3367
3659
|
throw err;
|
|
3368
3660
|
}
|
|
@@ -3412,8 +3704,8 @@ function registerEditTool(pi, options = {}) {
|
|
|
3412
3704
|
text = clampLineToWidth(text, context.width);
|
|
3413
3705
|
return upsertTextComponent(context.lastComponent, text);
|
|
3414
3706
|
},
|
|
3415
|
-
renderResult(result,
|
|
3416
|
-
const { isPartial, isError, expanded, width, context } = resolveRenderResultContext(
|
|
3707
|
+
renderResult(result, options, theme, ...rest) {
|
|
3708
|
+
const { isPartial, isError, expanded, width, context } = resolveRenderResultContext(options, rest, resolveReadSeekToolDisplayMode("edit") === "expanded");
|
|
3417
3709
|
if (isPartial) {
|
|
3418
3710
|
return renderPendingResult("pending edit", width, theme);
|
|
3419
3711
|
}
|
|
@@ -3469,7 +3761,7 @@ ${theme.fg("error", info.errorText)}`;
|
|
|
3469
3761
|
import { readFile as fsReadFile2, stat as fsStat } from "node:fs/promises";
|
|
3470
3762
|
import path3 from "node:path";
|
|
3471
3763
|
import { createGrepTool } from "@earendil-works/pi-coding-agent";
|
|
3472
|
-
import { Type as
|
|
3764
|
+
import { Type as Type5 } from "@sinclair/typebox";
|
|
3473
3765
|
import { Text as Text4 } from "@earendil-works/pi-tui";
|
|
3474
3766
|
|
|
3475
3767
|
// src/binary-detect.ts
|
|
@@ -3541,14 +3833,14 @@ function buildScopeMetadata(groups, warnings) {
|
|
|
3541
3833
|
}
|
|
3542
3834
|
function buildGrepOutput(input) {
|
|
3543
3835
|
const fileCount = new Set(input.groups.map((group) => group.absolutePath)).size;
|
|
3544
|
-
const
|
|
3836
|
+
const header = `[${input.totalMatches} matches in ${fileCount} files]`;
|
|
3545
3837
|
let text;
|
|
3546
3838
|
if (input.summary) {
|
|
3547
3839
|
const fileLines = [...input.groups].sort((a, b) => b.matchCount - a.matchCount).map((group) => `${group.absolutePath}: ${group.matchCount} matches`);
|
|
3548
|
-
text = [
|
|
3840
|
+
text = [header, ...fileLines].join(`
|
|
3549
3841
|
`);
|
|
3550
3842
|
} else {
|
|
3551
|
-
const blocks = [
|
|
3843
|
+
const blocks = [header];
|
|
3552
3844
|
for (const group of input.groups) {
|
|
3553
3845
|
blocks.push(renderGroupHeader(group));
|
|
3554
3846
|
for (const entry of group.entries) {
|
|
@@ -3653,12 +3945,12 @@ function firstLineNumber(group) {
|
|
|
3653
3945
|
}
|
|
3654
3946
|
function buildSymbolEntries(fileLines, symbol, matchLines, scopeContext) {
|
|
3655
3947
|
if (scopeContext === undefined) {
|
|
3656
|
-
const
|
|
3948
|
+
const entries = [];
|
|
3657
3949
|
for (let lineNumber = symbol.startLine;lineNumber <= symbol.endLine; lineNumber++) {
|
|
3658
3950
|
const built = buildReadSeekLine(lineNumber, fileLines[lineNumber - 1] ?? "");
|
|
3659
|
-
|
|
3951
|
+
entries.push({ kind: matchLines.has(lineNumber) ? "match" : "context", line: built });
|
|
3660
3952
|
}
|
|
3661
|
-
return
|
|
3953
|
+
return entries;
|
|
3662
3954
|
}
|
|
3663
3955
|
const ranges = [...matchLines].sort((a, b) => a - b).map((ln) => ({
|
|
3664
3956
|
startLine: Math.max(symbol.startLine, ln - scopeContext),
|
|
@@ -3841,11 +4133,8 @@ import {
|
|
|
3841
4133
|
} from "@jarkkojs/readseek-api";
|
|
3842
4134
|
|
|
3843
4135
|
// src/tui-source-render.ts
|
|
3844
|
-
import { getLanguageFromPath, highlightCode } from "@earendil-works/pi-coding-agent";
|
|
3845
|
-
import { wrapTextWithAnsi as wrapTextWithAnsi2 } from "@earendil-works/pi-tui";
|
|
3846
4136
|
var GREP_ANCHOR_RE = /^(.*?):(>>| )(\d+):([0-9a-zA-Z]{1,16})\|(.*)$/;
|
|
3847
|
-
|
|
3848
|
-
function renderGrepSourceForDisplay(text, anchors, renderPath = (path3) => path3) {
|
|
4137
|
+
function renderGrepSourceForDisplay(text, anchors, renderPath = (path) => path) {
|
|
3849
4138
|
return text.split(`
|
|
3850
4139
|
`).map((line) => {
|
|
3851
4140
|
const match = line.match(GREP_ANCHOR_RE);
|
|
@@ -3861,39 +4150,30 @@ function renderGrepSourceForDisplay(text, anchors, renderPath = (path3) => path3
|
|
|
3861
4150
|
}
|
|
3862
4151
|
|
|
3863
4152
|
// src/readseek-params.ts
|
|
3864
|
-
import { Type as
|
|
4153
|
+
import { Type as Type4 } from "@sinclair/typebox";
|
|
3865
4154
|
function searchPathParam() {
|
|
3866
|
-
return
|
|
4155
|
+
return Type4.Optional(Type4.String({ description: PARAM_DESCRIPTIONS.searchPath }));
|
|
3867
4156
|
}
|
|
3868
4157
|
function languageParam() {
|
|
3869
|
-
return
|
|
4158
|
+
return Type4.Optional(Type4.String({ description: PARAM_DESCRIPTIONS.language }));
|
|
3870
4159
|
}
|
|
3871
4160
|
function readSeekGitSearchParams() {
|
|
3872
4161
|
return {
|
|
3873
|
-
|
|
3874
|
-
others: Type3.Optional(Type3.Boolean({ description: PARAM_DESCRIPTIONS.others })),
|
|
3875
|
-
ignored: Type3.Optional(Type3.Boolean({ description: PARAM_DESCRIPTIONS.ignored }))
|
|
4162
|
+
gitScope: Type4.Optional(Type4.Union(GIT_SCOPES.map((scope) => Type4.Literal(scope)), { description: PARAM_DESCRIPTIONS.gitScope }))
|
|
3876
4163
|
};
|
|
3877
4164
|
}
|
|
3878
|
-
function validateIgnoredRequiresOthers(tool, params) {
|
|
3879
|
-
const validated = validateGitSelection(params);
|
|
3880
|
-
if (!validated.ok) {
|
|
3881
|
-
return buildToolErrorResult(tool, "invalid-parameter", `${tool} parameter 'ignored' requires 'others'`);
|
|
3882
|
-
}
|
|
3883
|
-
return null;
|
|
3884
|
-
}
|
|
3885
4165
|
|
|
3886
4166
|
// src/grep.ts
|
|
3887
|
-
var grepSchema =
|
|
3888
|
-
pattern:
|
|
4167
|
+
var grepSchema = Type5.Object({
|
|
4168
|
+
pattern: Type5.String({ description: PARAM_DESCRIPTIONS.grepPattern }),
|
|
3889
4169
|
path: searchPathParam(),
|
|
3890
|
-
glob:
|
|
3891
|
-
ignoreCase:
|
|
3892
|
-
literal:
|
|
4170
|
+
glob: Type5.Optional(Type5.String({ description: PARAM_DESCRIPTIONS.glob })),
|
|
4171
|
+
ignoreCase: Type5.Optional(Type5.Boolean({ description: PARAM_DESCRIPTIONS.ignoreCase })),
|
|
4172
|
+
literal: Type5.Optional(Type5.Boolean({ description: PARAM_DESCRIPTIONS.literal })),
|
|
3893
4173
|
context: optionalIntOrString(PARAM_DESCRIPTIONS.context),
|
|
3894
4174
|
limit: optionalIntOrString(PARAM_DESCRIPTIONS.matchLimit),
|
|
3895
|
-
summary:
|
|
3896
|
-
scope:
|
|
4175
|
+
summary: Type5.Optional(Type5.Boolean({ description: PARAM_DESCRIPTIONS.summary })),
|
|
4176
|
+
scope: Type5.Optional(Type5.Literal("symbol", {
|
|
3897
4177
|
description: PARAM_DESCRIPTIONS.grepScope
|
|
3898
4178
|
})),
|
|
3899
4179
|
scopeContext: optionalIntOrString(PARAM_DESCRIPTIONS.scopeContext)
|
|
@@ -3965,12 +4245,6 @@ function truncateGroupEntries(group) {
|
|
|
3965
4245
|
}
|
|
3966
4246
|
return { ...group, entries: kept };
|
|
3967
4247
|
}
|
|
3968
|
-
function recordsFromGroups(groups) {
|
|
3969
|
-
return groups.flatMap((group) => group.entries.flatMap((entry) => entry.kind === "separator" ? [] : [{ path: group.absolutePath, ...entry.line, kind: entry.kind }]));
|
|
3970
|
-
}
|
|
3971
|
-
function escapeForRegex(s) {
|
|
3972
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3973
|
-
}
|
|
3974
4248
|
async function executeGrep(opts) {
|
|
3975
4249
|
const { toolCallId, params, signal, onUpdate, cwd, onFileAnchored } = opts;
|
|
3976
4250
|
await ensureHashInit();
|
|
@@ -3979,10 +4253,16 @@ async function executeGrep(opts) {
|
|
|
3979
4253
|
if (!context.ok) {
|
|
3980
4254
|
return buildToolErrorResult("grep", "invalid-params-combo", context.message);
|
|
3981
4255
|
}
|
|
4256
|
+
if (context.value !== undefined && context.value < 0) {
|
|
4257
|
+
return buildToolErrorResult("grep", "invalid-params-combo", `Invalid context: expected a non-negative integer, received ${context.value}.`);
|
|
4258
|
+
}
|
|
3982
4259
|
const limit = coerceObviousBase10Int2(rawParams.limit, "limit");
|
|
3983
4260
|
if (!limit.ok) {
|
|
3984
4261
|
return buildToolErrorResult("grep", "invalid-limit", limit.message);
|
|
3985
4262
|
}
|
|
4263
|
+
if (limit.value !== undefined && limit.value < 1) {
|
|
4264
|
+
return buildToolErrorResult("grep", "invalid-limit", `Invalid limit: expected a positive integer, received ${limit.value}.`);
|
|
4265
|
+
}
|
|
3986
4266
|
const scopeContext = coerceObviousBase10Int2(rawParams.scopeContext, "scopeContext");
|
|
3987
4267
|
if (!scopeContext.ok) {
|
|
3988
4268
|
return buildToolErrorResult("grep", "invalid-params-combo", scopeContext.message);
|
|
@@ -4087,7 +4367,7 @@ async function executeGrep(opts) {
|
|
|
4087
4367
|
const CONCURRENCY = 8;
|
|
4088
4368
|
const pathList = [...pathsToRead];
|
|
4089
4369
|
for (let i = 0;i < pathList.length; i += CONCURRENCY) {
|
|
4090
|
-
await Promise.all(pathList.slice(i, i + CONCURRENCY).map((
|
|
4370
|
+
await Promise.all(pathList.slice(i, i + CONCURRENCY).map((p) => getFileLines(p)));
|
|
4091
4371
|
}
|
|
4092
4372
|
}
|
|
4093
4373
|
const addSummaryMatch = (displayPath, absolutePath) => {
|
|
@@ -4140,7 +4420,7 @@ async function executeGrep(opts) {
|
|
|
4140
4420
|
const flags = p.ignoreCase ? "i" : "";
|
|
4141
4421
|
let patternRe = null;
|
|
4142
4422
|
try {
|
|
4143
|
-
patternRe = p.literal ? new RegExp(
|
|
4423
|
+
patternRe = p.literal ? new RegExp(p.pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), flags) : new RegExp(p.pattern, flags);
|
|
4144
4424
|
} catch {}
|
|
4145
4425
|
if (patternRe !== null) {
|
|
4146
4426
|
let emitted = false;
|
|
@@ -4224,7 +4504,7 @@ ${warning}` } : item),
|
|
|
4224
4504
|
renderedGroups = scoped.groups;
|
|
4225
4505
|
scopeWarnings = scoped.warnings;
|
|
4226
4506
|
}
|
|
4227
|
-
const readSeekRecords =
|
|
4507
|
+
const readSeekRecords = renderedGroups.flatMap((group) => group.entries.flatMap((entry) => entry.kind === "separator" ? [] : [{ path: group.absolutePath, ...entry.line, kind: entry.kind }]));
|
|
4228
4508
|
const builtOutput = buildGrepOutput({
|
|
4229
4509
|
summary: !!summary,
|
|
4230
4510
|
totalMatches,
|
|
@@ -4295,8 +4575,8 @@ function registerGrepTool(pi, options = {}) {
|
|
|
4295
4575
|
}
|
|
4296
4576
|
return new Text4(clampLineToWidth(text, context.width), 0, 0);
|
|
4297
4577
|
},
|
|
4298
|
-
renderResult(result,
|
|
4299
|
-
const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(
|
|
4578
|
+
renderResult(result, options, theme, ...rest) {
|
|
4579
|
+
const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(options, rest, resolveReadSeekToolDisplayMode("grep") === "expanded");
|
|
4300
4580
|
if (isPartial)
|
|
4301
4581
|
return renderPendingResult("pending grep", width, theme);
|
|
4302
4582
|
const content = result.content?.[0];
|
|
@@ -4343,7 +4623,7 @@ function registerGrepTool(pi, options = {}) {
|
|
|
4343
4623
|
|
|
4344
4624
|
// src/search.ts
|
|
4345
4625
|
import path4 from "node:path";
|
|
4346
|
-
import { Type as
|
|
4626
|
+
import { Type as Type6 } from "@sinclair/typebox";
|
|
4347
4627
|
|
|
4348
4628
|
// src/stat-search-path.ts
|
|
4349
4629
|
import { stat as fsStat2 } from "node:fs/promises";
|
|
@@ -4351,9 +4631,9 @@ async function statSearchPathOrError(tool, rawPath, searchPath) {
|
|
|
4351
4631
|
try {
|
|
4352
4632
|
return { ok: true, stats: await fsStat2(searchPath) };
|
|
4353
4633
|
} catch (err) {
|
|
4354
|
-
const
|
|
4634
|
+
const path = rawPath ?? searchPath;
|
|
4355
4635
|
const { code, message } = formatFsError(err, "stat-error");
|
|
4356
|
-
return { ok: false, error: buildToolErrorResult(tool, code, message, { path
|
|
4636
|
+
return { ok: false, error: buildToolErrorResult(tool, code, message, { path }) };
|
|
4357
4637
|
}
|
|
4358
4638
|
}
|
|
4359
4639
|
|
|
@@ -4434,9 +4714,6 @@ function readSeekLanguageForPath(language, searchPath, isFile) {
|
|
|
4434
4714
|
async function executeSearch(opts) {
|
|
4435
4715
|
const { params, signal, cwd, onFileAnchored } = opts;
|
|
4436
4716
|
const p = params;
|
|
4437
|
-
const ignoredError = validateIgnoredRequiresOthers("search", p);
|
|
4438
|
-
if (ignoredError)
|
|
4439
|
-
return ignoredError;
|
|
4440
4717
|
const searchPath = resolveToCwd2(p.path ?? ".", cwd);
|
|
4441
4718
|
const statResult = await statSearchPathOrError("search", p.path, searchPath);
|
|
4442
4719
|
if (!statResult.ok)
|
|
@@ -4446,9 +4723,7 @@ async function executeSearch(opts) {
|
|
|
4446
4723
|
const effectiveLang = readSeekLanguageForPath(p.language, searchPath, searchPathIsFile);
|
|
4447
4724
|
const results = await readSeekSearch(searchPath, p.pattern, {
|
|
4448
4725
|
language: effectiveLang,
|
|
4449
|
-
|
|
4450
|
-
others: p.others,
|
|
4451
|
-
ignored: p.ignored,
|
|
4726
|
+
gitScope: p.gitScope,
|
|
4452
4727
|
signal
|
|
4453
4728
|
});
|
|
4454
4729
|
if (results.length === 0) {
|
|
@@ -4510,8 +4785,8 @@ function registerSearchTool(pi, options = {}) {
|
|
|
4510
4785
|
description: SEARCH_PROMPT_METADATA.description,
|
|
4511
4786
|
promptSnippet: SEARCH_PROMPT_METADATA.promptSnippet,
|
|
4512
4787
|
promptGuidelines: SEARCH_PROMPT_METADATA.promptGuidelines,
|
|
4513
|
-
parameters:
|
|
4514
|
-
pattern:
|
|
4788
|
+
parameters: Type6.Object({
|
|
4789
|
+
pattern: Type6.String({ description: PARAM_DESCRIPTIONS.pattern }),
|
|
4515
4790
|
language: languageParam(),
|
|
4516
4791
|
path: searchPathParam(),
|
|
4517
4792
|
...readSeekGitSearchParams()
|
|
@@ -4523,11 +4798,11 @@ function registerSearchTool(pi, options = {}) {
|
|
|
4523
4798
|
return renderReadSeekSearchCall(args, theme, rest, {
|
|
4524
4799
|
label: "search",
|
|
4525
4800
|
accent: `/${args.pattern}/`,
|
|
4526
|
-
flags: [args.
|
|
4801
|
+
flags: [args.gitScope && `git-scope=${args.gitScope}`]
|
|
4527
4802
|
});
|
|
4528
4803
|
},
|
|
4529
|
-
renderResult(result,
|
|
4530
|
-
return renderAnchoredFilesResult(result,
|
|
4804
|
+
renderResult(result, options, theme, ...rest) {
|
|
4805
|
+
return renderAnchoredFilesResult(result, options, theme, rest, {
|
|
4531
4806
|
pendingLabel: "pending search",
|
|
4532
4807
|
emptyLabel: "no matches",
|
|
4533
4808
|
unitSingular: "match",
|
|
@@ -4539,7 +4814,7 @@ function registerSearchTool(pi, options = {}) {
|
|
|
4539
4814
|
}
|
|
4540
4815
|
|
|
4541
4816
|
// src/refs.ts
|
|
4542
|
-
import { Type as
|
|
4817
|
+
import { Type as Type7 } from "@sinclair/typebox";
|
|
4543
4818
|
import path5 from "node:path";
|
|
4544
4819
|
|
|
4545
4820
|
// src/refs-output.ts
|
|
@@ -4605,9 +4880,6 @@ function isReadSeekCursorValidationFailure(message) {
|
|
|
4605
4880
|
async function executeRefs(opts) {
|
|
4606
4881
|
const { params, signal, cwd, onFileAnchored } = opts;
|
|
4607
4882
|
const p = params;
|
|
4608
|
-
const ignoredError = validateIgnoredRequiresOthers("refs", p);
|
|
4609
|
-
if (ignoredError)
|
|
4610
|
-
return ignoredError;
|
|
4611
4883
|
const scopeError = validateRefsNameVsScope(p);
|
|
4612
4884
|
if (!scopeError.ok) {
|
|
4613
4885
|
return buildToolErrorResult("refs", "invalid-parameter", `refs parameter: ${scopeError.error}`);
|
|
@@ -4623,9 +4895,7 @@ async function executeRefs(opts) {
|
|
|
4623
4895
|
line: p.line,
|
|
4624
4896
|
column: p.column,
|
|
4625
4897
|
language: p.language,
|
|
4626
|
-
|
|
4627
|
-
others: p.others,
|
|
4628
|
-
ignored: p.ignored,
|
|
4898
|
+
gitScope: p.gitScope,
|
|
4629
4899
|
signal
|
|
4630
4900
|
});
|
|
4631
4901
|
const files = groupReferences(references, cwd);
|
|
@@ -4658,13 +4928,13 @@ function registerRefsTool(pi, options = {}) {
|
|
|
4658
4928
|
description: REFS_PROMPT_METADATA.description,
|
|
4659
4929
|
promptSnippet: REFS_PROMPT_METADATA.promptSnippet,
|
|
4660
4930
|
promptGuidelines: REFS_PROMPT_METADATA.promptGuidelines,
|
|
4661
|
-
parameters:
|
|
4662
|
-
name:
|
|
4931
|
+
parameters: Type7.Object({
|
|
4932
|
+
name: Type7.Optional(Type7.String({ description: PARAM_DESCRIPTIONS.refsName })),
|
|
4663
4933
|
path: searchPathParam(),
|
|
4664
4934
|
language: languageParam(),
|
|
4665
|
-
scope:
|
|
4666
|
-
line:
|
|
4667
|
-
column:
|
|
4935
|
+
scope: Type7.Optional(Type7.Boolean({ description: PARAM_DESCRIPTIONS.scope })),
|
|
4936
|
+
line: Type7.Optional(Type7.Number({ description: PARAM_DESCRIPTIONS.line })),
|
|
4937
|
+
column: Type7.Optional(Type7.Number({ description: PARAM_DESCRIPTIONS.column })),
|
|
4668
4938
|
...readSeekGitSearchParams()
|
|
4669
4939
|
}),
|
|
4670
4940
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
@@ -4675,11 +4945,11 @@ function registerRefsTool(pi, options = {}) {
|
|
|
4675
4945
|
return renderReadSeekSearchCall(args, theme, rest, {
|
|
4676
4946
|
label: "refs",
|
|
4677
4947
|
accent,
|
|
4678
|
-
flags: [args.scope && "scope", args.
|
|
4948
|
+
flags: [args.scope && "scope", args.gitScope && `git-scope=${args.gitScope}`]
|
|
4679
4949
|
});
|
|
4680
4950
|
},
|
|
4681
|
-
renderResult(result,
|
|
4682
|
-
return renderAnchoredFilesResult(result,
|
|
4951
|
+
renderResult(result, options, theme, ...rest) {
|
|
4952
|
+
return renderAnchoredFilesResult(result, options, theme, rest, {
|
|
4683
4953
|
pendingLabel: "pending refs",
|
|
4684
4954
|
emptyLabel: "no references",
|
|
4685
4955
|
unitSingular: "reference",
|
|
@@ -4691,20 +4961,20 @@ function registerRefsTool(pi, options = {}) {
|
|
|
4691
4961
|
}
|
|
4692
4962
|
|
|
4693
4963
|
// src/rename.ts
|
|
4694
|
-
import { Type as
|
|
4964
|
+
import { Type as Type8 } from "@sinclair/typebox";
|
|
4695
4965
|
import path6 from "node:path";
|
|
4696
4966
|
import { Text as Text5 } from "@earendil-works/pi-tui";
|
|
4697
4967
|
var RENAME_PROMPT_METADATA = defineToolPromptMetadata({
|
|
4698
4968
|
promptUrl: new URL("../prompts/rename.md", import.meta.url),
|
|
4699
4969
|
promptSnippet: "Rename a symbol at a source location with verified edits"
|
|
4700
4970
|
});
|
|
4701
|
-
var renameSchema =
|
|
4971
|
+
var renameSchema = Type8.Object({
|
|
4702
4972
|
path: filePathParam(),
|
|
4703
|
-
line:
|
|
4704
|
-
column:
|
|
4705
|
-
to:
|
|
4706
|
-
workspace:
|
|
4707
|
-
apply:
|
|
4973
|
+
line: Type8.Integer({ minimum: 1, description: PARAM_DESCRIPTIONS.renameLine }),
|
|
4974
|
+
column: Type8.Optional(Type8.Integer({ minimum: 1, description: PARAM_DESCRIPTIONS.column })),
|
|
4975
|
+
to: Type8.String({ description: PARAM_DESCRIPTIONS.to }),
|
|
4976
|
+
workspace: Type8.Optional(Type8.Boolean({ description: PARAM_DESCRIPTIONS.workspace })),
|
|
4977
|
+
apply: Type8.Optional(Type8.Boolean({ description: PARAM_DESCRIPTIONS.applyDefaultTrue }))
|
|
4708
4978
|
});
|
|
4709
4979
|
async function executeRename(opts) {
|
|
4710
4980
|
const { params, signal, cwd, onFileMutated } = opts;
|
|
@@ -4736,7 +5006,8 @@ async function executeRename(opts) {
|
|
|
4736
5006
|
onFileMutated?.(absoluteFile);
|
|
4737
5007
|
}
|
|
4738
5008
|
}
|
|
4739
|
-
const
|
|
5009
|
+
const primaryAbs = path6.isAbsolute(output.file) ? output.file : path6.resolve(cwd, output.file);
|
|
5010
|
+
const files = [primaryAbs];
|
|
4740
5011
|
for (const other of output.others) {
|
|
4741
5012
|
const abs = path6.isAbsolute(other.file) ? other.file : path6.resolve(cwd, other.file);
|
|
4742
5013
|
if (!files.includes(abs))
|
|
@@ -4796,8 +5067,8 @@ function registerRenameTool(pi, options = {}) {
|
|
|
4796
5067
|
text += theme.fg("dim", ` → ${args.to}`);
|
|
4797
5068
|
return new Text5(clampLineToWidth(text, context.width), 0, 0);
|
|
4798
5069
|
},
|
|
4799
|
-
renderResult(result,
|
|
4800
|
-
const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(
|
|
5070
|
+
renderResult(result, options, theme, ...rest) {
|
|
5071
|
+
const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(options, rest);
|
|
4801
5072
|
if (isPartial)
|
|
4802
5073
|
return renderPendingResult("pending rename", width, theme);
|
|
4803
5074
|
const content = result.content?.[0];
|
|
@@ -4838,7 +5109,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
4838
5109
|
import { dirname as dirname2, relative as relative3 } from "node:path";
|
|
4839
5110
|
import { withFileMutationQueue as withFileMutationQueue2, truncateHead as truncateHead2 } from "@earendil-works/pi-coding-agent";
|
|
4840
5111
|
import { Text as Text6 } from "@earendil-works/pi-tui";
|
|
4841
|
-
import { Type as
|
|
5112
|
+
import { Type as Type9 } from "@sinclair/typebox";
|
|
4842
5113
|
|
|
4843
5114
|
// src/pending-diff-preview.ts
|
|
4844
5115
|
import { existsSync as existsSync3, readFileSync as readFileSync2, realpathSync, statSync as statSync4 } from "node:fs";
|
|
@@ -4985,9 +5256,9 @@ function pendingWritePreviewParts(summary, preview, expanded, theme) {
|
|
|
4985
5256
|
if (!preview || preview.type !== "ok")
|
|
4986
5257
|
return { lines: summary.split(`
|
|
4987
5258
|
`) };
|
|
4988
|
-
const
|
|
5259
|
+
const hasOldSide = preview.data.fileExistedBeforeWrite;
|
|
4989
5260
|
const headerLine = summaryLine(preview.data.headerLabel, { hidden: !expanded });
|
|
4990
|
-
if (!
|
|
5261
|
+
if (!hasOldSide) {
|
|
4991
5262
|
const lines = [summary, headerLine];
|
|
4992
5263
|
if (expanded)
|
|
4993
5264
|
lines.push(...formatContentPreviewLines(preview.data.nextContent, theme));
|
|
@@ -5043,7 +5314,7 @@ async function executeWrite(opts) {
|
|
|
5043
5314
|
const warnings = [];
|
|
5044
5315
|
const readSeekWarnings = [];
|
|
5045
5316
|
if (hasBareCarriageReturn(content)) {
|
|
5046
|
-
const message = "File content contains bare CR (\\r) line endings; readSeek_write refuses to emit anchors that
|
|
5317
|
+
const message = "File content contains bare CR (\\r) line endings; readSeek_write refuses to emit anchors that readSeek_inspect/readSeek_edit would normalize differently.";
|
|
5047
5318
|
warnings.push(message);
|
|
5048
5319
|
readSeekWarnings.push(buildReadSeekWarning("bare-cr", message));
|
|
5049
5320
|
return buildToolErrorResult("write", "bare-cr", `Cannot write ${filePath}
|
|
@@ -5134,9 +5405,9 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5134
5405
|
description: promptMetadata.description,
|
|
5135
5406
|
promptSnippet: promptMetadata.promptSnippet,
|
|
5136
5407
|
promptGuidelines: promptMetadata.promptGuidelines,
|
|
5137
|
-
parameters:
|
|
5408
|
+
parameters: Type9.Object({
|
|
5138
5409
|
path: filePathParam(),
|
|
5139
|
-
content:
|
|
5410
|
+
content: Type9.String({ description: PARAM_DESCRIPTIONS.content })
|
|
5140
5411
|
}),
|
|
5141
5412
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
5142
5413
|
const cwd = ctx?.cwd ?? process.cwd();
|
|
@@ -5174,13 +5445,13 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5174
5445
|
}
|
|
5175
5446
|
},
|
|
5176
5447
|
renderCall(args, theme, context = {}) {
|
|
5177
|
-
const { path
|
|
5448
|
+
const { path, content } = args;
|
|
5178
5449
|
const cwd = context.cwd ?? process.cwd();
|
|
5179
5450
|
const label = renderToolLabel(theme, "write");
|
|
5180
5451
|
const lineCount = typeof content === "string" ? content.split(`
|
|
5181
5452
|
`).length : 0;
|
|
5182
5453
|
const bytes = typeof content === "string" ? Buffer.byteLength(content, "utf8") : 0;
|
|
5183
|
-
const renderedPath = typeof
|
|
5454
|
+
const renderedPath = typeof path === "string" ? linkToolPath(theme.fg("muted", path), path, cwd) : theme.fg("toolOutput", "...");
|
|
5184
5455
|
let text = clampLineToWidth(`${label} ${renderedPath}${typeof content === "string" ? ` (${lineCount} ${lineCount === 1 ? "line" : "lines"} • ${bytes} B)` : ""}`, context.width);
|
|
5185
5456
|
if (context.executionStarted) {
|
|
5186
5457
|
return upsertTextComponent(context.lastComponent, text);
|
|
@@ -5195,8 +5466,8 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5195
5466
|
return upsertTextComponent(context.lastComponent, clampLinesToWidth(parts.lines, context.width).join(`
|
|
5196
5467
|
`));
|
|
5197
5468
|
},
|
|
5198
|
-
renderResult(result,
|
|
5199
|
-
const { isPartial, expanded, width, context } = resolveRenderResultContext(
|
|
5469
|
+
renderResult(result, options, theme, ...rest) {
|
|
5470
|
+
const { isPartial, expanded, width, context } = resolveRenderResultContext(options, rest, resolveReadSeekToolDisplayMode("write") === "expanded");
|
|
5200
5471
|
if (isPartial)
|
|
5201
5472
|
return renderPendingResult("pending write", width, theme);
|
|
5202
5473
|
const details = result.details ?? {};
|
|
@@ -5209,8 +5480,8 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5209
5480
|
if (state === "created") {
|
|
5210
5481
|
const readSeekLines = details.readSeekValue?.lines ?? [];
|
|
5211
5482
|
const hasContent = readSeekLines.length > 0;
|
|
5212
|
-
const
|
|
5213
|
-
const lines =
|
|
5483
|
+
const header = summaryLine(state, { hidden: hasContent && !expanded, theme, style: "success" });
|
|
5484
|
+
const lines = header.split(`
|
|
5214
5485
|
`);
|
|
5215
5486
|
if (expanded && hasContent) {
|
|
5216
5487
|
const content = readSeekLines.map((l) => l.raw).join(`
|
|
@@ -5235,7 +5506,7 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5235
5506
|
}
|
|
5236
5507
|
|
|
5237
5508
|
// src/def.ts
|
|
5238
|
-
import { Type as
|
|
5509
|
+
import { Type as Type10 } from "@sinclair/typebox";
|
|
5239
5510
|
import path7 from "node:path";
|
|
5240
5511
|
var DEF_PROMPT_METADATA = defineToolPromptMetadata({
|
|
5241
5512
|
promptUrl: new URL("../prompts/def.md", import.meta.url),
|
|
@@ -5244,9 +5515,6 @@ var DEF_PROMPT_METADATA = defineToolPromptMetadata({
|
|
|
5244
5515
|
async function executeDef(opts) {
|
|
5245
5516
|
const { params, signal, cwd, onFileAnchored } = opts;
|
|
5246
5517
|
const p = params;
|
|
5247
|
-
const ignoredError = validateIgnoredRequiresOthers("def", p);
|
|
5248
|
-
if (ignoredError)
|
|
5249
|
-
return ignoredError;
|
|
5250
5518
|
if (!p.name || !p.name.trim()) {
|
|
5251
5519
|
return buildToolErrorResult("def", "invalid-parameter", "readSeek_def requires 'name'");
|
|
5252
5520
|
}
|
|
@@ -5258,9 +5526,7 @@ async function executeDef(opts) {
|
|
|
5258
5526
|
const definitions = await readSeekDef(searchPath, {
|
|
5259
5527
|
name: p.name,
|
|
5260
5528
|
language: p.language,
|
|
5261
|
-
|
|
5262
|
-
others: p.others,
|
|
5263
|
-
ignored: p.ignored,
|
|
5529
|
+
gitScope: p.gitScope,
|
|
5264
5530
|
signal
|
|
5265
5531
|
});
|
|
5266
5532
|
if (definitions.length === 0) {
|
|
@@ -5311,8 +5577,8 @@ function registerDefTool(pi, options = {}) {
|
|
|
5311
5577
|
description: DEF_PROMPT_METADATA.description,
|
|
5312
5578
|
promptSnippet: DEF_PROMPT_METADATA.promptSnippet,
|
|
5313
5579
|
promptGuidelines: DEF_PROMPT_METADATA.promptGuidelines,
|
|
5314
|
-
parameters:
|
|
5315
|
-
name:
|
|
5580
|
+
parameters: Type10.Object({
|
|
5581
|
+
name: Type10.String({ description: PARAM_DESCRIPTIONS.name }),
|
|
5316
5582
|
path: searchPathParam(),
|
|
5317
5583
|
language: languageParam(),
|
|
5318
5584
|
...readSeekGitSearchParams()
|
|
@@ -5324,11 +5590,11 @@ function registerDefTool(pi, options = {}) {
|
|
|
5324
5590
|
return renderReadSeekSearchCall(args, theme, rest, {
|
|
5325
5591
|
label: "def",
|
|
5326
5592
|
accent: args.name,
|
|
5327
|
-
flags: [args.
|
|
5593
|
+
flags: [args.gitScope && `git-scope=${args.gitScope}`]
|
|
5328
5594
|
});
|
|
5329
5595
|
},
|
|
5330
|
-
renderResult(result,
|
|
5331
|
-
return renderAnchoredFilesResult(result,
|
|
5596
|
+
renderResult(result, options, theme, ...rest) {
|
|
5597
|
+
return renderAnchoredFilesResult(result, options, theme, rest, {
|
|
5332
5598
|
pendingLabel: "pending def",
|
|
5333
5599
|
emptyLabel: "no definitions",
|
|
5334
5600
|
unitSingular: "definition",
|
|
@@ -5338,38 +5604,33 @@ function registerDefTool(pi, options = {}) {
|
|
|
5338
5604
|
});
|
|
5339
5605
|
}
|
|
5340
5606
|
|
|
5341
|
-
// src/
|
|
5607
|
+
// src/inspect.ts
|
|
5342
5608
|
import { Text as Text7 } from "@earendil-works/pi-tui";
|
|
5343
|
-
import { Type as
|
|
5344
|
-
var
|
|
5345
|
-
var
|
|
5346
|
-
var digestSchema = Type10.Object({
|
|
5609
|
+
import { Type as Type11 } from "@sinclair/typebox";
|
|
5610
|
+
var facetSchema = Type11.Union(INSPECT_FACETS.map((facet) => Type11.Literal(facet)));
|
|
5611
|
+
var inspectSchema = Type11.Object({
|
|
5347
5612
|
path: filePathParam(),
|
|
5348
|
-
|
|
5613
|
+
selector: Type11.Optional(Type11.String({ description: PARAM_DESCRIPTIONS.selector })),
|
|
5614
|
+
select: Type11.Optional(Type11.Union([
|
|
5349
5615
|
facetSchema,
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
], {
|
|
5355
|
-
description: PARAM_DESCRIPTIONS.select
|
|
5356
|
-
})),
|
|
5357
|
-
at: Type10.Optional(Type10.String({
|
|
5358
|
-
description: PARAM_DESCRIPTIONS.at
|
|
5359
|
-
})),
|
|
5616
|
+
Type11.Array(facetSchema, { minItems: 1 }),
|
|
5617
|
+
Type11.String({ description: PARAM_DESCRIPTIONS.selectComma })
|
|
5618
|
+
], { description: PARAM_DESCRIPTIONS.select })),
|
|
5619
|
+
query: Type11.Optional(Type11.String({ description: PARAM_DESCRIPTIONS.query })),
|
|
5360
5620
|
end: optionalIntOrString(PARAM_DESCRIPTIONS.end),
|
|
5361
5621
|
limit: optionalIntOrString(PARAM_DESCRIPTIONS.limit),
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5622
|
+
depth: optionalIntOrString(PARAM_DESCRIPTIONS.inspectDepth),
|
|
5623
|
+
language: Type11.Optional(Type11.String({ description: PARAM_DESCRIPTIONS.languageOverride })),
|
|
5624
|
+
visionMode: Type11.Optional(Type11.Union(VISION_MODES.map((mode) => Type11.Literal(mode)), { description: PARAM_DESCRIPTIONS.visionModeInspect })),
|
|
5625
|
+
visionLevel: Type11.Optional(Type11.Union(VISION_LEVELS.map((level) => Type11.Literal(level)), { description: PARAM_DESCRIPTIONS.visionLevel }))
|
|
5365
5626
|
});
|
|
5366
|
-
function
|
|
5627
|
+
function hasInspectAnchors(value) {
|
|
5367
5628
|
if (!value || typeof value !== "object")
|
|
5368
5629
|
return false;
|
|
5369
5630
|
const record = value;
|
|
5370
5631
|
if (Array.isArray(record.hashlines) && record.hashlines.length > 0)
|
|
5371
5632
|
return true;
|
|
5372
|
-
return Object.values(record).some(
|
|
5633
|
+
return Object.values(record).some(hasInspectAnchors);
|
|
5373
5634
|
}
|
|
5374
5635
|
function parseSelect(select) {
|
|
5375
5636
|
if (select === undefined)
|
|
@@ -5379,8 +5640,8 @@ function parseSelect(select) {
|
|
|
5379
5640
|
if (facets.length === 0)
|
|
5380
5641
|
throw new Error("select must include at least one facet");
|
|
5381
5642
|
for (const facet of facets) {
|
|
5382
|
-
if (!
|
|
5383
|
-
throw new Error(`unknown
|
|
5643
|
+
if (!INSPECT_FACETS.includes(facet)) {
|
|
5644
|
+
throw new Error(`unknown inspect facet \`${facet}\`; expected ${INSPECT_FACETS.join(", ")}`);
|
|
5384
5645
|
}
|
|
5385
5646
|
}
|
|
5386
5647
|
return facets;
|
|
@@ -5388,15 +5649,15 @@ function parseSelect(select) {
|
|
|
5388
5649
|
if (typeof select !== "string") {
|
|
5389
5650
|
throw new Error("select must be a facet, facet list, or comma-separated facets");
|
|
5390
5651
|
}
|
|
5391
|
-
if (
|
|
5652
|
+
if (INSPECT_FACETS.includes(select)) {
|
|
5392
5653
|
return select;
|
|
5393
5654
|
}
|
|
5394
5655
|
const parts = select.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
|
|
5395
5656
|
if (parts.length === 0)
|
|
5396
5657
|
throw new Error("select must include at least one facet");
|
|
5397
5658
|
for (const part of parts) {
|
|
5398
|
-
if (!
|
|
5399
|
-
throw new Error(`unknown
|
|
5659
|
+
if (!INSPECT_FACETS.includes(part)) {
|
|
5660
|
+
throw new Error(`unknown inspect facet \`${part}\`; expected ${INSPECT_FACETS.join(", ")}`);
|
|
5400
5661
|
}
|
|
5401
5662
|
}
|
|
5402
5663
|
return parts;
|
|
@@ -5408,7 +5669,7 @@ function selectedFacets(select) {
|
|
|
5408
5669
|
}
|
|
5409
5670
|
function summarizeEnvelope(envelope, facets) {
|
|
5410
5671
|
if (!envelope || typeof envelope !== "object")
|
|
5411
|
-
return `
|
|
5672
|
+
return `inspected ${facets.join(",")}`;
|
|
5412
5673
|
const record = envelope;
|
|
5413
5674
|
const parts = [];
|
|
5414
5675
|
for (const facet of facets) {
|
|
@@ -5417,7 +5678,7 @@ function summarizeEnvelope(envelope, facets) {
|
|
|
5417
5678
|
}
|
|
5418
5679
|
if (record.metadata !== undefined && !parts.includes("metadata"))
|
|
5419
5680
|
parts.unshift("metadata");
|
|
5420
|
-
return `
|
|
5681
|
+
return `inspected ${parts.join(",") || "metadata"}`;
|
|
5421
5682
|
}
|
|
5422
5683
|
function parseImageEnvelope(envelope) {
|
|
5423
5684
|
if (!envelope || typeof envelope !== "object")
|
|
@@ -5435,12 +5696,12 @@ function parseImageEnvelope(envelope) {
|
|
|
5435
5696
|
const prepared = content.encoding === "base64" && typeof content.data === "string" && typeof mime === "string" ? { data: content.data, mimeType: mime } : undefined;
|
|
5436
5697
|
return { metadata, prepared };
|
|
5437
5698
|
}
|
|
5438
|
-
function
|
|
5699
|
+
function buildInspectSuccess(filePath, facets, envelope, content = [{ type: "text", text: JSON.stringify(envelope) }]) {
|
|
5439
5700
|
return {
|
|
5440
5701
|
content,
|
|
5441
5702
|
details: {
|
|
5442
5703
|
readSeekValue: {
|
|
5443
|
-
tool: "
|
|
5704
|
+
tool: "inspect",
|
|
5444
5705
|
ok: true,
|
|
5445
5706
|
path: filePath,
|
|
5446
5707
|
select: facets,
|
|
@@ -5449,14 +5710,14 @@ function buildDigestSuccess(filePath, facets, envelope, content = [{ type: "text
|
|
|
5449
5710
|
}
|
|
5450
5711
|
};
|
|
5451
5712
|
}
|
|
5452
|
-
async function
|
|
5713
|
+
async function executeInspect(opts) {
|
|
5453
5714
|
const { params, signal, cwd, onFileAnchored } = opts;
|
|
5454
5715
|
const p = params;
|
|
5455
5716
|
let select;
|
|
5456
5717
|
try {
|
|
5457
5718
|
select = parseSelect(p.select);
|
|
5458
5719
|
} catch (err) {
|
|
5459
|
-
return buildToolErrorResult("
|
|
5720
|
+
return buildToolErrorResult("inspect", "invalid-parameter", err?.message ?? String(err), {
|
|
5460
5721
|
path: p.path
|
|
5461
5722
|
});
|
|
5462
5723
|
}
|
|
@@ -5465,40 +5726,66 @@ async function executeDigest(opts) {
|
|
|
5465
5726
|
const end = coerceObviousBase10Int2(p.end, "end");
|
|
5466
5727
|
if (!end.ok || end.value !== undefined && end.value < 1) {
|
|
5467
5728
|
const message = end.ok ? `Invalid end: expected a positive integer, received ${end.value}.` : end.message;
|
|
5468
|
-
return buildToolErrorResult("
|
|
5729
|
+
return buildToolErrorResult("inspect", "invalid-parameter", message, { path: p.path });
|
|
5469
5730
|
}
|
|
5470
5731
|
const limit = coerceObviousBase10Int2(p.limit, "limit");
|
|
5471
5732
|
if (!limit.ok || limit.value !== undefined && limit.value < 1) {
|
|
5472
5733
|
const message = limit.ok ? `Invalid limit: expected a positive integer, received ${limit.value}.` : limit.message;
|
|
5473
|
-
return buildToolErrorResult("
|
|
5734
|
+
return buildToolErrorResult("inspect", "invalid-parameter", message, { path: p.path });
|
|
5474
5735
|
}
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
return buildToolErrorResult("digest", "invalid-parameter", "Invalid at: expected a non-empty location.", {
|
|
5478
|
-
path: p.path
|
|
5479
|
-
});
|
|
5736
|
+
if (end.value !== undefined && limit.value !== undefined) {
|
|
5737
|
+
return buildToolErrorResult("inspect", "invalid-params-combo", "end and limit cannot be combined.", { path: p.path });
|
|
5480
5738
|
}
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
});
|
|
5739
|
+
const depth = coerceObviousBase10Int2(p.depth, "depth");
|
|
5740
|
+
if (!depth.ok || depth.value !== undefined && depth.value < 0) {
|
|
5741
|
+
const message = depth.ok ? `Invalid depth: expected a non-negative integer, received ${depth.value}.` : depth.message;
|
|
5742
|
+
return buildToolErrorResult("inspect", "invalid-parameter", message, { path: p.path });
|
|
5743
|
+
}
|
|
5744
|
+
if (depth.value !== undefined && !facets.includes("map")) {
|
|
5745
|
+
return buildToolErrorResult("inspect", "invalid-parameter", "depth requires the map facet.", { path: p.path });
|
|
5746
|
+
}
|
|
5747
|
+
const selector = typeof p.selector === "string" ? p.selector.trim() : undefined;
|
|
5748
|
+
if (p.selector !== undefined && !selector) {
|
|
5749
|
+
return buildToolErrorResult("inspect", "invalid-parameter", "Invalid selector: expected a non-empty selector.", { path: p.path });
|
|
5750
|
+
}
|
|
5751
|
+
const query = typeof p.query === "string" ? p.query.trim() : undefined;
|
|
5752
|
+
if (p.query !== undefined && !query) {
|
|
5753
|
+
return buildToolErrorResult("inspect", "invalid-parameter", "Invalid query: expected a non-empty query.", { path: p.path });
|
|
5754
|
+
}
|
|
5755
|
+
if (selector !== undefined && query !== undefined) {
|
|
5756
|
+
return buildToolErrorResult("inspect", "invalid-params-combo", "selector and query cannot be combined.", { path: p.path });
|
|
5757
|
+
}
|
|
5758
|
+
if (facets.includes("identity") && selector === undefined) {
|
|
5759
|
+
return buildToolErrorResult("inspect", "invalid-params-combo", "the identity facet requires a selector.", { path: p.path });
|
|
5760
|
+
}
|
|
5761
|
+
if (p.visionLevel !== undefined && (p.visionMode === undefined || p.visionMode === "none") && !appliesImagePolicy) {
|
|
5762
|
+
return buildToolErrorResult("inspect", "invalid-params-combo", "visionLevel requires an analysis visionMode.", { path: p.path });
|
|
5485
5763
|
}
|
|
5486
5764
|
const filePath = resolveToCwd2(p.path, cwd);
|
|
5487
|
-
const commonOptions = {
|
|
5488
|
-
|
|
5765
|
+
const commonOptions = {
|
|
5766
|
+
select,
|
|
5767
|
+
signal,
|
|
5768
|
+
depth: depth.value ?? (facets.includes("map") ? DEFAULT_INSPECT_MAP_DEPTH : undefined)
|
|
5769
|
+
};
|
|
5770
|
+
const selectionOptions = {
|
|
5489
5771
|
...commonOptions,
|
|
5490
|
-
|
|
5772
|
+
selector,
|
|
5773
|
+
query,
|
|
5491
5774
|
end: end.value,
|
|
5492
5775
|
limit: limit.value,
|
|
5493
5776
|
language: p.language
|
|
5494
5777
|
};
|
|
5495
|
-
const
|
|
5778
|
+
const probeOptions = {
|
|
5779
|
+
...commonOptions,
|
|
5780
|
+
select: [...new Set(["metadata", ...facets])].filter((facet) => facet !== "identity")
|
|
5781
|
+
};
|
|
5782
|
+
const hasSelectionOptions = selector !== undefined || query !== undefined || end.value !== undefined || limit.value !== undefined || p.language !== undefined;
|
|
5496
5783
|
const hasVisionOptions = p.visionMode !== undefined || p.visionLevel !== undefined;
|
|
5497
5784
|
const initialVisionMode = appliesImagePolicy ? undefined : p.visionMode;
|
|
5498
5785
|
const initialVisionLevel = appliesImagePolicy ? undefined : p.visionLevel;
|
|
5499
5786
|
try {
|
|
5500
|
-
const envelope = await
|
|
5501
|
-
...appliesImagePolicy ?
|
|
5787
|
+
const envelope = await readSeekInspect(filePath, {
|
|
5788
|
+
...appliesImagePolicy ? probeOptions : selectionOptions,
|
|
5502
5789
|
visionMode: initialVisionMode,
|
|
5503
5790
|
visionLevel: initialVisionLevel,
|
|
5504
5791
|
vision: initialVisionMode !== undefined && initialVisionMode !== "none"
|
|
@@ -5507,60 +5794,60 @@ async function executeDigest(opts) {
|
|
|
5507
5794
|
if (image) {
|
|
5508
5795
|
const imageDetails = { metadata: image.metadata };
|
|
5509
5796
|
if (opts.imageMode === "off") {
|
|
5510
|
-
return
|
|
5797
|
+
return buildInspectSuccess(filePath, facets, imageDetails, [
|
|
5511
5798
|
{ type: "text", text: `[Skipped standalone image: ${p.path}; readseek.imageMode is off]` }
|
|
5512
5799
|
]);
|
|
5513
5800
|
}
|
|
5514
5801
|
if (opts.imageMode === "auto" && opts.modelSupportsImages && image.prepared) {
|
|
5515
|
-
return
|
|
5802
|
+
return buildInspectSuccess(filePath, facets, imageDetails, [
|
|
5516
5803
|
{ type: "image", data: image.prepared.data, mimeType: image.prepared.mimeType }
|
|
5517
5804
|
]);
|
|
5518
5805
|
}
|
|
5519
5806
|
const visionMode = p.visionMode && p.visionMode !== "none" ? p.visionMode : "all";
|
|
5520
|
-
const analyzedEnvelope = await
|
|
5807
|
+
const analyzedEnvelope = await readSeekInspect(filePath, {
|
|
5521
5808
|
...commonOptions,
|
|
5522
5809
|
visionMode,
|
|
5523
5810
|
visionLevel: p.visionLevel,
|
|
5524
5811
|
vision: true
|
|
5525
5812
|
});
|
|
5526
|
-
return
|
|
5813
|
+
return buildInspectSuccess(filePath, facets, analyzedEnvelope);
|
|
5527
5814
|
}
|
|
5528
|
-
if (p.visionLevel !== undefined && p.visionMode === undefined) {
|
|
5529
|
-
return buildToolErrorResult("
|
|
5815
|
+
if (p.visionLevel !== undefined && (p.visionMode === undefined || p.visionMode === "none")) {
|
|
5816
|
+
return buildToolErrorResult("inspect", "invalid-params-combo", "visionLevel requires an analysis visionMode.", {
|
|
5530
5817
|
path: p.path
|
|
5531
5818
|
});
|
|
5532
5819
|
}
|
|
5533
|
-
const
|
|
5534
|
-
...
|
|
5820
|
+
const selectedEnvelope = appliesImagePolicy && (hasSelectionOptions || hasVisionOptions) ? await readSeekInspect(filePath, {
|
|
5821
|
+
...selectionOptions,
|
|
5535
5822
|
visionMode: p.visionMode,
|
|
5536
5823
|
visionLevel: p.visionLevel,
|
|
5537
5824
|
vision: p.visionMode !== undefined && p.visionMode !== "none"
|
|
5538
5825
|
}) : envelope;
|
|
5539
|
-
if (
|
|
5826
|
+
if (hasInspectAnchors(selectedEnvelope))
|
|
5540
5827
|
onFileAnchored?.(filePath);
|
|
5541
|
-
return
|
|
5828
|
+
return buildInspectSuccess(filePath, facets, selectedEnvelope);
|
|
5542
5829
|
} catch (err) {
|
|
5543
5830
|
const failure = classifyReadSeekFailure(err);
|
|
5544
|
-
return buildToolErrorResult("
|
|
5831
|
+
return buildToolErrorResult("inspect", failure.code, failure.message, failure.hint ? { hint: failure.hint } : {});
|
|
5545
5832
|
}
|
|
5546
5833
|
}
|
|
5547
|
-
function
|
|
5834
|
+
function registerInspectTool(pi, options = {}) {
|
|
5548
5835
|
const promptMetadata = defineToolPromptMetadata({
|
|
5549
|
-
promptUrl: new URL("../prompts/
|
|
5550
|
-
promptSnippet: "
|
|
5551
|
-
registeredName: options.name ?? "
|
|
5836
|
+
promptUrl: new URL("../prompts/inspect.md", import.meta.url),
|
|
5837
|
+
promptSnippet: "Inspect source, images, and documents",
|
|
5838
|
+
registeredName: options.name ?? "readSeek_inspect",
|
|
5552
5839
|
toolAliases: options.toolAliases
|
|
5553
5840
|
});
|
|
5554
5841
|
return registerReadSeekTool(pi, {
|
|
5555
|
-
name: options.name ?? "
|
|
5556
|
-
label: "
|
|
5842
|
+
name: options.name ?? "readSeek_inspect",
|
|
5843
|
+
label: "Inspect",
|
|
5557
5844
|
description: promptMetadata.description,
|
|
5558
5845
|
promptSnippet: promptMetadata.promptSnippet,
|
|
5559
5846
|
toolAliases: options.toolAliases,
|
|
5560
5847
|
promptGuidelines: promptMetadata.promptGuidelines,
|
|
5561
|
-
parameters:
|
|
5848
|
+
parameters: inspectSchema,
|
|
5562
5849
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
5563
|
-
return
|
|
5850
|
+
return executeInspect({
|
|
5564
5851
|
params,
|
|
5565
5852
|
signal,
|
|
5566
5853
|
cwd: ctx.cwd,
|
|
@@ -5573,23 +5860,23 @@ function registerDigestTool(pi, options = {}) {
|
|
|
5573
5860
|
const context = rest[0] ?? {};
|
|
5574
5861
|
const cwd = context.cwd ?? process.cwd();
|
|
5575
5862
|
const displayPath = typeof args?.path === "string" ? args.path : "?";
|
|
5576
|
-
let text = theme.fg("toolTitle", theme.bold("
|
|
5863
|
+
let text = theme.fg("toolTitle", theme.bold("inspect"));
|
|
5577
5864
|
text += ` ${linkToolPath(theme.fg("accent", displayPath), displayPath, cwd)}`;
|
|
5578
5865
|
if (args?.select !== undefined) {
|
|
5579
5866
|
const select = Array.isArray(args.select) ? args.select.join(",") : String(args.select);
|
|
5580
5867
|
text += theme.fg("dim", ` [${select}]`);
|
|
5581
5868
|
}
|
|
5582
|
-
if (typeof args?.
|
|
5583
|
-
text += theme.fg("dim", ` @ ${args.
|
|
5869
|
+
if (typeof args?.selector === "string" && args.selector)
|
|
5870
|
+
text += theme.fg("dim", ` @ ${args.selector}`);
|
|
5584
5871
|
return new Text7(clampLineToWidth(text, context.width), 0, 0);
|
|
5585
5872
|
},
|
|
5586
|
-
renderResult(result,
|
|
5587
|
-
const { isPartial, isError, expanded, width } = resolveRenderResultContext(
|
|
5873
|
+
renderResult(result, options, theme, ...rest) {
|
|
5874
|
+
const { isPartial, isError, expanded, width } = resolveRenderResultContext(options, rest);
|
|
5588
5875
|
if (isPartial)
|
|
5589
|
-
return renderPendingResult("pending
|
|
5876
|
+
return renderPendingResult("pending inspect", width, theme);
|
|
5590
5877
|
const textContent = result.content?.[0]?.type === "text" ? result.content[0].text : "";
|
|
5591
5878
|
if (isError || result.isError) {
|
|
5592
|
-
return renderErrorResult(textContent, { expanded, width, fallback: "
|
|
5879
|
+
return renderErrorResult(textContent, { expanded, width, fallback: "inspect failed", theme });
|
|
5593
5880
|
}
|
|
5594
5881
|
const value = result.details?.readSeekValue;
|
|
5595
5882
|
const facets = Array.isArray(value?.select) ? value.select : ["content"];
|
|
@@ -5605,131 +5892,88 @@ ${textContent}`;
|
|
|
5605
5892
|
});
|
|
5606
5893
|
}
|
|
5607
5894
|
|
|
5608
|
-
// src/
|
|
5609
|
-
import {
|
|
5610
|
-
DEFAULT_MAX_BYTES as DEFAULT_MAX_BYTES2,
|
|
5611
|
-
DEFAULT_MAX_LINES as DEFAULT_MAX_LINES2,
|
|
5612
|
-
truncateHead as truncateHead3
|
|
5613
|
-
} from "@earendil-works/pi-coding-agent";
|
|
5895
|
+
// src/extract.ts
|
|
5614
5896
|
import { Text as Text8 } from "@earendil-works/pi-tui";
|
|
5615
|
-
import { Type as
|
|
5616
|
-
var
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
promptSnippet: "Digest a document's structure or selected content"
|
|
5897
|
+
import { Type as Type12 } from "@sinclair/typebox";
|
|
5898
|
+
var EXTRACT_PROMPT_METADATA = defineToolPromptMetadata({
|
|
5899
|
+
promptUrl: new URL("../prompts/extract.md", import.meta.url),
|
|
5900
|
+
promptSnippet: "Extract a selected document asset to a file"
|
|
5620
5901
|
});
|
|
5621
|
-
async function
|
|
5622
|
-
const {
|
|
5623
|
-
const input = params;
|
|
5624
|
-
const
|
|
5625
|
-
if (!
|
|
5626
|
-
|
|
5627
|
-
return buildToolErrorResult("view", "invalid-page", message, { path: input.path });
|
|
5628
|
-
}
|
|
5629
|
-
const depth = coerceObviousBase10Int2(input.depth, "depth");
|
|
5630
|
-
if (!depth.ok || depth.value !== undefined && depth.value < 0) {
|
|
5631
|
-
const message = depth.ok ? `Invalid depth: expected a non-negative integer, received ${depth.value}.` : depth.message;
|
|
5632
|
-
return buildToolErrorResult("view", "invalid-depth", message, { path: input.path });
|
|
5633
|
-
}
|
|
5634
|
-
const node = input.node?.trim();
|
|
5635
|
-
if (input.node !== undefined && !node) {
|
|
5636
|
-
return buildToolErrorResult("view", "invalid-node", "Invalid node: expected a non-empty ID.", {
|
|
5902
|
+
async function executeExtract(opts) {
|
|
5903
|
+
const { signal, cwd, onFileMutated } = opts;
|
|
5904
|
+
const input = opts.params;
|
|
5905
|
+
const selector = typeof input.selector === "string" ? input.selector.trim() : "";
|
|
5906
|
+
if (!selector.startsWith("asset:") || selector === "asset:") {
|
|
5907
|
+
return buildToolErrorResult("extract", "invalid-selector", "selector must identify an asset as asset:ID.", {
|
|
5637
5908
|
path: input.path
|
|
5638
5909
|
});
|
|
5639
5910
|
}
|
|
5640
|
-
const
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
visionLevel: input.visionLevel
|
|
5644
|
-
});
|
|
5645
|
-
if (!visionValidation.ok) {
|
|
5646
|
-
return buildToolErrorResult("view", "invalid-params-combo", `${visionValidation.error}.`, {
|
|
5911
|
+
const output = typeof input.output === "string" ? input.output.trim() : "";
|
|
5912
|
+
if (!output || output === "-") {
|
|
5913
|
+
return buildToolErrorResult("extract", "invalid-output", "output must be a file path, not stdout.", {
|
|
5647
5914
|
path: input.path
|
|
5648
5915
|
});
|
|
5649
5916
|
}
|
|
5650
5917
|
const filePath = resolveToCwd2(input.path, cwd);
|
|
5918
|
+
const outputPath = resolveToCwd2(output, cwd);
|
|
5651
5919
|
try {
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
page: page.value,
|
|
5655
|
-
kind: input.kind,
|
|
5656
|
-
depth: depth.value,
|
|
5657
|
-
outline: input.outline,
|
|
5658
|
-
visionMode: input.visionMode,
|
|
5659
|
-
visionLevel: input.visionLevel,
|
|
5660
|
-
signal
|
|
5661
|
-
});
|
|
5662
|
-
const truncation = truncateHead3(output, {
|
|
5663
|
-
maxLines: DEFAULT_MAX_LINES2,
|
|
5664
|
-
maxBytes: DEFAULT_MAX_BYTES2
|
|
5665
|
-
});
|
|
5666
|
-
const notice = truncation.truncated ? `
|
|
5667
|
-
[… document view truncated; narrow it with page, node, kind, or depth]` : "";
|
|
5920
|
+
await readSeekExtract(filePath, selector, outputPath, { signal, cancelable: false });
|
|
5921
|
+
onFileMutated?.(outputPath);
|
|
5668
5922
|
return {
|
|
5669
|
-
content: [{ type: "text", text:
|
|
5923
|
+
content: [{ type: "text", text: `Extracted ${selector} to ${outputPath}` }],
|
|
5670
5924
|
details: {
|
|
5671
5925
|
readSeekValue: {
|
|
5672
|
-
tool: "
|
|
5926
|
+
tool: "extract",
|
|
5927
|
+
ok: true,
|
|
5673
5928
|
path: filePath,
|
|
5674
|
-
|
|
5929
|
+
selector,
|
|
5930
|
+
output: outputPath
|
|
5675
5931
|
}
|
|
5676
5932
|
}
|
|
5677
5933
|
};
|
|
5678
5934
|
} catch (error) {
|
|
5679
5935
|
const failure = classifyReadSeekFailure(error);
|
|
5680
|
-
return buildToolErrorResult("
|
|
5936
|
+
return buildToolErrorResult("extract", failure.code, failure.message, failure.hint ? { hint: failure.hint } : {});
|
|
5681
5937
|
}
|
|
5682
5938
|
}
|
|
5683
|
-
function
|
|
5939
|
+
function registerExtractTool(pi, options = {}) {
|
|
5684
5940
|
return registerReadSeekTool(pi, {
|
|
5685
|
-
name: "
|
|
5686
|
-
label: "
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
promptGuidelines: VIEW_PROMPT_METADATA.promptGuidelines,
|
|
5690
|
-
parameters: Type11.Object({
|
|
5941
|
+
name: "readSeek_extract",
|
|
5942
|
+
label: "Extract",
|
|
5943
|
+
...EXTRACT_PROMPT_METADATA,
|
|
5944
|
+
parameters: Type12.Object({
|
|
5691
5945
|
path: filePathParam(),
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
kind: Type11.Optional(Type11.Union(NODE_KINDS.map((kind) => Type11.Literal(kind)), {
|
|
5695
|
-
description: PARAM_DESCRIPTIONS.kind
|
|
5696
|
-
})),
|
|
5697
|
-
depth: optionalIntOrString(PARAM_DESCRIPTIONS.depth),
|
|
5698
|
-
visionMode: Type11.Optional(Type11.Union(VISION_ANALYSIS_MODES.map((mode) => Type11.Literal(mode)), { description: PARAM_DESCRIPTIONS.visionModeView })),
|
|
5699
|
-
visionLevel: Type11.Optional(Type11.Union(VISION_LEVELS.map((level) => Type11.Literal(level)), { description: PARAM_DESCRIPTIONS.visionLevel })),
|
|
5700
|
-
outline: Type11.Optional(Type11.Boolean({ description: PARAM_DESCRIPTIONS.outline }))
|
|
5946
|
+
selector: Type12.String({ pattern: "^asset:.+", description: PARAM_DESCRIPTIONS.assetSelector }),
|
|
5947
|
+
output: Type12.String({ minLength: 1, pattern: "^(?!-$).+", description: PARAM_DESCRIPTIONS.output })
|
|
5701
5948
|
}),
|
|
5702
5949
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
5703
|
-
return
|
|
5950
|
+
return executeExtract({
|
|
5951
|
+
params,
|
|
5952
|
+
signal,
|
|
5953
|
+
cwd: ctx.cwd,
|
|
5954
|
+
onFileMutated: options.onFileMutated
|
|
5955
|
+
});
|
|
5704
5956
|
},
|
|
5705
5957
|
renderCall(args, theme, ...rest) {
|
|
5706
5958
|
const context = rest[0] ?? {};
|
|
5707
5959
|
const cwd = context.cwd ?? process.cwd();
|
|
5708
5960
|
const displayPath = typeof args?.path === "string" ? args.path : "?";
|
|
5709
|
-
const
|
|
5961
|
+
const output = typeof args?.output === "string" ? args.output : "?";
|
|
5962
|
+
let text = `${theme.fg("toolTitle", theme.bold("extract"))} ${linkToolPath(theme.fg("accent", displayPath), displayPath, cwd)}`;
|
|
5963
|
+
if (typeof args?.selector === "string")
|
|
5964
|
+
text += theme.fg("dim", ` ${args.selector}`);
|
|
5965
|
+
text += theme.fg("dim", ` → ${output}`);
|
|
5710
5966
|
return new Text8(clampLineToWidth(text, context.width), 0, 0);
|
|
5711
5967
|
},
|
|
5712
5968
|
renderResult(result, options, theme, ...rest) {
|
|
5713
|
-
const { isPartial, isError,
|
|
5969
|
+
const { isPartial, isError, width } = resolveRenderResultContext(options, rest);
|
|
5714
5970
|
if (isPartial)
|
|
5715
|
-
return renderPendingResult("pending
|
|
5971
|
+
return renderPendingResult("pending extract", width, theme);
|
|
5716
5972
|
const textContent = result.content?.[0]?.type === "text" ? result.content[0].text : "";
|
|
5717
5973
|
if (isError || result.isError) {
|
|
5718
|
-
return renderErrorResult(textContent, { expanded, width, fallback: "
|
|
5974
|
+
return renderErrorResult(textContent, { expanded: false, width, fallback: "extract failed", theme });
|
|
5719
5975
|
}
|
|
5720
|
-
|
|
5721
|
-
`).filter(Boolean).length;
|
|
5722
|
-
let text = summaryLine(`loaded ${lines} document ${lines === 1 ? "line" : "lines"}`, {
|
|
5723
|
-
hidden: !!textContent && !expanded,
|
|
5724
|
-
theme,
|
|
5725
|
-
style: "success"
|
|
5726
|
-
});
|
|
5727
|
-
if (expanded && textContent)
|
|
5728
|
-
text += `
|
|
5729
|
-
${textContent}`;
|
|
5730
|
-
return new Text8(clampLinesToWidth(text.split(`
|
|
5731
|
-
`), width).join(`
|
|
5732
|
-
`), 0, 0);
|
|
5976
|
+
return new Text8(clampLineToWidth(summaryLine(textContent || "asset extracted", { theme, style: "success" }), width), 0, 0);
|
|
5733
5977
|
}
|
|
5734
5978
|
});
|
|
5735
5979
|
}
|
|
@@ -5760,32 +6004,20 @@ var READSEEK_TOOL_ENTRIES = [
|
|
|
5760
6004
|
{ builtIn: null, readSeekName: "readSeek_rename" },
|
|
5761
6005
|
{ builtIn: "write", readSeekName: "readSeek_write" },
|
|
5762
6006
|
{ builtIn: null, readSeekName: "readSeek_def" },
|
|
5763
|
-
{ builtIn: "read", readSeekName: "
|
|
5764
|
-
{ builtIn: null, readSeekName: "
|
|
6007
|
+
{ builtIn: "read", readSeekName: "readSeek_inspect" },
|
|
6008
|
+
{ builtIn: null, readSeekName: "readSeek_extract" }
|
|
5765
6009
|
];
|
|
5766
|
-
function formatSettingsWarning(warning) {
|
|
5767
|
-
return `${warning.message} (${warning.source})`;
|
|
5768
|
-
}
|
|
5769
|
-
function toolRoutingPolicy(aliases) {
|
|
5770
|
-
return renderToolRoutingPolicy({
|
|
5771
|
-
aliases,
|
|
5772
|
-
title: "ReadSeek tool policy:"
|
|
5773
|
-
});
|
|
5774
|
-
}
|
|
5775
6010
|
function piReadSeekExtension(pi) {
|
|
5776
6011
|
const sessionAnchors = new SessionAnchors;
|
|
5777
|
-
const markAnchored = (absolutePath) => sessionAnchors.markAnchored(absolutePath);
|
|
5778
|
-
const hasFreshAnchors = (absolutePath) => sessionAnchors.hasFreshAnchors(absolutePath);
|
|
5779
|
-
const forgetAnchors = (absolutePath) => sessionAnchors.forget(absolutePath);
|
|
5780
6012
|
const { settings } = resolveReadSeekJsonSettings();
|
|
5781
6013
|
const replacedBuiltIns = new Set(settings.overrideTools ?? []);
|
|
5782
6014
|
const swap = (builtIn, readSeekName) => replacedBuiltIns.has(builtIn) ? builtIn : readSeekName;
|
|
5783
|
-
const readName = swap("read", "
|
|
6015
|
+
const readName = swap("read", "readSeek_inspect");
|
|
5784
6016
|
const editName = swap("edit", "readSeek_edit");
|
|
5785
6017
|
const writeName = swap("write", "readSeek_write");
|
|
5786
6018
|
const grepName = swap("grep", "readSeek_grep");
|
|
5787
6019
|
const toolAliases = {
|
|
5788
|
-
|
|
6020
|
+
readSeek_inspect: readName,
|
|
5789
6021
|
readSeek_edit: editName,
|
|
5790
6022
|
readSeek_grep: grepName,
|
|
5791
6023
|
readSeek_write: writeName
|
|
@@ -5794,20 +6026,33 @@ function piReadSeekExtension(pi) {
|
|
|
5794
6026
|
const readSeekName = `readSeek_${name}`;
|
|
5795
6027
|
return [name, toolAliases[readSeekName] ?? readSeekName];
|
|
5796
6028
|
}));
|
|
5797
|
-
registerEditTool(pi, {
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
onFileAnchored: markAnchored,
|
|
6029
|
+
registerEditTool(pi, {
|
|
6030
|
+
wasReadInSession: (absolutePath) => sessionAnchors.hasFreshAnchors(absolutePath),
|
|
6031
|
+
onFileMutated: (absolutePath) => sessionAnchors.forget(absolutePath),
|
|
6032
|
+
name: editName,
|
|
6033
|
+
toolAliases
|
|
6034
|
+
});
|
|
6035
|
+
registerGrepTool(pi, {
|
|
6036
|
+
onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath),
|
|
6037
|
+
name: grepName
|
|
6038
|
+
});
|
|
6039
|
+
registerSearchTool(pi, { onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath) });
|
|
6040
|
+
registerRefsTool(pi, { onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath) });
|
|
6041
|
+
registerRenameTool(pi, { onFileMutated: (absolutePath) => sessionAnchors.forget(absolutePath) });
|
|
6042
|
+
registerDefTool(pi, { onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath) });
|
|
6043
|
+
registerInspectTool(pi, {
|
|
6044
|
+
onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath),
|
|
5805
6045
|
name: readName,
|
|
5806
6046
|
toolAliases,
|
|
5807
6047
|
imageMode: replacedBuiltIns.has("read") ? resolveReadSeekImageMode() : undefined
|
|
5808
6048
|
});
|
|
5809
|
-
|
|
5810
|
-
|
|
6049
|
+
registerExtractTool(pi, {
|
|
6050
|
+
onFileMutated: (absolutePath) => sessionAnchors.forget(absolutePath)
|
|
6051
|
+
});
|
|
6052
|
+
registerWriteTool(pi, {
|
|
6053
|
+
onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath),
|
|
6054
|
+
name: writeName
|
|
6055
|
+
});
|
|
5811
6056
|
const activeReadSeekNames = READSEEK_TOOL_ENTRIES.map((entry) => entry.builtIn && replacedBuiltIns.has(entry.builtIn) ? entry.builtIn : entry.readSeekName);
|
|
5812
6057
|
const inactiveReadSeekNames = new Set(READSEEK_TOOL_ENTRIES.filter((entry) => entry.builtIn !== null && replacedBuiltIns.has(entry.builtIn)).map((entry) => entry.readSeekName));
|
|
5813
6058
|
const activateReadSeekTools = () => {
|
|
@@ -5827,14 +6072,16 @@ function piReadSeekExtension(pi) {
|
|
|
5827
6072
|
pi.on("before_agent_start", (event) => {
|
|
5828
6073
|
if (!activateReadSeekTools())
|
|
5829
6074
|
return;
|
|
5830
|
-
return {
|
|
6075
|
+
return {
|
|
6076
|
+
systemPrompt: `${event.systemPrompt}
|
|
5831
6077
|
|
|
5832
|
-
${
|
|
6078
|
+
${renderToolRoutingPolicy({ aliases: policyAliases, title: "ReadSeek tool policy:" })}`
|
|
6079
|
+
};
|
|
5833
6080
|
});
|
|
5834
6081
|
pi.on("session_start", (_event, ctx) => {
|
|
5835
6082
|
sessionAnchors.clear();
|
|
5836
6083
|
const { warnings } = resolveReadSeekJsonSettings();
|
|
5837
|
-
const problems = warnings.map(
|
|
6084
|
+
const problems = warnings.map((warning) => `${warning.message} (${warning.source})`);
|
|
5838
6085
|
const availability = readSeekBinaryAvailability();
|
|
5839
6086
|
if (!availability.available) {
|
|
5840
6087
|
problems.push(`readseek installation is broken: ${availability.reason}`);
|