mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 12:21:13 -05:00
Send only new errors to claude after he applies an edit
This commit is contained in:
@@ -28,6 +28,7 @@ import { extractTextFromFile } from "./utils/extract-text"
|
|||||||
import { regexSearchFiles } from "./utils/ripgrep"
|
import { regexSearchFiles } from "./utils/ripgrep"
|
||||||
import { parseMentions } from "./utils/context-mentions"
|
import { parseMentions } from "./utils/context-mentions"
|
||||||
import { UrlContentFetcher } from "./utils/UrlContentFetcher"
|
import { UrlContentFetcher } from "./utils/UrlContentFetcher"
|
||||||
|
import { diagnosticsToProblemsString, getNewDiagnostics } from "./utils/diagnostics"
|
||||||
|
|
||||||
const SYSTEM_PROMPT =
|
const SYSTEM_PROMPT =
|
||||||
async () => `You are Claude Dev, a highly skilled software developer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.
|
async () => `You are Claude Dev, a highly skilled software developer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.
|
||||||
@@ -762,6 +763,9 @@ export class ClaudeDev {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get diagnostics before editing the file, we'll compare to diagnostics after editing to see if claude needs to fix anything
|
||||||
|
const preDiagnostics = vscode.languages.getDiagnostics()
|
||||||
|
|
||||||
let originalContent: string
|
let originalContent: string
|
||||||
if (fileExists) {
|
if (fileExists) {
|
||||||
originalContent = await fs.readFile(absolutePath, "utf-8")
|
originalContent = await fs.readFile(absolutePath, "utf-8")
|
||||||
@@ -1037,6 +1041,27 @@ export class ClaudeDev {
|
|||||||
|
|
||||||
await this.closeDiffViews()
|
await this.closeDiffViews()
|
||||||
|
|
||||||
|
/*
|
||||||
|
Getting diagnostics before and after the file edit is a better approach than
|
||||||
|
automatically tracking problems in real-time. This method ensures we only
|
||||||
|
report new problems that are a direct result of this specific edit.
|
||||||
|
Since these are new problems resulting from Claude's edit, we know they're
|
||||||
|
directly related to the work he's doing. This eliminates the risk of Claude
|
||||||
|
going off-task or getting distracted by unrelated issues, which was a problem
|
||||||
|
with the previous auto-debug approach. Some users' machines may be slow to
|
||||||
|
update diagnostics, so this approach provides a good balance between automation
|
||||||
|
and avoiding potential issues where Claude might get stuck in loops due to
|
||||||
|
outdated problem information. If no new problems show up by the time the user
|
||||||
|
accepts the changes, they can always debug later using the '@problems' mention.
|
||||||
|
This way, Claude only becomes aware of new problems resulting from his edits
|
||||||
|
and can address them accordingly. If problems don't change immediately after
|
||||||
|
applying a fix, Claude won't be notified, which is generally fine since the
|
||||||
|
initial fix is usually correct and it may just take time for linters to catch up.
|
||||||
|
*/
|
||||||
|
const postDiagnostics = vscode.languages.getDiagnostics()
|
||||||
|
const newProblems = diagnosticsToProblemsString(getNewDiagnostics(preDiagnostics, postDiagnostics), cwd) // will be empty string if no errors/warnings
|
||||||
|
const newProblemsMessage =
|
||||||
|
newProblems.length > 0 ? `\n\nNew problems detected after saving the file:\n${newProblems}` : ""
|
||||||
// await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { preview: false })
|
// await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { preview: false })
|
||||||
|
|
||||||
// If the edited content has different EOL characters, we don't want to show a diff with all the EOL differences.
|
// If the edited content has different EOL characters, we don't want to show a diff with all the EOL differences.
|
||||||
@@ -1056,11 +1081,16 @@ export class ClaudeDev {
|
|||||||
return [
|
return [
|
||||||
false,
|
false,
|
||||||
await this.formatToolResult(
|
await this.formatToolResult(
|
||||||
`The user made the following updates to your content:\n\n${userDiff}\n\nThe updated content, which includes both your original modifications and the user's additional edits, has been successfully saved to ${relPath}. Note this does not mean you need to re-write the file with the user's changes, they have already been applied to the file.`
|
`The user made the following updates to your content:\n\n${userDiff}\n\nThe updated content, which includes both your original modifications and the user's additional edits, has been successfully saved to ${relPath}. (Note this does not mean you need to re-write the file with the user's changes, as they have already been applied to the file.)${newProblemsMessage}`
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
return [false, await this.formatToolResult(`The content was successfully saved to ${relPath}.`)]
|
return [
|
||||||
|
false,
|
||||||
|
await this.formatToolResult(
|
||||||
|
`The content was successfully saved to ${relPath}.${newProblemsMessage}`
|
||||||
|
),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorString = `Error writing file: ${JSON.stringify(serializeError(error))}`
|
const errorString = `Error writing file: ${JSON.stringify(serializeError(error))}`
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { mentionRegexGlobal } from "../shared/context-mentions"
|
|||||||
import fs from "fs/promises"
|
import fs from "fs/promises"
|
||||||
import { extractTextFromFile } from "./extract-text"
|
import { extractTextFromFile } from "./extract-text"
|
||||||
import { isBinaryFile } from "isbinaryfile"
|
import { isBinaryFile } from "isbinaryfile"
|
||||||
|
import { diagnosticsToProblemsString } from "./diagnostics"
|
||||||
|
|
||||||
export function openMention(mention?: string): void {
|
export function openMention(mention?: string): void {
|
||||||
if (!mention) {
|
if (!mention) {
|
||||||
@@ -93,8 +94,8 @@ export async function parseMentions(text: string, cwd: string, urlContentFetcher
|
|||||||
}
|
}
|
||||||
} else if (mention === "problems") {
|
} else if (mention === "problems") {
|
||||||
try {
|
try {
|
||||||
const diagnostics = await getWorkspaceDiagnostics(cwd)
|
const problems = getWorkspaceProblems(cwd)
|
||||||
parsedText += `\n\n<workspace_diagnostics>\n${diagnostics}\n</workspace_diagnostics>`
|
parsedText += `\n\n<workspace_diagnostics>\n${problems}\n</workspace_diagnostics>`
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
parsedText += `\n\n<workspace_diagnostics>\nError fetching diagnostics: ${error.message}\n</workspace_diagnostics>`
|
parsedText += `\n\n<workspace_diagnostics>\nError fetching diagnostics: ${error.message}\n</workspace_diagnostics>`
|
||||||
}
|
}
|
||||||
@@ -168,28 +169,11 @@ async function getFileOrFolderContent(mentionPath: string, cwd: string): Promise
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getWorkspaceDiagnostics(cwd: string): Promise<string> {
|
function getWorkspaceProblems(cwd: string): string {
|
||||||
const diagnostics = vscode.languages.getDiagnostics()
|
const diagnostics = vscode.languages.getDiagnostics()
|
||||||
|
const result = diagnosticsToProblemsString(diagnostics, cwd)
|
||||||
let diagnosticsDetails = ""
|
if (!result) {
|
||||||
for (const [uri, fileDiagnostics] of diagnostics) {
|
|
||||||
const problems = fileDiagnostics.filter(
|
|
||||||
(d) => d.severity === vscode.DiagnosticSeverity.Error || d.severity === vscode.DiagnosticSeverity.Warning
|
|
||||||
)
|
|
||||||
if (problems.length > 0) {
|
|
||||||
diagnosticsDetails += `\nFile: ${path.relative(cwd, uri.fsPath)}`
|
|
||||||
for (const diagnostic of problems) {
|
|
||||||
let severity = diagnostic.severity === vscode.DiagnosticSeverity.Error ? "Error" : "Warning"
|
|
||||||
const line = diagnostic.range.start.line + 1 // VSCode lines are 0-indexed
|
|
||||||
const source = diagnostic.source ? `${diagnostic.source} ` : ""
|
|
||||||
diagnosticsDetails += `\n- [${source}${severity}] Line ${line}: ${diagnostic.message}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!diagnosticsDetails) {
|
|
||||||
return "No errors or warnings detected."
|
return "No errors or warnings detected."
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
return diagnosticsDetails.trim()
|
|
||||||
}
|
}
|
||||||
|
|||||||
90
src/utils/diagnostics.ts
Normal file
90
src/utils/diagnostics.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import * as vscode from "vscode"
|
||||||
|
import * as path from "path"
|
||||||
|
import deepEqual from "fast-deep-equal"
|
||||||
|
|
||||||
|
export function getNewDiagnostics(
|
||||||
|
oldDiagnostics: [vscode.Uri, vscode.Diagnostic[]][],
|
||||||
|
newDiagnostics: [vscode.Uri, vscode.Diagnostic[]][]
|
||||||
|
): [vscode.Uri, vscode.Diagnostic[]][] {
|
||||||
|
const newProblems: [vscode.Uri, vscode.Diagnostic[]][] = []
|
||||||
|
const oldMap = new Map(oldDiagnostics)
|
||||||
|
|
||||||
|
for (const [uri, newDiags] of newDiagnostics) {
|
||||||
|
const oldDiags = oldMap.get(uri) || []
|
||||||
|
const newProblemsForUri = newDiags.filter((newDiag) => !oldDiags.some((oldDiag) => deepEqual(oldDiag, newDiag)))
|
||||||
|
|
||||||
|
if (newProblemsForUri.length > 0) {
|
||||||
|
newProblems.push([uri, newProblemsForUri])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newProblems
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage:
|
||||||
|
// const oldDiagnostics = // ... your old diagnostics array
|
||||||
|
// const newDiagnostics = // ... your new diagnostics array
|
||||||
|
// const newProblems = getNewDiagnostics(oldDiagnostics, newDiagnostics);
|
||||||
|
|
||||||
|
// Example usage with mocks:
|
||||||
|
//
|
||||||
|
// // Mock old diagnostics
|
||||||
|
// const oldDiagnostics: [vscode.Uri, vscode.Diagnostic[]][] = [
|
||||||
|
// [vscode.Uri.file("/path/to/file1.ts"), [
|
||||||
|
// new vscode.Diagnostic(new vscode.Range(0, 0, 0, 10), "Old error in file1", vscode.DiagnosticSeverity.Error)
|
||||||
|
// ]],
|
||||||
|
// [vscode.Uri.file("/path/to/file2.ts"), [
|
||||||
|
// new vscode.Diagnostic(new vscode.Range(5, 5, 5, 15), "Old warning in file2", vscode.DiagnosticSeverity.Warning)
|
||||||
|
// ]]
|
||||||
|
// ];
|
||||||
|
//
|
||||||
|
// // Mock new diagnostics
|
||||||
|
// const newDiagnostics: [vscode.Uri, vscode.Diagnostic[]][] = [
|
||||||
|
// [vscode.Uri.file("/path/to/file1.ts"), [
|
||||||
|
// new vscode.Diagnostic(new vscode.Range(0, 0, 0, 10), "Old error in file1", vscode.DiagnosticSeverity.Error),
|
||||||
|
// new vscode.Diagnostic(new vscode.Range(2, 2, 2, 12), "New error in file1", vscode.DiagnosticSeverity.Error)
|
||||||
|
// ]],
|
||||||
|
// [vscode.Uri.file("/path/to/file2.ts"), [
|
||||||
|
// new vscode.Diagnostic(new vscode.Range(5, 5, 5, 15), "Old warning in file2", vscode.DiagnosticSeverity.Warning)
|
||||||
|
// ]],
|
||||||
|
// [vscode.Uri.file("/path/to/file3.ts"), [
|
||||||
|
// new vscode.Diagnostic(new vscode.Range(1, 1, 1, 11), "New error in file3", vscode.DiagnosticSeverity.Error)
|
||||||
|
// ]]
|
||||||
|
// ];
|
||||||
|
//
|
||||||
|
// const newProblems = getNewProblems(oldDiagnostics, newDiagnostics);
|
||||||
|
//
|
||||||
|
// console.log("New problems:");
|
||||||
|
// for (const [uri, diagnostics] of newProblems) {
|
||||||
|
// console.log(`File: ${uri.fsPath}`);
|
||||||
|
// for (const diagnostic of diagnostics) {
|
||||||
|
// console.log(`- ${diagnostic.message} (${diagnostic.range.start.line}:${diagnostic.range.start.character})`);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Expected output:
|
||||||
|
// // New problems:
|
||||||
|
// // File: /path/to/file1.ts
|
||||||
|
// // - New error in file1 (2:2)
|
||||||
|
// // File: /path/to/file3.ts
|
||||||
|
// // - New error in file3 (1:1)
|
||||||
|
|
||||||
|
// will return empty string if no errors/warnings
|
||||||
|
export function diagnosticsToProblemsString(diagnostics: [vscode.Uri, vscode.Diagnostic[]][], cwd: string): string {
|
||||||
|
let result = ""
|
||||||
|
for (const [uri, fileDiagnostics] of diagnostics) {
|
||||||
|
const problems = fileDiagnostics.filter(
|
||||||
|
(d) => d.severity === vscode.DiagnosticSeverity.Error || d.severity === vscode.DiagnosticSeverity.Warning
|
||||||
|
)
|
||||||
|
if (problems.length > 0) {
|
||||||
|
result += `\n\n${path.relative(cwd, uri.fsPath)}`
|
||||||
|
for (const diagnostic of problems) {
|
||||||
|
let severity = diagnostic.severity === vscode.DiagnosticSeverity.Error ? "Error" : "Warning"
|
||||||
|
const line = diagnostic.range.start.line + 1 // VSCode lines are 0-indexed
|
||||||
|
const source = diagnostic.source ? `${diagnostic.source} ` : ""
|
||||||
|
result += `\n- [${source}${severity}] Line ${line}: ${diagnostic.message}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.trim()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user