@oh-my-pi/pi-coding-agent 14.2.1 → 14.4.0

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.
Files changed (137) hide show
  1. package/CHANGELOG.md +143 -1
  2. package/package.json +19 -19
  3. package/src/autoresearch/prompt.md +1 -1
  4. package/src/cli/args.ts +10 -1
  5. package/src/cli/shell-cli.ts +15 -3
  6. package/src/commit/agentic/prompts/analyze-file.md +1 -1
  7. package/src/config/model-registry.ts +67 -15
  8. package/src/config/prompt-templates.ts +5 -5
  9. package/src/config/settings-schema.ts +63 -4
  10. package/src/cursor.ts +3 -8
  11. package/src/debug/system-info.ts +6 -2
  12. package/src/discovery/claude.ts +58 -36
  13. package/src/discovery/helpers.ts +3 -3
  14. package/src/discovery/opencode.ts +20 -2
  15. package/src/edit/diff.ts +50 -47
  16. package/src/edit/index.ts +87 -57
  17. package/src/edit/line-hash.ts +735 -19
  18. package/src/edit/modes/apply-patch.ts +0 -9
  19. package/src/edit/modes/atom.ts +658 -0
  20. package/src/edit/modes/chunk.ts +144 -78
  21. package/src/edit/modes/hashline.ts +223 -146
  22. package/src/edit/modes/patch.ts +5 -9
  23. package/src/edit/modes/replace.ts +6 -11
  24. package/src/edit/renderer.ts +112 -143
  25. package/src/edit/streaming.ts +385 -0
  26. package/src/exec/bash-executor.ts +58 -5
  27. package/src/export/html/template.generated.ts +1 -1
  28. package/src/export/html/template.js +4 -12
  29. package/src/extensibility/custom-tools/types.ts +2 -0
  30. package/src/extensibility/custom-tools/wrapper.ts +2 -1
  31. package/src/internal-urls/docs-index.generated.ts +7 -7
  32. package/src/internal-urls/pi-protocol.ts +0 -2
  33. package/src/lsp/client.ts +8 -1
  34. package/src/lsp/defaults.json +2 -1
  35. package/src/lsp/index.ts +1 -1
  36. package/src/mcp/render.ts +1 -8
  37. package/src/modes/acp/acp-agent.ts +76 -2
  38. package/src/modes/components/assistant-message.ts +5 -34
  39. package/src/modes/components/diff.ts +23 -14
  40. package/src/modes/components/footer.ts +21 -16
  41. package/src/modes/components/hook-editor.ts +1 -1
  42. package/src/modes/components/settings-defs.ts +6 -1
  43. package/src/modes/components/todo-reminder.ts +1 -8
  44. package/src/modes/components/tool-execution.ts +112 -105
  45. package/src/modes/controllers/input-controller.ts +1 -1
  46. package/src/modes/controllers/selector-controller.ts +1 -1
  47. package/src/modes/interactive-mode.ts +0 -2
  48. package/src/modes/print-mode.ts +8 -0
  49. package/src/modes/theme/mermaid-cache.ts +13 -52
  50. package/src/modes/theme/theme.ts +2 -2
  51. package/src/prompts/agents/librarian.md +1 -1
  52. package/src/prompts/agents/reviewer.md +4 -4
  53. package/src/prompts/ci-green-request.md +1 -1
  54. package/src/prompts/review-request.md +1 -1
  55. package/src/prompts/system/subagent-system-prompt.md +3 -3
  56. package/src/prompts/system/subagent-yield-reminder.md +11 -0
  57. package/src/prompts/system/system-prompt.md +4 -1
  58. package/src/prompts/tools/ask.md +3 -2
  59. package/src/prompts/tools/ast-edit.md +15 -19
  60. package/src/prompts/tools/ast-grep.md +18 -24
  61. package/src/prompts/tools/atom.md +96 -0
  62. package/src/prompts/tools/browser.md +1 -0
  63. package/src/prompts/tools/chunk-edit.md +58 -179
  64. package/src/prompts/tools/debug.md +4 -5
  65. package/src/prompts/tools/exit-plan-mode.md +4 -5
  66. package/src/prompts/tools/find.md +4 -8
  67. package/src/prompts/tools/github.md +18 -0
  68. package/src/prompts/tools/grep.md +8 -8
  69. package/src/prompts/tools/hashline.md +22 -89
  70. package/src/prompts/tools/{gemini-image.md → image-gen.md} +1 -1
  71. package/src/prompts/tools/inspect-image.md +6 -6
  72. package/src/prompts/tools/lsp.md +6 -0
  73. package/src/prompts/tools/patch.md +12 -19
  74. package/src/prompts/tools/python.md +3 -2
  75. package/src/prompts/tools/read-chunk.md +46 -8
  76. package/src/prompts/tools/read.md +9 -6
  77. package/src/prompts/tools/ssh.md +8 -17
  78. package/src/prompts/tools/todo-write.md +54 -41
  79. package/src/sdk.ts +22 -14
  80. package/src/session/agent-session.ts +61 -22
  81. package/src/session/session-manager.ts +228 -57
  82. package/src/session/streaming-output.ts +11 -0
  83. package/src/system-prompt.ts +7 -2
  84. package/src/task/executor.ts +44 -48
  85. package/src/task/render.ts +11 -13
  86. package/src/tools/ask.ts +7 -7
  87. package/src/tools/ast-edit.ts +45 -41
  88. package/src/tools/ast-grep.ts +77 -85
  89. package/src/tools/bash.ts +21 -9
  90. package/src/tools/browser.ts +32 -30
  91. package/src/tools/calculator.ts +4 -4
  92. package/src/tools/cancel-job.ts +1 -1
  93. package/src/tools/checkpoint.ts +2 -2
  94. package/src/tools/debug.ts +41 -37
  95. package/src/tools/exit-plan-mode.ts +1 -1
  96. package/src/tools/find.ts +4 -4
  97. package/src/tools/gh-renderer.ts +12 -4
  98. package/src/tools/gh.ts +514 -712
  99. package/src/tools/grep.ts +115 -130
  100. package/src/tools/{gemini-image.ts → image-gen.ts} +459 -60
  101. package/src/tools/index.ts +14 -32
  102. package/src/tools/inspect-image.ts +3 -3
  103. package/src/tools/json-tree.ts +114 -114
  104. package/src/tools/match-line-format.ts +9 -8
  105. package/src/tools/notebook.ts +8 -7
  106. package/src/tools/poll-tool.ts +2 -1
  107. package/src/tools/python.ts +9 -23
  108. package/src/tools/read.ts +32 -21
  109. package/src/tools/render-mermaid.ts +1 -1
  110. package/src/tools/render-utils.ts +18 -0
  111. package/src/tools/renderers.ts +2 -2
  112. package/src/tools/report-tool-issue.ts +3 -2
  113. package/src/tools/resolve.ts +1 -1
  114. package/src/tools/review.ts +12 -10
  115. package/src/tools/search-tool-bm25.ts +2 -4
  116. package/src/tools/sqlite-reader.ts +116 -3
  117. package/src/tools/ssh.ts +4 -4
  118. package/src/tools/todo-write.ts +172 -147
  119. package/src/tools/vim.ts +14 -15
  120. package/src/tools/write.ts +4 -4
  121. package/src/tools/{submit-result.ts → yield.ts} +11 -13
  122. package/src/utils/edit-mode.ts +2 -1
  123. package/src/utils/file-display-mode.ts +10 -5
  124. package/src/utils/git.ts +9 -5
  125. package/src/utils/shell-snapshot.ts +2 -3
  126. package/src/vim/render.ts +4 -4
  127. package/src/web/search/providers/codex.ts +129 -6
  128. package/src/prompts/system/subagent-submit-reminder.md +0 -11
  129. package/src/prompts/tools/gh-issue-view.md +0 -11
  130. package/src/prompts/tools/gh-pr-checkout.md +0 -12
  131. package/src/prompts/tools/gh-pr-diff.md +0 -12
  132. package/src/prompts/tools/gh-pr-push.md +0 -11
  133. package/src/prompts/tools/gh-pr-view.md +0 -11
  134. package/src/prompts/tools/gh-repo-view.md +0 -11
  135. package/src/prompts/tools/gh-run-watch.md +0 -12
  136. package/src/prompts/tools/gh-search-issues.md +0 -11
  137. package/src/prompts/tools/gh-search-prs.md +0 -11
@@ -2,12 +2,12 @@ Performs structural AST-aware rewrites via native ast-grep.
2
2
 
3
3
  <instruction>
4
4
  - Use for codemods and structural rewrites where plain text replace is unsafe
5
- - `path` accepts a comma-separated list in addition to file/dir/glob
6
- - Set `lang` explicitly in mixed-language trees for deterministic rewrites
5
+ - `path` is required and accepts a file, directory, glob, comma-separated path list, or internal URL
6
+ - Language is inferred from `path`; narrow `path` to one language for deterministic rewrites
7
7
  - Metavariables captured in `pat` (`$A`, `$$$ARGS`) are substituted into that entry's `out` template
8
8
  - **Patterns match AST structure, not text.** `$NAME` = one node (captured); `$_` = one without binding; `$$$NAME` = zero-or-more (lazy — stops at next matchable element); `$$$` = zero-or-more without binding. Use `$$$NAME`, **NOT** `$$NAME` — the two-dollar form is invalid. Metavariable names are UPPERCASE and **MUST** be the whole AST node — partial text like `prefix$VAR` or `"hello $NAME"` does NOT work
9
9
  - When the same metavariable appears twice, both occurrences **MUST** match identical code (`$A == $A` matches `x == x`, not `x == y`)
10
- - Rewrite patterns **MUST** parse as a single valid AST node. For method fragments or body snippets that don't parse standalone, wrap in context (e.g. `class $_ { … }`) and set `sel` to target the inner node — match and replacement target the selected node, not the wrapper. If ast-grep reports `Multiple AST nodes are detected`, wrap and use `sel`
10
+ - Rewrite patterns **MUST** parse as a single valid AST node. For method fragments or body snippets that don't parse standalone, wrap in context (e.g. `class $_ { … }`)
11
11
  - For TS declarations/methods, tolerate unknown annotations: `async function $NAME($$$ARGS): $_ { $$$BODY }` or `class $_ { method($ARG: $_): $_ { $$$BODY } }`
12
12
  - Delete matched code with empty `out`: `{"pat":"console.log($$$)","out":""}`
13
13
  - Each rewrite is a 1:1 structural substitution — cannot split one capture across multiple nodes or merge multiple captures into one
@@ -19,22 +19,18 @@ Performs structural AST-aware rewrites via native ast-grep.
19
19
  </output>
20
20
 
21
21
  <examples>
22
- - Rename a call site across a directory:
23
- `{"ops":[{"pat":"oldApi($$$ARGS)","out":"newApi($$$ARGS)"}],"lang":"typescript","path":"src/"}`
24
- - Delete matching calls (empty `out` removes the node):
25
- `{"ops":[{"pat":"console.log($$$ARGS)","out":""}],"lang":"typescript","path":"src/"}`
26
- - Rewrite import source path:
27
- `{"ops":[{"pat":"import { $$$IMPORTS } from \"old-package\"","out":"import { $$$IMPORTS } from \"new-package\""}],"lang":"typescript","path":"src/"}`
28
- - Modernize to optional chaining (same metavariable enforces identity):
29
- `{"ops":[{"pat":"$A && $A()","out":"$A?.()"}],"lang":"typescript","path":"src/"}`
30
- - Swap two arguments using captures:
31
- `{"ops":[{"pat":"assertEqual($A, $B)","out":"assertEqual($B, $A)"}],"lang":"typescript","path":"tests/"}`
32
- - Rename a function declaration while tolerating any return type annotation:
33
- `{"ops":[{"pat":"async function fetchData($$$ARGS): $_ { $$$BODY }","out":"async function loadData($$$ARGS): $_ { $$$BODY }"}],"sel":"function_declaration","lang":"typescript","path":"src/api.ts"}`
34
- - Rewrite a method body fragment by wrapping in parseable context and selecting the method:
35
- `{"ops":[{"pat":"class $_ { async execute($INPUT: $_) { $$$BEFORE; const $PARSED = $_.parse($INPUT); $$$AFTER } }","out":"class $_ { async execute($INPUT: $_) { $$$BEFORE; const $PARSED = $SCHEMA.parse($INPUT); $$$AFTER } }"}],"sel":"method_definition","lang":"typescript","path":"src/tools/todo.ts"}`
36
- - Python — convert print calls to logging:
37
- `{"ops":[{"pat":"print($$$ARGS)","out":"logger.info($$$ARGS)"}],"lang":"python","path":"src/"}`
22
+ # Rename a call site across TypeScript files
23
+ `{"ops":[{"pat":"oldApi($$$ARGS)","out":"newApi($$$ARGS)"}],"path":"src/**/*.ts"}`
24
+ # Delete matching calls
25
+ `{"ops":[{"pat":"console.log($$$ARGS)","out":""}],"path":"src/**/*.ts"}`
26
+ # Rewrite import source path
27
+ `{"ops":[{"pat":"import { $$$IMPORTS } from \"old-package\"","out":"import { $$$IMPORTS } from \"new-package\""}],"path":"src/**/*.ts"}`
28
+ # Modernize to optional chaining (same metavariable enforces identity)
29
+ `{"ops":[{"pat":"$A && $A()","out":"$A?.()"}],"path":"src/**/*.ts"}`
30
+ # Swap two arguments using captures
31
+ `{"ops":[{"pat":"assertEqual($A, $B)","out":"assertEqual($B, $A)"}],"path":"tests/**/*.ts"}`
32
+ # Python convert print calls to logging
33
+ `{"ops":[{"pat":"print($$$ARGS)","out":"logger.info($$$ARGS)"}],"path":"src/**/*.py"}`
38
34
  </examples>
39
35
 
40
36
  <critical>
@@ -2,18 +2,18 @@ Performs structural code search using AST matching via native ast-grep.
2
2
 
3
3
  <instruction>
4
4
  - Use when syntax shape matters more than raw text (calls, declarations, specific language constructs)
5
- - `path` accepts a comma-separated list in addition to file/dir/glob
6
- - Set `lang` explicitly in mixed-language trees to avoid parse noise from non-source files
7
- - Multiple patterns in `pat` run in one native pass, merged, then `offset`/`limit` applied
5
+ - `path` is required and accepts a file, directory, glob, comma-separated path list, or internal URL
6
+ - Language is inferred from `path`; narrow `path` to one language when mixed-language trees could cause parse noise
7
+ - `pat` is a single AST pattern. Run separate calls for distinct unrelated patterns
8
8
  - **Patterns match AST structure, not text** — whitespace/formatting is ignored
9
9
  - `$NAME` captures one node; `$_` matches one without binding; `$$$NAME` captures zero-or-more (lazy — stops at next matchable element); `$$$` matches zero-or-more without binding. Use `$$$NAME`, **NOT** `$$NAME` — the two-dollar form is invalid and produces a parse error
10
10
  - Metavariable names are UPPERCASE and must be the whole AST node — partial-text like `prefix$VAR`, `"hello $NAME"`, or `a $OP b` does NOT work; match the whole node instead
11
11
  - When the same metavariable appears twice, both occurrences **MUST** match identical code (`$A == $A` matches `x == x`, not `x == y`)
12
- - Patterns **MUST** parse as a single valid AST node for the target language. For method fragments or body snippets that don't parse standalone, wrap in valid context (e.g. `class $_ { … }`) and set `sel` to target the inner node — results return for the selected node, not the outer wrapper. If ast-grep reports `Multiple AST nodes are detected`, the pattern isn't a single parseable node — wrap and use `sel`
13
- - C++ qualified calls used as expression statements need the statement semicolon in the pattern: use `ns::doThing($ARG);`, `$CALLEE($ARG)`, or wrap a statement snippet and select `call_expression`. Without `;`, tree-sitter-cpp may parse `ns::doThing($ARG)` as declaration-like syntax and return no matches
12
+ - Patterns **MUST** parse as a single valid AST node for the inferred target language. For method fragments or body snippets that don't parse standalone, wrap in valid context (e.g. `class $_ { … }`)
13
+ - C++ qualified calls used as expression statements need the statement semicolon in the pattern: use `ns::doThing($ARG);`, `$CALLEE($ARG);`, or wrap a statement snippet. Without `;`, tree-sitter-cpp may parse `ns::doThing($ARG)` as declaration-like syntax and return no matches
14
14
  - For TS declarations/methods, tolerate unknown annotations: `async function $NAME($$$ARGS): $_ { $$$BODY }` or `class $_ { method($ARG: $_): $_ { $$$BODY } }`
15
15
  - Declaration forms are structurally distinct — top-level `function foo`, class method `foo()`, and `const foo = () => {}` are different AST shapes; search the right form before concluding absence
16
- - Loosest existence check: `pat: ["executeBash"]` with `sel: "identifier"`
16
+ - Loosest existence check: `pat: "executeBash"` with a narrow `path`
17
17
  </instruction>
18
18
 
19
19
  <output>
@@ -22,26 +22,20 @@ Performs structural code search using AST matching via native ast-grep.
22
22
  </output>
23
23
 
24
24
  <examples>
25
- - Multi-pattern scoped search:
26
- `{"pat":["console.log($$$)","console.error($$$)"],"lang":"typescript","path":"src/"}`
27
- - Named imports from a specific package (quoted string inside pattern):
28
- `{"pat":["import { $$$IMPORTS } from \"react\""],"lang":"typescript","path":"src/"}`
29
- - Arrow functions assigned to a const (distinct AST from function declarations):
30
- `{"pat":["const $NAME = ($$$ARGS) => $BODY"],"lang":"typescript","path":"src/utils/"}`
31
- - Method call on any object, ignoring method name with `$_`:
32
- `{"pat":["logger.$_($$$ARGS)"],"lang":"typescript","path":"src/"}`
33
- - Contextual pattern with selector match the identifier `foo`, not the whole call:
34
- `{"pat":["foo()"],"sel":"identifier","lang":"typescript","path":"src/utils.ts"}`
35
- - Match a function declaration while tolerating any return type annotation (`sel` targets the inner node):
36
- `{"pat":["async function processItems($$$ARGS): $_ { $$$BODY }"],"sel":"function_declaration","lang":"typescript","path":"src/worker.ts"}`
37
- - Match a method body fragment by wrapping in parseable context and selecting the method:
38
- `{"pat":["class $_ { async execute($INPUT: $_) { $$$BEFORE; const $PARSED = $_.parse($INPUT); $$$AFTER } }"],"sel":"method_definition","lang":"typescript","path":"src/tools/todo.ts"}`
39
- - Loosest existence check for a symbol in one file:
40
- `{"pat":["processItems"],"sel":"identifier","lang":"typescript","path":"src/worker.ts"}`
25
+ # Search TypeScript files under src
26
+ `{"pat":"console.log($$$)","path":"src/**/*.ts"}`
27
+ # Named imports from a specific package
28
+ `{"pat":"import { $$$IMPORTS } from \"react\"","path":"src/**/*.ts"}`
29
+ # Arrow functions assigned to a const
30
+ `{"pat":"const $NAME = ($$$ARGS) => $BODY","path":"src/utils/**/*.ts"}`
31
+ # Method call on any object, ignoring method name with `$_`
32
+ `{"pat":"logger.$_($$$ARGS)","path":"src/**/*.ts"}`
33
+ # Loosest existence check for a symbol in one file
34
+ `{"pat":"processItems","path":"src/worker.ts"}`
41
35
  </examples>
42
36
 
43
37
  <critical>
44
- - Avoid repo-root AST scans when the target is language-specific — narrow `path` first
45
- - Parse issues are query failure, not evidence of absence: repair the pattern or tighten `path`/`glob`/`lang` before concluding "no matches"
38
+ - Avoid repo-root scans — narrow `path` first
39
+ - Parse issues are query failure, not evidence of absence: repair the pattern or tighten `path` before concluding "no matches"
46
40
  - For broad/open-ended exploration across subsystems, use Task tool with explore subagent first
47
41
  </critical>
@@ -0,0 +1,96 @@
1
+ Applies precise file edits using full anchors from `read` output (for example `160sr`).
2
+
3
+ Read the file first. Copy the full anchors exactly as shown by `read`.
4
+
5
+ <operations>
6
+ **Top level**: `{ path, edits: […] }` — `path` is shared by all entries. You may still override the file inside `loc` with forms like `other.ts:160sr`.
7
+
8
+ Each entry has one shared locator plus one or more verbs:
9
+ - `loc: "160sr"` — single anchored line
10
+ - `loc: "^"` — beginning of file (only valid with `pre`)
11
+ - `loc: "$"` — end of file (only valid with `post`)
12
+ - `loc: "a.ts:160sr"` — cross-file override inside the locator
13
+
14
+ Verbs:
15
+ - `set: ["…"]` — replace the anchor line
16
+ - `pre: ["…"]` — insert before the anchor line (or at BOF when `loc:"^"`)
17
+ - `post: ["…"]` — insert after the anchor line (or at EOF when `loc:"$"`)
18
+
19
+ Combination rules:
20
+ - On a single-anchor `loc`, you may combine `pre`, `set`, and `post` in the same entry.
21
+ - `set: []` on a single-anchor `loc` deletes that line.
22
+ - `set:[""]` is **not** delete — it replaces the line with a blank line.
23
+ </operations>
24
+
25
+ <examples>
26
+ All examples below reference the same file:
27
+
28
+ ```ts title="a.ts"
29
+ {{hline 1 "// @ts-ignore"}}
30
+ {{hline 2 "const timeout = 5000;"}}
31
+ {{hline 3 "const tag = \"DO NOT SHIP\";"}}
32
+ {{hline 4 "const fallback = group.targetFramework || 'All Frameworks';"}}
33
+ {{hline 5 "function alpha() {"}}
34
+ {{hline 6 "\tlog();"}}
35
+ {{hline 7 "}"}}
36
+ {{hline 8 ""}}
37
+ {{hline 9 "function beta(x) {"}}
38
+ {{hline 10 "\tif (x) {"}}
39
+ {{hline 11 "\t\treturn parse(data);"}}
40
+ {{hline 12 "\t}"}}
41
+ {{hline 13 "\treturn null;"}}
42
+ {{hline 14 "}"}}
43
+ ```
44
+
45
+ # Swap an operator by replacing the line
46
+ Original line 4: `const fallback = group.targetFramework || 'All Frameworks';`
47
+ `{path:"a.ts",edits:[{loc:{{href 4 "const fallback = group.targetFramework || 'All Frameworks';"}},set:["const fallback = group.targetFramework ?? 'All Frameworks';"]}]}`
48
+
49
+ # Flip a literal by replacing the line
50
+ Original line 2: `const timeout = 5000;`
51
+ `{path:"a.ts",edits:[{loc:{{href 2 "const timeout = 5000;"}},set:["const timeout = 30_000;"]}]}`
52
+
53
+ # Negate a condition by replacing the line
54
+ Original line 10: `\tif (x) {`
55
+ `{path:"a.ts",edits:[{loc:{{href 10 "\tif (x) {"}},set:["\tif (!x) {"]}]}`
56
+
57
+ # Combine `pre` + `set` + `post` in one entry
58
+ `{path:"a.ts",edits:[{loc:{{href 6 "\tlog();"}},pre:["\tvalidate();"],set:["\tlog();"],post:["\tcleanup();"]}]}`
59
+
60
+ # Replace one whole line with `set`
61
+ Use `set` to replace the full anchored line, preserving any unchanged surrounding lines yourself.
62
+ `{path:"a.ts",edits:[{loc:{{href 3 "const tag = \"DO NOT SHIP\";"}},set:["const tag = \"OK\";"]}]}`
63
+
64
+ # Replace multiple non-adjacent lines
65
+ `{path:"a.ts",edits:[{loc:{{href 11 "\t\treturn parse(data);"}},set:["\t\treturn parse(data) ?? fallback;"]},{loc:{{href 13 "\treturn null;"}},set:["\treturn fallback;"]}]}`
66
+
67
+ # Delete a line with `set: []`
68
+ `{path:"a.ts",edits:[{loc:{{href 11 "\t\treturn parse(data);"}},set:[]}]}`
69
+
70
+ # Preserve a blank line with `set:[""]`
71
+ `{path:"a.ts",edits:[{loc:{{href 8 ""}},set:[""]}]}`
72
+
73
+ # Insert before / after a line
74
+ `{path:"a.ts",edits:[{loc:{{href 9 "function beta(x) {"}},pre:["function gamma() {","\tvalidate();","}",""]}]}`
75
+ `{path:"a.ts",edits:[{loc:{{href 6 "\tlog();"}},post:["\tvalidate();"]}]}`
76
+
77
+ # Prepend / append at file edges
78
+ `{path:"a.ts",edits:[{loc:"^",pre:["// Copyright (c) 2026",""]}]}`
79
+ `{path:"a.ts",edits:[{loc:"$",post:["","export const VERSION = \"1.0.0\";"]}]}`
80
+
81
+ # Cross-file override inside `loc`
82
+ `{path:"a.ts",edits:[{loc:"b.ts:{{href 2 "const timeout = 5000;"}}",set:["const timeout = 30_000;"]}]}`
83
+ </examples>
84
+
85
+ <critical>
86
+ - Make the minimum exact edit.
87
+ - Copy the full anchors exactly as shown by `read/grep` (for example `160sr`, not just `sr`).
88
+ - `loc` chooses the target. Verbs describe what to do there.
89
+ - On a single-anchor `loc`, you may combine `pre`, `set`, and `post`.
90
+ - `loc:"^"` only supports `pre`. `loc:"$"` only supports `post`.
91
+ - `set: []` deletes the anchored line. `set:[""]` preserves a blank line.
92
+ - Within a single request you may submit edits in any order — the runtime applies them bottom-up so they don't shift each other. After any request that mutates a file, anchors below the mutation are stale on disk; re-read before issuing more edits to that file.
93
+ - `set` operations target the current file content only. Do not try to reference old line text after the file has changed.
94
+ - Text content must be literal file content with matching indentation. If the file uses tabs, use real tabs.
95
+ - You **MUST NOT** use this tool to reformat or clean up unrelated code.
96
+ </critical>
@@ -1,6 +1,7 @@
1
1
  Navigates, clicks, types, scrolls, drags, queries DOM content, and captures screenshots.
2
2
 
3
3
  <instruction>
4
+ - For fetching static web content (articles, docs, issues/PRs, JSON, PDFs, feeds), prefer the `read` tool with a URL — it returns clean reader-mode text without spinning up a browser. Use this tool only when you need JS execution, authentication, or interactive actions.
4
5
  - `"open"` starts a headless session (or implicitly on first action); `"goto"` navigates to `url`; `"close"` releases the browser
5
6
  - `"observe"` captures a numbered accessibility snapshot — prefer `click_id`/`type_id`/`fill_id` using returned `element_id` values; flags: `include_all`, `viewport_only`
6
7
  - `"click"`, `"type"`, `"fill"`, `"press"`, `"scroll"`, `"drag"` for selector-based interactions — prefer ARIA/text selectors (`p-aria/[name="Sign in"]`, `p-text/Continue`) over brittle CSS
@@ -1,13 +1,14 @@
1
- Edits files via syntax-aware chunks. Run `read(path="file.ts")` first.
1
+ Edits files via syntax-aware chunks. Use `read(path="file.ts")` to read and discover chunks before editing.
2
+ - `read` is the canonical read path for chunk source and `sel="?"` tree listings.
2
3
  - `write` rewrites the entire targeted region — best for most edits.
3
- - `replace` does surgical find-and-replace within a chunk — use when making small changes to a large chunk, or batching multiple substitutions.
4
4
  - `insert` adds content before/after a chunk.
5
+ - `delete` deletes a targeted chunk and must be explicit.
5
6
 
6
7
  Call format: `{"edits": [{"path": "file:chunk#ID~", "write": "new body"}, …]}`
7
8
 
8
9
  <rules>
9
- - **MUST** `read` first. Never invent chunk paths or IDs. Copy them from the latest `read` output or edit response.
10
- - `path` format: `file:selector` — e.g. `src/app.ts:fn_foo#ABCD~`. Append `~` for body, `^` for head, or nothing for the whole chunk. Include `#ID` for `put`/`find`+`replace`/`delete`.
10
+ - **MUST** inspect first with `read`. Never invent chunk paths or IDs. Copy them from the latest `read` output or edit response.
11
+ - `path` format: `file:selector` — e.g. `src/app.ts:fn_foo#thth~`. Append `~` for body, `^` for head, or nothing for the whole chunk. Include `#ID` for `write`/`delete`.
11
12
  - If the exact chunk path is unclear, run `read(path="file", sel="?")` and copy a selector from that listing.
12
13
  {{#if chunkAutoIndent}}
13
14
  - Use `\t` for indentation in `content`. Write content at indent-level 0 — the tool re-indents it to match the chunk's position in the file. For example, to replace `~` of a method, write the body starting at column 0:
@@ -16,6 +17,8 @@ Call format: `{"edits": [{"path": "file:chunk#ID~", "write": "new body"}, …]}`
16
17
  ```
17
18
  The tool adds the correct base indent automatically. Never manually pad with the chunk's own indentation.
18
19
  Multiple sibling body lines at the same level all start at column 0: `"print(a)\nprint(b)\nprint(c)\n"`. Only use `\t` when nesting deeper (e.g. `"if cond:\n\tinner\nouter\n"`).
20
+ Before applying the target's base indent, the tool strips any common leading whitespace shared by all non-empty `write` lines as a safety net. Do not rely on that cleanup for mixed indentation; write `~` bodies at column 0 and use one `\t` per relative nesting level.
21
+ Multi-line replacements use the same relative-indentation model: the replacement text is dedented, then re-indented to the matched source line. Do not include the chunk's base indentation in replacement text.
19
22
  **Common mistake** when replacing `~` of a function body: do NOT include the function's own indentation.
20
23
  Wrong: `"if b == 0:\n\t\treturn None\n\treturn a / b\n"` — adds the function's base `\t` to every line.
21
24
  Correct: `"if b == 0:\n\treturn None\nreturn a / b\n"` — `if` and `return a / b` at column 0, only `return None` gets `\t` for nesting.
@@ -26,10 +29,15 @@ Call format: `{"edits": [{"path": "file:chunk#ID~", "write": "new body"}, …]}`
26
29
  content: "if (x) {\n return true;\n}"
27
30
  ```
28
31
  The tool adds the correct base indent automatically, then preserves the tabs/spaces you used inside the snippet. Never manually pad with the chunk's own indentation.
32
+ Before applying the target's base indent, the tool strips any common leading whitespace shared by all non-empty `write` lines as a safety net. Do not rely on that cleanup for mixed indentation; write `~` bodies at column 0.
33
+ Multi-line replacements use the same relative-indentation model: the replacement text is dedented, then re-indented to the matched source line. Do not include the chunk's base indentation in replacement text.
29
34
  {{/if}}
30
- - Region suffixes only apply to container chunks (classes, functions, impl blocks, sections). On leaf chunks (enum variants, fields, single statements, and compound statements like `if`/`for`/`while`/`match`/`try`), `~` and `^` silently fall back to whole-chunk replacement — prefer the unsuffixed form and always supply the complete replacement (condition + body, not just the body) to avoid dropping structural parts.
31
- - `put`, `find`+`replace`, and `delete` require the current ID. `prepend`/`append` do not.
35
+ - Region suffixes only apply to chunks with a real head/body boundary (classes, functions, impl blocks, and similar containers). On code leaf chunks (enum variants, fields, single statements, and compound statements like `if`/`for`/`while`/`match`/`try`), `~` and `^` are rejected. Use the unsuffixed selector and supply the complete replacement content, or edit the parent container's `~` body.
36
+ - Unsuffixed `write` on a leaf chunk uses your content verbatim after normal replacement; it is not a body-region rewrite. Include the exact indentation and punctuation the leaf needs in the file.
37
+ - `^` head writes and `~` body writes use the same base-indent model: write content at column 0 relative to the target region, and the tool applies the chunk's file indentation.
38
+ - `write` and `delete` require the current ID. `prepend`/`append` do not.
32
39
  - **IDs change after every edit.** The edit response always carries the new IDs — use those for the next call or run `read(path="file", sel="?")` to refresh. Never reuse an ID from before the latest edit.
40
+ - Same-file edit batches are transactional: if any operation in that file fails, no changes from that file's batch are saved. Multi-file edit calls run per file, so a later file error does not roll back earlier files that already succeeded.
33
41
  </rules>
34
42
 
35
43
  <critical>
@@ -42,56 +50,57 @@ You **MUST** use the narrowest region that covers your change. Putting without a
42
50
 
43
51
  <regions>
44
52
  In `read` output, lines marked `^` between the line number and `|` are **head** lines (doc comments, attributes/decorators, signature). Lines without `^` are **body** lines. Use this to decide which region to target:
45
- - `fn_foo#ID~` — **body only (the default choice for most edits).** Head lines (`^`) are preserved automatically — doc comments, attributes, and signature stay untouched. On leaf chunks, falls back to whole chunk.
53
+ - `fn_foo#ID~` — **body only (the default choice for most edits).** Head lines (`^`) are preserved automatically — doc comments, attributes, and signature stay untouched. On code leaf chunks, this is rejected because there is no safe body boundary.
46
54
  - `fn_foo#ID^` — head only (decorators, attributes, doc comments, signature, opening delimiter). Body stays untouched.
47
55
  - `fn_foo#ID` — entire chunk including leading trivia. **You must include doc comments and attributes in `content`; omitting them deletes them.**
48
- - `chunk~` + `append`/`prepend` inserts *inside* the container. `chunk` + `append`/`prepend` inserts *outside*.
56
+ - `chunk~` + `append`/`prepend` inserts *inside* the container. `chunk` + `append`/`prepend` inserts *outside*. Appending to a container without `~` emits a warning because it lands after the closing delimiter, not before it.
49
57
 
50
- **Note on leading trivia:** whether a decorator/doc comment belongs to `^` depends on the parser. In Rust and Python, attributes and decorators are attached to the function chunk, so `^` covers them. In TypeScript/JavaScript, a `@decorator` + `/** jsdoc */` block immediately above a method often surfaces as a **separate sibling chunk** (shown as `chunk#ID` in the `?` listing) rather than as part of the function's `^`. If you need to rewrite a decorator, check the `?` listing for a sibling `chunk#ID` directly above your target.
58
+ **Note on leading trivia:** whether a decorator/doc comment belongs to `^` depends on the parser. In Rust and Python, attributes and decorators are attached to the function chunk, so `^` covers them. In TypeScript/JavaScript, a `@decorator` + `/** jsdoc */` block immediately above a method often surfaces as a **separate sibling chunk** (shown as `chunk#ID` in the `?` listing) rather than as part of the function's `^`. JSDoc directly above a plain function is more likely to be absorbed into that function's `^`. If you need to rewrite a decorated member, run `read(path="file", sel="?")` and check for a sibling `chunk#ID` directly above your target.
51
59
 
52
- **Note on non-code formats:** for prose and data formats (markdown, YAML, JSON, fenced code blocks, frontmatter), `^` and `~` fall back to the whole chunk. Always replace the entire chunk and include any delimiter syntax (fence backticks, `---` frontmatter markers, list markers) in your `content` omitting them deletes them. For markdown sections (`sect_*`), always use unsuffixed whole-chunk replace — `^` and `~` on section containers also fall back to whole-chunk replace. When editing fenced code blocks in markdown, use the exact whitespace from the file (read with `raw` first) — the tool preserves literal indentation inside fenced blocks, but any content you supply is written verbatim. To insert content after a markdown section heading, use `after` on the heading chunk (`sect_*.chunk` or `sect_*.chunk_1`) not `before`/`prepend` on the section itself, which lands physically before the heading and gets absorbed by the preceding section on reparse.
60
+ **Python notes:** Python docstrings are body lines, not head lines. A `~` body write on a function that has a docstring deletes the docstring unless you include the docstring in `content`. Python enum members and nested functions/closures are often opaque inside their parent chunk and may not appear as addressable child chunks; rewrite the parent container body. Python decorated class/function `^` writes and Python `^` deletes are rejected because indentation-sensitive bodies can become attached to the wrong block while still parsing.
61
+
62
+ **Note on non-code formats:** for prose and data formats (markdown, YAML, JSON, frontmatter), unsupported `^` and `~` suffixes warn and fall back to whole-chunk editing. Always replace the entire chunk and include any delimiter syntax (fence backticks, `---` frontmatter markers, list markers, table rows, headings) in your `content` — omitting them deletes them. For markdown sections (`sect_*`), prefer unsuffixed whole-chunk replace because `^`/`~` on prose sections can replace the heading and child content too; if you only need the heading, target the heading child chunk shown in `sel="?"`. Fenced code blocks with a declared language are parsed again and can expose inner chunks such as `code_py#ID.fn_gre#ID`; target those inner chunks when available. Markdown root writes preserve fenced code indentation verbatim. Recognized pipe tables expose `row_N` children for row-level edits; table cells and list items are not independently addressable, so rewrite the whole list/table chunk for those structural changes. Appending a table-row-shaped string (`| value |`) to a table chunk inserts it before the trailing blank-line separator so it remains part of the table. Otherwise read with `raw` first and preserve the exact whitespace inside fences. To insert content after a markdown section heading, use `after` on the heading chunk (`sect_*.chunk` or `sect_*.chunk_1`) — not `before`/`prepend` on the section itself, which lands physically before the heading and gets absorbed by the preceding section on reparse.
53
63
  </regions>
54
64
 
55
65
  <ops>
56
- Each edit entry has `path` (`file:selector`) plus **exactly one** operation field — `write`, `replace`, or `insert`. Never set more than one on the same entry.
66
+ Each edit entry has `path` (`file:selector`) plus **exactly one** operation field — `write`, `insert`, or `delete`. Never set more than one on the same entry. `write:null`, `write:""`, and bare `{path}` entries are rejected; they do not delete.
57
67
 
58
68
  |fields|path (selector part)|effect|
59
69
  |---|---|---|
60
70
  |`write: "content"`|`file:chunk#ID`, `file:chunk#ID~`, or `file:chunk#ID^`|write complete new content to the region|
61
- |`write: null`|`file:chunk#ID`|delete the chunk|
62
- |`replace: {old, new}`|`file:chunk#ID`|find a literal substring in the chunk and replace it|
71
+ |`delete: true`|`file:chunk#ID`|delete the chunk explicitly|
63
72
  |`insert: {loc, body}`|`file:chunk` or `file:chunk~`|insert before/after the chunk (`loc`: `"prepend"` or `"append"`)|
64
73
  </ops>
65
74
 
66
75
  <examples>
67
76
  Given this `read` output for `counter.rs`:
68
77
  ```
69
- | counter.rs·62L·rust·#ZRPW
78
+ | counter.rs·62L·rust·#anth
70
79
  |
71
- @imp#MNHH
80
+ @imp#erhe
72
81
  1 |use std::fmt;
73
82
  |
74
- @struct_Counte#QTSX
83
+ @struct_Counte#onat
75
84
  3^|/// A simple counter that tracks a value and its history.
76
85
  4^|#[derive(Debug, Clone)]
77
86
  5^|pub struct Counter {
78
- -@struct_Counte.field_value#MQTW
87
+ -@struct_Counte.field_value#enth
79
88
  6 | /// The current value.
80
89
  7 | value: i32,
81
- -@struct_Counte.field_max#HJMQ
90
+ -@struct_Counte.field_max#seti
82
91
  8 | /// Maximum allowed value.
83
92
  9 | max: i32,
84
93
  10 |}
85
94
  |
86
- @impl_Counte#VNPP
95
+ @impl_Counte#reha
87
96
  12^|impl Counter {
88
- -@impl_Counte.fn_new#RWZV
97
+ -@impl_Counte.fn_new#ndas
89
98
  13^| /// Creates a new counter starting at zero.
90
99
  14^| pub fn new(max: i32) -> Self {
91
100
  15 | Self { value: 0, max }
92
101
  16 | }
93
102
  17 |
94
- -@impl_Counte.fn_increm#MNHV
103
+ -@impl_Counte.fn_increm#ouer
95
104
  18^| /// Increments the counter by one, clamping at max.
96
105
  19^| pub fn increment(&mut self) {
97
106
  20 | if self.value < self.max {
@@ -99,7 +108,7 @@ Given this `read` output for `counter.rs`:
99
108
  22 | }
100
109
  23 | }
101
110
  24 |
102
- -@impl_Counte.fn_decrem#TTWB
111
+ -@impl_Counte.fn_decrem#arve
103
112
  25^| /// Decrements the counter by one, clamping at zero.
104
113
  26^| pub fn decrement(&mut self) {
105
114
  27 | if self.value > 0 {
@@ -107,173 +116,43 @@ Given this `read` output for `counter.rs`:
107
116
  29 | }
108
117
  30 | }
109
118
  31 |
110
- -@impl_Counte.fn_get#PTNT
119
+ -@impl_Counte.fn_get#arco
111
120
  32^| /// Returns the current value.
112
121
  33^| pub fn get(&self) -> i32 {
113
122
  34 | self.value
114
123
  35 | }
115
124
  36 |}
116
125
  |
117
- @impl_Displa#BNJH
126
+ @impl_Displa#meha
118
127
  38^|impl fmt::Display for Counter {
119
- -@impl_Displa.fn_fmt#NKRN
128
+ -@impl_Displa.fn_fmt#deri
120
129
  39^| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121
130
  40 | write!(f, "Counter({}/{})", self.value, self.max)
122
131
  41 | }
123
132
  42 |}
124
- |
125
- @mod_tests#YWXM
126
- 44^|#[cfg(test)]
127
- 45^|mod tests {
128
- -@mod_tests.chunk#VSMY
129
- 46 | use super::*;
130
- 47 |
131
- -@mod_tests.fn_test_i#YXQZ
132
- 48^| #[test]
133
- 49^| fn test_increment() {
134
- 50 | let mut c = Counter::new(10);
135
- 51 | c.increment();
136
- 52 | assert_eq!(c.get(), 1);
137
- 53 | }
138
- 54 |
139
- -@mod_tests.fn_test_d#XPBQ
140
- 55^| #[test]
141
- 56^| fn test_decrement_at_zero() {
142
- 57 | let mut c = Counter::new(10);
143
- 58 | c.decrement();
144
- 59 | assert_eq!(c.get(), 0);
145
- 60 | }
146
- 61 |}
147
- ```
148
-
149
- **Understanding `^` markers in `read` output:** Lines marked with `^` between the line number and `|` (e.g. ` 3^|`) are **head** lines — doc comments, attributes, and the signature. Lines without `^` (e.g. ` 7 |`) are **body** lines. `~` replaces body lines only, keeping head lines intact.
150
-
151
- **Put body** (`~` — the common case):
152
- ```
153
- { "path": "counter.rs:impl_Counte.fn_increm#MNHV~", "write": "self.value = (self.value + 1).min(self.max);\n" }
154
- ```
155
- Result — only the body (non-`^` lines) changes; the doc comment, signature, and closing `}` are all preserved:
156
- ```
157
- /// Increments the counter by one, clamping at max.
158
- pub fn increment(&mut self) {
159
- self.value = (self.value + 1).min(self.max);
160
- }
161
- ```
162
-
163
- **Write whole chunk** (rewrite signature + doc comment + body):
164
- ```
165
- { "path": "counter.rs:impl_Counte.fn_increm#MNHV", "write": "/// Increments by the given step, clamping at max.\npub fn increment(&mut self, step: i32) {\n\tself.value = (self.value + step).min(self.max);\n}\n" }
166
- ```
167
- Result — **everything** including the doc comment and signature is rewritten. You must include them; omitting them deletes them:
168
- ```
169
- /// Increments by the given step, clamping at max.
170
- pub fn increment(&mut self, step: i32) {
171
- self.value = (self.value + step).min(self.max);
172
- }
173
- ```
174
-
175
- **Write head** (`^` — attributes, doc comments, signature):
176
- ```
177
- { "path": "counter.rs:impl_Counte.fn_get#PTNT^", "write": "/// Returns the current counter value.\n#[inline]\npub fn get(&self) -> i32 {\n" }
178
- ```
179
- Result — the head (all `^` lines + opening brace) changes, body untouched:
180
- ```
181
- /// Returns the current counter value.
182
- #[inline]
183
- pub fn get(&self) -> i32 {
184
- self.value
185
- }
186
- ```
187
-
188
- **Find and replace** (surgical edit within a chunk):
189
- ```
190
- { "path": "counter.rs:impl_Counte.fn_increm#MNHV", "replace": { "old": "self.value += 1;", "new": "self.value = (self.value + 1).min(self.max);" } }
191
133
  ```
192
- Result only the matched substring changes, everything else is preserved.
193
-
194
- **Insert before a chunk** (`prepend`):
195
- ```
196
- { "path": "counter.rs:impl_Counte.fn_get", "insert": { "loc": "prepend", "body": "/// Resets the counter to zero.\npub fn reset(&mut self) {\n\tself.value = 0;\n}\n\n" } }
197
- ```
198
- Result a new method is inserted before `fn get`:
199
- ```
200
- /// Resets the counter to zero.
201
- pub fn reset(&mut self) {
202
- self.value = 0;
203
- }
204
-
205
- /// Returns the current value.
206
- pub fn get(&self) -> i32 {
207
- ```
208
-
209
- **Insert after a chunk** (`append`):
210
- ```
211
- { "path": "counter.rs:struct_Counte", "insert": { "loc": "append", "body": "\nimpl Default for Counter {\n\tfn default() -> Self {\n\t\tSelf { value: 0, max: 100 }\n\t}\n}\n" } }
212
- ```
213
- Result a new impl block appears after the struct:
214
- ```
215
- }
216
-
217
- impl Default for Counter {
218
- fn default() -> Self {
219
- Self { value: 0, max: 100 }
220
- }
221
- }
222
-
223
- impl Counter {
224
- ```
225
-
226
- **Insert at start of container body** (`~` + `prepend`):
227
- ```
228
- { "path": "counter.rs:impl_Counte~", "insert": { "loc": "prepend", "body": "/// Creates a counter starting at the given value.\npub fn with_value(value: i32, max: i32) -> Self {\n\tSelf { value: value.min(max), max }\n}\n\n" } }
229
- ```
230
- Result — a new method is added at the top of the impl body, before existing methods:
231
- ```
232
- impl Counter {
233
- /// Creates a counter starting at the given value.
234
- pub fn with_value(value: i32, max: i32) -> Self {
235
- Self { value: value.min(max), max }
236
- }
237
-
238
- /// Creates a new counter starting at zero.
239
- pub fn new(max: i32) -> Self {
240
- ```
241
-
242
- **Insert at end of container body** (`~` + `append`):
243
- ```
244
- { "path": "counter.rs:impl_Counte~", "insert": { "loc": "append", "body": "\n/// Returns true if the counter is at its maximum.\npub fn is_maxed(&self) -> bool {\n\tself.value >= self.max\n}\n" } }
245
- ```
246
- Result — a new method is added at the end of the impl body, before the closing `}`:
247
- ```
248
- pub fn get(&self) -> i32 {
249
- self.value
250
- }
251
-
252
- /// Returns true if the counter is at its maximum.
253
- pub fn is_maxed(&self) -> bool {
254
- self.value >= self.max
255
- }
256
- }
257
- ```
258
-
259
- **Delete a chunk**:
260
- ```
261
- { "path": "counter.rs:impl_Counte.fn_decrem#TTWB", "write": null }
262
- ```
263
- Result — the method (including its doc comment and signature) is removed.
264
- - Indentation rules (important):
265
- {{#if chunkAutoIndent}}
266
- - Use `\t` for each indent level. The tool converts tabs to the file's actual style (2-space, 4-space, etc.).
267
- {{else}}
268
- - Match the file's real indentation characters in your snippet. The tool preserves your literal tabs/spaces after adding the target region's base indent.
269
- {{/if}}
270
- - Do NOT include the chunk's base indentation — only indent relative to the region's opening level.
271
- - For `~` of a function: write at column 0, and use `\t` for *relative* nesting. Flat body: `"return x;\n"`. Multiple sibling lines: `"print(a)\nprint(b)\nprint(c)\n"` — all at column 0, the tool adds the function's base indent. Nested body: `"if (cond) {\n\treturn x;\n}\n"` — the `if` is at column 0, the `return` is one tab in. Python example — to replace `~` of `def divide(a, b):`, write: `"if b == 0:\n\treturn None\nreturn a / b\n"` — the `if` and `return a / b` are at column 0, `return None` is one `\t` in.
272
- - For `^`: write at the chunk's own depth. A class member's head uses `"/// doc\n#[attr]\npub fn start() {"`.
273
- {{#if chunkAutoIndent}}
274
- - For a top-level item: start at zero indent. Write `"fn foo() {\n\treturn 1;\n}\n"`.
275
- {{else}}
276
- - For a top-level item: start at zero indent. Write `"fn foo() {\n return 1;\n}\n"`.
277
- {{/if}}
278
- - The tool strips common leading indentation from your content as a safety net, so accidental over-indentation is corrected.
134
+ Lines marked `^` between the line number and `|` are **head** lines (doc comments, attributes, signature). Lines without `^` are **body** lines. `~` replaces body lines only; `^` replaces head lines only.
135
+
136
+ # Put body (`~` — the common case)
137
+ `{ "path": "counter.rs:impl_Counte.fn_increm#ouer~", "write": "self.value = (self.value + 1).min(self.max);\n" }`
138
+ Only body changes; doc comment, signature, and closing `}` are preserved.
139
+ # Write whole chunk (rewrite signature + doc + body)
140
+ `{ "path": "counter.rs:impl_Counte.fn_increm#ouer", "write": "/// Increments by the given step, clamping at max.\npub fn increment(&mut self, step: i32) {\n\tself.value = (self.value + step).min(self.max);\n}\n" }`
141
+ Everything is rewritten. Omitting the doc comment or signature deletes them.
142
+ # Write head (`^` attributes, doc comments, signature)
143
+ `{ "path": "counter.rs:impl_Counte.fn_get#arco^", "write": "/// Returns the current counter value.\n#[inline]\npub fn get(&self) → i32 {\n" }`
144
+ Head changes (all `^` lines + opening brace); body untouched.
145
+ # Insert before a chunk (`prepend`)
146
+ `{ "path": "counter.rs:impl_Counte.fn_get", "insert": { "loc": "prepend", "body": "/// Resets the counter to zero.\npub fn reset(&mut self) {\n\tself.value = 0;\n}\n\n" } }`
147
+ # Insert after a chunk (`append`)
148
+ `{ "path": "counter.rs:struct_Counte", "insert": { "loc": "append", "body": "\nimpl Default for Counter {\n\tfn default() Self {\n\t\tSelf { value: 0, max: 100 }\n\t}\n}\n" } }`
149
+ # Insert at start of container body (`~` + `prepend`)
150
+ `{ "path": "counter.rs:impl_Counte~", "insert": { "loc": "prepend", "body": "/// Creates a counter starting at the given value.\npub fn with_value(value: i32, max: i32) → Self {\n\tSelf { value: value.min(max), max }\n}\n\n" } }`
151
+ Lands at the top of the impl body, before existing methods.
152
+ # Insert at end of container body (`~` + `append`)
153
+ `{ "path": "counter.rs:impl_Counte~", "insert": { "loc": "append", "body": "\n/// Returns true if the counter is at its maximum.\npub fn is_maxed(&self) bool {\n\tself.value self.max\n}\n" } }`
154
+ Lands at the end of the impl body, before the closing `}`.
155
+ # Delete a chunk
156
+ `{ "path": "counter.rs:impl_Counte.fn_decrem#arve", "delete": true }`
157
+ Removes the method including its doc comment and signature.
279
158
  </examples>
@@ -16,14 +16,13 @@ Provides debugger access through the Debug Adapter Protocol (DAP). Use this to l
16
16
  - Adapter availability depends on local binaries. Common built-ins: `gdb`, `lldb-dap`, `python -m debugpy.adapter`, `dlv dap`.
17
17
  </caution>
18
18
 
19
- <example name="launch and inspect hang">
19
+ <examples>
20
+ # Launch and inspect hang
20
21
  1. `debug(action: "launch", program: "./my_app")`
21
22
  2. `debug(action: "set_breakpoint", file: "src/main.c", line: 42)`
22
23
  3. `debug(action: "continue")`
23
24
  4. If the program appears hung: `debug(action: "pause")`
24
25
  5. Inspect state with `threads`, `stack_trace`, `scopes`, and `variables`
25
- </example>
26
-
27
- <example name="raw debugger command through repl">
26
+ # Raw debugger command through repl
28
27
  `debug(action: "evaluate", expression: "info registers", context: "repl")`
29
- </example>
28
+ </examples>
@@ -19,15 +19,14 @@ Use when:
19
19
  Presents plan to user for approval. If approved, plan mode exits with full tool access restored and the plan is renamed to `local://<title>.md`.
20
20
  </output>
21
21
 
22
- <example name="ready">
22
+ <examples>
23
+ # Ready
23
24
  Plan complete at local://PLAN.md, no open questions.
24
25
  → Call `exit_plan_mode` with `{ "title": "WP_MIGRATION_PLAN" }`
25
- </example>
26
-
27
- <example name="unclear">
26
+ # Unclear
28
27
  Unsure about auth method (OAuth vs JWT).
29
28
  → Use `ask` first to clarify, then call `exit_plan_mode`
30
- </example>
29
+ </examples>
31
30
 
32
31
  <avoid>
33
32
  - **MUST NOT** call before plan is written to file