pi-hashline-edit-pro 6.0.0 → 6.0.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/copy-snippet.md +1 -1
- package/prompts/copy.md +2 -4
- package/prompts/grep-guidelines.md +1 -0
- package/prompts/grep.md +1 -1
- package/prompts/insert.md +1 -3
- package/prompts/move-guidelines.md +1 -0
- package/prompts/move-snippet.md +1 -1
- package/prompts/move.md +2 -4
- package/prompts/read.md +1 -1
- package/prompts/replace-guidelines.md +2 -3
- package/prompts/replace-match-guidelines.md +1 -1
- package/prompts/replace-match.md +1 -5
- package/prompts/replace-snippet.md +1 -1
- package/prompts/replace.md +1 -5
- package/prompts/transfer-guidelines.md +2 -0
- package/prompts/undo-last-change.md +1 -1
- package/src/constants.ts +1 -1
- package/src/edit-common.ts +48 -25
- package/src/grep.ts +7 -2
- package/src/insert.ts +1 -1
- package/src/replace-match.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-hashline-edit-pro",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Hash-anchored read/replace/insert/grep tools for pi-coding-agent. Every line gets a unique 4-char tokenizer-friendly anchor that stays stable across edits; stale or ambiguous anchors are rejected, never fuzzy-matched. Undo persists across restarts.",
|
|
6
6
|
"main": "index.ts",
|
package/prompts/copy-snippet.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Copy lines to another position by anchor: `source_from`/`source_to` mark the range, the copy lands after `insert_after`;
|
|
1
|
+
Copy lines to another position by anchor: `source_from`/`source_to` mark the range, the copy lands after `insert_after`; each anchor resolves its own file
|
package/prompts/copy.md
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
Copy a range of lines to another position, targeted by 4-character anchors from any served anchor│content row. Give `source_from` and `source_to` as bare anchors marking the first and last line to copy in the source file, and `insert_after` as the bare anchor of the destination line after which the copy goes.
|
|
1
|
+
Copy a range of lines to another position, targeted by 4-character anchors from any served anchor│content row. Give `source_from` and `source_to` as bare anchors marking the first and last line to copy in the source file, and `insert_after` as the bare anchor of the destination line after which the copy goes. The source lines stay in place, and the copied lines are written exactly as the source file holds them.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Same-file calls in one message batch, and a cross-file copy joins the destination file's batch: earlier calls reply `In batch N (queued)` and the last call shows the combined diff, with one undo for the whole batch. A copy always duplicates the content its source anchors were served from, so a same-message edit to the source file does not change what is copied.
|
|
3
|
+
A cross-file copy joins the destination file's batch. A copy always duplicates the content its source anchors were served from, so a same-message edit to the source file does not change what is copied.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
- `anchor_grep`: prefer it over shell `rg`/`grep` for searching, so hits carry anchors and need no `read`; use `literal: true` when the pattern contains regex metacharacters.
|
package/prompts/grep.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Search text files with ripgrep. Hits and context lines come back as `lineNumber │ anchor│content` rows, editable with replace, insert, copy, or move without a new read; the `=== path ===` header and line numbers locate the match. Searches respect `.gitignore`, always skip `.git`, and skip binary and image files silently. A match over 500 bytes is shown as a `...` fragment around the hit, but its anchor still covers the whole line. When cut by match limit, raise `limit` (default 100); when cut by rows/bytes, refine `pattern` / narrow path.
|
|
1
|
+
Search text files with ripgrep. Hits and context lines come back as `lineNumber │ anchor│content` rows, editable with replace, insert, copy, or move without a new read; the `=== path ===` header and line numbers locate the match. Patterns are regex by default; set `literal: true` to search exact text, `ignoreCase` for case-insensitive search, and `context` for surrounding rows. Avoid backreferences, nested quantifiers, and quantified alternation. Searches respect `.gitignore`, always skip `.git`, and skip binary and image files silently. A match over 500 bytes is shown as a `...` fragment around the hit, but its anchor still covers the whole line. When cut by match limit, raise `limit` (default 100); when cut by rows/bytes, refine `pattern` / narrow path.
|
package/prompts/insert.md
CHANGED
|
@@ -1,3 +1 @@
|
|
|
1
|
-
Insert text after or before one existing line in a text file, addressed by a bare anchor from any served anchor│content row. `text` goes after the anchor line with `direction: "after"` or before it with `direction: "before"`. `text` is one string holding the exact text to insert.
|
|
2
|
-
|
|
3
|
-
Same-file calls in one message batch: earlier calls reply `In batch N (queued)` and the last call shows the combined diff, with one undo for the whole batch.
|
|
1
|
+
Insert text after or before one existing line in a text file, addressed by the `anchor` field (a bare anchor from any served anchor│content row). `text` goes after the anchor line with `direction: "after"` or before it with `direction: "before"`. `text` is one string holding the exact text to insert. A trailing line break sets the last line's ending instead of adding a blank line, so a blank line next to the anchor line must be an extra break in `text`: `"x\n"` leaves the anchor line directly after `x`, and `"x\n\n"` leaves one blank line between them (`direction: "after"` mirrors this at the start of `text`).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
- `move`: moving a range to where it already sits reports `No changes made` and leaves the anchors alone.
|
package/prompts/move-snippet.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Move lines to another position by anchor: `source_from`/`source_to` mark the range, it lands after `insert_after`;
|
|
1
|
+
Move lines to another position by anchor: `source_from`/`source_to` mark the range, it lands after `insert_after`; each anchor resolves its own file
|
package/prompts/move.md
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
Move a range of lines to another position, targeted by 4-character anchors from any served anchor│content row. Give `source_from` and `source_to` as bare anchors marking the first and last line to move in the source file, and `insert_after` as the bare anchor of the destination line after which the range goes. `
|
|
1
|
+
Move a range of lines to another position, targeted by 4-character anchors from any served anchor│content row. Give `source_from` and `source_to` as bare anchors marking the first and last line to move in the source file, and `insert_after` as the bare anchor of the destination line after which the range goes. `insert_after` must sit outside the source range when both anchors share a file; moving a range in one file to where it already sits reports no change.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
A same-file `move` and a cross-file `move` join the same-message batch of the destination file; a cross-file `move` whose source file also has batched edits in the message commits on its own. A batched cross-file `move` commits its source removal with the batch but shows only the destination diff; read the source file for fresh anchors.
|
|
3
|
+
A cross-file `move` whose source file also has batched edits in the message commits on its own. A batched cross-file `move` commits its source removal with the batch but shows only the destination diff; read the source file for fresh anchors.
|
package/prompts/read.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Read a text file and return it as `anchor│content` rows, one per line: a 4-character anchor (letters only, case-sensitive), the `│` separator, then the line content. Target lines in
|
|
1
|
+
Read a text file and return it as `anchor│content` rows, one per line: a 4-character anchor (letters only, case-sensitive), the `│` separator, then the line content. Target lines in edits by anchor, never by content or line number. Page long files with `offset` and `limit`; when the output says truncated, continue with the hinted `offset/limit`. Images attach visually; binary, directory, and UTF-16/UTF-32 text are rejected; an empty file returns one empty row you can replace to seed content.
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
- `replace`: same-message calls must target disjoint ranges
|
|
1
|
+
- `replace`: same-message calls must target disjoint ranges.
|
|
2
2
|
- `replace`: a pure deletion (`text: ""`) is the cheap way to clear a large range: it verifies only the first and last line against the served record and removes the interior as it currently stands.
|
|
3
|
-
- `replace`:
|
|
4
|
-
- `replace`: for a single line, use the same anchor for `remove_from` and `remove_to`; a pasted `anchor│` prefix in `text` is stripped.
|
|
3
|
+
- `replace`: for a single line, use the same anchor for `remove_from` and `remove_to`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
- `replace_match`:
|
|
1
|
+
- `replace_match`: `old_string` is usually a fragment of the line, not the whole line; every occurrence inside the range is replaced, so scope the range with anchors when only some occurrences should change.
|
package/prompts/replace-match.md
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
1
|
Replace part of a line (or a range of lines) without retyping the rest. `replace_from` and `replace_to` are bare anchors from served `anchor│content` rows marking the first and last line of the range; use the same anchor for one line. `old_string` is the text to find inside that range, and `new_string` replaces every occurrence of it.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
JSON decoding happens once, before the tool; the tool writes the string it receives and never decodes — `\uXXXX` is the character, `\\uXXXX` the literal text. A missing match is refused with the current rows, so the retry needs no read. The two boundary anchors must still match what was last shown; lines strictly inside the range are matched against the file as it is on disk.
|
|
6
|
-
|
|
7
|
-
Same-file calls in one message batch: earlier calls reply `In batch N (queued)` and the last call shows the combined diff, with one undo for the whole batch. A missing `old_string` aborts the whole batch unwritten.
|
|
3
|
+
The two boundary anchors must still match what was last shown; lines strictly inside the range are matched against the file as it is on disk.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Replace lines by anchor: bare anchors in `remove_from`/`remove_to`, the exact replacement text in `text`
|
|
1
|
+
Replace lines by anchor: bare anchors in `remove_from`/`remove_to`, the exact replacement text in `text`
|
package/prompts/replace.md
CHANGED
|
@@ -1,6 +1,2 @@
|
|
|
1
|
-
Replace a range of lines (or a single line) in a text file by anchor. `remove_from` and `remove_to` are the 4-character anchors of the first and last line to remove, and `text` is one string holding the exact replacement text.
|
|
1
|
+
Replace a range of lines (or a single line) in a text file by anchor. `remove_from` and `remove_to` are the 4-character anchors of the first and last line to remove, and `text` is one string holding the exact replacement text. The text is written exactly as given, and nothing else in the file changes.
|
|
2
2
|
To change only part of a line without retyping the rest, use `replace_match` instead; it preserves every character the request does not name. Deleting every line empties the file; the result names the new empty-line anchor, so a follow-up `replace` on it can seed content without a `read`.
|
|
3
|
-
|
|
4
|
-
Same-file calls in one message batch: earlier calls reply `In batch N (queued)` and the last call shows the combined diff, with one undo for the whole batch.
|
|
5
|
-
|
|
6
|
-
Example: read served `Hasu│old` and `arvm│old2`. Call { "remove_from": "Hasu", "remove_to": "arvm", "text": "new line 1\nnew line 2" }. The post-edit diff shows `-Hasu│old`, `-arvm│old2`, `+Qwer│new line 1`: the `-` rows are dead anchors now; the `+` and ` ` rows are live anchors for the next edit.
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
- `copy`/`move`: for a block transfer, only the source's two boundary rows and the destination line need serving; the interior transfers verbatim and the block lands in one commit. To append at the end, use the destination's last served line as `insert_after`.
|
|
2
|
+
- `copy`/`move`: `insert_after` must sit outside the source range; `source_to` places the block right after itself.
|
|
3
|
+
- `copy`/`move`: `source_from` and `source_to` may live in a different file than `insert_after`; an empty destination file is seeded with the copied or moved lines.
|
|
2
4
|
- `move`: a batched cross-file move does not display the source file's diff; its removal commits with the batch, and the source file needs a read for fresh anchors.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Undo the last replace, replace_match, insert, copy, or move on a file, restoring the previous content, BOM, and line endings. Use it when an edit removed or changed the wrong lines. If the file changed since that edit, the undo is refused
|
|
1
|
+
Undo the last replace, replace_match, insert, copy, or move on a file, restoring the previous content, BOM, and line endings. Use it when an edit removed or changed the wrong lines. If the file changed since that edit, the undo is refused and the file is left untouched; do not force it. A file deleted since the edit is restored. If the diff is truncated, use read to see the full file.
|
package/src/constants.ts
CHANGED
|
@@ -20,7 +20,7 @@ export const TEXT_NOT_STRING_MSG =
|
|
|
20
20
|
`[E_BAD_SHAPE] "text" must be a string holding the exact text to insert. Use "\\n" for one blank line; line breaks inside the string separate lines.`;
|
|
21
21
|
|
|
22
22
|
export const NUL_CONTENT_MSG =
|
|
23
|
-
`[E_BAD_SHAPE] Content contains a NUL byte (U+0000); a text file cannot contain NUL, and writing it would break further reads and edits. Remove the NUL byte and retry
|
|
23
|
+
`[E_BAD_SHAPE] Content contains a NUL byte (U+0000); a text file cannot contain NUL, and writing it would break further reads and edits. Remove the NUL byte and retry.`;
|
|
24
24
|
|
|
25
25
|
export const ANCHOR_POOL_EXHAUSTED_PREFIX =
|
|
26
26
|
"[E_FILE_TOO_LARGE] The session's anchor pool is exhausted";
|
package/src/edit-common.ts
CHANGED
|
@@ -43,39 +43,63 @@ function preferenceGuideline(flags: EditToolFlags): string {
|
|
|
43
43
|
return `Prefer the hashline tools for anything that touches files: ${joinOps(tools, { backtick: true })}.`;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
const SHARED_EDIT_OPS = ["replace", "replace_match", "insert", "copy", "move"];
|
|
47
|
+
const SHARED_PAYLOAD_OPS = ["replace", "replace_match", "insert"];
|
|
48
|
+
const SHARED_DIFF_OPS = ["replace", "replace_match", "insert", "copy", "move", "undo_last_change"];
|
|
49
|
+
|
|
50
|
+
function operationNames(ops: string[], flags: EditToolFlags): string {
|
|
51
|
+
return joinOps(gatedEditOps(ops, flags), { backtick: true, separator: "/" });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function batchGuideline(flags: EditToolFlags): string {
|
|
55
|
+
const tools = operationNames(SHARED_EDIT_OPS, flags);
|
|
56
|
+
const outcome = flags.autoRead ? "diff" : "result";
|
|
57
|
+
return `${tools}: same-file calls in one message are grouped into one batch; earlier calls reply \`In batch N (queued)\` and the last call shows the combined ${outcome}, with one undo for the whole batch.`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function diffGuideline(flags: EditToolFlags): string {
|
|
61
|
+
const tools = operationNames(SHARED_DIFF_OPS, flags);
|
|
62
|
+
return `${tools}: in the post-edit diff, \`-anchor│\` rows are dead anchors; \`+anchor│\` and \` anchor│\` rows are live anchors for the next edit.`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function pathGuideline(flags: EditToolFlags): string {
|
|
66
|
+
const tools = operationNames(SHARED_EDIT_OPS, flags);
|
|
67
|
+
return flags.requirePath
|
|
68
|
+
? `${tools}: pass \`path\` matching the file the anchors were served for; it is required and must match anchor ownership.`
|
|
69
|
+
: `${tools}: path resolution is anchor-only; do not pass \`path\`.`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function payloadGuideline(flags: EditToolFlags): string {
|
|
73
|
+
const tools = operationNames(SHARED_PAYLOAD_OPS, flags);
|
|
74
|
+
return `${tools}: JSON decoding happens once, before the tool; the tool writes the string it receives and never decodes — \`\\uXXXX\` is the character, \`\\\\uXXXX\` the literal text.`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function strictInputGuideline(flags: EditToolFlags): string {
|
|
78
|
+
const tools = operationNames(SHARED_EDIT_OPS, flags);
|
|
79
|
+
return `${tools}: strict-input mode is on; auto-fixable slips are rejected instead of fixed with warnings.`;
|
|
80
|
+
}
|
|
81
|
+
|
|
46
82
|
function finalizePrompts(
|
|
47
83
|
description: string,
|
|
48
84
|
snippet: string,
|
|
49
85
|
guidelines: string[],
|
|
50
86
|
flags: EditToolFlags,
|
|
51
|
-
|
|
87
|
+
options?: { stringPayload?: boolean },
|
|
52
88
|
): { description: string; snippet: string; guidelines: string[] } {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
if (flags
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
} else {
|
|
59
|
-
descriptionParts.push("Path resolution is anchor-only; do not pass `path`.");
|
|
60
|
-
}
|
|
61
|
-
if (flags.strictInput) {
|
|
62
|
-
descriptionParts.push("Strict-input mode is on: auto-fixable slips are rejected instead of fixed with warnings.");
|
|
63
|
-
}
|
|
64
|
-
return { description: descriptionParts.join(" "), snippet: snippetParts.join(""), guidelines };
|
|
89
|
+
const shared = [batchGuideline(flags), pathGuideline(flags)];
|
|
90
|
+
if (flags.autoRead) shared.push(diffGuideline(flags));
|
|
91
|
+
if (options?.stringPayload !== false) shared.push(payloadGuideline(flags));
|
|
92
|
+
if (flags.strictInput) shared.push(strictInputGuideline(flags));
|
|
93
|
+
return { description, snippet, guidelines: [...guidelines, ...shared] };
|
|
65
94
|
}
|
|
66
95
|
|
|
67
96
|
export function withReplacePrompts(base: { description: string; snippet: string; guidelines: string[] }, flags: EditToolFlags): { description: string; snippet: string; guidelines: string[] } {
|
|
68
97
|
let description = base.description;
|
|
69
|
-
|
|
70
|
-
if (!flags.autoRead) {
|
|
71
|
-
description = description.replace(/\n\nExample:[\s\S]*$/, "");
|
|
72
|
-
guidelines = guidelines.filter((guideline) => !guideline.includes("post-edit diff"));
|
|
73
|
-
description = description.replace("and the last call shows the combined diff,", "and the last call shows the combined result,");
|
|
74
|
-
}
|
|
98
|
+
const guidelines = [preferenceGuideline(flags), ...base.guidelines];
|
|
75
99
|
if (!flags.replaceMatchEnabled) {
|
|
76
100
|
description = description.replace(/\n?To change only part of a line without retyping the rest, use `replace_match` instead; it preserves every character the request does not name\./, "");
|
|
77
101
|
}
|
|
78
|
-
return finalizePrompts(description, base.snippet, guidelines, flags
|
|
102
|
+
return finalizePrompts(description, base.snippet, guidelines, flags);
|
|
79
103
|
}
|
|
80
104
|
|
|
81
105
|
export function withReadPrompts(base: { description: string; snippet: string; guidelines: string[] }, flags: EditToolFlags): { description: string; snippet: string; guidelines: string[] } {
|
|
@@ -92,14 +116,13 @@ export function withReadPrompts(base: { description: string; snippet: string; gu
|
|
|
92
116
|
}
|
|
93
117
|
|
|
94
118
|
export function withInsertPrompts(base: { description: string; snippet: string; guidelines: string[] }, flags: EditToolFlags): { description: string; snippet: string; guidelines: string[] } {
|
|
95
|
-
const baseDescription = flags.autoRead ? base.description : base.description.replace("and the last call shows the combined diff,", "and the last call shows the combined result,");
|
|
96
119
|
const guidelines = [...base.guidelines];
|
|
97
|
-
return finalizePrompts(
|
|
120
|
+
return finalizePrompts(base.description, base.snippet, guidelines, flags);
|
|
98
121
|
}
|
|
99
122
|
|
|
100
123
|
export function withReplaceMatchPrompts(base: { description: string; snippet: string; guidelines: string[] }, flags: EditToolFlags): { description: string; snippet: string; guidelines: string[] } {
|
|
101
124
|
const guidelines = [...base.guidelines];
|
|
102
|
-
return finalizePrompts(base.description, base.snippet, guidelines, flags
|
|
125
|
+
return finalizePrompts(base.description, base.snippet, guidelines, flags);
|
|
103
126
|
}
|
|
104
127
|
|
|
105
128
|
function gatedEditOps(ops: string[], flags: EditToolFlags): string[] {
|
|
@@ -119,7 +142,7 @@ function joinOps(ops: string[], options?: { backtick?: boolean; separator?: "/"
|
|
|
119
142
|
return formatted.length === 2 ? `${head} or ${last}` : `${head}, or ${last}`;
|
|
120
143
|
}
|
|
121
144
|
|
|
122
|
-
export function withGrepPrompts(base: { description: string; snippet: string }, flags: EditToolFlags): { description: string; snippet: string } {
|
|
145
|
+
export function withGrepPrompts(base: { description: string; snippet: string; guidelines: string[] }, flags: EditToolFlags): { description: string; snippet: string; guidelines: string[] } {
|
|
123
146
|
if (flags.copyMoveEnabled) return base;
|
|
124
147
|
return { ...base, description: base.description.replaceAll("replace, insert, copy, or move", joinOps(gatedEditOps(["replace", "insert", "copy", "move"], flags))) };
|
|
125
148
|
}
|
|
@@ -145,7 +168,7 @@ export function withUndoPrompts(base: { description: string; snippet: string; gu
|
|
|
145
168
|
|
|
146
169
|
export function withTransferPrompts(base: { description: string; snippet: string; guidelines: string[] }, flags: EditToolFlags): { description: string; snippet: string; guidelines: string[] } {
|
|
147
170
|
const guidelines = [...base.guidelines];
|
|
148
|
-
return finalizePrompts(base.description, base.snippet, guidelines, flags,
|
|
171
|
+
return finalizePrompts(base.description, base.snippet, guidelines, flags, { stringPayload: false });
|
|
149
172
|
}
|
|
150
173
|
|
|
151
174
|
function staleAnchorMessage(ref: string, hash: string, owners: Array<OwnedAnchor | undefined>): string {
|
package/src/grep.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { globToRegex } from "./glob";
|
|
|
11
11
|
import { MAX_HASH_LINES, fmtRow, HASH_LEN, HASH_SEP, HASH_CLASS } from "./hashline";
|
|
12
12
|
import { ANCHOR_POOL_EXHAUSTED_PREFIX, MAX_GREP_LINE_BYTES } from "./constants";
|
|
13
13
|
import { toCwd, toDisplayPath } from "./paths";
|
|
14
|
-
import { loadP } from "./prompts";
|
|
14
|
+
import { loadP, loadGuide } from "./prompts";
|
|
15
15
|
import { normReq } from "./payload-contract";
|
|
16
16
|
import { DEFAULT_EDIT_FLAGS, withGrepPrompts, type EditToolFlags } from "./edit-common";
|
|
17
17
|
import { abortIf, clipLine, errCode, isRec, makePrepareArguments, rejectUnknownFields, truncateToBytes, visLines, withLineNumbers } from "./utils";
|
|
@@ -524,12 +524,17 @@ export function renderGrepResult(result: { content?: Array<{ type: string; text?
|
|
|
524
524
|
return reuseText(context, shown.join("\n"));
|
|
525
525
|
}
|
|
526
526
|
export function regGrep(pi: ExtensionAPI, flags: EditToolFlags = DEFAULT_EDIT_FLAGS): void {
|
|
527
|
-
const prompted = withGrepPrompts({
|
|
527
|
+
const prompted = withGrepPrompts({
|
|
528
|
+
description: loadP("../prompts/grep.md"),
|
|
529
|
+
snippet: loadP("../prompts/grep-snippet.md"),
|
|
530
|
+
guidelines: loadGuide("../prompts/grep-guidelines.md"),
|
|
531
|
+
}, flags);
|
|
528
532
|
pi.registerTool({
|
|
529
533
|
name: "anchor_grep",
|
|
530
534
|
label: "Anchor Grep",
|
|
531
535
|
description: prompted.description,
|
|
532
536
|
promptSnippet: prompted.snippet,
|
|
537
|
+
promptGuidelines: prompted.guidelines,
|
|
533
538
|
prepareArguments: makePrepareArguments(),
|
|
534
539
|
parameters: grepToolSchema,
|
|
535
540
|
executionMode: "sequential",
|
package/src/insert.ts
CHANGED
|
@@ -18,7 +18,7 @@ export { assertInsertReq, type InsertReq };
|
|
|
18
18
|
|
|
19
19
|
const insertAnchorSchema = Type.String({
|
|
20
20
|
description:
|
|
21
|
-
"4-char anchor of the line to insert next to (never the row content).
|
|
21
|
+
"4-char anchor of the line to insert next to (never the row content).",
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
const insertDirectionSchema = Type.Union(
|
package/src/replace-match.ts
CHANGED
|
@@ -88,7 +88,7 @@ export function buildReplaceMatchEdit(
|
|
|
88
88
|
const end = Math.max(fromLine, toLine);
|
|
89
89
|
assertBoundaryLinesServed(fileLines, preload.fileHashes, served, start, end, displayPath);
|
|
90
90
|
const rangeLines = fileLines.slice(start - 1, end);
|
|
91
|
-
const rangeText = rangeLines.join("\n")
|
|
91
|
+
const rangeText = `${rangeLines.join("\n")}${rangeLines[rangeLines.length - 1] === "" ? "\n" : ""}`;
|
|
92
92
|
const oldText = toLF(req.old_string);
|
|
93
93
|
const { text: replacement, count } = replaceAllOccurrences(rangeText, oldText, req.new_string);
|
|
94
94
|
if (count === 0) {
|