pi-readseek 0.9.3 → 0.9.5
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/dist/index.ts +1059 -383
- package/package.json +2 -2
- package/prompts/def.md +1 -1
- package/prompts/refs.md +4 -4
- package/prompts/search.md +1 -1
package/dist/index.ts
CHANGED
|
@@ -2,15 +2,800 @@
|
|
|
2
2
|
import { readFile as fsReadFile } from "node:fs/promises";
|
|
3
3
|
import {
|
|
4
4
|
truncateHead as truncateHead2,
|
|
5
|
-
DEFAULT_MAX_BYTES as
|
|
6
|
-
DEFAULT_MAX_LINES as
|
|
5
|
+
DEFAULT_MAX_BYTES as DEFAULT_MAX_BYTES2,
|
|
6
|
+
DEFAULT_MAX_LINES as DEFAULT_MAX_LINES2
|
|
7
7
|
} from "@earendil-works/pi-coding-agent";
|
|
8
8
|
import { Type as Type2 } from "@sinclair/typebox";
|
|
9
9
|
import { Text as Text2 } from "@earendil-works/pi-tui";
|
|
10
10
|
|
|
11
|
+
// ../readseek-api/src/contract.ts
|
|
12
|
+
var DIGEST_FACETS = [
|
|
13
|
+
"metadata",
|
|
14
|
+
"content",
|
|
15
|
+
"map",
|
|
16
|
+
"diagnostics",
|
|
17
|
+
"identity"
|
|
18
|
+
];
|
|
19
|
+
var VISION_MODES = ["none", "all", "ocr", "caption", "objects"];
|
|
20
|
+
var VISION_ANALYSIS_MODES = ["all", "ocr", "caption", "objects"];
|
|
21
|
+
var VISION_LEVELS = ["low", "medium", "high"];
|
|
22
|
+
var DOCUMENT_NODE_KINDS = [
|
|
23
|
+
"artifact",
|
|
24
|
+
"footer",
|
|
25
|
+
"header",
|
|
26
|
+
"heading",
|
|
27
|
+
"marginal_label",
|
|
28
|
+
"page",
|
|
29
|
+
"page_number",
|
|
30
|
+
"paragraph",
|
|
31
|
+
"section",
|
|
32
|
+
"structural_section"
|
|
33
|
+
];
|
|
34
|
+
var TOOL_NAMES = [
|
|
35
|
+
"read",
|
|
36
|
+
"edit",
|
|
37
|
+
"write",
|
|
38
|
+
"grep",
|
|
39
|
+
"digest",
|
|
40
|
+
"view",
|
|
41
|
+
"search",
|
|
42
|
+
"def",
|
|
43
|
+
"refs",
|
|
44
|
+
"rename"
|
|
45
|
+
];
|
|
46
|
+
var PARAM_DESCRIPTIONS = {
|
|
47
|
+
path: "Absolute or relative file path",
|
|
48
|
+
searchPath: "File or directory to search; defaults to the current directory",
|
|
49
|
+
language: "Language override when auto-detection is ambiguous",
|
|
50
|
+
languageOverride: "Language override",
|
|
51
|
+
languageSymbol: "Language override for symbol replacement",
|
|
52
|
+
pattern: "AST pattern (ast-grep style), such as console.log($$$ARGS)",
|
|
53
|
+
grepPattern: "Regex pattern; set literal for exact text",
|
|
54
|
+
name: "Qualified or unqualified symbol name",
|
|
55
|
+
refsName: "Identifier to find references for",
|
|
56
|
+
scope: "Restrict results to the binding at the cursor",
|
|
57
|
+
line: "One-based cursor line; required with scope",
|
|
58
|
+
renameLine: "One-based cursor line of the symbol to rename",
|
|
59
|
+
column: "One-based cursor byte column for disambiguation",
|
|
60
|
+
cached: "In a Git repository, search tracked/indexed files",
|
|
61
|
+
others: "In a Git repository, search untracked files",
|
|
62
|
+
ignored: "With others=true, include ignored untracked files",
|
|
63
|
+
select: "Facets: metadata, content, map, diagnostics, identity (default content)",
|
|
64
|
+
selectComma: "Comma-separated facets: metadata, content, map, diagnostics, identity",
|
|
65
|
+
at: "Location: line:N[:COLUMN], hash:HASH[:COLUMN], or symbol:NAME",
|
|
66
|
+
end: "Last line to include",
|
|
67
|
+
limit: "Maximum number of lines to include",
|
|
68
|
+
offset: "One-based starting line",
|
|
69
|
+
readLimit: "Maximum lines to return",
|
|
70
|
+
symbolRead: "Symbol name to read",
|
|
71
|
+
bundle: "Include related symbols from the same file; requires symbol",
|
|
72
|
+
image: "Standalone image mode: none, all, ocr, caption, objects. Required for standalone images.",
|
|
73
|
+
visionModeDigest: "Standalone image mode: none, all, caption, objects, or ocr",
|
|
74
|
+
visionModeView: "Analyze selected assets: all, ocr, caption, or objects",
|
|
75
|
+
visionLevel: "Vision inference level: low (default), medium, or high",
|
|
76
|
+
node: "Node ID to use as the view root",
|
|
77
|
+
page: "One-based source page",
|
|
78
|
+
kind: "Node kind filter",
|
|
79
|
+
depth: "Maximum depth below selected roots",
|
|
80
|
+
outline: "Return outline nodes only",
|
|
81
|
+
edits: "Use set_line, replace_lines, insert_after, replace_symbol, or replace",
|
|
82
|
+
editVariant: "Exactly one nested edit variant",
|
|
83
|
+
setLine: 'Replace one line: {"set_line":{"anchor":"LINE:HASH","new_text":"..."}}',
|
|
84
|
+
replaceLines: 'Replace a range: {"replace_lines":{"start_anchor":"LINE:HASH","end_anchor":"LINE:HASH","new_text":"..."}}',
|
|
85
|
+
insertAfter: 'Insert text: {"insert_after":{"anchor":"LINE:HASH","new_text":"..."}}',
|
|
86
|
+
replace: 'Replace text: {"replace":{"old_text":"...","new_text":"..."}}',
|
|
87
|
+
replaceSymbol: 'Replace a symbol: {"replace_symbol":{"symbol":"name","new_body":"..."}}',
|
|
88
|
+
anchor: "Fresh LINE:HASH anchor for the line to replace",
|
|
89
|
+
startAnchor: "Fresh LINE:HASH anchor for the first line in the range",
|
|
90
|
+
endAnchor: "Fresh LINE:HASH anchor for the last line in the range",
|
|
91
|
+
insertAnchor: "Fresh LINE:HASH anchor for the line after which to insert",
|
|
92
|
+
newTextLine: "Replacement text; use an empty string to delete the line",
|
|
93
|
+
newTextRange: "Replacement text; use an empty string to delete the range",
|
|
94
|
+
newTextInsert: "Text to insert after the anchored line",
|
|
95
|
+
oldText: "Exact text to find",
|
|
96
|
+
newText: "Replacement text",
|
|
97
|
+
all: "Replace every exact match",
|
|
98
|
+
symbol: "Mapped symbol name",
|
|
99
|
+
newBody: "Complete replacement body for the symbol",
|
|
100
|
+
apply: "When supported by the host, apply the verified plan instead of previewing only",
|
|
101
|
+
planHash: "Require the apply plan to match this dry-run plan hash",
|
|
102
|
+
workspace: "Rename across the project",
|
|
103
|
+
to: "New symbol name",
|
|
104
|
+
postEditVerify: "Read back and verify persisted content",
|
|
105
|
+
content: "Complete text file content",
|
|
106
|
+
glob: "File-name glob, such as *.ts",
|
|
107
|
+
ignoreCase: "Ignore case",
|
|
108
|
+
literal: "Treat pattern literally",
|
|
109
|
+
context: "Surrounding lines for each match",
|
|
110
|
+
matchLimit: "Maximum matches to return",
|
|
111
|
+
summary: "Return per-file counts",
|
|
112
|
+
grepScope: "Group matches by enclosing symbol",
|
|
113
|
+
scopeContext: "Context lines within each symbol"
|
|
114
|
+
};
|
|
115
|
+
var TOOL_DESCRIPTIONS = {
|
|
116
|
+
read: "Read anchored text by range or symbol. Standalone images require an explicit mode. Use it before edits to obtain fresh `LINE:HASH` anchors; use `view` for documents and `digest` with the map facet for structural outlines.",
|
|
117
|
+
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":"..."}}`.',
|
|
118
|
+
write: "Create or replace a complete text file and return `LINE:HASH` anchors. Use `edit` for small changes to an existing file.",
|
|
119
|
+
grep: "Search text or regex in files and return edit-ready `LINE:HASH` anchors. Use for identifiers, strings, configuration, errors, comments, and documentation.",
|
|
120
|
+
digest: "Digest source content and images into the native CLI envelope. Returns metadata plus requested facets unchanged from `readseek digest`; use for structural maps, diagnostics, and cursor identity.",
|
|
121
|
+
view: "Digest a document's structure or selected content. Start with the overview, then narrow by page, node, kind, or depth instead of reading the whole document. PDF is the first supported document format.",
|
|
122
|
+
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.",
|
|
123
|
+
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.",
|
|
124
|
+
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.",
|
|
125
|
+
rename: "Rename a symbol at a source location. Resolvable same-named bindings are preserved; verified edits apply by default. Set `apply: false` to preview the plan before changing files."
|
|
126
|
+
};
|
|
127
|
+
function renderToolDescription(name, aliases = {}) {
|
|
128
|
+
return TOOL_NAMES.reduce((description, toolName) => description.replaceAll(`\`${toolName}\``, `\`${aliases[toolName] ?? toolName}\``), TOOL_DESCRIPTIONS[name]);
|
|
129
|
+
}
|
|
130
|
+
var gitFlagsSchema = {
|
|
131
|
+
cached: {
|
|
132
|
+
type: "boolean",
|
|
133
|
+
description: PARAM_DESCRIPTIONS.cached
|
|
134
|
+
},
|
|
135
|
+
others: {
|
|
136
|
+
type: "boolean",
|
|
137
|
+
description: PARAM_DESCRIPTIONS.others
|
|
138
|
+
},
|
|
139
|
+
ignored: {
|
|
140
|
+
type: "boolean",
|
|
141
|
+
description: PARAM_DESCRIPTIONS.ignored
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
var editItemSchema = {
|
|
145
|
+
type: "object",
|
|
146
|
+
additionalProperties: false,
|
|
147
|
+
minProperties: 1,
|
|
148
|
+
maxProperties: 1,
|
|
149
|
+
properties: {
|
|
150
|
+
set_line: {
|
|
151
|
+
type: "object",
|
|
152
|
+
additionalProperties: false,
|
|
153
|
+
required: ["anchor", "new_text"],
|
|
154
|
+
properties: {
|
|
155
|
+
anchor: { type: "string", description: PARAM_DESCRIPTIONS.anchor },
|
|
156
|
+
new_text: { type: "string", description: PARAM_DESCRIPTIONS.newTextLine }
|
|
157
|
+
},
|
|
158
|
+
description: PARAM_DESCRIPTIONS.setLine
|
|
159
|
+
},
|
|
160
|
+
replace_lines: {
|
|
161
|
+
type: "object",
|
|
162
|
+
additionalProperties: false,
|
|
163
|
+
required: ["start_anchor", "end_anchor", "new_text"],
|
|
164
|
+
properties: {
|
|
165
|
+
start_anchor: { type: "string", description: PARAM_DESCRIPTIONS.startAnchor },
|
|
166
|
+
end_anchor: { type: "string", description: PARAM_DESCRIPTIONS.endAnchor },
|
|
167
|
+
new_text: { type: "string", description: PARAM_DESCRIPTIONS.newTextRange }
|
|
168
|
+
},
|
|
169
|
+
description: PARAM_DESCRIPTIONS.replaceLines
|
|
170
|
+
},
|
|
171
|
+
insert_after: {
|
|
172
|
+
type: "object",
|
|
173
|
+
additionalProperties: false,
|
|
174
|
+
required: ["anchor", "new_text"],
|
|
175
|
+
properties: {
|
|
176
|
+
anchor: { type: "string", description: PARAM_DESCRIPTIONS.insertAnchor },
|
|
177
|
+
new_text: { type: "string", description: PARAM_DESCRIPTIONS.newTextInsert }
|
|
178
|
+
},
|
|
179
|
+
description: PARAM_DESCRIPTIONS.insertAfter
|
|
180
|
+
},
|
|
181
|
+
replace: {
|
|
182
|
+
type: "object",
|
|
183
|
+
additionalProperties: false,
|
|
184
|
+
required: ["old_text", "new_text"],
|
|
185
|
+
properties: {
|
|
186
|
+
old_text: { type: "string", description: PARAM_DESCRIPTIONS.oldText },
|
|
187
|
+
new_text: { type: "string", description: PARAM_DESCRIPTIONS.newText },
|
|
188
|
+
all: { type: "boolean", description: PARAM_DESCRIPTIONS.all }
|
|
189
|
+
},
|
|
190
|
+
description: PARAM_DESCRIPTIONS.replace
|
|
191
|
+
},
|
|
192
|
+
replace_symbol: {
|
|
193
|
+
type: "object",
|
|
194
|
+
additionalProperties: false,
|
|
195
|
+
required: ["symbol", "new_body"],
|
|
196
|
+
properties: {
|
|
197
|
+
symbol: { type: "string", description: PARAM_DESCRIPTIONS.symbol },
|
|
198
|
+
new_body: { type: "string", description: PARAM_DESCRIPTIONS.newBody }
|
|
199
|
+
},
|
|
200
|
+
description: PARAM_DESCRIPTIONS.replaceSymbol
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
description: PARAM_DESCRIPTIONS.editVariant
|
|
204
|
+
};
|
|
205
|
+
var TOOL_CONTRACTS = [
|
|
206
|
+
{
|
|
207
|
+
name: "read",
|
|
208
|
+
title: "Read anchored text",
|
|
209
|
+
description: TOOL_DESCRIPTIONS.read,
|
|
210
|
+
inputSchema: {
|
|
211
|
+
type: "object",
|
|
212
|
+
properties: {
|
|
213
|
+
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
214
|
+
offset: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.offset },
|
|
215
|
+
limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.readLimit },
|
|
216
|
+
symbol: { type: "string", description: PARAM_DESCRIPTIONS.symbolRead },
|
|
217
|
+
bundle: {
|
|
218
|
+
type: "string",
|
|
219
|
+
enum: ["local"],
|
|
220
|
+
description: PARAM_DESCRIPTIONS.bundle
|
|
221
|
+
},
|
|
222
|
+
image: {
|
|
223
|
+
type: "string",
|
|
224
|
+
enum: [...VISION_MODES],
|
|
225
|
+
description: PARAM_DESCRIPTIONS.image
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
required: ["path"],
|
|
229
|
+
additionalProperties: false
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: "edit",
|
|
234
|
+
title: "Hash-verified edit",
|
|
235
|
+
description: TOOL_DESCRIPTIONS.edit,
|
|
236
|
+
inputSchema: {
|
|
237
|
+
type: "object",
|
|
238
|
+
properties: {
|
|
239
|
+
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
240
|
+
edits: {
|
|
241
|
+
type: "array",
|
|
242
|
+
minItems: 1,
|
|
243
|
+
description: PARAM_DESCRIPTIONS.edits,
|
|
244
|
+
items: editItemSchema
|
|
245
|
+
},
|
|
246
|
+
language: { type: "string", description: PARAM_DESCRIPTIONS.languageSymbol },
|
|
247
|
+
apply: { type: "boolean", description: PARAM_DESCRIPTIONS.apply },
|
|
248
|
+
planHash: { type: "string", description: PARAM_DESCRIPTIONS.planHash },
|
|
249
|
+
postEditVerify: { type: "boolean", description: PARAM_DESCRIPTIONS.postEditVerify }
|
|
250
|
+
},
|
|
251
|
+
required: ["path", "edits"],
|
|
252
|
+
additionalProperties: false
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
name: "write",
|
|
257
|
+
title: "Write file",
|
|
258
|
+
description: TOOL_DESCRIPTIONS.write,
|
|
259
|
+
inputSchema: {
|
|
260
|
+
type: "object",
|
|
261
|
+
properties: {
|
|
262
|
+
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
263
|
+
content: { type: "string", description: PARAM_DESCRIPTIONS.content }
|
|
264
|
+
},
|
|
265
|
+
required: ["path", "content"],
|
|
266
|
+
additionalProperties: false
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "grep",
|
|
271
|
+
title: "Text search",
|
|
272
|
+
description: TOOL_DESCRIPTIONS.grep,
|
|
273
|
+
inputSchema: {
|
|
274
|
+
type: "object",
|
|
275
|
+
properties: {
|
|
276
|
+
pattern: { type: "string", description: PARAM_DESCRIPTIONS.grepPattern },
|
|
277
|
+
path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
|
|
278
|
+
glob: { type: "string", description: PARAM_DESCRIPTIONS.glob },
|
|
279
|
+
ignoreCase: { type: "boolean", description: PARAM_DESCRIPTIONS.ignoreCase },
|
|
280
|
+
literal: { type: "boolean", description: PARAM_DESCRIPTIONS.literal },
|
|
281
|
+
context: { type: "integer", minimum: 0, description: PARAM_DESCRIPTIONS.context },
|
|
282
|
+
limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.matchLimit },
|
|
283
|
+
summary: { type: "boolean", description: PARAM_DESCRIPTIONS.summary },
|
|
284
|
+
scope: {
|
|
285
|
+
type: "string",
|
|
286
|
+
enum: ["symbol"],
|
|
287
|
+
description: PARAM_DESCRIPTIONS.grepScope
|
|
288
|
+
},
|
|
289
|
+
scopeContext: {
|
|
290
|
+
type: "integer",
|
|
291
|
+
minimum: 0,
|
|
292
|
+
description: PARAM_DESCRIPTIONS.scopeContext
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
required: ["pattern"],
|
|
296
|
+
additionalProperties: false
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: "digest",
|
|
301
|
+
title: "Digest source",
|
|
302
|
+
description: TOOL_DESCRIPTIONS.digest,
|
|
303
|
+
inputSchema: {
|
|
304
|
+
type: "object",
|
|
305
|
+
properties: {
|
|
306
|
+
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
307
|
+
select: { type: "string", description: PARAM_DESCRIPTIONS.select },
|
|
308
|
+
at: { type: "string", description: PARAM_DESCRIPTIONS.at },
|
|
309
|
+
end: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.end },
|
|
310
|
+
limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.limit },
|
|
311
|
+
language: { type: "string", description: PARAM_DESCRIPTIONS.languageOverride },
|
|
312
|
+
visionMode: {
|
|
313
|
+
type: "string",
|
|
314
|
+
enum: [...VISION_MODES],
|
|
315
|
+
description: PARAM_DESCRIPTIONS.visionModeDigest
|
|
316
|
+
},
|
|
317
|
+
visionLevel: {
|
|
318
|
+
type: "string",
|
|
319
|
+
enum: [...VISION_LEVELS],
|
|
320
|
+
description: PARAM_DESCRIPTIONS.visionLevel
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
required: ["path"],
|
|
324
|
+
additionalProperties: false
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
name: "view",
|
|
329
|
+
title: "View document",
|
|
330
|
+
description: TOOL_DESCRIPTIONS.view,
|
|
331
|
+
inputSchema: {
|
|
332
|
+
type: "object",
|
|
333
|
+
properties: {
|
|
334
|
+
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
335
|
+
node: { type: "string", description: PARAM_DESCRIPTIONS.node },
|
|
336
|
+
page: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.page },
|
|
337
|
+
kind: {
|
|
338
|
+
type: "string",
|
|
339
|
+
enum: [...DOCUMENT_NODE_KINDS],
|
|
340
|
+
description: PARAM_DESCRIPTIONS.kind
|
|
341
|
+
},
|
|
342
|
+
depth: { type: "integer", minimum: 0, description: PARAM_DESCRIPTIONS.depth },
|
|
343
|
+
outline: { type: "boolean", description: PARAM_DESCRIPTIONS.outline },
|
|
344
|
+
visionMode: {
|
|
345
|
+
type: "string",
|
|
346
|
+
enum: [...VISION_ANALYSIS_MODES],
|
|
347
|
+
description: PARAM_DESCRIPTIONS.visionModeView
|
|
348
|
+
},
|
|
349
|
+
visionLevel: {
|
|
350
|
+
type: "string",
|
|
351
|
+
enum: [...VISION_LEVELS],
|
|
352
|
+
description: PARAM_DESCRIPTIONS.visionLevel
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
required: ["path"],
|
|
356
|
+
additionalProperties: false
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
name: "search",
|
|
361
|
+
title: "AST search",
|
|
362
|
+
description: TOOL_DESCRIPTIONS.search,
|
|
363
|
+
inputSchema: {
|
|
364
|
+
type: "object",
|
|
365
|
+
properties: {
|
|
366
|
+
pattern: { type: "string", description: PARAM_DESCRIPTIONS.pattern },
|
|
367
|
+
path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
|
|
368
|
+
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
369
|
+
...gitFlagsSchema
|
|
370
|
+
},
|
|
371
|
+
required: ["pattern"],
|
|
372
|
+
additionalProperties: false
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
name: "def",
|
|
377
|
+
title: "Find definition",
|
|
378
|
+
description: TOOL_DESCRIPTIONS.def,
|
|
379
|
+
inputSchema: {
|
|
380
|
+
type: "object",
|
|
381
|
+
properties: {
|
|
382
|
+
name: { type: "string", description: PARAM_DESCRIPTIONS.name },
|
|
383
|
+
path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
|
|
384
|
+
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
385
|
+
...gitFlagsSchema
|
|
386
|
+
},
|
|
387
|
+
required: ["name"],
|
|
388
|
+
additionalProperties: false
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
name: "refs",
|
|
393
|
+
title: "Find references",
|
|
394
|
+
description: TOOL_DESCRIPTIONS.refs,
|
|
395
|
+
inputSchema: {
|
|
396
|
+
type: "object",
|
|
397
|
+
properties: {
|
|
398
|
+
name: { type: "string", description: PARAM_DESCRIPTIONS.refsName },
|
|
399
|
+
path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
|
|
400
|
+
language: { type: "string", description: PARAM_DESCRIPTIONS.language },
|
|
401
|
+
scope: { type: "boolean", description: PARAM_DESCRIPTIONS.scope },
|
|
402
|
+
line: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.line },
|
|
403
|
+
column: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.column },
|
|
404
|
+
...gitFlagsSchema
|
|
405
|
+
},
|
|
406
|
+
additionalProperties: false
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
name: "rename",
|
|
411
|
+
title: "Rename symbol",
|
|
412
|
+
description: TOOL_DESCRIPTIONS.rename,
|
|
413
|
+
inputSchema: {
|
|
414
|
+
type: "object",
|
|
415
|
+
properties: {
|
|
416
|
+
path: { type: "string", description: PARAM_DESCRIPTIONS.path },
|
|
417
|
+
line: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.renameLine },
|
|
418
|
+
column: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.column },
|
|
419
|
+
to: { type: "string", description: PARAM_DESCRIPTIONS.to },
|
|
420
|
+
workspace: { type: "boolean", description: PARAM_DESCRIPTIONS.workspace },
|
|
421
|
+
apply: { type: "boolean", description: PARAM_DESCRIPTIONS.apply },
|
|
422
|
+
planHash: { type: "string", description: PARAM_DESCRIPTIONS.planHash }
|
|
423
|
+
},
|
|
424
|
+
required: ["path", "line", "to"],
|
|
425
|
+
additionalProperties: false
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
];
|
|
429
|
+
// ../readseek-api/src/validation.ts
|
|
430
|
+
function validateGitSelection(input) {
|
|
431
|
+
if (input.ignored && !input.others) {
|
|
432
|
+
return { ok: false, error: "ignored requires others=true" };
|
|
433
|
+
}
|
|
434
|
+
return { ok: true };
|
|
435
|
+
}
|
|
436
|
+
function validateRefsNameVsScope(input) {
|
|
437
|
+
if (input.scope) {
|
|
438
|
+
if (input.line === undefined) {
|
|
439
|
+
return { ok: false, error: "scope requires line" };
|
|
440
|
+
}
|
|
441
|
+
return { ok: true };
|
|
442
|
+
}
|
|
443
|
+
if (input.line !== undefined || input.column !== undefined) {
|
|
444
|
+
return { ok: false, error: "line/column require scope=true" };
|
|
445
|
+
}
|
|
446
|
+
if (typeof input.name !== "string" || input.name.length === 0) {
|
|
447
|
+
return { ok: false, error: "name is required when scope is not set" };
|
|
448
|
+
}
|
|
449
|
+
return { ok: true };
|
|
450
|
+
}
|
|
451
|
+
function validateVisionLevelVsMode(input) {
|
|
452
|
+
if (input.outline && (input.visionMode !== undefined || input.visionLevel !== undefined)) {
|
|
453
|
+
return { ok: false, error: "Cannot combine outline with visionMode or visionLevel" };
|
|
454
|
+
}
|
|
455
|
+
if (input.visionLevel !== undefined && input.visionMode === undefined) {
|
|
456
|
+
return { ok: false, error: "visionLevel requires visionMode" };
|
|
457
|
+
}
|
|
458
|
+
return { ok: true };
|
|
459
|
+
}
|
|
460
|
+
// ../readseek-api/src/argv.ts
|
|
461
|
+
function buildLineLocation(line, column) {
|
|
462
|
+
return column === undefined ? `line:${line}` : `line:${line}:${column}`;
|
|
463
|
+
}
|
|
464
|
+
function appendGitSelectionArgs(args, input) {
|
|
465
|
+
const validated = validateGitSelection(input);
|
|
466
|
+
if (!validated.ok) {
|
|
467
|
+
throw new Error(validated.error);
|
|
468
|
+
}
|
|
469
|
+
const selection = [
|
|
470
|
+
input.cached ? "cached" : undefined,
|
|
471
|
+
input.others ? "others" : undefined,
|
|
472
|
+
input.ignored ? "ignored" : undefined
|
|
473
|
+
].filter((value) => value !== undefined);
|
|
474
|
+
if (selection.length > 0) {
|
|
475
|
+
args.push("--git", selection.join(","));
|
|
476
|
+
}
|
|
477
|
+
return args;
|
|
478
|
+
}
|
|
479
|
+
function pushOptionalString(args, flag, value) {
|
|
480
|
+
if (value !== undefined)
|
|
481
|
+
args.push(flag, value);
|
|
482
|
+
}
|
|
483
|
+
function pushOptionalNumber(args, flag, value) {
|
|
484
|
+
if (value !== undefined)
|
|
485
|
+
args.push(flag, String(value));
|
|
486
|
+
}
|
|
487
|
+
function appendApplyAndPlanHash(args, input) {
|
|
488
|
+
const order = input.flagOrder ?? "apply-first";
|
|
489
|
+
if (order === "plan-hash-first") {
|
|
490
|
+
pushOptionalString(args, "--plan-hash", input.planHash);
|
|
491
|
+
if (input.apply)
|
|
492
|
+
args.push("--apply");
|
|
493
|
+
return args;
|
|
494
|
+
}
|
|
495
|
+
if (input.apply)
|
|
496
|
+
args.push("--apply");
|
|
497
|
+
pushOptionalString(args, "--plan-hash", input.planHash);
|
|
498
|
+
return args;
|
|
499
|
+
}
|
|
500
|
+
function selectArg(select) {
|
|
501
|
+
if (select === undefined)
|
|
502
|
+
return;
|
|
503
|
+
return Array.isArray(select) ? select.join(",") : select;
|
|
504
|
+
}
|
|
505
|
+
function buildDigestArgs(input) {
|
|
506
|
+
const vision = validateVisionLevelVsMode({
|
|
507
|
+
visionMode: input.visionMode,
|
|
508
|
+
visionLevel: input.visionLevel
|
|
509
|
+
});
|
|
510
|
+
if (!vision.ok)
|
|
511
|
+
throw new Error(vision.error);
|
|
512
|
+
const args = [];
|
|
513
|
+
if (input.readseekDir !== undefined)
|
|
514
|
+
args.push("--readseek-dir", input.readseekDir);
|
|
515
|
+
args.push("digest", input.path);
|
|
516
|
+
const select = selectArg(input.select);
|
|
517
|
+
if (select !== undefined)
|
|
518
|
+
args.push("--select", select);
|
|
519
|
+
pushOptionalString(args, "--at", input.at);
|
|
520
|
+
pushOptionalNumber(args, "--end", input.end);
|
|
521
|
+
pushOptionalNumber(args, "--limit", input.limit);
|
|
522
|
+
pushOptionalString(args, "--language", input.language);
|
|
523
|
+
pushOptionalString(args, "--vision-mode", input.visionMode);
|
|
524
|
+
pushOptionalString(args, "--vision-level", input.visionLevel);
|
|
525
|
+
pushOptionalString(args, "--stdin-name", input.stdinName);
|
|
526
|
+
return args;
|
|
527
|
+
}
|
|
528
|
+
function buildViewArgs(input) {
|
|
529
|
+
const vision = validateVisionLevelVsMode({
|
|
530
|
+
visionMode: input.visionMode,
|
|
531
|
+
visionLevel: input.visionLevel,
|
|
532
|
+
outline: input.outline
|
|
533
|
+
});
|
|
534
|
+
if (!vision.ok)
|
|
535
|
+
throw new Error(vision.error);
|
|
536
|
+
const args = [];
|
|
537
|
+
if (input.readseekDir !== undefined)
|
|
538
|
+
args.push("--readseek-dir", input.readseekDir);
|
|
539
|
+
args.push("view", input.path);
|
|
540
|
+
if (input.format !== undefined)
|
|
541
|
+
args.push("--format", input.format);
|
|
542
|
+
pushOptionalString(args, "--node", input.node);
|
|
543
|
+
pushOptionalNumber(args, "--page", input.page);
|
|
544
|
+
pushOptionalString(args, "--kind", input.kind);
|
|
545
|
+
pushOptionalNumber(args, "--depth", input.depth);
|
|
546
|
+
if (input.outline)
|
|
547
|
+
args.push("--outline");
|
|
548
|
+
pushOptionalString(args, "--vision-mode", input.visionMode);
|
|
549
|
+
pushOptionalString(args, "--vision-level", input.visionLevel);
|
|
550
|
+
return args;
|
|
551
|
+
}
|
|
552
|
+
function buildSearchArgs(input) {
|
|
553
|
+
const args = ["search", input.path, input.pattern];
|
|
554
|
+
pushOptionalString(args, "--language", input.language);
|
|
555
|
+
return appendGitSelectionArgs(args, input);
|
|
556
|
+
}
|
|
557
|
+
function buildDefArgs(input) {
|
|
558
|
+
const args = ["def", input.path];
|
|
559
|
+
if (input.format !== undefined)
|
|
560
|
+
args.push("--format", input.format);
|
|
561
|
+
args.push(input.name);
|
|
562
|
+
pushOptionalString(args, "--language", input.language);
|
|
563
|
+
return appendGitSelectionArgs(args, input);
|
|
564
|
+
}
|
|
565
|
+
function buildRefsArgs(input) {
|
|
566
|
+
const validated = validateRefsNameVsScope(input);
|
|
567
|
+
if (!validated.ok)
|
|
568
|
+
throw new Error(validated.error);
|
|
569
|
+
const args = ["refs", input.path];
|
|
570
|
+
if (input.scope) {
|
|
571
|
+
args.push("--at", buildLineLocation(input.line, input.column));
|
|
572
|
+
} else if (input.name !== undefined) {
|
|
573
|
+
args.push(input.name);
|
|
574
|
+
}
|
|
575
|
+
if (input.format !== undefined)
|
|
576
|
+
args.push("--format", input.format);
|
|
577
|
+
pushOptionalString(args, "--language", input.language);
|
|
578
|
+
return appendGitSelectionArgs(args, input);
|
|
579
|
+
}
|
|
580
|
+
function buildEditArgs(input) {
|
|
581
|
+
const args = ["edit", input.path];
|
|
582
|
+
appendApplyAndPlanHash(args, {
|
|
583
|
+
apply: input.apply,
|
|
584
|
+
planHash: input.planHash,
|
|
585
|
+
flagOrder: input.flagOrder ?? "apply-first"
|
|
586
|
+
});
|
|
587
|
+
pushOptionalString(args, "--language", input.language);
|
|
588
|
+
if (input.requestFromStdin)
|
|
589
|
+
args.push("--request", "-");
|
|
590
|
+
return args;
|
|
591
|
+
}
|
|
592
|
+
function buildRenameArgs(input) {
|
|
593
|
+
const args = ["rename", input.path, "--at", buildLineLocation(input.line, input.column), "--to", input.to];
|
|
594
|
+
if (input.workspace !== undefined)
|
|
595
|
+
args.push("--workspace", input.workspace);
|
|
596
|
+
appendApplyAndPlanHash(args, {
|
|
597
|
+
apply: input.apply,
|
|
598
|
+
planHash: input.planHash,
|
|
599
|
+
flagOrder: input.flagOrder ?? "plan-hash-first"
|
|
600
|
+
});
|
|
601
|
+
pushOptionalString(args, "--language", input.language);
|
|
602
|
+
return appendGitSelectionArgs(args, input);
|
|
603
|
+
}
|
|
604
|
+
// ../readseek-api/src/platform.ts
|
|
605
|
+
import { createRequire } from "node:module";
|
|
606
|
+
import path from "node:path";
|
|
607
|
+
var READSEEK_PLATFORM_PACKAGES = {
|
|
608
|
+
"darwin-arm64": "@jarkkojs/readseek-darwin-arm64",
|
|
609
|
+
"linux-arm64": "@jarkkojs/readseek-linux-arm64",
|
|
610
|
+
"linux-x64": "@jarkkojs/readseek-linux-x64",
|
|
611
|
+
"win32-x64": "@jarkkojs/readseek-win32-x64"
|
|
612
|
+
};
|
|
613
|
+
var resolveFromPackage = createRequire(import.meta.url).resolve;
|
|
614
|
+
function readSeekBinaryName(platform = process.platform) {
|
|
615
|
+
return platform === "win32" ? "readseek.exe" : "readseek";
|
|
616
|
+
}
|
|
617
|
+
function readSeekPlatformKey(platform = process.platform, arch = process.arch) {
|
|
618
|
+
return `${platform}-${arch}`;
|
|
619
|
+
}
|
|
620
|
+
function resolveReadSeekBinaryFromPackages(resolver = resolveFromPackage, options = {}) {
|
|
621
|
+
const resolve = typeof resolver === "function" ? resolver : resolver.resolve.bind(resolver);
|
|
622
|
+
const platform = options.platform ?? process.platform;
|
|
623
|
+
const arch = options.arch ?? process.arch;
|
|
624
|
+
const key = readSeekPlatformKey(platform, arch);
|
|
625
|
+
const platformPackage = READSEEK_PLATFORM_PACKAGES[key];
|
|
626
|
+
if (!platformPackage) {
|
|
627
|
+
const supported = Object.keys(READSEEK_PLATFORM_PACKAGES).join(", ");
|
|
628
|
+
throw new Error(`@jarkkojs/readseek ships no binary for ${key}; it supports ${supported}`);
|
|
629
|
+
}
|
|
630
|
+
const metaPackage = options.metaPackage ?? "@jarkkojs/readseek";
|
|
631
|
+
const metaPackageJson = resolve(`${metaPackage}/package.json`);
|
|
632
|
+
const metaPackageDir = path.dirname(metaPackageJson);
|
|
633
|
+
const packageJsonPath = resolve(`${platformPackage}/package.json`, {
|
|
634
|
+
paths: [metaPackageDir]
|
|
635
|
+
});
|
|
636
|
+
return path.join(path.dirname(packageJsonPath), "bin", readSeekBinaryName(platform));
|
|
637
|
+
}
|
|
638
|
+
// ../readseek-api/src/json.ts
|
|
639
|
+
var READSEEK_USAGE_HINT = /\n\s*Run readseek(?: [\w-]+)* --help for more information\.?\s*$/;
|
|
640
|
+
function cleanReadSeekErrorText(stderr) {
|
|
641
|
+
return stderr.replace(READSEEK_USAGE_HINT, "").replace(/^error:\s*/i, "").replace(/\s*\n\s*/g, " ").trim();
|
|
642
|
+
}
|
|
643
|
+
function parseReadSeekJson(stdout) {
|
|
644
|
+
try {
|
|
645
|
+
return JSON.parse(stdout);
|
|
646
|
+
} catch {
|
|
647
|
+
const preview = stdout.trim().slice(0, 200);
|
|
648
|
+
throw new Error(`readseek returned invalid JSON: ${preview}`);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
// ../readseek-api/src/process.ts
|
|
652
|
+
var DEFAULT_READSEEK_TIMEOUT_MS = 120000;
|
|
653
|
+
var DEFAULT_MAX_OUTPUT_BYTES = 32 * 1024 * 1024;
|
|
654
|
+
function resolveProcessLimits(options = {}) {
|
|
655
|
+
return {
|
|
656
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_READSEEK_TIMEOUT_MS,
|
|
657
|
+
maxOutputBytes: options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES,
|
|
658
|
+
cancelable: options.cancelable !== false
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
function readSeekCancellationError(signal) {
|
|
662
|
+
const reason = signal?.reason;
|
|
663
|
+
return reason instanceof Error && reason.name !== "AbortError" ? reason : new Error("readseek cancelled");
|
|
664
|
+
}
|
|
665
|
+
function failureFromProcessResult(result, options = {}) {
|
|
666
|
+
if (options.overflow) {
|
|
667
|
+
return new Error(`readseek ${options.overflow} exceeded the ${options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES}-byte output limit`);
|
|
668
|
+
}
|
|
669
|
+
if (options.timedOut) {
|
|
670
|
+
return new Error(`readseek timed out after ${options.timeoutMs ?? DEFAULT_READSEEK_TIMEOUT_MS} ms`);
|
|
671
|
+
}
|
|
672
|
+
const cleaned = cleanReadSeekErrorText(result.stderr);
|
|
673
|
+
if (cleaned)
|
|
674
|
+
return new Error(cleaned);
|
|
675
|
+
if (result.signal)
|
|
676
|
+
return new Error(`readseek killed by signal ${result.signal}`);
|
|
677
|
+
return new Error(`readseek exited with status ${result.exitCode}`);
|
|
678
|
+
}
|
|
679
|
+
function ensureSuccessfulStdout(result) {
|
|
680
|
+
if (result.exitCode === 0)
|
|
681
|
+
return result.stdout;
|
|
682
|
+
throw failureFromProcessResult(result);
|
|
683
|
+
}
|
|
684
|
+
function parseSuccessfulJson(result) {
|
|
685
|
+
return parseReadSeekJson(ensureSuccessfulStdout(result));
|
|
686
|
+
}
|
|
687
|
+
// ../readseek-api/src/process-node.ts
|
|
688
|
+
import { spawn } from "node:child_process";
|
|
689
|
+
import { StringDecoder } from "node:string_decoder";
|
|
690
|
+
function attachBoundedStream(stream, maxOutputBytes, onOverflow) {
|
|
691
|
+
const decoder = new StringDecoder("utf8");
|
|
692
|
+
let text = "";
|
|
693
|
+
let bytes = 0;
|
|
694
|
+
let overflowed = false;
|
|
695
|
+
stream.on("data", (chunk) => {
|
|
696
|
+
if (overflowed)
|
|
697
|
+
return;
|
|
698
|
+
const buffer = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
699
|
+
bytes += buffer.length;
|
|
700
|
+
if (bytes > maxOutputBytes) {
|
|
701
|
+
overflowed = true;
|
|
702
|
+
onOverflow();
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
text += decoder.write(buffer);
|
|
706
|
+
});
|
|
707
|
+
return {
|
|
708
|
+
text: () => text + decoder.end()
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
async function runReadSeekNode(binaryPath, args, options = {}) {
|
|
712
|
+
const limits = resolveProcessLimits(options);
|
|
713
|
+
if (limits.cancelable && options.signal?.aborted) {
|
|
714
|
+
throw readSeekCancellationError(options.signal);
|
|
715
|
+
}
|
|
716
|
+
return await new Promise((resolve, reject) => {
|
|
717
|
+
let settled = false;
|
|
718
|
+
let child;
|
|
719
|
+
let timer;
|
|
720
|
+
const finish = (result) => {
|
|
721
|
+
if (settled)
|
|
722
|
+
return;
|
|
723
|
+
settled = true;
|
|
724
|
+
if (timer !== undefined)
|
|
725
|
+
clearTimeout(timer);
|
|
726
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
727
|
+
resolve(result);
|
|
728
|
+
};
|
|
729
|
+
const fail = (error) => {
|
|
730
|
+
if (settled)
|
|
731
|
+
return;
|
|
732
|
+
settled = true;
|
|
733
|
+
if (timer !== undefined)
|
|
734
|
+
clearTimeout(timer);
|
|
735
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
736
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
737
|
+
};
|
|
738
|
+
const terminate = (error) => {
|
|
739
|
+
if (settled)
|
|
740
|
+
return;
|
|
741
|
+
fail(error);
|
|
742
|
+
try {
|
|
743
|
+
child.kill("SIGKILL");
|
|
744
|
+
} catch {}
|
|
745
|
+
};
|
|
746
|
+
const onAbort = () => {
|
|
747
|
+
terminate(readSeekCancellationError(options.signal));
|
|
748
|
+
};
|
|
749
|
+
try {
|
|
750
|
+
child = spawn(binaryPath, [...args], {
|
|
751
|
+
cwd: options.cwd,
|
|
752
|
+
env: process.env,
|
|
753
|
+
shell: false,
|
|
754
|
+
stdio: [options.stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"],
|
|
755
|
+
windowsHide: true
|
|
756
|
+
});
|
|
757
|
+
} catch (error) {
|
|
758
|
+
fail(error);
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
const stdout = attachBoundedStream(child.stdout, limits.maxOutputBytes, () => {
|
|
762
|
+
terminate(failureFromProcessResult({ stdout: "", stderr: "", exitCode: null, signal: null }, { overflow: "stdout", maxOutputBytes: limits.maxOutputBytes }));
|
|
763
|
+
});
|
|
764
|
+
const stderr = attachBoundedStream(child.stderr, limits.maxOutputBytes, () => {
|
|
765
|
+
terminate(failureFromProcessResult({ stdout: "", stderr: "", exitCode: null, signal: null }, { overflow: "stderr", maxOutputBytes: limits.maxOutputBytes }));
|
|
766
|
+
});
|
|
767
|
+
timer = setTimeout(() => {
|
|
768
|
+
terminate(failureFromProcessResult({ stdout: "", stderr: "", exitCode: null, signal: null }, { timedOut: true, timeoutMs: limits.timeoutMs }));
|
|
769
|
+
}, limits.timeoutMs);
|
|
770
|
+
timer.unref?.();
|
|
771
|
+
if (limits.cancelable && options.signal) {
|
|
772
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
773
|
+
}
|
|
774
|
+
if (options.stdin !== undefined) {
|
|
775
|
+
child.stdin.on("error", (error) => {
|
|
776
|
+
if (error.code !== "EPIPE")
|
|
777
|
+
fail(error);
|
|
778
|
+
});
|
|
779
|
+
child.stdin.end(options.stdin, "utf8");
|
|
780
|
+
}
|
|
781
|
+
child.on("error", fail);
|
|
782
|
+
child.on("close", (code, signal) => {
|
|
783
|
+
finish({
|
|
784
|
+
stdout: stdout.text(),
|
|
785
|
+
stderr: stderr.text(),
|
|
786
|
+
exitCode: code,
|
|
787
|
+
signal
|
|
788
|
+
});
|
|
789
|
+
});
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
async function runReadSeekNodeText(binaryPath, args, options = {}) {
|
|
793
|
+
return ensureSuccessfulStdout(await runReadSeekNode(binaryPath, args, options));
|
|
794
|
+
}
|
|
795
|
+
async function runReadSeekNodeJson(binaryPath, args, options = {}) {
|
|
796
|
+
return parseSuccessfulJson(await runReadSeekNode(binaryPath, args, options));
|
|
797
|
+
}
|
|
11
798
|
// src/tool-prompt-metadata.ts
|
|
12
|
-
import { readFileSync } from "node:fs";
|
|
13
|
-
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize } from "@earendil-works/pi-coding-agent";
|
|
14
799
|
var REPLACEABLE_TOOL_GUIDELINES = {
|
|
15
800
|
"read.md": {
|
|
16
801
|
readSeekName: "readSeek_read",
|
|
@@ -56,11 +841,11 @@ var COMPACT_GUIDELINES = {
|
|
|
56
841
|
"Use readSeek_refs before changing a symbol; add scope plus line/column to follow one binding."
|
|
57
842
|
]
|
|
58
843
|
};
|
|
59
|
-
function
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return
|
|
844
|
+
function toolNameFromPrompt(fileName) {
|
|
845
|
+
const name = fileName.replace(/\.md$/, "");
|
|
846
|
+
if (!TOOL_NAMES.includes(name))
|
|
847
|
+
throw new Error(`unknown ReadSeek tool prompt: ${fileName}`);
|
|
848
|
+
return name;
|
|
64
849
|
}
|
|
65
850
|
function promptFileName(promptUrl) {
|
|
66
851
|
return promptUrl.pathname.split("/").pop() ?? "";
|
|
@@ -71,13 +856,18 @@ function rewriteToolAliases(value, toolAliases) {
|
|
|
71
856
|
return Object.entries(toolAliases).reduce((rewritten, [canonicalName, registeredName]) => rewritten.replaceAll(canonicalName, registeredName), value);
|
|
72
857
|
}
|
|
73
858
|
function defineToolPromptMetadata(options) {
|
|
74
|
-
const prompt = loadPrompt(options.promptUrl);
|
|
75
859
|
const fileName = promptFileName(options.promptUrl);
|
|
860
|
+
const toolName = toolNameFromPrompt(fileName);
|
|
76
861
|
const replaceable = REPLACEABLE_TOOL_GUIDELINES[fileName];
|
|
77
862
|
const registeredName = options.registeredName ?? replaceable?.readSeekName;
|
|
78
863
|
const preferenceGuideline = replaceable && registeredName ? registeredName === replaceable.readSeekName ? `Prefer ${registeredName} over ${replaceable.builtInName} when both are available; ${replaceable.benefit}` : `Use ${registeredName}; ${replaceable.benefit}` : undefined;
|
|
864
|
+
const aliases = Object.fromEntries(TOOL_NAMES.map((name) => {
|
|
865
|
+
const readSeekName = `readSeek_${name}`;
|
|
866
|
+
const alias = name === toolName && registeredName ? registeredName : options.toolAliases?.[readSeekName] ?? readSeekName;
|
|
867
|
+
return [name, alias];
|
|
868
|
+
}));
|
|
79
869
|
return {
|
|
80
|
-
description:
|
|
870
|
+
description: renderToolDescription(toolName, aliases),
|
|
81
871
|
promptSnippet: rewriteToolAliases(options.promptSnippet, options.toolAliases),
|
|
82
872
|
promptGuidelines: [
|
|
83
873
|
...preferenceGuideline ? [preferenceGuideline] : [],
|
|
@@ -409,11 +1199,10 @@ function formatFsError(err, domain) {
|
|
|
409
1199
|
import { stat } from "node:fs/promises";
|
|
410
1200
|
|
|
411
1201
|
// src/readseek-client.ts
|
|
412
|
-
import {
|
|
413
|
-
import {
|
|
414
|
-
import { createRequire } from "node:module";
|
|
1202
|
+
import { existsSync, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
1203
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
415
1204
|
import { homedir as homedir3 } from "node:os";
|
|
416
|
-
import
|
|
1205
|
+
import path2 from "node:path";
|
|
417
1206
|
|
|
418
1207
|
// src/readseek/enums.ts
|
|
419
1208
|
var SymbolKind;
|
|
@@ -443,7 +1232,7 @@ var SymbolKind;
|
|
|
443
1232
|
})(SymbolKind ||= {});
|
|
444
1233
|
|
|
445
1234
|
// src/readseek-settings.ts
|
|
446
|
-
import { readFileSync
|
|
1235
|
+
import { readFileSync, statSync } from "node:fs";
|
|
447
1236
|
import { homedir as homedir2 } from "node:os";
|
|
448
1237
|
import { join } from "node:path";
|
|
449
1238
|
var READSEEK_TOOL_REPLACEMENTS = {
|
|
@@ -470,33 +1259,33 @@ function projectSettingsPath() {
|
|
|
470
1259
|
function isRecord(value) {
|
|
471
1260
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
472
1261
|
}
|
|
473
|
-
function invalid(source,
|
|
474
|
-
return { source, path, message: `Invalid readseek setting at ${
|
|
1262
|
+
function invalid(source, path2) {
|
|
1263
|
+
return { source, path: path2, message: `Invalid readseek setting at ${path2}` };
|
|
475
1264
|
}
|
|
476
1265
|
function warnUnknownKeys(raw, known, prefix, source, warnings) {
|
|
477
1266
|
for (const key of Object.keys(raw)) {
|
|
478
1267
|
if (known.includes(key))
|
|
479
1268
|
continue;
|
|
480
|
-
const
|
|
481
|
-
warnings.push({ source, path, message: `Unknown readseek setting at ${
|
|
1269
|
+
const path2 = `${prefix}.${key}`;
|
|
1270
|
+
warnings.push({ source, path: path2, message: `Unknown readseek setting at ${path2}` });
|
|
482
1271
|
}
|
|
483
1272
|
}
|
|
484
|
-
function readPositive(raw, key,
|
|
1273
|
+
function readPositive(raw, key, path2, source, warnings) {
|
|
485
1274
|
if (!(key in raw))
|
|
486
1275
|
return;
|
|
487
1276
|
const val = raw[key];
|
|
488
1277
|
if (typeof val === "number" && Number.isSafeInteger(val) && val > 0)
|
|
489
1278
|
return val;
|
|
490
|
-
warnings.push(invalid(source,
|
|
1279
|
+
warnings.push(invalid(source, path2));
|
|
491
1280
|
return;
|
|
492
1281
|
}
|
|
493
|
-
function readEnum(raw, key, values,
|
|
1282
|
+
function readEnum(raw, key, values, path2, source, warnings) {
|
|
494
1283
|
if (!(key in raw))
|
|
495
1284
|
return;
|
|
496
1285
|
const val = raw[key];
|
|
497
1286
|
if (typeof val === "string" && values.includes(val))
|
|
498
1287
|
return val;
|
|
499
|
-
warnings.push(invalid(source,
|
|
1288
|
+
warnings.push(invalid(source, path2));
|
|
500
1289
|
return;
|
|
501
1290
|
}
|
|
502
1291
|
function readImageMode(raw, source, warnings) {
|
|
@@ -600,31 +1389,31 @@ function validateSettings(raw, source) {
|
|
|
600
1389
|
return { settings, warnings };
|
|
601
1390
|
}
|
|
602
1391
|
var settingsFileCache = new Map;
|
|
603
|
-
function statFileOrNull(
|
|
1392
|
+
function statFileOrNull(path2) {
|
|
604
1393
|
try {
|
|
605
|
-
return statSync(
|
|
1394
|
+
return statSync(path2);
|
|
606
1395
|
} catch {
|
|
607
1396
|
return null;
|
|
608
1397
|
}
|
|
609
1398
|
}
|
|
610
|
-
function readSettingsFile(
|
|
611
|
-
const stats = statFileOrNull(
|
|
1399
|
+
function readSettingsFile(path2) {
|
|
1400
|
+
const stats = statFileOrNull(path2);
|
|
612
1401
|
if (!stats) {
|
|
613
|
-
settingsFileCache.delete(
|
|
1402
|
+
settingsFileCache.delete(path2);
|
|
614
1403
|
return { settings: {}, warnings: [] };
|
|
615
1404
|
}
|
|
616
|
-
const cached = settingsFileCache.get(
|
|
1405
|
+
const cached = settingsFileCache.get(path2);
|
|
617
1406
|
if (cached && cached.mtimeMs === stats.mtimeMs && cached.size === stats.size)
|
|
618
1407
|
return cached.result;
|
|
619
1408
|
let result;
|
|
620
1409
|
try {
|
|
621
|
-
const text =
|
|
622
|
-
result = validateSettings(JSON.parse(text),
|
|
1410
|
+
const text = readFileSync(path2, "utf8");
|
|
1411
|
+
result = validateSettings(JSON.parse(text), path2);
|
|
623
1412
|
} catch (error) {
|
|
624
1413
|
const message = error instanceof Error ? error.message : String(error);
|
|
625
|
-
result = { settings: {}, warnings: [{ source:
|
|
1414
|
+
result = { settings: {}, warnings: [{ source: path2, message: `Invalid JSON: ${message}` }] };
|
|
626
1415
|
}
|
|
627
|
-
settingsFileCache.set(
|
|
1416
|
+
settingsFileCache.set(path2, { mtimeMs: stats.mtimeMs, size: stats.size, result });
|
|
628
1417
|
return result;
|
|
629
1418
|
}
|
|
630
1419
|
function mergeSettings(base, override) {
|
|
@@ -659,18 +1448,7 @@ function resolveReadSeekToolDisplayMode(tool) {
|
|
|
659
1448
|
}
|
|
660
1449
|
|
|
661
1450
|
// src/readseek-client.ts
|
|
662
|
-
|
|
663
|
-
const selection = [
|
|
664
|
-
options.cached && "cached",
|
|
665
|
-
options.others && "others",
|
|
666
|
-
options.ignored && "ignored"
|
|
667
|
-
].filter((value) => Boolean(value));
|
|
668
|
-
if (selection.length > 0)
|
|
669
|
-
args.push("--git", selection.join(","));
|
|
670
|
-
}
|
|
671
|
-
function lineLocationSelector(line, column) {
|
|
672
|
-
return column === undefined ? `line:${line}` : `line:${line}:${column}`;
|
|
673
|
-
}
|
|
1451
|
+
var resolveFromHost = createRequire2(import.meta.url).resolve;
|
|
674
1452
|
function normalizeLanguage(language) {
|
|
675
1453
|
return language === "java" ? "Java" : language;
|
|
676
1454
|
}
|
|
@@ -715,32 +1493,12 @@ function symbolsFromReadSeek(symbols) {
|
|
|
715
1493
|
}
|
|
716
1494
|
return roots;
|
|
717
1495
|
}
|
|
718
|
-
var require2 = createRequire(import.meta.url);
|
|
719
1496
|
var READSEEK_REPO_PACKAGE_NAMES = new Set(["@jarkkojs/readseek", "readseek"]);
|
|
720
1497
|
var defaultReadSeekDirInit;
|
|
721
|
-
function readSeekPackageDir() {
|
|
722
|
-
return path.dirname(require2.resolve("@jarkkojs/readseek/package.json"));
|
|
723
|
-
}
|
|
724
|
-
var READSEEK_PLATFORM_PACKAGES = {
|
|
725
|
-
"darwin-arm64": "@jarkkojs/readseek-darwin-arm64",
|
|
726
|
-
"linux-arm64": "@jarkkojs/readseek-linux-arm64",
|
|
727
|
-
"linux-x64": "@jarkkojs/readseek-linux-x64",
|
|
728
|
-
"win32-x64": "@jarkkojs/readseek-win32-x64"
|
|
729
|
-
};
|
|
730
|
-
function readSeekPlatform() {
|
|
731
|
-
return `${process.platform}-${process.arch}`;
|
|
732
|
-
}
|
|
733
1498
|
function readSeekBinaryPath() {
|
|
734
1499
|
if (process.env.READSEEK_BIN)
|
|
735
1500
|
return process.env.READSEEK_BIN;
|
|
736
|
-
|
|
737
|
-
const platformPackage = READSEEK_PLATFORM_PACKAGES[platform];
|
|
738
|
-
if (!platformPackage) {
|
|
739
|
-
const supported = Object.keys(READSEEK_PLATFORM_PACKAGES).join(", ");
|
|
740
|
-
throw new Error(`@jarkkojs/readseek ships no binary for ${platform}; it supports ${supported}`);
|
|
741
|
-
}
|
|
742
|
-
const packageJson = require2.resolve(`${platformPackage}/package.json`, { paths: [readSeekPackageDir()] });
|
|
743
|
-
return path.join(path.dirname(packageJson), "bin", process.platform === "win32" ? "readseek.exe" : "readseek");
|
|
1501
|
+
return resolveReadSeekBinaryFromPackages(resolveFromHost);
|
|
744
1502
|
}
|
|
745
1503
|
function readSeekBinaryAvailability() {
|
|
746
1504
|
try {
|
|
@@ -768,24 +1526,24 @@ function directoryExists(dirPath) {
|
|
|
768
1526
|
}
|
|
769
1527
|
var ownRepositoryByDir = new Map;
|
|
770
1528
|
function isOwnReadSeekRepository(cwd = process.cwd()) {
|
|
771
|
-
const start =
|
|
1529
|
+
const start = path2.resolve(cwd);
|
|
772
1530
|
const cached = ownRepositoryByDir.get(start);
|
|
773
1531
|
if (cached !== undefined)
|
|
774
1532
|
return cached;
|
|
775
1533
|
let dir = start;
|
|
776
1534
|
let result = false;
|
|
777
1535
|
while (true) {
|
|
778
|
-
const packageJsonPath =
|
|
1536
|
+
const packageJsonPath = path2.join(dir, "package.json");
|
|
779
1537
|
if (existsSync(packageJsonPath)) {
|
|
780
1538
|
try {
|
|
781
|
-
const packageJson = JSON.parse(
|
|
1539
|
+
const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf8"));
|
|
782
1540
|
if (typeof packageJson.name === "string" && READSEEK_REPO_PACKAGE_NAMES.has(packageJson.name)) {
|
|
783
1541
|
result = true;
|
|
784
1542
|
break;
|
|
785
1543
|
}
|
|
786
1544
|
} catch {}
|
|
787
1545
|
}
|
|
788
|
-
const parent =
|
|
1546
|
+
const parent = path2.dirname(dir);
|
|
789
1547
|
if (parent === dir)
|
|
790
1548
|
break;
|
|
791
1549
|
dir = parent;
|
|
@@ -795,96 +1553,18 @@ function isOwnReadSeekRepository(cwd = process.cwd()) {
|
|
|
795
1553
|
}
|
|
796
1554
|
function defaultReadSeekDir() {
|
|
797
1555
|
const home = homedir3();
|
|
798
|
-
return home ?
|
|
1556
|
+
return home ? path2.join(home, ".pi", "readseek") : null;
|
|
799
1557
|
}
|
|
800
|
-
var DEFAULT_READSEEK_TIMEOUT_MS = 120000;
|
|
801
1558
|
var DEFAULT_READSEEK_VISION_TIMEOUT_MS = 30 * 60000;
|
|
802
1559
|
function readSeekTimeoutMs() {
|
|
803
1560
|
return resolveReadSeekTimeoutMs() ?? DEFAULT_READSEEK_TIMEOUT_MS;
|
|
804
1561
|
}
|
|
805
|
-
var READSEEK_USAGE_HINT = /\n\s*Run readseek(?: [\w-]+)* --help for more information\.?\s*$/;
|
|
806
|
-
function readSeekErrorMessage(stderr) {
|
|
807
|
-
return stderr.replace(READSEEK_USAGE_HINT, "").replace(/^error:\s*/i, "").replace(/\s*\n\s*/g, " ").trim();
|
|
808
|
-
}
|
|
809
1562
|
async function spawnReadSeekRaw(args, options = {}) {
|
|
810
|
-
return
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
return;
|
|
816
|
-
settled = true;
|
|
817
|
-
if (timeout !== undefined)
|
|
818
|
-
clearTimeout(timeout);
|
|
819
|
-
reject(error);
|
|
820
|
-
};
|
|
821
|
-
const succeed = (value) => {
|
|
822
|
-
if (settled)
|
|
823
|
-
return;
|
|
824
|
-
settled = true;
|
|
825
|
-
if (timeout !== undefined)
|
|
826
|
-
clearTimeout(timeout);
|
|
827
|
-
resolve(value);
|
|
828
|
-
};
|
|
829
|
-
const stdin = options.stdin;
|
|
830
|
-
const stdio = [stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"];
|
|
831
|
-
const cancelable = options.cancelable !== false;
|
|
832
|
-
const child = spawn(readSeekBinaryPath(), args, {
|
|
833
|
-
stdio,
|
|
834
|
-
signal: cancelable ? options.signal : undefined
|
|
835
|
-
});
|
|
836
|
-
const timeoutMs = options.timeoutMs ?? readSeekTimeoutMs();
|
|
837
|
-
timeout = setTimeout(() => {
|
|
838
|
-
child.kill("SIGKILL");
|
|
839
|
-
fail(new Error(`readseek timed out after ${timeoutMs} ms`));
|
|
840
|
-
}, timeoutMs);
|
|
841
|
-
timeout.unref?.();
|
|
842
|
-
const childStdout = child.stdout;
|
|
843
|
-
const childStderr = child.stderr;
|
|
844
|
-
const childStdin = child.stdin;
|
|
845
|
-
if (!childStdout || !childStderr) {
|
|
846
|
-
child.kill();
|
|
847
|
-
fail(new Error("readseek stdio streams are unavailable"));
|
|
848
|
-
return;
|
|
849
|
-
}
|
|
850
|
-
const stdoutChunks = [];
|
|
851
|
-
const stderrChunks = [];
|
|
852
|
-
let stdoutBytes = 0;
|
|
853
|
-
childStdout.on("data", (chunk) => {
|
|
854
|
-
if (settled)
|
|
855
|
-
return;
|
|
856
|
-
stdoutBytes += chunk.length;
|
|
857
|
-
if (stdoutBytes > 32 * 1024 * 1024) {
|
|
858
|
-
child.kill();
|
|
859
|
-
fail(new Error("readseek output exceeded 32 MiB"));
|
|
860
|
-
return;
|
|
861
|
-
}
|
|
862
|
-
stdoutChunks.push(chunk);
|
|
863
|
-
});
|
|
864
|
-
childStderr.on("data", (chunk) => stderrChunks.push(chunk));
|
|
865
|
-
child.on("error", (error) => fail(error));
|
|
866
|
-
if (stdin !== undefined) {
|
|
867
|
-
if (!childStdin) {
|
|
868
|
-
child.kill();
|
|
869
|
-
fail(new Error("readseek stdin stream is unavailable"));
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
|
-
childStdin.on("error", (error) => {
|
|
873
|
-
if (error?.code !== "EPIPE")
|
|
874
|
-
fail(error);
|
|
875
|
-
});
|
|
876
|
-
childStdin.end(stdin, "utf-8");
|
|
877
|
-
}
|
|
878
|
-
child.on("close", (code, signal) => {
|
|
879
|
-
const stdout = Buffer.concat(stdoutChunks).toString("utf-8");
|
|
880
|
-
const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
|
|
881
|
-
if (code === 0)
|
|
882
|
-
succeed(stdout);
|
|
883
|
-
else if (signal)
|
|
884
|
-
fail(new Error(readSeekErrorMessage(stderr) || `readseek killed by signal ${signal}`));
|
|
885
|
-
else
|
|
886
|
-
fail(new Error(readSeekErrorMessage(stderr) || `readseek exited with status ${code}`));
|
|
887
|
-
});
|
|
1563
|
+
return runReadSeekNodeText(readSeekBinaryPath(), args, {
|
|
1564
|
+
signal: options.signal,
|
|
1565
|
+
stdin: options.stdin,
|
|
1566
|
+
timeoutMs: options.timeoutMs ?? readSeekTimeoutMs(),
|
|
1567
|
+
cancelable: options.cancelable
|
|
888
1568
|
});
|
|
889
1569
|
}
|
|
890
1570
|
async function ensureDefaultReadSeekDir() {
|
|
@@ -908,25 +1588,24 @@ async function runReadSeekRaw(args, options = {}) {
|
|
|
908
1588
|
return spawnReadSeekRaw(await readSeekInvocationArgs(args), options);
|
|
909
1589
|
}
|
|
910
1590
|
async function runReadSeek(args, options = {}) {
|
|
911
|
-
|
|
912
|
-
|
|
1591
|
+
return runReadSeekNodeJson(readSeekBinaryPath(), await readSeekInvocationArgs(args), {
|
|
1592
|
+
signal: options.signal,
|
|
1593
|
+
stdin: options.stdin,
|
|
1594
|
+
timeoutMs: options.timeoutMs ?? readSeekTimeoutMs(),
|
|
1595
|
+
cancelable: options.cancelable
|
|
1596
|
+
});
|
|
913
1597
|
}
|
|
914
1598
|
async function readSeekView(filePath, options = {}) {
|
|
915
|
-
const args =
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
args.push("--outline");
|
|
926
|
-
if (options.visionMode !== undefined)
|
|
927
|
-
args.push("--vision-mode", options.visionMode);
|
|
928
|
-
if (options.visionLevel !== undefined)
|
|
929
|
-
args.push("--vision-level", options.visionLevel);
|
|
1599
|
+
const args = buildViewArgs({
|
|
1600
|
+
path: filePath,
|
|
1601
|
+
node: options.node,
|
|
1602
|
+
page: options.page,
|
|
1603
|
+
kind: options.kind,
|
|
1604
|
+
depth: options.depth,
|
|
1605
|
+
outline: options.outline,
|
|
1606
|
+
visionMode: options.visionMode,
|
|
1607
|
+
visionLevel: options.visionLevel
|
|
1608
|
+
});
|
|
930
1609
|
const runOptions = { signal: options.signal };
|
|
931
1610
|
return options.visionMode !== undefined ? runReadSeekVisionRaw(args, runOptions) : runReadSeekRaw(args, runOptions);
|
|
932
1611
|
}
|
|
@@ -1316,31 +1995,18 @@ function parseDigestEnvelope(value) {
|
|
|
1316
1995
|
result.identity = parseIdentifyOutput(output.identity);
|
|
1317
1996
|
return result;
|
|
1318
1997
|
}
|
|
1319
|
-
function digestSelectArg(select) {
|
|
1320
|
-
if (select === undefined)
|
|
1321
|
-
return "content";
|
|
1322
|
-
return Array.isArray(select) ? select.join(",") : select;
|
|
1323
|
-
}
|
|
1324
1998
|
function digestArgs(target, options) {
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
if (options.visionLevel !== undefined)
|
|
1337
|
-
args.push("--vision-level", options.visionLevel);
|
|
1338
|
-
if (options.stdin !== undefined) {
|
|
1339
|
-
const stdinName = options.stdinName ?? target;
|
|
1340
|
-
if (target === "-")
|
|
1341
|
-
args.push("--stdin-name", stdinName);
|
|
1342
|
-
}
|
|
1343
|
-
return args;
|
|
1999
|
+
return buildDigestArgs({
|
|
2000
|
+
path: target,
|
|
2001
|
+
select: options.select ?? "content",
|
|
2002
|
+
at: options.at,
|
|
2003
|
+
end: options.end,
|
|
2004
|
+
limit: options.limit,
|
|
2005
|
+
language: options.language,
|
|
2006
|
+
visionMode: options.visionMode,
|
|
2007
|
+
visionLevel: options.visionLevel,
|
|
2008
|
+
stdinName: options.stdin !== undefined && target === "-" ? options.stdinName ?? target : undefined
|
|
2009
|
+
});
|
|
1344
2010
|
}
|
|
1345
2011
|
async function runDigestRaw(target, options = {}) {
|
|
1346
2012
|
const args = digestArgs(target, options);
|
|
@@ -1381,23 +2047,28 @@ async function readSeekMap(filePath, totalBytes, options = {}) {
|
|
|
1381
2047
|
return fileMapFromReadSeekOutput(envelope.map, filePath, totalBytes);
|
|
1382
2048
|
}
|
|
1383
2049
|
async function readSeekSearch(target, pattern, options = {}) {
|
|
1384
|
-
const args =
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
2050
|
+
const args = buildSearchArgs({
|
|
2051
|
+
path: target,
|
|
2052
|
+
pattern,
|
|
2053
|
+
language: options.language,
|
|
2054
|
+
cached: options.cached,
|
|
2055
|
+
others: options.others,
|
|
2056
|
+
ignored: options.ignored
|
|
2057
|
+
});
|
|
1388
2058
|
return parseSearchOutput(await runReadSeek(args, { signal: options.signal })).results;
|
|
1389
2059
|
}
|
|
1390
|
-
async function readSeekRefs(target,
|
|
1391
|
-
const args =
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
2060
|
+
async function readSeekRefs(target, options = {}) {
|
|
2061
|
+
const args = buildRefsArgs({
|
|
2062
|
+
path: target,
|
|
2063
|
+
name: options.name,
|
|
2064
|
+
scope: options.scope,
|
|
2065
|
+
line: options.line,
|
|
2066
|
+
column: options.column,
|
|
2067
|
+
language: options.language,
|
|
2068
|
+
cached: options.cached,
|
|
2069
|
+
others: options.others,
|
|
2070
|
+
ignored: options.ignored
|
|
2071
|
+
});
|
|
1401
2072
|
return parseRefsOutput(await runReadSeek(args, { signal: options.signal })).references;
|
|
1402
2073
|
}
|
|
1403
2074
|
async function readSeekCheck(filePath, content, options = {}) {
|
|
@@ -1418,7 +2089,7 @@ async function readSeekDetect(filePath, options = {}) {
|
|
|
1418
2089
|
async function readSeekDigestContent(filePath, options = {}) {
|
|
1419
2090
|
const envelope = await readSeekDigest(filePath, {
|
|
1420
2091
|
select: "content",
|
|
1421
|
-
at: options.startLine !== undefined ?
|
|
2092
|
+
at: options.startLine !== undefined ? buildLineLocation(options.startLine) : undefined,
|
|
1422
2093
|
end: options.endLine,
|
|
1423
2094
|
visionMode: options.visionMode,
|
|
1424
2095
|
signal: options.signal,
|
|
@@ -1492,13 +2163,12 @@ function parseEditOutput(value) {
|
|
|
1492
2163
|
};
|
|
1493
2164
|
}
|
|
1494
2165
|
async function readSeekEdit(filePath, edits, options = {}) {
|
|
1495
|
-
const args =
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
args.push("--language", options.language);
|
|
2166
|
+
const args = buildEditArgs({
|
|
2167
|
+
path: filePath,
|
|
2168
|
+
apply: options.apply,
|
|
2169
|
+
planHash: options.planHash,
|
|
2170
|
+
language: options.language
|
|
2171
|
+
});
|
|
1502
2172
|
return parseEditOutput(await runReadSeek(args, {
|
|
1503
2173
|
signal: options.signal,
|
|
1504
2174
|
stdin: JSON.stringify({ edits }),
|
|
@@ -1576,14 +2246,18 @@ function parseRenameOutput(value) {
|
|
|
1576
2246
|
};
|
|
1577
2247
|
}
|
|
1578
2248
|
async function readSeekRename(filePath, options) {
|
|
1579
|
-
const args =
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
2249
|
+
const args = buildRenameArgs({
|
|
2250
|
+
path: filePath,
|
|
2251
|
+
to: options.to,
|
|
2252
|
+
line: options.line,
|
|
2253
|
+
column: options.column,
|
|
2254
|
+
workspace: options.workspace,
|
|
2255
|
+
apply: options.apply,
|
|
2256
|
+
language: options.language,
|
|
2257
|
+
cached: options.cached,
|
|
2258
|
+
others: options.others,
|
|
2259
|
+
ignored: options.ignored
|
|
2260
|
+
});
|
|
1587
2261
|
return parseRenameOutput(await runReadSeek(args, { signal: options.signal }));
|
|
1588
2262
|
}
|
|
1589
2263
|
function parseDefCompact(value) {
|
|
@@ -1610,10 +2284,15 @@ function parseDefCompact(value) {
|
|
|
1610
2284
|
});
|
|
1611
2285
|
}
|
|
1612
2286
|
async function readSeekDef(target, options) {
|
|
1613
|
-
const args =
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
2287
|
+
const args = buildDefArgs({
|
|
2288
|
+
path: target,
|
|
2289
|
+
name: options.name,
|
|
2290
|
+
format: "plain",
|
|
2291
|
+
language: options.language,
|
|
2292
|
+
cached: options.cached,
|
|
2293
|
+
others: options.others,
|
|
2294
|
+
ignored: options.ignored
|
|
2295
|
+
});
|
|
1617
2296
|
return parseDefCompact(await runReadSeek(args, { signal: options.signal }));
|
|
1618
2297
|
}
|
|
1619
2298
|
|
|
@@ -1642,7 +2321,7 @@ var THRESHOLDS = {
|
|
|
1642
2321
|
|
|
1643
2322
|
// src/readseek/formatter.ts
|
|
1644
2323
|
var BOX_LINE = "───────────────────────────────────────";
|
|
1645
|
-
function
|
|
2324
|
+
function formatSize(bytes) {
|
|
1646
2325
|
if (bytes < 1024) {
|
|
1647
2326
|
return `${bytes} B`;
|
|
1648
2327
|
}
|
|
@@ -1707,22 +2386,22 @@ function formatFileMap(map, level) {
|
|
|
1707
2386
|
"",
|
|
1708
2387
|
BOX_LINE,
|
|
1709
2388
|
`File Map: ${fileName}`,
|
|
1710
|
-
`${formatNumber(map.totalLines)} lines │ ${
|
|
2389
|
+
`${formatNumber(map.totalLines)} lines │ ${formatSize(map.totalBytes)} │ ${map.language}`,
|
|
1711
2390
|
BOX_LINE,
|
|
1712
2391
|
""
|
|
1713
2392
|
];
|
|
1714
2393
|
if (map.truncatedInfo) {
|
|
1715
2394
|
const { shownSymbols, totalSymbols } = map.truncatedInfo;
|
|
1716
|
-
lines.push(`[Map ≤${
|
|
2395
|
+
lines.push(`[Map ≤${formatSize(THRESHOLDS.MAX_TRUNCATED_BYTES)} | ${shownSymbols} of ${formatNumber(totalSymbols)} symbols]`);
|
|
1717
2396
|
lines.push("");
|
|
1718
2397
|
} else if (effectiveLevel === "outline" /* Outline */) {
|
|
1719
|
-
lines.push(`[Map ≤${
|
|
2398
|
+
lines.push(`[Map ≤${formatSize(THRESHOLDS.MAX_OUTLINE_BYTES)} | outline]`);
|
|
1720
2399
|
lines.push("");
|
|
1721
2400
|
} else if (effectiveLevel === "minimal" /* Minimal */) {
|
|
1722
|
-
lines.push(`[Map ≤${
|
|
2401
|
+
lines.push(`[Map ≤${formatSize(THRESHOLDS.MAX_MAP_BYTES)} | minimal]`);
|
|
1723
2402
|
lines.push("");
|
|
1724
2403
|
} else if (effectiveLevel === "compact" /* Compact */) {
|
|
1725
|
-
lines.push(`[Map ≤${
|
|
2404
|
+
lines.push(`[Map ≤${formatSize(THRESHOLDS.COMPACT_TARGET_BYTES)} | compact]`);
|
|
1726
2405
|
lines.push("");
|
|
1727
2406
|
}
|
|
1728
2407
|
if (map.truncatedInfo) {
|
|
@@ -2084,9 +2763,9 @@ function formatNotFound(query, map) {
|
|
|
2084
2763
|
|
|
2085
2764
|
// src/read-output.ts
|
|
2086
2765
|
import {
|
|
2087
|
-
DEFAULT_MAX_BYTES
|
|
2088
|
-
DEFAULT_MAX_LINES
|
|
2089
|
-
formatSize as
|
|
2766
|
+
DEFAULT_MAX_BYTES,
|
|
2767
|
+
DEFAULT_MAX_LINES,
|
|
2768
|
+
formatSize as formatSize2,
|
|
2090
2769
|
truncateHead
|
|
2091
2770
|
} from "@earendil-works/pi-coding-agent";
|
|
2092
2771
|
function buildReadOutput(input) {
|
|
@@ -2095,14 +2774,14 @@ function buildReadOutput(input) {
|
|
|
2095
2774
|
const warnings = input.warnings ?? [];
|
|
2096
2775
|
const renderedLines = renderReadSeekLines(lines);
|
|
2097
2776
|
const truncated = truncateHead(renderedLines, {
|
|
2098
|
-
maxLines:
|
|
2099
|
-
maxBytes:
|
|
2777
|
+
maxLines: DEFAULT_MAX_LINES,
|
|
2778
|
+
maxBytes: DEFAULT_MAX_BYTES
|
|
2100
2779
|
});
|
|
2101
2780
|
let text = input.truncation ? truncated.content : renderedLines;
|
|
2102
2781
|
if (input.truncation) {
|
|
2103
2782
|
text += `
|
|
2104
2783
|
|
|
2105
|
-
[Output truncated: showing ${input.truncation.outputLines} of ${input.totalLines} lines (${
|
|
2784
|
+
[Output truncated: showing ${input.truncation.outputLines} of ${input.totalLines} lines (${formatSize2(input.truncation.outputBytes)} of ${formatSize2(input.truncation.totalBytes)}). Use offset=${input.startLine + input.truncation.outputLines} to continue.]`;
|
|
2106
2785
|
} else if (input.continuation) {
|
|
2107
2786
|
text += `
|
|
2108
2787
|
|
|
@@ -2426,8 +3105,9 @@ function renderReadSeekSearchCall(args, theme, rest, opts) {
|
|
|
2426
3105
|
const context = rest[0] ?? {};
|
|
2427
3106
|
let text = `${renderToolLabel(theme, opts.label)} ${theme.fg("accent", opts.accent)}`;
|
|
2428
3107
|
text += theme.fg("dim", ` in ${args.path ?? "."}`);
|
|
2429
|
-
|
|
2430
|
-
|
|
3108
|
+
const language = args.language ?? args.lang;
|
|
3109
|
+
if (language)
|
|
3110
|
+
text += theme.fg("dim", ` (${language})`);
|
|
2431
3111
|
const flags = opts.flags.filter(Boolean);
|
|
2432
3112
|
if (flags.length > 0)
|
|
2433
3113
|
text += theme.fg("dim", ` [${flags.join(",")}]`);
|
|
@@ -2498,17 +3178,17 @@ function wrapDisplayLines(lines, width) {
|
|
|
2498
3178
|
return wrapped.length > 0 ? wrapped : [""];
|
|
2499
3179
|
});
|
|
2500
3180
|
}
|
|
2501
|
-
function renderSourceBlock(lines,
|
|
3181
|
+
function renderSourceBlock(lines, path3) {
|
|
2502
3182
|
return highlightCode(lines.join(`
|
|
2503
|
-
`), getLanguageFromPath(
|
|
3183
|
+
`), getLanguageFromPath(path3));
|
|
2504
3184
|
}
|
|
2505
|
-
function renderReadSourceForDisplay(text,
|
|
3185
|
+
function renderReadSourceForDisplay(text, path3, anchors, width) {
|
|
2506
3186
|
const output = [];
|
|
2507
3187
|
let sourceLines = [];
|
|
2508
3188
|
const flushSourceLines = () => {
|
|
2509
3189
|
if (sourceLines.length === 0)
|
|
2510
3190
|
return;
|
|
2511
|
-
output.push(...wrapDisplayLines(renderSourceBlock(sourceLines,
|
|
3191
|
+
output.push(...wrapDisplayLines(renderSourceBlock(sourceLines, path3), width));
|
|
2512
3192
|
sourceLines = [];
|
|
2513
3193
|
};
|
|
2514
3194
|
for (const line of text.split(`
|
|
@@ -2526,19 +3206,19 @@ function renderReadSourceForDisplay(text, path2, anchors, width) {
|
|
|
2526
3206
|
`);
|
|
2527
3207
|
}
|
|
2528
3208
|
var readSourceCache = new WeakMap;
|
|
2529
|
-
function renderReadSourceForDisplayCached(cacheKey, text,
|
|
3209
|
+
function renderReadSourceForDisplayCached(cacheKey, text, path3, anchors, width, theme) {
|
|
2530
3210
|
const themeKey = SOURCE_THEME_STYLES.map((style) => theme.fg(style, "")).join("");
|
|
2531
3211
|
const anchorKey = [...anchors].join(`
|
|
2532
3212
|
`);
|
|
2533
3213
|
const cached = readSourceCache.get(cacheKey);
|
|
2534
|
-
if (cached && cached.text === text && cached.path ===
|
|
3214
|
+
if (cached && cached.text === text && cached.path === path3 && cached.width === width && cached.theme === themeKey && cached.anchors === anchorKey) {
|
|
2535
3215
|
return cached.rendered;
|
|
2536
3216
|
}
|
|
2537
|
-
const rendered = renderReadSourceForDisplay(text,
|
|
2538
|
-
readSourceCache.set(cacheKey, { text, path:
|
|
3217
|
+
const rendered = renderReadSourceForDisplay(text, path3, anchors, width);
|
|
3218
|
+
readSourceCache.set(cacheKey, { text, path: path3, width, theme: themeKey, anchors: anchorKey, rendered });
|
|
2539
3219
|
return rendered;
|
|
2540
3220
|
}
|
|
2541
|
-
function renderGrepSourceForDisplay(text, anchors, renderPath = (
|
|
3221
|
+
function renderGrepSourceForDisplay(text, anchors, renderPath = (path3) => path3) {
|
|
2542
3222
|
return text.split(`
|
|
2543
3223
|
`).map((line) => {
|
|
2544
3224
|
const match = line.match(GREP_ANCHOR_RE);
|
|
@@ -2607,13 +3287,13 @@ ${lines.join(`
|
|
|
2607
3287
|
return "No image analysis returned.";
|
|
2608
3288
|
return "Image caption returned no text.";
|
|
2609
3289
|
}
|
|
2610
|
-
function documentRedirectMessage(
|
|
3290
|
+
function documentRedirectMessage(path3, cache) {
|
|
2611
3291
|
const cacheNote = cache ? ` Index ${cache.state} (hash ${cache.content_hash}).` : "";
|
|
2612
|
-
return `Document content is not available via readSeek_read for ${
|
|
3292
|
+
return `Document content is not available via readSeek_read for ${path3}.${cacheNote} Use readSeek_view to digest structure or selected content.`;
|
|
2613
3293
|
}
|
|
2614
|
-
function skippedImageFile(
|
|
3294
|
+
function skippedImageFile(path3) {
|
|
2615
3295
|
return {
|
|
2616
|
-
content: [{ type: "text", text: `[Skipped image: ${
|
|
3296
|
+
content: [{ type: "text", text: `[Skipped image: ${path3}; no image mode selected]` }],
|
|
2617
3297
|
details: {}
|
|
2618
3298
|
};
|
|
2619
3299
|
}
|
|
@@ -2899,7 +3579,7 @@ async function executeRead(opts) {
|
|
|
2899
3579
|
}));
|
|
2900
3580
|
const selected = readSeekLines.map((line) => line.raw);
|
|
2901
3581
|
const formatted = renderReadSeekLines(readSeekLines);
|
|
2902
|
-
const truncation = truncateHead2(formatted, { maxLines:
|
|
3582
|
+
const truncation = truncateHead2(formatted, { maxLines: DEFAULT_MAX_LINES2, maxBytes: DEFAULT_MAX_BYTES2 });
|
|
2903
3583
|
const shouldAppendMap = !!truncation.truncated && !p.offset && !p.limit && !symbolMatch;
|
|
2904
3584
|
let appendedMap = false;
|
|
2905
3585
|
let mapText = null;
|
|
@@ -3402,8 +4082,8 @@ var LANGUAGE_BY_EXTENSION = new Map([
|
|
|
3402
4082
|
[".rs", "rust"],
|
|
3403
4083
|
[".java", "java"]
|
|
3404
4084
|
]);
|
|
3405
|
-
function inferLanguage(
|
|
3406
|
-
const extensionMatch =
|
|
4085
|
+
function inferLanguage(path3) {
|
|
4086
|
+
const extensionMatch = path3.match(/\.[^.\/\\]+$/);
|
|
3407
4087
|
if (!extensionMatch)
|
|
3408
4088
|
return;
|
|
3409
4089
|
return LANGUAGE_BY_EXTENSION.get(extensionMatch[0].toLowerCase());
|
|
@@ -3612,8 +4292,8 @@ function buildInlineDiffs(entries) {
|
|
|
3612
4292
|
}
|
|
3613
4293
|
return inlineDiffs.length ? inlineDiffs : undefined;
|
|
3614
4294
|
}
|
|
3615
|
-
function finalizeDiffData(
|
|
3616
|
-
const language = inferLanguage(
|
|
4295
|
+
function finalizeDiffData(path3, entries, blockRanges) {
|
|
4296
|
+
const language = inferLanguage(path3);
|
|
3617
4297
|
const inlineDiffs = buildInlineDiffs(entries);
|
|
3618
4298
|
return {
|
|
3619
4299
|
version: 1,
|
|
@@ -3844,7 +4524,7 @@ var hashlineEditSchema = Type3.Object({
|
|
|
3844
4524
|
description: "Read back and verify persisted content"
|
|
3845
4525
|
}))
|
|
3846
4526
|
}, { additionalProperties: false });
|
|
3847
|
-
function buildEditError(
|
|
4527
|
+
function buildEditError(path3, code, message, hint, errorDetails) {
|
|
3848
4528
|
return {
|
|
3849
4529
|
content: [{ type: "text", text: message }],
|
|
3850
4530
|
isError: true,
|
|
@@ -3855,7 +4535,7 @@ function buildEditError(path2, code, message, hint, errorDetails) {
|
|
|
3855
4535
|
readSeekValue: {
|
|
3856
4536
|
tool: "edit",
|
|
3857
4537
|
ok: false,
|
|
3858
|
-
path:
|
|
4538
|
+
path: path3,
|
|
3859
4539
|
error: buildReadSeekError(code, message, hint, errorDetails)
|
|
3860
4540
|
}
|
|
3861
4541
|
}
|
|
@@ -3869,8 +4549,8 @@ async function executeEdit(opts) {
|
|
|
3869
4549
|
const { params, signal, cwd, wasReadInSession, onFileMutated, syntaxValidate } = opts;
|
|
3870
4550
|
const parsed = params;
|
|
3871
4551
|
const rawPath = parsed.path;
|
|
3872
|
-
const
|
|
3873
|
-
const absolutePath = resolveToCwd(
|
|
4552
|
+
const path3 = rawPath.replace(/^@/, "");
|
|
4553
|
+
const absolutePath = resolveToCwd(path3, cwd);
|
|
3874
4554
|
throwIfAborted(signal);
|
|
3875
4555
|
try {
|
|
3876
4556
|
return await withFileMutationQueue(absolutePath, async () => {
|
|
@@ -3908,7 +4588,7 @@ async function executeEdit(opts) {
|
|
|
3908
4588
|
const originalForSyntax = normalizeToLF(stripBom(original).text);
|
|
3909
4589
|
const resultForSyntax = normalizeToLF(stripBom(result).text);
|
|
3910
4590
|
if (!plan.changed) {
|
|
3911
|
-
return buildEditError(absolutePath, "no-op", `No changes made to ${
|
|
4591
|
+
return buildEditError(absolutePath, "no-op", `No changes made to ${path3}. The edits produced identical content.`);
|
|
3912
4592
|
}
|
|
3913
4593
|
const syntaxMode = resolveSyntaxValidateMode({ syntaxValidate });
|
|
3914
4594
|
let syntaxWarning;
|
|
@@ -3949,14 +4629,14 @@ async function executeEdit(opts) {
|
|
|
3949
4629
|
try {
|
|
3950
4630
|
verifiedContent = await fsReadFile2(absolutePath, "utf-8");
|
|
3951
4631
|
} catch (err) {
|
|
3952
|
-
return buildEditError(absolutePath, "post-edit-verification-read-failed", `Edit write completed but post-edit verification failed: could not read ${
|
|
4632
|
+
return buildEditError(absolutePath, "post-edit-verification-read-failed", `Edit write completed but post-edit verification failed: could not read ${path3} after writing.`, undefined, { fsCode: err?.code, fsMessage: err?.message });
|
|
3953
4633
|
}
|
|
3954
4634
|
if (verifiedContent !== plan.content) {
|
|
3955
|
-
return buildEditError(absolutePath, "post-edit-verification-mismatch", `Edit write completed but post-edit verification did not confirm the intended content for ${
|
|
4635
|
+
return buildEditError(absolutePath, "post-edit-verification-mismatch", `Edit write completed but post-edit verification did not confirm the intended content for ${path3}. Re-read the file before making follow-up edits.`, undefined, { expectedLength: plan.content.length, actualLength: verifiedContent.length });
|
|
3956
4636
|
}
|
|
3957
4637
|
}
|
|
3958
4638
|
const diffResult = generateCompactOrFullDiff(original, result);
|
|
3959
|
-
const patch = createPatch(
|
|
4639
|
+
const patch = createPatch(path3, original, result);
|
|
3960
4640
|
const diffData = buildDiffData({
|
|
3961
4641
|
path: absolutePath,
|
|
3962
4642
|
oldContent: original,
|
|
@@ -3968,7 +4648,7 @@ async function executeEdit(opts) {
|
|
|
3968
4648
|
const semanticSummary = { classification: classification.classification };
|
|
3969
4649
|
const builtOutput = buildEditOutput({
|
|
3970
4650
|
path: absolutePath,
|
|
3971
|
-
displayPath:
|
|
4651
|
+
displayPath: path3,
|
|
3972
4652
|
diff: diffResult.diff,
|
|
3973
4653
|
patch,
|
|
3974
4654
|
diffData,
|
|
@@ -3991,7 +4671,7 @@ async function executeEdit(opts) {
|
|
|
3991
4671
|
});
|
|
3992
4672
|
} catch (err) {
|
|
3993
4673
|
if (typeof err?.code === "string") {
|
|
3994
|
-
return mapEditFileError(err, absolutePath,
|
|
4674
|
+
return mapEditFileError(err, absolutePath, path3, "read");
|
|
3995
4675
|
}
|
|
3996
4676
|
throw err;
|
|
3997
4677
|
}
|
|
@@ -4096,21 +4776,21 @@ ${theme.fg("error", info.errorText)}`;
|
|
|
4096
4776
|
|
|
4097
4777
|
// src/grep.ts
|
|
4098
4778
|
import { readFile as fsReadFile3, stat as fsStat } from "node:fs/promises";
|
|
4099
|
-
import
|
|
4779
|
+
import path3 from "node:path";
|
|
4100
4780
|
import { createGrepTool } from "@earendil-works/pi-coding-agent";
|
|
4101
4781
|
import { Type as Type5 } from "@sinclair/typebox";
|
|
4102
4782
|
import { Text as Text5 } from "@earendil-works/pi-tui";
|
|
4103
4783
|
|
|
4104
4784
|
// src/grep-output.ts
|
|
4105
4785
|
import {
|
|
4106
|
-
formatSize as
|
|
4786
|
+
formatSize as formatSize3,
|
|
4107
4787
|
truncateHead as truncateHead3
|
|
4108
4788
|
} from "@earendil-works/pi-coding-agent";
|
|
4109
4789
|
|
|
4110
4790
|
// src/grep-budget.ts
|
|
4111
|
-
import { DEFAULT_MAX_BYTES as
|
|
4112
|
-
var GREP_OUTPUT_DEFAULT_MAX_LINES =
|
|
4113
|
-
var GREP_OUTPUT_DEFAULT_MAX_BYTES = Math.min(
|
|
4791
|
+
import { DEFAULT_MAX_BYTES as DEFAULT_MAX_BYTES3, DEFAULT_MAX_LINES as DEFAULT_MAX_LINES3 } from "@earendil-works/pi-coding-agent";
|
|
4792
|
+
var GREP_OUTPUT_DEFAULT_MAX_LINES = DEFAULT_MAX_LINES3;
|
|
4793
|
+
var GREP_OUTPUT_DEFAULT_MAX_BYTES = Math.min(DEFAULT_MAX_BYTES3, 50 * 1024);
|
|
4114
4794
|
function resolveDimension(jsonValue, ceiling) {
|
|
4115
4795
|
if (jsonValue !== undefined)
|
|
4116
4796
|
return Math.min(jsonValue, ceiling);
|
|
@@ -4203,7 +4883,7 @@ ${text}`;
|
|
|
4203
4883
|
if (truncated.truncated) {
|
|
4204
4884
|
text = `${truncated.content}
|
|
4205
4885
|
|
|
4206
|
-
[Output truncated: showing ${truncated.outputLines} of ${truncated.totalLines} lines (${
|
|
4886
|
+
[Output truncated: showing ${truncated.outputLines} of ${truncated.totalLines} lines (${formatSize3(truncated.outputBytes)} of ${formatSize3(truncated.totalBytes)}). Refine pattern or increase limit.]`;
|
|
4207
4887
|
}
|
|
4208
4888
|
const readSeekValue = {
|
|
4209
4889
|
tool: "grep",
|
|
@@ -4435,20 +5115,21 @@ function formatGrepResultText(input) {
|
|
|
4435
5115
|
// src/readseek-params.ts
|
|
4436
5116
|
import { Type as Type4 } from "@sinclair/typebox";
|
|
4437
5117
|
function searchPathParam() {
|
|
4438
|
-
return Type4.Optional(Type4.String({ description:
|
|
5118
|
+
return Type4.Optional(Type4.String({ description: PARAM_DESCRIPTIONS.searchPath }));
|
|
4439
5119
|
}
|
|
4440
|
-
function
|
|
4441
|
-
return Type4.Optional(Type4.String({ description:
|
|
5120
|
+
function languageParam() {
|
|
5121
|
+
return Type4.Optional(Type4.String({ description: PARAM_DESCRIPTIONS.language }));
|
|
4442
5122
|
}
|
|
4443
5123
|
function readSeekGitSearchParams() {
|
|
4444
5124
|
return {
|
|
4445
|
-
cached: Type4.Optional(Type4.Boolean({ description:
|
|
4446
|
-
others: Type4.Optional(Type4.Boolean({ description:
|
|
4447
|
-
ignored: Type4.Optional(Type4.Boolean({ description:
|
|
5125
|
+
cached: Type4.Optional(Type4.Boolean({ description: PARAM_DESCRIPTIONS.cached })),
|
|
5126
|
+
others: Type4.Optional(Type4.Boolean({ description: PARAM_DESCRIPTIONS.others })),
|
|
5127
|
+
ignored: Type4.Optional(Type4.Boolean({ description: PARAM_DESCRIPTIONS.ignored }))
|
|
4448
5128
|
};
|
|
4449
5129
|
}
|
|
4450
5130
|
function validateIgnoredRequiresOthers(tool, params) {
|
|
4451
|
-
|
|
5131
|
+
const validated = validateGitSelection(params);
|
|
5132
|
+
if (!validated.ok) {
|
|
4452
5133
|
return buildToolErrorResult(tool, "invalid-parameter", `${tool} parameter 'ignored' requires 'others'`);
|
|
4453
5134
|
}
|
|
4454
5135
|
return null;
|
|
@@ -4636,7 +5317,7 @@ async function executeGrep(opts) {
|
|
|
4636
5317
|
};
|
|
4637
5318
|
const toAbsolutePath = (displayPath) => {
|
|
4638
5319
|
if (searchPathIsDirectory)
|
|
4639
|
-
return
|
|
5320
|
+
return path3.resolve(searchPath, displayPath);
|
|
4640
5321
|
return searchPath;
|
|
4641
5322
|
};
|
|
4642
5323
|
const groupsByPath = new Map;
|
|
@@ -4913,7 +5594,7 @@ function registerGrepTool(pi, options = {}) {
|
|
|
4913
5594
|
}
|
|
4914
5595
|
|
|
4915
5596
|
// src/search.ts
|
|
4916
|
-
import
|
|
5597
|
+
import path4 from "node:path";
|
|
4917
5598
|
import { Type as Type6 } from "@sinclair/typebox";
|
|
4918
5599
|
|
|
4919
5600
|
// src/stat-search-path.ts
|
|
@@ -4922,9 +5603,9 @@ async function statSearchPathOrError(tool, rawPath, searchPath) {
|
|
|
4922
5603
|
try {
|
|
4923
5604
|
return { ok: true, stats: await fsStat2(searchPath) };
|
|
4924
5605
|
} catch (err) {
|
|
4925
|
-
const
|
|
5606
|
+
const path4 = rawPath ?? searchPath;
|
|
4926
5607
|
const { code, message } = formatFsError(err, "stat-error");
|
|
4927
|
-
return { ok: false, error: buildToolErrorResult(tool, code, message, { path:
|
|
5608
|
+
return { ok: false, error: buildToolErrorResult(tool, code, message, { path: path4 }) };
|
|
4928
5609
|
}
|
|
4929
5610
|
}
|
|
4930
5611
|
|
|
@@ -4998,7 +5679,7 @@ function linesFromSearchResult(result, ranges) {
|
|
|
4998
5679
|
return lines;
|
|
4999
5680
|
}
|
|
5000
5681
|
function readSeekLanguageForPath(language, searchPath, isFile) {
|
|
5001
|
-
if (language === "typescript" && isFile &&
|
|
5682
|
+
if (language === "typescript" && isFile && path4.extname(searchPath).toLowerCase() === ".tsx")
|
|
5002
5683
|
return "tsx";
|
|
5003
5684
|
return language;
|
|
5004
5685
|
}
|
|
@@ -5014,7 +5695,7 @@ async function executeSearch(opts) {
|
|
|
5014
5695
|
return statResult.error;
|
|
5015
5696
|
const searchPathIsFile = statResult.stats.isFile();
|
|
5016
5697
|
try {
|
|
5017
|
-
const effectiveLang = readSeekLanguageForPath(p.
|
|
5698
|
+
const effectiveLang = readSeekLanguageForPath(p.language, searchPath, searchPathIsFile);
|
|
5018
5699
|
const results = await readSeekSearch(searchPath, p.pattern, {
|
|
5019
5700
|
language: effectiveLang,
|
|
5020
5701
|
cached: p.cached,
|
|
@@ -5033,8 +5714,8 @@ async function executeSearch(opts) {
|
|
|
5033
5714
|
}
|
|
5034
5715
|
const readSeekFiles = [];
|
|
5035
5716
|
for (const result of results) {
|
|
5036
|
-
const abs =
|
|
5037
|
-
const display =
|
|
5717
|
+
const abs = path4.isAbsolute(result.file) ? result.file : path4.resolve(cwd, result.file);
|
|
5718
|
+
const display = path4.relative(cwd, abs) || abs;
|
|
5038
5719
|
const ranges = result.matches.map((match) => ({ startLine: match.start_line, endLine: match.end_line }));
|
|
5039
5720
|
const mergedRanges = mergeRanges(ranges);
|
|
5040
5721
|
const lines = linesFromSearchResult(result, mergedRanges);
|
|
@@ -5082,8 +5763,8 @@ function registerSearchTool(pi, options = {}) {
|
|
|
5082
5763
|
promptSnippet: SEARCH_PROMPT_METADATA.promptSnippet,
|
|
5083
5764
|
promptGuidelines: SEARCH_PROMPT_METADATA.promptGuidelines,
|
|
5084
5765
|
parameters: Type6.Object({
|
|
5085
|
-
pattern: Type6.String({ description:
|
|
5086
|
-
|
|
5766
|
+
pattern: Type6.String({ description: PARAM_DESCRIPTIONS.pattern }),
|
|
5767
|
+
language: languageParam(),
|
|
5087
5768
|
path: searchPathParam(),
|
|
5088
5769
|
...readSeekGitSearchParams()
|
|
5089
5770
|
}),
|
|
@@ -5111,13 +5792,21 @@ function registerSearchTool(pi, options = {}) {
|
|
|
5111
5792
|
|
|
5112
5793
|
// src/refs.ts
|
|
5113
5794
|
import { Type as Type7 } from "@sinclair/typebox";
|
|
5114
|
-
import
|
|
5795
|
+
import path5 from "node:path";
|
|
5115
5796
|
|
|
5116
5797
|
// src/refs-output.ts
|
|
5798
|
+
function refsQueryLabel(input) {
|
|
5799
|
+
if (input.name)
|
|
5800
|
+
return input.name;
|
|
5801
|
+
if (input.scope && input.line !== undefined) {
|
|
5802
|
+
return input.column === undefined ? `line:${input.line}` : `line:${input.line}:${input.column}`;
|
|
5803
|
+
}
|
|
5804
|
+
return "?";
|
|
5805
|
+
}
|
|
5117
5806
|
function buildRefsOutput(input) {
|
|
5118
5807
|
if (input.files.length === 0) {
|
|
5119
5808
|
return {
|
|
5120
|
-
text: `No references found for: ${input
|
|
5809
|
+
text: `No references found for: ${refsQueryLabel(input)}`,
|
|
5121
5810
|
readSeekValue: { tool: "refs", files: [] }
|
|
5122
5811
|
};
|
|
5123
5812
|
}
|
|
@@ -5148,14 +5837,14 @@ function groupReferences(references, cwd) {
|
|
|
5148
5837
|
const files = new Map;
|
|
5149
5838
|
const seen = new Set;
|
|
5150
5839
|
for (const reference of references) {
|
|
5151
|
-
const abs =
|
|
5840
|
+
const abs = path5.isAbsolute(reference.file) ? reference.file : path5.resolve(cwd, reference.file);
|
|
5152
5841
|
const dedupeKey = `${abs}:${reference.line}:${reference.column}`;
|
|
5153
5842
|
if (seen.has(dedupeKey))
|
|
5154
5843
|
continue;
|
|
5155
5844
|
seen.add(dedupeKey);
|
|
5156
5845
|
let file = files.get(abs);
|
|
5157
5846
|
if (!file) {
|
|
5158
|
-
file = { displayPath:
|
|
5847
|
+
file = { displayPath: path5.relative(cwd, abs) || abs, path: abs, lines: [] };
|
|
5159
5848
|
files.set(abs, file);
|
|
5160
5849
|
}
|
|
5161
5850
|
file.lines.push(refsLine(reference));
|
|
@@ -5171,26 +5860,34 @@ async function executeRefs(opts) {
|
|
|
5171
5860
|
const ignoredError = validateIgnoredRequiresOthers("refs", p);
|
|
5172
5861
|
if (ignoredError)
|
|
5173
5862
|
return ignoredError;
|
|
5174
|
-
|
|
5175
|
-
|
|
5863
|
+
const scopeError = validateRefsNameVsScope(p);
|
|
5864
|
+
if (!scopeError.ok) {
|
|
5865
|
+
return buildToolErrorResult("refs", "invalid-parameter", `refs parameter: ${scopeError.error}`);
|
|
5176
5866
|
}
|
|
5177
5867
|
const searchPath = resolveToCwd(p.path ?? ".", cwd);
|
|
5178
5868
|
const statResult = await statSearchPathOrError("refs", p.path, searchPath);
|
|
5179
5869
|
if (!statResult.ok)
|
|
5180
5870
|
return statResult.error;
|
|
5181
5871
|
try {
|
|
5182
|
-
const references = await readSeekRefs(searchPath,
|
|
5872
|
+
const references = await readSeekRefs(searchPath, {
|
|
5873
|
+
name: p.name,
|
|
5183
5874
|
scope: p.scope,
|
|
5184
5875
|
line: p.line,
|
|
5185
5876
|
column: p.column,
|
|
5186
|
-
language: p.
|
|
5877
|
+
language: p.language,
|
|
5187
5878
|
cached: p.cached,
|
|
5188
5879
|
others: p.others,
|
|
5189
5880
|
ignored: p.ignored,
|
|
5190
5881
|
signal
|
|
5191
5882
|
});
|
|
5192
5883
|
const files = groupReferences(references, cwd);
|
|
5193
|
-
const builtOutput = buildRefsOutput({
|
|
5884
|
+
const builtOutput = buildRefsOutput({
|
|
5885
|
+
name: p.name,
|
|
5886
|
+
scope: p.scope,
|
|
5887
|
+
line: p.line,
|
|
5888
|
+
column: p.column,
|
|
5889
|
+
files
|
|
5890
|
+
});
|
|
5194
5891
|
for (const file of files) {
|
|
5195
5892
|
onFileAnchored?.(file.path);
|
|
5196
5893
|
}
|
|
@@ -5214,21 +5911,22 @@ function registerRefsTool(pi, options = {}) {
|
|
|
5214
5911
|
promptSnippet: REFS_PROMPT_METADATA.promptSnippet,
|
|
5215
5912
|
promptGuidelines: REFS_PROMPT_METADATA.promptGuidelines,
|
|
5216
5913
|
parameters: Type7.Object({
|
|
5217
|
-
name: Type7.String({ description:
|
|
5914
|
+
name: Type7.Optional(Type7.String({ description: PARAM_DESCRIPTIONS.refsName })),
|
|
5218
5915
|
path: searchPathParam(),
|
|
5219
|
-
|
|
5220
|
-
scope: Type7.Optional(Type7.Boolean({ description:
|
|
5221
|
-
line: Type7.Optional(Type7.Number({ description:
|
|
5222
|
-
column: Type7.Optional(Type7.Number({ description:
|
|
5916
|
+
language: languageParam(),
|
|
5917
|
+
scope: Type7.Optional(Type7.Boolean({ description: PARAM_DESCRIPTIONS.scope })),
|
|
5918
|
+
line: Type7.Optional(Type7.Number({ description: PARAM_DESCRIPTIONS.line })),
|
|
5919
|
+
column: Type7.Optional(Type7.Number({ description: PARAM_DESCRIPTIONS.column })),
|
|
5223
5920
|
...readSeekGitSearchParams()
|
|
5224
5921
|
}),
|
|
5225
5922
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
5226
5923
|
return executeRefs({ params, signal, cwd: ctx.cwd, onFileAnchored: options.onFileAnchored });
|
|
5227
5924
|
},
|
|
5228
5925
|
renderCall(args, theme, ...rest) {
|
|
5926
|
+
const accent = args.name ?? (args.scope ? args.column === undefined ? `line:${args.line}` : `line:${args.line}:${args.column}` : "?");
|
|
5229
5927
|
return renderReadSeekSearchCall(args, theme, rest, {
|
|
5230
5928
|
label: "refs",
|
|
5231
|
-
accent
|
|
5929
|
+
accent,
|
|
5232
5930
|
flags: [args.scope && "scope", args.cached && "cached", args.others && "others", args.ignored && "ignored"]
|
|
5233
5931
|
});
|
|
5234
5932
|
},
|
|
@@ -5246,7 +5944,7 @@ function registerRefsTool(pi, options = {}) {
|
|
|
5246
5944
|
|
|
5247
5945
|
// src/rename.ts
|
|
5248
5946
|
import { Type as Type8 } from "@sinclair/typebox";
|
|
5249
|
-
import
|
|
5947
|
+
import path6 from "node:path";
|
|
5250
5948
|
import { Text as Text6 } from "@earendil-works/pi-tui";
|
|
5251
5949
|
var RENAME_PROMPT_METADATA = defineToolPromptMetadata({
|
|
5252
5950
|
promptUrl: new URL("../prompts/rename.md", import.meta.url),
|
|
@@ -5286,13 +5984,13 @@ async function executeRename(opts) {
|
|
|
5286
5984
|
for (const file of [output, ...output.others]) {
|
|
5287
5985
|
if (file.edits.length === 0)
|
|
5288
5986
|
continue;
|
|
5289
|
-
const absoluteFile =
|
|
5987
|
+
const absoluteFile = path6.isAbsolute(file.file) ? file.file : path6.resolve(cwd, file.file);
|
|
5290
5988
|
onFileMutated?.(absoluteFile);
|
|
5291
5989
|
}
|
|
5292
5990
|
}
|
|
5293
5991
|
const files = [output.file];
|
|
5294
5992
|
for (const other of output.others) {
|
|
5295
|
-
const abs =
|
|
5993
|
+
const abs = path6.isAbsolute(other.file) ? other.file : path6.resolve(cwd, other.file);
|
|
5296
5994
|
if (!files.includes(abs))
|
|
5297
5995
|
files.push(abs);
|
|
5298
5996
|
}
|
|
@@ -5374,7 +6072,7 @@ function registerRenameTool(pi, options = {}) {
|
|
|
5374
6072
|
});
|
|
5375
6073
|
if (expanded && output) {
|
|
5376
6074
|
for (const file of outputs) {
|
|
5377
|
-
const display =
|
|
6075
|
+
const display = path6.relative(cwd, file.file) || file.file;
|
|
5378
6076
|
text += `
|
|
5379
6077
|
${linkedPathLine(theme, file.file, display, cwd, ` (${file.edits.length} edits)`, width)}`;
|
|
5380
6078
|
}
|
|
@@ -5395,7 +6093,7 @@ import { Text as Text7 } from "@earendil-works/pi-tui";
|
|
|
5395
6093
|
import { Type as Type9 } from "@sinclair/typebox";
|
|
5396
6094
|
|
|
5397
6095
|
// src/pending-diff-preview.ts
|
|
5398
|
-
import { existsSync as existsSync2, readFileSync as
|
|
6096
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, realpathSync, statSync as statSync3 } from "node:fs";
|
|
5399
6097
|
import { dirname, isAbsolute as isAbsolute2, relative as relative2, resolve, sep } from "node:path";
|
|
5400
6098
|
var PENDING_DIFF_MAX_BYTES = 1024 * 1024;
|
|
5401
6099
|
function skip(reason) {
|
|
@@ -5433,7 +6131,7 @@ function readUtf8File(filePath) {
|
|
|
5433
6131
|
return { type: "skip", reason: "not a file" };
|
|
5434
6132
|
if (stat2.size > PENDING_DIFF_MAX_BYTES)
|
|
5435
6133
|
return { type: "skip", reason: "file too large" };
|
|
5436
|
-
const content =
|
|
6134
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
5437
6135
|
if (content.includes("\x00"))
|
|
5438
6136
|
return { type: "skip", reason: "binary file" };
|
|
5439
6137
|
return { type: "ok", content };
|
|
@@ -5728,13 +6426,13 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5728
6426
|
}
|
|
5729
6427
|
},
|
|
5730
6428
|
renderCall(args, theme, context = {}) {
|
|
5731
|
-
const { path:
|
|
6429
|
+
const { path: path7, content } = args;
|
|
5732
6430
|
const cwd = context.cwd ?? process.cwd();
|
|
5733
6431
|
const label = renderToolLabel(theme, "write");
|
|
5734
6432
|
const lineCount = typeof content === "string" ? content.split(`
|
|
5735
6433
|
`).length : 0;
|
|
5736
6434
|
const bytes = typeof content === "string" ? Buffer.byteLength(content, "utf8") : 0;
|
|
5737
|
-
const renderedPath = typeof
|
|
6435
|
+
const renderedPath = typeof path7 === "string" ? linkToolPath(theme.fg("muted", path7), path7, cwd) : theme.fg("toolOutput", "...");
|
|
5738
6436
|
let text = clampLineToWidth(`${label} ${renderedPath}${typeof content === "string" ? ` (${lineCount} ${lineCount === 1 ? "line" : "lines"} • ${bytes} B)` : ""}`, context.width);
|
|
5739
6437
|
if (context.executionStarted) {
|
|
5740
6438
|
return upsertTextComponent(context.lastComponent, text);
|
|
@@ -5790,7 +6488,7 @@ function registerWriteTool(pi, options = {}) {
|
|
|
5790
6488
|
|
|
5791
6489
|
// src/def.ts
|
|
5792
6490
|
import { Type as Type10 } from "@sinclair/typebox";
|
|
5793
|
-
import
|
|
6491
|
+
import path7 from "node:path";
|
|
5794
6492
|
var DEF_PROMPT_METADATA = defineToolPromptMetadata({
|
|
5795
6493
|
promptUrl: new URL("../prompts/def.md", import.meta.url),
|
|
5796
6494
|
promptSnippet: "Go to a symbol definition or declaration by name"
|
|
@@ -5811,7 +6509,7 @@ async function executeDef(opts) {
|
|
|
5811
6509
|
try {
|
|
5812
6510
|
const definitions = await readSeekDef(searchPath, {
|
|
5813
6511
|
name: p.name,
|
|
5814
|
-
language: p.
|
|
6512
|
+
language: p.language,
|
|
5815
6513
|
cached: p.cached,
|
|
5816
6514
|
others: p.others,
|
|
5817
6515
|
ignored: p.ignored,
|
|
@@ -5827,10 +6525,10 @@ async function executeDef(opts) {
|
|
|
5827
6525
|
}
|
|
5828
6526
|
const files = new Map;
|
|
5829
6527
|
for (const def of definitions) {
|
|
5830
|
-
const abs =
|
|
6528
|
+
const abs = path7.isAbsolute(def.file) ? def.file : path7.resolve(cwd, def.file);
|
|
5831
6529
|
let file = files.get(abs);
|
|
5832
6530
|
if (!file) {
|
|
5833
|
-
file = { displayPath:
|
|
6531
|
+
file = { displayPath: path7.relative(cwd, abs) || abs, path: abs, lines: [] };
|
|
5834
6532
|
files.set(abs, file);
|
|
5835
6533
|
}
|
|
5836
6534
|
file.lines.push(buildReadSeekLineWithHash(def.line, def.line_hash, def.text));
|
|
@@ -5866,9 +6564,9 @@ function registerDefTool(pi, options = {}) {
|
|
|
5866
6564
|
promptSnippet: DEF_PROMPT_METADATA.promptSnippet,
|
|
5867
6565
|
promptGuidelines: DEF_PROMPT_METADATA.promptGuidelines,
|
|
5868
6566
|
parameters: Type10.Object({
|
|
5869
|
-
name: Type10.String({ description:
|
|
6567
|
+
name: Type10.String({ description: PARAM_DESCRIPTIONS.name }),
|
|
5870
6568
|
path: searchPathParam(),
|
|
5871
|
-
|
|
6569
|
+
language: languageParam(),
|
|
5872
6570
|
...readSeekGitSearchParams()
|
|
5873
6571
|
}),
|
|
5874
6572
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
@@ -5895,37 +6593,31 @@ function registerDefTool(pi, options = {}) {
|
|
|
5895
6593
|
// src/digest.ts
|
|
5896
6594
|
import { Text as Text8 } from "@earendil-works/pi-tui";
|
|
5897
6595
|
import { Type as Type11 } from "@sinclair/typebox";
|
|
5898
|
-
var
|
|
6596
|
+
var DIGEST_FACETS2 = DIGEST_FACETS;
|
|
5899
6597
|
var DIGEST_PROMPT_METADATA = defineToolPromptMetadata({
|
|
5900
6598
|
promptUrl: new URL("../prompts/digest.md", import.meta.url),
|
|
5901
6599
|
promptSnippet: "Digest source facets as a native CLI envelope"
|
|
5902
6600
|
});
|
|
5903
|
-
var facetSchema = Type11.Union(
|
|
6601
|
+
var facetSchema = Type11.Union(DIGEST_FACETS2.map((facet) => Type11.Literal(facet)));
|
|
5904
6602
|
var digestSchema = Type11.Object({
|
|
5905
6603
|
path: filePathParam(),
|
|
5906
6604
|
select: Type11.Optional(Type11.Union([
|
|
5907
6605
|
facetSchema,
|
|
5908
6606
|
Type11.Array(facetSchema, { minItems: 1 }),
|
|
5909
6607
|
Type11.String({
|
|
5910
|
-
description:
|
|
6608
|
+
description: PARAM_DESCRIPTIONS.selectComma
|
|
5911
6609
|
})
|
|
5912
6610
|
], {
|
|
5913
|
-
description:
|
|
6611
|
+
description: PARAM_DESCRIPTIONS.select
|
|
5914
6612
|
})),
|
|
5915
6613
|
at: Type11.Optional(Type11.String({
|
|
5916
|
-
description:
|
|
6614
|
+
description: PARAM_DESCRIPTIONS.at
|
|
5917
6615
|
})),
|
|
5918
|
-
end: optionalIntOrString(
|
|
5919
|
-
limit: optionalIntOrString(
|
|
5920
|
-
language: Type11.Optional(Type11.String({ description:
|
|
5921
|
-
visionMode: Type11.Optional(Type11.Union(
|
|
5922
|
-
|
|
5923
|
-
Type11.Literal("all"),
|
|
5924
|
-
Type11.Literal("ocr"),
|
|
5925
|
-
Type11.Literal("caption"),
|
|
5926
|
-
Type11.Literal("objects")
|
|
5927
|
-
], { description: "Standalone image mode: none, all, caption, objects, or ocr" })),
|
|
5928
|
-
visionLevel: Type11.Optional(Type11.Union([Type11.Literal("low"), Type11.Literal("medium"), Type11.Literal("high")], { description: "Vision inference level: low (default), medium, or high" }))
|
|
6616
|
+
end: optionalIntOrString(PARAM_DESCRIPTIONS.end),
|
|
6617
|
+
limit: optionalIntOrString(PARAM_DESCRIPTIONS.limit),
|
|
6618
|
+
language: Type11.Optional(Type11.String({ description: PARAM_DESCRIPTIONS.languageOverride })),
|
|
6619
|
+
visionMode: Type11.Optional(Type11.Union(VISION_MODES.map((mode) => Type11.Literal(mode)), { description: PARAM_DESCRIPTIONS.visionModeDigest })),
|
|
6620
|
+
visionLevel: Type11.Optional(Type11.Union(VISION_LEVELS.map((level) => Type11.Literal(level)), { description: PARAM_DESCRIPTIONS.visionLevel }))
|
|
5929
6621
|
});
|
|
5930
6622
|
function hasDigestAnchors(value) {
|
|
5931
6623
|
if (!value || typeof value !== "object")
|
|
@@ -5943,8 +6635,8 @@ function parseSelect(select) {
|
|
|
5943
6635
|
if (facets.length === 0)
|
|
5944
6636
|
throw new Error("select must include at least one facet");
|
|
5945
6637
|
for (const facet of facets) {
|
|
5946
|
-
if (!
|
|
5947
|
-
throw new Error(`unknown digest facet \`${facet}\`; expected ${
|
|
6638
|
+
if (!DIGEST_FACETS2.includes(facet)) {
|
|
6639
|
+
throw new Error(`unknown digest facet \`${facet}\`; expected ${DIGEST_FACETS2.join(", ")}`);
|
|
5948
6640
|
}
|
|
5949
6641
|
}
|
|
5950
6642
|
return facets;
|
|
@@ -5952,15 +6644,15 @@ function parseSelect(select) {
|
|
|
5952
6644
|
if (typeof select !== "string") {
|
|
5953
6645
|
throw new Error("select must be a facet, facet list, or comma-separated facets");
|
|
5954
6646
|
}
|
|
5955
|
-
if (
|
|
6647
|
+
if (DIGEST_FACETS2.includes(select)) {
|
|
5956
6648
|
return select;
|
|
5957
6649
|
}
|
|
5958
6650
|
const parts = select.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
|
|
5959
6651
|
if (parts.length === 0)
|
|
5960
6652
|
throw new Error("select must include at least one facet");
|
|
5961
6653
|
for (const part of parts) {
|
|
5962
|
-
if (!
|
|
5963
|
-
throw new Error(`unknown digest facet \`${part}\`; expected ${
|
|
6654
|
+
if (!DIGEST_FACETS2.includes(part)) {
|
|
6655
|
+
throw new Error(`unknown digest facet \`${part}\`; expected ${DIGEST_FACETS2.join(", ")}`);
|
|
5964
6656
|
}
|
|
5965
6657
|
}
|
|
5966
6658
|
return parts;
|
|
@@ -6099,24 +6791,13 @@ ${textContent}`;
|
|
|
6099
6791
|
|
|
6100
6792
|
// src/view.ts
|
|
6101
6793
|
import {
|
|
6102
|
-
DEFAULT_MAX_BYTES as
|
|
6103
|
-
DEFAULT_MAX_LINES as
|
|
6794
|
+
DEFAULT_MAX_BYTES as DEFAULT_MAX_BYTES4,
|
|
6795
|
+
DEFAULT_MAX_LINES as DEFAULT_MAX_LINES4,
|
|
6104
6796
|
truncateHead as truncateHead5
|
|
6105
6797
|
} from "@earendil-works/pi-coding-agent";
|
|
6106
6798
|
import { Text as Text9 } from "@earendil-works/pi-tui";
|
|
6107
6799
|
import { Type as Type12 } from "@sinclair/typebox";
|
|
6108
|
-
var NODE_KINDS =
|
|
6109
|
-
"artifact",
|
|
6110
|
-
"footer",
|
|
6111
|
-
"header",
|
|
6112
|
-
"heading",
|
|
6113
|
-
"marginal_label",
|
|
6114
|
-
"page",
|
|
6115
|
-
"page_number",
|
|
6116
|
-
"paragraph",
|
|
6117
|
-
"section",
|
|
6118
|
-
"structural_section"
|
|
6119
|
-
];
|
|
6800
|
+
var NODE_KINDS = DOCUMENT_NODE_KINDS;
|
|
6120
6801
|
var VIEW_PROMPT_METADATA = defineToolPromptMetadata({
|
|
6121
6802
|
promptUrl: new URL("../prompts/view.md", import.meta.url),
|
|
6122
6803
|
promptSnippet: "Digest a document's structure or selected content"
|
|
@@ -6140,11 +6821,15 @@ async function executeView(opts) {
|
|
|
6140
6821
|
path: input.path
|
|
6141
6822
|
});
|
|
6142
6823
|
}
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6824
|
+
const visionValidation = validateVisionLevelVsMode({
|
|
6825
|
+
outline: input.outline,
|
|
6826
|
+
visionMode: input.visionMode,
|
|
6827
|
+
visionLevel: input.visionLevel
|
|
6828
|
+
});
|
|
6829
|
+
if (!visionValidation.ok) {
|
|
6830
|
+
return buildToolErrorResult("view", "invalid-params-combo", `${visionValidation.error}.`, {
|
|
6831
|
+
path: input.path
|
|
6832
|
+
});
|
|
6148
6833
|
}
|
|
6149
6834
|
const filePath = resolveToCwd(input.path, cwd);
|
|
6150
6835
|
try {
|
|
@@ -6159,8 +6844,8 @@ async function executeView(opts) {
|
|
|
6159
6844
|
signal
|
|
6160
6845
|
});
|
|
6161
6846
|
const truncation = truncateHead5(output, {
|
|
6162
|
-
maxLines:
|
|
6163
|
-
maxBytes:
|
|
6847
|
+
maxLines: DEFAULT_MAX_LINES4,
|
|
6848
|
+
maxBytes: DEFAULT_MAX_BYTES4
|
|
6164
6849
|
});
|
|
6165
6850
|
const notice = truncation.truncated ? `
|
|
6166
6851
|
[… document view truncated; narrow it with page, node, kind, or depth]` : "";
|
|
@@ -6188,24 +6873,15 @@ function registerViewTool(pi) {
|
|
|
6188
6873
|
promptGuidelines: VIEW_PROMPT_METADATA.promptGuidelines,
|
|
6189
6874
|
parameters: Type12.Object({
|
|
6190
6875
|
path: filePathParam(),
|
|
6191
|
-
node: Type12.Optional(Type12.String({ description:
|
|
6192
|
-
page: optionalIntOrString(
|
|
6876
|
+
node: Type12.Optional(Type12.String({ description: PARAM_DESCRIPTIONS.node })),
|
|
6877
|
+
page: optionalIntOrString(PARAM_DESCRIPTIONS.page),
|
|
6193
6878
|
kind: Type12.Optional(Type12.Union(NODE_KINDS.map((kind) => Type12.Literal(kind)), {
|
|
6194
|
-
description:
|
|
6879
|
+
description: PARAM_DESCRIPTIONS.kind
|
|
6195
6880
|
})),
|
|
6196
|
-
depth: optionalIntOrString(
|
|
6197
|
-
visionMode: Type12.Optional(Type12.Union(
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
Type12.Literal("caption"),
|
|
6201
|
-
Type12.Literal("objects")
|
|
6202
|
-
], { description: "Analyze selected assets: all, ocr, caption, or objects" })),
|
|
6203
|
-
visionLevel: Type12.Optional(Type12.Union([
|
|
6204
|
-
Type12.Literal("low"),
|
|
6205
|
-
Type12.Literal("medium"),
|
|
6206
|
-
Type12.Literal("high")
|
|
6207
|
-
], { description: "Vision inference level: low (default), medium, or high" })),
|
|
6208
|
-
outline: Type12.Optional(Type12.Boolean({ description: "Return outline nodes only" }))
|
|
6881
|
+
depth: optionalIntOrString(PARAM_DESCRIPTIONS.depth),
|
|
6882
|
+
visionMode: Type12.Optional(Type12.Union(VISION_ANALYSIS_MODES.map((mode) => Type12.Literal(mode)), { description: PARAM_DESCRIPTIONS.visionModeView })),
|
|
6883
|
+
visionLevel: Type12.Optional(Type12.Union(VISION_LEVELS.map((level) => Type12.Literal(level)), { description: PARAM_DESCRIPTIONS.visionLevel })),
|
|
6884
|
+
outline: Type12.Optional(Type12.Boolean({ description: PARAM_DESCRIPTIONS.outline }))
|
|
6209
6885
|
}),
|
|
6210
6886
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
6211
6887
|
return executeView({ params, signal, cwd: ctx.cwd });
|