@d3ara1n/pi-hashline-edit 0.3.2 → 0.3.3

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": "@d3ara1n/pi-hashline-edit",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "type": "module",
5
5
  "description": "Hashline-style file editing for pi — line-anchored edits verified by content hash, replacing oldText/newText matching",
6
6
  "keywords": [
@@ -32,25 +32,41 @@ import { getState } from "./state.ts";
32
32
  /** Cap on the number of updated anchors returned inline (bounds token cost for large inserts). */
33
33
  const MAX_ANCHOR_LINES = 40;
34
34
 
35
- const anchorSchema = Type.Object({
36
- line: Type.Number({ description: "1-based line number" }),
37
- hash: Type.String({ description: "Line content hash copied from read output (the #HASH after the line number)" }),
38
- });
35
+ /** `{line, hash}` anchor pair as an op field, described per use. */
36
+ function anchorRef(description: string) {
37
+ return Type.Optional(
38
+ Type.Object(
39
+ {
40
+ line: Type.Number({ description: "1-based line number" }),
41
+ hash: Type.String({ description: "Line content hash copied from read output (the #HASH after the line number)" }),
42
+ },
43
+ { description },
44
+ ),
45
+ );
46
+ }
39
47
 
40
48
  const editOpSchema = Type.Object({
41
49
  op: Type.Union(
42
50
  [
43
- Type.Literal("replace"),
44
- Type.Literal("delete"),
45
- Type.Literal("insert_after"),
46
- Type.Literal("insert_before"),
47
- Type.Literal("append"),
48
- Type.Literal("prepend"),
51
+ Type.Literal("replace", { description: "Replace the cited line(s) with `body`." }),
52
+ Type.Literal("delete", { description: "Delete the cited line(s)." }),
53
+ Type.Literal("insert_after", {
54
+ description: "Insert `body` immediately after the anchor line — the anchor line is kept as-is; do NOT copy it into `body`.",
55
+ }),
56
+ Type.Literal("insert_before", {
57
+ description: "Insert `body` immediately before the anchor line — the anchor line is kept as-is; do NOT copy it into `body`.",
58
+ }),
59
+ Type.Literal("append", { description: "Append `body` at the end of the file." }),
60
+ Type.Literal("prepend", { description: "Prepend `body` at the start of the file." }),
49
61
  ],
50
62
  { description: "Operation kind" },
51
63
  ),
52
- anchor: Type.Optional(anchorSchema),
53
- end: Type.Optional(anchorSchema),
64
+ anchor: anchorRef(
65
+ "First line of the range (replace/delete) or the insertion point (insert_after/insert_before).",
66
+ ),
67
+ end: anchorRef(
68
+ "Last line of the range to replace/delete, inclusive: the op touches exactly [anchor..end]. Omit only for a single-line change (end == anchor). A multi-line change that forgets `end` succeeds silently with the rest of the intended range left in the file — a corrupted file, not an error.",
69
+ ),
54
70
  body: Type.Optional(Type.Array(Type.String(), { description: "New content lines (required for replace/insert/append/prepend; omit for delete)" })),
55
71
  });
56
72
 
@@ -283,10 +283,16 @@ async function runReplace(
283
283
  }
284
284
  }
285
285
 
286
- const { diff, firstChangedLine } = generateDiffString(currentText, newText);
286
+ // generateDiffString / generateUnifiedPatch split on \n, so raw CRLF content would
287
+ // leave a trailing \r on every diff line — the TUI line-wrapper (wrapTextWithAnsi)
288
+ // then emits a spurious blank line per diff line. Normalize to LF for diff/patch
289
+ // only; the disk write above already preserved the original line endings.
290
+ const oldLf = currentText.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
291
+ const newLf = newText.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
292
+ const { diff, firstChangedLine } = generateDiffString(oldLf, newLf);
287
293
  const details: EditToolDetails = {
288
294
  diff,
289
- patch: generateUnifiedPatch(displayPath, currentText, newText),
295
+ patch: generateUnifiedPatch(displayPath, oldLf, newLf),
290
296
  firstChangedLine,
291
297
  };
292
298