pi-readseek 0.4.7 → 0.4.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-readseek",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
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": {
package/src/edit-diff.ts CHANGED
@@ -14,6 +14,7 @@ export function detectLineEnding(content: string): "\r\n" | "\n" {
14
14
  lf++;
15
15
  }
16
16
  }
17
+ if (lf === 0 && crlf === 0) return "\n";
17
18
  if (lf === 0) return "\r\n";
18
19
  if (crlf === 0) return "\n";
19
20
  return crlf > lf ? "\r\n" : "\n";
package/src/edit.ts CHANGED
@@ -277,6 +277,7 @@ export async function executeEdit(opts: ExecuteEditOptions): Promise<any> {
277
277
  const replaceSymbolRanges: { start: number; end: number }[] = [];
278
278
  const rsProbeResults: { type: "ok"; content: string; replacement: string; warnings: string[]; range: { start: number; end: number } }[] = [];
279
279
  for (const rs of replaceSymbolEdits) {
280
+ throwIfAborted(signal);
280
281
  const probe = await replaceSymbol({
281
282
  filePath: absolutePath,
282
283
  content: originalNormalized,
package/src/grep.ts CHANGED
@@ -315,6 +315,24 @@ export async function executeGrep(opts: ExecuteGrepOptions): Promise<any> {
315
315
  let parsedCount = 0;
316
316
  let candidateUnparsedCount = 0;
317
317
  const candidateLinePattern = /^.+(?::|-)\d+(?::|-)\s/;
318
+ // Pre-read all matched files with bounded concurrency so the processing
319
+ // loop below hits the cache on every getFileLines call instead of
320
+ // serialising disk I/O.
321
+ if (!p.summary) {
322
+ const pathsToRead = new Set<string>();
323
+ for (const line of textBlock.text.split("\n")) {
324
+ throwIfAborted(signal);
325
+ const parsed = parseGrepOutputLine(line);
326
+ if (parsed && Number.isFinite(parsed.lineNumber) && parsed.lineNumber >= 1) {
327
+ pathsToRead.add(toAbsolutePath(parsed.displayPath));
328
+ }
329
+ }
330
+ const CONCURRENCY = 8;
331
+ const pathList = [...pathsToRead];
332
+ for (let i = 0; i < pathList.length; i += CONCURRENCY) {
333
+ await Promise.all(pathList.slice(i, i + CONCURRENCY).map((p) => getFileLines(p)));
334
+ }
335
+ }
318
336
 
319
337
  const addSummaryMatch = (displayPath: string, absolutePath: string) => {
320
338
  let group = groupsByPath.get(displayPath);
package/src/map-cache.ts CHANGED
@@ -29,11 +29,15 @@ function getMapCacheState(): MapCacheGlobalState {
29
29
  globalObject[MAP_CACHE_STATE_KEY] = state;
30
30
  return state;
31
31
  }
32
+ /** Move an existing entry to the most-recently-used position (Map insertion-order tail). */
33
+ function touchMapEntry<K, V>(map: Map<K, V>, key: K, value: V): void {
34
+ map.delete(key);
35
+ map.set(key, value);
36
+ }
32
37
 
33
38
  function rememberInMemory(absPath: string, entry: CacheEntry): void {
34
39
  const state = getMapCacheState();
35
- if (state.cache.has(absPath)) state.cache.delete(absPath);
36
- state.cache.set(absPath, entry);
40
+ touchMapEntry(state.cache, absPath, entry);
37
41
  if (state.cache.size > state.maxSize) {
38
42
  const oldestKey = state.cache.keys().next().value;
39
43
  if (oldestKey !== undefined) state.cache.delete(oldestKey);
@@ -55,8 +59,7 @@ export async function getOrGenerateMap(absPath: string): Promise<FileMap | null>
55
59
  const state = getMapCacheState();
56
60
  const cached = state.cache.get(absPath);
57
61
  if (cached && cached.mtimeMs === mtimeMs) {
58
- state.cache.delete(absPath);
59
- state.cache.set(absPath, cached);
62
+ touchMapEntry(state.cache, absPath, cached);
60
63
  return cached.map;
61
64
  }
62
65
  const inflight = state.inflight.get(absPath);
package/src/read.ts CHANGED
@@ -91,7 +91,7 @@ export async function executeRead(opts: ExecuteReadOptions): Promise<AgentToolRe
91
91
  const rawBundle = typeof rawParams.bundle === "string" ? rawParams.bundle.trim() : undefined;
92
92
  const requestedMapViaBundle =
93
93
  rawBundle === "map" ||
94
- (rawBundle === "local" && rawParams.symbol === undefined && rawParams.map !== false);
94
+ (rawBundle === "local" && rawParams.symbol === undefined && (rawParams.map ?? true));
95
95
  const p = {
96
96
  ...rawParams,
97
97
  offset: offset.value,
@@ -174,7 +174,8 @@ export async function executeRead(opts: ExecuteReadOptions): Promise<AgentToolRe
174
174
  }
175
175
  const hasBinaryContent = looksLikeBinary(rawBuffer);
176
176
  throwIfAborted(signal);
177
- const normalized = normalizeToLF(stripBom(rawBuffer.toString("utf-8")).text);
177
+ const rawText = rawBuffer.toString("utf-8");
178
+ const normalized = normalizeToLF(stripBom(rawText).text);
178
179
  const allLines = splitReadseekLines(normalized);
179
180
  const total = allLines.length;
180
181
  const structuredWarnings: ReadseekWarning[] = [];
@@ -370,7 +371,7 @@ export async function executeRead(opts: ExecuteReadOptions): Promise<AgentToolRe
370
371
  structuredWarnings.push(buildReadseekWarning("binary-content", warning));
371
372
  }
372
373
 
373
- if (hasBareCarriageReturn(rawBuffer.toString("utf-8"))) {
374
+ if (hasBareCarriageReturn(rawText)) {
374
375
  const warning = "[Warning: file contains bare CR (\\r) line endings — line numbering may be inconsistent with grep and other tools]";
375
376
  structuredWarnings.push(buildReadseekWarning("bare-cr", warning));
376
377
  }
package/src/write.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
2
3
  import { dirname, relative } from "node:path";
3
4
 
4
5
  import { withFileMutationQueue, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
@@ -92,10 +93,11 @@ export interface WriteResult extends WriteDiffFields {
92
93
  };
93
94
  }
94
95
 
95
- function readPreviousTextForDiff(filePath: string): string {
96
+
97
+ async function readPreviousTextForDiff(filePath: string): Promise<string> {
96
98
  try {
97
99
  if (!existsSync(filePath)) return "";
98
- const previous = readFileSync(filePath);
100
+ const previous = await readFile(filePath);
99
101
  if (looksLikeBinary(previous)) return "";
100
102
  return previous.toString("utf-8");
101
103
  } catch {
@@ -223,7 +225,7 @@ export async function executeWrite(opts: {
223
225
  },
224
226
  };
225
227
  }
226
- const previousContent = readPreviousTextForDiff(filePath);
228
+ const previousContent = await readPreviousTextForDiff(filePath);
227
229
  const existedBeforeWrite = existsSync(filePath);
228
230
 
229
231
  // Create parent directories