pi-readseek 0.4.12 → 0.4.14
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/index.ts +1 -1
- package/package.json +2 -2
- package/src/edit-output.ts +3 -3
- package/src/edit-syntax-validate.ts +5 -5
- package/src/edit.ts +38 -6
- package/src/file-map.ts +17 -0
- package/src/grep-budget.ts +3 -3
- package/src/grep-output.ts +8 -8
- package/src/grep-render-helpers.ts +3 -3
- package/src/grep-symbol-scope.ts +4 -4
- package/src/grep.ts +7 -7
- package/src/hashline.ts +5 -5
- package/src/pending-diff-preview.ts +3 -3
- package/src/read-output.ts +21 -21
- package/src/read-render-helpers.ts +2 -2
- package/src/read.ts +43 -31
- package/src/readseek/constants.ts +0 -4
- package/src/readseek/formatter.ts +9 -74
- package/src/readseek/symbol-lookup.ts +1 -43
- package/src/readseek/types.ts +0 -20
- package/src/readseek-client.ts +165 -69
- package/src/readseek-settings.ts +17 -17
- package/src/readseek-value.ts +27 -27
- package/src/refs-output.ts +4 -4
- package/src/refs.ts +10 -10
- package/src/register-tool.ts +10 -10
- package/src/replace-symbol.ts +4 -3
- package/src/sg-output.ts +5 -5
- package/src/sg.ts +12 -12
- package/src/tool-prompt-metadata.ts +5 -5
- package/src/write.ts +18 -33
- package/src/image-detect.ts +0 -47
- package/src/map-cache.ts +0 -82
package/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { registerRefsTool } from "./src/refs.js";
|
|
|
7
7
|
import { registerWriteTool } from "./src/write.js";
|
|
8
8
|
import { SessionAnchors } from "./src/session-anchors.js";
|
|
9
9
|
|
|
10
|
-
export default function
|
|
10
|
+
export default function piReadSeekExtension(pi: ExtensionAPI): void {
|
|
11
11
|
const sessionAnchors = new SessionAnchors();
|
|
12
12
|
const markAnchored = (absolutePath: string) => sessionAnchors.markAnchored(absolutePath);
|
|
13
13
|
const hasFreshAnchors = (absolutePath: string) => sessionAnchors.hasFreshAnchors(absolutePath);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-readseek",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.14",
|
|
4
4
|
"description": "Pi extension for readseek-backed hash-anchored read/edit/grep, structural code maps, structural search, and file exploration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"node": ">=20.0.0"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@jarkkojs/readseek": "^0.4.
|
|
42
|
+
"@jarkkojs/readseek": "^0.4.19",
|
|
43
43
|
"diff": "^8.0.3",
|
|
44
44
|
"ignore": "^7.0.5",
|
|
45
45
|
"picomatch": "^4.0.4",
|
package/src/edit-output.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { countEditTypes, parseDiffStats } from "./edit-render-helpers.js";
|
|
2
|
-
import {
|
|
2
|
+
import { buildReadSeekEditResult, type SemanticSummary } from "./readseek-value.js";
|
|
3
3
|
|
|
4
4
|
import type { DiffData } from "./diff-data.js";
|
|
5
5
|
export interface BuildEditOutputInput {
|
|
@@ -17,7 +17,7 @@ export interface BuildEditOutputInput {
|
|
|
17
17
|
export interface EditOutputResult {
|
|
18
18
|
text: string;
|
|
19
19
|
patch: string;
|
|
20
|
-
readseekValue: ReturnType<typeof
|
|
20
|
+
readseekValue: ReturnType<typeof buildReadSeekEditResult>;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const EDIT_OPERATION_NAMES = ["set_line", "replace_lines", "insert_after", "replace"] as const;
|
|
@@ -105,7 +105,7 @@ export function buildEditOutput(input: BuildEditOutputInput): EditOutputResult {
|
|
|
105
105
|
return {
|
|
106
106
|
text,
|
|
107
107
|
patch: input.patch ?? "",
|
|
108
|
-
readseekValue:
|
|
108
|
+
readseekValue: buildReadSeekEditResult({
|
|
109
109
|
path: input.path,
|
|
110
110
|
summary,
|
|
111
111
|
diff: input.diff,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readseekCheck, type
|
|
1
|
+
import { readseekCheck, type ReadSeekCheckOutput, type ReadSeekDiagnostic } from "./readseek-client.js";
|
|
2
2
|
|
|
3
3
|
export interface ValidateInput {
|
|
4
4
|
filePath: string;
|
|
@@ -12,7 +12,7 @@ export interface ValidateResult {
|
|
|
12
12
|
newMissingCount: number;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
function dedupeSortLines(diagnostics:
|
|
15
|
+
function dedupeSortLines(diagnostics: ReadSeekDiagnostic[]): string[] {
|
|
16
16
|
const seen = new Set<string>();
|
|
17
17
|
const out: Array<{ key: string; start: number }> = [];
|
|
18
18
|
for (const diagnostic of diagnostics) {
|
|
@@ -29,7 +29,7 @@ function dedupeSortLines(diagnostics: ReadseekDiagnostic[]): string[] {
|
|
|
29
29
|
return out.map((o) => o.key);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
const EMPTY:
|
|
32
|
+
const EMPTY: ReadSeekCheckOutput = { errorCount: 0, missingCount: 0, diagnostics: [] };
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Compare parse diagnostics between `before` and `after` and report any newly
|
|
@@ -42,8 +42,8 @@ const EMPTY: ReadseekCheckOutput = { errorCount: 0, missingCount: 0, diagnostics
|
|
|
42
42
|
export async function validateSyntaxRegression(
|
|
43
43
|
input: ValidateInput,
|
|
44
44
|
): Promise<ValidateResult | null> {
|
|
45
|
-
let before:
|
|
46
|
-
let after:
|
|
45
|
+
let before: ReadSeekCheckOutput;
|
|
46
|
+
let after: ReadSeekCheckOutput;
|
|
47
47
|
try {
|
|
48
48
|
before = input.before === undefined ? EMPTY : await readseekCheck(input.filePath, input.before);
|
|
49
49
|
after = await readseekCheck(input.filePath, input.after);
|
package/src/edit.ts
CHANGED
|
@@ -15,8 +15,8 @@ import { throwIfAborted } from "./runtime.js";
|
|
|
15
15
|
import { buildEditOutput } from "./edit-output.js";
|
|
16
16
|
import { classifyEdit, isDifftAvailable, runDifftastic } from "./edit-classify.js";
|
|
17
17
|
import type { SemanticSummary } from "./readseek-value.js";
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
18
|
+
import { buildReadSeekError } from "./readseek-value.js";
|
|
19
|
+
import { classifyReadSeekFailure } from "./readseek-client.js";
|
|
20
20
|
import { countEditTypes, formatEditCallText, formatEditResultText } from "./edit-render-helpers.js";
|
|
21
21
|
import { validateSyntaxRegression } from "./edit-syntax-validate.js";
|
|
22
22
|
import { resolveSyntaxValidateMode, type SyntaxValidateOptions } from "./syntax-validate-mode.js";
|
|
@@ -26,7 +26,7 @@ import { buildDiffData, type DiffBlockRange } from "./diff-data.js";
|
|
|
26
26
|
import { clampLineToWidth, clampLinesToWidth, linkToolPath, renderPendingResult, resolveRenderResultContext, summaryLine } from "./tui-render-utils.js";
|
|
27
27
|
import { DiffPreviewComponent } from "./tui-diff-component.js";
|
|
28
28
|
import type { FreshAnchorsPredicate } from "./tool-types.js";
|
|
29
|
-
import {
|
|
29
|
+
import { registerReadSeekTool } from "./register-tool.js";
|
|
30
30
|
|
|
31
31
|
import { resolveEditDiffDisplay } from "./readseek-settings.js";
|
|
32
32
|
|
|
@@ -104,7 +104,7 @@ function buildEditError(
|
|
|
104
104
|
tool: "edit",
|
|
105
105
|
ok: false,
|
|
106
106
|
path,
|
|
107
|
-
error:
|
|
107
|
+
error: buildReadSeekError(code, message, hint, errorDetails),
|
|
108
108
|
},
|
|
109
109
|
} as EditToolDetails & { readseekValue: any },
|
|
110
110
|
};
|
|
@@ -295,7 +295,7 @@ export async function executeEdit(opts: ExecuteEditOptions): Promise<any> {
|
|
|
295
295
|
}
|
|
296
296
|
} catch (err) {
|
|
297
297
|
throwIfAborted(signal);
|
|
298
|
-
const failure =
|
|
298
|
+
const failure = classifyReadSeekFailure(err);
|
|
299
299
|
return buildEditError(absolutePath, failure.code, failure.message, failure.hint);
|
|
300
300
|
}
|
|
301
301
|
|
|
@@ -351,6 +351,38 @@ export async function executeEdit(opts: ExecuteEditOptions): Promise<any> {
|
|
|
351
351
|
}
|
|
352
352
|
}
|
|
353
353
|
}
|
|
354
|
+
|
|
355
|
+
const anchorIntervals: { lo: number; hi: number }[] = [];
|
|
356
|
+
for (const edit of anchorEdits) {
|
|
357
|
+
try {
|
|
358
|
+
if ("set_line" in edit) {
|
|
359
|
+
const line = parseLineRef(edit.set_line.anchor).line;
|
|
360
|
+
anchorIntervals.push({ lo: line, hi: line });
|
|
361
|
+
} else if ("replace_lines" in edit) {
|
|
362
|
+
const start = parseLineRef(edit.replace_lines.start_anchor).line;
|
|
363
|
+
const end = parseLineRef(edit.replace_lines.end_anchor).line;
|
|
364
|
+
anchorIntervals.push({ lo: Math.min(start, end), hi: Math.max(start, end) });
|
|
365
|
+
}
|
|
366
|
+
} catch {
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
const seenAnchorIntervals = new Set<string>();
|
|
370
|
+
const uniqueAnchorIntervals: { lo: number; hi: number }[] = [];
|
|
371
|
+
for (const interval of anchorIntervals) {
|
|
372
|
+
const key = `${interval.lo}:${interval.hi}`;
|
|
373
|
+
if (seenAnchorIntervals.has(key)) continue;
|
|
374
|
+
seenAnchorIntervals.add(key);
|
|
375
|
+
uniqueAnchorIntervals.push(interval);
|
|
376
|
+
}
|
|
377
|
+
uniqueAnchorIntervals.sort((a, b) => a.lo - b.lo || a.hi - b.hi);
|
|
378
|
+
for (let i = 1; i < uniqueAnchorIntervals.length; i++) {
|
|
379
|
+
const prev = uniqueAnchorIntervals[i - 1];
|
|
380
|
+
const current = uniqueAnchorIntervals[i];
|
|
381
|
+
if (current.lo <= prev.hi) {
|
|
382
|
+
const message = `Anchored edits overlap (lines ${prev.lo}-${prev.hi} and ${current.lo}-${current.hi}). Split into separate, non-overlapping edits or re-read for fresh anchors.`;
|
|
383
|
+
return buildEditError(absolutePath, "invalid-edit-variant", message);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
354
386
|
// Apply pass: reuse all probe results. The probe pass resolved every
|
|
355
387
|
// replace_symbol against originalNormalized; apply those replacements in
|
|
356
388
|
// reverse source order so original line ranges stay valid and no second
|
|
@@ -569,7 +601,7 @@ export async function executeEdit(opts: ExecuteEditOptions): Promise<any> {
|
|
|
569
601
|
|
|
570
602
|
|
|
571
603
|
export function registerEditTool(pi: ExtensionAPI, options: EditToolOptions = {}) {
|
|
572
|
-
const tool =
|
|
604
|
+
const tool = registerReadSeekTool(pi, {
|
|
573
605
|
policy: "mutating",
|
|
574
606
|
pythonName: "edit",
|
|
575
607
|
defaultExposure: "not-safe-by-default",
|
package/src/file-map.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
import type { FileMap } from "./readseek/types.js";
|
|
4
|
+
import { readseekMap } from "./readseek-client.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Fetch a structural file map from readseek, which maintains its own on-disk
|
|
8
|
+
* map cache keyed by file hash. Returns null on any failure — never throws.
|
|
9
|
+
*/
|
|
10
|
+
export async function getOrGenerateMap(absPath: string): Promise<FileMap | null> {
|
|
11
|
+
try {
|
|
12
|
+
const { size } = await stat(absPath);
|
|
13
|
+
return await readseekMap(absPath, size);
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
package/src/grep-budget.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import {
|
|
2
|
+
import { resolveReadSeekJsonSettings } from "./readseek-settings.js";
|
|
3
3
|
|
|
4
4
|
const POSITIVE_BASE10_INT = /^[1-9][0-9]*$/;
|
|
5
5
|
|
|
@@ -15,7 +15,7 @@ const POSITIVE_BASE10_INT = /^[1-9][0-9]*$/;
|
|
|
15
15
|
*
|
|
16
16
|
* Returns `undefined` on rejection; never throws.
|
|
17
17
|
*/
|
|
18
|
-
|
|
18
|
+
function parsePositiveBase10Int(raw: string | undefined | null): number | undefined {
|
|
19
19
|
if (raw === undefined || raw === null) return undefined;
|
|
20
20
|
const trimmed = String(raw).trim();
|
|
21
21
|
if (!POSITIVE_BASE10_INT.test(trimmed)) return undefined;
|
|
@@ -63,7 +63,7 @@ function resolveDimension(rawEnvValue: string | undefined, jsonValue: number | u
|
|
|
63
63
|
* are used as-is.
|
|
64
64
|
*/
|
|
65
65
|
export function resolveGrepOutputBudget(): GrepOutputBudget {
|
|
66
|
-
const settings =
|
|
66
|
+
const settings = resolveReadSeekJsonSettings().settings.grep;
|
|
67
67
|
return {
|
|
68
68
|
maxLines: resolveDimension(
|
|
69
69
|
process.env.READSEEK_GREP_MAX_LINES,
|
package/src/grep-output.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReadSeekLine, ReadSeekWarning } from "./readseek-value.js";
|
|
2
2
|
import {
|
|
3
3
|
formatSize,
|
|
4
4
|
truncateHead,
|
|
5
5
|
} from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import { resolveGrepOutputBudget } from "./grep-budget.js";
|
|
7
7
|
|
|
8
|
-
export interface GrepOutputRecord extends
|
|
8
|
+
export interface GrepOutputRecord extends ReadSeekLine {
|
|
9
9
|
path: string;
|
|
10
10
|
kind: "match" | "context";
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
interface GrepOutputReadSeekRecord {
|
|
14
14
|
path: string;
|
|
15
15
|
line: number;
|
|
16
16
|
anchor: string;
|
|
@@ -18,7 +18,7 @@ export interface GrepOutputReadseekRecord {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export type GrepOutputEntry =
|
|
21
|
-
| { kind: "match" | "context"; line:
|
|
21
|
+
| { kind: "match" | "context"; line: ReadSeekLine }
|
|
22
22
|
| { kind: "separator"; text: string };
|
|
23
23
|
|
|
24
24
|
export interface GrepOutputScopeSymbol {
|
|
@@ -29,7 +29,7 @@ export interface GrepOutputScopeSymbol {
|
|
|
29
29
|
parentName?: string;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export interface GrepScopeWarning extends
|
|
32
|
+
export interface GrepScopeWarning extends ReadSeekWarning {
|
|
33
33
|
path?: string;
|
|
34
34
|
line?: number;
|
|
35
35
|
}
|
|
@@ -55,7 +55,7 @@ function hasScope(group: GrepOutputGroup): group is ScopedGrepOutputGroup {
|
|
|
55
55
|
return group.scope !== undefined;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
interface BuildGrepOutputInput {
|
|
59
59
|
summary: boolean;
|
|
60
60
|
totalMatches: number;
|
|
61
61
|
groups: GrepOutputGroup[];
|
|
@@ -66,13 +66,13 @@ export interface BuildGrepOutputInput {
|
|
|
66
66
|
passthroughLines?: string[];
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
interface GrepOutputResult {
|
|
70
70
|
text: string;
|
|
71
71
|
readseekValue: {
|
|
72
72
|
tool: "grep";
|
|
73
73
|
summary: boolean;
|
|
74
74
|
totalMatches: number;
|
|
75
|
-
records:
|
|
75
|
+
records: GrepOutputReadSeekRecord[];
|
|
76
76
|
scopes?: {
|
|
77
77
|
mode: "symbol";
|
|
78
78
|
groups: Array<{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
interface GrepCallTextResult {
|
|
2
2
|
pattern: string;
|
|
3
3
|
suffix: string | undefined;
|
|
4
4
|
}
|
|
@@ -24,7 +24,7 @@ export function formatGrepCallText(
|
|
|
24
24
|
|
|
25
25
|
const GREP_TRUNCATION_THRESHOLD = 50;
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
interface GrepResultTextInput {
|
|
28
28
|
totalMatches: number;
|
|
29
29
|
summary: boolean;
|
|
30
30
|
records: unknown[];
|
|
@@ -34,7 +34,7 @@ export interface GrepResultTextInput {
|
|
|
34
34
|
errorText?: string;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
interface GrepResultTextOutput {
|
|
38
38
|
summary: string;
|
|
39
39
|
badges: string[];
|
|
40
40
|
noMatches: boolean;
|
package/src/grep-symbol-scope.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildReadSeekLine } from "./readseek-value.js";
|
|
2
2
|
import type { GrepOutputEntry, GrepOutputGroup, GrepOutputScopeSymbol, GrepScopeWarning } from "./grep-output.js";
|
|
3
3
|
import type { FileMap, FileSymbol } from "./readseek/types.js";
|
|
4
4
|
|
|
@@ -49,7 +49,7 @@ function buildSymbolEntries(
|
|
|
49
49
|
if (scopeContext === undefined) {
|
|
50
50
|
const entries: GrepOutputEntry[] = [];
|
|
51
51
|
for (let lineNumber = symbol.startLine; lineNumber <= symbol.endLine; lineNumber++) {
|
|
52
|
-
const built =
|
|
52
|
+
const built = buildReadSeekLine(lineNumber, fileLines[lineNumber - 1] ?? "");
|
|
53
53
|
entries.push({ kind: matchLines.has(lineNumber) ? "match" : "context", line: built });
|
|
54
54
|
}
|
|
55
55
|
return entries;
|
|
@@ -73,7 +73,7 @@ function buildSymbolEntries(
|
|
|
73
73
|
if (i > 0) entries.push({ kind: "separator", text: "--" });
|
|
74
74
|
const range = merged[i];
|
|
75
75
|
for (let ln = range.startLine; ln <= range.endLine; ln++) {
|
|
76
|
-
const built =
|
|
76
|
+
const built = buildReadSeekLine(ln, fileLines[ln - 1] ?? "");
|
|
77
77
|
entries.push({ kind: matchLines.has(ln) ? "match" : "context", line: built });
|
|
78
78
|
}
|
|
79
79
|
}
|
|
@@ -86,7 +86,7 @@ function buildFallbackEntries(fileLines: string[], matchLines: number[], context
|
|
|
86
86
|
const start = Math.max(1, matchLine - contextLines);
|
|
87
87
|
const end = Math.min(fileLines.length, matchLine + contextLines);
|
|
88
88
|
for (let lineNumber = start; lineNumber <= end; lineNumber++) {
|
|
89
|
-
const built =
|
|
89
|
+
const built = buildReadSeekLine(lineNumber, fileLines[lineNumber - 1] ?? "");
|
|
90
90
|
const candidate: GrepOutputEntry = { kind: lineNumber === matchLine ? "match" : "context", line: built };
|
|
91
91
|
const existing = lineMap.get(lineNumber);
|
|
92
92
|
if (!existing || (existing.kind === "context" && candidate.kind === "match")) {
|
package/src/grep.ts
CHANGED
|
@@ -10,10 +10,10 @@ import { defineToolPromptMetadata } from "./tool-prompt-metadata.js";
|
|
|
10
10
|
import { normalizeToLF, stripBom, hasBareCarriageReturn } from "./edit-diff.js";
|
|
11
11
|
import { looksLikeBinary } from "./binary-detect.js";
|
|
12
12
|
import { ensureHashInit, escapeControlCharsForDisplay } from "./hashline.js";
|
|
13
|
-
import {
|
|
13
|
+
import { buildReadSeekLine, buildToolErrorResult, type ReadSeekLine } from "./readseek-value.js";
|
|
14
14
|
import { buildGrepOutput, type GrepOutputEntry, type GrepOutputGroup, type GrepOutputRecord, type GrepScopeWarning } from "./grep-output.js";
|
|
15
15
|
|
|
16
|
-
import { getOrGenerateMap } from "./map
|
|
16
|
+
import { getOrGenerateMap } from "./file-map.js";
|
|
17
17
|
import { scopeGrepGroupsToSymbols } from "./grep-symbol-scope.js";
|
|
18
18
|
import { resolveToCwd } from "./path-utils.js";
|
|
19
19
|
import { throwIfAborted } from "./runtime.js";
|
|
@@ -21,7 +21,7 @@ import { formatGrepCallText, formatGrepResultText } from "./grep-render-helpers.
|
|
|
21
21
|
import { coerceObviousBase10Int } from "./coerce-obvious-int.js";
|
|
22
22
|
import { clampLineToWidth, clampLinesToWidth, linkToolPath, renderErrorResult, renderPendingResult, renderToolLabel, resolveRenderResultContext, summaryLine } from "./tui-render-utils.js";
|
|
23
23
|
import type { FileAnchoredCallback } from "./tool-types.js";
|
|
24
|
-
import {
|
|
24
|
+
import { registerReadSeekTool } from "./register-tool.js";
|
|
25
25
|
|
|
26
26
|
const GREP_PROMPT_METADATA = defineToolPromptMetadata({
|
|
27
27
|
promptUrl: new URL("../prompts/grep.md", import.meta.url),
|
|
@@ -344,7 +344,7 @@ export async function executeGrep(opts: ExecuteGrepOptions): Promise<any> {
|
|
|
344
344
|
totalMatches++;
|
|
345
345
|
};
|
|
346
346
|
|
|
347
|
-
const addEntry = (displayPath: string, absolutePath: string, kind: "match" | "context", line:
|
|
347
|
+
const addEntry = (displayPath: string, absolutePath: string, kind: "match" | "context", line: ReadSeekLine) => {
|
|
348
348
|
let group = groupsByPath.get(displayPath);
|
|
349
349
|
if (!group) {
|
|
350
350
|
group = { displayPath, absolutePath, matchCount: 0, entries: [] };
|
|
@@ -400,7 +400,7 @@ export async function executeGrep(opts: ExecuteGrepOptions): Promise<any> {
|
|
|
400
400
|
let emitted = false;
|
|
401
401
|
for (let i = 0; i < fileLines.length; i++) {
|
|
402
402
|
if (!patternRe.test(fileLines[i])) continue;
|
|
403
|
-
addEntry(parsed.displayPath, absolute, "match",
|
|
403
|
+
addEntry(parsed.displayPath, absolute, "match", buildReadSeekLine(i + 1, fileLines[i]));
|
|
404
404
|
emitted = true;
|
|
405
405
|
}
|
|
406
406
|
if (emitted) continue;
|
|
@@ -409,7 +409,7 @@ export async function executeGrep(opts: ExecuteGrepOptions): Promise<any> {
|
|
|
409
409
|
}
|
|
410
410
|
// Normal (non-bare-CR) path
|
|
411
411
|
const sourceLine = fileLines[parsed.lineNumber - 1] ?? parsed.text;
|
|
412
|
-
const built =
|
|
412
|
+
const built = buildReadSeekLine(parsed.lineNumber, sourceLine);
|
|
413
413
|
addEntry(parsed.displayPath, absolute, parsed.kind, {
|
|
414
414
|
...built,
|
|
415
415
|
display: escapeControlCharsForDisplay(parsed.text),
|
|
@@ -529,7 +529,7 @@ export async function executeGrep(opts: ExecuteGrepOptions): Promise<any> {
|
|
|
529
529
|
}
|
|
530
530
|
|
|
531
531
|
export function registerGrepTool(pi: ExtensionAPI, options: GrepToolOptions = {}) {
|
|
532
|
-
const tool =
|
|
532
|
+
const tool = registerReadSeekTool(pi, {
|
|
533
533
|
policy: "read-only",
|
|
534
534
|
pythonName: "grep",
|
|
535
535
|
defaultExposure: "safe-by-default",
|
package/src/hashline.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import xxhashWasm from "xxhash-wasm";
|
|
9
9
|
import { throwIfAborted } from "./runtime.js";
|
|
10
|
-
import type {
|
|
10
|
+
import type { ReadSeekLine } from "./readseek-value.js";
|
|
11
11
|
import { CONFUSABLE_HYPHENS_RE } from "./confusable-hyphens.js";
|
|
12
12
|
|
|
13
13
|
|
|
@@ -25,9 +25,9 @@ interface HashMismatch {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export class HashlineMismatchError extends Error {
|
|
28
|
-
readonly updatedAnchors:
|
|
28
|
+
readonly updatedAnchors: ReadSeekLine[];
|
|
29
29
|
|
|
30
|
-
constructor(message: string, updatedAnchors:
|
|
30
|
+
constructor(message: string, updatedAnchors: ReadSeekLine[]) {
|
|
31
31
|
super(message);
|
|
32
32
|
this.name = "HashlineMismatchError";
|
|
33
33
|
this.updatedAnchors = updatedAnchors;
|
|
@@ -175,10 +175,10 @@ function formatMismatchError(
|
|
|
175
175
|
mismatches: HashMismatch[],
|
|
176
176
|
fileLines: string[],
|
|
177
177
|
relocationWindow: number,
|
|
178
|
-
): { message: string; updatedAnchors:
|
|
178
|
+
): { message: string; updatedAnchors: ReadSeekLine[] } {
|
|
179
179
|
const mismatchSet = new Map<number, HashMismatch>();
|
|
180
180
|
for (const m of mismatches) mismatchSet.set(m.line, m);
|
|
181
|
-
const updatedAnchors:
|
|
181
|
+
const updatedAnchors: ReadSeekLine[] = mismatches.map((m) => {
|
|
182
182
|
const raw = fileLines[m.line - 1] ?? "";
|
|
183
183
|
const hash = computeLineHash(raw);
|
|
184
184
|
return {
|
|
@@ -4,9 +4,9 @@ import { generateDiffString, normalizeToLF, replaceText } from "./edit-diff.js";
|
|
|
4
4
|
import { applyHashlineEdits, type HashlineEditItem } from "./hashline.js";
|
|
5
5
|
import { replaceSymbol } from "./replace-symbol.js";
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
const PENDING_DIFF_MAX_BYTES = 1024 * 1024;
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
interface PendingDiffPreviewData {
|
|
10
10
|
filePath: string;
|
|
11
11
|
previousContent: string;
|
|
12
12
|
nextContent: string;
|
|
@@ -204,7 +204,7 @@ export async function buildPendingEditPreviewData(input: PendingEditInput, cwd:
|
|
|
204
204
|
return buildData(resolved.path, previous.content, next, true, "pending edit");
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
|
|
207
|
+
interface PendingDiffPreviewCacheSlot<T = PendingDiffPreviewResult> {
|
|
208
208
|
key?: string;
|
|
209
209
|
data?: T;
|
|
210
210
|
pending?: boolean;
|
package/src/read-output.ts
CHANGED
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
formatSize,
|
|
5
5
|
truncateHead,
|
|
6
6
|
} from "@earendil-works/pi-coding-agent";
|
|
7
|
-
import {
|
|
7
|
+
import { buildReadSeekLines, renderReadSeekLines, type ReadSeekLine, type ReadSeekWarning } from "./readseek-value.js";
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
interface ReadSymbolMetadata {
|
|
11
11
|
query: string;
|
|
12
12
|
name: string;
|
|
13
13
|
kind: string;
|
|
@@ -16,43 +16,43 @@ export interface ReadSymbolMetadata {
|
|
|
16
16
|
endLine: number;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
interface ReadTruncationMetadata {
|
|
20
20
|
outputLines: number;
|
|
21
21
|
totalLines: number;
|
|
22
22
|
outputBytes: number;
|
|
23
23
|
totalBytes: number;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
interface ReadMapMetadata {
|
|
27
27
|
requested: boolean;
|
|
28
28
|
appended: boolean;
|
|
29
29
|
text?: string | null;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
interface ReadContinuationMetadata {
|
|
33
33
|
nextOffset: number;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
interface ReadBundleSupportItem {
|
|
37
37
|
symbol: ReadSymbolMetadata;
|
|
38
38
|
lines: string[];
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
interface ReadBundleMetadata {
|
|
42
42
|
mode: "local";
|
|
43
43
|
applied: boolean;
|
|
44
44
|
localSupport: ReadBundleSupportItem[];
|
|
45
|
-
warnings?:
|
|
45
|
+
warnings?: ReadSeekWarning[];
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
interface ReadOutputInput {
|
|
49
49
|
path: string;
|
|
50
50
|
startLine: number;
|
|
51
51
|
endLine: number;
|
|
52
52
|
totalLines: number;
|
|
53
53
|
selectedLines?: string[];
|
|
54
|
-
lines?:
|
|
55
|
-
warnings?:
|
|
54
|
+
lines?: ReadSeekLine[];
|
|
55
|
+
warnings?: ReadSeekWarning[];
|
|
56
56
|
truncation?: ReadTruncationMetadata | null;
|
|
57
57
|
continuation?: ReadContinuationMetadata | null;
|
|
58
58
|
symbol?: ReadSymbolMetadata | null;
|
|
@@ -60,9 +60,9 @@ export interface ReadOutputInput {
|
|
|
60
60
|
bundle?: ReadBundleMetadata | null;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
interface ReadOutputResult {
|
|
64
64
|
text: string;
|
|
65
|
-
lines:
|
|
65
|
+
lines: ReadSeekLine[];
|
|
66
66
|
readseekValue: {
|
|
67
67
|
tool: "read";
|
|
68
68
|
path: string;
|
|
@@ -71,14 +71,14 @@ export interface ReadOutputResult {
|
|
|
71
71
|
endLine: number;
|
|
72
72
|
totalLines: number;
|
|
73
73
|
};
|
|
74
|
-
warnings:
|
|
74
|
+
warnings: ReadSeekWarning[];
|
|
75
75
|
truncation: ReadTruncationMetadata | null;
|
|
76
76
|
symbol: ReadSymbolMetadata | null;
|
|
77
77
|
map: {
|
|
78
78
|
requested: boolean;
|
|
79
79
|
appended: boolean;
|
|
80
80
|
};
|
|
81
|
-
lines:
|
|
81
|
+
lines: ReadSeekLine[];
|
|
82
82
|
bundle?: {
|
|
83
83
|
mode: "local";
|
|
84
84
|
applied: boolean;
|
|
@@ -90,16 +90,16 @@ export interface ReadOutputResult {
|
|
|
90
90
|
endLine: number;
|
|
91
91
|
lineAnchors: string[];
|
|
92
92
|
}>;
|
|
93
|
-
warnings:
|
|
93
|
+
warnings: ReadSeekWarning[];
|
|
94
94
|
};
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
export function buildReadOutput(input: ReadOutputInput): ReadOutputResult {
|
|
99
99
|
const selectedLines = input.selectedLines ?? input.lines?.map((line) => line.raw) ?? [];
|
|
100
|
-
const lines = input.lines ??
|
|
100
|
+
const lines = input.lines ?? buildReadSeekLines(input.startLine, selectedLines);
|
|
101
101
|
const warnings = input.warnings ?? [];
|
|
102
|
-
const renderedLines =
|
|
102
|
+
const renderedLines = renderReadSeekLines(lines);
|
|
103
103
|
const truncated = truncateHead(renderedLines, {
|
|
104
104
|
maxLines: DEFAULT_MAX_LINES,
|
|
105
105
|
maxBytes: DEFAULT_MAX_BYTES,
|
|
@@ -115,8 +115,8 @@ export function buildReadOutput(input: ReadOutputInput): ReadOutputResult {
|
|
|
115
115
|
|
|
116
116
|
if (input.bundle?.applied) {
|
|
117
117
|
const supportBlocks = input.bundle.localSupport.map((item) => {
|
|
118
|
-
const supportLines =
|
|
119
|
-
return
|
|
118
|
+
const supportLines = buildReadSeekLines(item.symbol.startLine, item.lines);
|
|
119
|
+
return renderReadSeekLines(supportLines);
|
|
120
120
|
});
|
|
121
121
|
|
|
122
122
|
text = [
|
|
@@ -164,7 +164,7 @@ export function buildReadOutput(input: ReadOutputInput): ReadOutputResult {
|
|
|
164
164
|
mode: input.bundle.mode,
|
|
165
165
|
applied: input.bundle.applied,
|
|
166
166
|
localSupport: input.bundle.localSupport.map((item) => {
|
|
167
|
-
const supportLines =
|
|
167
|
+
const supportLines = buildReadSeekLines(item.symbol.startLine, item.lines);
|
|
168
168
|
return {
|
|
169
169
|
name: item.symbol.name,
|
|
170
170
|
kind: item.symbol.kind,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReadSeekWarning } from "./readseek-value.js";
|
|
2
2
|
export interface ReadCallTextResult {
|
|
3
3
|
path: string | null;
|
|
4
4
|
suffix: string | undefined;
|
|
@@ -41,7 +41,7 @@ export interface ReadResultTextInput {
|
|
|
41
41
|
truncation: { outputLines: number; totalLines: number; outputBytes: number; totalBytes: number } | null;
|
|
42
42
|
symbol: { query: string; name: string; kind: string; parentName?: string; startLine: number; endLine: number } | null;
|
|
43
43
|
map: { requested: boolean; appended: boolean };
|
|
44
|
-
warnings:
|
|
44
|
+
warnings: ReadSeekWarning[];
|
|
45
45
|
isError?: boolean;
|
|
46
46
|
errorText?: string;
|
|
47
47
|
}
|