pi-readseek 0.3.5 → 0.3.6

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.3.5",
3
+ "version": "0.3.6",
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": {
@@ -62,9 +62,6 @@ export function classifyEdit(oldContent: string, newContent: string): EditClassi
62
62
 
63
63
  let difftCachedResult: boolean | null = null;
64
64
 
65
- export function _resetDifftCache(): void {
66
- difftCachedResult = null;
67
- }
68
65
 
69
66
  export async function isDifftAvailable(): Promise<boolean> {
70
67
  if (difftCachedResult !== null) return difftCachedResult;
package/src/hashline.ts CHANGED
@@ -115,18 +115,6 @@ export function formatHashlineDisplay(lineNumber: number, content: string): stri
115
115
  return `${lineNumber}:${computeLineHash(lineNumber, content)}|${escapeControlCharsForDisplay(content)}`;
116
116
  }
117
117
 
118
- export function hashLine(lineNumber: number, content: string): string {
119
- return formatHashlineDisplay(lineNumber, content);
120
- }
121
-
122
- export function hashLines(content: string): string {
123
- return content
124
- .split("\n")
125
- .map((line, i) => formatHashlineDisplay(i + 1, line))
126
- .join("\n");
127
- }
128
-
129
-
130
118
  export function parseLineRef(ref: string): { line: number; hash: string; content?: string } {
131
119
  const contentMatch = ref.match(/^[^|]*\|(.*)$/);
132
120
  const contentAfterPipe = contentMatch ? contentMatch[1] : undefined;
package/src/map-cache.ts CHANGED
@@ -133,20 +133,4 @@ export async function getOrGenerateMap(absPath: string): Promise<FileMap | null>
133
133
  } catch {
134
134
  return null;
135
135
  }
136
- }
137
- export function setMapCacheMaxSize(size: number): void {
138
- getMapCacheState().maxSize = size;
139
- }
140
-
141
- /**
142
- * Clear the map cache. Exported for testing.
143
- */
144
- export function clearMapCache(): void {
145
- const state = getMapCacheState();
146
- state.cache.clear();
147
- state.maxSize = MAP_CACHE_MAX_SIZE;
148
- }
149
-
150
- export function __getInMemoryMapCacheSizeForTest(): number {
151
- return getMapCacheState().cache.size;
152
136
  }
@@ -13,7 +13,7 @@ import { resolveReadseekJsonSettings } from "./readseek-settings.js";
13
13
  * 3. `$XDG_CACHE_HOME/pi-readseek/maps` (when non-empty).
14
14
  * 4. `~/.cache/pi-readseek/maps`.
15
15
  */
16
- export function resolveCacheDir(): string {
16
+ function resolveCacheDir(): string {
17
17
  const explicit = process.env.PI_HASHLINE_MAP_CACHE_DIR;
18
18
  if (explicit && explicit.length > 0) return explicit;
19
19
 
@@ -170,39 +170,22 @@ async function tryWriteCachedRaw(key: string, map: FileMap): Promise<boolean> {
170
170
  }
171
171
  }
172
172
 
173
- /**
174
- * Low-level atomic write. Internal helper (exported so tests can seed fixtures
175
- * without going through the eviction counter).
176
- */
177
- export async function writeCachedRaw(key: string, map: FileMap): Promise<void> {
173
+ /** Low-level atomic write. Internal helper. */
174
+ async function writeCachedRaw(key: string, map: FileMap): Promise<void> {
178
175
  await tryWriteCachedRaw(key, map);
179
176
  }
180
177
 
181
- const DEFAULT_EVICTION_INTERVAL = 20;
182
- const DEFAULT_EVICTION_CAP = 5000;
178
+ const EVICTION_INTERVAL = 20;
179
+ const EVICTION_CAP = 5000;
183
180
  let writeCounter = 0;
184
- let evictionInterval = DEFAULT_EVICTION_INTERVAL;
185
- let evictionCap = DEFAULT_EVICTION_CAP;
186
-
187
- export function __setEvictionHooksForTest(
188
- hooks: { interval?: number; cap?: number } | null,
189
- ): void {
190
- if (hooks === null) {
191
- evictionInterval = DEFAULT_EVICTION_INTERVAL;
192
- evictionCap = DEFAULT_EVICTION_CAP;
193
- return;
194
- }
195
181
 
196
- if (typeof hooks.interval === "number") evictionInterval = hooks.interval;
197
- if (typeof hooks.cap === "number") evictionCap = hooks.cap;
198
- }
199
182
 
200
183
  const CACHE_KEY_FILE = /^[0-9a-f]{32}\.json$/;
201
184
  async function maybeEvict(): Promise<void> {
202
185
  try {
203
186
  const dir = resolveCacheDir();
204
187
  const names = (await readdir(dir)).filter((n) => CACHE_KEY_FILE.test(n));
205
- if (names.length <= evictionCap) return;
188
+ if (names.length <= EVICTION_CAP) return;
206
189
 
207
190
  const entries: Array<{ path: string; age: number }> = [];
208
191
  for (const name of names) {
@@ -217,7 +200,7 @@ async function maybeEvict(): Promise<void> {
217
200
  }
218
201
 
219
202
  entries.sort((a, b) => a.age - b.age);
220
- const excess = entries.length - evictionCap;
203
+ const excess = entries.length - EVICTION_CAP;
221
204
  for (let i = 0; i < excess; i += 1) {
222
205
  try {
223
206
  await unlink(entries[i].path);
@@ -236,7 +219,7 @@ async function maybeEvict(): Promise<void> {
236
219
  export async function writeCached(key: string, map: FileMap): Promise<void> {
237
220
  if (!(await tryWriteCachedRaw(key, map))) return;
238
221
  writeCounter += 1;
239
- if (writeCounter % evictionInterval === 0) {
222
+ if (writeCounter % EVICTION_INTERVAL === 0) {
240
223
  try {
241
224
  await maybeEvict();
242
225
  } catch {
@@ -245,7 +228,3 @@ export async function writeCached(key: string, map: FileMap): Promise<void> {
245
228
  }
246
229
  }
247
230
 
248
- // Test-only: reset the write counter between suites.
249
- export function __resetWriteCounter(): void {
250
- writeCounter = 0;
251
- }
@@ -1,7 +1,7 @@
1
1
  import { stat } from "node:fs/promises";
2
2
 
3
3
  import { readseekMap, readseekMapContent } from "../readseek-client.js";
4
- import { THRESHOLDS } from "./constants.js";
4
+ import { throwIfAborted } from "../runtime.js";
5
5
  import type { FileMap, MapOptions } from "./types.js";
6
6
 
7
7
  export const READSEEK_MAPPER_NAME = "readseek";
@@ -20,17 +20,6 @@ export const READSEEK_MAPPER_IDENTITY: MapperIdentity = {
20
20
  mapperName: READSEEK_MAPPER_NAME,
21
21
  mapperVersion: READSEEK_MAPPER_VERSION,
22
22
  };
23
-
24
- export const ALL_MAPPER_IDENTITIES: Record<string, MapperIdentity> = {
25
- readseek: READSEEK_MAPPER_IDENTITY,
26
- };
27
-
28
- function throwIfAborted(signal?: AbortSignal): void {
29
- if (!signal?.aborted) return;
30
- const reason = signal.reason;
31
- throw reason instanceof Error ? reason : new Error("aborted");
32
- }
33
-
34
23
  export async function generateMapWithIdentity(
35
24
  filePath: string,
36
25
  options: MapOptions = {},
@@ -61,9 +50,3 @@ export async function generateMapFromContent(
61
50
  return map;
62
51
  }
63
52
 
64
- export function shouldGenerateMap(
65
- totalLines: number,
66
- totalBytes: number,
67
- ): boolean {
68
- return totalLines > THRESHOLDS.MAX_LINES || totalBytes > THRESHOLDS.MAX_BYTES;
69
- }
@@ -17,6 +17,3 @@ export function reportParserError(
17
17
  console.error(`[readseek] ${context}: ${causeMessage(err)}`);
18
18
  }
19
19
 
20
- export function __resetParserErrorReporterForTests(): void {
21
- emitted.clear();
22
- }
@@ -76,8 +76,3 @@ export async function getWasmParser(langId: WasmLanguageId): Promise<WasmParser
76
76
  }
77
77
  }
78
78
 
79
- export function __resetWasmParserLoaderForTests(): void {
80
- initPromise = null;
81
- languages.clear();
82
- languageLoads.clear();
83
- }
@@ -19,18 +19,6 @@ export interface ReadseekSettingsResult {
19
19
  warnings: ReadseekSettingsWarning[];
20
20
  }
21
21
 
22
- let pathOverride: { globalSettingsPath?: string; projectSettingsPath?: string } | null = null;
23
-
24
- export function __setReadseekSettingsPathsForTest(paths: {
25
- globalSettingsPath?: string;
26
- projectSettingsPath?: string;
27
- }): void {
28
- pathOverride = { ...paths };
29
- }
30
-
31
- export function __resetReadseekSettingsPathsForTest(): void {
32
- pathOverride = null;
33
- }
34
22
 
35
23
  function defaultGlobalSettingsPath(): string {
36
24
  return join(homedir(), ".pi/agent/readseek/settings.json");
@@ -137,10 +125,8 @@ function mergeSettings(base: ReadseekJsonSettings, override: ReadseekJsonSetting
137
125
  }
138
126
 
139
127
  export function resolveReadseekJsonSettings(): ReadseekSettingsResult {
140
- const globalPath = pathOverride?.globalSettingsPath ?? defaultGlobalSettingsPath();
141
- const projectPath = pathOverride?.projectSettingsPath ?? defaultProjectSettingsPath();
142
- const globalResult = readSettingsFile(globalPath);
143
- const projectResult = readSettingsFile(projectPath);
128
+ const globalResult = readSettingsFile(defaultGlobalSettingsPath());
129
+ const projectResult = readSettingsFile(defaultProjectSettingsPath());
144
130
  return {
145
131
  settings: mergeSettings(globalResult.settings, projectResult.settings),
146
132
  warnings: [...globalResult.warnings, ...projectResult.warnings],
@@ -37,12 +37,6 @@ export interface ReadseekRange {
37
37
  totalLines?: number;
38
38
  }
39
39
 
40
- export interface ReadseekFileGroup {
41
- path: string;
42
- ranges: ReadseekRange[];
43
- lines: ReadseekLine[];
44
- }
45
-
46
40
  export interface SemanticSummary {
47
41
  classification: "no-op" | "whitespace-only" | "semantic" | "mixed";
48
42
  difftasticAvailable: boolean;
@@ -76,7 +70,7 @@ export function buildReadseekLines(startLine: number, rawLines: string[]): Reads
76
70
  return rawLines.map((raw, index) => buildReadseekLine(startLine + index, raw));
77
71
  }
78
72
 
79
- export function renderReadseekLine(line: ReadseekLine): string {
73
+ function renderReadseekLine(line: ReadseekLine): string {
80
74
  return `${line.anchor}|${line.display}`;
81
75
  }
82
76
 
@@ -106,18 +100,6 @@ export function buildReadseekError(
106
100
  };
107
101
  }
108
102
 
109
- export function buildReadseekRange(startLine: number, endLine: number, totalLines?: number): ReadseekRange {
110
- return totalLines === undefined ? { startLine, endLine } : { startLine, endLine, totalLines };
111
- }
112
-
113
- export function buildReadseekFileGroup(path: string, ranges: ReadseekRange[], lines: ReadseekLine[]): ReadseekFileGroup {
114
- return {
115
- path,
116
- ranges: ranges.map((range) => ({ ...range })),
117
- lines: lines.map((line) => ({ ...line })),
118
- };
119
- }
120
-
121
103
  export function buildReadseekEditResult(input: {
122
104
  ok?: boolean;
123
105
  path: string;
@@ -1,54 +0,0 @@
1
- /**
2
- * READSEEK error code taxonomy — single source of truth.
3
- *
4
- * Every error returned via `readseekValue.error.code` MUST be a key in this map.
5
- * To add a new error: extend this object with a kebab-case code, a one-line
6
- * description, and the trigger condition. Codes are stable: renaming is a
7
- * breaking change for downstream consumers.
8
- *
9
- * Distinction:
10
- * - errors (this file): fatal — the tool could not produce its primary result.
11
- * - warnings (ReadseekWarning): non-fatal — the tool produced a result but flagged
12
- * something the agent should know.
13
- */
14
- export const READSEEK_ERROR_CODES = {
15
- // read
16
- "file-not-found": { description: "Target file does not exist", trigger: "fs ENOENT on read" },
17
- "path-is-directory": { description: "Path resolves to a directory, not a file", trigger: "fs EISDIR on read" },
18
- "permission-denied": { description: "Filesystem refused access", trigger: "fs EACCES or EPERM" },
19
- "fs-error": { description: "Unexpected filesystem failure outside the specific classified cases", trigger: "non-ENOENT/non-EISDIR/non-EACCES/non-EPERM fs error while reading, writing, or stat'ing a path" },
20
- "offset-past-end": { description: "Requested offset exceeds file length", trigger: "offset > total lines" },
21
- "invalid-params-combo": { description: "Mutually exclusive parameters combined", trigger: "e.g. symbol + offset, bundle + map, map + symbol" },
22
- "invalid-offset": { description: "offset is not a positive integer", trigger: "non-int or value < 1" },
23
- "invalid-limit": { description: "limit is not a positive integer", trigger: "non-int or value < 1" },
24
-
25
- // edit
26
- "file-not-read": { description: "edit called on a path that was not read in this session", trigger: "wasReadInSession returned false" },
27
- "hash-mismatch": { description: "edit anchors do not verify against current file contents", trigger: "applyHashlineEdits detected stale anchors" },
28
- "no-op": { description: "edits produced identical content", trigger: "originalNormalized === result after applying edits" },
29
- "text-not-found": { description: "replace.old_text not present in file", trigger: "replaceText returned 0 matches" },
30
- "binary-file": { description: "edit refused because file is binary", trigger: "isBinaryBuffer detected NUL bytes" },
31
- "invalid-edit-variant": { description: "edits[i] is not exactly one of set_line/replace_lines/insert_after/replace", trigger: "exactly-one variant check failed" },
32
-
33
- // grep
34
- "binary-file-target": { description: "grep target is a binary file", trigger: "explicit binary path supplied" },
35
- "passthrough-unparsed": { description: "builtin grep result format unrecognized", trigger: "passthrough warning from underlying tool" },
36
-
37
- // search
38
- "readseek-not-installed": { description: "readseek CLI is not installed", trigger: "ENOENT on `readseek` invocation or bundled package resolution failure" },
39
- "readseek-execution-error": { description: "readseek search exited with an error", trigger: "non-zero exit or invalid search output" },
40
-
41
- // find / ls (shared shape)
42
- "path-not-found": { description: "search/target path does not exist", trigger: "stat failed on path" },
43
- "path-not-directory": { description: "path is a file, not a directory", trigger: "stat returned non-directory" },
44
-
45
- // write
46
- "binary-content": { description: "content written to write tool looks binary", trigger: "looksLikeBinary on supplied content" },
47
-
48
- "syntax-regression": {
49
- description: "edit introduced new tree-sitter syntax errors compared to pre-edit content",
50
- trigger: "post-write validator detected net-new ERROR or MISSING nodes (block mode)",
51
- },
52
- } as const;
53
-
54
- export type ReadseekErrorCode = keyof typeof READSEEK_ERROR_CODES;