@oh-my-pi/pi-coding-agent 13.9.5 → 13.9.11
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 +45 -0
- package/package.json +7 -7
- package/src/modes/components/tool-execution.ts +9 -5
- package/src/modes/controllers/event-controller.ts +8 -2
- package/src/modes/utils/ui-helpers.ts +5 -1
- package/src/prompts/agents/explore.md +1 -0
- package/src/prompts/agents/librarian.md +2 -0
- package/src/prompts/system/handoff-document.md +46 -0
- package/src/prompts/system/subagent-system-prompt.md +3 -9
- package/src/prompts/system/system-prompt.md +2 -1
- package/src/prompts/tools/ast-edit.md +20 -5
- package/src/prompts/tools/ast-grep.md +26 -9
- package/src/prompts/tools/bash.md +2 -0
- package/src/session/agent-session.ts +55 -59
- package/src/tools/ast-edit.ts +8 -7
- package/src/tools/ast-grep.ts +16 -18
- package/src/tools/bash.ts +147 -7
- package/src/tools/fetch.ts +47 -30
- package/src/tools/grep.ts +2 -7
- package/src/tools/path-utils.ts +10 -0
- package/src/web/scrapers/docs-rs.ts +653 -0
- package/src/web/scrapers/index.ts +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,51 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [13.9.10] - 2026-03-08
|
|
6
|
+
### Added
|
|
7
|
+
|
|
8
|
+
- Added `env` parameter to bash tool to pass environment variables safely without shell re-parsing, preventing quote and special character bugs with multiline or untrusted values
|
|
9
|
+
- Added support for rendering partial `env` assignments in command preview while tool arguments are still streaming
|
|
10
|
+
- Added `env` support to the bash tool so commands can reference safe shell variables without inline quoting bugs for multiline or quote-heavy values
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Changed bash tool to display environment variable assignments in command preview when `env` parameter is used
|
|
15
|
+
|
|
16
|
+
## [13.9.8] - 2026-03-08
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- Added docs.rs scraper for extracting Rust crate documentation from rustdoc JSON, including support for modules, functions, structs, traits, enums, and other Rust items with caching
|
|
20
|
+
|
|
21
|
+
## [13.9.7] - 2026-03-08
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- Added `skipPostPromptRecoveryWait` option to handoff operations to defer recovery work until after handoff completion
|
|
26
|
+
- Added deferred auto-compaction scheduling to allow threshold-triggered handoffs to complete while the original prompt is still unwinding
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Extracted handoff document template to dedicated prompt file for improved maintainability and template variable support
|
|
31
|
+
- Changed handoff prompt generation to use template rendering with support for custom focus instructions
|
|
32
|
+
- Refactored internal prompt-in-flight tracking from boolean flag to counter to properly handle nested prompt operations
|
|
33
|
+
- Moved llms.txt endpoint discovery to fallback strategy when rendered page content is low quality, prioritizing page-specific content over site-wide files
|
|
34
|
+
- Enhanced llms.txt endpoint detection to scope candidates to the requested URL path, searching section-specific files before site-wide ones
|
|
35
|
+
|
|
36
|
+
## [13.9.6] - 2026-03-08
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
|
|
40
|
+
- Added `glob` parameter to `ast_grep` and `ast_edit` tools for additional glob filtering relative to the `path` parameter
|
|
41
|
+
- Added `combineSearchGlobs` utility function to merge glob patterns from `path` and `glob` parameters
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- Renamed `patterns` parameter to `pat` in `ast_grep` tool for consistency
|
|
46
|
+
- Renamed `selector` parameter to `sel` in `ast_grep` and `ast_edit` tools for brevity
|
|
47
|
+
- Updated tool documentation with expanded guidance on AST pattern syntax, metavariable usage, and contextual matching strategies
|
|
48
|
+
- Updated `grep` tool to combine glob patterns from `path` and `glob` parameters instead of throwing an error when both are provided
|
|
49
|
+
|
|
5
50
|
## [13.9.4] - 2026-03-07
|
|
6
51
|
### Added
|
|
7
52
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "13.9.
|
|
4
|
+
"version": "13.9.11",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/can1357/oh-my-pi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -41,12 +41,12 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@mozilla/readability": "^0.6",
|
|
44
|
-
"@oh-my-pi/omp-stats": "13.9.
|
|
45
|
-
"@oh-my-pi/pi-agent-core": "13.9.
|
|
46
|
-
"@oh-my-pi/pi-ai": "13.9.
|
|
47
|
-
"@oh-my-pi/pi-natives": "13.9.
|
|
48
|
-
"@oh-my-pi/pi-tui": "13.9.
|
|
49
|
-
"@oh-my-pi/pi-utils": "13.9.
|
|
44
|
+
"@oh-my-pi/omp-stats": "13.9.11",
|
|
45
|
+
"@oh-my-pi/pi-agent-core": "13.9.11",
|
|
46
|
+
"@oh-my-pi/pi-ai": "13.9.11",
|
|
47
|
+
"@oh-my-pi/pi-natives": "13.9.11",
|
|
48
|
+
"@oh-my-pi/pi-tui": "13.9.11",
|
|
49
|
+
"@oh-my-pi/pi-utils": "13.9.11",
|
|
50
50
|
"@sinclair/typebox": "^0.34",
|
|
51
51
|
"@xterm/headless": "^6.0",
|
|
52
52
|
"ajv": "^8.18",
|
|
@@ -478,7 +478,7 @@ export class ToolExecutionComponent extends Container {
|
|
|
478
478
|
},
|
|
479
479
|
this.#renderState,
|
|
480
480
|
theme,
|
|
481
|
-
this.#
|
|
481
|
+
this.#getCallArgsForRender(),
|
|
482
482
|
);
|
|
483
483
|
if (resultComponent) {
|
|
484
484
|
this.#contentBox.addChild(ensureInvalidate(resultComponent));
|
|
@@ -560,10 +560,14 @@ export class ToolExecutionComponent extends Container {
|
|
|
560
560
|
return Math.max(1, Math.min(maxSeconds, value));
|
|
561
561
|
};
|
|
562
562
|
|
|
563
|
-
if (this.#toolName === "bash"
|
|
564
|
-
//
|
|
565
|
-
|
|
566
|
-
|
|
563
|
+
if (this.#toolName === "bash") {
|
|
564
|
+
// Bash needs render context even before a result exists. The renderer uses the pending-call args
|
|
565
|
+
// plus this context to keep the inline command preview visible while tool-call JSON is still streaming.
|
|
566
|
+
if (this.#result) {
|
|
567
|
+
// Pass raw output and expanded state - renderer handles width-aware truncation
|
|
568
|
+
const output = this.#getTextOutput().trimEnd();
|
|
569
|
+
context.output = output;
|
|
570
|
+
}
|
|
567
571
|
context.expanded = this.#expanded;
|
|
568
572
|
context.previewLines = BASH_DEFAULT_PREVIEW_LINES;
|
|
569
573
|
context.timeout = normalizeTimeoutSeconds(this.#args?.timeout, 3600);
|
|
@@ -184,13 +184,19 @@ export class EventController {
|
|
|
184
184
|
continue;
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
+
// Preserve the raw partial JSON for renderers that need to surface fields before the JSON object closes.
|
|
188
|
+
// Bash uses this to show inline env assignments during streaming instead of popping them in at completion.
|
|
189
|
+
const renderArgs =
|
|
190
|
+
"partialJson" in content
|
|
191
|
+
? { ...content.arguments, __partialJson: content.partialJson }
|
|
192
|
+
: content.arguments;
|
|
187
193
|
if (!this.ctx.pendingTools.has(content.id)) {
|
|
188
194
|
this.#resetReadGroup();
|
|
189
195
|
this.ctx.chatContainer.addChild(new Text("", 0, 0));
|
|
190
196
|
const tool = this.ctx.session.getToolByName(content.name);
|
|
191
197
|
const component = new ToolExecutionComponent(
|
|
192
198
|
content.name,
|
|
193
|
-
|
|
199
|
+
renderArgs,
|
|
194
200
|
{
|
|
195
201
|
showImages: settings.get("terminal.showImages"),
|
|
196
202
|
editFuzzyThreshold: settings.get("edit.fuzzyThreshold"),
|
|
@@ -206,7 +212,7 @@ export class EventController {
|
|
|
206
212
|
} else {
|
|
207
213
|
const component = this.ctx.pendingTools.get(content.id);
|
|
208
214
|
if (component) {
|
|
209
|
-
component.updateArgs(
|
|
215
|
+
component.updateArgs(renderArgs, content.id);
|
|
210
216
|
}
|
|
211
217
|
}
|
|
212
218
|
}
|
|
@@ -272,9 +272,13 @@ export class UiHelpers {
|
|
|
272
272
|
|
|
273
273
|
readGroup = null;
|
|
274
274
|
const tool = this.ctx.session.getToolByName(content.name);
|
|
275
|
+
const renderArgs =
|
|
276
|
+
"partialJson" in content
|
|
277
|
+
? { ...content.arguments, __partialJson: content.partialJson }
|
|
278
|
+
: content.arguments;
|
|
275
279
|
const component = new ToolExecutionComponent(
|
|
276
280
|
content.name,
|
|
277
|
-
|
|
281
|
+
renderArgs,
|
|
278
282
|
{
|
|
279
283
|
showImages: settings.get("terminal.showImages"),
|
|
280
284
|
editFuzzyThreshold: settings.get("edit.fuzzyThreshold"),
|
|
@@ -99,6 +99,7 @@ Given a task, you rapidly investigate the codebase and return structured finding
|
|
|
99
99
|
<directives>
|
|
100
100
|
- You **MUST** use tools for broad pattern matching / code search as much as possible.
|
|
101
101
|
- You **SHOULD** invoke tools in parallel when possible—this is a short investigation, and you are supposed to finish in a few seconds.
|
|
102
|
+
- If a search returns empty results, you **MUST** try at least one alternate strategy (different pattern, broader path, or AST search) before concluding the target doesn't exist.
|
|
102
103
|
</directives>
|
|
103
104
|
|
|
104
105
|
<thoroughness>
|
|
@@ -111,6 +111,8 @@ Before acting, determine what kind of question this is:
|
|
|
111
111
|
- If you discover undocumented behavior or gotchas, you **MUST** populate `caveats`.
|
|
112
112
|
- When local `node_modules` has the package, you **SHOULD** prefer it over cloning — it reflects the version the project actually uses.
|
|
113
113
|
- You **SHOULD** use `web_search` to find the canonical repo URL and to check for known issues, but the definitive answer **MUST** come from reading source code.
|
|
114
|
+
- If a search or lookup returns empty or unexpectedly few results, you **MUST** try at least 2 fallback strategies (broader query, alternate path, different source) before concluding nothing exists.
|
|
115
|
+
- If the package is absent from local `node_modules` and cloning fails, you **MUST** fall back to `web_search` for official API documentation before reporting failure.
|
|
114
116
|
</directives>
|
|
115
117
|
|
|
116
118
|
<critical>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<critical>
|
|
2
|
+
Write a comprehensive handoff document for another instance of yourself.
|
|
3
|
+
The handoff **MUST** be sufficient for seamless continuation without access to this conversation.
|
|
4
|
+
Output ONLY the handoff document. No preamble, no commentary, no wrapper text.
|
|
5
|
+
</critical>
|
|
6
|
+
|
|
7
|
+
<instruction>
|
|
8
|
+
Capture exact technical state, not abstractions.
|
|
9
|
+
Include concrete file paths, symbol names, commands run, test results, observed failures, decisions made, and any partial work that materially affects the next step.
|
|
10
|
+
</instruction>
|
|
11
|
+
|
|
12
|
+
<output>
|
|
13
|
+
Use exactly this structure:
|
|
14
|
+
|
|
15
|
+
## Goal
|
|
16
|
+
[What the user is trying to accomplish]
|
|
17
|
+
|
|
18
|
+
## Constraints & Preferences
|
|
19
|
+
- [Any constraints, preferences, or requirements mentioned]
|
|
20
|
+
|
|
21
|
+
## Progress
|
|
22
|
+
### Done
|
|
23
|
+
- [x] [Completed tasks with specifics]
|
|
24
|
+
|
|
25
|
+
### In Progress
|
|
26
|
+
- [ ] [Current work if any]
|
|
27
|
+
|
|
28
|
+
### Pending
|
|
29
|
+
- [ ] [Tasks mentioned but not started]
|
|
30
|
+
|
|
31
|
+
## Key Decisions
|
|
32
|
+
- **[Decision]**: [Rationale]
|
|
33
|
+
|
|
34
|
+
## Critical Context
|
|
35
|
+
- [Code snippets, file paths, function/type names, error messages, or data essential to continue]
|
|
36
|
+
- [Repository state if relevant]
|
|
37
|
+
|
|
38
|
+
## Next Steps
|
|
39
|
+
1. [What should happen next]
|
|
40
|
+
</output>
|
|
41
|
+
|
|
42
|
+
{{#if additionalFocus}}
|
|
43
|
+
<instruction>
|
|
44
|
+
Additional focus: {{additionalFocus}}
|
|
45
|
+
</instruction>
|
|
46
|
+
{{/if}}
|
|
@@ -29,13 +29,7 @@ Your result **MUST** match this TypeScript interface:
|
|
|
29
29
|
{{/if}}
|
|
30
30
|
|
|
31
31
|
{{SECTION_SEPERATOR "Giving Up"}}
|
|
32
|
-
|
|
32
|
+
Giving up is a last resort. If truly blocked, you **MUST** call `submit_result` exactly once with `result.error` describing what you tried and the exact blocker.
|
|
33
|
+
You **MUST NOT** give up due to uncertainty, missing information obtainable via tools or repo context, or needing a design decision you can derive yourself.
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
You **MUST NOT** give up due to uncertainty or missing information obtainable via tools or repo context.
|
|
36
|
-
You **MUST NOT** give up due to requiring a design, you can derive that yourself, more than capable of that.
|
|
37
|
-
|
|
38
|
-
Proceed with the best approach using the most reasonable option.
|
|
39
|
-
|
|
40
|
-
You **MUST** keep going until this ticket is closed.
|
|
41
|
-
This matters.
|
|
35
|
+
You **MUST** keep going until this ticket is closed. This matters.
|
|
@@ -313,8 +313,9 @@ Today is '{{date}}', and your work begins now. Get it right.
|
|
|
313
313
|
- You **MUST** use the most specialized tool, **NEVER** `cat` if there's tool.bash, `rg/grep`:tool.grep, `find`:tool.find, `sed`:tool.edit…
|
|
314
314
|
- Every turn **MUST** materially advance the deliverable.
|
|
315
315
|
- You **MUST** default to action. You **MUST NOT** ask for confirmation to continue work. If you hit an error, you **MUST** fix it. If you know the next step, you **MUST** take it. The user will intervene if needed.
|
|
316
|
-
- You **MUST NOT** make speculative edits before understanding the surrounding design.
|
|
317
316
|
- You **MUST** default to informed action. You **MUST NOT** ask for confirmation to continue work. If you hit an error, you **MUST** fix it. If you know the next step, you **MUST** take it. The user will intervene if needed.
|
|
317
|
+
- You **MUST NOT** make speculative edits before understanding the surrounding design.
|
|
318
|
+
- You **MUST NOT** stop calling tools to save round-trips when the task is incomplete. Completeness beats efficiency.
|
|
318
319
|
- You **MUST NOT** ask when the answer may be obtained from available tools or repo context/files.
|
|
319
320
|
- You **MUST** verify the effect. When a task involves a behavioral change, you **MUST** confirm the change is observable before yielding: run the specific test, command, or scenario that covers your change.
|
|
320
321
|
</critical>
|
|
@@ -2,10 +2,16 @@ 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
|
-
- Narrow scope with `path` before replacing (`path` accepts files, directories, or glob patterns)
|
|
6
|
-
- Default to language-scoped rewrites in mixed repositories: set `lang` and keep `path` narrow
|
|
7
|
-
- Treat parse issues as a scoping signal: tighten `path`/`lang
|
|
5
|
+
- Narrow scope with `path` before replacing (`path` accepts files, directories, or glob patterns; use `glob` for an additional filter relative to `path`)
|
|
6
|
+
- Default to language-scoped rewrites in mixed repositories: set `lang` and keep `path`/`glob` narrow
|
|
7
|
+
- Treat parse issues as a scoping or pattern-shape signal: tighten `path`/`lang`, or rewrite the pattern into valid syntax before retrying
|
|
8
8
|
- Metavariables captured in each rewrite pattern (`$A`, `$$$ARGS`) are substituted into that entry's rewrite template
|
|
9
|
+
- For variadic captures, use `$$$NAME` (not `$$NAME`)
|
|
10
|
+
- Rewrite patterns must parse as valid AST for the target language; if a method or declaration does not parse standalone, wrap it in valid context or switch to a contextual `sel`
|
|
11
|
+
- When using contextual `sel`, the match and replacement target the selected node, not the outer wrapper you used to make the pattern parse
|
|
12
|
+
- For TypeScript declarations and methods, prefer patterns that tolerate annotations you do not care about, e.g. `async function $NAME($$$ARGS): $_ { $$$BODY }` or `class $_ { method($$$ARGS): $_ { $$$BODY } }`
|
|
13
|
+
- Metavariables must be the sole content of an AST node; partial-text metavariables like `prefix$VAR` or `"hello $NAME"` do NOT work in patterns or rewrites
|
|
14
|
+
- To delete matched code, use an empty `out` string: `{"pat":"console.log($$$)","out":""}`
|
|
9
15
|
- Each matched rewrite is a 1:1 structural substitution; you cannot split one capture into multiple nodes or merge multiple captures into one node
|
|
10
16
|
</instruction>
|
|
11
17
|
|
|
@@ -17,14 +23,23 @@ Performs structural AST-aware rewrites via native ast-grep.
|
|
|
17
23
|
<examples>
|
|
18
24
|
- Rename a call site across a directory:
|
|
19
25
|
`{"ops":[{"pat":"oldApi($$$ARGS)","out":"newApi($$$ARGS)"}],"lang":"typescript","path":"src/"}`
|
|
20
|
-
-
|
|
21
|
-
`{"ops":[{"pat":"
|
|
26
|
+
- Delete all matching calls (empty `out` removes the matched node):
|
|
27
|
+
`{"ops":[{"pat":"console.log($$$ARGS)","out":""}],"lang":"typescript","path":"src/"}`
|
|
28
|
+
- Rewrite an import source path:
|
|
29
|
+
`{"ops":[{"pat":"import { $$$IMPORTS } from \"old-package\"","out":"import { $$$IMPORTS } from \"new-package\""}],"lang":"typescript","path":"src/"}`
|
|
30
|
+
- Modernize to optional chaining (same metavariable enforces identity):
|
|
31
|
+
`{"ops":[{"pat":"$A && $A()","out":"$A?.()"}],"lang":"typescript","path":"src/"}`
|
|
22
32
|
- Swap two arguments using captures:
|
|
23
33
|
`{"ops":[{"pat":"assertEqual($A, $B)","out":"assertEqual($B, $A)"}],"lang":"typescript","path":"tests/"}`
|
|
34
|
+
- Rename a TypeScript function declaration while tolerating any return type annotation:
|
|
35
|
+
`{"ops":[{"pat":"async function fetchData($$$ARGS): $_ { $$$BODY }","out":"async function loadData($$$ARGS): $_ { $$$BODY }"}],"sel":"function_declaration","lang":"typescript","path":"src/api.ts"}`
|
|
36
|
+
- Convert Python print calls to logging:
|
|
37
|
+
`{"ops":[{"pat":"print($$$ARGS)","out":"logger.info($$$ARGS)"}],"lang":"python","path":"src/"}`
|
|
24
38
|
</examples>
|
|
25
39
|
|
|
26
40
|
<critical>
|
|
27
41
|
- `ops` **MUST** contain at least one concrete `{ pat, out }` entry
|
|
28
42
|
- If the path pattern spans multiple languages, set `lang` explicitly for deterministic rewrites
|
|
43
|
+
- Parse issues mean the rewrite request is malformed or mis-scoped; do not assume a clean no-op until the pattern parses successfully
|
|
29
44
|
- For one-off local text edits, prefer the Edit tool instead of AST edit
|
|
30
45
|
</critical>
|
|
@@ -2,14 +2,22 @@ Performs structural code search using AST matching via native ast-grep.
|
|
|
2
2
|
|
|
3
3
|
<instruction>
|
|
4
4
|
- Use this when syntax shape matters more than raw text (calls, declarations, specific language constructs)
|
|
5
|
-
- Prefer a precise `path` scope to keep results targeted and deterministic (`path` accepts files, directories, or glob patterns)
|
|
6
|
-
- Default to language-scoped search in mixed repositories: pair `path` glob + explicit `lang` to avoid parse-noise from non-source files
|
|
7
|
-
- `
|
|
5
|
+
- Prefer a precise `path` scope to keep results targeted and deterministic (`path` accepts files, directories, or glob patterns; use `glob` for an additional filter relative to `path`)
|
|
6
|
+
- Default to language-scoped search in mixed repositories: pair `path` + `glob` + explicit `lang` to avoid parse-noise from non-source files
|
|
7
|
+
- `pat` is required and must include at least one non-empty AST pattern; `lang` is optional (`lang` is inferred per file extension when omitted)
|
|
8
8
|
- Multiple patterns run in one native pass; results are merged and then `offset`/`limit` are applied to the combined match set
|
|
9
|
-
- Use `
|
|
9
|
+
- Use `sel` only for contextual pattern mode; otherwise provide direct patterns
|
|
10
|
+
- In contextual pattern mode, results are returned for the selected node (`sel`), not the outer wrapper used to make the pattern parse
|
|
10
11
|
- For variadic arguments/fields, use `$$$NAME` (not `$$NAME`)
|
|
12
|
+
- Patterns must parse as a single valid AST node for the target language; if a bare pattern fails, wrap it in valid context or use `sel`
|
|
11
13
|
- Patterns match AST structure, not text — whitespace/formatting differences are ignored
|
|
12
14
|
- When the same metavariable appears multiple times, all occurrences must match identical code
|
|
15
|
+
- For TypeScript declarations and methods, prefer shapes that tolerate annotations you do not care about, e.g. `async function $NAME($$$ARGS): $_ { $$$BODY }` or `class $_ { method($$$ARGS): $_ { $$$BODY } }` instead of omitting the return type entirely
|
|
16
|
+
- Metavariables must be the sole content of an AST node; partial-text metavariables like `prefix$VAR`, `"hello $NAME"`, or `a $OP b` do NOT work — match the whole node instead
|
|
17
|
+
- `$$$` captures are lazy (non-greedy): they stop when the next element in the pattern can match; place the most specific node after `$$$` to control where capture ends
|
|
18
|
+
- `$_` is a non-capturing wildcard (matches any single node without binding); use it when you need to tolerate a node but don't need its value
|
|
19
|
+
- Search the right declaration form before concluding absence: top-level function, class method, and variable-assigned function are different AST shapes
|
|
20
|
+
- If you only need to prove a symbol exists, prefer a looser contextual search such as `pat: ["executeBash"]` with `sel: "identifier"`
|
|
13
21
|
</instruction>
|
|
14
22
|
|
|
15
23
|
<output>
|
|
@@ -19,16 +27,25 @@ Performs structural code search using AST matching via native ast-grep.
|
|
|
19
27
|
|
|
20
28
|
<examples>
|
|
21
29
|
- Find all console logging calls in one pass (multi-pattern, scoped):
|
|
22
|
-
`{"
|
|
23
|
-
-
|
|
24
|
-
`{"
|
|
30
|
+
`{"pat":["console.log($$$)","console.error($$$)"],"lang":"typescript","path":"src/"}`
|
|
31
|
+
- Find all named imports from a specific package:
|
|
32
|
+
`{"pat":["import { $$$IMPORTS } from \"react\""],"lang":"typescript","path":"src/"}`
|
|
33
|
+
- Match arrow functions assigned to a const (different AST shape than function declarations):
|
|
34
|
+
`{"pat":["const $NAME = ($$$ARGS) => $BODY"],"lang":"typescript","path":"src/utils/"}`
|
|
35
|
+
- Match any method call on an object using wildcard `$_` (ignores method name):
|
|
36
|
+
`{"pat":["logger.$_($$$ARGS)"],"lang":"typescript","path":"src/"}`
|
|
25
37
|
- Contextual pattern with selector — match only the identifier `foo`, not the whole call:
|
|
26
|
-
`{"
|
|
38
|
+
`{"pat":["foo()"],"sel":"identifier","lang":"typescript","path":"src/utils.ts"}`
|
|
39
|
+
- Match a TypeScript function declaration without caring about its exact return type:
|
|
40
|
+
`{"pat":["async function processItems($$$ARGS): $_ { $$$BODY }"],"sel":"function_declaration","lang":"typescript","path":"src/worker.ts"}`
|
|
41
|
+
- Loosest existence check for a symbol in one file:
|
|
42
|
+
`{"pat":["processItems"],"sel":"identifier","lang":"typescript","path":"src/worker.ts"}`
|
|
27
43
|
</examples>
|
|
28
44
|
|
|
29
45
|
<critical>
|
|
30
|
-
- `
|
|
46
|
+
- `pat` is required
|
|
31
47
|
- Set `lang` explicitly to constrain matching when path pattern spans mixed-language trees
|
|
32
48
|
- Avoid repo-root AST scans when the target is language-specific; narrow `path` first
|
|
49
|
+
- Treat parse issues as query failure, not evidence of absence: repair the pattern or tighten `path`/`glob`/`lang` before concluding "no matches"
|
|
33
50
|
- If exploration is broad/open-ended across subsystems, use Task tool with explore subagent first
|
|
34
51
|
</critical>
|
|
@@ -2,6 +2,8 @@ Executes bash command in shell session for terminal operations like git, bun, ca
|
|
|
2
2
|
|
|
3
3
|
<instruction>
|
|
4
4
|
- You **MUST** use `cwd` parameter to set working directory instead of `cd dir && …`
|
|
5
|
+
- Prefer `env: { NAME: "…" }` for multiline, quote-heavy, or untrusted values instead of inlining them into shell syntax; reference them from the command as `$NAME`
|
|
6
|
+
- Quote variable expansions like `"$NAME"` to preserve exact content and avoid shell parsing bugs
|
|
5
7
|
- PTY mode is opt-in: set `pty: true` only when command expects a real terminal (for example `sudo`, `ssh` where you need input from the user); default is `false`
|
|
6
8
|
- You **MUST** use `;` only when later commands should run regardless of earlier failures
|
|
7
9
|
- `skill://` URIs are auto-resolved to filesystem paths before execution
|
|
@@ -89,6 +89,7 @@ import { getCurrentThemeName, theme } from "../modes/theme/theme";
|
|
|
89
89
|
import { normalizeDiff, normalizeToLF, ParseError, previewPatch, stripBom } from "../patch";
|
|
90
90
|
import type { PlanModeState } from "../plan-mode/state";
|
|
91
91
|
import autoHandoffThresholdFocusPrompt from "../prompts/system/auto-handoff-threshold-focus.md" with { type: "text" };
|
|
92
|
+
import handoffDocumentPrompt from "../prompts/system/handoff-document.md" with { type: "text" };
|
|
92
93
|
import planModeActivePrompt from "../prompts/system/plan-mode-active.md" with { type: "text" };
|
|
93
94
|
import planModeReferencePrompt from "../prompts/system/plan-mode-reference.md" with { type: "text" };
|
|
94
95
|
import planModeToolDecisionReminderPrompt from "../prompts/system/plan-mode-tool-decision-reminder.md" with {
|
|
@@ -260,6 +261,7 @@ export interface HandoffResult {
|
|
|
260
261
|
interface HandoffOptions {
|
|
261
262
|
autoTriggered?: boolean;
|
|
262
263
|
signal?: AbortSignal;
|
|
264
|
+
skipPostPromptRecoveryWait?: boolean;
|
|
263
265
|
}
|
|
264
266
|
|
|
265
267
|
/** Internal marker for hook messages queued through the agent loop */
|
|
@@ -397,7 +399,7 @@ export class AgentSession {
|
|
|
397
399
|
#streamingEditAbortTriggered = false;
|
|
398
400
|
#streamingEditCheckedLineCounts = new Map<string, number>();
|
|
399
401
|
#streamingEditFileCache = new Map<string, string>();
|
|
400
|
-
#
|
|
402
|
+
#promptInFlightCount = 0;
|
|
401
403
|
#obfuscator: SecretObfuscator | undefined;
|
|
402
404
|
#pendingActionStore: PendingActionStore | undefined;
|
|
403
405
|
#checkpointState: CheckpointState | undefined = undefined;
|
|
@@ -1560,7 +1562,7 @@ export class AgentSession {
|
|
|
1560
1562
|
|
|
1561
1563
|
/** Whether agent is currently streaming a response */
|
|
1562
1564
|
get isStreaming(): boolean {
|
|
1563
|
-
return this.agent.state.isStreaming || this.#
|
|
1565
|
+
return this.agent.state.isStreaming || this.#promptInFlightCount > 0;
|
|
1564
1566
|
}
|
|
1565
1567
|
|
|
1566
1568
|
/** Wait until streaming and deferred recovery work are fully settled. */
|
|
@@ -1999,7 +2001,7 @@ export class AgentSession {
|
|
|
1999
2001
|
skipPostPromptRecoveryWait?: boolean;
|
|
2000
2002
|
},
|
|
2001
2003
|
): Promise<void> {
|
|
2002
|
-
this.#
|
|
2004
|
+
this.#promptInFlightCount++;
|
|
2003
2005
|
const generation = this.#promptGeneration;
|
|
2004
2006
|
try {
|
|
2005
2007
|
// Flush any pending bash messages before the new prompt
|
|
@@ -2109,7 +2111,7 @@ export class AgentSession {
|
|
|
2109
2111
|
await this.#waitForPostPromptRecovery();
|
|
2110
2112
|
}
|
|
2111
2113
|
} finally {
|
|
2112
|
-
this.#
|
|
2114
|
+
this.#promptInFlightCount = Math.max(0, this.#promptInFlightCount - 1);
|
|
2113
2115
|
}
|
|
2114
2116
|
}
|
|
2115
2117
|
|
|
@@ -2508,11 +2510,10 @@ export class AgentSession {
|
|
|
2508
2510
|
this.#cancelPostPromptTasks();
|
|
2509
2511
|
this.agent.abort();
|
|
2510
2512
|
await this.agent.waitForIdle();
|
|
2511
|
-
// Clear
|
|
2512
|
-
// block runs
|
|
2513
|
-
//
|
|
2514
|
-
|
|
2515
|
-
this.#promptInFlight = false;
|
|
2513
|
+
// Clear prompt-in-flight state: waitForIdle resolves when the agent loop's finally
|
|
2514
|
+
// block runs, but nested prompt setup/finalizers may still be unwinding. Without this,
|
|
2515
|
+
// a subsequent prompt() can incorrectly observe the session as busy after an abort.
|
|
2516
|
+
this.#promptInFlightCount = 0;
|
|
2516
2517
|
}
|
|
2517
2518
|
|
|
2518
2519
|
/**
|
|
@@ -3194,41 +3195,9 @@ export class AgentSession {
|
|
|
3194
3195
|
}
|
|
3195
3196
|
|
|
3196
3197
|
// Build the handoff prompt
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
## Goal
|
|
3202
|
-
[What the user is trying to accomplish]
|
|
3203
|
-
|
|
3204
|
-
## Constraints & Preferences
|
|
3205
|
-
- [Any constraints, preferences, or requirements mentioned]
|
|
3206
|
-
|
|
3207
|
-
## Progress
|
|
3208
|
-
### Done
|
|
3209
|
-
- [x] [Completed tasks with specifics]
|
|
3210
|
-
|
|
3211
|
-
### In Progress
|
|
3212
|
-
- [ ] [Current work if any]
|
|
3213
|
-
|
|
3214
|
-
### Pending
|
|
3215
|
-
- [ ] [Tasks mentioned but not started]
|
|
3216
|
-
|
|
3217
|
-
## Key Decisions
|
|
3218
|
-
- **[Decision]**: [Rationale]
|
|
3219
|
-
|
|
3220
|
-
## Critical Context
|
|
3221
|
-
- [Code snippets, file paths, error messages, or data essential to continue]
|
|
3222
|
-
- [Repository state if relevant]
|
|
3223
|
-
|
|
3224
|
-
## Next Steps
|
|
3225
|
-
1. [What should happen next]
|
|
3226
|
-
|
|
3227
|
-
Be thorough - include exact file paths, function names, error messages, and technical details. Output ONLY the handoff document, no other text.`;
|
|
3228
|
-
|
|
3229
|
-
if (customInstructions) {
|
|
3230
|
-
handoffPrompt += `\n\nAdditional focus: ${customInstructions}`;
|
|
3231
|
-
}
|
|
3198
|
+
const handoffPrompt = renderPromptTemplate(handoffDocumentPrompt, {
|
|
3199
|
+
additionalFocus: customInstructions,
|
|
3200
|
+
});
|
|
3232
3201
|
|
|
3233
3202
|
// Create a promise that resolves when the agent completes
|
|
3234
3203
|
let handoffText: string | undefined;
|
|
@@ -3273,11 +3242,16 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3273
3242
|
if (handoffSignal.aborted) {
|
|
3274
3243
|
throw new Error("Handoff cancelled");
|
|
3275
3244
|
}
|
|
3276
|
-
await this
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3245
|
+
await this.#promptWithMessage(
|
|
3246
|
+
{
|
|
3247
|
+
role: "developer",
|
|
3248
|
+
content: [{ type: "text", text: handoffPrompt }],
|
|
3249
|
+
attribution: "agent",
|
|
3250
|
+
timestamp: Date.now(),
|
|
3251
|
+
},
|
|
3252
|
+
handoffPrompt,
|
|
3253
|
+
{ skipCompactionCheck: true, skipPostPromptRecoveryWait: options?.skipPostPromptRecoveryWait },
|
|
3254
|
+
);
|
|
3281
3255
|
await completionPromise;
|
|
3282
3256
|
|
|
3283
3257
|
if (handoffCancelled || handoffSignal.aborted) {
|
|
@@ -3730,11 +3704,22 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3730
3704
|
/**
|
|
3731
3705
|
* Internal: Run auto-compaction with events.
|
|
3732
3706
|
*/
|
|
3733
|
-
async #runAutoCompaction(reason: "overflow" | "threshold", willRetry: boolean): Promise<void> {
|
|
3707
|
+
async #runAutoCompaction(reason: "overflow" | "threshold", willRetry: boolean, deferred = false): Promise<void> {
|
|
3734
3708
|
const compactionSettings = this.settings.getGroup("compaction");
|
|
3735
|
-
|
|
3736
3709
|
if (!compactionSettings.enabled || compactionSettings.strategy === "off") return;
|
|
3737
3710
|
const generation = this.#promptGeneration;
|
|
3711
|
+
if (!deferred && reason !== "overflow" && compactionSettings.strategy === "handoff") {
|
|
3712
|
+
this.#schedulePostPromptTask(
|
|
3713
|
+
async signal => {
|
|
3714
|
+
await Promise.resolve();
|
|
3715
|
+
if (signal.aborted) return;
|
|
3716
|
+
await this.#runAutoCompaction(reason, willRetry, true);
|
|
3717
|
+
},
|
|
3718
|
+
{ generation },
|
|
3719
|
+
);
|
|
3720
|
+
return;
|
|
3721
|
+
}
|
|
3722
|
+
|
|
3738
3723
|
let action: "context-full" | "handoff" =
|
|
3739
3724
|
compactionSettings.strategy === "handoff" && reason !== "overflow" ? "handoff" : "context-full";
|
|
3740
3725
|
await this.#emitSessionEvent({ type: "auto_compaction_start", reason, action });
|
|
@@ -3750,6 +3735,7 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3750
3735
|
const handoffResult = await this.handoff(handoffFocus, {
|
|
3751
3736
|
autoTriggered: true,
|
|
3752
3737
|
signal: this.#autoCompactionAbortController.signal,
|
|
3738
|
+
skipPostPromptRecoveryWait: true,
|
|
3753
3739
|
});
|
|
3754
3740
|
if (!handoffResult) {
|
|
3755
3741
|
const aborted = this.#autoCompactionAbortController.signal.aborted;
|
|
@@ -4028,15 +4014,25 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
4028
4014
|
await this.#emitSessionEvent({ type: "auto_compaction_end", action, result, aborted: false, willRetry });
|
|
4029
4015
|
|
|
4030
4016
|
if (!willRetry && compactionSettings.autoContinue !== false) {
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4017
|
+
const continuePrompt = async () => {
|
|
4018
|
+
await this.#promptWithMessage(
|
|
4019
|
+
{
|
|
4020
|
+
role: "developer",
|
|
4021
|
+
content: [{ type: "text", text: "Continue if you have next steps." }],
|
|
4022
|
+
attribution: "agent",
|
|
4023
|
+
timestamp: Date.now(),
|
|
4024
|
+
},
|
|
4025
|
+
"Continue if you have next steps.",
|
|
4026
|
+
{ skipPostPromptRecoveryWait: true },
|
|
4027
|
+
);
|
|
4028
|
+
};
|
|
4029
|
+
this.#schedulePostPromptTask(
|
|
4030
|
+
async signal => {
|
|
4031
|
+
await Promise.resolve();
|
|
4032
|
+
if (signal.aborted) return;
|
|
4033
|
+
await continuePrompt();
|
|
4037
4034
|
},
|
|
4038
|
-
|
|
4039
|
-
{ skipPostPromptRecoveryWait: true },
|
|
4035
|
+
{ generation },
|
|
4040
4036
|
);
|
|
4041
4037
|
}
|
|
4042
4038
|
|
package/src/tools/ast-edit.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { Ellipsis, Hasher, type RenderCache, renderStatusLine, renderTreeList, t
|
|
|
14
14
|
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
15
15
|
import type { ToolSession } from ".";
|
|
16
16
|
import type { OutputMeta } from "./output-meta";
|
|
17
|
-
import { hasGlobPathChars, parseSearchPath, resolveToCwd } from "./path-utils";
|
|
17
|
+
import { combineSearchGlobs, hasGlobPathChars, parseSearchPath, resolveToCwd } from "./path-utils";
|
|
18
18
|
import {
|
|
19
19
|
dedupeParseErrors,
|
|
20
20
|
formatCount,
|
|
@@ -38,7 +38,8 @@ const astEditSchema = Type.Object({
|
|
|
38
38
|
}),
|
|
39
39
|
lang: Type.Optional(Type.String({ description: "Language override" })),
|
|
40
40
|
path: Type.Optional(Type.String({ description: "File, directory, or glob pattern to rewrite (default: cwd)" })),
|
|
41
|
-
|
|
41
|
+
glob: Type.Optional(Type.String({ description: "Optional glob filter relative to path" })),
|
|
42
|
+
sel: Type.Optional(Type.String({ description: "Optional selector for contextual pattern mode" })),
|
|
42
43
|
limit: Type.Optional(Type.Number({ description: "Max total replacements" })),
|
|
43
44
|
});
|
|
44
45
|
|
|
@@ -98,7 +99,7 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
|
|
|
98
99
|
const maxFiles = parseInt(process.env.PI_MAX_AST_FILES ?? "", 10) || 1000;
|
|
99
100
|
|
|
100
101
|
let searchPath: string | undefined;
|
|
101
|
-
let globFilter
|
|
102
|
+
let globFilter = params.glob?.trim() || undefined;
|
|
102
103
|
const rawPath = params.path?.trim();
|
|
103
104
|
if (rawPath) {
|
|
104
105
|
const internalRouter = this.session.internalRouter;
|
|
@@ -114,7 +115,7 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
|
|
|
114
115
|
} else {
|
|
115
116
|
const parsedPath = parseSearchPath(rawPath);
|
|
116
117
|
searchPath = resolveToCwd(parsedPath.basePath, this.session.cwd);
|
|
117
|
-
globFilter = parsedPath.glob;
|
|
118
|
+
globFilter = combineSearchGlobs(parsedPath.glob, globFilter);
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
121
|
|
|
@@ -133,7 +134,7 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
|
|
|
133
134
|
lang: params.lang?.trim(),
|
|
134
135
|
path: resolvedSearchPath,
|
|
135
136
|
glob: globFilter,
|
|
136
|
-
selector: params.
|
|
137
|
+
selector: params.sel?.trim(),
|
|
137
138
|
dryRun: true,
|
|
138
139
|
maxReplacements,
|
|
139
140
|
maxFiles,
|
|
@@ -277,7 +278,7 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
|
|
|
277
278
|
lang: params.lang?.trim(),
|
|
278
279
|
path: resolvedSearchPath,
|
|
279
280
|
glob: globFilter,
|
|
280
|
-
selector: params.
|
|
281
|
+
selector: params.sel?.trim(),
|
|
281
282
|
dryRun: false,
|
|
282
283
|
maxReplacements,
|
|
283
284
|
maxFiles,
|
|
@@ -320,7 +321,7 @@ interface AstEditRenderArgs {
|
|
|
320
321
|
ops?: Array<{ pat?: string; out?: string }>;
|
|
321
322
|
lang?: string;
|
|
322
323
|
path?: string;
|
|
323
|
-
|
|
324
|
+
sel?: string;
|
|
324
325
|
limit?: number;
|
|
325
326
|
}
|
|
326
327
|
|