mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 12:21:13 -05:00
Checkbox to experiment with letting Cline edit through diffs (#48)
Co-authored-by: Jozi <jozigila@gmail.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
import * as diff from "diff"
|
||||
import cloneDeep from "clone-deep"
|
||||
import delay from "delay"
|
||||
import fs from "fs/promises"
|
||||
@@ -64,6 +65,7 @@ export class Cline {
|
||||
private browserSession: BrowserSession
|
||||
private didEditFile: boolean = false
|
||||
customInstructions?: string
|
||||
diffEnabled?: boolean
|
||||
|
||||
apiConversationHistory: Anthropic.MessageParam[] = []
|
||||
clineMessages: ClineMessage[] = []
|
||||
@@ -93,6 +95,7 @@ export class Cline {
|
||||
provider: ClineProvider,
|
||||
apiConfiguration: ApiConfiguration,
|
||||
customInstructions?: string,
|
||||
diffEnabled?: boolean,
|
||||
task?: string,
|
||||
images?: string[],
|
||||
historyItem?: HistoryItem,
|
||||
@@ -104,7 +107,7 @@ export class Cline {
|
||||
this.browserSession = new BrowserSession(provider.context)
|
||||
this.diffViewProvider = new DiffViewProvider(cwd)
|
||||
this.customInstructions = customInstructions
|
||||
|
||||
this.diffEnabled = diffEnabled
|
||||
if (historyItem) {
|
||||
this.taskId = historyItem.id
|
||||
this.resumeTaskFromHistory()
|
||||
@@ -749,7 +752,7 @@ export class Cline {
|
||||
}
|
||||
|
||||
async *attemptApiRequest(previousApiReqIndex: number): ApiStream {
|
||||
const systemPrompt = await SYSTEM_PROMPT(cwd, this.api.getModel().info.supportsComputerUse ?? false) + await addCustomInstructions(this.customInstructions ?? '', cwd)
|
||||
const systemPrompt = await SYSTEM_PROMPT(cwd, this.api.getModel().info.supportsComputerUse ?? false, !!this.diffEnabled) + await addCustomInstructions(this.customInstructions ?? '', cwd)
|
||||
|
||||
// If the previous API request's total token usage is close to the context window, truncate the conversation history to free up space for the new request
|
||||
if (previousApiReqIndex >= 0) {
|
||||
@@ -876,6 +879,8 @@ export class Cline {
|
||||
return `[${block.name} for '${block.params.path}']`
|
||||
case "write_to_file":
|
||||
return `[${block.name} for '${block.params.path}']`
|
||||
case "apply_diff":
|
||||
return `[${block.name} for '${block.params.path}']`
|
||||
case "search_files":
|
||||
return `[${block.name} for '${block.params.regex}'${
|
||||
block.params.file_pattern ? ` in '${block.params.file_pattern}'` : ""
|
||||
@@ -1150,6 +1155,95 @@ export class Cline {
|
||||
break
|
||||
}
|
||||
}
|
||||
case "apply_diff": {
|
||||
const relPath: string | undefined = block.params.path
|
||||
const diffContent: string | undefined = block.params.diff
|
||||
|
||||
const sharedMessageProps: ClineSayTool = {
|
||||
tool: "appliedDiff",
|
||||
path: getReadablePath(cwd, removeClosingTag("path", relPath)),
|
||||
}
|
||||
|
||||
try {
|
||||
if (block.partial) {
|
||||
// update gui message
|
||||
const partialMessage = JSON.stringify(sharedMessageProps)
|
||||
await this.ask("tool", partialMessage, block.partial).catch(() => {})
|
||||
break
|
||||
} else {
|
||||
if (!relPath) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("apply_diff", "path"))
|
||||
break
|
||||
}
|
||||
if (!diffContent) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("apply_diff", "diff"))
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
|
||||
const absolutePath = path.resolve(cwd, relPath)
|
||||
const fileExists = await fileExistsAtPath(absolutePath)
|
||||
|
||||
if (!fileExists) {
|
||||
await this.say("error", `File does not exist at path: ${absolutePath}`)
|
||||
pushToolResult(`Error: File does not exist at path: ${absolutePath}`)
|
||||
break
|
||||
}
|
||||
|
||||
const originalContent = await fs.readFile(absolutePath, "utf-8")
|
||||
|
||||
// Apply the diff to the original content
|
||||
let newContent = diff.applyPatch(originalContent, diffContent) as string | false
|
||||
if (newContent === false) {
|
||||
await this.say("error", `Error applying diff to file: ${absolutePath}`)
|
||||
pushToolResult(`Error applying diff to file: ${absolutePath}`)
|
||||
break
|
||||
}
|
||||
|
||||
// Create a diff for display purposes
|
||||
const diffRepresentation = diff
|
||||
.diffLines(originalContent, newContent)
|
||||
.map((part) => {
|
||||
const prefix = part.added ? "+" : part.removed ? "-" : " "
|
||||
return (part.value || "")
|
||||
.split("\n")
|
||||
.map((line) => (line ? prefix + line : ""))
|
||||
.join("\n")
|
||||
})
|
||||
.join("")
|
||||
|
||||
// Show diff view before asking for approval
|
||||
this.diffViewProvider.editType = "modify"
|
||||
await this.diffViewProvider.open(relPath);
|
||||
await this.diffViewProvider.update(newContent, true);
|
||||
await this.diffViewProvider.scrollToFirstDiff();
|
||||
|
||||
const completeMessage = JSON.stringify({
|
||||
...sharedMessageProps,
|
||||
diff: diffRepresentation,
|
||||
} satisfies ClineSayTool)
|
||||
|
||||
const didApprove = await askApproval("tool", completeMessage)
|
||||
if (!didApprove) {
|
||||
await this.diffViewProvider.revertChanges() // This likely handles closing the diff view
|
||||
break
|
||||
}
|
||||
|
||||
await fs.writeFile(absolutePath, newContent)
|
||||
await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { preview: false })
|
||||
await this.diffViewProvider.reset()
|
||||
|
||||
pushToolResult(`Changes successfully applied to ${relPath.toPosix()}:\n\n${diffRepresentation}`)
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("applying diff", error)
|
||||
await this.diffViewProvider.reset()
|
||||
break
|
||||
}
|
||||
}
|
||||
case "read_file": {
|
||||
const relPath: string | undefined = block.params.path
|
||||
const sharedMessageProps: ClineSayTool = {
|
||||
|
||||
Reference in New Issue
Block a user