pi-hashline-edit-pro 0.6.0 → 0.6.1
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 +1 -1
- package/prompts/replace.md +1 -0
- package/src/hashline/apply.ts +1 -2
- package/src/hashline/resolve.ts +6 -23
- package/src/replace.ts +0 -1
package/package.json
CHANGED
package/prompts/replace.md
CHANGED
|
@@ -57,6 +57,7 @@ Rules:
|
|
|
57
57
|
- Copy anchors from the most recent `read` of the file. Do not guess or construct them.
|
|
58
58
|
- All edits in one call must be non-conflicting. The runtime rejects with `[E_EDIT_CONFLICT]` if two ranges overlap.
|
|
59
59
|
- If `lines` matches current content, the replace is classified as `noop` (file unchanged).
|
|
60
|
+
- The `start`/`end` 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.
|
|
60
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
63
|
|
package/src/hashline/apply.ts
CHANGED
|
@@ -220,7 +220,6 @@ export function applyHashlineEdits(
|
|
|
220
220
|
edits: import("./resolve").HashlineEdit[],
|
|
221
221
|
signal?: AbortSignal,
|
|
222
222
|
precomputedHashes?: string[],
|
|
223
|
-
filePath?: string,
|
|
224
223
|
): {
|
|
225
224
|
content: string;
|
|
226
225
|
firstChangedLine: number | undefined;
|
|
@@ -262,7 +261,7 @@ export function applyHashlineEdits(
|
|
|
262
261
|
);
|
|
263
262
|
}
|
|
264
263
|
|
|
265
|
-
const barePrefixWarnings = assertNoBareHashPrefixLines(edits, lineIndex.fileLines, fileHashes
|
|
264
|
+
const barePrefixWarnings = assertNoBareHashPrefixLines(edits, lineIndex.fileLines, fileHashes);
|
|
266
265
|
warnings.push(...barePrefixWarnings);
|
|
267
266
|
maybeWarnSuspiciousUnicodeEscapePlaceholder(edits, warnings);
|
|
268
267
|
|
package/src/hashline/resolve.ts
CHANGED
|
@@ -78,15 +78,15 @@ export function formatMismatchError(
|
|
|
78
78
|
|
|
79
79
|
if (notFound.length > 0) {
|
|
80
80
|
const refList = notFound.map((m) => `"${m.ref.hash}"`).join(", ");
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
out.push(
|
|
82
|
+
`[E_STALE_ANCHOR] ${notFound.length} stale anchor${notFound.length > 1 ? "s" : ""}: ${refList}. Call read() to get fresh anchors, then copy the 4-character HASH from each line into your next replace call.`
|
|
83
|
+
);
|
|
84
84
|
}
|
|
85
85
|
if (ambiguous.length > 0) {
|
|
86
86
|
if (out.length > 0) out.push("");
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
out.push(
|
|
88
|
+
`[E_AMBIGUOUS_ANCHOR] ${ambiguous.length} ambiguous anchor${ambiguous.length > 1 ? "s" : ""}. Call read() to get fresh anchors, then copy the 4-character HASH from each line into your next replace call.`
|
|
89
|
+
);
|
|
90
90
|
for (const m of ambiguous) {
|
|
91
91
|
const sample = (m.candidates ?? []).slice(0, 5);
|
|
92
92
|
const more =
|
|
@@ -105,15 +105,6 @@ export function formatMismatchError(
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
out.push("");
|
|
109
|
-
out.push("Current state (first lines):");
|
|
110
|
-
const sampleSize = Math.min(fileLines.length, 5);
|
|
111
|
-
for (let i = 0; i < sampleSize; i++) {
|
|
112
|
-
out.push(`>>> ${fileHashes[i]}│${fileLines[i]}`);
|
|
113
|
-
}
|
|
114
|
-
if (fileLines.length > sampleSize) {
|
|
115
|
-
out.push(`... ${fileLines.length - sampleSize} more.`);
|
|
116
|
-
}
|
|
117
108
|
|
|
118
109
|
return out.join("\n");
|
|
119
110
|
}
|
|
@@ -198,7 +189,6 @@ export function assertNoBareHashPrefixLines(
|
|
|
198
189
|
edits: HashlineEdit[],
|
|
199
190
|
fileLines: string[],
|
|
200
191
|
fileHashes: string[],
|
|
201
|
-
filePath?: string,
|
|
202
192
|
): string[] {
|
|
203
193
|
if (fileHashes.length !== fileLines.length) {
|
|
204
194
|
throw new Error(
|
|
@@ -216,18 +206,11 @@ export function assertNoBareHashPrefixLines(
|
|
|
216
206
|
}
|
|
217
207
|
if (suspects.length === 0) return [];
|
|
218
208
|
|
|
219
|
-
const isPython = filePath?.endsWith('.py');
|
|
220
209
|
const fileHashSet = new Set(fileHashes);
|
|
221
210
|
const matched = suspects.filter((s) => fileHashSet.has(s.hash));
|
|
222
211
|
const matchedCount = matched.length;
|
|
223
212
|
const exampleLine = `${suspects[0]!.hash}│${suspects[0]!.line}`;
|
|
224
213
|
|
|
225
|
-
if (isPython) {
|
|
226
|
-
const hint = matchedCount > 0
|
|
227
|
-
? `${matchedCount} prefix(es) match file line hashes.`
|
|
228
|
-
: `None match file line hashes — likely Python syntax.`;
|
|
229
|
-
return [`[W_BARE_HASH_PREFIX] ${suspects.length} edit line(s) start with a hash-like prefix (e.g. ${JSON.stringify(exampleLine)}). ${hint}`];
|
|
230
|
-
}
|
|
231
214
|
|
|
232
215
|
const linesHint =
|
|
233
216
|
matchedCount === 0
|