From c7ce9c0d239013eb9cd2937cfb2dae41ce6cf748 Mon Sep 17 00:00:00 2001 From: Ayyan Shaikh <72731349+Ayyanaruto@users.noreply.github.com> Date: Thu, 16 Oct 2025 03:31:39 +0000 Subject: [PATCH 1/2] feat: Add command to generate commit messages from staged changes --- extensions/vscode/package.json | 14 ++++ extensions/vscode/src/commands.ts | 125 ++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json index b52a0fa31a..6e0044d054 100644 --- a/extensions/vscode/package.json +++ b/extensions/vscode/package.json @@ -333,6 +333,13 @@ "title": "Focus Continue Chat", "group": "Continue" }, + { + "command": "continue.generateCommitMessage", + "category": "Continue", + "title": "Generate Commit Message", + "icon": "media/icon.png", + "group": "Continue" + }, { "command": "continue.enterEnterpriseLicenseKey", "category": "Continue", @@ -598,6 +605,13 @@ "command": "continue.debugTerminal", "group": "navigation@top" } + ], + "scm/title": [ + { + "command": "continue.generateCommitMessage", + "group": "navigation", + "when": "scmProvider == git" + } ] }, "viewsContainers": { diff --git a/extensions/vscode/src/commands.ts b/extensions/vscode/src/commands.ts index c9374ea9a1..85a3d38ffc 100644 --- a/extensions/vscode/src/commands.ts +++ b/extensions/vscode/src/commands.ts @@ -22,6 +22,7 @@ import * as YAML from "yaml"; import { convertJsonToYamlConfig } from "../../../packages/config-yaml/dist"; +import { stripImages } from "core/util/messageContent"; import { getAutocompleteStatusBarDescription, getAutocompleteStatusBarTitle, @@ -38,6 +39,7 @@ import { ContinueConsoleWebviewViewProvider } from "./ContinueConsoleWebviewView import { ContinueGUIWebviewViewProvider } from "./ContinueGUIWebviewViewProvider"; import { processDiff } from "./diff/processDiff"; import { VerticalDiffManager } from "./diff/vertical/manager"; +import { GitExtension } from "./otherExtensions/git"; import EditDecorationManager from "./quickEdit/EditDecorationManager"; import { QuickEdit, QuickEditShowParams } from "./quickEdit/QuickEditQuickPick"; import { @@ -883,6 +885,129 @@ const getCommandsMap: ( "editor.action.inlineSuggest.trigger", ); }, + "continue.generateCommitMessage": async () => { + captureCommandTelemetry("generateCommitMessage"); + + try { + const gitExtension = + vscode.extensions.getExtension("vscode.git"); + + if (!gitExtension?.isActive) { + void vscode.window.showErrorMessage( + "Git extension is not active. Please ensure Git is installed and the repository is initialized.", + ); + return; + } + + const git = gitExtension.exports.getAPI(1); + const repositories = git.repositories; + + if (repositories.length === 0) { + void vscode.window.showErrorMessage( + "No Git repositories found in the workspace.", + ); + return; + } + + // Handle multiple repositories - let user pick or use the active one + let repository = repositories[0]; + if (repositories.length > 1) { + const activeEditor = vscode.window.activeTextEditor; + if (activeEditor) { + const activeRepo = git.getRepository(activeEditor.document.uri); + if (activeRepo) { + repository = activeRepo; + } + } + } + + const stagedDiff = await repository.diff(true); + if (!stagedDiff || stagedDiff.trim().length === 0) { + void vscode.window.showInformationMessage( + "No staged changes found. Please stage your changes first.", + ); + return; + } + + const { config } = await configHandler.loadConfig(); + if (!config) { + void vscode.window.showErrorMessage( + "Continue configuration not loaded.", + ); + return; + } + + const llm = config.selectedModelByRole.chat; + if (!llm) { + void vscode.window.showErrorMessage( + "No chat model selected. Please configure a model in Continue settings.", + ); + return; + } + + const originalValue = repository.inputBox.value; + if (originalValue && originalValue.trim().length > 0) { + const overwrite = await vscode.window.showWarningMessage( + "This will replace your current commit message. Continue?", + { modal: true }, + "Generate", + "Cancel", + ); + + if (overwrite !== "Generate") { + return; + } + } + + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: "Generating commit message...", + cancellable: true, + }, + async (progress, token) => { + const prompt = `${stagedDiff}\n\nGenerate a concise commit message for the above changes.\nRules:\n- Output ONLY the commit message as plain text.\n- Do NOT include code blocks, backticks, diffs, or markdown.\n- First line: single sentence, max 80 chars (summary).\n- Then two line breaks.\n- Then up to 5 bullet points ("- "), each <= 40 chars.\n- No quotes, no prefixes like 'Commit message:'.`; + + let generatedMessage = ""; + const abortController = new AbortController(); + + token.onCancellationRequested(() => { + abortController.abort(); + }); + + try { + for await (const chunk of llm.streamChat( + [{ role: "user", content: prompt }], + abortController.signal, + )) { + generatedMessage += stripImages(chunk.content) ?? ""; + progress.report({ message: "Generating..." }); + repository.inputBox.value = generatedMessage.trim(); + } + + // Focus the Source Control view so user can review and commit + await vscode.commands.executeCommand("workbench.view.scm"); + } catch (error) { + if (abortController.signal.aborted) { + repository.inputBox.value = originalValue; + // User cancelled - no need for a notification + } else { + console.error("Error generating commit message:", error); + void vscode.window.showErrorMessage( + `Failed to generate commit message: ${error instanceof Error ? error.message : String(error)}`, + ); + repository.inputBox.value = originalValue; + } + } + }, + ); + } catch (error) { + console.error("Error in generateCommitMessage:", error); + void vscode.window.showErrorMessage( + `Failed to generate commit message: ${error instanceof Error ? error.message : String(error)}`, + ); + } + }, }; }; From e03f56f2281d8def25f63803321d60ad6ce894b7 Mon Sep 17 00:00:00 2001 From: Ayyan Shaikh <72731349+Ayyanaruto@users.noreply.github.com> Date: Thu, 16 Oct 2025 03:45:07 +0000 Subject: [PATCH 2/2] fix: Remove redundant line setting inputBox value in command generation --- extensions/vscode/src/commands.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/extensions/vscode/src/commands.ts b/extensions/vscode/src/commands.ts index 85a3d38ffc..43ac3f799c 100644 --- a/extensions/vscode/src/commands.ts +++ b/extensions/vscode/src/commands.ts @@ -982,10 +982,8 @@ const getCommandsMap: ( )) { generatedMessage += stripImages(chunk.content) ?? ""; progress.report({ message: "Generating..." }); - repository.inputBox.value = generatedMessage.trim(); } - - // Focus the Source Control view so user can review and commit + repository.inputBox.value = generatedMessage.trim(); await vscode.commands.executeCommand("workbench.view.scm"); } catch (error) { if (abortController.signal.aborted) {