@@ -25,7 +25,9 @@ import type {
2525 ComposerContext ,
2626 ComposerGenerateCommitMessageEventData ,
2727 ComposerGenerateCommitsEventData ,
28+ ComposerHunk ,
2829 ComposerLoadedErrorData ,
30+ ComposerSafetyState ,
2931 ComposerTelemetryEvent ,
3032 FinishAndCommitParams ,
3133 GenerateCommitMessageParams ,
@@ -78,6 +80,7 @@ import {
7880import type { ComposerWebviewShowingArgs } from './registration' ;
7981import {
8082 convertToComposerDiffInfo ,
83+ createCombinedDiffForCommit ,
8184 createHunksFromDiffs ,
8285 createSafetyState ,
8386 getWorkingTreeDiffs ,
@@ -98,6 +101,10 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
98101 private _repositorySubscription ?: Disposable ;
99102 private _currentRepository ?: Repository ;
100103
104+ // Hunk map and safety state
105+ private _hunks : ComposerHunk [ ] = [ ] ;
106+ private _safetyState : ComposerSafetyState ;
107+
101108 // Telemetry context - tracks composer-specific data for getTelemetryContext
102109 private _context : ComposerContext ;
103110
@@ -111,6 +118,15 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
111118 this . container . ai . onDidChangeModel ( this . onAIModelChanged , this ) ,
112119 ) ;
113120 this . _context = { ...baseContext } ;
121+ this . _safetyState = {
122+ repoPath : '' ,
123+ headSha : '' ,
124+ hashes : {
125+ staged : null ,
126+ unstaged : null ,
127+ unified : null ,
128+ } ,
129+ } ;
114130 }
115131
116132 dispose ( ) : void {
@@ -308,7 +324,8 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
308324 // Allow composer to open with no changes - we'll handle this in the UI
309325 const hasChanges = Boolean ( staged ?. contents || unstaged ?. contents ) ;
310326
311- const { hunkMap, hunks } = createHunksFromDiffs ( staged ?. contents , unstaged ?. contents ) ;
327+ const hunks = createHunksFromDiffs ( staged ?. contents , unstaged ?. contents ) ;
328+ this . _hunks = hunks ;
312329
313330 const baseCommit = getSettledValue ( commitResult ) ;
314331 if ( baseCommit == null ) {
@@ -359,6 +376,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
359376
360377 // Create safety state snapshot for validation
361378 const safetyState = await createSafetyState ( repo , diffs , baseCommit . sha ) ;
379+ this . _safetyState = safetyState ;
362380
363381 const aiEnabled = this . getAiEnabled ( ) ;
364382 const aiModel = await this . container . ai . getModel (
@@ -395,15 +413,13 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
395413 return {
396414 ...this . initialState ,
397415 hunks : hunks ,
398- hunkMap : hunkMap ,
399416 baseCommit : {
400417 sha : baseCommit . sha ,
401418 message : baseCommit . message ?? '' ,
402419 repoName : repo . name ,
403420 branchName : currentBranch . name ,
404421 } ,
405422 commits : commits ,
406- safetyState : safetyState ,
407423 aiEnabled : aiEnabled ,
408424 ai : {
409425 model : aiModel ,
@@ -533,9 +549,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
533549 await this . host . notify ( DidReloadComposerNotification , {
534550 hunks : composerData . hunks ,
535551 commits : composerData . commits ,
536- hunkMap : composerData . hunkMap ,
537552 baseCommit : composerData . baseCommit ,
538- safetyState : composerData . safetyState ,
539553 loadingError : composerData . loadingError ,
540554 hasChanges : composerData . hasChanges ,
541555 repositoryState : composerData . repositoryState ,
@@ -788,14 +802,10 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
788802 await this . host . notify ( DidStartGeneratingNotification , undefined ) ;
789803
790804 // Transform the data for the AI service
791- const hunks = params . hunks . map ( hunk => ( {
792- index : hunk . index ,
793- fileName : hunk . fileName ,
794- diffHeader : hunk . diffHeader || `diff --git a/${ hunk . fileName } b/${ hunk . fileName } ` ,
795- hunkHeader : hunk . hunkHeader ,
796- content : hunk . content ,
797- source : hunk . source ,
798- } ) ) ;
805+ const hunks = [ ] ;
806+ for ( const index of params . hunkIndices ) {
807+ hunks . push ( { ...this . _hunks . find ( m => m . index === index ) ! , assigned : true } ) ;
808+ }
799809
800810 const existingCommits = params . commits . map ( commit => ( {
801811 id : commit . id ,
@@ -808,7 +818,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
808818 const result = await this . container . ai . generateCommits (
809819 hunks ,
810820 existingCommits ,
811- params . hunkMap ,
821+ this . _hunks . map ( m => ( { index : m . index , hunkHeader : m . hunkHeader } ) ) ,
812822 { source : 'composer' , correlationId : this . host . instanceId } ,
813823 {
814824 cancellation : this . _generateCommitsCancellation . token ,
@@ -942,9 +952,28 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
942952 // Notify webview that commit message generation is starting
943953 await this . host . notify ( DidStartGeneratingCommitMessageNotification , { commitId : params . commitId } ) ;
944954
955+ // Create combined diff for the commit
956+ const { patch } = createCombinedDiffForCommit (
957+ this . _hunks . filter ( h => params . commitHunkIndices . includes ( h . index ) ) ,
958+ ) ;
959+ if ( ! patch ) {
960+ this . _context . operations . generateCommitMessage . errorCount ++ ;
961+ this . _context . errors . operation . count ++ ;
962+ // Send error notification for failure (not cancellation)
963+ this . sendTelemetryEvent ( 'composer/action/generateCommitMessage/failed' , {
964+ ...eventData ,
965+ 'failure.reason' : 'error' ,
966+ 'failure.error.message' : 'Failed to create diff for commit' ,
967+ } ) ;
968+ await this . host . notify ( DidErrorAIOperationNotification , {
969+ operation : 'generate commit message' ,
970+ error : 'Failed to create diff for commit' ,
971+ } ) ;
972+ }
973+
945974 // Call the AI service to generate commit message
946975 const result = await this . container . ai . generateCommitMessage (
947- params . diff ,
976+ patch ,
948977 { source : 'composer' , correlationId : this . host . instanceId } ,
949978 {
950979 cancellation : this . _generateCommitMessageCancellation . token ,
@@ -1032,7 +1061,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
10321061 await this . host . notify ( DidStartCommittingNotification , undefined ) ;
10331062
10341063 // Get the specific repository from the safety state
1035- const repo = this . container . git . getRepository ( params . safetyState . repoPath ) ;
1064+ const repo = this . container . git . getRepository ( this . _safetyState . repoPath ) ;
10361065 if ( ! repo ) {
10371066 // Clear loading state and show safety error
10381067 await this . host . notify ( DidFinishCommittingNotification , undefined ) ;
@@ -1049,13 +1078,18 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
10491078 return ;
10501079 }
10511080
1052- // Extract hunk sources for smart validation
1053- const hunksBeingCommitted = params . hunks . filter ( hunk =>
1081+ const commitHunkIndices = params . commits . flatMap ( c => c . hunkIndices ) ;
1082+ const hunks : ComposerHunk [ ] = [ ] ;
1083+ for ( const hunk of commitHunkIndices ) {
1084+ hunks . push ( { ...this . _hunks . find ( m => m . index === hunk ) ! , assigned : true } ) ;
1085+ }
1086+
1087+ const hunksBeingCommitted = hunks . filter ( hunk =>
10541088 params . commits . some ( c => c . hunkIndices . includes ( hunk . index ) ) ,
10551089 ) ;
10561090
10571091 // Validate repository safety state before proceeding
1058- const validation = await validateSafetyState ( repo , params . safetyState , hunksBeingCommitted ) ;
1092+ const validation = await validateSafetyState ( repo , this . _safetyState , hunksBeingCommitted ) ;
10591093 if ( ! validation . isValid ) {
10601094 // Clear loading state and show safety error
10611095 await this . host . notify ( DidFinishCommittingNotification , undefined ) ;
@@ -1073,8 +1107,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
10731107 return ;
10741108 }
10751109
1076- // Convert composer data to ComposerDiffInfo format
1077- const diffInfo = convertToComposerDiffInfo ( params . commits , params . hunks ) ;
1110+ const diffInfo = convertToComposerDiffInfo ( params . commits , hunks ) ;
10781111 const svc = this . container . git . getRepositoryService ( repo . path ) ;
10791112 if ( ! svc ) {
10801113 this . _context . errors . operation . count ++ ;
@@ -1120,7 +1153,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
11201153
11211154 if (
11221155 ! validateResultingDiff (
1123- params . safetyState ,
1156+ this . _safetyState ,
11241157 await sha256 ( resultingDiff ) ,
11251158 this . _context . diff . unstagedIncluded ,
11261159 )
0 commit comments