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 { parseMentions } from "./utils/context-mentions"
|
||||
import { UrlContentFetcher } from "./utils/UrlContentFetcher"
|
||||
import { diagnosticsToProblemsString, getNewDiagnostics } from "./utils/diagnostics"
|
||||
|
||||
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.
|
||||
@@ -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
|
||||
if (fileExists) {
|
||||
originalContent = await fs.readFile(absolutePath, "utf-8")
|
||||
@@ -1037,6 +1041,27 @@ export class ClaudeDev {
|
||||
|
||||
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 })
|
||||
|
||||
// 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 [
|
||||
false,
|
||||
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 {
|
||||
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) {
|
||||
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 { extractTextFromFile } from "./extract-text"
|
||||
import { isBinaryFile } from "isbinaryfile"
|
||||
import { diagnosticsToProblemsString } from "./diagnostics"
|
||||
|
||||
export function openMention(mention?: string): void {
|
||||
if (!mention) {
|
||||
@@ -93,8 +94,8 @@ export async function parseMentions(text: string, cwd: string, urlContentFetcher
|
||||
}
|
||||
} else if (mention === "problems") {
|
||||
try {
|
||||
const diagnostics = await getWorkspaceDiagnostics(cwd)
|
||||
parsedText += `\n\n<workspace_diagnostics>\n${diagnostics}\n</workspace_diagnostics>`
|
||||
const problems = getWorkspaceProblems(cwd)
|
||||
parsedText += `\n\n<workspace_diagnostics>\n${problems}\n</workspace_diagnostics>`
|
||||
} catch (error) {
|
||||
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()
|
||||
|
||||
let diagnosticsDetails = ""
|
||||
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) {
|
||||
const result = diagnosticsToProblemsString(diagnostics, cwd)
|
||||
if (!result) {
|
||||
return "No errors or warnings detected."
|
||||
}
|
||||
|
||||
return diagnosticsDetails.trim()
|
||||
return result
|
||||
}
|
||||
|
||||
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