pi-readseek 0.4.30 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -44
- package/index.ts +45 -7
- package/package.json +2 -2
- package/prompts/def.md +2 -9
- package/prompts/edit.md +13 -13
- package/prompts/grep.md +3 -3
- package/prompts/hover.md +2 -6
- package/prompts/read.md +3 -3
- package/prompts/refs.md +1 -1
- package/prompts/rename.md +3 -7
- package/prompts/sg.md +1 -1
- package/prompts/write.md +5 -5
- package/src/def.ts +12 -19
- package/src/edit-output.ts +3 -3
- package/src/edit-syntax-validate.ts +3 -3
- package/src/edit.ts +18 -25
- package/src/file-map.ts +2 -2
- package/src/grep-budget.ts +7 -51
- package/src/grep-output.ts +4 -4
- package/src/grep.ts +19 -26
- package/src/hover.ts +4 -8
- package/src/read-output.ts +4 -4
- package/src/read.ts +30 -43
- package/src/readseek-client.ts +111 -79
- package/src/readseek-params.ts +1 -1
- package/src/readseek-settings.ts +144 -61
- package/src/readseek-value.ts +4 -4
- package/src/refs-output.ts +3 -3
- package/src/refs.ts +6 -10
- package/src/register-tool.ts +4 -42
- package/src/rename.ts +6 -10
- package/src/replace-symbol.ts +2 -2
- package/src/sg-output.ts +3 -3
- package/src/sg.ts +21 -25
- package/src/syntax-validate-mode.ts +3 -13
- package/src/tool-prompt-metadata.ts +13 -13
- package/src/tui-render-utils.ts +2 -2
- package/src/write.ts +22 -26
|
@@ -1,25 +1,15 @@
|
|
|
1
|
+
import { resolveReadSeekSyntaxValidation } from "./readseek-settings.js";
|
|
2
|
+
|
|
1
3
|
export type SyntaxValidateMode = "warn" | "block" | "off";
|
|
2
4
|
|
|
3
5
|
export interface SyntaxValidateOptions {
|
|
4
6
|
syntaxValidate?: SyntaxValidateMode;
|
|
5
7
|
}
|
|
6
8
|
|
|
7
|
-
const VALID = new Set<SyntaxValidateMode>(["warn", "block", "off"]);
|
|
8
9
|
const DEFAULT: SyntaxValidateMode = "warn";
|
|
9
10
|
|
|
10
|
-
function coerce(value: unknown): SyntaxValidateMode | undefined {
|
|
11
|
-
if (typeof value !== "string") return undefined;
|
|
12
|
-
return VALID.has(value as SyntaxValidateMode)
|
|
13
|
-
? (value as SyntaxValidateMode)
|
|
14
|
-
: undefined;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
11
|
export function resolveSyntaxValidateMode(
|
|
18
12
|
opts: SyntaxValidateOptions,
|
|
19
13
|
): SyntaxValidateMode {
|
|
20
|
-
|
|
21
|
-
if (fromOpt) return fromOpt;
|
|
22
|
-
const fromEnv = coerce(process.env.READSEEK_SYNTAX_VALIDATE);
|
|
23
|
-
if (fromEnv) return fromEnv;
|
|
24
|
-
return DEFAULT;
|
|
14
|
+
return opts.syntaxValidate ?? resolveReadSeekSyntaxValidation() ?? DEFAULT;
|
|
25
15
|
}
|
|
@@ -3,8 +3,8 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
|
|
5
5
|
const COMPACT_DESCRIPTIONS: Record<string, string> = {
|
|
6
|
-
"read.md": "Read text files/images by path; text has LINE:HASH anchors, images return the attachment plus OCR
|
|
7
|
-
"edit.md": "Edit existing text files using fresh LINE:HASH anchors from
|
|
6
|
+
"read.md": "Read text files/images by path; text has LINE:HASH anchors, images return the attachment plus OCR text, a caption, and detected objects.",
|
|
7
|
+
"edit.md": "Edit existing text files using fresh LINE:HASH anchors from readSeek_read, readSeek_grep, readSeek_search, or readSeek_write.",
|
|
8
8
|
"grep.md": "Search file contents; non-summary results include LINE:HASH anchors for edits.",
|
|
9
9
|
|
|
10
10
|
"write.md": "Create or overwrite a complete file and return anchors.",
|
|
@@ -14,30 +14,30 @@ const COMPACT_DESCRIPTIONS: Record<string, string> = {
|
|
|
14
14
|
|
|
15
15
|
const COMPACT_GUIDELINES: Record<string, string[]> = {
|
|
16
16
|
"read.md": [
|
|
17
|
-
"Use
|
|
17
|
+
"Use readSeek_read for file contents, images/screenshots, ranges, symbols, and edit anchors.",
|
|
18
18
|
"Use map or symbol mode before pulling large code files into context.",
|
|
19
|
-
"Use
|
|
19
|
+
"Use readSeek_read for images; it returns the image attachment plus OCR text, a caption, and detected objects, so you don't need separate OCR tools.",
|
|
20
20
|
],
|
|
21
21
|
"edit.md": [
|
|
22
|
-
"Use
|
|
22
|
+
"Use readSeek_edit with fresh LINE:HASH anchors for existing files.",
|
|
23
23
|
"Prefer set_line, replace_lines, and insert_after; use replace only when anchors are impractical.",
|
|
24
24
|
],
|
|
25
25
|
"grep.md": [
|
|
26
|
-
"Use
|
|
27
|
-
"Use
|
|
26
|
+
"Use readSeek_grep for text search and edit-ready matching anchors.",
|
|
27
|
+
"Use readSeek_grep summary mode for broad count/file discovery before narrowing.",
|
|
28
28
|
],
|
|
29
29
|
|
|
30
30
|
"write.md": [
|
|
31
|
-
"Use
|
|
32
|
-
"Use
|
|
31
|
+
"Use readSeek_write to create files or intentionally overwrite whole files.",
|
|
32
|
+
"Use readSeek_edit rather than readSeek_write for small changes or appends to existing files.",
|
|
33
33
|
],
|
|
34
34
|
"sg.md": [
|
|
35
|
-
"Use
|
|
36
|
-
"Use
|
|
35
|
+
"Use readSeek_search for AST-shaped code patterns.",
|
|
36
|
+
"Use readSeek_grep instead of readSeek_search for plain text.",
|
|
37
37
|
],
|
|
38
38
|
"refs.md": [
|
|
39
|
-
"Use
|
|
40
|
-
"Use
|
|
39
|
+
"Use readSeek_refs to find every usage of an identifier before renaming or deleting it.",
|
|
40
|
+
"Use readSeek_refs with scope plus line/column to follow a specific binding instead of every same-named identifier.",
|
|
41
41
|
],
|
|
42
42
|
};
|
|
43
43
|
|
package/src/tui-render-utils.ts
CHANGED
|
@@ -238,10 +238,10 @@ export function renderAnchoredFilesResult(
|
|
|
238
238
|
const textContent = content?.type === "text" ? content.text : "";
|
|
239
239
|
if (isError || result.isError) return renderErrorResult(textContent, { expanded, width });
|
|
240
240
|
|
|
241
|
-
const
|
|
241
|
+
const readSeekValue = (result.details as any)?.readSeekValue as
|
|
242
242
|
| { files: Array<{ path: string; lines: any[] }> }
|
|
243
243
|
| undefined;
|
|
244
|
-
const files =
|
|
244
|
+
const files = readSeekValue?.files ?? [];
|
|
245
245
|
if (files.length === 0) return new Text(summaryLine(labels.emptyLabel), 0, 0);
|
|
246
246
|
|
|
247
247
|
const fileCount = files.length;
|
package/src/write.ts
CHANGED
|
@@ -80,7 +80,7 @@ export interface WriteResult extends WriteDiffFields {
|
|
|
80
80
|
text: string;
|
|
81
81
|
warnings: string[];
|
|
82
82
|
writeState?: "created" | "overwritten";
|
|
83
|
-
|
|
83
|
+
readSeekValue: {
|
|
84
84
|
tool: "write";
|
|
85
85
|
path: string;
|
|
86
86
|
lines: ReadSeekLine[];
|
|
@@ -143,24 +143,24 @@ export async function executeWrite(opts: {
|
|
|
143
143
|
|
|
144
144
|
const { path: filePath, content, map: requestMap, cwd } = opts;
|
|
145
145
|
const warnings: string[] = [];
|
|
146
|
-
const
|
|
146
|
+
const readSeekWarnings: ReadSeekWarning[] = [];
|
|
147
147
|
|
|
148
148
|
if (hasBareCarriageReturn(content)) {
|
|
149
|
-
const message = "File content contains bare CR (\\r) line endings;
|
|
149
|
+
const message = "File content contains bare CR (\\r) line endings; readSeek_write refuses to emit anchors that readSeek_read/readSeek_edit would normalize differently.";
|
|
150
150
|
warnings.push(message);
|
|
151
|
-
|
|
151
|
+
readSeekWarnings.push(buildReadSeekWarning("bare-cr", message));
|
|
152
152
|
return buildToolErrorResult("write", "bare-cr", `Cannot write ${filePath}\n⚠️ ${message}`, {
|
|
153
153
|
path: filePath,
|
|
154
|
-
extra: { lines: [], warnings:
|
|
154
|
+
extra: { lines: [], warnings: readSeekWarnings },
|
|
155
155
|
});
|
|
156
156
|
}
|
|
157
157
|
if (looksLikeBinary(Buffer.from(content, "utf-8"))) {
|
|
158
158
|
const message = "File content appears to be binary.";
|
|
159
159
|
warnings.push(message);
|
|
160
|
-
|
|
160
|
+
readSeekWarnings.push(buildReadSeekWarning("binary-content", message));
|
|
161
161
|
return buildToolErrorResult("write", "binary-content", `Cannot write ${filePath}\n⚠️ ${message} — refusing to write.`, {
|
|
162
162
|
path: filePath,
|
|
163
|
-
extra: { lines: [], warnings:
|
|
163
|
+
extra: { lines: [], warnings: readSeekWarnings },
|
|
164
164
|
});
|
|
165
165
|
}
|
|
166
166
|
|
|
@@ -173,13 +173,13 @@ export async function executeWrite(opts: {
|
|
|
173
173
|
await writeFile(filePath, content, "utf-8");
|
|
174
174
|
// Compute hashlines
|
|
175
175
|
const rawLines = content.split("\n");
|
|
176
|
-
const
|
|
176
|
+
const readSeekLines: ReadSeekLine[] = [];
|
|
177
177
|
const displayLines: string[] = [];
|
|
178
178
|
|
|
179
179
|
for (let i = 0; i < rawLines.length; i++) {
|
|
180
180
|
const lineNum = i + 1;
|
|
181
|
-
const
|
|
182
|
-
|
|
181
|
+
const readSeekLine = buildReadSeekLine(lineNum, rawLines[i]);
|
|
182
|
+
readSeekLines.push(readSeekLine);
|
|
183
183
|
displayLines.push(formatHashlineDisplay(lineNum, rawLines[i]));
|
|
184
184
|
}
|
|
185
185
|
|
|
@@ -188,9 +188,9 @@ export async function executeWrite(opts: {
|
|
|
188
188
|
let text = truncated.content;
|
|
189
189
|
if (truncated.truncated) {
|
|
190
190
|
if (truncated.truncatedBy === "lines") {
|
|
191
|
-
text += `\n[… ${truncated.totalLines - truncated.outputLines} more lines not shown — full anchors in
|
|
191
|
+
text += `\n[… ${truncated.totalLines - truncated.outputLines} more lines not shown — full anchors in readSeekValue]`;
|
|
192
192
|
} else {
|
|
193
|
-
text += "\n[… output truncated at 50 KB — full anchors in
|
|
193
|
+
text += "\n[… output truncated at 50 KB — full anchors in readSeekValue]";
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
|
|
@@ -228,11 +228,11 @@ export async function executeWrite(opts: {
|
|
|
228
228
|
writeState: existedBeforeWrite ? "overwritten" : "created",
|
|
229
229
|
diff: diffResult.diff,
|
|
230
230
|
diffData,
|
|
231
|
-
|
|
231
|
+
readSeekValue: {
|
|
232
232
|
tool: "write",
|
|
233
233
|
path: displayPath,
|
|
234
|
-
lines:
|
|
235
|
-
warnings:
|
|
234
|
+
lines: readSeekLines,
|
|
235
|
+
warnings: readSeekWarnings,
|
|
236
236
|
diff: diffResult.diff,
|
|
237
237
|
diffData,
|
|
238
238
|
...(requestMap ? { map: { appended: mapAppended } } : {}),
|
|
@@ -242,11 +242,7 @@ export async function executeWrite(opts: {
|
|
|
242
242
|
|
|
243
243
|
export function registerWriteTool(pi: ExtensionAPI, options: WriteToolOptions = {}) {
|
|
244
244
|
const tool = registerReadSeekTool(pi, {
|
|
245
|
-
|
|
246
|
-
pythonName: "write",
|
|
247
|
-
defaultExposure: "not-safe-by-default",
|
|
248
|
-
}, {
|
|
249
|
-
name: "write",
|
|
245
|
+
name: "readSeek_write",
|
|
250
246
|
label: "write",
|
|
251
247
|
description: WRITE_PROMPT_METADATA.description,
|
|
252
248
|
promptSnippet: WRITE_PROMPT_METADATA.promptSnippet,
|
|
@@ -275,7 +271,7 @@ export function registerWriteTool(pi: ExtensionAPI, options: WriteToolOptions =
|
|
|
275
271
|
|
|
276
272
|
if (isToolErrorResult(result)) return result;
|
|
277
273
|
|
|
278
|
-
if (result.
|
|
274
|
+
if (result.readSeekValue.lines.length > 0) {
|
|
279
275
|
options.onFileAnchored?.(absolutePath);
|
|
280
276
|
}
|
|
281
277
|
|
|
@@ -285,7 +281,7 @@ export function registerWriteTool(pi: ExtensionAPI, options: WriteToolOptions =
|
|
|
285
281
|
...(result.diff !== undefined ? { diff: result.diff } : {}),
|
|
286
282
|
...(result.diffData !== undefined ? { diffData: result.diffData } : {}),
|
|
287
283
|
...(result.writeState ? { writeState: result.writeState } : {}),
|
|
288
|
-
|
|
284
|
+
readSeekValue: result.readSeekValue,
|
|
289
285
|
warnings: result.warnings,
|
|
290
286
|
},
|
|
291
287
|
};
|
|
@@ -326,7 +322,7 @@ export function registerWriteTool(pi: ExtensionAPI, options: WriteToolOptions =
|
|
|
326
322
|
if (isPartial) return renderPendingResult("pending write", width);
|
|
327
323
|
const details = result.details ?? {};
|
|
328
324
|
const output = result.content?.[0]?.type === "text" ? result.content[0].text : "";
|
|
329
|
-
if (result.isError || details.
|
|
325
|
+
if (result.isError || details.readSeekValue?.ok === false) {
|
|
330
326
|
return renderErrorResult(output, { expanded, width, fallback: "write failed" });
|
|
331
327
|
}
|
|
332
328
|
const diffData = details.diffData;
|
|
@@ -335,12 +331,12 @@ export function registerWriteTool(pi: ExtensionAPI, options: WriteToolOptions =
|
|
|
335
331
|
// instead of a diff body — every line is an add, so the gutter, line
|
|
336
332
|
// numbers, and red/green tinting are noise.
|
|
337
333
|
if (state === "created") {
|
|
338
|
-
const
|
|
339
|
-
const hasContent =
|
|
334
|
+
const readSeekLines = (details.readSeekValue?.lines ?? []) as Array<{ raw: string }>;
|
|
335
|
+
const hasContent = readSeekLines.length > 0;
|
|
340
336
|
const header = summaryLine(state, { hidden: hasContent && !expanded });
|
|
341
337
|
const lines = header.split("\n");
|
|
342
338
|
if (expanded && hasContent) {
|
|
343
|
-
const content =
|
|
339
|
+
const content = readSeekLines.map((l) => l.raw).join("\n");
|
|
344
340
|
lines.push(...formatContentPreviewLines(content, theme));
|
|
345
341
|
}
|
|
346
342
|
return new Text(clampLinesToWidth(lines, width).join("\n"), 0, 0);
|