@oh-my-pi/pi-coding-agent 13.0.0 → 13.0.2
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/CHANGELOG.md +7 -0
- package/package.json +7 -7
- package/scripts/format-prompts.ts +33 -3
- package/src/commit/prompts/analysis-system.md +3 -3
- package/src/commit/prompts/changelog-system.md +3 -3
- package/src/commit/prompts/summary-system.md +5 -5
- package/src/extensibility/custom-tools/wrapper.ts +1 -0
- package/src/extensibility/extensions/wrapper.ts +2 -0
- package/src/extensibility/hooks/tool-wrapper.ts +1 -0
- package/src/lsp/index.ts +1 -0
- package/src/patch/diff.ts +2 -2
- package/src/patch/hashline.ts +88 -119
- package/src/patch/index.ts +138 -224
- package/src/patch/shared.ts +21 -35
- package/src/prompts/compaction/compaction-short-summary.md +1 -1
- package/src/prompts/system/agent-creation-architect.md +6 -6
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/find.md +9 -0
- package/src/prompts/tools/hashline.md +130 -154
- package/src/prompts/tools/patch.md +1 -1
- package/src/prompts/tools/python.md +2 -2
- package/src/prompts/tools/replace.md +1 -1
- package/src/prompts/tools/task.md +18 -19
- package/src/task/index.ts +1 -0
- package/src/tools/ask.ts +1 -0
- package/src/tools/bash.ts +1 -0
- package/src/tools/browser.ts +1 -0
- package/src/tools/calculator.ts +1 -0
- package/src/tools/cancel-job.ts +1 -0
- package/src/tools/exit-plan-mode.ts +1 -0
- package/src/tools/fetch.ts +1 -0
- package/src/tools/find.ts +1 -0
- package/src/tools/grep.ts +1 -0
- package/src/tools/notebook.ts +1 -0
- package/src/tools/plan-mode-guard.ts +2 -2
- package/src/tools/poll-jobs.ts +1 -0
- package/src/tools/python.ts +1 -0
- package/src/tools/read.ts +1 -0
- package/src/tools/ssh.ts +1 -0
- package/src/tools/submit-result.ts +1 -0
- package/src/tools/todo-write.ts +1 -0
- package/src/tools/write.ts +1 -0
- package/src/web/search/index.ts +1 -0
package/src/patch/shared.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
truncateDiffByHunk,
|
|
23
23
|
} from "../tools/render-utils";
|
|
24
24
|
import { Ellipsis, Hasher, type RenderCache, renderStatusLine, truncateToWidth } from "../tui";
|
|
25
|
+
import type { HashlineToolEdit } from "./index";
|
|
25
26
|
import type { DiffError, DiffResult, Operation } from "./types";
|
|
26
27
|
|
|
27
28
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -58,7 +59,7 @@ export interface EditToolDetails {
|
|
|
58
59
|
/** Operation type (patch mode only) */
|
|
59
60
|
op?: Operation;
|
|
60
61
|
/** New path after move/rename (patch mode only) */
|
|
61
|
-
|
|
62
|
+
move?: string;
|
|
62
63
|
/** Structured output metadata */
|
|
63
64
|
meta?: OutputMeta;
|
|
64
65
|
}
|
|
@@ -70,6 +71,7 @@ export interface EditToolDetails {
|
|
|
70
71
|
interface EditRenderArgs {
|
|
71
72
|
path?: string;
|
|
72
73
|
file_path?: string;
|
|
74
|
+
file?: string;
|
|
73
75
|
oldText?: string;
|
|
74
76
|
newText?: string;
|
|
75
77
|
patch?: string;
|
|
@@ -83,16 +85,9 @@ interface EditRenderArgs {
|
|
|
83
85
|
*/
|
|
84
86
|
previewDiff?: string;
|
|
85
87
|
// Hashline mode fields
|
|
86
|
-
edits?:
|
|
88
|
+
edits?: Partial<HashlineToolEdit>[];
|
|
87
89
|
}
|
|
88
90
|
|
|
89
|
-
type HashlineEditPreview =
|
|
90
|
-
| { op: "replace"; tag: string; content: string[] }
|
|
91
|
-
| { op: "replace"; first: string; last: string; content: string[] }
|
|
92
|
-
| { op: "append"; after?: string; content: string[] }
|
|
93
|
-
| { op: "prepend"; before?: string; content: string[] }
|
|
94
|
-
| { op: "insert"; before: string; after: string; content: string[] };
|
|
95
|
-
|
|
96
91
|
/** Extended context for edit tool rendering */
|
|
97
92
|
export interface EditRenderContext {
|
|
98
93
|
/** Pre-computed diff preview (computed before tool executes) */
|
|
@@ -123,7 +118,7 @@ function formatStreamingDiff(diff: string, rawPath: string, uiTheme: Theme, labe
|
|
|
123
118
|
return text;
|
|
124
119
|
}
|
|
125
120
|
|
|
126
|
-
function formatStreamingHashlineEdits(edits:
|
|
121
|
+
function formatStreamingHashlineEdits(edits: Partial<HashlineToolEdit>[], uiTheme: Theme): string {
|
|
127
122
|
const MAX_EDITS = 4;
|
|
128
123
|
const MAX_DST_LINES = 8;
|
|
129
124
|
let text = "\n\n";
|
|
@@ -158,34 +153,25 @@ function formatStreamingHashlineEdits(edits: unknown[], uiTheme: Theme): string
|
|
|
158
153
|
}
|
|
159
154
|
|
|
160
155
|
return text.trimEnd();
|
|
161
|
-
function formatHashlineEdit(edit:
|
|
162
|
-
|
|
163
|
-
if (!editRecord) {
|
|
156
|
+
function formatHashlineEdit(edit: Partial<HashlineToolEdit>): { srcLabel: string; dst: string } {
|
|
157
|
+
if (typeof edit !== "object" || !edit) {
|
|
164
158
|
return { srcLabel: "• (incomplete edit)", dst: "" };
|
|
165
159
|
}
|
|
166
160
|
|
|
167
|
-
const contentLines = Array.isArray(
|
|
161
|
+
const contentLines = Array.isArray(edit.lines) ? (edit.lines as string[]).join("\n") : "";
|
|
168
162
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if ("first" in editRecord || "last" in editRecord) {
|
|
176
|
-
const first = typeof editRecord.first === "string" ? editRecord.first : "…";
|
|
177
|
-
const last = typeof editRecord.last === "string" ? editRecord.last : "…";
|
|
178
|
-
return { srcLabel: `• range ${first}..${last}`, dst: contentLines };
|
|
163
|
+
const op = typeof edit.op === "string" ? edit.op : "?";
|
|
164
|
+
const pos = typeof edit.pos === "string" ? edit.pos : undefined;
|
|
165
|
+
const end = typeof edit.end === "string" ? edit.end : undefined;
|
|
166
|
+
|
|
167
|
+
if (pos && end && pos !== end) {
|
|
168
|
+
return { srcLabel: `• ${op} ${pos}…${end}`, dst: contentLines };
|
|
179
169
|
}
|
|
180
|
-
|
|
181
|
-
if (
|
|
182
|
-
|
|
183
|
-
const before = typeof editRecord.before === "string" ? editRecord.before : undefined;
|
|
184
|
-
const refs = [after, before].filter(Boolean).join("..") || "…";
|
|
185
|
-
return { srcLabel: `• insert ${refs}`, dst: contentLines };
|
|
170
|
+
const anchor = pos ?? end;
|
|
171
|
+
if (anchor) {
|
|
172
|
+
return { srcLabel: `\u2022 ${op} ${anchor}`, dst: contentLines };
|
|
186
173
|
}
|
|
187
|
-
|
|
188
|
-
return { srcLabel: "• (incomplete edit)", dst: "" };
|
|
174
|
+
return { srcLabel: `\u2022 ${op} (file-level)`, dst: contentLines };
|
|
189
175
|
}
|
|
190
176
|
}
|
|
191
177
|
function formatMetadataLine(lineCount: number | null, language: string | undefined, uiTheme: Theme): string {
|
|
@@ -234,7 +220,7 @@ export const editToolRenderer = {
|
|
|
234
220
|
mergeCallAndResult: true,
|
|
235
221
|
|
|
236
222
|
renderCall(args: EditRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component {
|
|
237
|
-
const rawPath = args.file_path || args.path || "";
|
|
223
|
+
const rawPath = args.file_path || args.path || args.file || "";
|
|
238
224
|
const filePath = shortenPath(rawPath);
|
|
239
225
|
const editLanguage = getLanguageFromPath(rawPath) ?? "text";
|
|
240
226
|
const editIcon = uiTheme.fg("muted", uiTheme.getLangIcon(editLanguage));
|
|
@@ -289,13 +275,13 @@ export const editToolRenderer = {
|
|
|
289
275
|
uiTheme: Theme,
|
|
290
276
|
args?: EditRenderArgs,
|
|
291
277
|
): Component {
|
|
292
|
-
const rawPath = args?.file_path || args?.path || "";
|
|
278
|
+
const rawPath = args?.file_path || args?.path || args?.file || "";
|
|
293
279
|
const filePath = shortenPath(rawPath);
|
|
294
280
|
const editLanguage = getLanguageFromPath(rawPath) ?? "text";
|
|
295
281
|
const editIcon = uiTheme.fg("muted", uiTheme.getLangIcon(editLanguage));
|
|
296
282
|
|
|
297
283
|
const op = args?.op || result.details?.op;
|
|
298
|
-
const rename = args?.rename || result.details?.
|
|
284
|
+
const rename = args?.rename || result.details?.move;
|
|
299
285
|
const opTitle = op === "create" ? "Create" : op === "delete" ? "Delete" : "Edit";
|
|
300
286
|
|
|
301
287
|
// Pre-compute metadata line (static across renders)
|
|
@@ -5,5 +5,5 @@ Rules:
|
|
|
5
5
|
- MUST describe the changes made, not the process
|
|
6
6
|
- MUST NOT mention running tests, builds, or other validation steps
|
|
7
7
|
- MUST NOT explain what the user asked for
|
|
8
|
-
- MUST write in first person (I added
|
|
8
|
+
- MUST write in first person (I added…, I fixed…)
|
|
9
9
|
- MUST NOT ask questions
|
|
@@ -33,25 +33,25 @@ When a user describes what they want an agent to do, you will:
|
|
|
33
33
|
<function call omitted for brevity only for this example>
|
|
34
34
|
<commentary>
|
|
35
35
|
Since a significant piece of code was written, use the {{TASK_TOOL_NAME}} tool to launch the test-runner agent to run the tests.
|
|
36
|
-
</commentary>
|
|
36
|
+
</commentary>
|
|
37
37
|
assistant: "Now let me use the test-runner agent to run the tests"
|
|
38
|
-
</example>
|
|
38
|
+
</example>
|
|
39
39
|
- <example>
|
|
40
40
|
Context: User is creating an agent to respond to the word "hello" with a friendly jok.
|
|
41
41
|
user: "Hello"
|
|
42
42
|
assistant: "I'm going to use the {{TASK_TOOL_NAME}} tool to launch the greeting-responder agent to respond with a friendly joke"
|
|
43
43
|
<commentary>
|
|
44
44
|
Since the user is greeting, use the greeting-responder agent to respond with a friendly joke.
|
|
45
|
-
</commentary>
|
|
46
|
-
</example>
|
|
45
|
+
</commentary>
|
|
46
|
+
</example>
|
|
47
47
|
- If the user mentioned or implied that the agent should be used proactively, you SHOULD include examples of this.
|
|
48
48
|
- NOTE: You MUST ensure that in the examples, you are making the assistant use the Agent tool and MUST NOT simply respond directly to the task.
|
|
49
49
|
|
|
50
50
|
Your output MUST be a valid JSON object with exactly these fields:
|
|
51
51
|
{
|
|
52
52
|
"identifier": "A unique, descriptive identifier using lowercase letters, numbers, and hyphens (e.g., 'test-runner', 'api-docs-writer', 'code-formatter')",
|
|
53
|
-
"whenToUse": "A precise, actionable description starting with 'Use this agent when
|
|
54
|
-
"systemPrompt": "The complete system prompt that will govern the agent's behavior, written in second person ('You are
|
|
53
|
+
"whenToUse": "A precise, actionable description starting with 'Use this agent when…' that clearly defines the triggering conditions and use cases. Ensure you include examples as described above.",
|
|
54
|
+
"systemPrompt": "The complete system prompt that will govern the agent's behavior, written in second person ('You are…', 'You will…') and structured for maximum clarity and effectiveness"
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
Key principles for your system prompts:
|
|
@@ -90,7 +90,7 @@ Semantic questions MUST be answered with semantic tools.
|
|
|
90
90
|
{{#has tools "ssh"}}
|
|
91
91
|
### SSH: match commands to host shell
|
|
92
92
|
Commands MUST match the host shell. linux/bash, macos/zsh: Unix. windows/cmd: dir, type, findstr. windows/powershell: Get-ChildItem, Get-Content.
|
|
93
|
-
Remote filesystems: `~/.omp/remote/<hostname>/`. Windows paths need colons: `C:/Users
|
|
93
|
+
Remote filesystems: `~/.omp/remote/<hostname>/`. Windows paths need colons: `C:/Users/…`
|
|
94
94
|
{{/has}}
|
|
95
95
|
|
|
96
96
|
{{#ifAny (includes tools "grep") (includes tools "find")}}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Executes bash command in shell session for terminal operations like git, bun, cargo, python.
|
|
4
4
|
|
|
5
5
|
<instruction>
|
|
6
|
-
- You MUST use `cwd` parameter to set working directory instead of `cd dir &&
|
|
6
|
+
- You MUST use `cwd` parameter to set working directory instead of `cd dir && …`
|
|
7
7
|
- PTY mode is opt-in: set `pty: true` only when command expects a real terminal (for example `sudo`, `ssh` where you need input from the user); default is `false`
|
|
8
8
|
- You MUST use `;` only when later commands should run regardless of earlier failures
|
|
9
9
|
- `skill://` URIs are auto-resolved to filesystem paths before execution
|
|
@@ -13,6 +13,15 @@ Fast file pattern matching that works with any codebase size.
|
|
|
13
13
|
Matching file paths sorted by modification time (most recent first). Results truncated at 1000 entries or 50KB (configurable via `limit`).
|
|
14
14
|
</output>
|
|
15
15
|
|
|
16
|
+
<example name="find files">
|
|
17
|
+
```
|
|
18
|
+
{
|
|
19
|
+
"pattern": "src/**/*.ts",
|
|
20
|
+
"limit": 1000
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
</example>
|
|
24
|
+
|
|
16
25
|
<avoid>
|
|
17
26
|
For open-ended searches requiring multiple rounds of globbing and grepping, you MUST use Task tool instead.
|
|
18
27
|
</avoid>
|
|
@@ -1,96 +1,115 @@
|
|
|
1
1
|
# Edit
|
|
2
2
|
|
|
3
|
-
Apply precise file edits using `LINE#ID` tags
|
|
3
|
+
Apply precise file edits using `LINE#ID` tags from `read` output.
|
|
4
4
|
|
|
5
5
|
<workflow>
|
|
6
|
-
1. You
|
|
7
|
-
2. You MUST pick the smallest operation per change site
|
|
8
|
-
3. You MUST
|
|
9
|
-
4. You MUST submit one `edit` call per file containing all operations.
|
|
10
|
-
5. If another edit is needed in that file, you MUST re-read first (hashes changed).
|
|
11
|
-
6. You MUST output tool calls only; no prose.
|
|
6
|
+
1. You SHOULD issue a `read` call before editing if you have no tagged context for a file.
|
|
7
|
+
2. You MUST pick the smallest operation per change site.
|
|
8
|
+
3. You MUST submit one `edit` call per file with all operations, think your changes through before submitting.
|
|
12
9
|
</workflow>
|
|
13
10
|
|
|
14
11
|
<operations>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- **
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
12
|
+
Every edit has `op`, `pos`, and `lines`. Range replaces also have `end`. Both `pos` and `end` use `"N#ID"` format (e.g. `"23#XY"`).
|
|
13
|
+
**`pos`** — the anchor line. Meaning depends on `op`:
|
|
14
|
+
- `replace`: start of range (or the single line to replace)
|
|
15
|
+
- `prepend`: insert new lines **before** this line; omit for beginning of file
|
|
16
|
+
- `append`: insert new lines **after** this line; omit for end of file
|
|
17
|
+
**`end`** — range replace only. The last line of the range (inclusive). Omit for single-line replace.
|
|
18
|
+
**`lines`** — the replacement content:
|
|
19
|
+
- `["line1", "line2"]` — replace with these lines (array of strings)
|
|
20
|
+
- `"line1"` — shorthand for `["line1"]` (single-line replace)
|
|
21
|
+
- `[""]` — replace content with a blank line (line preserved, content cleared)
|
|
22
|
+
- `null` or `[]` — **delete** the line(s) entirely
|
|
23
|
+
|
|
24
|
+
### Line or range replace/delete
|
|
25
|
+
- `{ file: "…", edits: [{ op: "replace", pos: "N#ID", lines: null }] }` — delete one line
|
|
26
|
+
- `{ file: "…", edits: [{ op: "replace", pos: "N#ID", end: "M#ID", lines: null }] }` — delete a range
|
|
27
|
+
- `{ file: "…", edits: [{ op: "replace", pos: "N#ID", lines: […] }] }` — replace one line
|
|
28
|
+
- `{ file: "…", edits: [{ op: "replace", pos: "N#ID", end: "M#ID", lines: […] }] }` — replace a range
|
|
29
|
+
|
|
30
|
+
### Insert new lines
|
|
31
|
+
- `{ file: "…", edits: [{ op: "prepend", pos: "N#ID", lines: […] }] }` — insert before tagged line
|
|
32
|
+
- `{ file: "…", edits: [{ op: "prepend", lines: […] }] }` — insert at beginning of file (no tag)
|
|
33
|
+
- `{ file: "…", edits: [{ op: "append", pos: "N#ID", lines: […] }] }` — insert after tagged line
|
|
34
|
+
- `{ file: "…", edits: [{ op: "append", lines: […] }] }` — insert at end of file (no tag)
|
|
35
|
+
|
|
36
|
+
### File-level controls
|
|
37
|
+
- `{ file: "…", delete: true, edits: [] }` — delete the file
|
|
38
|
+
- `{ file: "…", move: "new/path.ts", edits: […] }` — move file to new path (edits applied first)
|
|
39
|
+
**Atomicity:** all ops in one call validate against the same pre-edit snapshot; tags reference the last `read`. Edits are applied bottom-up, so earlier tags stay valid even when later ops add or remove lines.
|
|
27
40
|
</operations>
|
|
28
41
|
|
|
29
42
|
<rules>
|
|
30
|
-
1. **Minimize scope:** You MUST use one logical mutation
|
|
31
|
-
2. **
|
|
32
|
-
3. **Prefer insertion over neighbor rewrites:** You SHOULD anchor on structural boundaries (`}`, `]`, `},`) not interior
|
|
33
|
-
4. **
|
|
34
|
-
5. **Touch only requested code:** You MUST NOT make incidental edits.
|
|
35
|
-
6. **Use exact current tokens:** You MUST NOT rewrite approximately; mutate the token that exists now.
|
|
36
|
-
7. **For swaps/moves:** You SHOULD prefer one range operation over multiple single-line operations.
|
|
43
|
+
1. **Minimize scope:** You MUST use one logical mutation per operation.
|
|
44
|
+
2. **No no-ops:** replacement MUST differ from current.
|
|
45
|
+
3. **Prefer insertion over neighbor rewrites:** You SHOULD anchor on structural boundaries (`}`, `]`, `},`), not interior lines.
|
|
46
|
+
4. **For swaps/moves:** You SHOULD prefer one range op over multiple single-line ops.
|
|
37
47
|
</rules>
|
|
38
48
|
|
|
39
|
-
<op-choice>
|
|
40
|
-
- One wrong line → MUST use `set`
|
|
41
|
-
- Adjacent block changed → MUST use `insert`
|
|
42
|
-
- Missing line/block → MUST use `append`/`prepend`
|
|
43
|
-
</op-choice>
|
|
44
|
-
|
|
45
|
-
<tag-choice>
|
|
46
|
-
- You MUST copy tags exactly from the prefix of the `read` or error output.
|
|
47
|
-
- You MUST NOT guess tags.
|
|
48
|
-
- For inserts, you SHOULD prefer `insert` > `append`/`prepend` when both boundaries are known.
|
|
49
|
-
- You MUST re-read after each successful edit call before issuing another on same file.
|
|
50
|
-
</tag-choice>
|
|
51
|
-
|
|
52
49
|
<recovery>
|
|
53
|
-
**Tag mismatch (`>>>`)
|
|
54
|
-
- You MUST
|
|
55
|
-
- You MUST re-read only if required tags are missing from error snippet.
|
|
56
|
-
- If mismatch repeats, you MUST stop and re-read the exact block.
|
|
50
|
+
**Tag mismatch (`>>>`):** You MUST retry using fresh tags from the error snippet. Re-read only if snippet lacks context.
|
|
51
|
+
**No-op (`identical`):** You MUST NOT resubmit. Re-read target lines and adjust the edit.
|
|
57
52
|
</recovery>
|
|
58
53
|
|
|
59
|
-
<example name="
|
|
54
|
+
<example name="single-line replace">
|
|
60
55
|
```ts
|
|
61
56
|
{{hlinefull 23 " const timeout: number = 5000;"}}
|
|
62
57
|
```
|
|
63
58
|
```
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
59
|
+
{
|
|
60
|
+
file: "…",
|
|
61
|
+
edits: [{
|
|
62
|
+
op: "replace",
|
|
63
|
+
pos: "{{hlineref 23 " const timeout: number = 5000;"}}",
|
|
64
|
+
lines: [" const timeout: number = 30_000;"]
|
|
65
|
+
}]
|
|
66
|
+
}
|
|
67
67
|
```
|
|
68
68
|
</example>
|
|
69
69
|
|
|
70
|
-
<example name="
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
op: "replace"
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
<example name="delete lines">
|
|
71
|
+
Single line — `lines: null` deletes entirely:
|
|
72
|
+
```
|
|
73
|
+
{
|
|
74
|
+
file: "…",
|
|
75
|
+
edits: [{
|
|
76
|
+
op: "replace",
|
|
77
|
+
pos: "{{hlineref 7 "// @ts-ignore"}}",
|
|
78
|
+
lines: null
|
|
79
|
+
}]
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
Range — add `end`:
|
|
83
|
+
```
|
|
84
|
+
{
|
|
85
|
+
file: "…",
|
|
86
|
+
edits: [{
|
|
87
|
+
op: "replace",
|
|
88
|
+
pos: "{{hlineref 80 " // TODO: remove after migration"}}",
|
|
89
|
+
end: "{{hlineref 83 " }"}}",
|
|
90
|
+
lines: null
|
|
91
|
+
}]
|
|
92
|
+
}
|
|
79
93
|
```
|
|
80
94
|
</example>
|
|
81
95
|
|
|
82
|
-
<example name="clear
|
|
96
|
+
<example name="clear text but keep the line break">
|
|
83
97
|
```ts
|
|
84
98
|
{{hlinefull 14 " placeholder: \"DO NOT SHIP\","}}
|
|
85
99
|
```
|
|
86
100
|
```
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
101
|
+
{
|
|
102
|
+
file: "…",
|
|
103
|
+
edits: [{
|
|
104
|
+
op: "replace",
|
|
105
|
+
pos: "{{hlineref 14 " placeholder: \"DO NOT SHIP\","}}",
|
|
106
|
+
lines: [""]
|
|
107
|
+
}]
|
|
108
|
+
}
|
|
90
109
|
```
|
|
91
110
|
</example>
|
|
92
111
|
|
|
93
|
-
<example name="rewrite a block
|
|
112
|
+
<example name="rewrite a block">
|
|
94
113
|
```ts
|
|
95
114
|
{{hlinefull 60 " } catch (err) {"}}
|
|
96
115
|
{{hlinefull 61 " console.error(err);"}}
|
|
@@ -98,117 +117,74 @@ content: [""]
|
|
|
98
117
|
{{hlinefull 63 " }"}}
|
|
99
118
|
```
|
|
100
119
|
```
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
```
|
|
116
|
-
op: "replace"
|
|
117
|
-
first: "{{hlineref 80 " // TODO: remove after migration"}}"
|
|
118
|
-
last: "{{hlineref 83 " }"}}"
|
|
119
|
-
content: null
|
|
120
|
+
{
|
|
121
|
+
file: "…",
|
|
122
|
+
edits: [{
|
|
123
|
+
op: "replace",
|
|
124
|
+
pos: "{{hlineref 60 " } catch (err) {"}}",
|
|
125
|
+
end: "{{hlineref 63 " }"}}",
|
|
126
|
+
lines: [
|
|
127
|
+
" } catch (err) {",
|
|
128
|
+
" if (isEnoent(err)) return null;",
|
|
129
|
+
" throw err;",
|
|
130
|
+
" }"
|
|
131
|
+
]
|
|
132
|
+
}]
|
|
133
|
+
}
|
|
120
134
|
```
|
|
121
135
|
</example>
|
|
122
136
|
|
|
123
|
-
<example name="
|
|
137
|
+
<example name="insert between siblings">
|
|
124
138
|
```ts
|
|
125
|
-
{{hlinefull
|
|
126
|
-
{{hlinefull
|
|
127
|
-
```
|
|
128
|
-
```
|
|
129
|
-
op: "prepend"
|
|
130
|
-
before: "{{hlineref 1 "import * as fs from \"node:fs/promises\";"}}"
|
|
131
|
-
content: ["import * as os from \"node:os\";"]
|
|
132
|
-
```
|
|
133
|
-
Use `before` for anchored insertion before a specific line. Omit `before` to prepend at BOF.
|
|
134
|
-
</example>
|
|
135
|
-
|
|
136
|
-
<example name="append at end of file">
|
|
137
|
-
```ts
|
|
138
|
-
{{hlinefull 260 "export { serialize, deserialize };"}}
|
|
139
|
+
{{hlinefull 44 " \"build\": \"bun run compile\","}}
|
|
140
|
+
{{hlinefull 45 " \"test\": \"bun test\""}}
|
|
139
141
|
```
|
|
140
142
|
```
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
{
|
|
144
|
+
file: "…",
|
|
145
|
+
edits: [{
|
|
146
|
+
op: "prepend",
|
|
147
|
+
pos: "{{hlineref 45 " \"test\": \"bun test\""}}",
|
|
148
|
+
lines: [" \"lint\": \"biome check\","]
|
|
149
|
+
}]
|
|
150
|
+
}
|
|
144
151
|
```
|
|
145
|
-
|
|
146
|
-
</example>
|
|
147
|
-
|
|
148
|
-
<example name="add an entry between known siblings">
|
|
152
|
+
Result:
|
|
149
153
|
```ts
|
|
150
154
|
{{hlinefull 44 " \"build\": \"bun run compile\","}}
|
|
151
|
-
{{hlinefull 45 " \"
|
|
152
|
-
|
|
153
|
-
```
|
|
154
|
-
op: "insert"
|
|
155
|
-
after: "{{hlineref 44 " \"build\": \"bun run compile\","}}"
|
|
156
|
-
before: "{{hlineref 45 " \"test\": \"bun test\""}}"
|
|
157
|
-
content: [" \"lint\": \"biome check\","]
|
|
155
|
+
{{hlinefull 45 " \"lint\": \"biome check\","}}
|
|
156
|
+
{{hlinefull 46 " \"test\": \"bun test\""}}
|
|
158
157
|
```
|
|
159
|
-
Dual anchors pin the insert to exactly one gap, preventing drift from edits elsewhere in the file. **Always prefer dual anchors when both boundaries are content lines.**
|
|
160
158
|
</example>
|
|
161
159
|
|
|
162
|
-
<example name="
|
|
160
|
+
<example name="anchor to structure, not whitespace">
|
|
161
|
+
Trailing `""` in `lines` preserves blank-line separators. Anchor to the structural line, not the blank line above — blank lines are ambiguous and shift.
|
|
163
162
|
```ts
|
|
164
|
-
{{hlinefull 100 " return buf.toString(\"hex\");"}}
|
|
165
163
|
{{hlinefull 101 "}"}}
|
|
166
164
|
{{hlinefull 102 ""}}
|
|
167
165
|
{{hlinefull 103 "export function serialize(data: unknown): string {"}}
|
|
168
166
|
```
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
```
|
|
186
|
-
path: "src/utils.ts"
|
|
187
|
-
rename: "src/helpers/utils.ts"
|
|
188
|
-
edits: […]
|
|
189
|
-
```
|
|
190
|
-
</example>
|
|
191
|
-
|
|
192
|
-
<example name="anti-pattern: anchoring to whitespace">
|
|
193
|
-
Bad — tags to a blank line; fragile if blank lines shift:
|
|
194
|
-
```
|
|
195
|
-
after: "{{hlineref 102 ""}}"
|
|
196
|
-
content: ["function validate() {", …, "}"]
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
Good — anchors to the structural target:
|
|
200
|
-
|
|
201
|
-
```
|
|
202
|
-
before: "{{hlineref 103 "export function serialize(data: unknown): string {"}}"
|
|
203
|
-
content: ["function validate() {", …, "}"]
|
|
167
|
+
Bad — append after "}"
|
|
168
|
+
Good — anchors to structural line:
|
|
169
|
+
```
|
|
170
|
+
{
|
|
171
|
+
file: "…",
|
|
172
|
+
edits: [{
|
|
173
|
+
op: "prepend",
|
|
174
|
+
pos: "{{hlineref 103 "export function serialize(data: unknown): string {"}}",
|
|
175
|
+
lines: [
|
|
176
|
+
"function validate(data: unknown): boolean {",
|
|
177
|
+
" return data != null && typeof data === \"object\";",
|
|
178
|
+
"}",
|
|
179
|
+
""
|
|
180
|
+
]
|
|
181
|
+
}]
|
|
182
|
+
}
|
|
204
183
|
```
|
|
205
184
|
</example>
|
|
206
185
|
|
|
207
186
|
<critical>
|
|
208
|
-
|
|
209
|
-
-
|
|
210
|
-
-
|
|
211
|
-
|
|
212
|
-
- Scope MUST be minimal and formatting MUST be preserved except targeted token changes
|
|
213
|
-
</critical>
|
|
214
|
-
**Final reminder:** tags are immutable references to the last read snapshot. You MUST re-read when state changes, then edit.
|
|
187
|
+
- Edit payload: `{ file, edits[] }`. Each entry: `op`, `lines`, optional `pos`/`end`. No extra keys.
|
|
188
|
+
- Every tag MUST be copied exactly from fresh tool result as `N#ID`.
|
|
189
|
+
- You MUST re-read after each edit call before issuing another on same file.
|
|
190
|
+
</critical>
|
|
@@ -60,7 +60,7 @@ edit {"path":"src/app.py","op":"update","diff":"@@ def greet():\n def greet():\n
|
|
|
60
60
|
</example>
|
|
61
61
|
|
|
62
62
|
<example name="rename">
|
|
63
|
-
edit {"path":"src/app.py","op":"update","rename":"src/main.py","diff":"@@\n
|
|
63
|
+
edit {"path":"src/app.py","op":"update","rename":"src/main.py","diff":"@@\n …\n"}
|
|
64
64
|
</example>
|
|
65
65
|
|
|
66
66
|
<example name="delete">
|
|
@@ -36,8 +36,8 @@ All helpers auto-print results and return values for chaining.
|
|
|
36
36
|
<output>
|
|
37
37
|
User sees output like Jupyter notebook; rich displays render fully:
|
|
38
38
|
- `display(JSON(data))` → interactive JSON tree
|
|
39
|
-
- `display(HTML(
|
|
40
|
-
- `display(Markdown(
|
|
39
|
+
- `display(HTML(…))` → rendered HTML
|
|
40
|
+
- `display(Markdown(…))` → formatted markdown
|
|
41
41
|
- `plt.show()` → inline figures
|
|
42
42
|
**You will see object repr** (e.g., `<IPython.core.display.JSON object>`). Trust `display()`; you MUST NOT assume user sees only repr.
|
|
43
43
|
</output>
|
|
@@ -24,7 +24,7 @@ For position-addressed or pattern-addressed changes, bash more efficient:
|
|
|
24
24
|
|
|
25
25
|
|Operation|Command|
|
|
26
26
|
|---|---|
|
|
27
|
-
|Append to file|`cat >> file <<'EOF'
|
|
27
|
+
|Append to file|`cat >> file <<'EOF'`…`EOF`|
|
|
28
28
|
|Prepend to file|`{ cat - file; } <<'EOF' > tmp && mv tmp file`|
|
|
29
29
|
|Delete lines N-M|`sed -i 'N,Md' file`|
|
|
30
30
|
|Insert after line N|`sed -i 'Na\text' file`|
|