pi-hashline-edit-pro 0.15.2 → 0.15.4

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.15.2",
3
+ "version": "0.15.4",
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": {
@@ -45,10 +45,19 @@ export function genDiff(
45
45
  newContent: string,
46
46
  contextLines = 2,
47
47
  newContentHashes?: string[],
48
+ oldHashes?: string[],
48
49
  ): { diff: string; firstChangedLine: number | undefined } {
50
+ // Run Diff.diffLines on raw content only (no hash annotations) so that
51
+ // lines whose content is identical are never reported as changed even
52
+ // when their hash differs due to collision resolution or position
53
+ // tracking. Hashes are used purely for display via fmtDiffLine.
54
+ const oldLines = oldContent.split("\n");
55
+ const newLines = newContent.split("\n");
56
+ const effectiveOldHashes = oldHashes ?? _lineHashesPure(oldContent);
57
+ const effectiveNewHashes = newContentHashes ?? _lineHashesPure(newContent);
58
+
49
59
  const parts = Diff.diffLines(oldContent, newContent);
50
60
  const output: string[] = [];
51
- const effectiveNewHashes = newContentHashes ?? _lineHashesPure(newContent);
52
61
  let oldLineNum = 1;
53
62
  let newLineNum = 1;
54
63
  let lastWasChange = false;
@@ -58,18 +67,17 @@ export function genDiff(
58
67
  const part = parts[i]!;
59
68
  const raw = part.value.split("\n");
60
69
  if (raw[raw.length - 1] === "") raw.pop();
70
+ const displayLines = raw;
61
71
 
62
72
  if (part.added || part.removed) {
63
73
  if (firstChangedLine === undefined) firstChangedLine = newLineNum;
64
- for (const line of raw) {
74
+ for (let k = 0; k < displayLines.length; k++) {
65
75
  if (part.added) {
66
76
  const hash = effectiveNewHashes[newLineNum - 1];
67
- output.push(fmtDiffLine("+", line, hash));
77
+ output.push(fmtDiffLine("+", displayLines[k]!, hash));
68
78
  newLineNum++;
69
79
  } else {
70
- output.push(
71
- fmtDiffLine("-", line, undefined),
72
- );
80
+ output.push(fmtDiffLine("-", displayLines[k]!, undefined));
73
81
  oldLineNum++;
74
82
  }
75
83
  }
@@ -80,44 +88,43 @@ export function genDiff(
80
88
  const nextPartIsChange =
81
89
  i < parts.length - 1 && (parts[i + 1]!.added || parts[i + 1]!.removed);
82
90
  if (lastWasChange || nextPartIsChange) {
83
- let linesToShow = raw;
91
+ let linesToShow = displayLines;
84
92
  let skipStart = 0;
85
93
  let skipEnd = 0;
86
94
  let skipMiddle = 0;
87
95
 
88
96
  if (!lastWasChange) {
89
- skipStart = Math.max(0, raw.length - contextLines);
90
- linesToShow = raw.slice(skipStart);
91
- } else if (nextPartIsChange && raw.length > contextLines * 2) {
92
- const tail = raw.slice(-contextLines);
93
- linesToShow = [...raw.slice(0, contextLines), "__ELLIPSIS__", ...tail];
94
- skipMiddle = raw.length - contextLines * 2;
97
+ skipStart = Math.max(0, displayLines.length - contextLines);
98
+ linesToShow = displayLines.slice(skipStart);
99
+ } else if (nextPartIsChange && displayLines.length > contextLines * 2) {
100
+ const tail = displayLines.slice(-contextLines);
101
+ linesToShow = [...displayLines.slice(0, contextLines), "__ELLIPSIS__", ...tail];
102
+ skipMiddle = displayLines.length - contextLines * 2;
95
103
  } else if (linesToShow.length > contextLines) {
96
104
  skipEnd = linesToShow.length - contextLines;
97
105
  linesToShow = linesToShow.slice(0, contextLines);
98
106
  }
99
107
 
100
108
  if (skipStart > 0) {
101
- output.push(` ...`);
109
+ output.push(" ...");
102
110
  oldLineNum += skipStart;
103
111
  newLineNum += skipStart;
104
112
  }
105
113
  for (const line of linesToShow) {
106
114
  if (line === "__ELLIPSIS__") {
107
- output.push(` ...`);
115
+ output.push(" ...");
108
116
  oldLineNum += skipMiddle;
109
117
  newLineNum += skipMiddle;
110
118
  continue;
111
119
  }
112
120
  const hash = effectiveNewHashes[newLineNum - 1];
113
121
  output.push(fmtDiffLine(" ", line, hash));
114
-
115
122
  oldLineNum++;
116
123
  newLineNum++;
117
124
  }
118
125
  } else {
119
- oldLineNum += raw.length;
120
- newLineNum += raw.length;
126
+ oldLineNum += displayLines.length;
127
+ newLineNum += displayLines.length;
121
128
  }
122
129
  lastWasChange = false;
123
130
  }
@@ -238,6 +238,7 @@ export function buildToolDef(): ToolDef {
238
238
 
239
239
  const {
240
240
  originalNormalized,
241
+ originalHashes,
241
242
  result,
242
243
  bom,
243
244
  originalEnding,
@@ -294,13 +295,13 @@ export function buildToolDef(): ToolDef {
294
295
  const successInput = {
295
296
  path,
296
297
  originalNormalized,
298
+ originalHashes,
297
299
  result,
298
300
  resultHashes,
299
301
  warnings,
300
302
  snapshotId: updatedSnapshotId,
301
303
  editMeta,
302
304
  };
303
-
304
305
  return buildChanged(successInput);
305
306
  });
306
307
  },
@@ -40,13 +40,14 @@ export interface NoopInput {
40
40
  }
41
41
 
42
42
  export interface SuccessInput {
43
- path: string;
44
- originalNormalized: string;
45
- result: string;
46
- resultHashes: string[];
47
- warnings: string[] | undefined;
48
- snapshotId: string;
49
- editMeta: RMeta;
43
+ path: string;
44
+ originalNormalized: string;
45
+ originalHashes: string[];
46
+ result: string;
47
+ resultHashes: string[];
48
+ warnings: string[] | undefined;
49
+ snapshotId: string;
50
+ editMeta: RMeta;
50
51
  }
51
52
 
52
53
  function cntDiff(diff: string, marker: "+" | "-"): number {
@@ -139,39 +140,39 @@ export function buildNoop(input: NoopInput): TResult {
139
140
  }
140
141
 
141
142
  export function buildChanged(input: SuccessInput): TResult {
142
- const { path, result, warnings, snapshotId, originalNormalized, editMeta, resultHashes } = input;
143
-
144
- const resultLines = visLines(result);
145
- const diffResult = genDiff(originalNormalized, result, 2, resultHashes);
146
- const addedLines = cntDiff(diffResult.diff, "+");
147
- const removedLines = cntDiff(diffResult.diff, "-");
148
- const warningsBlock = warnBlock(warnings);
149
- const successPrefix = `Successfully replaced in ${path}.`;
150
- const text = resultLines.length === 0
151
- ? "File is empty. Use replace to insert content."
152
- : warningsBlock
153
- ? `${successPrefix}${warningsBlock}`
154
- : successPrefix;
155
-
156
- const metrics = buildM({
157
- classification: "applied",
158
- editsAttempted: editMeta.editsAttempted,
159
- noopEditsCount: editMeta.noopEditsCount,
160
- warningsCount: warnings?.length ?? 0,
161
- firstChangedLine: editMeta.firstChangedLine,
162
- lastChangedLine: editMeta.lastChangedLine,
163
- addedLines,
164
- removedLines,
165
- });
166
-
167
- return {
168
- content: [{ type: "text", text }],
169
- details: {
170
- diff: diffResult.diff,
171
- firstChangedLine:
172
- editMeta.firstChangedLine ?? diffResult.firstChangedLine,
173
- snapshotId,
174
- metrics,
175
- },
176
- };
143
+ const { path, result, warnings, snapshotId, originalNormalized, originalHashes, editMeta, resultHashes } = input;
144
+
145
+ const resultLines = visLines(result);
146
+ const diffResult = genDiff(originalNormalized, result, 2, resultHashes, originalHashes);
147
+ const addedLines = cntDiff(diffResult.diff, "+");
148
+ const removedLines = cntDiff(diffResult.diff, "-");
149
+ const warningsBlock = warnBlock(warnings);
150
+ const successPrefix = `Successfully replaced in ${path}.`;
151
+ const text = resultLines.length === 0
152
+ ? "File is empty. Use replace to insert content."
153
+ : warningsBlock
154
+ ? `${successPrefix}${warningsBlock}`
155
+ : successPrefix;
156
+
157
+ const metrics = buildM({
158
+ classification: "applied",
159
+ editsAttempted: editMeta.editsAttempted,
160
+ noopEditsCount: editMeta.noopEditsCount,
161
+ warningsCount: warnings?.length ?? 0,
162
+ firstChangedLine: editMeta.firstChangedLine,
163
+ lastChangedLine: editMeta.lastChangedLine,
164
+ addedLines,
165
+ removedLines,
166
+ });
167
+
168
+ return {
169
+ content: [{ type: "text", text }],
170
+ details: {
171
+ diff: diffResult.diff,
172
+ firstChangedLine:
173
+ editMeta.firstChangedLine ?? diffResult.firstChangedLine,
174
+ snapshotId,
175
+ metrics,
176
+ },
177
+ };
177
178
  }
package/src/replace.ts CHANGED
@@ -101,7 +101,7 @@ interface PipelineResult {
101
101
  noopEdits?: { editIndex: number; loc: string; currentContent: string }[];
102
102
  firstChangedLine?: number;
103
103
  lastChangedLine?: number;
104
- originalHashes?: string[];
104
+ originalHashes: string[];
105
105
  resultHashes: string[];
106
106
  }
107
107
 
@@ -228,7 +228,7 @@ export async function compPreview(
228
228
  try {
229
229
  const normalized = normReq(request);
230
230
  assertReq(normalized);
231
- const { path, originalNormalized, result, resultHashes } = await execPipeline(
231
+ const { path, originalNormalized, originalHashes, result, resultHashes } = await execPipeline(
232
232
  normalized,
233
233
  cwd,
234
234
  constants.R_OK,
@@ -240,7 +240,7 @@ export async function compPreview(
240
240
  };
241
241
  }
242
242
 
243
- return { diff: genDiff(originalNormalized, result, 4, resultHashes).diff };
243
+ return { diff: genDiff(originalNormalized, result, 4, resultHashes, originalHashes).diff };
244
244
  } catch (error: unknown) {
245
245
  return { error: error instanceof Error ? error.message : String(error) };
246
246
  }
@@ -391,6 +391,7 @@ export function buildToolDef(): ToolDef {
391
391
 
392
392
  const {
393
393
  originalNormalized,
394
+ originalHashes,
394
395
  result,
395
396
  bom,
396
397
  originalEnding,
@@ -449,13 +450,13 @@ export function buildToolDef(): ToolDef {
449
450
  const successInput = {
450
451
  path,
451
452
  originalNormalized,
453
+ originalHashes,
452
454
  result,
453
455
  resultHashes,
454
456
  warnings,
455
457
  snapshotId: updatedSnapshotId,
456
458
  editMeta,
457
459
  };
458
-
459
460
  return buildChanged(successInput);
460
461
  });
461
462
  },