@@ -8,26 +8,38 @@ import { TelemetryEventName, type ProviderSettings } from "@roo-code/types"
88
99import { GenerateMessageParams , PromptOptions , ProgressUpdate } from "./types/core"
1010
11+ /** Provides the extension settings needed to generate commit messages. */
1112export interface CommitMessageContextProxy {
13+ /** Whether the underlying extension configuration is ready to read. */
1214 isInitialized : boolean
15+ /** Returns the active provider settings used as the default generation profile. */
1316 getProviderSettings ( ) : ProviderSettings
17+ /** Reads a persisted extension setting by key. */
1418 getValue ( key : any ) : unknown
1519}
1620
21+ /** Overrides used to isolate commit message generation in tests and integrations. */
1722export interface CommitMessageGeneratorDependencies {
23+ /** Supplies the context proxy that owns provider settings and user configuration. */
1824 getContextProxy ?: ( ) => CommitMessageContextProxy
25+ /** Completes the prepared commit-message prompt with the selected provider. */
1926 completePrompt ?: ( apiConfiguration : ProviderSettings , promptText : string ) => Promise < string >
27+ /** Adds repository-specific custom instructions to the commit-message prompt. */
2028 addCustomInstructions ?: typeof defaultAddCustomInstructions
29+ /** Records successful commit-message generation telemetry. */
2130 captureGenerated ?: ( ) => void
31+ /** Receives non-fatal generation warnings, such as profile fallback failures. */
2232 logger ?: Pick < Console , "warn" >
2333}
2434
35+ /** Builds prompts, selects provider settings, and extracts AI generated commit messages. */
2536export class CommitMessageGenerator {
2637 private readonly providerSettingsManager : ProviderSettingsManager
2738 private readonly dependencies : Required < CommitMessageGeneratorDependencies >
2839 private previousGitContext : string | null = null
2940 private previousCommitMessage : string | null = null
3041
42+ /** Creates a generator using the provider settings manager and optional test seams. */
3143 constructor (
3244 providerSettingsManager : ProviderSettingsManager ,
3345 dependencies : CommitMessageGeneratorDependencies = { } ,
@@ -44,10 +56,13 @@ export class CommitMessageGenerator {
4456 }
4557 }
4658
59+ /** Generates a commit message for the supplied Git context. */
4760 async generateMessage ( params : GenerateMessageParams ) : Promise < string > {
4861 const { gitContext, onProgress } = params
4962
5063 try {
64+ this . validateGitContext ( gitContext )
65+
5166 onProgress ?.( {
5267 message : "Generating commit message..." ,
5368 percentage : 75 ,
@@ -72,6 +87,7 @@ export class CommitMessageGenerator {
7287 }
7388 }
7489
90+ /** Creates the final model prompt, including custom and regeneration instructions. */
7591 async buildPrompt ( gitContext : string , options : PromptOptions , workspacePath : string ) : Promise < string > {
7692 const { customSupportPrompts = { } , previousContext, previousMessage } = options
7793
@@ -128,6 +144,7 @@ FINAL REMINDER: Your message MUST be COMPLETELY DIFFERENT from the previous mess
128144 }
129145 }
130146
147+ /** Calls the configured AI provider and returns the cleaned commit message text. */
131148 private async callAIForCommitMessage (
132149 gitContextString : string ,
133150 workspacePath : string ,
@@ -190,9 +207,34 @@ FINAL REMINDER: Your message MUST be COMPLETELY DIFFERENT from the previous mess
190207 return this . extractCommitMessage ( response )
191208 }
192209
210+ /** Throws when there is no meaningful Git change data to describe. */
211+ private validateGitContext ( gitContext : string ) : void {
212+ if ( ! this . hasGitChanges ( gitContext ) ) {
213+ throw new Error ( "No changes to generate a commit message for" )
214+ }
215+ }
216+
217+ /** Detects whether collected Git context includes at least one changed file. */
218+ private hasGitChanges ( gitContext : string ) : boolean {
219+ const normalizedContext = gitContext . trim ( )
220+
221+ if ( ! normalizedContext || normalizedContext . includes ( "(No changes matched selection)" ) ) {
222+ return false
223+ }
224+
225+ return (
226+ / ^ d i f f - - g i t / m. test ( normalizedContext ) ||
227+ / ^ B i n a r y f i l e / m. test ( normalizedContext ) ||
228+ / ^ ( A d d e d | M o d i f i e d | D e l e t e d | R e n a m e d | C o p i e d | U p d a t e d | U n t r a c k e d | U n k n o w n ) \( ( s t a g e d | u n s t a g e d ) \) : .+ $ / m. test (
229+ normalizedContext ,
230+ )
231+ )
232+ }
233+
234+ /** Cleans formatting wrappers from an AI response without enforcing message style. */
193235 private extractCommitMessage ( response : string ) : string {
194236 const cleaned = response . trim ( )
195- const withoutCodeBlocks = cleaned . replace ( / ` ` ` [ a - z ] * \n | ` ` ` / g , "" )
237+ const withoutCodeBlocks = cleaned . replace ( / ^ ` ` ` [ a - z A - Z 0 - 9 _ - ] * \r ? \n / , "" ) . replace ( / \r ? \n ` ` ` $ / , "" )
196238 const withoutQuotes = withoutCodeBlocks . replace ( / ^ [ " ' ` ] | [ " ' ` ] $ / g, "" )
197239 return withoutQuotes . trim ( )
198240 }
0 commit comments