@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.
- package/CHANGELOG.md +143 -1
- package/package.json +19 -19
- package/src/autoresearch/prompt.md +1 -1
- package/src/cli/args.ts +10 -1
- package/src/cli/shell-cli.ts +15 -3
- package/src/commit/agentic/prompts/analyze-file.md +1 -1
- package/src/config/model-registry.ts +67 -15
- package/src/config/prompt-templates.ts +5 -5
- package/src/config/settings-schema.ts +63 -4
- package/src/cursor.ts +3 -8
- package/src/debug/system-info.ts +6 -2
- package/src/discovery/claude.ts +58 -36
- package/src/discovery/helpers.ts +3 -3
- package/src/discovery/opencode.ts +20 -2
- package/src/edit/diff.ts +50 -47
- package/src/edit/index.ts +87 -57
- package/src/edit/line-hash.ts +735 -19
- package/src/edit/modes/apply-patch.ts +0 -9
- package/src/edit/modes/atom.ts +658 -0
- package/src/edit/modes/chunk.ts +144 -78
- package/src/edit/modes/hashline.ts +223 -146
- package/src/edit/modes/patch.ts +5 -9
- package/src/edit/modes/replace.ts +6 -11
- package/src/edit/renderer.ts +112 -143
- package/src/edit/streaming.ts +385 -0
- package/src/exec/bash-executor.ts +58 -5
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +4 -12
- package/src/extensibility/custom-tools/types.ts +2 -0
- package/src/extensibility/custom-tools/wrapper.ts +2 -1
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/internal-urls/pi-protocol.ts +0 -2
- package/src/lsp/client.ts +8 -1
- package/src/lsp/defaults.json +2 -1
- package/src/lsp/index.ts +1 -1
- package/src/mcp/render.ts +1 -8
- package/src/modes/acp/acp-agent.ts +76 -2
- package/src/modes/components/assistant-message.ts +5 -34
- package/src/modes/components/diff.ts +23 -14
- package/src/modes/components/footer.ts +21 -16
- package/src/modes/components/hook-editor.ts +1 -1
- package/src/modes/components/settings-defs.ts +6 -1
- package/src/modes/components/todo-reminder.ts +1 -8
- package/src/modes/components/tool-execution.ts +112 -105
- package/src/modes/controllers/input-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +0 -2
- package/src/modes/print-mode.ts +8 -0
- package/src/modes/theme/mermaid-cache.ts +13 -52
- package/src/modes/theme/theme.ts +2 -2
- package/src/prompts/agents/librarian.md +1 -1
- package/src/prompts/agents/reviewer.md +4 -4
- package/src/prompts/ci-green-request.md +1 -1
- package/src/prompts/review-request.md +1 -1
- package/src/prompts/system/subagent-system-prompt.md +3 -3
- package/src/prompts/system/subagent-yield-reminder.md +11 -0
- package/src/prompts/system/system-prompt.md +4 -1
- package/src/prompts/tools/ask.md +3 -2
- package/src/prompts/tools/ast-edit.md +15 -19
- package/src/prompts/tools/ast-grep.md +18 -24
- package/src/prompts/tools/atom.md +96 -0
- package/src/prompts/tools/browser.md +1 -0
- package/src/prompts/tools/chunk-edit.md +58 -179
- package/src/prompts/tools/debug.md +4 -5
- package/src/prompts/tools/exit-plan-mode.md +4 -5
- package/src/prompts/tools/find.md +4 -8
- package/src/prompts/tools/github.md +18 -0
- package/src/prompts/tools/grep.md +8 -8
- package/src/prompts/tools/hashline.md +22 -89
- package/src/prompts/tools/{gemini-image.md → image-gen.md} +1 -1
- package/src/prompts/tools/inspect-image.md +6 -6
- package/src/prompts/tools/lsp.md +6 -0
- package/src/prompts/tools/patch.md +12 -19
- package/src/prompts/tools/python.md +3 -2
- package/src/prompts/tools/read-chunk.md +46 -8
- package/src/prompts/tools/read.md +9 -6
- package/src/prompts/tools/ssh.md +8 -17
- package/src/prompts/tools/todo-write.md +54 -41
- package/src/sdk.ts +22 -14
- package/src/session/agent-session.ts +61 -22
- package/src/session/session-manager.ts +228 -57
- package/src/session/streaming-output.ts +11 -0
- package/src/system-prompt.ts +7 -2
- package/src/task/executor.ts +44 -48
- package/src/task/render.ts +11 -13
- package/src/tools/ask.ts +7 -7
- package/src/tools/ast-edit.ts +45 -41
- package/src/tools/ast-grep.ts +77 -85
- package/src/tools/bash.ts +21 -9
- package/src/tools/browser.ts +32 -30
- package/src/tools/calculator.ts +4 -4
- package/src/tools/cancel-job.ts +1 -1
- package/src/tools/checkpoint.ts +2 -2
- package/src/tools/debug.ts +41 -37
- package/src/tools/exit-plan-mode.ts +1 -1
- package/src/tools/find.ts +4 -4
- package/src/tools/gh-renderer.ts +12 -4
- package/src/tools/gh.ts +514 -712
- package/src/tools/grep.ts +115 -130
- package/src/tools/{gemini-image.ts → image-gen.ts} +459 -60
- package/src/tools/index.ts +14 -32
- package/src/tools/inspect-image.ts +3 -3
- package/src/tools/json-tree.ts +114 -114
- package/src/tools/match-line-format.ts +9 -8
- package/src/tools/notebook.ts +8 -7
- package/src/tools/poll-tool.ts +2 -1
- package/src/tools/python.ts +9 -23
- package/src/tools/read.ts +32 -21
- package/src/tools/render-mermaid.ts +1 -1
- package/src/tools/render-utils.ts +18 -0
- package/src/tools/renderers.ts +2 -2
- package/src/tools/report-tool-issue.ts +3 -2
- package/src/tools/resolve.ts +1 -1
- package/src/tools/review.ts +12 -10
- package/src/tools/search-tool-bm25.ts +2 -4
- package/src/tools/sqlite-reader.ts +116 -3
- package/src/tools/ssh.ts +4 -4
- package/src/tools/todo-write.ts +172 -147
- package/src/tools/vim.ts +14 -15
- package/src/tools/write.ts +4 -4
- package/src/tools/{submit-result.ts → yield.ts} +11 -13
- package/src/utils/edit-mode.ts +2 -1
- package/src/utils/file-display-mode.ts +10 -5
- package/src/utils/git.ts +9 -5
- package/src/utils/shell-snapshot.ts +2 -3
- package/src/vim/render.ts +4 -4
- package/src/web/search/providers/codex.ts +129 -6
- package/src/prompts/system/subagent-submit-reminder.md +0 -11
- package/src/prompts/tools/gh-issue-view.md +0 -11
- package/src/prompts/tools/gh-pr-checkout.md +0 -12
- package/src/prompts/tools/gh-pr-diff.md +0 -12
- package/src/prompts/tools/gh-pr-push.md +0 -11
- package/src/prompts/tools/gh-pr-view.md +0 -11
- package/src/prompts/tools/gh-repo-view.md +0 -11
- package/src/prompts/tools/gh-run-watch.md +0 -12
- package/src/prompts/tools/gh-search-issues.md +0 -11
- 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
|
|
6
|
-
-
|
|
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 $_ { … }`)
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
6
|
-
-
|
|
7
|
-
-
|
|
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 $_ { … }`)
|
|
13
|
-
- C++ qualified calls used as expression statements need the statement semicolon in the pattern: use `ns::doThing($ARG);`, `$CALLEE($ARG)
|
|
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:
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
|
45
|
-
- Parse issues are query failure, not evidence of absence: repair the pattern or tighten `path
|
|
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.
|
|
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
|
|
10
|
-
- `path` format: `file:selector` — e.g. `src/app.ts:fn_foo#
|
|
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
|
|
31
|
-
-
|
|
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,
|
|
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
|
|
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
|
-
**
|
|
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`, `
|
|
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
|
-
|`
|
|
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·#
|
|
78
|
+
| counter.rs·62L·rust·#anth
|
|
70
79
|
|
|
|
71
|
-
@imp#
|
|
80
|
+
@imp#erhe
|
|
72
81
|
1 |use std::fmt;
|
|
73
82
|
|
|
|
74
|
-
@struct_Counte#
|
|
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#
|
|
87
|
+
-@struct_Counte.field_value#enth
|
|
79
88
|
6 | /// The current value.
|
|
80
89
|
7 | value: i32,
|
|
81
|
-
-@struct_Counte.field_max#
|
|
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#
|
|
95
|
+
@impl_Counte#reha
|
|
87
96
|
12^|impl Counter {
|
|
88
|
-
-@impl_Counte.fn_new#
|
|
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#
|
|
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#
|
|
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#
|
|
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#
|
|
126
|
+
@impl_Displa#meha
|
|
118
127
|
38^|impl fmt::Display for Counter {
|
|
119
|
-
-@impl_Displa.fn_fmt#
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
{ "path": "counter.rs:
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
<
|
|
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
|
-
|
|
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
|
-
</
|
|
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
|
-
<
|
|
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
|
-
|
|
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
|
-
</
|
|
29
|
+
</examples>
|
|
31
30
|
|
|
32
31
|
<avoid>
|
|
33
32
|
- **MUST NOT** call before plan is written to file
|