Skip to content

Commit da31a23

Browse files
authored
Create a structure for different diff strategies, starting with unified (#55)
1 parent 39b51fa commit da31a23

File tree

13 files changed

+422
-129
lines changed

13 files changed

+422
-129
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Roo Cline Changelog
22

3+
## [2.1.15]
4+
5+
- Incorporate dbasclpy's [PR](https://github.com/RooVetGit/Roo-Cline/pull/54) to add support for gemini-exp-1206
6+
- Make it clear that diff editing is very experimental
7+
38
## [2.1.14]
49

510
- Fix bug where diffs were not being applied correctly and try Aider's [unified diff prompt](https://github.com/Aider-AI/aider/blob/3995accd0ca71cea90ef76d516837f8c2731b9fe/aider/coders/udiff_prompts.py#L75-L105)

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ A fork of Cline, an autonomous coding agent, with some added experimental config
44
- Auto-approval capabilities for commands, write, and browser operations
55
- Support for .clinerules per-project custom instructions
66
- Ability to run side-by-side with Cline
7+
- Code is unit-tested
78
- Support for playing sound effects
89
- Support for OpenRouter compression
9-
- Support for editing through diffs
10+
- Support for editing through diffs (very experimental)
11+
- Support for gemini-exp-1206
1012

1113
Here's an example of Roo-Cline autonomously creating a snake game with "Always approve write operations" and "Always approve browser actions" turned on:
1214

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"displayName": "Roo Cline",
44
"description": "A fork of Cline, an autonomous coding agent, with some added experimental configuration and automation features.",
55
"publisher": "RooVeterinaryInc",
6-
"version": "2.1.14",
6+
"version": "2.1.15",
77
"icon": "assets/icons/rocket.png",
88
"galleryBanner": {
99
"color": "#617A91",
@@ -147,7 +147,7 @@
147147
"pretest": "npm run compile-tests && npm run compile && npm run lint",
148148
"check-types": "tsc --noEmit",
149149
"lint": "eslint src --ext ts",
150-
"test": "vscode-test",
150+
"test": "jest",
151151
"install:all": "npm install && cd webview-ui && npm install",
152152
"start:webview": "cd webview-ui && npm run start",
153153
"build:webview": "cd webview-ui && npm run build",

src/api/providers/__tests__/openrouter.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ describe('OpenRouterHandler', () => {
3535
baseURL: 'https://openrouter.ai/api/v1',
3636
apiKey: mockOptions.openRouterApiKey,
3737
defaultHeaders: {
38-
'HTTP-Referer': 'https://cline.bot',
39-
'X-Title': 'Cline',
38+
'HTTP-Referer': 'https://github.com/RooVetGit/Roo-Cline',
39+
'X-Title': 'Roo-Cline',
4040
},
4141
})
4242
})

src/api/providers/openrouter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ export class OpenRouterHandler implements ApiHandler {
2727
baseURL: "https://openrouter.ai/api/v1",
2828
apiKey: this.options.openRouterApiKey,
2929
defaultHeaders: {
30-
"HTTP-Referer": "https://cline.bot", // Optional, for including your app on openrouter.ai rankings.
31-
"X-Title": "Cline", // Optional. Shows in rankings on openrouter.ai.
30+
"HTTP-Referer": "https://github.com/RooVetGit/Roo-Cline", // Optional, for including your app on openrouter.ai rankings.
31+
"X-Title": "Roo-Cline", // Optional. Shows in rankings on openrouter.ai.
3232
},
3333
})
3434
}

src/core/Cline.ts

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Anthropic } from "@anthropic-ai/sdk"
2-
import * as diff from "diff"
32
import cloneDeep from "clone-deep"
3+
import { DiffStrategy, getDiffStrategy, UnifiedDiffStrategy } from "./diff/DiffStrategy"
44
import delay from "delay"
55
import fs from "fs/promises"
66
import os from "os"
@@ -65,7 +65,7 @@ export class Cline {
6565
private browserSession: BrowserSession
6666
private didEditFile: boolean = false
6767
customInstructions?: string
68-
diffEnabled?: boolean
68+
diffStrategy?: DiffStrategy
6969

7070
apiConversationHistory: Anthropic.MessageParam[] = []
7171
clineMessages: ClineMessage[] = []
@@ -107,7 +107,9 @@ export class Cline {
107107
this.browserSession = new BrowserSession(provider.context)
108108
this.diffViewProvider = new DiffViewProvider(cwd)
109109
this.customInstructions = customInstructions
110-
this.diffEnabled = diffEnabled
110+
if (diffEnabled && this.api.getModel().id) {
111+
this.diffStrategy = getDiffStrategy(this.api.getModel().id)
112+
}
111113
if (historyItem) {
112114
this.taskId = historyItem.id
113115
this.resumeTaskFromHistory()
@@ -752,7 +754,7 @@ export class Cline {
752754
}
753755

754756
async *attemptApiRequest(previousApiReqIndex: number): ApiStream {
755-
const systemPrompt = await SYSTEM_PROMPT(cwd, this.api.getModel().info.supportsComputerUse ?? false, !!this.diffEnabled) + await addCustomInstructions(this.customInstructions ?? '', cwd)
757+
const systemPrompt = await SYSTEM_PROMPT(cwd, this.api.getModel().info.supportsComputerUse ?? false, this.diffStrategy) + await addCustomInstructions(this.customInstructions ?? '', cwd)
756758

757759
// 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
758760
if (previousApiReqIndex >= 0) {
@@ -1104,7 +1106,7 @@ export class Cline {
11041106

11051107
// Check for code omissions before proceeding
11061108
if (detectCodeOmission(this.diffViewProvider.originalContent || "", newContent)) {
1107-
if (this.diffEnabled) {
1109+
if (this.diffStrategy) {
11081110
await this.diffViewProvider.revertChanges()
11091111
pushToolResult(formatResponse.toolError(
11101112
"Content appears to be truncated. Found comments indicating omitted code (e.g., '// rest of code unchanged', '/* previous code */'). Please provide the complete file content without any omissions if possible, or otherwise use the 'apply_diff' tool to apply the diff to the original file."
@@ -1220,25 +1222,13 @@ export class Cline {
12201222
const originalContent = await fs.readFile(absolutePath, "utf-8")
12211223

12221224
// Apply the diff to the original content
1223-
let newContent = diff.applyPatch(originalContent, diffContent) as string | false
1225+
let newContent = this.diffStrategy?.applyDiff(originalContent, diffContent) ?? false
12241226
if (newContent === false) {
12251227
await this.say("error", `Error applying diff to file: ${absolutePath}`)
12261228
pushToolResult(`Error applying diff to file: ${absolutePath}`)
12271229
break
12281230
}
12291231

1230-
// Create a diff for display purposes
1231-
const diffRepresentation = diff
1232-
.diffLines(originalContent, newContent)
1233-
.map((part) => {
1234-
const prefix = part.added ? "+" : part.removed ? "-" : " "
1235-
return (part.value || "")
1236-
.split("\n")
1237-
.map((line) => (line ? prefix + line : ""))
1238-
.join("\n")
1239-
})
1240-
.join("")
1241-
12421232
// Show diff view before asking for approval
12431233
this.diffViewProvider.editType = "modify"
12441234
await this.diffViewProvider.open(relPath);
@@ -1247,7 +1237,7 @@ export class Cline {
12471237

12481238
const completeMessage = JSON.stringify({
12491239
...sharedMessageProps,
1250-
diff: diffRepresentation,
1240+
diff: diffContent,
12511241
} satisfies ClineSayTool)
12521242

12531243
const didApprove = await askApproval("tool", completeMessage)
@@ -2287,5 +2277,4 @@ export class Cline {
22872277

22882278
return `<environment_details>\n${details.trim()}\n</environment_details>`
22892279
}
2290-
}
2291-
2280+
}

src/core/diff/DiffStrategy.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { DiffStrategy } from './types'
2+
import { UnifiedDiffStrategy } from './strategies/unified'
3+
4+
/**
5+
* Get the appropriate diff strategy for the given model
6+
* @param model The name of the model being used (e.g., 'gpt-4', 'claude-3-opus')
7+
* @returns The appropriate diff strategy for the model
8+
*/
9+
export function getDiffStrategy(model: string): DiffStrategy {
10+
// For now, return UnifiedDiffStrategy for all models
11+
// This architecture allows for future optimizations based on model capabilities
12+
return new UnifiedDiffStrategy()
13+
}
14+
15+
export type { DiffStrategy }
16+
export { UnifiedDiffStrategy }

0 commit comments

Comments
 (0)