@jerryan/pi-hashline-edit 0.7.1 → 0.7.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 +127 -114
- package/package.json +53 -53
- package/prompts/edit.md +4 -4
- package/prompts/read.md +1 -1
- package/src/edit-diff.ts +39 -77
- package/src/edit-response.ts +5 -67
- package/src/edit.ts +520 -642
- package/src/hashline.ts +1071 -1058
package/src/edit-response.ts
CHANGED
|
@@ -1,21 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Edit response builders.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* No behaviour change: outputs are byte-identical to the previous inline
|
|
8
|
-
* implementation. The only additive surface is `details.metrics` (Phase 2 C
|
|
9
|
-
* — observability for hosts; the LLM-visible text is unchanged).
|
|
4
|
+
* Unified diff output: agent and user see the same content. The diff is
|
|
5
|
+
* generated from structuredPatch hunks with hashline-formatted lines.
|
|
10
6
|
*/
|
|
11
7
|
|
|
12
8
|
import { generateDiffString } from "./edit-diff";
|
|
13
|
-
import {
|
|
14
|
-
computeAffectedLineRange,
|
|
15
|
-
formatHashlineRegion,
|
|
16
|
-
} from "./hashline";
|
|
17
|
-
|
|
18
|
-
const CHANGED_ANCHOR_TEXT_BUDGET_BYTES = 50 * 1024;
|
|
19
9
|
|
|
20
10
|
// ─── Public types ───────────────────────────────────────────────────────
|
|
21
11
|
|
|
@@ -24,7 +14,6 @@ export type EditMetrics = {
|
|
|
24
14
|
edits_noop: number;
|
|
25
15
|
warnings: number;
|
|
26
16
|
classification: "applied" | "noop";
|
|
27
|
-
changed_lines?: { first: number; last: number };
|
|
28
17
|
added_lines?: number;
|
|
29
18
|
removed_lines?: number;
|
|
30
19
|
};
|
|
@@ -51,8 +40,6 @@ export interface SuccessResponseInput {
|
|
|
51
40
|
originalNormalized: string;
|
|
52
41
|
result: string;
|
|
53
42
|
warnings: string[] | undefined;
|
|
54
|
-
firstChangedLine: number | undefined;
|
|
55
|
-
lastChangedLine: number | undefined;
|
|
56
43
|
snapshotId: string;
|
|
57
44
|
editsAttempted: number;
|
|
58
45
|
noopEditsCount: number;
|
|
@@ -60,12 +47,6 @@ export interface SuccessResponseInput {
|
|
|
60
47
|
|
|
61
48
|
// ─── Helpers ────────────────────────────────────────────────────────────
|
|
62
49
|
|
|
63
|
-
function getVisibleLines(text: string): string[] {
|
|
64
|
-
if (text.length === 0) return [];
|
|
65
|
-
const lines = text.split("\n");
|
|
66
|
-
return text.endsWith("\n") ? lines.slice(0, -1) : lines;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
50
|
function countDiffLines(diff: string, marker: "+" | "-"): number {
|
|
70
51
|
if (!diff) return 0;
|
|
71
52
|
let count = 0;
|
|
@@ -82,8 +63,6 @@ function buildMetrics(args: {
|
|
|
82
63
|
editsAttempted: number;
|
|
83
64
|
noopEditsCount: number;
|
|
84
65
|
warningsCount: number;
|
|
85
|
-
firstChangedLine?: number;
|
|
86
|
-
lastChangedLine?: number;
|
|
87
66
|
addedLines?: number;
|
|
88
67
|
removedLines?: number;
|
|
89
68
|
}): EditMetrics {
|
|
@@ -93,16 +72,6 @@ function buildMetrics(args: {
|
|
|
93
72
|
warnings: args.warningsCount,
|
|
94
73
|
classification: args.classification,
|
|
95
74
|
};
|
|
96
|
-
if (
|
|
97
|
-
args.classification === "applied" &&
|
|
98
|
-
args.firstChangedLine !== undefined &&
|
|
99
|
-
args.lastChangedLine !== undefined
|
|
100
|
-
) {
|
|
101
|
-
metrics.changed_lines = {
|
|
102
|
-
first: args.firstChangedLine,
|
|
103
|
-
last: args.lastChangedLine,
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
75
|
if (args.addedLines !== undefined) metrics.added_lines = args.addedLines;
|
|
107
76
|
if (args.removedLines !== undefined) metrics.removed_lines = args.removedLines;
|
|
108
77
|
return metrics;
|
|
@@ -145,7 +114,6 @@ export function buildNoopResponse(input: NoopResponseInput): ToolResult {
|
|
|
145
114
|
content: [{ type: "text", text }],
|
|
146
115
|
details: {
|
|
147
116
|
diff: "",
|
|
148
|
-
firstChangedLine: undefined,
|
|
149
117
|
snapshotId,
|
|
150
118
|
classification: "noop" as const,
|
|
151
119
|
metrics,
|
|
@@ -154,42 +122,15 @@ export function buildNoopResponse(input: NoopResponseInput): ToolResult {
|
|
|
154
122
|
}
|
|
155
123
|
|
|
156
124
|
export function buildChangedResponse(input: SuccessResponseInput): ToolResult {
|
|
157
|
-
const {
|
|
158
|
-
|
|
159
|
-
warnings,
|
|
160
|
-
firstChangedLine,
|
|
161
|
-
lastChangedLine,
|
|
162
|
-
snapshotId,
|
|
163
|
-
originalNormalized,
|
|
164
|
-
editsAttempted,
|
|
165
|
-
noopEditsCount,
|
|
166
|
-
} = input;
|
|
125
|
+
const { result, warnings, snapshotId, originalNormalized, editsAttempted, noopEditsCount } =
|
|
126
|
+
input;
|
|
167
127
|
|
|
168
128
|
const diffResult = generateDiffString(originalNormalized, result);
|
|
169
129
|
const addedLines = countDiffLines(diffResult.diff, "+");
|
|
170
130
|
const removedLines = countDiffLines(diffResult.diff, "-");
|
|
171
131
|
const warningsBlock = warningsBlockOf(warnings);
|
|
172
132
|
|
|
173
|
-
const
|
|
174
|
-
const anchorRange = computeAffectedLineRange({
|
|
175
|
-
firstChangedLine,
|
|
176
|
-
lastChangedLine,
|
|
177
|
-
resultLineCount: resultLines.length,
|
|
178
|
-
});
|
|
179
|
-
const anchorsBlock = anchorRange
|
|
180
|
-
? (() => {
|
|
181
|
-
const region = resultLines.slice(anchorRange.start - 1, anchorRange.end);
|
|
182
|
-
const formatted = formatHashlineRegion(region, anchorRange.start);
|
|
183
|
-
const block = `--- Anchors ${anchorRange.start}-${anchorRange.end} ---\n${formatted}`;
|
|
184
|
-
return Buffer.byteLength(block, "utf8") <= CHANGED_ANCHOR_TEXT_BUDGET_BYTES
|
|
185
|
-
? block
|
|
186
|
-
: "Anchors omitted; use read for subsequent edits.";
|
|
187
|
-
})()
|
|
188
|
-
: resultLines.length === 0
|
|
189
|
-
? "File is empty. Use edit with prepend or append and omit pos to insert content."
|
|
190
|
-
: "Anchors omitted; use read for subsequent edits.";
|
|
191
|
-
|
|
192
|
-
const text = [anchorsBlock, warningsBlock.trimStart()]
|
|
133
|
+
const text = [diffResult.diff, warningsBlock.trimStart()]
|
|
193
134
|
.filter((section) => section.length > 0)
|
|
194
135
|
.join("\n\n");
|
|
195
136
|
|
|
@@ -198,8 +139,6 @@ export function buildChangedResponse(input: SuccessResponseInput): ToolResult {
|
|
|
198
139
|
editsAttempted,
|
|
199
140
|
noopEditsCount,
|
|
200
141
|
warningsCount: warnings?.length ?? 0,
|
|
201
|
-
firstChangedLine,
|
|
202
|
-
lastChangedLine,
|
|
203
142
|
addedLines,
|
|
204
143
|
removedLines,
|
|
205
144
|
});
|
|
@@ -208,7 +147,6 @@ export function buildChangedResponse(input: SuccessResponseInput): ToolResult {
|
|
|
208
147
|
content: [{ type: "text", text }],
|
|
209
148
|
details: {
|
|
210
149
|
diff: diffResult.diff,
|
|
211
|
-
firstChangedLine: firstChangedLine ?? diffResult.firstChangedLine,
|
|
212
150
|
snapshotId,
|
|
213
151
|
metrics,
|
|
214
152
|
},
|