@oh-my-pi/pi-coding-agent 11.8.2 → 11.9.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 +42 -0
- package/docs/tui.md +9 -9
- package/package.json +7 -7
- package/src/capability/mcp.ts +9 -0
- package/src/cli/file-processor.ts +8 -13
- package/src/cli/oclif-help.ts +1 -1
- package/src/cli.ts +14 -0
- package/src/commit/git/index.ts +16 -16
- package/src/config/file-lock.ts +1 -1
- package/src/config/keybindings.ts +11 -11
- package/src/config/model-registry.ts +31 -66
- package/src/config/settings.ts +88 -95
- package/src/config.ts +2 -2
- package/src/cursor.ts +4 -4
- package/src/debug/index.ts +28 -28
- package/src/discovery/builtin.ts +48 -0
- package/src/discovery/codex.ts +5 -13
- package/src/discovery/cursor.ts +2 -7
- package/src/discovery/mcp-json.ts +33 -0
- package/src/exa/mcp-client.ts +2 -2
- package/src/exa/websets.ts +2 -2
- package/src/export/html/index.ts +3 -3
- package/src/export/ttsr.ts +27 -27
- package/src/extensibility/custom-tools/loader.ts +9 -9
- package/src/extensibility/extensions/runner.ts +64 -64
- package/src/extensibility/hooks/runner.ts +46 -46
- package/src/extensibility/plugins/manager.ts +49 -49
- package/src/extensibility/slash-commands.ts +1 -0
- package/src/index.ts +0 -3
- package/src/internal-urls/router.ts +5 -5
- package/src/ipy/kernel.ts +61 -57
- package/src/lsp/client.ts +1 -1
- package/src/lsp/clients/biome-client.ts +2 -2
- package/src/lsp/clients/lsp-linter-client.ts +7 -7
- package/src/lsp/index.ts +9 -9
- package/src/mcp/config-writer.ts +194 -0
- package/src/mcp/config.ts +20 -6
- package/src/mcp/index.ts +4 -0
- package/src/mcp/loader.ts +6 -0
- package/src/mcp/manager.ts +139 -50
- package/src/mcp/oauth-discovery.ts +274 -0
- package/src/mcp/oauth-flow.ts +229 -0
- package/src/mcp/tool-bridge.ts +20 -20
- package/src/mcp/transports/http.ts +107 -66
- package/src/mcp/transports/stdio.ts +74 -59
- package/src/mcp/types.ts +15 -1
- package/src/modes/components/assistant-message.ts +25 -25
- package/src/modes/components/bash-execution.ts +51 -51
- package/src/modes/components/bordered-loader.ts +7 -7
- package/src/modes/components/branch-summary-message.ts +7 -7
- package/src/modes/components/compaction-summary-message.ts +7 -7
- package/src/modes/components/countdown-timer.ts +15 -15
- package/src/modes/components/custom-editor.ts +22 -22
- package/src/modes/components/custom-message.ts +21 -21
- package/src/modes/components/dynamic-border.ts +3 -3
- package/src/modes/components/extensions/extension-dashboard.ts +72 -72
- package/src/modes/components/extensions/extension-list.ts +99 -97
- package/src/modes/components/extensions/inspector-panel.ts +26 -26
- package/src/modes/components/footer.ts +36 -36
- package/src/modes/components/history-search.ts +52 -52
- package/src/modes/components/hook-editor.ts +20 -20
- package/src/modes/components/hook-input.ts +20 -20
- package/src/modes/components/hook-message.ts +22 -22
- package/src/modes/components/hook-selector.ts +52 -52
- package/src/modes/components/index.ts +0 -1
- package/src/modes/components/login-dialog.ts +57 -57
- package/src/modes/components/mcp-add-wizard.ts +1286 -0
- package/src/modes/components/model-selector.ts +173 -173
- package/src/modes/components/oauth-selector.ts +45 -45
- package/src/modes/components/plugin-settings.ts +52 -52
- package/src/modes/components/python-execution.ts +53 -53
- package/src/modes/components/queue-mode-selector.ts +7 -7
- package/src/modes/components/read-tool-group.ts +23 -23
- package/src/modes/components/session-selector.ts +40 -37
- package/src/modes/components/settings-selector.ts +80 -80
- package/src/modes/components/show-images-selector.ts +7 -7
- package/src/modes/components/skill-message.ts +27 -27
- package/src/modes/components/status-line-segment-editor.ts +81 -81
- package/src/modes/components/status-line.ts +73 -73
- package/src/modes/components/theme-selector.ts +11 -11
- package/src/modes/components/thinking-selector.ts +7 -7
- package/src/modes/components/todo-display.ts +19 -19
- package/src/modes/components/todo-reminder.ts +9 -9
- package/src/modes/components/tool-execution.ts +212 -216
- package/src/modes/components/tree-selector.ts +144 -144
- package/src/modes/components/ttsr-notification.ts +17 -17
- package/src/modes/components/user-message-selector.ts +18 -18
- package/src/modes/components/welcome.ts +10 -10
- package/src/modes/controllers/command-controller.ts +0 -7
- package/src/modes/controllers/event-controller.ts +23 -23
- package/src/modes/controllers/extension-ui-controller.ts +13 -13
- package/src/modes/controllers/input-controller.ts +12 -9
- package/src/modes/controllers/mcp-command-controller.ts +1223 -0
- package/src/modes/interactive-mode.ts +240 -241
- package/src/modes/rpc/rpc-client.ts +77 -77
- package/src/modes/rpc/rpc-mode.ts +5 -5
- package/src/modes/theme/theme.ts +113 -113
- package/src/modes/types.ts +1 -1
- package/src/patch/index.ts +45 -45
- package/src/prompts/tools/task.md +22 -2
- package/src/sdk.ts +1 -0
- package/src/session/agent-session.ts +512 -476
- package/src/session/agent-storage.ts +72 -75
- package/src/session/auth-storage.ts +186 -252
- package/src/session/history-storage.ts +36 -38
- package/src/session/session-manager.ts +300 -299
- package/src/session/session-storage.ts +65 -90
- package/src/ssh/connection-manager.ts +9 -9
- package/src/system-prompt.ts +2 -3
- package/src/task/agents.ts +1 -1
- package/src/task/executor.ts +28 -40
- package/src/task/index.ts +13 -12
- package/src/task/subprocess-tool-registry.ts +5 -5
- package/src/task/worktree.ts +8 -5
- package/src/tools/ask.ts +7 -7
- package/src/tools/bash.ts +15 -10
- package/src/tools/browser.ts +130 -127
- package/src/tools/calculator.ts +46 -46
- package/src/tools/context.ts +9 -9
- package/src/tools/exit-plan-mode.ts +5 -5
- package/src/tools/fetch.ts +5 -5
- package/src/tools/find.ts +16 -16
- package/src/tools/grep.ts +12 -24
- package/src/tools/index.ts +1 -1
- package/src/tools/notebook.ts +6 -6
- package/src/tools/output-meta.ts +10 -2
- package/src/tools/python.ts +12 -11
- package/src/tools/read.ts +17 -17
- package/src/tools/ssh.ts +9 -9
- package/src/tools/submit-result.ts +13 -13
- package/src/tools/todo-write.ts +6 -6
- package/src/tools/write.ts +10 -10
- package/src/tui/output-block.ts +6 -6
- package/src/tui/utils.ts +9 -9
- package/src/utils/event-bus.ts +13 -11
- package/src/utils/frontmatter.ts +1 -1
- package/src/utils/ignore-files.ts +1 -1
- package/src/web/search/index.ts +5 -5
- package/src/web/search/providers/anthropic.ts +7 -2
- package/examples/hooks/snake.ts +0 -342
- package/src/modes/components/armin.ts +0 -379
package/src/tools/calculator.ts
CHANGED
|
@@ -216,14 +216,14 @@ function tokenizeExpression(expression: string): Token[] {
|
|
|
216
216
|
* the call stack.
|
|
217
217
|
*/
|
|
218
218
|
class ExpressionParser {
|
|
219
|
-
|
|
219
|
+
#index = 0;
|
|
220
220
|
|
|
221
221
|
constructor(private readonly tokens: Token[]) {}
|
|
222
222
|
|
|
223
223
|
/** Parse the full expression and ensure all tokens are consumed. */
|
|
224
224
|
parse(): number {
|
|
225
|
-
const value = this
|
|
226
|
-
if (this
|
|
225
|
+
const value = this.#parseExpression();
|
|
226
|
+
if (this.#index < this.tokens.length) {
|
|
227
227
|
throw new Error("Unexpected token in expression");
|
|
228
228
|
}
|
|
229
229
|
return value;
|
|
@@ -233,15 +233,15 @@ class ExpressionParser {
|
|
|
233
233
|
* Parse addition and subtraction (lowest precedence).
|
|
234
234
|
* Left-associative: 1 - 2 - 3 = (1 - 2) - 3
|
|
235
235
|
*/
|
|
236
|
-
|
|
237
|
-
let value = this
|
|
236
|
+
#parseExpression(): number {
|
|
237
|
+
let value = this.#parseTerm();
|
|
238
238
|
while (true) {
|
|
239
|
-
if (this
|
|
240
|
-
value += this
|
|
239
|
+
if (this.#matchOperator("+")) {
|
|
240
|
+
value += this.#parseTerm();
|
|
241
241
|
continue;
|
|
242
242
|
}
|
|
243
|
-
if (this
|
|
244
|
-
value -= this
|
|
243
|
+
if (this.#matchOperator("-")) {
|
|
244
|
+
value -= this.#parseTerm();
|
|
245
245
|
continue;
|
|
246
246
|
}
|
|
247
247
|
break;
|
|
@@ -253,19 +253,19 @@ class ExpressionParser {
|
|
|
253
253
|
* Parse multiplication, division, and modulo.
|
|
254
254
|
* Left-associative: 8 / 4 / 2 = (8 / 4) / 2
|
|
255
255
|
*/
|
|
256
|
-
|
|
257
|
-
let value = this
|
|
256
|
+
#parseTerm(): number {
|
|
257
|
+
let value = this.#parseUnary();
|
|
258
258
|
while (true) {
|
|
259
|
-
if (this
|
|
260
|
-
value *= this
|
|
259
|
+
if (this.#matchOperator("*")) {
|
|
260
|
+
value *= this.#parseUnary();
|
|
261
261
|
continue;
|
|
262
262
|
}
|
|
263
|
-
if (this
|
|
264
|
-
value /= this
|
|
263
|
+
if (this.#matchOperator("/")) {
|
|
264
|
+
value /= this.#parseUnary();
|
|
265
265
|
continue;
|
|
266
266
|
}
|
|
267
|
-
if (this
|
|
268
|
-
value %= this
|
|
267
|
+
if (this.#matchOperator("%")) {
|
|
268
|
+
value %= this.#parseUnary();
|
|
269
269
|
continue;
|
|
270
270
|
}
|
|
271
271
|
break;
|
|
@@ -277,14 +277,14 @@ class ExpressionParser {
|
|
|
277
277
|
* Parse unary + and - operators.
|
|
278
278
|
* Recursive to handle chained unary: --x, +-x
|
|
279
279
|
*/
|
|
280
|
-
|
|
281
|
-
if (this
|
|
282
|
-
return this
|
|
280
|
+
#parseUnary(): number {
|
|
281
|
+
if (this.#matchOperator("+")) {
|
|
282
|
+
return this.#parseUnary();
|
|
283
283
|
}
|
|
284
|
-
if (this
|
|
285
|
-
return -this
|
|
284
|
+
if (this.#matchOperator("-")) {
|
|
285
|
+
return -this.#parseUnary();
|
|
286
286
|
}
|
|
287
|
-
return this
|
|
287
|
+
return this.#parsePower();
|
|
288
288
|
}
|
|
289
289
|
|
|
290
290
|
/**
|
|
@@ -292,10 +292,10 @@ class ExpressionParser {
|
|
|
292
292
|
* Right-associative: 2 ** 3 ** 2 = 2 ** (3 ** 2) = 512
|
|
293
293
|
* Achieved by recursive call to parsePower for the right operand.
|
|
294
294
|
*/
|
|
295
|
-
|
|
296
|
-
let value = this
|
|
297
|
-
if (this
|
|
298
|
-
value = value ** this
|
|
295
|
+
#parsePower(): number {
|
|
296
|
+
let value = this.#parsePrimary();
|
|
297
|
+
if (this.#matchOperator("**")) {
|
|
298
|
+
value = value ** this.#parsePower(); // Right-associative via recursion
|
|
299
299
|
}
|
|
300
300
|
return value;
|
|
301
301
|
}
|
|
@@ -304,21 +304,21 @@ class ExpressionParser {
|
|
|
304
304
|
* Parse primary expressions: number literals and parenthesized subexpressions.
|
|
305
305
|
* Parentheses restart parsing at lowest precedence (parseExpression).
|
|
306
306
|
*/
|
|
307
|
-
|
|
308
|
-
const token = this
|
|
307
|
+
#parsePrimary(): number {
|
|
308
|
+
const token = this.#peek();
|
|
309
309
|
if (!token) {
|
|
310
310
|
throw new Error("Unexpected end of expression");
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
if (token.type === "number") {
|
|
314
|
-
this
|
|
314
|
+
this.#index += 1;
|
|
315
315
|
return token.value;
|
|
316
316
|
}
|
|
317
317
|
|
|
318
318
|
if (token.type === "paren" && token.value === "(") {
|
|
319
|
-
this
|
|
320
|
-
const value = this
|
|
321
|
-
if (!this
|
|
319
|
+
this.#index += 1;
|
|
320
|
+
const value = this.#parseExpression(); // Reset to lowest precedence
|
|
321
|
+
if (!this.#matchParen(")")) {
|
|
322
322
|
throw new Error("Missing closing parenthesis");
|
|
323
323
|
}
|
|
324
324
|
return value;
|
|
@@ -328,28 +328,28 @@ class ExpressionParser {
|
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
/** Consume operator if it matches, advancing the token index. */
|
|
331
|
-
|
|
332
|
-
const token = this.tokens[this
|
|
331
|
+
#matchOperator(value: Operator): boolean {
|
|
332
|
+
const token = this.tokens[this.#index];
|
|
333
333
|
if (token && token.type === "operator" && token.value === value) {
|
|
334
|
-
this
|
|
334
|
+
this.#index += 1;
|
|
335
335
|
return true;
|
|
336
336
|
}
|
|
337
337
|
return false;
|
|
338
338
|
}
|
|
339
339
|
|
|
340
340
|
/** Consume parenthesis if it matches, advancing the token index. */
|
|
341
|
-
|
|
342
|
-
const token = this.tokens[this
|
|
341
|
+
#matchParen(value: "(" | ")"): boolean {
|
|
342
|
+
const token = this.tokens[this.#index];
|
|
343
343
|
if (token && token.type === "paren" && token.value === value) {
|
|
344
|
-
this
|
|
344
|
+
this.#index += 1;
|
|
345
345
|
return true;
|
|
346
346
|
}
|
|
347
347
|
return false;
|
|
348
348
|
}
|
|
349
349
|
|
|
350
350
|
/** Look at current token without consuming it. */
|
|
351
|
-
|
|
352
|
-
return this.tokens[this
|
|
351
|
+
#peek(): Token | undefined {
|
|
352
|
+
return this.tokens[this.#index];
|
|
353
353
|
}
|
|
354
354
|
}
|
|
355
355
|
|
|
@@ -395,16 +395,16 @@ type CalculatorParams = { calculations: Array<{ expression: string; prefix: stri
|
|
|
395
395
|
* standard arithmetic operators, and parentheses.
|
|
396
396
|
*/
|
|
397
397
|
export class CalculatorTool implements AgentTool<typeof calculatorSchema, CalculatorToolDetails> {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
398
|
+
readonly name = "calc";
|
|
399
|
+
readonly label = "Calc";
|
|
400
|
+
readonly description: string;
|
|
401
|
+
readonly parameters = calculatorSchema;
|
|
402
402
|
|
|
403
403
|
constructor(_session: ToolSession) {
|
|
404
404
|
this.description = renderPromptTemplate(calculatorDescription);
|
|
405
405
|
}
|
|
406
406
|
|
|
407
|
-
|
|
407
|
+
async execute(
|
|
408
408
|
_toolCallId: string,
|
|
409
409
|
{ calculations }: CalculatorParams,
|
|
410
410
|
signal?: AbortSignal,
|
package/src/tools/context.ts
CHANGED
|
@@ -12,28 +12,28 @@ declare module "@oh-my-pi/pi-agent-core" {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export class ToolContextStore {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
#uiContext: ExtensionUIContext | undefined;
|
|
16
|
+
#hasUI = false;
|
|
17
|
+
#toolNames: string[] = [];
|
|
18
18
|
|
|
19
19
|
constructor(private readonly getBaseContext: () => CustomToolContext) {}
|
|
20
20
|
|
|
21
21
|
getContext(toolCall?: ToolCallContext): AgentToolContext {
|
|
22
22
|
return {
|
|
23
23
|
...this.getBaseContext(),
|
|
24
|
-
ui: this
|
|
25
|
-
hasUI: this
|
|
26
|
-
toolNames: this
|
|
24
|
+
ui: this.#uiContext,
|
|
25
|
+
hasUI: this.#hasUI,
|
|
26
|
+
toolNames: this.#toolNames,
|
|
27
27
|
toolCall,
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
setUIContext(uiContext: ExtensionUIContext, hasUI: boolean): void {
|
|
32
|
-
this
|
|
33
|
-
this
|
|
32
|
+
this.#uiContext = uiContext;
|
|
33
|
+
this.#hasUI = hasUI;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
setToolNames(names: string[]): void {
|
|
37
|
-
this
|
|
37
|
+
this.#toolNames = names;
|
|
38
38
|
}
|
|
39
39
|
}
|
|
@@ -16,16 +16,16 @@ export interface ExitPlanModeDetails {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export class ExitPlanModeTool implements AgentTool<typeof exitPlanModeSchema, ExitPlanModeDetails> {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
readonly name = "exit_plan_mode";
|
|
20
|
+
readonly label = "ExitPlanMode";
|
|
21
|
+
readonly description: string;
|
|
22
|
+
readonly parameters = exitPlanModeSchema;
|
|
23
23
|
|
|
24
24
|
constructor(private readonly session: ToolSession) {
|
|
25
25
|
this.description = renderPromptTemplate(exitPlanModeDescription);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
async execute(
|
|
29
29
|
_toolCallId: string,
|
|
30
30
|
_params: Record<string, never>,
|
|
31
31
|
_signal?: AbortSignal,
|
package/src/tools/fetch.ts
CHANGED
|
@@ -852,16 +852,16 @@ export interface FetchToolDetails {
|
|
|
852
852
|
}
|
|
853
853
|
|
|
854
854
|
export class FetchTool implements AgentTool<typeof fetchSchema, FetchToolDetails> {
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
855
|
+
readonly name = "fetch";
|
|
856
|
+
readonly label = "Fetch";
|
|
857
|
+
readonly description: string;
|
|
858
|
+
readonly parameters = fetchSchema;
|
|
859
859
|
|
|
860
860
|
constructor(private readonly session: ToolSession) {
|
|
861
861
|
this.description = renderPromptTemplate(fetchDescription);
|
|
862
862
|
}
|
|
863
863
|
|
|
864
|
-
|
|
864
|
+
async execute(
|
|
865
865
|
_toolCallId: string,
|
|
866
866
|
params: Static<typeof fetchSchema>,
|
|
867
867
|
signal?: AbortSignal,
|
package/src/tools/find.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as fs from "node:fs
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
4
4
|
import { FileType, type GlobMatch, glob } from "@oh-my-pi/pi-natives";
|
|
@@ -123,22 +123,22 @@ export interface FindToolOptions {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
126
|
+
readonly name = "find";
|
|
127
|
+
readonly label = "Find";
|
|
128
|
+
readonly description: string;
|
|
129
|
+
readonly parameters = findSchema;
|
|
130
130
|
|
|
131
|
-
|
|
131
|
+
readonly #customOps?: FindOperations;
|
|
132
132
|
|
|
133
133
|
constructor(
|
|
134
134
|
private readonly session: ToolSession,
|
|
135
135
|
options?: FindToolOptions,
|
|
136
136
|
) {
|
|
137
|
-
this
|
|
137
|
+
this.#customOps = options?.operations;
|
|
138
138
|
this.description = renderPromptTemplate(findDescription);
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
async execute(
|
|
142
142
|
_toolCallId: string,
|
|
143
143
|
params: Static<typeof findSchema>,
|
|
144
144
|
signal?: AbortSignal,
|
|
@@ -177,13 +177,13 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
177
177
|
const includeHidden = hidden ?? true;
|
|
178
178
|
|
|
179
179
|
// If custom operations provided with glob, use that instead of fd
|
|
180
|
-
if (this
|
|
181
|
-
if (!(await this
|
|
180
|
+
if (this.#customOps?.glob) {
|
|
181
|
+
if (!(await this.#customOps.exists(searchPath))) {
|
|
182
182
|
throw new ToolError(`Path not found: ${searchPath}`);
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
if (!hasGlob && this
|
|
186
|
-
const stat = await this
|
|
185
|
+
if (!hasGlob && this.#customOps.stat) {
|
|
186
|
+
const stat = await this.#customOps.stat(searchPath);
|
|
187
187
|
if (stat.isFile()) {
|
|
188
188
|
const files = [scopePath];
|
|
189
189
|
const details: FindToolDetails = {
|
|
@@ -196,7 +196,7 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
const results = await this
|
|
199
|
+
const results = await this.#customOps.glob(globPattern, searchPath, {
|
|
200
200
|
ignore: ["**/node_modules/**", "**/.git/**"],
|
|
201
201
|
limit: effectiveLimit,
|
|
202
202
|
});
|
|
@@ -239,9 +239,9 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
239
239
|
return resultBuilder.done();
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
let searchStat:
|
|
242
|
+
let searchStat: fs.Stats;
|
|
243
243
|
try {
|
|
244
|
-
searchStat = await fs.stat(searchPath);
|
|
244
|
+
searchStat = await fs.promises.stat(searchPath);
|
|
245
245
|
} catch (err) {
|
|
246
246
|
if (isEnoent(err)) {
|
|
247
247
|
throw new ToolError(`Path not found: ${searchPath}`);
|
|
@@ -263,7 +263,7 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
263
263
|
throw new ToolError(`Path is not a directory: ${searchPath}`);
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
let matches:
|
|
266
|
+
let matches: GlobMatch[];
|
|
267
267
|
const onUpdateMatches: string[] = [];
|
|
268
268
|
const updateIntervalMs = 200;
|
|
269
269
|
let lastUpdate = 0;
|
package/src/tools/grep.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as path from "node:path";
|
|
2
2
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
3
3
|
|
|
4
|
-
import { grep } from "@oh-my-pi/pi-natives";
|
|
4
|
+
import { type GrepResult, grep } from "@oh-my-pi/pi-natives";
|
|
5
5
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
6
6
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
7
7
|
import { untilAborted } from "@oh-my-pi/pi-utils";
|
|
@@ -51,31 +51,19 @@ export interface GrepToolDetails {
|
|
|
51
51
|
error?: string;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
export interface GrepOperations {
|
|
55
|
-
isDirectory: (absolutePath: string) => Promise<boolean> | boolean;
|
|
56
|
-
readFile: (absolutePath: string) => Promise<string> | string;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface GrepToolOptions {
|
|
60
|
-
operations?: GrepOperations;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
54
|
type GrepParams = Static<typeof grepSchema>;
|
|
64
55
|
|
|
65
56
|
export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
constructor(
|
|
72
|
-
private readonly session: ToolSession,
|
|
73
|
-
_options?: GrepToolOptions,
|
|
74
|
-
) {
|
|
57
|
+
readonly name = "grep";
|
|
58
|
+
readonly label = "Grep";
|
|
59
|
+
readonly description: string;
|
|
60
|
+
readonly parameters = grepSchema;
|
|
61
|
+
|
|
62
|
+
constructor(private readonly session: ToolSession) {
|
|
75
63
|
this.description = renderPromptTemplate(grepDescription);
|
|
76
64
|
}
|
|
77
65
|
|
|
78
|
-
|
|
66
|
+
async execute(
|
|
79
67
|
_toolCallId: string,
|
|
80
68
|
params: GrepParams,
|
|
81
69
|
signal?: AbortSignal,
|
|
@@ -111,7 +99,7 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
|
|
|
111
99
|
|
|
112
100
|
const searchPath = resolveToCwd(searchDir || ".", this.session.cwd);
|
|
113
101
|
const scopePath = (() => {
|
|
114
|
-
const relative =
|
|
102
|
+
const relative = path.relative(this.session.cwd, searchPath).replace(/\\/g, "/");
|
|
115
103
|
return relative.length === 0 ? "." : relative;
|
|
116
104
|
})();
|
|
117
105
|
|
|
@@ -127,7 +115,7 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
|
|
|
127
115
|
const effectiveLimit = normalizedLimit ?? DEFAULT_MATCH_LIMIT;
|
|
128
116
|
|
|
129
117
|
// Run grep
|
|
130
|
-
let result:
|
|
118
|
+
let result: GrepResult;
|
|
131
119
|
try {
|
|
132
120
|
result = await grep({
|
|
133
121
|
pattern: normalizedPattern,
|
|
@@ -157,7 +145,7 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
|
|
|
157
145
|
if (isDirectory) {
|
|
158
146
|
return cleanPath.replace(/\\/g, "/");
|
|
159
147
|
}
|
|
160
|
-
return
|
|
148
|
+
return path.basename(cleanPath);
|
|
161
149
|
};
|
|
162
150
|
|
|
163
151
|
// Build output
|
package/src/tools/index.ts
CHANGED
|
@@ -74,7 +74,7 @@ export { type ExitPlanModeDetails, ExitPlanModeTool } from "./exit-plan-mode";
|
|
|
74
74
|
export { FetchTool, type FetchToolDetails } from "./fetch";
|
|
75
75
|
export { type FindOperations, FindTool, type FindToolDetails, type FindToolInput, type FindToolOptions } from "./find";
|
|
76
76
|
export { setPreferredImageProvider } from "./gemini-image";
|
|
77
|
-
export {
|
|
77
|
+
export { GrepTool, type GrepToolDetails, type GrepToolInput } from "./grep";
|
|
78
78
|
export { NotebookTool, type NotebookToolDetails } from "./notebook";
|
|
79
79
|
export { PythonTool, type PythonToolDetails, type PythonToolOptions } from "./python";
|
|
80
80
|
export { ReadTool, type ReadToolDetails, type ReadToolInput } from "./read";
|
package/src/tools/notebook.ts
CHANGED
|
@@ -60,16 +60,16 @@ function splitIntoLines(content: string): string[] {
|
|
|
60
60
|
type NotebookParams = Static<typeof notebookSchema>;
|
|
61
61
|
|
|
62
62
|
export class NotebookTool implements AgentTool<typeof notebookSchema, NotebookToolDetails> {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
readonly name = "notebook";
|
|
64
|
+
readonly label = "Notebook";
|
|
65
|
+
readonly description =
|
|
66
66
|
"Completely replaces the contents of a specific cell in a Jupyter notebook (.ipynb file) with new source. Jupyter notebooks are interactive documents that combine code, text, and visualizations, commonly used for data analysis and scientific computing. The notebook_path parameter must be an absolute path, not a relative path. The cell_number is 0-indexed. Use edit_mode=insert to add a new cell at the index specified by cell_number. Use edit_mode=delete to delete the cell at the index specified by cell_number.";
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
readonly parameters = notebookSchema;
|
|
68
|
+
readonly concurrency = "exclusive";
|
|
69
69
|
|
|
70
70
|
constructor(private readonly session: ToolSession) {}
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
async execute(
|
|
73
73
|
_toolCallId: string,
|
|
74
74
|
params: NotebookParams,
|
|
75
75
|
signal?: AbortSignal,
|
package/src/tools/output-meta.ts
CHANGED
|
@@ -428,8 +428,16 @@ export function wrapToolWithMetaNotice<T extends AgentTool<any, any, any>>(tool:
|
|
|
428
428
|
return result;
|
|
429
429
|
};
|
|
430
430
|
|
|
431
|
-
|
|
432
|
-
|
|
431
|
+
// Use Proxy so property access (description, parameters, mode) stays on the original
|
|
432
|
+
// tool. Object.create(tool) would make getters run with this=wrapper, breaking
|
|
433
|
+
// private fields on tools like EditTool.
|
|
434
|
+
return new Proxy(tool, {
|
|
435
|
+
get(target, prop) {
|
|
436
|
+
if (prop === "execute") return wrappedExecute;
|
|
437
|
+
const value = (target as Record<string | symbol, unknown>)[prop];
|
|
438
|
+
if (typeof value === "function") return value.bind(target);
|
|
439
|
+
return value;
|
|
440
|
+
},
|
|
433
441
|
}) as T;
|
|
434
442
|
}
|
|
435
443
|
|
package/src/tools/python.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type * as fs from "node:fs";
|
|
1
2
|
import * as path from "node:path";
|
|
2
3
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
3
4
|
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
@@ -142,31 +143,31 @@ export interface PythonToolOptions {
|
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
export class PythonTool implements AgentTool<typeof pythonSchema> {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
readonly name = "python";
|
|
147
|
+
readonly label = "Python";
|
|
148
|
+
readonly description: string;
|
|
149
|
+
readonly parameters = pythonSchema;
|
|
150
|
+
readonly concurrency = "exclusive";
|
|
150
151
|
|
|
151
|
-
|
|
152
|
+
readonly #proxyExecutor?: PythonProxyExecutor;
|
|
152
153
|
|
|
153
154
|
constructor(
|
|
154
155
|
private readonly session: ToolSession | null,
|
|
155
156
|
options?: PythonToolOptions,
|
|
156
157
|
) {
|
|
157
|
-
this
|
|
158
|
+
this.#proxyExecutor = options?.proxyExecutor;
|
|
158
159
|
this.description = getPythonToolDescription();
|
|
159
160
|
}
|
|
160
161
|
|
|
161
|
-
|
|
162
|
+
async execute(
|
|
162
163
|
_toolCallId: string,
|
|
163
164
|
params: Static<typeof pythonSchema>,
|
|
164
165
|
signal?: AbortSignal,
|
|
165
166
|
onUpdate?: AgentToolUpdateCallback,
|
|
166
167
|
_ctx?: AgentToolContext,
|
|
167
168
|
): Promise<AgentToolResult<PythonToolDetails | undefined>> {
|
|
168
|
-
if (this
|
|
169
|
-
return this
|
|
169
|
+
if (this.#proxyExecutor) {
|
|
170
|
+
return this.#proxyExecutor(params, signal);
|
|
170
171
|
}
|
|
171
172
|
|
|
172
173
|
if (!this.session) {
|
|
@@ -195,7 +196,7 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
|
|
|
195
196
|
}
|
|
196
197
|
|
|
197
198
|
const commandCwd = cwd ? resolveToCwd(cwd, this.session.cwd) : this.session.cwd;
|
|
198
|
-
let cwdStat:
|
|
199
|
+
let cwdStat: fs.Stats;
|
|
199
200
|
try {
|
|
200
201
|
cwdStat = await Bun.file(commandCwd).stat();
|
|
201
202
|
} catch {
|
package/src/tools/read.ts
CHANGED
|
@@ -535,24 +535,24 @@ type ReadParams = { path: string; offset?: number; limit?: number; lines?: boole
|
|
|
535
535
|
* Directories return a formatted listing with modification times.
|
|
536
536
|
*/
|
|
537
537
|
export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
538
|
+
readonly name = "read";
|
|
539
|
+
readonly label = "Read";
|
|
540
|
+
readonly description: string;
|
|
541
|
+
readonly parameters = readSchema;
|
|
542
|
+
readonly nonAbortable = true;
|
|
543
543
|
|
|
544
|
-
|
|
545
|
-
|
|
544
|
+
readonly #autoResizeImages: boolean;
|
|
545
|
+
readonly #defaultLineNumbers: boolean;
|
|
546
546
|
|
|
547
547
|
constructor(private readonly session: ToolSession) {
|
|
548
|
-
this
|
|
549
|
-
this
|
|
548
|
+
this.#autoResizeImages = session.settings.get("images.autoResize");
|
|
549
|
+
this.#defaultLineNumbers = session.settings.get("readLineNumbers");
|
|
550
550
|
this.description = renderPromptTemplate(readDescription, {
|
|
551
551
|
DEFAULT_MAX_LINES: String(DEFAULT_MAX_LINES),
|
|
552
552
|
});
|
|
553
553
|
}
|
|
554
554
|
|
|
555
|
-
|
|
555
|
+
async execute(
|
|
556
556
|
_toolCallId: string,
|
|
557
557
|
params: ReadParams,
|
|
558
558
|
signal?: AbortSignal,
|
|
@@ -564,7 +564,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
564
564
|
// Handle internal URLs (agent://, skill://)
|
|
565
565
|
const internalRouter = this.session.internalRouter;
|
|
566
566
|
if (internalRouter?.canHandle(readPath)) {
|
|
567
|
-
return this
|
|
567
|
+
return this.#handleInternalUrl(readPath, offset, limit, lines);
|
|
568
568
|
}
|
|
569
569
|
|
|
570
570
|
const absolutePath = resolveReadPath(readPath, this.session.cwd);
|
|
@@ -604,7 +604,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
604
604
|
}
|
|
605
605
|
|
|
606
606
|
if (isDirectory) {
|
|
607
|
-
return this
|
|
607
|
+
return this.#readDirectory(absolutePath, limit, signal);
|
|
608
608
|
}
|
|
609
609
|
|
|
610
610
|
const mimeType = await detectSupportedImageMimeTypeFromFile(absolutePath);
|
|
@@ -636,7 +636,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
636
636
|
} else {
|
|
637
637
|
const base64 = new Uint8Array(buffer).toBase64();
|
|
638
638
|
|
|
639
|
-
if (this
|
|
639
|
+
if (this.#autoResizeImages) {
|
|
640
640
|
// Resize image if needed - catch errors from Photon
|
|
641
641
|
try {
|
|
642
642
|
const resized = await resizeImage({ type: "image", data: base64, mimeType });
|
|
@@ -754,7 +754,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
754
754
|
};
|
|
755
755
|
|
|
756
756
|
// Add line numbers if requested (uses setting default if not specified)
|
|
757
|
-
const shouldAddLineNumbers = lines ?? this
|
|
757
|
+
const shouldAddLineNumbers = lines ?? this.#defaultLineNumbers;
|
|
758
758
|
const prependLineNumbers = (text: string, startNum: number): string => {
|
|
759
759
|
const textLines = text.split("\n");
|
|
760
760
|
const lastLineNum = startNum + textLines.length - 1;
|
|
@@ -831,7 +831,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
831
831
|
* Handle internal URLs (agent://, skill://).
|
|
832
832
|
* Supports pagination via offset/limit but rejects them when query extraction is used.
|
|
833
833
|
*/
|
|
834
|
-
|
|
834
|
+
async #handleInternalUrl(
|
|
835
835
|
url: string,
|
|
836
836
|
offset?: number,
|
|
837
837
|
limit?: number,
|
|
@@ -904,7 +904,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
904
904
|
const truncation = truncateHead(selectedContent);
|
|
905
905
|
|
|
906
906
|
// Add line numbers if requested
|
|
907
|
-
const shouldAddLineNumbers = lines ?? this
|
|
907
|
+
const shouldAddLineNumbers = lines ?? this.#defaultLineNumbers;
|
|
908
908
|
const prependLineNumbers = (text: string, startNum: number): string => {
|
|
909
909
|
const textLines = text.split("\n");
|
|
910
910
|
const lastLineNum = startNum + textLines.length - 1;
|
|
@@ -976,7 +976,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
976
976
|
}
|
|
977
977
|
|
|
978
978
|
/** Read directory contents as a formatted listing */
|
|
979
|
-
|
|
979
|
+
async #readDirectory(
|
|
980
980
|
absolutePath: string,
|
|
981
981
|
limit: number | undefined,
|
|
982
982
|
signal?: AbortSignal,
|