From c2b4b0545971721958ff5380bc81596724f8ebfb Mon Sep 17 00:00:00 2001
From: Matt Rubens
Date: Mon, 16 Dec 2024 10:24:08 -0500
Subject: [PATCH 1/2] Diff debugging
---
CHANGELOG.md | 4 ++
package-lock.json | 4 +-
package.json | 2 +-
src/core/Cline.ts | 3 +-
src/core/__tests__/Cline.test.ts | 3 +-
src/core/diff/DiffStrategy.ts | 4 +-
src/core/diff/strategies/search-replace.ts | 56 +++++++++--------
src/core/diff/types.ts | 5 ++
src/core/webview/ClineProvider.ts | 61 ++++++++++++-------
.../misc/__tests__/extract-text.test.ts | 32 ++++++++++
src/integrations/misc/extract-text.ts | 11 ++--
src/shared/ExtensionMessage.ts | 1 +
src/shared/WebviewMessage.ts | 1 +
.../src/components/settings/SettingsView.tsx | 22 ++++++-
.../src/context/ExtensionStateContext.tsx | 11 +++-
15 files changed, 157 insertions(+), 63 deletions(-)
create mode 100644 src/integrations/misc/__tests__/extract-text.test.ts
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 85023ec..eeba972 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Roo Cline Changelog
+## [2.2.11]
+
+- Added settings checkbox for verbose diff debugging
+
## [2.2.6 - 2.2.10]
- More fixes to search/replace diffs
diff --git a/package-lock.json b/package-lock.json
index 3806ccb..4e5c9e4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "roo-cline",
- "version": "2.2.10",
+ "version": "2.2.11",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "roo-cline",
- "version": "2.2.10",
+ "version": "2.2.11",
"dependencies": {
"@anthropic-ai/bedrock-sdk": "^0.10.2",
"@anthropic-ai/sdk": "^0.26.0",
diff --git a/package.json b/package.json
index 2b605d3..c0dd669 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"displayName": "Roo Cline",
"description": "A fork of Cline, an autonomous coding agent, with some added experimental configuration and automation features.",
"publisher": "RooVeterinaryInc",
- "version": "2.2.10",
+ "version": "2.2.11",
"icon": "assets/icons/rocket.png",
"galleryBanner": {
"color": "#617A91",
diff --git a/src/core/Cline.ts b/src/core/Cline.ts
index 5b190d7..e0f7c1b 100644
--- a/src/core/Cline.ts
+++ b/src/core/Cline.ts
@@ -97,6 +97,7 @@ export class Cline {
apiConfiguration: ApiConfiguration,
customInstructions?: string,
diffEnabled?: boolean,
+ debugDiffEnabled?: boolean,
task?: string,
images?: string[],
historyItem?: HistoryItem,
@@ -109,7 +110,7 @@ export class Cline {
this.diffViewProvider = new DiffViewProvider(cwd)
this.customInstructions = customInstructions
if (diffEnabled && this.api.getModel().id) {
- this.diffStrategy = getDiffStrategy(this.api.getModel().id)
+ this.diffStrategy = getDiffStrategy(this.api.getModel().id, debugDiffEnabled)
}
if (historyItem) {
this.taskId = historyItem.id
diff --git a/src/core/__tests__/Cline.test.ts b/src/core/__tests__/Cline.test.ts
index f50ed4f..041298f 100644
--- a/src/core/__tests__/Cline.test.ts
+++ b/src/core/__tests__/Cline.test.ts
@@ -278,7 +278,8 @@ describe('Cline', () => {
mockProvider,
mockApiConfig,
'custom instructions',
- false,
+ false, // diffEnabled
+ false, // debugDiffEnabled
'test task'
);
diff --git a/src/core/diff/DiffStrategy.ts b/src/core/diff/DiffStrategy.ts
index 355424e..c35ea83 100644
--- a/src/core/diff/DiffStrategy.ts
+++ b/src/core/diff/DiffStrategy.ts
@@ -6,10 +6,10 @@ import { SearchReplaceDiffStrategy } from './strategies/search-replace'
* @param model The name of the model being used (e.g., 'gpt-4', 'claude-3-opus')
* @returns The appropriate diff strategy for the model
*/
-export function getDiffStrategy(model: string): DiffStrategy {
+export function getDiffStrategy(model: string, debugEnabled?: boolean): DiffStrategy {
// For now, return SearchReplaceDiffStrategy for all models (with a fuzzy threshold of 0.9)
// This architecture allows for future optimizations based on model capabilities
- return new SearchReplaceDiffStrategy(0.9)
+ return new SearchReplaceDiffStrategy(0.9, debugEnabled)
}
export type { DiffStrategy }
diff --git a/src/core/diff/strategies/search-replace.ts b/src/core/diff/strategies/search-replace.ts
index a950b08..65a74dd 100644
--- a/src/core/diff/strategies/search-replace.ts
+++ b/src/core/diff/strategies/search-replace.ts
@@ -1,4 +1,7 @@
import { DiffStrategy, DiffResult } from "../types"
+import { addLineNumbers } from "../../../integrations/misc/extract-text"
+
+const BUFFER_LINES = 5; // Number of extra context lines to show before and after matches
function levenshteinDistance(a: string, b: string): number {
const matrix: number[][] = [];
@@ -48,10 +51,12 @@ function getSimilarity(original: string, search: string): number {
export class SearchReplaceDiffStrategy implements DiffStrategy {
private fuzzyThreshold: number;
+ public debugEnabled: boolean;
- constructor(fuzzyThreshold?: number) {
+ constructor(fuzzyThreshold?: number, debugEnabled?: boolean) {
// Default to exact matching (1.0) unless fuzzy threshold specified
this.fuzzyThreshold = fuzzyThreshold ?? 1.0;
+ this.debugEnabled = debugEnabled ?? false;
}
getToolDescription(cwd: string): string {
@@ -119,15 +124,11 @@ Your search/replace content here
// Extract the search and replace blocks
const match = diffContent.match(/<<<<<<< SEARCH\n([\s\S]*?)\n=======\n([\s\S]*?)\n>>>>>>> REPLACE/);
if (!match) {
- // Log detailed format information
- console.log('Invalid Diff Format Debug:', {
- expectedFormat: "<<<<<<< SEARCH\\n[search content]\\n=======\\n[replace content]\\n>>>>>>> REPLACE",
- tip: "Make sure to include both SEARCH and REPLACE sections with correct markers"
- });
+ const debugInfo = this.debugEnabled ? `\n\nDebug Info:\n- Expected Format: <<<<<<< SEARCH\\n[search content]\\n=======\\n[replace content]\\n>>>>>>> REPLACE\n- Tip: Make sure to include both SEARCH and REPLACE sections with correct markers` : '';
return {
success: false,
- error: "Invalid diff format - missing required SEARCH/REPLACE sections"
+ error: `Invalid diff format - missing required SEARCH/REPLACE sections${debugInfo}`
};
}
@@ -167,15 +168,11 @@ Your search/replace content here
const exactEndIndex = endLine - 1;
if (exactStartIndex < 0 || exactEndIndex >= originalLines.length) {
- // Log detailed debug information
- console.log('Invalid Line Range Debug:', {
- requestedRange: { start: startLine, end: endLine },
- fileBounds: { start: 1, end: originalLines.length }
- });
-
+ const debugInfo = this.debugEnabled ? `\n\nDebug Info:\n- Requested Range: lines ${startLine}-${endLine}\n- File Bounds: lines 1-${originalLines.length}` : '';
+
return {
success: false,
- error: `Line range ${startLine}-${endLine} is invalid (file has ${originalLines.length} lines)`,
+ error: `Line range ${startLine}-${endLine} is invalid (file has ${originalLines.length} lines)${debugInfo}`,
};
}
@@ -198,11 +195,12 @@ Your search/replace content here
if (startLine !== undefined || endLine !== undefined) {
// Convert to 0-based index and add buffer
+ const BUFFER_LINES = 5;
if (startLine !== undefined) {
- searchStartIndex = Math.max(0, startLine - 6);
+ searchStartIndex = Math.max(0, startLine - (BUFFER_LINES + 1));
}
if (endLine !== undefined) {
- searchEndIndex = Math.min(originalLines.length, endLine + 5);
+ searchEndIndex = Math.min(originalLines.length, endLine + BUFFER_LINES);
}
}
@@ -224,17 +222,27 @@ Your search/replace content here
// Require similarity to meet threshold
if (matchIndex === -1 || bestMatchScore < this.fuzzyThreshold) {
const searchChunk = searchLines.join('\n');
- // Log detailed debug information to console
- console.log('Search/Replace Debug Info:', {
- similarity: bestMatchScore,
- threshold: this.fuzzyThreshold,
- searchContent: searchChunk,
- bestMatch: bestMatchContent || undefined
- });
+ const originalContentSection = startLine !== undefined && endLine !== undefined
+ ? `\n\nOriginal Content:\n${addLineNumbers(
+ originalLines.slice(
+ Math.max(0, startLine - 1 - BUFFER_LINES),
+ Math.min(originalLines.length, endLine + BUFFER_LINES)
+ ).join('\n'),
+ Math.max(1, startLine - BUFFER_LINES)
+ )}`
+ : `\n\nOriginal Content:\n${addLineNumbers(originalLines.join('\n'))}`;
+ const bestMatchSection = bestMatchContent
+ ? `\n\nBest Match Found:\n${addLineNumbers(bestMatchContent, matchIndex + 1)}`
+ : `\n\nBest Match Found:\n(no match)`;
+
+ const debugInfo = this.debugEnabled ? `\n\nDebug Info:\n- Similarity Score: ${Math.floor(bestMatchScore * 100)}%\n- Required Threshold: ${Math.floor(this.fuzzyThreshold * 100)}%\n- Line Range: lines ${startLine}-${endLine}\n\nSearch Content:\n${searchChunk}${bestMatchSection}${originalContentSection}` : '';
+
+ const lineRange = startLine !== undefined || endLine !== undefined ?
+ ` at ${startLine !== undefined ? `start: ${startLine}` : 'start'} to ${endLine !== undefined ? `end: ${endLine}` : 'end'}` : '';
return {
success: false,
- error: `No sufficiently similar match found${startLine !== undefined ? ` near lines ${startLine}-${endLine}` : ''} (${Math.round(bestMatchScore * 100)}% similar, needs ${Math.round(this.fuzzyThreshold * 100)}%)`
+ error: `No sufficiently similar match found${lineRange} (${Math.floor(bestMatchScore * 100)}% similar, needs ${Math.floor(this.fuzzyThreshold * 100)}%)${debugInfo}`
};
}
diff --git a/src/core/diff/types.ts b/src/core/diff/types.ts
index 3957a1f..a662c47 100644
--- a/src/core/diff/types.ts
+++ b/src/core/diff/types.ts
@@ -13,6 +13,11 @@ export type DiffResult =
}};
export interface DiffStrategy {
+ /**
+ * Whether to enable detailed debug logging
+ */
+ debugEnabled?: boolean;
+
/**
* Get the tool description for this diff strategy
* @param cwd The current working directory
diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts
index e998332..3f17b06 100644
--- a/src/core/webview/ClineProvider.ts
+++ b/src/core/webview/ClineProvider.ts
@@ -67,6 +67,7 @@ type GlobalStateKey =
| "allowedCommands"
| "soundEnabled"
| "diffEnabled"
+ | "debugDiffEnabled"
| "alwaysAllowMcp"
export const GlobalFileNames = {
@@ -207,28 +208,11 @@ export class ClineProvider implements vscode.WebviewViewProvider {
async initClineWithTask(task?: string, images?: string[]) {
await this.clearTask()
- const {
- apiConfiguration,
- customInstructions,
- diffEnabled,
- } = await this.getState()
-
- this.cline = new Cline(
- this,
- apiConfiguration,
- customInstructions,
- diffEnabled,
- task,
- images
- )
- }
-
- async initClineWithHistoryItem(historyItem: HistoryItem) {
- await this.clearTask()
- const {
- apiConfiguration,
- customInstructions,
+ const {
+ apiConfiguration,
+ customInstructions,
diffEnabled,
+ debugDiffEnabled,
} = await this.getState()
this.cline = new Cline(
@@ -236,6 +220,27 @@ export class ClineProvider implements vscode.WebviewViewProvider {
apiConfiguration,
customInstructions,
diffEnabled,
+ debugDiffEnabled,
+ task,
+ images
+ )
+ }
+
+ async initClineWithHistoryItem(historyItem: HistoryItem) {
+ await this.clearTask()
+ const {
+ apiConfiguration,
+ customInstructions,
+ diffEnabled,
+ debugDiffEnabled,
+ } = await this.getState()
+
+ this.cline = new Cline(
+ this,
+ apiConfiguration,
+ customInstructions,
+ diffEnabled,
+ debugDiffEnabled,
undefined,
undefined,
historyItem,
@@ -597,6 +602,11 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.updateGlobalState("diffEnabled", diffEnabled)
await this.postStateToWebview()
break
+ case "debugDiffEnabled":
+ const debugDiffEnabled = message.bool ?? false
+ await this.updateGlobalState("debugDiffEnabled", debugDiffEnabled)
+ await this.postStateToWebview()
+ break
}
},
null,
@@ -923,6 +933,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
alwaysAllowMcp,
soundEnabled,
diffEnabled,
+ debugDiffEnabled,
taskHistory,
} = await this.getState()
@@ -946,6 +957,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
.sort((a, b) => b.ts - a.ts),
soundEnabled: soundEnabled ?? false,
diffEnabled: diffEnabled ?? false,
+ debugDiffEnabled: debugDiffEnabled ?? false,
shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId,
allowedCommands,
}
@@ -1040,6 +1052,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
allowedCommands,
soundEnabled,
diffEnabled,
+ debugDiffEnabled,
] = await Promise.all([
this.getGlobalState("apiProvider") as Promise,
this.getGlobalState("apiModelId") as Promise,
@@ -1077,6 +1090,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
this.getGlobalState("allowedCommands") as Promise,
this.getGlobalState("soundEnabled") as Promise,
this.getGlobalState("diffEnabled") as Promise,
+ this.getGlobalState("debugDiffEnabled") as Promise,
])
let apiProvider: ApiProvider
@@ -1130,8 +1144,9 @@ export class ClineProvider implements vscode.WebviewViewProvider {
alwaysAllowMcp: alwaysAllowMcp ?? false,
taskHistory,
allowedCommands,
- soundEnabled,
- diffEnabled,
+ soundEnabled: soundEnabled ?? false,
+ diffEnabled: diffEnabled ?? false,
+ debugDiffEnabled: debugDiffEnabled ?? false,
}
}
diff --git a/src/integrations/misc/__tests__/extract-text.test.ts b/src/integrations/misc/__tests__/extract-text.test.ts
new file mode 100644
index 0000000..89adbb1
--- /dev/null
+++ b/src/integrations/misc/__tests__/extract-text.test.ts
@@ -0,0 +1,32 @@
+import { addLineNumbers } from '../extract-text';
+
+describe('addLineNumbers', () => {
+ it('should add line numbers starting from 1 by default', () => {
+ const input = 'line 1\nline 2\nline 3';
+ const expected = '1 | line 1\n2 | line 2\n3 | line 3';
+ expect(addLineNumbers(input)).toBe(expected);
+ });
+
+ it('should add line numbers starting from specified line number', () => {
+ const input = 'line 1\nline 2\nline 3';
+ const expected = '10 | line 1\n11 | line 2\n12 | line 3';
+ expect(addLineNumbers(input, 10)).toBe(expected);
+ });
+
+ it('should handle empty content', () => {
+ expect(addLineNumbers('')).toBe('1 | ');
+ expect(addLineNumbers('', 5)).toBe('5 | ');
+ });
+
+ it('should handle single line content', () => {
+ expect(addLineNumbers('single line')).toBe('1 | single line');
+ expect(addLineNumbers('single line', 42)).toBe('42 | single line');
+ });
+
+ it('should pad line numbers based on the highest line number', () => {
+ const input = 'line 1\nline 2';
+ // When starting from 99, highest line will be 100, so needs 3 spaces padding
+ const expected = ' 99 | line 1\n100 | line 2';
+ expect(addLineNumbers(input, 99)).toBe(expected);
+ });
+});
\ No newline at end of file
diff --git a/src/integrations/misc/extract-text.ts b/src/integrations/misc/extract-text.ts
index 3f9ff1c..576194c 100644
--- a/src/integrations/misc/extract-text.ts
+++ b/src/integrations/misc/extract-text.ts
@@ -53,15 +53,12 @@ async function extractTextFromIPYNB(filePath: string): Promise {
return addLineNumbers(extractedText)
}
-
-export function addLineNumbers(content: string): string {
+export function addLineNumbers(content: string, startLine: number = 1): string {
const lines = content.split('\n')
- const maxLineNumberWidth = String(lines.length).length
+ const maxLineNumberWidth = String(startLine + lines.length - 1).length
return lines
.map((line, index) => {
- const lineNumber = String(index + 1).padStart(maxLineNumberWidth, ' ')
+ const lineNumber = String(startLine + index).padStart(maxLineNumberWidth, ' ')
return `${lineNumber} | ${line}`
}).join('\n')
-}
-
-
+}
\ No newline at end of file
diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts
index 608b5e5..b9ba21f 100644
--- a/src/shared/ExtensionMessage.ts
+++ b/src/shared/ExtensionMessage.ts
@@ -52,6 +52,7 @@ export interface ExtensionState {
allowedCommands?: string[]
soundEnabled?: boolean
diffEnabled?: boolean
+ debugDiffEnabled?: boolean
}
export interface ClineMessage {
diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts
index 31802b9..d4377ca 100644
--- a/src/shared/WebviewMessage.ts
+++ b/src/shared/WebviewMessage.ts
@@ -33,6 +33,7 @@ export interface WebviewMessage {
| "playSound"
| "soundEnabled"
| "diffEnabled"
+ | "debugDiffEnabled"
| "openMcpSettings"
| "restartMcpServer"
| "toggleToolAlwaysAllow"
diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx
index ffa45d0..28e42d3 100644
--- a/webview-ui/src/components/settings/SettingsView.tsx
+++ b/webview-ui/src/components/settings/SettingsView.tsx
@@ -31,6 +31,8 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
setSoundEnabled,
diffEnabled,
setDiffEnabled,
+ debugDiffEnabled,
+ setDebugDiffEnabled,
openRouterModels,
setAllowedCommands,
allowedCommands,
@@ -46,7 +48,10 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
setApiErrorMessage(apiValidationResult)
setModelIdErrorMessage(modelIdValidationResult)
if (!apiValidationResult && !modelIdValidationResult) {
- vscode.postMessage({ type: "apiConfiguration", apiConfiguration })
+ vscode.postMessage({
+ type: "apiConfiguration",
+ apiConfiguration
+ })
vscode.postMessage({ type: "customInstructions", text: customInstructions })
vscode.postMessage({ type: "alwaysAllowReadOnly", bool: alwaysAllowReadOnly })
vscode.postMessage({ type: "alwaysAllowWrite", bool: alwaysAllowWrite })
@@ -56,6 +61,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
vscode.postMessage({ type: "allowedCommands", commands: allowedCommands ?? [] })
vscode.postMessage({ type: "soundEnabled", bool: soundEnabled })
vscode.postMessage({ type: "diffEnabled", bool: diffEnabled })
+ vscode.postMessage({ type: "debugDiffEnabled", bool: debugDiffEnabled })
onDone()
}
}
@@ -324,6 +330,20 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
When enabled, Cline will play sound effects for notifications and events.
+
+
+
setDebugDiffEnabled(e.target.checked)}>
+ Debug diff operations
+
+
+ When enabled, Cline will show detailed debug information when applying diffs fails.
+
+
{IS_DEV && (
diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx
index f9690b6..4d0e97f 100644
--- a/webview-ui/src/context/ExtensionStateContext.tsx
+++ b/webview-ui/src/context/ExtensionStateContext.tsx
@@ -30,6 +30,7 @@ export interface ExtensionStateContextType extends ExtensionState {
setAllowedCommands: (value: string[]) => void
setSoundEnabled: (value: boolean) => void
setDiffEnabled: (value: boolean) => void
+ setDebugDiffEnabled: (value: boolean) => void
}
const ExtensionStateContext = createContext(undefined)
@@ -43,6 +44,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
allowedCommands: [],
soundEnabled: false,
diffEnabled: false,
+ debugDiffEnabled: false,
})
const [didHydrateState, setDidHydrateState] = useState(false)
const [showWelcome, setShowWelcome] = useState(false)
@@ -129,7 +131,10 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
openRouterModels,
mcpServers,
filePaths,
- setApiConfiguration: (value) => setState((prevState) => ({ ...prevState, apiConfiguration: value })),
+ setApiConfiguration: (value) => setState((prevState) => ({
+ ...prevState,
+ apiConfiguration: value
+ })),
setCustomInstructions: (value) => setState((prevState) => ({ ...prevState, customInstructions: value })),
setAlwaysAllowReadOnly: (value) => setState((prevState) => ({ ...prevState, alwaysAllowReadOnly: value })),
setAlwaysAllowWrite: (value) => setState((prevState) => ({ ...prevState, alwaysAllowWrite: value })),
@@ -140,6 +145,10 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
setAllowedCommands: (value) => setState((prevState) => ({ ...prevState, allowedCommands: value })),
setSoundEnabled: (value) => setState((prevState) => ({ ...prevState, soundEnabled: value })),
setDiffEnabled: (value) => setState((prevState) => ({ ...prevState, diffEnabled: value })),
+ setDebugDiffEnabled: (value) => setState((prevState) => ({
+ ...prevState,
+ debugDiffEnabled: value
+ })),
}
return {children}
From 61b21043452e299fa33204361206ba674cb558fd Mon Sep 17 00:00:00 2001
From: Matt Rubens
Date: Mon, 16 Dec 2024 12:22:23 -0500
Subject: [PATCH 2/2] Fix bug where start/end line not passed to diff
---
src/core/Cline.ts | 7 ++++++-
src/core/assistant-message/index.ts | 2 ++
src/core/diff/strategies/search-replace.ts | 17 ++++++++---------
3 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/src/core/Cline.ts b/src/core/Cline.ts
index e0f7c1b..e98be5e 100644
--- a/src/core/Cline.ts
+++ b/src/core/Cline.ts
@@ -1238,7 +1238,12 @@ export class Cline {
const originalContent = await fs.readFile(absolutePath, "utf-8")
// Apply the diff to the original content
- const diffResult = this.diffStrategy?.applyDiff(originalContent, diffContent) ?? {
+ const diffResult = this.diffStrategy?.applyDiff(
+ originalContent,
+ diffContent,
+ parseInt(block.params.start_line ?? ''),
+ parseInt(block.params.end_line ?? '')
+ ) ?? {
success: false,
error: "No diff strategy available"
}
diff --git a/src/core/assistant-message/index.ts b/src/core/assistant-message/index.ts
index d10967c..241f8c7 100644
--- a/src/core/assistant-message/index.ts
+++ b/src/core/assistant-message/index.ts
@@ -44,6 +44,8 @@ export const toolParamNames = [
"question",
"result",
"diff",
+ "start_line",
+ "end_line",
] as const
export type ToolParamName = (typeof toolParamNames)[number]
diff --git a/src/core/diff/strategies/search-replace.ts b/src/core/diff/strategies/search-replace.ts
index 65a74dd..2fbfe5b 100644
--- a/src/core/diff/strategies/search-replace.ts
+++ b/src/core/diff/strategies/search-replace.ts
@@ -162,12 +162,12 @@ Your search/replace content here
let bestMatchScore = 0;
let bestMatchContent = "";
- if (startLine !== undefined && endLine !== undefined) {
+ if (startLine && endLine) {
// Convert to 0-based index
const exactStartIndex = startLine - 1;
const exactEndIndex = endLine - 1;
- if (exactStartIndex < 0 || exactEndIndex >= originalLines.length) {
+ if (exactStartIndex < 0 || exactEndIndex >= originalLines.length || exactStartIndex > exactEndIndex) {
const debugInfo = this.debugEnabled ? `\n\nDebug Info:\n- Requested Range: lines ${startLine}-${endLine}\n- File Bounds: lines 1-${originalLines.length}` : '';
return {
@@ -193,13 +193,12 @@ Your search/replace content here
let searchStartIndex = 0;
let searchEndIndex = originalLines.length;
- if (startLine !== undefined || endLine !== undefined) {
+ if (startLine || endLine) {
// Convert to 0-based index and add buffer
- const BUFFER_LINES = 5;
- if (startLine !== undefined) {
+ if (startLine) {
searchStartIndex = Math.max(0, startLine - (BUFFER_LINES + 1));
}
- if (endLine !== undefined) {
+ if (endLine) {
searchEndIndex = Math.min(originalLines.length, endLine + BUFFER_LINES);
}
}
@@ -236,10 +235,10 @@ Your search/replace content here
? `\n\nBest Match Found:\n${addLineNumbers(bestMatchContent, matchIndex + 1)}`
: `\n\nBest Match Found:\n(no match)`;
- const debugInfo = this.debugEnabled ? `\n\nDebug Info:\n- Similarity Score: ${Math.floor(bestMatchScore * 100)}%\n- Required Threshold: ${Math.floor(this.fuzzyThreshold * 100)}%\n- Line Range: lines ${startLine}-${endLine}\n\nSearch Content:\n${searchChunk}${bestMatchSection}${originalContentSection}` : '';
+ const debugInfo = this.debugEnabled ? `\n\nDebug Info:\n- Similarity Score: ${Math.floor(bestMatchScore * 100)}%\n- Required Threshold: ${Math.floor(this.fuzzyThreshold * 100)}%\n- Search Range: ${startLine && endLine ? `lines ${startLine}-${endLine}` : 'start to end'}\n\nSearch Content:\n${searchChunk}${bestMatchSection}${originalContentSection}` : '';
- const lineRange = startLine !== undefined || endLine !== undefined ?
- ` at ${startLine !== undefined ? `start: ${startLine}` : 'start'} to ${endLine !== undefined ? `end: ${endLine}` : 'end'}` : '';
+ const lineRange = startLine || endLine ?
+ ` at ${startLine ? `start: ${startLine}` : 'start'} to ${endLine ? `end: ${endLine}` : 'end'}` : '';
return {
success: false,
error: `No sufficiently similar match found${lineRange} (${Math.floor(bestMatchScore * 100)}% similar, needs ${Math.floor(this.fuzzyThreshold * 100)}%)${debugInfo}`