pi-hashline-edit-pro 2.8.4 → 3.0.0

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.
@@ -7,36 +7,20 @@ import {
7
7
  persistSnapshot,
8
8
  } from "../hash-store";
9
9
  import { xxh32, initHasher } from "./hasher";
10
- import { HASH_LEN, ALPH, ALPH_RE, HASH_CLASS, HASH_RUN } from "./alphabet";
11
- export { initHasher, HASH_LEN, ALPH_RE, HASH_CLASS, HASH_RUN };
10
+ import { HASH_LEN, ANCHOR_COUNT, anchorAt, anchorIndex, HASH_CLASS, HASH_RUN } from "./alphabet";
11
+ export { initHasher, HASH_LEN, HASH_CLASS, HASH_RUN };
12
12
 
13
13
  export const ANCHOR_LEN = HASH_LEN;
14
14
 
15
15
  export const HASH_SEP = "│";
16
16
 
17
- export const HASH_SPACE = ALPH.length ** HASH_LEN;
17
+ export const HASH_SPACE = ANCHOR_COUNT;
18
18
  export const MAX_HASH_LINES = HASH_SPACE;
19
19
 
20
- export const HASH_PROBE_STRIDE = ALPH.length ** 2 + ALPH.length + 1;
21
-
22
- function idxToHash(idx: number): string {
23
- let out = "";
24
- for (let j = 0; j < HASH_LEN; j++) {
25
- out = ALPH[idx % ALPH.length]! + out;
26
- idx = Math.floor(idx / ALPH.length);
27
- }
28
- return out;
29
- }
30
-
31
- const hashCache = new Map<number, string>();
20
+ export const HASH_PROBE_STRIDE = 571;
32
21
 
33
22
  function hashAt(idx: number): string {
34
- let hash = hashCache.get(idx);
35
- if (hash === undefined) {
36
- hash = idxToHash(idx);
37
- hashCache.set(idx, hash);
38
- }
39
- return hash;
23
+ return anchorAt(idx);
40
24
  }
41
25
 
42
26
  export const HL_PREFIX_PLUS_RE = new RegExp(
@@ -182,13 +166,7 @@ export async function lineHashes(
182
166
  }
183
167
 
184
168
  function hashToIndex(hash: string): number {
185
- let idx = 0;
186
- for (let j = 0; j < HASH_LEN; j++) {
187
- const charIdx = ALPH.indexOf(hash[j]!);
188
- if (charIdx < 0) return -1;
189
- idx = idx * ALPH.length + charIdx;
190
- }
191
- return idx;
169
+ return anchorIndex(hash);
192
170
  }
193
171
 
194
172
  function nearestNew(
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  ANCHOR_LEN,
3
- ALPH_RE,
4
3
  HASH_CLASS,
5
4
  } from "./hash";
5
+ import { ALPH_RE } from "./alphabet";
6
6
  import { NEW_CONTENT_NOT_ARRAY_MSG } from "../constants";
7
7
 
8
8
  const HASH_EXTRACT_RE = new RegExp(HASH_CLASS);
@@ -13,11 +13,11 @@ function diagRef(ref: string): string {
13
13
  const trimmed = ref.trim();
14
14
 
15
15
  if (!trimmed.length) {
16
- return `[E_BAD_REF] Invalid anchor. Expected a 3-char alphanumeric anchor (e.g. "aB3").`;
16
+ return `[E_BAD_REF] Invalid anchor. Expected a 4-char alphanumeric anchor (e.g. "Hasu").`;
17
17
  }
18
18
 
19
19
  if (/^\d+/.test(trimmed)) {
20
- return `[E_BAD_REF] Invalid anchor. Use the anchor alone (e.g. "aB3"): no line numbers or trailing content.`;
20
+ return `[E_BAD_REF] Invalid anchor. Use the anchor alone (e.g. "Hasu"): no line numbers or trailing content.`;
21
21
  }
22
22
  if (trimmed.includes("│") && trimmed.includes("\n")) {
23
23
  const lines = trimmed.split(/\r?\n/);
@@ -26,16 +26,16 @@ function diagRef(ref: string): string {
26
26
  const hashRe = HASH_EXTRACT_RE;
27
27
  const firstMatch = first.match(hashRe);
28
28
  const lastMatch = last.match(hashRe);
29
- const firstHash = firstMatch?.[0] ?? "aB3";
30
- const lastHash = lastMatch?.[0] ?? "aB3";
29
+ const firstHash = firstMatch?.[0] ?? "Hasu";
30
+ const lastHash = lastMatch?.[0] ?? "Hasu";
31
31
  const preview = first.slice(0, 60);
32
- return `[E_BAD_REF] Invalid anchor — remove_from and remove_to must each be a single bare 3-char hash (e.g. "aB3"), not a block with HASH│content. Received ${lines.length} lines starting "${preview}…" — use only the first hash "${firstHash}" as remove_from and "${lastHash}" as remove_to, and put the new content (without HASH│) in replacement_lines.`;
32
+ return `[E_BAD_REF] Invalid anchor — remove_from and remove_to must each be a single bare 4-char hash (e.g. "Hasu"), not a block with HASH│content. Received ${lines.length} lines starting "${preview}…" — use only the first hash "${firstHash}" as remove_from and "${lastHash}" as remove_to, and put the new content (without HASH│) in replacement_lines.`;
33
33
  }
34
34
  if (trimmed.includes("│")) {
35
- return `[E_BAD_REF] Invalid anchor "${trimmed}": use only the 3-char anchor, drop everything from "│" onward.`;
35
+ return `[E_BAD_REF] Invalid anchor "${trimmed}": use only the 4-char anchor, drop everything from "│" onward.`;
36
36
  }
37
37
 
38
- return `[E_BAD_REF] Invalid anchor "${trimmed}". Expected a 3-char alphanumeric anchor (e.g. "aB3").`;
38
+ return `[E_BAD_REF] Invalid anchor "${trimmed}". Expected a 4-char alphanumeric anchor (e.g. "Hasu").`;
39
39
  }
40
40
 
41
41
  function parseRef(ref: string): Anchor {
@@ -159,12 +159,12 @@ function assertItem(edit: Record<string, unknown>): void {
159
159
 
160
160
  if ("remove_from" in edit && typeof edit.remove_from !== "string") {
161
161
  throw new Error(
162
- `[E_BAD_SHAPE] Field "remove_from" must be an anchor string (3-char anchor).`,
162
+ `[E_BAD_SHAPE] Field "remove_from" must be an anchor string (4-char anchor).`,
163
163
  );
164
164
  }
165
165
  if ("remove_to" in edit && typeof edit.remove_to !== "string") {
166
166
  throw new Error(
167
- `[E_BAD_SHAPE] Field "remove_to" must be an anchor string (3-char anchor).`,
167
+ `[E_BAD_SHAPE] Field "remove_to" must be an anchor string (4-char anchor).`,
168
168
  );
169
169
  }
170
170
  if (!("replacement_lines" in edit)) {
@@ -175,7 +175,7 @@ function assertItem(edit: Record<string, unknown>): void {
175
175
  }
176
176
  if (typeof edit.remove_from !== "string" || typeof edit.remove_to !== "string") {
177
177
  throw new Error(
178
- `[E_BAD_SHAPE] The edit requires "remove_from" and "remove_to" anchor strings (3-char anchors from read output).`,
178
+ `[E_BAD_SHAPE] The edit requires "remove_from" and "remove_to" anchor strings (4-char anchors from read output).`,
179
179
  );
180
180
  }
181
181
  }
package/src/insert.ts CHANGED
@@ -31,7 +31,7 @@ export function assertInsertReq(request: unknown): asserts request is InsertReq
31
31
  throw new Error('[E_BAD_SHAPE] Insert request requires a non-empty "path" string.');
32
32
  }
33
33
  if (typeof request.anchor !== "string" || request.anchor.length === 0) {
34
- throw new Error('[E_BAD_SHAPE] Insert request requires an "anchor" string (3-char anchor from read output).');
34
+ throw new Error('[E_BAD_SHAPE] Insert request requires an "anchor" string (4-char anchor from read output).');
35
35
  }
36
36
  if (request.direction !== "before" && request.direction !== "after") {
37
37
  throw new Error('[E_BAD_SHAPE] Insert request "direction" must be "before" or "after".');
@@ -49,23 +49,23 @@ const insertToolSchema = Type.Object(
49
49
  }),
50
50
  anchor: Type.String({
51
51
  description:
52
- 'Bare 3-char anchor only (e.g. "aB3"): copy just the anchor from the leftmost column of a read row like `aB3│content`; never the line content. A pasted diff row like `+aB3│x` or an `anchor│` prefix is stripped automatically with a warning. The anchored line is preserved; the new lines go after or before it.',
52
+ 'Bare 4-char anchor from a read row like `Hasu│content`, never the content. A pasted `+Hasu│x` diff row or `anchor│` prefix is stripped with a warning. The anchor line is preserved; lines go after or before it.',
53
53
  }),
54
54
  direction: Type.Union(
55
55
  [
56
- Type.Literal("after", { description: "Insert the lines after the anchor line" }),
57
- Type.Literal("before", { description: "Insert the lines before the anchor line" }),
56
+ Type.Literal("after"),
57
+ Type.Literal("before"),
58
58
  ],
59
59
  { description: '"after" or "before"' },
60
60
  ),
61
61
  lines: Type.Array(
62
62
  Type.String({
63
63
  description:
64
- "One line to insert. Each element is exactly one line; do not embed \\n inside an element: use separate elements.",
64
+ "One line to insert; never embed \\n inside an element.",
65
65
  }),
66
66
  {
67
67
  description:
68
- 'Lines to insert as an array of strings, one element per line. Use [""] for a blank line. The anchor line is preserved; never include it in lines.',
68
+ 'One string per line; [""] is a blank line; never include the anchor line.',
69
69
  }
70
70
  ),
71
71
  },
@@ -4,22 +4,22 @@ import { isRec, normalizeFilePath, rejectUnknownFields } from "./utils";
4
4
  const replacementLinesSchema = Type.Array(
5
5
  Type.String({
6
6
  description:
7
- "One replacement line. Each element is exactly one line; do not embed \\n inside an element: use separate elements.",
7
+ "One replacement line; never embed \\n inside an element.",
8
8
  }),
9
9
  {
10
10
  description:
11
- "Replacement lines as an array of strings, one element per line. Use [] to delete the range.",
11
+ "One string per line. Use [] to delete the range.",
12
12
  },
13
13
  );
14
14
 
15
15
  const removeFromSchema = Type.String({
16
16
  description:
17
- "Bare 3-char anchor only (e.g. \"aB3\"): copy just the anchor from the leftmost column of a read row like `aB3│content`; never the line content. Marks the FIRST line to remove (inclusive)",
17
+ "Bare 4-char anchor from a read row like `Hasu│content` (the leftmost column), never the row content. Marks the FIRST line to remove (inclusive)",
18
18
  });
19
19
 
20
20
  const removeToSchema = Type.String({
21
21
  description:
22
- "Bare 3-char anchor only (e.g. \"aB3\"): copy just the anchor from the leftmost column of a read row like `aB3│content`; never the line content. Marks the LAST line to remove (inclusive)",
22
+ "Bare 4-char anchor from a read row like `Hasu│content` (the leftmost column), never the row content. Marks the LAST line to remove (inclusive)",
23
23
  });
24
24
 
25
25
  export const editToolSchema = Type.Object(
@@ -27,7 +27,7 @@ export const editToolSchema = Type.Object(
27
27
  path: Type.Optional(
28
28
  Type.String({
29
29
  description:
30
- "Path to edit. Required: always provide it explicitly; it is only auto-resolved from the anchors as a fallback when omitted by mistake.",
30
+ "Path to edit; always provide it explicitly — it is only auto-resolved from the anchors as a fallback.",
31
31
  }),
32
32
  ),
33
33
  remove_from: removeFromSchema,
package/src/utils.ts CHANGED
@@ -114,7 +114,7 @@ export function getCached<K, V>(map: Map<K, V>, key: K, compute: (key: K) => V):
114
114
  }
115
115
 
116
116
  export function isHashRow(line: string): boolean {
117
- return /^[A-Za-z0-9]{3}│/.test(line);
117
+ return /^[A-Za-z0-9]{4}│/.test(line);
118
118
  }
119
119
 
120
120
  function gutterWidth(max: number, fallback: number): number {