@oh-my-pi/pi-coding-agent 12.13.0 → 12.14.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 +62 -0
- package/package.json +8 -7
- package/scripts/generate-docs-index.ts +56 -0
- package/src/config/prompt-templates.ts +2 -2
- package/src/config/settings-schema.ts +10 -1
- package/src/discovery/builtin.ts +14 -4
- package/src/extensibility/extensions/types.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +101 -0
- package/src/internal-urls/docs-protocol.ts +84 -0
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/router.ts +1 -1
- package/src/modes/controllers/event-controller.ts +20 -0
- package/src/patch/diff.ts +1 -1
- package/src/patch/hashline.ts +197 -328
- package/src/patch/index.ts +325 -102
- package/src/patch/shared.ts +23 -40
- package/src/prompts/system/system-prompt.md +13 -2
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/grep.md +1 -1
- package/src/prompts/tools/hashline.md +192 -90
- package/src/prompts/tools/read.md +3 -1
- package/src/sdk.ts +17 -0
- package/src/session/agent-session.ts +1 -0
- package/src/tools/fetch.ts +4 -3
- package/src/tools/grep.ts +13 -3
- package/src/tools/read.ts +2 -2
- package/src/web/search/render.ts +2 -2
|
@@ -8,7 +8,7 @@ Executes bash command in shell session for terminal operations like git, bun, ca
|
|
|
8
8
|
- `skill://` URIs are auto-resolved to filesystem paths before execution
|
|
9
9
|
- `python skill://my-skill/scripts/init.py` runs the script from the skill directory
|
|
10
10
|
- `skill://<name>/<relative-path>` resolves within the skill's base directory
|
|
11
|
-
- `agent://`, `artifact://`, `plan://`, `memory://`, and `
|
|
11
|
+
- `agent://`, `artifact://`, `plan://`, `memory://`, `rule://`, and `docs://` URIs are also auto-resolved to filesystem paths before execution
|
|
12
12
|
</instruction>
|
|
13
13
|
|
|
14
14
|
<output>
|
|
@@ -13,7 +13,7 @@ Powerful search tool built on ripgrep.
|
|
|
13
13
|
<output>
|
|
14
14
|
- Results are always content mode.
|
|
15
15
|
{{#if IS_HASHLINE_MODE}}
|
|
16
|
-
- Text output is CID prefixed: `LINE#ID
|
|
16
|
+
- Text output is CID prefixed: `LINE#ID:content`
|
|
17
17
|
{{else}}
|
|
18
18
|
{{#if IS_LINE_NUMBER_MODE}}
|
|
19
19
|
- Text output is line-number-prefixed
|
|
@@ -1,31 +1,34 @@
|
|
|
1
|
-
# Edit
|
|
1
|
+
# Edit
|
|
2
2
|
|
|
3
|
-
Apply precise file edits using `LINE#ID`
|
|
4
|
-
**CRITICAL:** anchors are `LINE#ID` only. Copy verbatim from the prefix (example: `{{hlineref 42 "const x = 1"}}`). Never include `|content`.
|
|
3
|
+
Apply precise file edits using `LINE#ID` tags, anchoring to the file content.
|
|
5
4
|
|
|
6
5
|
<workflow>
|
|
7
|
-
1. `read` the target range to capture current `LINE#ID`
|
|
8
|
-
2. Pick the smallest operation per change site (
|
|
9
|
-
3. Direction-lock every edit: exact current text
|
|
6
|
+
1. `read` the target range to capture current `LINE#ID` tags.
|
|
7
|
+
2. Pick the smallest operation per change site (line/range/insert/content-replace).
|
|
8
|
+
3. Direction-lock every edit: exact current text → intended text.
|
|
10
9
|
4. Submit one `edit` call per file containing all operations.
|
|
11
10
|
5. If another edit is needed in that file, re-read first (hashes changed).
|
|
12
11
|
6. Output tool calls only; no prose.
|
|
13
12
|
</workflow>
|
|
14
13
|
|
|
15
14
|
<operations>
|
|
16
|
-
-
|
|
17
|
-
- `{
|
|
18
|
-
- `
|
|
19
|
-
-
|
|
20
|
-
- `{
|
|
21
|
-
- Use for swaps, block rewrites, or deleting a full span (`
|
|
22
|
-
-
|
|
23
|
-
- `{
|
|
24
|
-
- `{
|
|
25
|
-
- `{
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
- `{
|
|
15
|
+
- **Single line replace/delete**
|
|
16
|
+
- `{ op: "set", tag: "N#ID", content: […] }`
|
|
17
|
+
- `content: null` deletes the line; `content: [""]` keeps a blank line.
|
|
18
|
+
- **Range replace/delete**
|
|
19
|
+
- `{ op: "replace", first: "N#ID", last: "N#ID", content: […] }`
|
|
20
|
+
- Use for swaps, block rewrites, or deleting a full span (`content: null`).
|
|
21
|
+
- **Insert** (new content)
|
|
22
|
+
- `{ op: "prepend", before: "N#ID", content: […] }` or `{ op: "prepend", content: […] }` (no `before` = insert at beginning of file)
|
|
23
|
+
- `{ op: "append", after: "N#ID", content: […] }` or `{ op: "append", content: […] }` (no `after` = insert at end of file)
|
|
24
|
+
- `{ op: "insert", after: "N#ID", before: "N#ID", content: […] }` (between adjacent anchors; safest for blocks)
|
|
25
|
+
{{#if allowReplaceText}}
|
|
26
|
+
- **Content replace**
|
|
27
|
+
- `{ op: "replaceText", old_text: "…", new_text: "…", all?: boolean }`
|
|
28
|
+
{{/if}}
|
|
29
|
+
- **File-level controls**
|
|
30
|
+
- `{ delete: true, edits: [] }` deletes the file (cannot be combined with `rename`).
|
|
31
|
+
- `{ rename: "new/path.ts", edits: […] }` writes result to new path and removes old path.
|
|
29
32
|
**Atomicity:** all ops validate against the same pre-edit file snapshot; refs are interpreted against last `read`; applicator applies bottom-up.
|
|
30
33
|
</operations>
|
|
31
34
|
|
|
@@ -33,98 +36,197 @@ Apply precise file edits using `LINE#ID` anchors from `read` output.
|
|
|
33
36
|
1. **Minimize scope:** one logical mutation site per operation.
|
|
34
37
|
2. **Preserve formatting:** keep indentation, punctuation, line breaks, trailing commas, brace style.
|
|
35
38
|
3. **Prefer insertion over neighbor rewrites:** anchor on structural boundaries (`}`, `]`, `},`) not interior property lines.
|
|
36
|
-
4. **No no-ops:** replacement
|
|
39
|
+
4. **No no-ops:** replacement content must differ from current content.
|
|
37
40
|
5. **Touch only requested code:** avoid incidental edits.
|
|
38
|
-
6. **Use exact current tokens:** never
|
|
39
|
-
7. **For swaps/moves:** prefer one
|
|
41
|
+
6. **Use exact current tokens:** never rewrite approximately; mutate the token that exists now.
|
|
42
|
+
7. **For swaps/moves:** prefer one range operation over multiple single-line operations.
|
|
40
43
|
</rules>
|
|
41
44
|
|
|
42
|
-
<
|
|
43
|
-
- One wrong line
|
|
44
|
-
- Adjacent block changed
|
|
45
|
-
- Missing line/block
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
- For inserts, prefer `after+before` dual anchors when both boundaries are known.
|
|
45
|
+
<op_choice>
|
|
46
|
+
- One wrong line → `set`
|
|
47
|
+
- Adjacent block changed → `insert`
|
|
48
|
+
- Missing line/block → insert with `append`/`prepend`
|
|
49
|
+
</op_choice>
|
|
50
|
+
|
|
51
|
+
<tag_choice>
|
|
52
|
+
- Copy tags exactly from the prefix of the `read` or error output.
|
|
53
|
+
- Never guess tags.
|
|
54
|
+
- For inserts, prefer `insert` > `append`/`prepend` when both boundaries are known.
|
|
53
55
|
- Re-read after each successful edit call before issuing another on same file.
|
|
54
|
-
</
|
|
56
|
+
</tag_choice>
|
|
55
57
|
|
|
56
58
|
<recovery>
|
|
57
|
-
**
|
|
58
|
-
- Retry with the updated
|
|
59
|
-
- Re-read only if required
|
|
59
|
+
**Tag mismatch (`>>>`)**
|
|
60
|
+
- Retry with the updated tags shown in error output.
|
|
61
|
+
- Re-read only if required tags are missing from error snippet.
|
|
60
62
|
- If mismatch repeats, stop and re-read the exact block.
|
|
61
|
-
**No-op / identical content**
|
|
62
|
-
- Re-read immediately; target is stale or replacement equals current text.
|
|
63
|
-
- After two no-ops on same area, re-read the full function/block before retry.
|
|
64
63
|
</recovery>
|
|
65
64
|
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
<example name="fix a value or type">
|
|
66
|
+
```ts
|
|
67
|
+
{{hlinefull 23 " const timeout: number = 5000;"}}
|
|
68
|
+
```
|
|
69
|
+
```
|
|
70
|
+
op: "set"
|
|
71
|
+
tag: "{{hlineref 23 " const timeout: number = 5000;"}}"
|
|
72
|
+
content: [" const timeout: number = 30_000;"]
|
|
73
|
+
```
|
|
74
|
+
</example>
|
|
75
|
+
|
|
76
|
+
<example name="remove a line entirely">
|
|
77
|
+
```ts
|
|
78
|
+
{{hlinefull 7 "// @ts-ignore"}}
|
|
79
|
+
{{hlinefull 8 "const data = fetchSync(url);"}}
|
|
80
|
+
```
|
|
81
|
+
```
|
|
82
|
+
op: "set"
|
|
83
|
+
tag: "{{hlineref 7 "// @ts-ignore"}}"
|
|
84
|
+
content: null
|
|
85
|
+
```
|
|
86
|
+
</example>
|
|
87
|
+
|
|
88
|
+
<example name="clear content but keep the line break">
|
|
89
|
+
```ts
|
|
90
|
+
{{hlinefull 14 " placeholder: \"DO NOT SHIP\","}}
|
|
91
|
+
```
|
|
92
|
+
```
|
|
93
|
+
op: "set"
|
|
94
|
+
tag: "{{hlineref 14 " placeholder: \"DO NOT SHIP\","}}"
|
|
95
|
+
content: [""]
|
|
96
|
+
```
|
|
97
|
+
</example>
|
|
98
|
+
|
|
99
|
+
<example name="rewrite a block of logic">
|
|
100
|
+
```ts
|
|
101
|
+
{{hlinefull 60 " } catch (err) {"}}
|
|
102
|
+
{{hlinefull 61 " console.error(err);"}}
|
|
103
|
+
{{hlinefull 62 " return null;"}}
|
|
104
|
+
{{hlinefull 63 " }"}}
|
|
105
|
+
```
|
|
106
|
+
```
|
|
107
|
+
op: "replace"
|
|
108
|
+
first: "{{hlineref 60 " } catch (err) {"}}"
|
|
109
|
+
last: "{{hlineref 63 " }"}}"
|
|
110
|
+
content: [" } catch (err) {", " if (isEnoent(err)) return null;", " throw err;", " }"]
|
|
111
|
+
```
|
|
72
112
|
</example>
|
|
73
113
|
|
|
74
|
-
<example name="
|
|
75
|
-
|
|
76
|
-
{{hlinefull
|
|
77
|
-
{{hlinefull
|
|
78
|
-
|
|
79
|
-
|
|
114
|
+
<example name="remove a full block">
|
|
115
|
+
```ts
|
|
116
|
+
{{hlinefull 80 " // TODO: remove after migration"}}
|
|
117
|
+
{{hlinefull 81 " if (legacy) {"}}
|
|
118
|
+
{{hlinefull 82 " legacyHandler(req);"}}
|
|
119
|
+
{{hlinefull 83 " }"}}
|
|
120
|
+
```
|
|
121
|
+
```
|
|
122
|
+
op: "replace"
|
|
123
|
+
first: "{{hlineref 80 " // TODO: remove after migration"}}"
|
|
124
|
+
last: "{{hlineref 83 " }"}}"
|
|
125
|
+
content: null
|
|
126
|
+
```
|
|
80
127
|
</example>
|
|
81
128
|
|
|
82
|
-
<example name="
|
|
83
|
-
|
|
84
|
-
{{hlinefull
|
|
85
|
-
{{hlinefull
|
|
86
|
-
|
|
87
|
-
|
|
129
|
+
<example name="add an import above the first import">
|
|
130
|
+
```ts
|
|
131
|
+
{{hlinefull 1 "import * as fs from \"node:fs/promises\";"}}
|
|
132
|
+
{{hlinefull 2 "import * as path from \"node:path\";"}}
|
|
133
|
+
```
|
|
134
|
+
```
|
|
135
|
+
op: "prepend"
|
|
136
|
+
before: "{{hlineref 1 "import * as fs from \"node:fs/promises\";"}}"
|
|
137
|
+
content: ["import * as os from \"node:os\";"]
|
|
138
|
+
```
|
|
139
|
+
Use `before` for anchored insertion before a specific line. Omit `before` to prepend at BOF.
|
|
88
140
|
</example>
|
|
89
141
|
|
|
90
|
-
<example name="
|
|
91
|
-
|
|
92
|
-
{{hlinefull
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
142
|
+
<example name="append at end of file">
|
|
143
|
+
```ts
|
|
144
|
+
{{hlinefull 260 "export { serialize, deserialize };"}}
|
|
145
|
+
```
|
|
146
|
+
```
|
|
147
|
+
op: "append"
|
|
148
|
+
after: "{{hlineref 260 "export { serialize, deserialize };"}}"
|
|
149
|
+
content: ["export { validate };"]
|
|
150
|
+
```
|
|
151
|
+
Use `after` for anchored insertion after a specific line. Omit `after` to append at EOF.
|
|
96
152
|
</example>
|
|
97
153
|
|
|
98
|
-
<example name="
|
|
99
|
-
|
|
100
|
-
{{hlinefull
|
|
101
|
-
{{hlinefull
|
|
102
|
-
|
|
103
|
-
|
|
154
|
+
<example name="add an entry between known siblings">
|
|
155
|
+
```ts
|
|
156
|
+
{{hlinefull 44 " \"build\": \"bun run compile\","}}
|
|
157
|
+
{{hlinefull 45 " \"test\": \"bun test\""}}
|
|
158
|
+
```
|
|
159
|
+
```
|
|
160
|
+
op: "insert"
|
|
161
|
+
after: "{{hlineref 44 " \"build\": \"bun run compile\","}}"
|
|
162
|
+
before: "{{hlineref 45 " \"test\": \"bun test\""}}"
|
|
163
|
+
content: [" \"lint\": \"biome check\","]
|
|
164
|
+
```
|
|
165
|
+
Dual anchors pin the insert to exactly one gap, preventing drift from edits elsewhere in the file. **Always prefer dual anchors when both boundaries are content lines.**
|
|
104
166
|
</example>
|
|
105
167
|
|
|
106
|
-
<example name="
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
168
|
+
<example name="insert a function before another function">
|
|
169
|
+
```ts
|
|
170
|
+
{{hlinefull 100 " return buf.toString(\"hex\");"}}
|
|
171
|
+
{{hlinefull 101 "}"}}
|
|
172
|
+
{{hlinefull 102 ""}}
|
|
173
|
+
{{hlinefull 103 "export function serialize(data: unknown): string {"}}
|
|
174
|
+
```
|
|
175
|
+
```
|
|
176
|
+
op: "insert"
|
|
177
|
+
before: "{{hlineref 103 "export function serialize(data: unknown): string {"}}"
|
|
178
|
+
content: ["function validate(data: unknown): boolean {", " return data != null && typeof data === \"object\";", "}", ""]
|
|
179
|
+
```
|
|
180
|
+
The trailing `""` in `content` preserves the blank-line separator. **Anchor to the structural line (`export function ...`), not the blank line above it** — blank lines are ambiguous and may be added or removed by other edits.
|
|
111
181
|
</example>
|
|
112
182
|
|
|
113
|
-
|
|
114
|
-
|
|
183
|
+
{{#if allowReplaceText}}
|
|
184
|
+
<example name="content replace (rare)">
|
|
185
|
+
```
|
|
186
|
+
op: "replaceText"
|
|
187
|
+
old_text: "x = 42"
|
|
188
|
+
new_text: "x = 99"
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Use only when line anchors aren't available. `old_text` must match exactly one location in the file (or set `"all": true` for all occurrences).
|
|
115
192
|
</example>
|
|
193
|
+
{{/if}}
|
|
116
194
|
|
|
117
|
-
<example name="
|
|
118
|
-
|
|
195
|
+
<example name="file delete">
|
|
196
|
+
```
|
|
197
|
+
path: "src/deprecated/legacy.ts"
|
|
198
|
+
delete: true
|
|
199
|
+
```
|
|
119
200
|
</example>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
201
|
+
|
|
202
|
+
<example name="file rename with edits">
|
|
203
|
+
```
|
|
204
|
+
path: "src/utils.ts"
|
|
205
|
+
rename: "src/helpers/utils.ts"
|
|
206
|
+
edits: […]
|
|
207
|
+
```
|
|
208
|
+
</example>
|
|
209
|
+
|
|
210
|
+
<example name="anti-pattern: anchoring to whitespace">
|
|
211
|
+
Bad — tags to a blank line; fragile if blank lines shift:
|
|
212
|
+
```
|
|
213
|
+
after: "{{hlineref 102 ""}}"
|
|
214
|
+
content: ["function validate() {", …, "}"]
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Good — anchors to the structural target:
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
before: "{{hlineref 103 "export function serialize(data: unknown): string {"}}"
|
|
221
|
+
content: ["function validate() {", …, "}"]
|
|
222
|
+
```
|
|
223
|
+
</example>
|
|
224
|
+
|
|
225
|
+
<critical>
|
|
226
|
+
Ensure:
|
|
227
|
+
- Payload shape is `{ "path": string, "edits": [operation, …], "delete"?: boolean, "rename"?: string }`
|
|
228
|
+
- Every edit matches exactly one variant
|
|
229
|
+
- Every tag has been copied EXACTLY from a tool result as `N#ID`
|
|
230
|
+
- Scope is minimal and formatting is preserved except targeted token changes
|
|
231
|
+
</critical>
|
|
232
|
+
**Final reminder:** tags are immutable references to the last read snapshot. Re-read when state changes, then edit.
|
|
@@ -6,7 +6,7 @@ Reads files from local filesystem or internal URLs.
|
|
|
6
6
|
- Reads up to {{DEFAULT_MAX_LINES}} lines default
|
|
7
7
|
- Use `offset` and `limit` for large files
|
|
8
8
|
{{#if IS_HASHLINE_MODE}}
|
|
9
|
-
- Text output is CID prefixed: `LINE#ID
|
|
9
|
+
- Text output is CID prefixed: `LINE#ID:content`
|
|
10
10
|
{{else}}
|
|
11
11
|
{{#if IS_LINE_NUMBER_MODE}}
|
|
12
12
|
- Text output is line-number-prefixed
|
|
@@ -23,6 +23,8 @@ Reads files from local filesystem or internal URLs.
|
|
|
23
23
|
- `memory://root/<path>` - read relative path within project memory root
|
|
24
24
|
- `agent://<id>` - read agent output artifact
|
|
25
25
|
- `agent://<id>/<path>` or `agent://<id>?q=<query>` - extract JSON from agent output
|
|
26
|
+
- `docs://` - list available pi documentation files
|
|
27
|
+
- `docs://<file>.md` - read a specific pi documentation file
|
|
26
28
|
</instruction>
|
|
27
29
|
|
|
28
30
|
<output>
|
package/src/sdk.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
type CustomCommandsLoadResult,
|
|
20
20
|
loadCustomCommands as loadCustomCommandsInternal,
|
|
21
21
|
} from "./extensibility/custom-commands";
|
|
22
|
+
import { discoverAndLoadCustomTools } from "./extensibility/custom-tools";
|
|
22
23
|
import type { CustomTool, CustomToolContext, CustomToolSessionEvent } from "./extensibility/custom-tools/types";
|
|
23
24
|
import { CustomToolAdapter } from "./extensibility/custom-tools/wrapper";
|
|
24
25
|
import {
|
|
@@ -39,6 +40,7 @@ import { type FileSlashCommand, loadSlashCommands as loadSlashCommandsInternal }
|
|
|
39
40
|
import {
|
|
40
41
|
AgentProtocolHandler,
|
|
41
42
|
ArtifactProtocolHandler,
|
|
43
|
+
DocsProtocolHandler,
|
|
42
44
|
InternalUrlRouter,
|
|
43
45
|
MemoryProtocolHandler,
|
|
44
46
|
PlanProtocolHandler,
|
|
@@ -770,6 +772,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
770
772
|
getRules: () => rulebookRules,
|
|
771
773
|
}),
|
|
772
774
|
);
|
|
775
|
+
internalRouter.register(new DocsProtocolHandler());
|
|
773
776
|
toolSession.internalRouter = internalRouter;
|
|
774
777
|
toolSession.getArtifactsDir = getArtifactsDir;
|
|
775
778
|
toolSession.agentOutputManager = new AgentOutputManager(
|
|
@@ -848,6 +851,19 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
848
851
|
time("getSearchTools");
|
|
849
852
|
}
|
|
850
853
|
|
|
854
|
+
debugStartup("sdk:discoverCustomTools:start");
|
|
855
|
+
// Discover and load custom tools from .omp/tools/, .claude/tools/, etc.
|
|
856
|
+
const builtInToolNames = builtinTools.map(t => t.name);
|
|
857
|
+
const discoveredCustomTools = await discoverAndLoadCustomTools([], cwd, builtInToolNames);
|
|
858
|
+
for (const { path, error } of discoveredCustomTools.errors) {
|
|
859
|
+
logger.error("Custom tool load failed", { path, error });
|
|
860
|
+
}
|
|
861
|
+
if (discoveredCustomTools.tools.length > 0) {
|
|
862
|
+
customTools.push(...discoveredCustomTools.tools.map(loaded => loaded.tool));
|
|
863
|
+
}
|
|
864
|
+
time("discoverAndLoadCustomTools");
|
|
865
|
+
debugStartup("sdk:discoverCustomTools:done");
|
|
866
|
+
|
|
851
867
|
const inlineExtensions: ExtensionFactory[] = options.extensions ? [...options.extensions] : [];
|
|
852
868
|
if (customTools.length > 0) {
|
|
853
869
|
inlineExtensions.push(createCustomToolsExtension(customTools));
|
|
@@ -1207,6 +1223,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1207
1223
|
},
|
|
1208
1224
|
cursorExecHandlers,
|
|
1209
1225
|
transformToolCallArguments: obfuscator?.hasSecrets() ? args => obfuscator!.deobfuscateObject(args) : undefined,
|
|
1226
|
+
intentTracing: settings.get("tools.intentTracing") || $env.PI_INTENT_TRACING === "1",
|
|
1210
1227
|
});
|
|
1211
1228
|
cursorEventEmitter = event => agent.emitExternalEvent(event);
|
|
1212
1229
|
debugStartup("sdk:createAgent");
|
|
@@ -1140,6 +1140,7 @@ export class AgentSession {
|
|
|
1140
1140
|
toolCallId: event.toolCallId,
|
|
1141
1141
|
toolName: event.toolName,
|
|
1142
1142
|
args: event.args,
|
|
1143
|
+
intent: event.intent,
|
|
1143
1144
|
};
|
|
1144
1145
|
await this.#extensionRunner.emit(extensionEvent);
|
|
1145
1146
|
} else if (event.type === "tool_execution_update") {
|
package/src/tools/fetch.ts
CHANGED
|
@@ -966,11 +966,12 @@ function countNonEmptyLines(text: string): number {
|
|
|
966
966
|
|
|
967
967
|
/** Render fetch call (URL preview) */
|
|
968
968
|
export function renderFetchCall(
|
|
969
|
-
args: { url
|
|
969
|
+
args: { url?: string; timeout?: number; raw?: boolean },
|
|
970
970
|
uiTheme: Theme = theme,
|
|
971
971
|
): Component {
|
|
972
|
-
const
|
|
973
|
-
const
|
|
972
|
+
const url = args.url ?? "";
|
|
973
|
+
const domain = getDomain(url);
|
|
974
|
+
const path = truncate(url.replace(/^https?:\/\/[^/]+/, ""), 50, "\u2026");
|
|
974
975
|
const description = `${domain}${path ? ` ${path}` : ""}`.trim();
|
|
975
976
|
const meta: string[] = [];
|
|
976
977
|
if (args.raw) meta.push("raw");
|
package/src/tools/grep.ts
CHANGED
|
@@ -104,7 +104,17 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
|
|
|
104
104
|
const effectiveMultiline = multiline ?? patternHasNewline;
|
|
105
105
|
|
|
106
106
|
const useHashLines = resolveFileDisplayMode(this.session).hashLines;
|
|
107
|
-
|
|
107
|
+
let searchPath: string;
|
|
108
|
+
const internalRouter = this.session.internalRouter;
|
|
109
|
+
if (searchDir && internalRouter?.canHandle(searchDir)) {
|
|
110
|
+
const resource = await internalRouter.resolve(searchDir);
|
|
111
|
+
if (!resource.sourcePath) {
|
|
112
|
+
throw new ToolError(`Cannot grep internal URL without a backing file: ${searchDir}`);
|
|
113
|
+
}
|
|
114
|
+
searchPath = resource.sourcePath;
|
|
115
|
+
} else {
|
|
116
|
+
searchPath = resolveToCwd(searchDir || ".", this.session.cwd);
|
|
117
|
+
}
|
|
108
118
|
const scopePath = (() => {
|
|
109
119
|
const relative = path.relative(this.session.cwd, searchPath).replace(/\\/g, "/");
|
|
110
120
|
return relative.length === 0 ? "." : relative;
|
|
@@ -210,10 +220,10 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
|
|
|
210
220
|
const formatLine = (lineNumber: number, line: string, isMatch: boolean): string => {
|
|
211
221
|
if (useHashLines) {
|
|
212
222
|
const ref = `${lineNumber}#${computeLineHash(lineNumber, line)}`;
|
|
213
|
-
return isMatch ? `>>${ref}
|
|
223
|
+
return isMatch ? `>>${ref}:${line}` : ` ${ref}:${line}`;
|
|
214
224
|
}
|
|
215
225
|
const padded = lineNumber.toString().padStart(lineWidth, " ");
|
|
216
|
-
return isMatch ? `>>${padded}
|
|
226
|
+
return isMatch ? `>>${padded}:${line}` : ` ${padded}:${line}`;
|
|
217
227
|
};
|
|
218
228
|
|
|
219
229
|
// Add context before
|
package/src/tools/read.ts
CHANGED
|
@@ -772,7 +772,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
772
772
|
const prependHashLines = (text: string, startNum: number): string => {
|
|
773
773
|
const textLines = text.split("\n");
|
|
774
774
|
return textLines
|
|
775
|
-
.map((line, i) => `${startNum + i}#${computeLineHash(startNum + i, line)}
|
|
775
|
+
.map((line, i) => `${startNum + i}#${computeLineHash(startNum + i, line)}:${line}`)
|
|
776
776
|
.join("\n");
|
|
777
777
|
};
|
|
778
778
|
const formatText = (text: string, startNum: number): string => {
|
|
@@ -929,7 +929,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
929
929
|
};
|
|
930
930
|
const prependHashLines = (text: string, startNum: number): string => {
|
|
931
931
|
const textLines = text.split("\n");
|
|
932
|
-
return textLines.map((line, i) => `${startNum + i}#${computeLineHash(startNum + i, line)}
|
|
932
|
+
return textLines.map((line, i) => `${startNum + i}#${computeLineHash(startNum + i, line)}:${line}`).join("\n");
|
|
933
933
|
};
|
|
934
934
|
const formatText = (text: string, startNum: number): string => {
|
|
935
935
|
if (shouldAddHashLines) return prependHashLines(text, startNum);
|
package/src/web/search/render.ts
CHANGED
|
@@ -282,11 +282,11 @@ export function renderSearchResult(
|
|
|
282
282
|
|
|
283
283
|
/** Render web search call (query preview) */
|
|
284
284
|
export function renderSearchCall(
|
|
285
|
-
args: { query
|
|
285
|
+
args: { query?: string; provider?: string; [key: string]: unknown },
|
|
286
286
|
theme: Theme,
|
|
287
287
|
): Component {
|
|
288
288
|
const provider = args.provider ?? "auto";
|
|
289
|
-
const query = truncateToWidth(args.query, 80);
|
|
289
|
+
const query = truncateToWidth(args.query ?? "", 80);
|
|
290
290
|
const text = renderStatusLine({ icon: "pending", title: "Web Search", description: query, meta: [provider] }, theme);
|
|
291
291
|
return new Text(text, 0, 0);
|
|
292
292
|
}
|