pi-hashline-edit-pro 0.9.1 → 0.9.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/README.md CHANGED
@@ -76,14 +76,14 @@ Replaces using the `HASH│content` anchors from `read` output to target lines p
76
76
  | `old_range` | Inclusive line range `[start_hash, end_hash]` (required). |
77
77
  | `new_lines` | Replacement content (one string per line). Use `[]` to delete. |
78
78
 
79
- - **Request structure validation.** The request envelope (`path`, `edits`) and individual edit items are validated before any file I/O. Unknown fields, missing required fields, invalid types, and malformed anchors are rejected with `[E_BAD_SHAPE]` or `[E_BAD_OP]`.
79
+ - **Request structure validation.** The request envelope (`path`, `edits`) and individual edit items are validated before any file I/O. Unknown fields, missing required fields, invalid types, and malformed anchors are rejected with `[E_BAD_SHAPE]` or `[E_BAD_REF]`.
80
80
  - **Legacy dialect rejected.** The native top-level `oldText`/`newText` (and `old_text`/`new_text`) dialect is rejected with `[E_LEGACY_SHAPE]`. The error message tells the model to call `read` first and send `{old_range: ["<START>", "<END>"], new_lines: [...]}`.
81
81
 
82
82
  All edits in a single call validate against the same pre-edit snapshot and apply bottom-up, so line numbers stay consistent across operations.
83
83
 
84
84
  ### Chained edits
85
85
 
86
- After a successful replace, the result text contains an `--- Anchors ---` block with fresh `HASH│content` references for the changed region. These can be used directly in the next `replace` call on the same file without a full re-read, provided the next replace targets the same or nearby lines. For distant changes, use `read` first.
86
+ After a successful replace, the response text is empty (warnings are still shown if present). To get fresh anchors for follow-up edits, call `read` on the file first. This avoids token overhead from re-displaying content the model already knows.
87
87
 
88
88
  ### Auto-read after write
89
89
 
@@ -98,7 +98,7 @@ For large files (>2000 lines), the auto-read output is truncated with a paginati
98
98
 
99
99
  ### Diff for the host
100
100
 
101
- The post-edit diff (with `+`/`-` markers and new `HASH│content` anchors) is exposed to the host UI via `details.diff`. It is intentionally not in the LLM-visible text. The model only needs the fresh anchors in `text` to chain follow-up edits, and re-emitting the diff would cost extra tokens.
101
+ The post-edit diff (with `+`/`-` markers) is exposed to the host UI via `details.diff`. It is intentionally not in the LLM-visible text. The model already knows what it changed and can call `read` for fresh anchors when needed.
102
102
 
103
103
  ## Design Decisions
104
104
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-hashline-edit-pro",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
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": {
@@ -59,7 +59,7 @@ Rules:
59
59
  - If `new_lines` matches current content, the replace is classified as `noop` (file unchanged).
60
60
  - The `old_range` is inclusive — both anchors and every line between them are replaced. If your replacement content includes lines that already exist in the file (e.g. closing brackets), make sure those lines are within your range, otherwise they will appear twice.
61
61
 
62
- On success, the response contains an `--- Anchors ---` block with fresh HASH anchors for the changed region. Use those for nearby follow-up replaces instead of re-reading.
62
+ On success, the response text is empty (or contains only warnings if present). Call `read` to get fresh anchors for follow-up edits.
63
63
 
64
64
  Error recovery:
65
65
  - `[E_STALE_ANCHOR]` — file changed since last read. Call `read` to get fresh anchors, then copy the HASH and retry.
@@ -287,7 +287,7 @@ export function applyHashlineEdits(
287
287
  }
288
288
 
289
289
 
290
- const ANCHOR_CONTEXT_LINES = 2;
290
+ const ANCHOR_CONTEXT_LINES = 0;
291
291
  const ANCHOR_MAX_OUTPUT_LINES = 12;
292
292
 
293
293
  export function computeAffectedLineRange(params: {
@@ -309,6 +309,12 @@ export function computeAffectedLineRange(params: {
309
309
  return null;
310
310
  }
311
311
 
312
+ // When contextLines is 0, skip the anchor block entirely.
313
+ // The LLM already knows what it changed and can call read for fresh anchors.
314
+ if (contextLines === 0) {
315
+ return null;
316
+ }
317
+
312
318
  if (resultLineCount === 0) {
313
319
  return null;
314
320
  }
@@ -44,7 +44,7 @@ function formatDiffPreviewLine(
44
44
  export function generateDiffString(
45
45
  oldContent: string,
46
46
  newContent: string,
47
- contextLines = 4,
47
+ contextLines = 2,
48
48
  newContentHashes?: string[],
49
49
  ): { diff: string; firstChangedLine: number | undefined } {
50
50
  const parts = Diff.diffLines(oldContent, newContent);
@@ -190,18 +190,10 @@ export function buildChangedResponse(input: SuccessResponseInput): ToolResult {
190
190
  CHANGED_ANCHOR_TEXT_BUDGET_BYTES
191
191
  ? block
192
192
  : "Anchors omitted; use read for subsequent edits.";
193
- })()
193
+ })()
194
194
  : resultLines.length === 0
195
195
  ? "File is empty. Use edit to insert content."
196
- : (() => {
197
- const diffLines = diffResult.diff.split("\n");
198
- const MAX_DIFF_LINES = 30;
199
- if (diffLines.length <= MAX_DIFF_LINES) {
200
- return `Diff preview:\n${diffResult.diff}`;
201
- }
202
- const truncated = diffLines.slice(0, MAX_DIFF_LINES).join("\n");
203
- return `Diff preview (${diffLines.length} lines, showing first ${MAX_DIFF_LINES}):\n${truncated}\n...`;
204
- })();
196
+ : ""; // No anchor context → show nothing; LLM can call read for fresh anchors
205
197
  const text = [anchorsBlock, warningsBlock.trimStart()]
206
198
  .filter((section) => section.length > 0)
207
199
  .join("\n\n");