pi-hashline-edit-pro 0.16.7 → 0.16.8

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-hashline-edit-pro",
3
- "version": "0.16.7",
3
+ "version": "0.16.8",
4
4
  "description": "Strict hashline read/replace tool for pi-coding-agent with hash-anchored edits (3-char, 18-bit, perfect hashing)",
5
5
  "main": "index.ts",
6
6
  "repository": {
package/src/config.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { readFileSync } from "fs";
2
2
  import { readFile, writeFile, mkdir } from "fs/promises";
3
3
  import { configDir, configPath } from "./paths";
4
+ import { errCode } from "./validation";
4
5
 
5
6
  export type ReplaceMode = "bulk" | "flat";
6
7
 
@@ -22,7 +23,10 @@ export async function readConfig(): Promise<Config> {
22
23
  replaceMode: parsed.replaceMode === "flat" ? "flat" : "bulk",
23
24
  autoRead: parsed.autoRead === true,
24
25
  };
25
- } catch {
26
+ } catch (error: unknown) {
27
+ if (errCode(error) !== "ENOENT") {
28
+ console.error("Config file corrupted, using defaults:", error);
29
+ }
26
30
  return { ...DEFAULT_CONFIG };
27
31
  }
28
32
  }
@@ -35,7 +39,10 @@ export function readConfigSync(): Config {
35
39
  replaceMode: parsed.replaceMode === "flat" ? "flat" : "bulk",
36
40
  autoRead: parsed.autoRead === true,
37
41
  };
38
- } catch {
42
+ } catch (error: unknown) {
43
+ if (errCode(error) !== "ENOENT") {
44
+ console.error("Config file corrupted, using defaults:", error);
45
+ }
39
46
  return { ...DEFAULT_CONFIG };
40
47
  }
41
48
  }
package/src/hash-store.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { readFile, writeFile, mkdir } from "fs/promises";
2
2
  import { stat } from "fs/promises";
3
3
  import { hashStorePath, hashStoreDir } from "./paths";
4
+ import { errCode } from "./validation";
4
5
 
5
6
  export interface FileSnapshot {
6
7
  content: string;
@@ -20,7 +21,10 @@ export async function loadHashStore(): Promise<HashStore> {
20
21
  version: 1,
21
22
  snapshots: parsed.snapshots ?? {},
22
23
  };
23
- } catch {
24
+ } catch (error: unknown) {
25
+ if (errCode(error) !== "ENOENT") {
26
+ console.error("Hash store corrupted, creating fresh store:", error);
27
+ }
24
28
  await mkdir(hashStoreDir(), { recursive: true });
25
29
  const defaultStore: HashStore = {
26
30
  version: 1,
@@ -294,12 +294,10 @@ export function applyEdits(
294
294
  assertNoBarePrefix(edits, lineIndex.fileLines, fileHashes);
295
295
  warnUnicodeEsc(edits, warnings);
296
296
 
297
- // Auto-fix boundary duplications: strip the offending line from content_lines
298
297
  let resolved = initialResolved;
299
298
  let autoFixes: AutoFix[] | undefined;
300
299
  if (boundaryWarnings.length > 0) {
301
300
  autoFixes = [];
302
- // Deep-copy edits so we don't mutate the originals
303
301
  const correctedEdits: import("./resolve").HEdit[] = edits.map(e => ({
304
302
  ...e,
305
303
  content_lines: [...e.content_lines],
@@ -319,7 +317,6 @@ export function applyEdits(
319
317
  }
320
318
  }
321
319
  }
322
- // Re-validate with corrected edits
323
320
  const correctedResult = valEdits(
324
321
  correctedEdits,
325
322
  lineIndex.fileLines,
@@ -43,7 +43,6 @@ export function normReq(input: unknown): unknown {
43
43
 
44
44
  normalizeFilePath(record);
45
45
 
46
- // Early validation: reject string-typed content_lines at the top level
47
46
  if (has(record, "content_lines") && typeof record.content_lines === "string") {
48
47
  assertContentLinesNotString(record.content_lines, "Top-level");
49
48
  }
@@ -51,7 +50,6 @@ export function normReq(input: unknown): unknown {
51
50
  normalizeField(record, "changes", "changes");
52
51
  normalizeField(record, "edits", "changes");
53
52
 
54
- // Validate items in the changes array before wrapping flat format
55
53
  if (Array.isArray(record.changes)) {
56
54
  for (let i = 0; i < record.changes.length; i++) {
57
55
  const item = record.changes[i];
package/src/utils.ts CHANGED
@@ -6,25 +6,12 @@ export function has(record: Record<string, unknown>, key: string): boolean {
6
6
  return Object.hasOwn(record, key);
7
7
  }
8
8
 
9
- /**
10
- * Splits text into visible lines, stripping the trailing empty element
11
- * that `split("\n")` produces when the text ends with "\n".
12
- *
13
- * This is the canonical way to get user-visible lines from text.
14
- * For internal hashing that needs the trailing empty string (e.g.
15
- * `_lineHashesPure`, `mapStableHashes`, `buildIdx`), use
16
- * `text.split("\n")` directly with a comment explaining why.
17
- */
18
- export function splitLines(text: string): string[] {
9
+ export function visLines(text: string): string[] {
19
10
  if (text.length === 0) return [];
20
11
  const lines = text.split("\n");
21
12
  return text.endsWith("\n") ? lines.slice(0, -1) : lines;
22
13
  }
23
14
 
24
- export function visLines(text: string): string[] {
25
- return splitLines(text);
26
- }
27
-
28
15
  export function cntLines(text: string): number {
29
16
  return visLines(text).length;
30
17
  }