Skip to content

Instantly share code, notes, and snippets.

@laiso
Last active March 3, 2025 06:21
Show Gist options
  • Save laiso/df56c55f2b7f0581fcfebed4751499a0 to your computer and use it in GitHub Desktop.
Save laiso/df56c55f2b7f0581fcfebed4751499a0 to your computer and use it in GitHub Desktop.
import * as vscode from 'vscode';
import * as fs from 'fs';
export function activate(context: vscode.ExtensionContext) {
const outputChannel = vscode.window.createOutputChannel('Print Debug Info');
const disposable = vscode.commands.registerCommand('min-cline.RunMain', async () => {
outputChannel.clear();
const diagnostics = vscode.languages.getDiagnostics();
const formattedDiagnostics = await formatDiagnosticsWithContent(diagnostics);
const [model] = await vscode.lm.selectChatModels({
vendor: 'copilot',
family: 'gpt-4o'
});
const prompt = [
vscode.LanguageModelChatMessage.User(
'Please generate SEARCH/REPLACE blocks to fix the following problems in the codebase:'
),
vscode.LanguageModelChatMessage.User(
formattedDiagnostics
)
];
const response = await model.sendRequest(prompt, {
tools: [{
name: "replace_in_file",
description: "Request to replace sections of content in an existing file using SEARCH/REPLACE blocks that define exact changes to specific parts of the file.",
inputSchema: {
type: "object",
properties: {
path: {
type: "string",
description: "The path of the file to modify (relative to the current working directory ${cwd.toPosix()})"
},
diff: {
type: "string",
description: "One or more SEARCH/REPLACE blocks following this exact format: <<<<<<< SEARCH [exact content to find] ======= [new content to replace with] >>>>>>> REPLACE"
}
},
required: ["path", "diff"]
}
}
]
});
const toolCallResults: { path: string, diff: string }[] = [];
for await (const chunk of response.stream) {
outputChannel.append('\n\n--- Tool Call Result ---\n');
outputChannel.append(`chunk.args.path: ${chunk.input.path}\n`);
outputChannel.append(`chunk.args.diff: ${chunk.input.diff}\n`);
outputChannel.append('------------------------\n\n');
toolCallResults.push({
path: chunk.input.path,
diff: chunk.input.diff
});
}
outputChannel.show();
if (toolCallResults.length > 0) {
const applyAction = 'Apply Changes';
const userChoice = await vscode.window.showInformationMessage(
'Do you want to apply the suggested changes?',
applyAction
);
if (userChoice === applyAction) {
let appliedCount = 0;
for (const result of toolCallResults) {
const success = await applyChanges(result.path, result.diff);
if (success) {
appliedCount++;
}
}
vscode.window.showInformationMessage(
`Applied changes to ${appliedCount} of ${toolCallResults.length} files.`
);
}
}
vscode.window.showInformationMessage('Task completed!');
});
context.subscriptions.push(disposable);
// --- Utility Functions ---
async function getFileContent(uri: vscode.Uri): Promise<string> {
try {
const document = await vscode.workspace.openTextDocument(uri);
return document.getText();
} catch (error) {
return `Unable to read file: ${error}`;
}
}
async function formatDiagnosticsWithContent(diagnostics: [vscode.Uri, vscode.Diagnostic[]][]): Promise<string> {
if (diagnostics.length === 0) {
return 'No diagnostics found.';
}
let result = '';
for (const [uri, fileDiagnostics] of diagnostics) {
if (fileDiagnostics.length === 0) {
continue;
}
result += `\nFile: ${uri.fsPath}\n`;
result += `${'-'.repeat(80)}\n`;
const content = await getFileContent(uri);
result += `Content:\n${content}\n\n`;
result += `Diagnostics:\n`;
result += `${'-'.repeat(80)}\n`;
for (const diagnostic of fileDiagnostics) {
const line = diagnostic.range.start.line + 1;
const column = diagnostic.range.start.character + 1;
const severity = getSeverityString(diagnostic.severity);
const message = diagnostic.message.replace(/\n/g, '\n ');
result += `[${severity}] Line ${line}, Column ${column}: ${message}\n`;
}
result += '\n';
}
return result;
}
function getSeverityString(severity: vscode.DiagnosticSeverity | undefined): 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';
}
}
async function applyChanges(filePath: string, diff: string): Promise<boolean> {
try {
if (!fs.existsSync(filePath)) {
vscode.window.showErrorMessage(`File not found: ${filePath}`);
return false;
}
const fileContent = fs.readFileSync(filePath, 'utf8');
const searchReplacePattern = /<<<<<<< SEARCH\n([\s\S]*?)=======\n([\s\S]*?)>>>>>>> REPLACE/g;
let matches;
let modifiedContent = fileContent;
while ((matches = searchReplacePattern.exec(diff)) !== null) {
const searchText = matches[1];
const replaceText = matches[2];
if (!modifiedContent.includes(searchText)) {
vscode.window.showWarningMessage(`Search text not found in ${filePath}`);
return false;
}
modifiedContent = modifiedContent.replace(searchText, replaceText);
}
fs.writeFileSync(filePath, modifiedContent, 'utf8');
const fileUri = vscode.Uri.file(filePath);
vscode.window.showTextDocument(fileUri);
return true;
} catch (error) {
vscode.window.showErrorMessage(`Error applying changes: ${error}`);
return false;
}
}
}
export function deactivate() { }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment