import * as vscode from "vscode"; import type { SfClient } from "./sf-client.js"; /** * Integrates with VS Code's diagnostic system: * - Reads diagnostics (errors/warnings) from the Problems panel and sends them to the agent * - Provides a DiagnosticCollection for the agent to surface its own findings */ export class SfDiagnosticBridge implements vscode.Disposable { private readonly collection: vscode.DiagnosticCollection; private disposables: vscode.Disposable[] = []; constructor(private readonly client: SfClient) { this.collection = vscode.languages.createDiagnosticCollection("sf"); this.disposables.push(this.collection); } /** * Read all diagnostics for the active file and send them to the agent * as a "fix these problems" prompt. */ async fixProblemsInFile(): Promise { const editor = vscode.window.activeTextEditor; if (!editor) { vscode.window.showWarningMessage("No active file to fix."); return; } const uri = editor.document.uri; const diagnostics = vscode.languages.getDiagnostics(uri); if (diagnostics.length === 0) { vscode.window.showInformationMessage("No problems found in this file."); return; } const fileName = vscode.workspace.asRelativePath(uri); const problemText = formatDiagnostics(fileName, diagnostics); const prompt = [ `Fix the following problems in \`${fileName}\`:`, "", problemText, "", "Fix all of these issues. Show me the changes.", ].join("\n"); await this.client.sendPrompt(prompt); } /** * Read all diagnostics across the workspace (errors only) and send * them to the agent as a "fix all errors" prompt. */ async fixAllProblems(): Promise { const allDiagnostics = vscode.languages.getDiagnostics(); const errorFiles: { fileName: string; diagnostics: vscode.Diagnostic[] }[] = []; for (const [uri, diagnostics] of allDiagnostics) { // Only include errors and warnings, skip hints/info const significant = diagnostics.filter( (d) => d.severity === vscode.DiagnosticSeverity.Error || d.severity === vscode.DiagnosticSeverity.Warning, ); if (significant.length > 0) { errorFiles.push({ fileName: vscode.workspace.asRelativePath(uri), diagnostics: significant, }); } } if (errorFiles.length === 0) { vscode.window.showInformationMessage( "No errors or warnings found in the workspace.", ); return; } // Cap at 20 files to avoid overwhelming the agent const capped = errorFiles.slice(0, 20); const totalProblems = capped.reduce( (sum, f) => sum + f.diagnostics.length, 0, ); const sections = capped.map((f) => formatDiagnostics(f.fileName, f.diagnostics), ); const prompt = [ `Fix the following ${totalProblems} problems across ${capped.length} file${capped.length > 1 ? "s" : ""}:`, "", ...sections, "", "Fix all of these issues.", ].join("\n"); await this.client.sendPrompt(prompt); } /** * Add a SF diagnostic (agent finding) to a file. * Can be used to surface agent review findings in the Problems panel. */ addFinding( uri: vscode.Uri, range: vscode.Range, message: string, severity: vscode.DiagnosticSeverity = vscode.DiagnosticSeverity.Warning, ): void { const existing = this.collection.get(uri) ?? []; const diagnostic = new vscode.Diagnostic(range, message, severity); diagnostic.source = "SF Agent"; this.collection.set(uri, [...existing, diagnostic]); } /** Clear all SF diagnostics */ clearFindings(): void { this.collection.clear(); } dispose(): void { for (const d of this.disposables) { d.dispose(); } } } function formatDiagnostics( fileName: string, diagnostics: vscode.Diagnostic[], ): string { const lines = [`**${fileName}**`]; for (const d of diagnostics) { const severity = severityLabel(d.severity); const line = d.range.start.line + 1; const col = d.range.start.character + 1; const source = d.source ? ` [${d.source}]` : ""; lines.push(` - ${severity} (line ${line}:${col}): ${d.message}${source}`); } return lines.join("\n"); } function severityLabel(severity: vscode.DiagnosticSeverity): string { switch (severity) { case vscode.DiagnosticSeverity.Error: return "Error"; case vscode.DiagnosticSeverity.Warning: return "Warning"; case vscode.DiagnosticSeverity.Information: return "Info"; case vscode.DiagnosticSeverity.Hint: return "Hint"; default: return "Unknown"; } }