@oh-my-pi/pi-coding-agent 6.2.0 → 6.7.67

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 (93) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/docs/sdk.md +1 -1
  3. package/package.json +5 -5
  4. package/scripts/generate-template.ts +6 -6
  5. package/src/cli/args.ts +3 -0
  6. package/src/core/agent-session.ts +39 -0
  7. package/src/core/bash-executor.ts +3 -3
  8. package/src/core/cursor/exec-bridge.ts +95 -88
  9. package/src/core/custom-commands/bundled/review/index.ts +142 -145
  10. package/src/core/custom-commands/bundled/wt/index.ts +68 -66
  11. package/src/core/custom-commands/loader.ts +4 -6
  12. package/src/core/custom-tools/index.ts +2 -2
  13. package/src/core/custom-tools/loader.ts +66 -61
  14. package/src/core/custom-tools/types.ts +4 -4
  15. package/src/core/custom-tools/wrapper.ts +61 -25
  16. package/src/core/event-bus.ts +19 -47
  17. package/src/core/extensions/index.ts +8 -4
  18. package/src/core/extensions/loader.ts +160 -120
  19. package/src/core/extensions/types.ts +4 -4
  20. package/src/core/extensions/wrapper.ts +149 -100
  21. package/src/core/hooks/index.ts +1 -1
  22. package/src/core/hooks/tool-wrapper.ts +96 -70
  23. package/src/core/hooks/types.ts +1 -2
  24. package/src/core/index.ts +1 -0
  25. package/src/core/mcp/index.ts +6 -2
  26. package/src/core/mcp/json-rpc.ts +88 -0
  27. package/src/core/mcp/loader.ts +22 -4
  28. package/src/core/mcp/manager.ts +202 -48
  29. package/src/core/mcp/tool-bridge.ts +143 -55
  30. package/src/core/mcp/tool-cache.ts +122 -0
  31. package/src/core/python-executor.ts +3 -9
  32. package/src/core/sdk.ts +33 -32
  33. package/src/core/session-manager.ts +30 -0
  34. package/src/core/settings-manager.ts +54 -1
  35. package/src/core/ssh/ssh-executor.ts +6 -84
  36. package/src/core/streaming-output.ts +107 -53
  37. package/src/core/tools/ask.ts +92 -93
  38. package/src/core/tools/bash.ts +103 -94
  39. package/src/core/tools/calculator.ts +41 -26
  40. package/src/core/tools/complete.ts +76 -66
  41. package/src/core/tools/context.ts +22 -24
  42. package/src/core/tools/exa/index.ts +1 -1
  43. package/src/core/tools/exa/mcp-client.ts +56 -101
  44. package/src/core/tools/find.ts +250 -253
  45. package/src/core/tools/git.ts +39 -33
  46. package/src/core/tools/grep.ts +440 -427
  47. package/src/core/tools/index.ts +63 -61
  48. package/src/core/tools/ls.ts +119 -114
  49. package/src/core/tools/lsp/clients/biome-client.ts +5 -7
  50. package/src/core/tools/lsp/clients/index.ts +4 -4
  51. package/src/core/tools/lsp/clients/lsp-linter-client.ts +5 -7
  52. package/src/core/tools/lsp/config.ts +2 -2
  53. package/src/core/tools/lsp/index.ts +604 -578
  54. package/src/core/tools/notebook.ts +121 -119
  55. package/src/core/tools/output.ts +163 -147
  56. package/src/core/tools/patch/applicator.ts +1100 -0
  57. package/src/core/tools/patch/diff.ts +362 -0
  58. package/src/core/tools/patch/fuzzy.ts +647 -0
  59. package/src/core/tools/patch/index.ts +430 -0
  60. package/src/core/tools/patch/normalize.ts +220 -0
  61. package/src/core/tools/patch/normative.ts +73 -0
  62. package/src/core/tools/patch/parser.ts +528 -0
  63. package/src/core/tools/patch/shared.ts +257 -0
  64. package/src/core/tools/patch/types.ts +244 -0
  65. package/src/core/tools/python.ts +139 -136
  66. package/src/core/tools/read.ts +239 -216
  67. package/src/core/tools/render-utils.ts +196 -77
  68. package/src/core/tools/renderers.ts +6 -2
  69. package/src/core/tools/ssh.ts +99 -80
  70. package/src/core/tools/task/executor.ts +11 -7
  71. package/src/core/tools/task/index.ts +352 -343
  72. package/src/core/tools/task/worker.ts +13 -23
  73. package/src/core/tools/todo-write.ts +74 -59
  74. package/src/core/tools/web-fetch.ts +54 -47
  75. package/src/core/tools/web-search/index.ts +27 -16
  76. package/src/core/tools/write.ts +108 -47
  77. package/src/core/ttsr.ts +106 -152
  78. package/src/core/voice.ts +49 -39
  79. package/src/index.ts +16 -12
  80. package/src/lib/worktree/index.ts +1 -9
  81. package/src/modes/interactive/components/diff.ts +15 -8
  82. package/src/modes/interactive/components/settings-defs.ts +42 -0
  83. package/src/modes/interactive/components/tool-execution.ts +46 -8
  84. package/src/modes/interactive/controllers/event-controller.ts +6 -19
  85. package/src/modes/interactive/controllers/input-controller.ts +1 -1
  86. package/src/modes/interactive/utils/ui-helpers.ts +5 -1
  87. package/src/modes/rpc/rpc-mode.ts +99 -81
  88. package/src/prompts/tools/patch.md +76 -0
  89. package/src/prompts/tools/read.md +1 -1
  90. package/src/prompts/tools/{edit.md → replace.md} +1 -0
  91. package/src/utils/shell.ts +0 -40
  92. package/src/core/tools/edit-diff.ts +0 -574
  93. package/src/core/tools/edit.ts +0 -345
package/CHANGELOG.md CHANGED
@@ -2,6 +2,66 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [6.7.67] - 2026-01-19
6
+ ### Added
7
+
8
+ - Added normative rewrite setting to control tool call argument normalization in session history
9
+ - Added read line numbers setting to prepend line numbers to read tool output by default
10
+ - Added streaming preview for edit and write tools with spinner animation
11
+ - Added automatic anchor derivation for normative patches when anchors not specified
12
+
13
+ ### Changed
14
+
15
+ - Enhanced edit and write tool renderers to show streaming content preview
16
+ - Updated read tool to respect default line numbers setting
17
+ - Improved normative patch anchor handling to support undefined anchors
18
+
19
+ ## [6.7.0] - 2026-01-19
20
+
21
+ ### Added
22
+
23
+ - Normative patch generation to canonicalize edit tool output with tool call argument rewriting for session history
24
+ - Patch matching fallback variants: trimmed context, collapsed duplicates, single-line reduction, comment-prefix normalization
25
+ - Extended anchor syntax: ellipsis placeholders, `top of file`/`start of file`, `@@ line N`, nested `@@` anchors, space-separated hierarchical contexts
26
+ - Relaxed fuzzy threshold fallback and unique substring acceptance for context matching
27
+ - Added `--no-title` flag to disable automatic session title generation
28
+ - Environment variables for edit tool configuration (OMP_EDIT_VARIANT, OMP_EDIT_FUZZY, OMP_EDIT_FUZZY_THRESHOLD)
29
+ - Configurable fuzzy matching threshold setting (0.85 lenient to 0.98 strict)
30
+ - Apply-patch mode for edit tool (`edit.patchMode` setting) with create, update, delete, and rename operations
31
+ - Added MCP tool caching for faster startup with cached tool definitions
32
+
33
+ ### Changed
34
+
35
+ - Patch applicator now supports normalized input, implicit context lines, and improved indentation adjustment
36
+ - Patch operation schema uses 'op' instead of 'operation' and 'rename' instead of 'moveTo'
37
+ - Fuzzy matching tries comment-prefix normalized matches before unicode normalization
38
+ - Updated patch prompts with clearer anchor selection rules and verbatim context requirements
39
+ - Changed default behavior of read tool to omit line numbers by default
40
+ - Changed default edit tool mode to use apply-patch format instead of oldText/newText
41
+ - Converted tool implementations from factory functions to class-based architecture
42
+ - Refactored edit tool with modular patch architecture (moved from `edit/` to `patch/` module)
43
+ - Enhanced patch parsing: unified diff format, Codex-style patches, nested anchors, multi-file markers
44
+ - Improved fuzzy matching with multiple match tracking, ambiguity detection, and out-of-order hunk processing
45
+ - Better diff rendering: smarter truncation, optional line numbers, trailing newline preservation
46
+ - Improved error messages with hierarchical context display using `>` separator
47
+ - Centralized output sanitization in streaming-output module
48
+ - Enhanced MCP startup with deferred tool loading and cached fallback
49
+
50
+ ### Fixed
51
+
52
+ - Patch application handles repeated context blocks, preserves original indentation on fuzzy match
53
+ - Ambiguous context matching resolves duplicates using adjacent @@ anchor positioning
54
+ - Patch parser handles bare *** terminators, model hallucination markers, line hint ranges
55
+ - Function context matching handles signatures with and without empty parentheses
56
+ - Fixed session title generation to respect OMP_NO_TITLE environment variable
57
+ - Fixed Python module discovery to use import.meta.dir for ES module compatibility
58
+ - Fixed LSP writethrough batching to flush when delete operations complete a batch
59
+ - Fixed line number validation, BOM detection, and trailing newline preservation in patches
60
+ - Fixed hierarchical context matching and space-separated anchor parsing
61
+ - Fixed fuzzy matching to avoid infinite loops when `allowFuzzy` is disabled
62
+ - Fixed tool completion logic to only mark tools as complete when streaming is not aborted or in error state
63
+ - Fixed MCP tool path formatting to correctly display provider information
64
+
5
65
  ## [6.2.0] - 2026-01-19
6
66
  ### Changed
7
67
 
package/docs/sdk.md CHANGED
@@ -942,7 +942,7 @@ createTools // Create all tools from ToolSession
942
942
  type ToolSession // Session context for tool creation
943
943
 
944
944
  // Individual tool factories
945
- createReadTool, createBashTool, createEditTool, createWriteTool
945
+ createReadTool, createBashTool, EditTool, createWriteTool
946
946
  createGrepTool, createFindTool, createLsTool, createGitTool
947
947
 
948
948
  // Types
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-coding-agent",
3
- "version": "6.2.0",
3
+ "version": "6.7.67",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "ompConfig": {
@@ -40,10 +40,10 @@
40
40
  "prepublishOnly": "bun run generate-template && bun run clean && bun run build"
41
41
  },
42
42
  "dependencies": {
43
- "@oh-my-pi/pi-agent-core": "6.2.0",
44
- "@oh-my-pi/pi-ai": "6.2.0",
45
- "@oh-my-pi/pi-git-tool": "6.2.0",
46
- "@oh-my-pi/pi-tui": "6.2.0",
43
+ "@oh-my-pi/pi-agent-core": "6.7.67",
44
+ "@oh-my-pi/pi-ai": "6.7.67",
45
+ "@oh-my-pi/pi-git-tool": "6.7.67",
46
+ "@oh-my-pi/pi-tui": "6.7.67",
47
47
  "@openai/agents": "^0.3.7",
48
48
  "@sinclair/typebox": "^0.34.46",
49
49
  "ajv": "^8.17.1",
@@ -13,15 +13,15 @@ const js = await Bun.file(`${dir}template.js`).text();
13
13
 
14
14
  // Minify CSS
15
15
  const minifiedCss = css
16
- .replace(/\/\*[\s\S]*?\*\//g, "")
17
- .replace(/\s+/g, " ")
18
- .replace(/\s*([{}:;,])\s*/g, "$1")
19
- .trim();
16
+ .replace(/\/\*[\s\S]*?\*\//g, "")
17
+ .replace(/\s+/g, " ")
18
+ .replace(/\s*([{}:;,])\s*/g, "$1")
19
+ .trim();
20
20
 
21
21
  // Inline everything
22
22
  const template = html
23
- .replace("<template-css/>", `<style>${minifiedCss}</style>`)
24
- .replace("<template-js/>", `<script>${js}</script>`);
23
+ .replace("<template-css/>", `<style>${minifiedCss}</style>`)
24
+ .replace("<template-js/>", `<script>${js}</script>`);
25
25
 
26
26
  // Write generated file
27
27
  const output = `// Auto-generated by scripts/generate-template.ts - DO NOT EDIT
package/src/cli/args.ts CHANGED
@@ -40,6 +40,7 @@ export interface Args {
40
40
  noSkills?: boolean;
41
41
  skills?: string[];
42
42
  listModels?: string | true;
43
+ noTitle?: boolean;
43
44
  messages: string[];
44
45
  fileArgs: string[];
45
46
  /** Unknown flags (potentially extension flags) - map of flag name to value */
@@ -143,6 +144,8 @@ export function parseArgs(args: string[], extensionFlags?: Map<string, { type: "
143
144
  result.noExtensions = true;
144
145
  } else if (arg === "--no-skills") {
145
146
  result.noSkills = true;
147
+ } else if (arg === "--no-title") {
148
+ result.noTitle = true;
146
149
  } else if (arg === "--skills" && i + 1 < args.length) {
147
150
  // Comma-separated glob patterns for skill filtering
148
151
  result.skills = args[++i].split(",").map((s) => s.trim());
@@ -432,6 +432,18 @@ export class AgentSession {
432
432
  if (event.message.role === "assistant") {
433
433
  this._lastAssistantMessage = event.message;
434
434
  }
435
+
436
+ if (event.message.role === "toolResult") {
437
+ const { $normative, toolCallId } = event.message as {
438
+ toolName?: string;
439
+ toolCallId?: string;
440
+ details?: unknown;
441
+ $normative?: Record<string, unknown>;
442
+ };
443
+ if ($normative && toolCallId && this.settingsManager.getNormativeRewrite()) {
444
+ await this._rewriteToolCallArgs(toolCallId, $normative);
445
+ }
446
+ }
435
447
  }
436
448
 
437
449
  // Check auto-retry and auto-compaction after agent completes
@@ -513,6 +525,33 @@ export class AgentSession {
513
525
  return undefined;
514
526
  }
515
527
 
528
+ /** Rewrite tool call arguments in agent state and persisted session history. */
529
+ private async _rewriteToolCallArgs(toolCallId: string, args: Record<string, unknown>): Promise<void> {
530
+ let updated = false;
531
+ const messages = this.agent.state.messages;
532
+ for (let i = messages.length - 1; i >= 0; i--) {
533
+ const msg = messages[i];
534
+ if (msg.role !== "assistant") continue;
535
+ const assistantMsg = msg as AssistantMessage;
536
+ if (!Array.isArray(assistantMsg.content)) continue;
537
+ for (const block of assistantMsg.content) {
538
+ if (typeof block !== "object" || block === null) continue;
539
+ if (!("type" in block) || (block as { type?: string }).type !== "toolCall") continue;
540
+ const toolCall = block as { id?: string; arguments?: Record<string, unknown> };
541
+ if (toolCall.id === toolCallId) {
542
+ toolCall.arguments = args;
543
+ updated = true;
544
+ break;
545
+ }
546
+ }
547
+ if (updated) break;
548
+ }
549
+
550
+ if (updated) {
551
+ await this.sessionManager.rewriteAssistantToolCallArgs(toolCallId, args);
552
+ }
553
+ }
554
+
516
555
  /** Emit extension events based on agent events */
517
556
  private async _emitExtensionEvent(event: AgentEvent): Promise<void> {
518
557
  if (!this._extensionRunner) return;
@@ -9,7 +9,7 @@
9
9
  import type { Subprocess } from "bun";
10
10
  import { getShellConfig, killProcessTree } from "../utils/shell";
11
11
  import { getOrCreateSnapshot, getSnapshotSourceCommand } from "../utils/shell-snapshot";
12
- import { createOutputSink, pumpStream } from "./streaming-output";
12
+ import { OutputSink, pumpStream } from "./streaming-output";
13
13
  import type { BashOperations } from "./tools/bash";
14
14
  import { DEFAULT_MAX_BYTES } from "./tools/truncate";
15
15
  import { ScopeSignal } from "./utils";
@@ -85,7 +85,7 @@ export async function executeBash(command: string, options?: BashExecutorOptions
85
85
  killProcessTree(child.pid);
86
86
  });
87
87
 
88
- const sink = createOutputSink(DEFAULT_MAX_BYTES, DEFAULT_MAX_BYTES * 2, options?.onChunk);
88
+ const sink = new OutputSink(DEFAULT_MAX_BYTES, DEFAULT_MAX_BYTES * 2, options?.onChunk);
89
89
 
90
90
  const writer = sink.getWriter();
91
91
  try {
@@ -128,7 +128,7 @@ export async function executeBashWithOperations(
128
128
  operations: BashOperations,
129
129
  options?: BashExecutorOptions,
130
130
  ): Promise<BashResult> {
131
- const sink = createOutputSink(DEFAULT_MAX_BYTES, DEFAULT_MAX_BYTES * 2, options?.onChunk);
131
+ const sink = new OutputSink(DEFAULT_MAX_BYTES, DEFAULT_MAX_BYTES * 2, options?.onChunk);
132
132
  const writer = sink.getWriter();
133
133
 
134
134
  // Create a ReadableStream from the callback-based operations.exec
@@ -7,7 +7,7 @@ import type {
7
7
  AgentToolResult,
8
8
  AgentToolUpdateCallback,
9
9
  } from "@oh-my-pi/pi-agent-core";
10
- import type { CursorExecHandlers, CursorMcpCall, ToolResultMessage } from "@oh-my-pi/pi-ai";
10
+ import type { CursorMcpCall, CursorExecHandlers as ICursorExecHandlers, ToolResultMessage } from "@oh-my-pi/pi-ai";
11
11
  import { resolveToCwd } from "../tools/path-utils";
12
12
 
13
13
  interface CursorExecBridgeOptions {
@@ -143,92 +143,99 @@ function formatMcpToolErrorMessage(toolName: string, availableTools: string[]):
143
143
  return `MCP tool "${toolName}" not found. Available tools: ${list}`;
144
144
  }
145
145
 
146
- export function createCursorExecHandlers(options: CursorExecBridgeOptions): CursorExecHandlers {
147
- return {
148
- read: async (args) => {
149
- const toolCallId = decodeToolCallId(args.toolCallId);
150
- const toolResultMessage = await executeTool(options, "read", toolCallId, { path: args.path });
151
- return toolResultMessage;
152
- },
153
- ls: async (args) => {
154
- const toolCallId = decodeToolCallId(args.toolCallId);
155
- const toolResultMessage = await executeTool(options, "ls", toolCallId, { path: args.path });
156
- return toolResultMessage;
157
- },
158
- grep: async (args) => {
159
- const toolCallId = decodeToolCallId(args.toolCallId);
160
- const toolResultMessage = await executeTool(options, "grep", toolCallId, {
161
- pattern: args.pattern,
162
- path: args.path || undefined,
163
- glob: args.glob || undefined,
164
- outputMode: args.outputMode || undefined,
165
- context: args.context ?? args.contextBefore ?? args.contextAfter ?? undefined,
166
- ignoreCase: args.caseInsensitive || undefined,
167
- type: args.type || undefined,
168
- headLimit: args.headLimit ?? undefined,
169
- multiline: args.multiline || undefined,
170
- });
171
- return toolResultMessage;
172
- },
173
- write: async (args) => {
174
- const toolCallId = decodeToolCallId(args.toolCallId);
175
- const content = args.fileText ?? new TextDecoder().decode(args.fileBytes ?? new Uint8Array());
176
- const toolResultMessage = await executeTool(options, "write", toolCallId, {
177
- path: args.path,
178
- content,
179
- });
180
- return toolResultMessage;
181
- },
182
- delete: async (args) => {
183
- const toolCallId = decodeToolCallId(args.toolCallId);
184
- const toolResultMessage = await executeDelete(options, args.path, toolCallId);
185
- return toolResultMessage;
186
- },
187
- shell: async (args) => {
188
- const toolCallId = decodeToolCallId(args.toolCallId);
189
- const timeoutSeconds =
190
- args.timeout && args.timeout > 0
191
- ? args.timeout > 1000
192
- ? Math.ceil(args.timeout / 1000)
193
- : args.timeout
194
- : undefined;
195
- const toolResultMessage = await executeTool(options, "bash", toolCallId, {
196
- command: args.command,
197
- workdir: args.workingDirectory || undefined,
198
- timeout: timeoutSeconds,
199
- });
200
- return toolResultMessage;
201
- },
202
- diagnostics: async (args) => {
203
- const toolCallId = decodeToolCallId(args.toolCallId);
204
- const toolResultMessage = await executeTool(options, "lsp", toolCallId, {
205
- action: "diagnostics",
206
- file: args.path,
207
- });
208
- return toolResultMessage;
209
- },
210
- mcp: async (call: CursorMcpCall) => {
211
- const toolName = call.toolName || call.name;
212
- const toolCallId = decodeToolCallId(call.toolCallId);
213
- const tool = options.tools.get(toolName);
214
- if (!tool) {
215
- const availableTools = Array.from(options.tools.keys()).filter((name) => name.startsWith("mcp_"));
216
- const message = formatMcpToolErrorMessage(toolName, availableTools);
217
- const toolResult: ToolResultMessage = {
218
- role: "toolResult",
219
- toolCallId,
220
- toolName,
221
- content: [{ type: "text", text: message }],
222
- details: {},
223
- isError: true,
224
- timestamp: Date.now(),
225
- };
226
- return toolResult;
227
- }
146
+ export class CursorExecHandlers implements ICursorExecHandlers {
147
+ constructor(private options: CursorExecBridgeOptions) {}
228
148
 
229
- const args = Object.keys(call.args ?? {}).length > 0 ? call.args : decodeMcpArgs(call.rawArgs ?? {});
230
- const toolResultMessage = await executeTool(options, toolName, toolCallId, args);
231
- return toolResultMessage;
232
- },
233
- };
149
+ async read(args: Parameters<NonNullable<ICursorExecHandlers["read"]>>[0]) {
150
+ const toolCallId = decodeToolCallId(args.toolCallId);
151
+ const toolResultMessage = await executeTool(this.options, "read", toolCallId, { path: args.path });
152
+ return toolResultMessage;
153
+ }
154
+
155
+ async ls(args: Parameters<NonNullable<ICursorExecHandlers["ls"]>>[0]) {
156
+ const toolCallId = decodeToolCallId(args.toolCallId);
157
+ const toolResultMessage = await executeTool(this.options, "ls", toolCallId, { path: args.path });
158
+ return toolResultMessage;
159
+ }
160
+
161
+ async grep(args: Parameters<NonNullable<ICursorExecHandlers["grep"]>>[0]) {
162
+ const toolCallId = decodeToolCallId(args.toolCallId);
163
+ const toolResultMessage = await executeTool(this.options, "grep", toolCallId, {
164
+ pattern: args.pattern,
165
+ path: args.path || undefined,
166
+ glob: args.glob || undefined,
167
+ outputMode: args.outputMode || undefined,
168
+ context: args.context ?? args.contextBefore ?? args.contextAfter ?? undefined,
169
+ ignoreCase: args.caseInsensitive || undefined,
170
+ type: args.type || undefined,
171
+ headLimit: args.headLimit ?? undefined,
172
+ multiline: args.multiline || undefined,
173
+ });
174
+ return toolResultMessage;
175
+ }
176
+
177
+ async write(args: Parameters<NonNullable<ICursorExecHandlers["write"]>>[0]) {
178
+ const toolCallId = decodeToolCallId(args.toolCallId);
179
+ const content = args.fileText ?? new TextDecoder().decode(args.fileBytes ?? new Uint8Array());
180
+ const toolResultMessage = await executeTool(this.options, "write", toolCallId, {
181
+ path: args.path,
182
+ content,
183
+ });
184
+ return toolResultMessage;
185
+ }
186
+
187
+ async delete(args: Parameters<NonNullable<ICursorExecHandlers["delete"]>>[0]) {
188
+ const toolCallId = decodeToolCallId(args.toolCallId);
189
+ const toolResultMessage = await executeDelete(this.options, args.path, toolCallId);
190
+ return toolResultMessage;
191
+ }
192
+
193
+ async shell(args: Parameters<NonNullable<ICursorExecHandlers["shell"]>>[0]) {
194
+ const toolCallId = decodeToolCallId(args.toolCallId);
195
+ const timeoutSeconds =
196
+ args.timeout && args.timeout > 0
197
+ ? args.timeout > 1000
198
+ ? Math.ceil(args.timeout / 1000)
199
+ : args.timeout
200
+ : undefined;
201
+ const toolResultMessage = await executeTool(this.options, "bash", toolCallId, {
202
+ command: args.command,
203
+ workdir: args.workingDirectory || undefined,
204
+ timeout: timeoutSeconds,
205
+ });
206
+ return toolResultMessage;
207
+ }
208
+
209
+ async diagnostics(args: Parameters<NonNullable<ICursorExecHandlers["diagnostics"]>>[0]) {
210
+ const toolCallId = decodeToolCallId(args.toolCallId);
211
+ const toolResultMessage = await executeTool(this.options, "lsp", toolCallId, {
212
+ action: "diagnostics",
213
+ file: args.path,
214
+ });
215
+ return toolResultMessage;
216
+ }
217
+
218
+ async mcp(call: CursorMcpCall) {
219
+ const toolName = call.toolName || call.name;
220
+ const toolCallId = decodeToolCallId(call.toolCallId);
221
+ const tool = this.options.tools.get(toolName);
222
+ if (!tool) {
223
+ const availableTools = Array.from(this.options.tools.keys()).filter((name) => name.startsWith("mcp_"));
224
+ const message = formatMcpToolErrorMessage(toolName, availableTools);
225
+ const toolResult: ToolResultMessage = {
226
+ role: "toolResult",
227
+ toolCallId,
228
+ toolName,
229
+ content: [{ type: "text", text: message }],
230
+ details: {},
231
+ isError: true,
232
+ timestamp: Date.now(),
233
+ };
234
+ return toolResult;
235
+ }
236
+
237
+ const args = Object.keys(call.args ?? {}).length > 0 ? call.args : decodeMcpArgs(call.rawArgs ?? {});
238
+ const toolResultMessage = await executeTool(this.options, toolName, toolCallId, args);
239
+ return toolResultMessage;
240
+ }
234
241
  }