pi-hashline-edit-pro 0.3.5 → 0.3.7
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/index.ts +4 -3
- package/package.json +1 -1
- package/src/edit-response.ts +1 -5
- package/src/edit.ts +1 -1
- package/src/hashline/apply.ts +2 -17
- package/src/hashline/index.ts +1 -0
- package/src/hashline/resolve.ts +16 -11
- package/src/read.ts +2 -9
- package/src/utils.ts +16 -0
package/index.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { join, isAbsolute } from "path";
|
|
|
4
4
|
import { computeLineHashes, formatHashlineRegion } from "./src/hashline";
|
|
5
5
|
import { registerEditTool } from "./src/edit";
|
|
6
6
|
import { registerReadTool } from "./src/read";
|
|
7
|
+
import { normalizeToLF } from "./src/edit-diff";
|
|
8
|
+
import { getVisibleLines } from "./src/utils";
|
|
7
9
|
|
|
8
10
|
export default function (pi: ExtensionAPI): void {
|
|
9
11
|
registerReadTool(pi);
|
|
@@ -22,9 +24,8 @@ export default function (pi: ExtensionAPI): void {
|
|
|
22
24
|
const content = await readFile(absolutePath, "utf-8");
|
|
23
25
|
|
|
24
26
|
// Normalize and compute hashline output
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const visibleLines = normalized.endsWith("\n") ? lines.slice(0, -1) : lines;
|
|
27
|
+
const normalized = normalizeToLF(content);
|
|
28
|
+
const visibleLines = getVisibleLines(normalized);
|
|
28
29
|
|
|
29
30
|
if (visibleLines.length === 0) return;
|
|
30
31
|
|
package/package.json
CHANGED
package/src/edit-response.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
formatHashlineRegion,
|
|
7
7
|
} from "./hashline";
|
|
8
8
|
import { formatHashlineReadPreview } from "./read";
|
|
9
|
+
import { getVisibleLines } from "./utils";
|
|
9
10
|
|
|
10
11
|
type ToolResult = {
|
|
11
12
|
content: Array<{ type: "text"; text: string }>;
|
|
@@ -96,11 +97,6 @@ export interface SuccessResponseInput {
|
|
|
96
97
|
|
|
97
98
|
// ─── Helpers ────────────────────────────────────────────────────────────
|
|
98
99
|
|
|
99
|
-
function getVisibleLines(text: string): string[] {
|
|
100
|
-
if (text.length === 0) return [];
|
|
101
|
-
const lines = text.split("\n");
|
|
102
|
-
return text.endsWith("\n") ? lines.slice(0, -1) : lines;
|
|
103
|
-
}
|
|
104
100
|
|
|
105
101
|
function countDiffLines(diff: string, marker: "+" | "-"): number {
|
|
106
102
|
if (!diff) return 0;
|
package/src/edit.ts
CHANGED
|
@@ -322,7 +322,7 @@ async function executeEditPipeline(
|
|
|
322
322
|
: [];
|
|
323
323
|
|
|
324
324
|
if (toolEdits.length === 0) {
|
|
325
|
-
throw new Error("
|
|
325
|
+
throw new Error("[E_BAD_SHAPE] Edit request requires a non-empty \"edits\" array.");
|
|
326
326
|
}
|
|
327
327
|
|
|
328
328
|
throwIfAborted(signal);
|
package/src/hashline/apply.ts
CHANGED
|
@@ -6,10 +6,12 @@ import {
|
|
|
6
6
|
assertNoBareHashPrefixLines,
|
|
7
7
|
maybeWarnSuspiciousUnicodeEscapePlaceholder,
|
|
8
8
|
formatMismatchError,
|
|
9
|
+
describeEdit,
|
|
9
10
|
type ResolvedHashlineEdit,
|
|
10
11
|
type NoopEdit,
|
|
11
12
|
type HashlineEdit,
|
|
12
13
|
} from "./resolve";
|
|
14
|
+
import { countVisibleLines } from "../utils";
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
type LineIndex = {
|
|
@@ -58,16 +60,6 @@ function assertDoesNotEmptyFile(originalContent: string, result: string): void {
|
|
|
58
60
|
}
|
|
59
61
|
}
|
|
60
62
|
|
|
61
|
-
function describeEdit(edit: ResolvedHashlineEdit): string {
|
|
62
|
-
switch (edit.op) {
|
|
63
|
-
case "replace":
|
|
64
|
-
return `replace ${edit.start.hash}-${edit.end.hash}`;
|
|
65
|
-
case "append":
|
|
66
|
-
return edit.pos ? `append after ${edit.pos.hash}` : "append at EOF";
|
|
67
|
-
case "prepend":
|
|
68
|
-
return edit.pos ? `prepend before ${edit.pos.hash}` : "prepend at BOF";
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
63
|
|
|
72
64
|
function throwEditConflict(
|
|
73
65
|
left: { index: number; label: string },
|
|
@@ -521,13 +513,6 @@ export function computeChangedLineRange(
|
|
|
521
513
|
): { firstChangedLine: number; lastChangedLine: number } | null {
|
|
522
514
|
if (original === result) return null;
|
|
523
515
|
|
|
524
|
-
function countVisibleLines(text: string): number {
|
|
525
|
-
if (text.length === 0) {
|
|
526
|
-
return 0;
|
|
527
|
-
}
|
|
528
|
-
const lines = text.split("\n");
|
|
529
|
-
return text.endsWith("\n") ? lines.length - 1 : lines.length;
|
|
530
|
-
}
|
|
531
516
|
|
|
532
517
|
if (original.length === 0) {
|
|
533
518
|
return {
|
package/src/hashline/index.ts
CHANGED
package/src/hashline/resolve.ts
CHANGED
|
@@ -157,7 +157,7 @@ function assertEditItem(edit: Record<string, unknown>, index: number): void {
|
|
|
157
157
|
`[E_BAD_OP] Edit ${index} uses unknown op "${edit.op}". Expected "replace", "append", or "prepend".`,
|
|
158
158
|
);
|
|
159
159
|
}
|
|
160
|
-
if ("pos" in edit && typeof edit.pos !== "string") {
|
|
160
|
+
if ("pos" in edit && typeof edit.pos !== "string" && edit.op !== "replace") {
|
|
161
161
|
throw new Error(
|
|
162
162
|
`[E_BAD_SHAPE] Edit ${index} field "pos" must be a string when provided.`,
|
|
163
163
|
);
|
|
@@ -303,6 +303,21 @@ export function assertNoBareHashPrefixLines(
|
|
|
303
303
|
);
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Human-readable label for a resolved edit (used in warnings and conflict errors).
|
|
309
|
+
*/
|
|
310
|
+
export function describeEdit(edit: ResolvedHashlineEdit): string {
|
|
311
|
+
switch (edit.op) {
|
|
312
|
+
case "replace":
|
|
313
|
+
return `replace ${edit.start.hash}-${edit.end.hash}`;
|
|
314
|
+
case "append":
|
|
315
|
+
return edit.pos ? `append after ${edit.pos.hash}` : "append at EOF";
|
|
316
|
+
case "prepend":
|
|
317
|
+
return edit.pos ? `prepend before ${edit.pos.hash}` : "prepend at BOF";
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
306
321
|
export function validateAnchorEdits(
|
|
307
322
|
edits: HashlineEdit[],
|
|
308
323
|
fileLines: string[],
|
|
@@ -327,16 +342,6 @@ export function validateAnchorEdits(
|
|
|
327
342
|
return result;
|
|
328
343
|
};
|
|
329
344
|
|
|
330
|
-
function describeEdit(edit: ResolvedHashlineEdit): string {
|
|
331
|
-
switch (edit.op) {
|
|
332
|
-
case "replace":
|
|
333
|
-
return `replace ${edit.start.hash}-${edit.end.hash}`;
|
|
334
|
-
case "append":
|
|
335
|
-
return edit.pos ? `append after ${edit.pos.hash}` : "append at EOF";
|
|
336
|
-
case "prepend":
|
|
337
|
-
return edit.pos ? `prepend before ${edit.pos.hash}` : "prepend at BOF";
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
345
|
|
|
341
346
|
for (const edit of edits) {
|
|
342
347
|
throwIfAborted(signal);
|
package/src/read.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { computeLineHashes, formatHashlineRegion } from "./hashline";
|
|
|
17
17
|
import { resolveToCwd } from "./path-utils";
|
|
18
18
|
import { throwIfAborted } from "./runtime";
|
|
19
19
|
import { getFileSnapshot } from "./snapshot";
|
|
20
|
+
import { getVisibleLines } from "./utils";
|
|
20
21
|
|
|
21
22
|
const READ_DESC = readFileSync(
|
|
22
23
|
new URL("../prompts/read.md", import.meta.url),
|
|
@@ -55,21 +56,13 @@ function normalizePositiveInteger(
|
|
|
55
56
|
return value;
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
function getPreviewLines(text: string): string[] {
|
|
59
|
-
if (text.length === 0) {
|
|
60
|
-
return [];
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const lines = text.split("\n");
|
|
64
|
-
return text.endsWith("\n") ? lines.slice(0, -1) : lines;
|
|
65
|
-
}
|
|
66
59
|
|
|
67
60
|
export function formatHashlineReadPreview(
|
|
68
61
|
text: string,
|
|
69
62
|
options: { offset?: number; limit?: number },
|
|
70
63
|
precomputedHashes?: string[],
|
|
71
64
|
): { text: string; truncation?: TruncationResult; nextOffset?: number } {
|
|
72
|
-
const allLines =
|
|
65
|
+
const allLines = getVisibleLines(text);
|
|
73
66
|
const totalLines = allLines.length;
|
|
74
67
|
const startLine = normalizePositiveInteger(options.offset, "offset") ?? 1;
|
|
75
68
|
if (totalLines === 0) {
|
package/src/utils.ts
CHANGED
|
@@ -9,3 +9,19 @@ export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
9
9
|
export function hasOwn(record: Record<string, unknown>, key: string): boolean {
|
|
10
10
|
return Object.hasOwn(record, key);
|
|
11
11
|
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Return the visible lines of a text (excluding the terminal-newline sentinel).
|
|
15
|
+
*/
|
|
16
|
+
export function getVisibleLines(text: string): string[] {
|
|
17
|
+
if (text.length === 0) return [];
|
|
18
|
+
const lines = text.split("\n");
|
|
19
|
+
return text.endsWith("\n") ? lines.slice(0, -1) : lines;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Count the visible lines of a text (excluding the terminal-newline sentinel).
|
|
24
|
+
*/
|
|
25
|
+
export function countVisibleLines(text: string): number {
|
|
26
|
+
return getVisibleLines(text).length;
|
|
27
|
+
}
|