diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 3a232902f5..ade6c37244 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -29,7 +29,7 @@ import { everyLineHasLineNumbers, } from "../integrations/misc/extract-text" import { countFileLines } from "../integrations/misc/line-counter" -import { fetchInstructions } from "./prompts/instructions/instructions" +import { fetchInstructionsTool } from "./tools/fetchInstructionsTool" import { ExitCodeDetails } from "../integrations/terminal/TerminalProcess" import { Terminal } from "../integrations/terminal/Terminal" import { TerminalRegistry } from "../integrations/terminal/TerminalRegistry" @@ -86,7 +86,7 @@ import { readLines } from "../integrations/misc/read-lines" import { getWorkspacePath } from "../utils/path" import { isBinaryFile } from "isbinaryfile" -type ToolResponse = string | Array +export type ToolResponse = string | Array type UserContent = Array export type ClineEvents = { @@ -148,9 +148,11 @@ export class Cline extends EventEmitter { private askResponseText?: string private askResponseImages?: string[] private lastMessageTs?: number - private consecutiveMistakeCount: number = 0 + // Not private since it needs to be accessible by tools + consecutiveMistakeCount: number = 0 private consecutiveMistakeCountForApplyDiff: Map = new Map() - private providerRef: WeakRef + // Not private since it needs to be accessible by tools + providerRef: WeakRef private abort: boolean = false didFinishAbortingStream = false abandoned = false @@ -2402,59 +2404,8 @@ export class Cline extends EventEmitter { } case "fetch_instructions": { - const task: string | undefined = block.params.task - const sharedMessageProps: ClineSayTool = { - tool: "fetchInstructions", - content: task, - } - try { - if (block.partial) { - const partialMessage = JSON.stringify({ - ...sharedMessageProps, - content: undefined, - } satisfies ClineSayTool) - await this.ask("tool", partialMessage, block.partial).catch(() => {}) - break - } else { - if (!task) { - this.consecutiveMistakeCount++ - pushToolResult( - await this.sayAndCreateMissingParamError("fetch_instructions", "task"), - ) - break - } - - this.consecutiveMistakeCount = 0 - const completeMessage = JSON.stringify({ - ...sharedMessageProps, - content: task, - } satisfies ClineSayTool) - - const didApprove = await askApproval("tool", completeMessage) - if (!didApprove) { - break - } - - // now fetch the content and provide it to the agent. - const provider = this.providerRef.deref() - const mcpHub = provider?.getMcpHub() - if (!mcpHub) { - throw new Error("MCP hub not available") - } - const diffStrategy = this.diffStrategy - const context = provider?.context - const content = await fetchInstructions(task, { mcpHub, diffStrategy, context }) - if (!content) { - pushToolResult(formatResponse.toolError(`Invalid instructions request: ${task}`)) - break - } - pushToolResult(content) - break - } - } catch (error) { - await handleError("fetch instructions", error) - break - } + fetchInstructionsTool(this, block, askApproval, handleError, pushToolResult) + break } case "list_files": { diff --git a/src/core/tools/fetchInstructionsTool.ts b/src/core/tools/fetchInstructionsTool.ts new file mode 100644 index 0000000000..8304c76317 --- /dev/null +++ b/src/core/tools/fetchInstructionsTool.ts @@ -0,0 +1,69 @@ +import { Cline } from "../Cline" +import { fetchInstructions } from "../prompts/instructions/instructions" +import { ClineSayTool } from "../../shared/ExtensionMessage" +import { ToolUse } from "../assistant-message" +import { formatResponse } from "../prompts/responses" +import { AskApproval, HandleError, PushToolResult } from "./types" + +export async function fetchInstructionsTool( + cline: Cline, + block: ToolUse, + askApproval: AskApproval, + handleError: HandleError, + pushToolResult: PushToolResult, +) { + switch (true) { + default: + const task: string | undefined = block.params.task + const sharedMessageProps: ClineSayTool = { + tool: "fetchInstructions", + content: task, + } + try { + if (block.partial) { + const partialMessage = JSON.stringify({ + ...sharedMessageProps, + content: undefined, + } satisfies ClineSayTool) + await cline.ask("tool", partialMessage, block.partial).catch(() => {}) + break + } else { + if (!task) { + cline.consecutiveMistakeCount++ + pushToolResult(await cline.sayAndCreateMissingParamError("fetch_instructions", "task")) + break + } + + cline.consecutiveMistakeCount = 0 + const completeMessage = JSON.stringify({ + ...sharedMessageProps, + content: task, + } satisfies ClineSayTool) + + const didApprove = await askApproval("tool", completeMessage) + if (!didApprove) { + break + } + + // now fetch the content and provide it to the agent. + const provider = cline.providerRef.deref() + const mcpHub = provider?.getMcpHub() + if (!mcpHub) { + throw new Error("MCP hub not available") + } + const diffStrategy = cline.diffStrategy + const context = provider?.context + const content = await fetchInstructions(task, { mcpHub, diffStrategy, context }) + if (!content) { + pushToolResult(formatResponse.toolError(`Invalid instructions request: ${task}`)) + break + } + pushToolResult(content) + break + } + } catch (error) { + await handleError("fetch instructions", error) + break + } + } +} diff --git a/src/core/tools/types.ts b/src/core/tools/types.ts new file mode 100644 index 0000000000..d03ddd016e --- /dev/null +++ b/src/core/tools/types.ts @@ -0,0 +1,12 @@ +import { ClineAsk, ToolProgressStatus } from "../../schemas" +import { ToolResponse } from "../Cline" + +export type AskApproval = ( + type: ClineAsk, + partialMessage?: string, + progressStatus?: ToolProgressStatus, +) => Promise + +export type HandleError = (action: string, error: Error) => void + +export type PushToolResult = (content: ToolResponse) => void