pi-hashline-edit-pro 0.16.4 → 0.16.5
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-bulk-guidelines.md +1 -0
- package/prompts/replace-bulk.md +12 -0
- package/prompts/replace-flat-guidelines.md +1 -0
- package/prompts/replace-flat.md +12 -0
- package/prompts/replace-guidelines.md +1 -0
- package/prompts/replace.md +12 -0
- package/src/hashline/parse.ts +9 -9
- package/src/hashline/resolve.ts +7 -0
- package/src/replace-normalize.ts +27 -0
package/package.json
CHANGED
|
@@ -3,4 +3,5 @@
|
|
|
3
3
|
- On `[E_STALE_ANCHOR]`, call `read` to get fresh anchors, copy the 3-character HASH of the start and end of the range you are replacing into `hash_range_inclusive`, and retry.
|
|
4
4
|
- `hash_range_inclusive` replaces the ENTIRE range inclusively. Every line from the first anchor through the second anchor is deleted. Only put the replacement lines in `content_lines` — do not include lines that already exist outside the range.
|
|
5
5
|
- **Preserve leading whitespace (indentation) exactly.** The content after `│` in read output includes all leading spaces and tabs — copy them into `content_lines` unchanged.
|
|
6
|
+
- **`content_lines` must be a native JSON array of strings**, not a JSON string. Do NOT serialize it: `"content_lines": "[\"line1\", \"line2\"]"` is WRONG. Use `"content_lines": ["line1", "line2"]` instead.
|
|
6
7
|
- If the response includes a "Boundary duplication" warning, the edge line of your `content_lines` matches a line outside the replaced range. Remove that line from `content_lines` — the original is still in the file outside the range.
|
package/prompts/replace-bulk.md
CHANGED
|
@@ -80,6 +80,18 @@ Right:
|
|
|
80
80
|
```json
|
|
81
81
|
{ "content_lines": [...], "hash_range_inclusive": ["F4T", "F4T"] }
|
|
82
82
|
|
|
83
|
+
⚠️ Common mistake: do not serialize `content_lines` as a JSON string.
|
|
84
|
+
|
|
85
|
+
Wrong:
|
|
86
|
+
```json
|
|
87
|
+
{ "changes": [{ "content_lines": "[\"line1\", \"line2\"]", "hash_range_inclusive": ["F4T", "F4T"] }], "path": "src/main.ts" }
|
|
88
|
+
|
|
89
|
+
Right:
|
|
90
|
+
```json
|
|
91
|
+
{ "changes": [{ "content_lines": ["line1", "line2"], "hash_range_inclusive": ["F4T", "F4T"] }], "path": "src/main.ts" }
|
|
92
|
+
|
|
93
|
+
`content_lines` must be a native JSON array of strings, not a string that looks like an array. Pass it as a proper JSON array value.
|
|
94
|
+
|
|
83
95
|
Rules:
|
|
84
96
|
- `hash_range_inclusive` is a pair `[start, end]`. A single-line replace is `hash_range_inclusive: ["X", "X"]`.
|
|
85
97
|
- To delete a range, use `content_lines: []`.
|
|
@@ -3,4 +3,5 @@
|
|
|
3
3
|
- On `[E_STALE_ANCHOR]`, call `read` to get fresh anchors, copy the 3-character HASH of the start and end of the range you are replacing into `hash_range_inclusive`, and retry.
|
|
4
4
|
- `hash_range_inclusive` replaces the ENTIRE range inclusively. Every line from the first anchor through the second anchor is deleted. Only put the replacement lines in `content_lines` — do not include lines that already exist outside the range.
|
|
5
5
|
- **Preserve leading whitespace (indentation) exactly.** The content after `│` in read output includes all leading spaces and tabs — copy them into `content_lines` unchanged.
|
|
6
|
+
- **`content_lines` must be a native JSON array of strings**, not a JSON string. Do NOT serialize it: `"content_lines": "[\"line1\", \"line2\"]"` is WRONG. Use `"content_lines": ["line1", "line2"]` instead.
|
|
6
7
|
- If the response includes a "Boundary duplication" warning, the edge line of your `content_lines` matches a line outside the replaced range. Remove that line from `content_lines` — the original is still in the file outside the range.
|
package/prompts/replace-flat.md
CHANGED
|
@@ -65,6 +65,18 @@ Right:
|
|
|
65
65
|
```json
|
|
66
66
|
{ "content_lines": [...], "hash_range_inclusive": ["F4T", "F4T"] }
|
|
67
67
|
|
|
68
|
+
⚠️ Common mistake: do not serialize `content_lines` as a JSON string.
|
|
69
|
+
|
|
70
|
+
Wrong:
|
|
71
|
+
```json
|
|
72
|
+
{ "content_lines": "[\"line1\", \"line2\"]", "hash_range_inclusive": ["F4T", "F4T"], "path": "src/main.ts" }
|
|
73
|
+
|
|
74
|
+
Right:
|
|
75
|
+
```json
|
|
76
|
+
{ "content_lines": ["line1", "line2"], "hash_range_inclusive": ["F4T", "F4T"], "path": "src/main.ts" }
|
|
77
|
+
|
|
78
|
+
`content_lines` must be a native JSON array of strings, not a string that looks like an array. Pass it as a proper JSON array value.
|
|
79
|
+
|
|
68
80
|
Rules:
|
|
69
81
|
- `hash_range_inclusive` is a pair `[start, end]`. A single-line replace is `hash_range_inclusive: ["X", "X"]`.
|
|
70
82
|
- To delete a range, use `content_lines: []`.
|
|
@@ -3,5 +3,6 @@
|
|
|
3
3
|
- On `[E_STALE_ANCHOR]`, call `read` to get fresh anchors, copy the 3-character HASH of the start and end of the range you are replacing into `hash_range_inclusive`, and retry.
|
|
4
4
|
- `hash_range_inclusive` replaces the ENTIRE range inclusively. Every line from the first anchor through the second anchor is deleted. Only put the replacement lines in `content_lines` — do not include lines that already exist outside the range.
|
|
5
5
|
- **Preserve leading whitespace (indentation) exactly.** The content after `│` in read output includes all leading spaces and tabs — copy them into `content_lines` unchanged.
|
|
6
|
+
- **`content_lines` must be a native JSON array of strings**, not a JSON string. Do NOT serialize it: `"content_lines": "[\"line1\", \"line2\"]"` is WRONG. Use `"content_lines": ["line1", "line2"]` instead.
|
|
6
7
|
- Two modes are available: bulk (default, uses `changes` array) and flat (top-level `hash_range_inclusive`/`content_lines`). Toggle with `/toggle-replace-mode`.
|
|
7
8
|
- If the response includes a "Boundary duplication" warning, the edge line of your `content_lines` matches a line outside the replaced range. Remove that line from `content_lines` — the original is still in the file outside the range.
|
package/prompts/replace.md
CHANGED
|
@@ -127,6 +127,18 @@ Right:
|
|
|
127
127
|
```json
|
|
128
128
|
{ "content_lines": [...], "hash_range_inclusive": ["F4T", "F4T"] }
|
|
129
129
|
|
|
130
|
+
⚠️ Common mistake: do not serialize `content_lines` as a JSON string.
|
|
131
|
+
|
|
132
|
+
Wrong:
|
|
133
|
+
```json
|
|
134
|
+
{ "content_lines": "[\"line1\", \"line2\"]", "hash_range_inclusive": ["F4T", "F4T"] }
|
|
135
|
+
|
|
136
|
+
Right:
|
|
137
|
+
```json
|
|
138
|
+
{ "content_lines": ["line1", "line2"], "hash_range_inclusive": ["F4T", "F4T"] }
|
|
139
|
+
|
|
140
|
+
`content_lines` must be a native JSON array of strings, not a string that looks like an array. Pass it as a proper JSON array value.
|
|
141
|
+
|
|
130
142
|
Rules:
|
|
131
143
|
- `hash_range_inclusive` is a pair `[start, end]`. A single-line replace is `hash_range_inclusive: ["X", "X"]`.
|
|
132
144
|
- To delete a range, use `content_lines: []`.
|
package/src/hashline/parse.ts
CHANGED
|
@@ -56,13 +56,13 @@ function assertNoPrefixes(lines: string[]): void {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
export function parseText(edit: string[] | string | null): string[] {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
if (edit === null) return [];
|
|
60
|
+
if (typeof edit === "string") {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`[E_BAD_SHAPE] "content_lines" must be a native JSON array of strings, not a JSON string.`
|
|
63
|
+
+ ` Do not serialize the array (e.g. '["line1", "line2"]') — pass it as a proper JSON array: ["line1", "line2"].`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
assertNoPrefixes(edit);
|
|
67
|
+
return edit;
|
|
68
68
|
}
|
package/src/hashline/resolve.ts
CHANGED
|
@@ -148,6 +148,13 @@ function assertItem(edit: Record<string, unknown>, index: number): void {
|
|
|
148
148
|
throw new Error(`[E_BAD_SHAPE] Edit ${index} requires a "content_lines" field. Provide the replacement lines (use [] to delete).`);
|
|
149
149
|
}
|
|
150
150
|
if ("content_lines" in edit && !isStrArr(edit.content_lines)) {
|
|
151
|
+
const val = edit.content_lines;
|
|
152
|
+
if (typeof val === "string") {
|
|
153
|
+
throw new Error(
|
|
154
|
+
`[E_BAD_SHAPE] Edit ${index} field "content_lines" must be a native JSON array of strings, not a JSON string.`
|
|
155
|
+
+ ` Do not serialize the array (e.g. '["line1", "line2"]') — pass it as a proper JSON array: ["line1", "line2"].`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
151
158
|
throw new Error(`[E_BAD_SHAPE] Edit ${index} field "content_lines" must be a string array.`);
|
|
152
159
|
}
|
|
153
160
|
if (!isStrPair(edit.hash_range_inclusive)) {
|
package/src/replace-normalize.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { isRec, has } from "./utils";
|
|
2
2
|
|
|
3
|
+
function assertContentLinesNotString(
|
|
4
|
+
value: unknown,
|
|
5
|
+
label: string,
|
|
6
|
+
): void {
|
|
7
|
+
if (typeof value === "string") {
|
|
8
|
+
throw new Error(
|
|
9
|
+
`[E_BAD_SHAPE] ${label}: "content_lines" must be a native JSON array of strings, not a JSON string.`
|
|
10
|
+
+ ` Do not serialize the array (e.g. '["line1", "line2"]') — pass it as a proper JSON array: ["line1", "line2"].`
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
3
15
|
export function normalizeFilePath(record: Record<string, unknown>): void {
|
|
4
16
|
if (typeof record.path !== "string" && typeof record.file_path === "string") {
|
|
5
17
|
record.path = record.file_path;
|
|
@@ -31,9 +43,24 @@ export function normReq(input: unknown): unknown {
|
|
|
31
43
|
|
|
32
44
|
normalizeFilePath(record);
|
|
33
45
|
|
|
46
|
+
// Early validation: reject string-typed content_lines at the top level
|
|
47
|
+
if (has(record, "content_lines") && typeof record.content_lines === "string") {
|
|
48
|
+
assertContentLinesNotString(record.content_lines, "Top-level");
|
|
49
|
+
}
|
|
50
|
+
|
|
34
51
|
normalizeField(record, "changes", "changes");
|
|
35
52
|
normalizeField(record, "edits", "changes");
|
|
36
53
|
|
|
54
|
+
// Validate items in the changes array before wrapping flat format
|
|
55
|
+
if (Array.isArray(record.changes)) {
|
|
56
|
+
for (let i = 0; i < record.changes.length; i++) {
|
|
57
|
+
const item = record.changes[i];
|
|
58
|
+
if (isRec(item) && has(item, "content_lines") && typeof item.content_lines === "string") {
|
|
59
|
+
assertContentLinesNotString(item.content_lines, `changes[${i}]`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
37
64
|
if (!Array.isArray(record.changes) && has(record, "hash_range_inclusive") && has(record, "content_lines")) {
|
|
38
65
|
const hri = record.hash_range_inclusive;
|
|
39
66
|
const cl = record.content_lines;
|