@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.
Files changed (141) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/docs/tui.md +9 -9
  3. package/package.json +7 -7
  4. package/src/capability/mcp.ts +9 -0
  5. package/src/cli/file-processor.ts +8 -13
  6. package/src/cli/oclif-help.ts +1 -1
  7. package/src/cli.ts +14 -0
  8. package/src/commit/git/index.ts +16 -16
  9. package/src/config/file-lock.ts +1 -1
  10. package/src/config/keybindings.ts +11 -11
  11. package/src/config/model-registry.ts +31 -66
  12. package/src/config/settings.ts +88 -95
  13. package/src/config.ts +2 -2
  14. package/src/cursor.ts +4 -4
  15. package/src/debug/index.ts +28 -28
  16. package/src/discovery/builtin.ts +48 -0
  17. package/src/discovery/codex.ts +5 -13
  18. package/src/discovery/cursor.ts +2 -7
  19. package/src/discovery/mcp-json.ts +33 -0
  20. package/src/exa/mcp-client.ts +2 -2
  21. package/src/exa/websets.ts +2 -2
  22. package/src/export/html/index.ts +3 -3
  23. package/src/export/ttsr.ts +27 -27
  24. package/src/extensibility/custom-tools/loader.ts +9 -9
  25. package/src/extensibility/extensions/runner.ts +64 -64
  26. package/src/extensibility/hooks/runner.ts +46 -46
  27. package/src/extensibility/plugins/manager.ts +49 -49
  28. package/src/extensibility/slash-commands.ts +1 -0
  29. package/src/index.ts +0 -3
  30. package/src/internal-urls/router.ts +5 -5
  31. package/src/ipy/kernel.ts +61 -57
  32. package/src/lsp/client.ts +1 -1
  33. package/src/lsp/clients/biome-client.ts +2 -2
  34. package/src/lsp/clients/lsp-linter-client.ts +7 -7
  35. package/src/lsp/index.ts +9 -9
  36. package/src/mcp/config-writer.ts +194 -0
  37. package/src/mcp/config.ts +20 -6
  38. package/src/mcp/index.ts +4 -0
  39. package/src/mcp/loader.ts +6 -0
  40. package/src/mcp/manager.ts +139 -50
  41. package/src/mcp/oauth-discovery.ts +274 -0
  42. package/src/mcp/oauth-flow.ts +229 -0
  43. package/src/mcp/tool-bridge.ts +20 -20
  44. package/src/mcp/transports/http.ts +107 -66
  45. package/src/mcp/transports/stdio.ts +74 -59
  46. package/src/mcp/types.ts +15 -1
  47. package/src/modes/components/assistant-message.ts +25 -25
  48. package/src/modes/components/bash-execution.ts +51 -51
  49. package/src/modes/components/bordered-loader.ts +7 -7
  50. package/src/modes/components/branch-summary-message.ts +7 -7
  51. package/src/modes/components/compaction-summary-message.ts +7 -7
  52. package/src/modes/components/countdown-timer.ts +15 -15
  53. package/src/modes/components/custom-editor.ts +22 -22
  54. package/src/modes/components/custom-message.ts +21 -21
  55. package/src/modes/components/dynamic-border.ts +3 -3
  56. package/src/modes/components/extensions/extension-dashboard.ts +72 -72
  57. package/src/modes/components/extensions/extension-list.ts +99 -97
  58. package/src/modes/components/extensions/inspector-panel.ts +26 -26
  59. package/src/modes/components/footer.ts +36 -36
  60. package/src/modes/components/history-search.ts +52 -52
  61. package/src/modes/components/hook-editor.ts +20 -20
  62. package/src/modes/components/hook-input.ts +20 -20
  63. package/src/modes/components/hook-message.ts +22 -22
  64. package/src/modes/components/hook-selector.ts +52 -52
  65. package/src/modes/components/index.ts +0 -1
  66. package/src/modes/components/login-dialog.ts +57 -57
  67. package/src/modes/components/mcp-add-wizard.ts +1286 -0
  68. package/src/modes/components/model-selector.ts +173 -173
  69. package/src/modes/components/oauth-selector.ts +45 -45
  70. package/src/modes/components/plugin-settings.ts +52 -52
  71. package/src/modes/components/python-execution.ts +53 -53
  72. package/src/modes/components/queue-mode-selector.ts +7 -7
  73. package/src/modes/components/read-tool-group.ts +23 -23
  74. package/src/modes/components/session-selector.ts +40 -37
  75. package/src/modes/components/settings-selector.ts +80 -80
  76. package/src/modes/components/show-images-selector.ts +7 -7
  77. package/src/modes/components/skill-message.ts +27 -27
  78. package/src/modes/components/status-line-segment-editor.ts +81 -81
  79. package/src/modes/components/status-line.ts +73 -73
  80. package/src/modes/components/theme-selector.ts +11 -11
  81. package/src/modes/components/thinking-selector.ts +7 -7
  82. package/src/modes/components/todo-display.ts +19 -19
  83. package/src/modes/components/todo-reminder.ts +9 -9
  84. package/src/modes/components/tool-execution.ts +212 -216
  85. package/src/modes/components/tree-selector.ts +144 -144
  86. package/src/modes/components/ttsr-notification.ts +17 -17
  87. package/src/modes/components/user-message-selector.ts +18 -18
  88. package/src/modes/components/welcome.ts +10 -10
  89. package/src/modes/controllers/command-controller.ts +0 -7
  90. package/src/modes/controllers/event-controller.ts +23 -23
  91. package/src/modes/controllers/extension-ui-controller.ts +13 -13
  92. package/src/modes/controllers/input-controller.ts +12 -9
  93. package/src/modes/controllers/mcp-command-controller.ts +1223 -0
  94. package/src/modes/interactive-mode.ts +240 -241
  95. package/src/modes/rpc/rpc-client.ts +77 -77
  96. package/src/modes/rpc/rpc-mode.ts +5 -5
  97. package/src/modes/theme/theme.ts +113 -113
  98. package/src/modes/types.ts +1 -1
  99. package/src/patch/index.ts +45 -45
  100. package/src/prompts/tools/task.md +22 -2
  101. package/src/sdk.ts +1 -0
  102. package/src/session/agent-session.ts +512 -476
  103. package/src/session/agent-storage.ts +72 -75
  104. package/src/session/auth-storage.ts +186 -252
  105. package/src/session/history-storage.ts +36 -38
  106. package/src/session/session-manager.ts +300 -299
  107. package/src/session/session-storage.ts +65 -90
  108. package/src/ssh/connection-manager.ts +9 -9
  109. package/src/system-prompt.ts +2 -3
  110. package/src/task/agents.ts +1 -1
  111. package/src/task/executor.ts +28 -40
  112. package/src/task/index.ts +13 -12
  113. package/src/task/subprocess-tool-registry.ts +5 -5
  114. package/src/task/worktree.ts +8 -5
  115. package/src/tools/ask.ts +7 -7
  116. package/src/tools/bash.ts +15 -10
  117. package/src/tools/browser.ts +130 -127
  118. package/src/tools/calculator.ts +46 -46
  119. package/src/tools/context.ts +9 -9
  120. package/src/tools/exit-plan-mode.ts +5 -5
  121. package/src/tools/fetch.ts +5 -5
  122. package/src/tools/find.ts +16 -16
  123. package/src/tools/grep.ts +12 -24
  124. package/src/tools/index.ts +1 -1
  125. package/src/tools/notebook.ts +6 -6
  126. package/src/tools/output-meta.ts +10 -2
  127. package/src/tools/python.ts +12 -11
  128. package/src/tools/read.ts +17 -17
  129. package/src/tools/ssh.ts +9 -9
  130. package/src/tools/submit-result.ts +13 -13
  131. package/src/tools/todo-write.ts +6 -6
  132. package/src/tools/write.ts +10 -10
  133. package/src/tui/output-block.ts +6 -6
  134. package/src/tui/utils.ts +9 -9
  135. package/src/utils/event-bus.ts +13 -11
  136. package/src/utils/frontmatter.ts +1 -1
  137. package/src/utils/ignore-files.ts +1 -1
  138. package/src/web/search/index.ts +5 -5
  139. package/src/web/search/providers/anthropic.ts +7 -2
  140. package/examples/hooks/snake.ts +0 -342
  141. package/src/modes/components/armin.ts +0 -379
@@ -216,14 +216,14 @@ function tokenizeExpression(expression: string): Token[] {
216
216
  * the call stack.
217
217
  */
218
218
  class ExpressionParser {
219
- private index = 0;
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.parseExpression();
226
- if (this.index < this.tokens.length) {
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
- private parseExpression(): number {
237
- let value = this.parseTerm();
236
+ #parseExpression(): number {
237
+ let value = this.#parseTerm();
238
238
  while (true) {
239
- if (this.matchOperator("+")) {
240
- value += this.parseTerm();
239
+ if (this.#matchOperator("+")) {
240
+ value += this.#parseTerm();
241
241
  continue;
242
242
  }
243
- if (this.matchOperator("-")) {
244
- value -= this.parseTerm();
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
- private parseTerm(): number {
257
- let value = this.parseUnary();
256
+ #parseTerm(): number {
257
+ let value = this.#parseUnary();
258
258
  while (true) {
259
- if (this.matchOperator("*")) {
260
- value *= this.parseUnary();
259
+ if (this.#matchOperator("*")) {
260
+ value *= this.#parseUnary();
261
261
  continue;
262
262
  }
263
- if (this.matchOperator("/")) {
264
- value /= this.parseUnary();
263
+ if (this.#matchOperator("/")) {
264
+ value /= this.#parseUnary();
265
265
  continue;
266
266
  }
267
- if (this.matchOperator("%")) {
268
- value %= this.parseUnary();
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
- private parseUnary(): number {
281
- if (this.matchOperator("+")) {
282
- return this.parseUnary();
280
+ #parseUnary(): number {
281
+ if (this.#matchOperator("+")) {
282
+ return this.#parseUnary();
283
283
  }
284
- if (this.matchOperator("-")) {
285
- return -this.parseUnary();
284
+ if (this.#matchOperator("-")) {
285
+ return -this.#parseUnary();
286
286
  }
287
- return this.parsePower();
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
- private parsePower(): number {
296
- let value = this.parsePrimary();
297
- if (this.matchOperator("**")) {
298
- value = value ** this.parsePower(); // Right-associative via recursion
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
- private parsePrimary(): number {
308
- const token = this.peek();
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.index += 1;
314
+ this.#index += 1;
315
315
  return token.value;
316
316
  }
317
317
 
318
318
  if (token.type === "paren" && token.value === "(") {
319
- this.index += 1;
320
- const value = this.parseExpression(); // Reset to lowest precedence
321
- if (!this.matchParen(")")) {
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
- private matchOperator(value: Operator): boolean {
332
- const token = this.tokens[this.index];
331
+ #matchOperator(value: Operator): boolean {
332
+ const token = this.tokens[this.#index];
333
333
  if (token && token.type === "operator" && token.value === value) {
334
- this.index += 1;
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
- private matchParen(value: "(" | ")"): boolean {
342
- const token = this.tokens[this.index];
341
+ #matchParen(value: "(" | ")"): boolean {
342
+ const token = this.tokens[this.#index];
343
343
  if (token && token.type === "paren" && token.value === value) {
344
- this.index += 1;
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
- private peek(): Token | undefined {
352
- return this.tokens[this.index];
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
- public readonly name = "calc";
399
- public readonly label = "Calc";
400
- public readonly description: string;
401
- public readonly parameters = calculatorSchema;
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
- public async execute(
407
+ async execute(
408
408
  _toolCallId: string,
409
409
  { calculations }: CalculatorParams,
410
410
  signal?: AbortSignal,
@@ -12,28 +12,28 @@ declare module "@oh-my-pi/pi-agent-core" {
12
12
  }
13
13
 
14
14
  export class ToolContextStore {
15
- private uiContext: ExtensionUIContext | undefined;
16
- private hasUI = false;
17
- private toolNames: string[] = [];
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.uiContext,
25
- hasUI: this.hasUI,
26
- toolNames: this.toolNames,
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.uiContext = uiContext;
33
- this.hasUI = hasUI;
32
+ this.#uiContext = uiContext;
33
+ this.#hasUI = hasUI;
34
34
  }
35
35
 
36
36
  setToolNames(names: string[]): void {
37
- this.toolNames = names;
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
- public readonly name = "exit_plan_mode";
20
- public readonly label = "ExitPlanMode";
21
- public readonly description: string;
22
- public readonly parameters = exitPlanModeSchema;
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
- public async execute(
28
+ async execute(
29
29
  _toolCallId: string,
30
30
  _params: Record<string, never>,
31
31
  _signal?: AbortSignal,
@@ -852,16 +852,16 @@ export interface FetchToolDetails {
852
852
  }
853
853
 
854
854
  export class FetchTool implements AgentTool<typeof fetchSchema, FetchToolDetails> {
855
- public readonly name = "fetch";
856
- public readonly label = "Fetch";
857
- public readonly description: string;
858
- public readonly parameters = fetchSchema;
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
- public async execute(
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/promises";
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
- public readonly name = "find";
127
- public readonly label = "Find";
128
- public readonly description: string;
129
- public readonly parameters = findSchema;
126
+ readonly name = "find";
127
+ readonly label = "Find";
128
+ readonly description: string;
129
+ readonly parameters = findSchema;
130
130
 
131
- private readonly customOps?: FindOperations;
131
+ readonly #customOps?: FindOperations;
132
132
 
133
133
  constructor(
134
134
  private readonly session: ToolSession,
135
135
  options?: FindToolOptions,
136
136
  ) {
137
- this.customOps = options?.operations;
137
+ this.#customOps = options?.operations;
138
138
  this.description = renderPromptTemplate(findDescription);
139
139
  }
140
140
 
141
- public async execute(
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.customOps?.glob) {
181
- if (!(await this.customOps.exists(searchPath))) {
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.customOps.stat) {
186
- const stat = await this.customOps.stat(searchPath);
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.customOps.glob(globPattern, searchPath, {
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: Awaited<ReturnType<typeof fs.stat>>;
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: Awaited<ReturnType<typeof glob>>["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 nodePath from "node:path";
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
- public readonly name = "grep";
67
- public readonly label = "Grep";
68
- public readonly description: string;
69
- public readonly parameters = grepSchema;
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
- public async execute(
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 = nodePath.relative(this.session.cwd, searchPath).replace(/\\/g, "/");
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: Awaited<ReturnType<typeof grep>>;
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 nodePath.basename(cleanPath);
148
+ return path.basename(cleanPath);
161
149
  };
162
150
 
163
151
  // Build output
@@ -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 { type GrepOperations, GrepTool, type GrepToolDetails, type GrepToolInput, type GrepToolOptions } from "./grep";
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";
@@ -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
- public readonly name = "notebook";
64
- public readonly label = "Notebook";
65
- public readonly description =
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
- public readonly parameters = notebookSchema;
68
- public readonly concurrency = "exclusive";
67
+ readonly parameters = notebookSchema;
68
+ readonly concurrency = "exclusive";
69
69
 
70
70
  constructor(private readonly session: ToolSession) {}
71
71
 
72
- public async execute(
72
+ async execute(
73
73
  _toolCallId: string,
74
74
  params: NotebookParams,
75
75
  signal?: AbortSignal,
@@ -428,8 +428,16 @@ export function wrapToolWithMetaNotice<T extends AgentTool<any, any, any>>(tool:
428
428
  return result;
429
429
  };
430
430
 
431
- return Object.create(tool, {
432
- execute: { value: wrappedExecute, enumerable: true },
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
 
@@ -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
- public readonly name = "python";
146
- public readonly label = "Python";
147
- public readonly description: string;
148
- public readonly parameters = pythonSchema;
149
- public readonly concurrency = "exclusive";
146
+ readonly name = "python";
147
+ readonly label = "Python";
148
+ readonly description: string;
149
+ readonly parameters = pythonSchema;
150
+ readonly concurrency = "exclusive";
150
151
 
151
- private readonly proxyExecutor?: PythonProxyExecutor;
152
+ readonly #proxyExecutor?: PythonProxyExecutor;
152
153
 
153
154
  constructor(
154
155
  private readonly session: ToolSession | null,
155
156
  options?: PythonToolOptions,
156
157
  ) {
157
- this.proxyExecutor = options?.proxyExecutor;
158
+ this.#proxyExecutor = options?.proxyExecutor;
158
159
  this.description = getPythonToolDescription();
159
160
  }
160
161
 
161
- public async execute(
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.proxyExecutor) {
169
- return this.proxyExecutor(params, signal);
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: Awaited<ReturnType<Bun.BunFile["stat"]>>;
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
- public readonly name = "read";
539
- public readonly label = "Read";
540
- public readonly description: string;
541
- public readonly parameters = readSchema;
542
- public readonly nonAbortable = true;
538
+ readonly name = "read";
539
+ readonly label = "Read";
540
+ readonly description: string;
541
+ readonly parameters = readSchema;
542
+ readonly nonAbortable = true;
543
543
 
544
- private readonly autoResizeImages: boolean;
545
- private readonly defaultLineNumbers: boolean;
544
+ readonly #autoResizeImages: boolean;
545
+ readonly #defaultLineNumbers: boolean;
546
546
 
547
547
  constructor(private readonly session: ToolSession) {
548
- this.autoResizeImages = session.settings.get("images.autoResize");
549
- this.defaultLineNumbers = session.settings.get("readLineNumbers");
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
- public async execute(
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.handleInternalUrl(readPath, offset, limit, lines);
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.readDirectory(absolutePath, limit, signal);
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.autoResizeImages) {
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.defaultLineNumbers;
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
- private async handleInternalUrl(
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.defaultLineNumbers;
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
- private async readDirectory(
979
+ async #readDirectory(
980
980
  absolutePath: string,
981
981
  limit: number | undefined,
982
982
  signal?: AbortSignal,