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 +3 -3
- package/package.json +1 -1
- package/prompts/replace.md +1 -1
- package/src/hashline/apply.ts +7 -1
- package/src/replace-diff.ts +1 -1
- package/src/replace-response.ts +2 -10
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 `[
|
|
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
|
|
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
|
|
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
package/prompts/replace.md
CHANGED
|
@@ -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
|
|
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.
|
package/src/hashline/apply.ts
CHANGED
|
@@ -287,7 +287,7 @@ export function applyHashlineEdits(
|
|
|
287
287
|
}
|
|
288
288
|
|
|
289
289
|
|
|
290
|
-
const ANCHOR_CONTEXT_LINES =
|
|
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
|
}
|
package/src/replace-diff.ts
CHANGED
|
@@ -44,7 +44,7 @@ function formatDiffPreviewLine(
|
|
|
44
44
|
export function generateDiffString(
|
|
45
45
|
oldContent: string,
|
|
46
46
|
newContent: string,
|
|
47
|
-
contextLines =
|
|
47
|
+
contextLines = 2,
|
|
48
48
|
newContentHashes?: string[],
|
|
49
49
|
): { diff: string; firstChangedLine: number | undefined } {
|
|
50
50
|
const parts = Diff.diffLines(oldContent, newContent);
|
package/src/replace-response.ts
CHANGED
|
@@ -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");
|