@@ -25,7 +25,9 @@ import type {
25
25
ComposerContext ,
26
26
ComposerGenerateCommitMessageEventData ,
27
27
ComposerGenerateCommitsEventData ,
28
+ ComposerHunk ,
28
29
ComposerLoadedErrorData ,
30
+ ComposerSafetyState ,
29
31
ComposerTelemetryEvent ,
30
32
FinishAndCommitParams ,
31
33
GenerateCommitMessageParams ,
@@ -78,6 +80,7 @@ import {
78
80
import type { ComposerWebviewShowingArgs } from './registration' ;
79
81
import {
80
82
convertToComposerDiffInfo ,
83
+ createCombinedDiffForCommit ,
81
84
createHunksFromDiffs ,
82
85
createSafetyState ,
83
86
getWorkingTreeDiffs ,
@@ -98,6 +101,10 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
98
101
private _repositorySubscription ?: Disposable ;
99
102
private _currentRepository ?: Repository ;
100
103
104
+ // Hunk map and safety state
105
+ private _hunks : ComposerHunk [ ] = [ ] ;
106
+ private _safetyState : ComposerSafetyState ;
107
+
101
108
// Telemetry context - tracks composer-specific data for getTelemetryContext
102
109
private _context : ComposerContext ;
103
110
@@ -111,6 +118,15 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
111
118
this . container . ai . onDidChangeModel ( this . onAIModelChanged , this ) ,
112
119
) ;
113
120
this . _context = { ...baseContext } ;
121
+ this . _safetyState = {
122
+ repoPath : '' ,
123
+ headSha : '' ,
124
+ hashes : {
125
+ staged : null ,
126
+ unstaged : null ,
127
+ unified : null ,
128
+ } ,
129
+ } ;
114
130
}
115
131
116
132
dispose ( ) : void {
@@ -308,7 +324,8 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
308
324
// Allow composer to open with no changes - we'll handle this in the UI
309
325
const hasChanges = Boolean ( staged ?. contents || unstaged ?. contents ) ;
310
326
311
- const { hunkMap, hunks } = createHunksFromDiffs ( staged ?. contents , unstaged ?. contents ) ;
327
+ const hunks = createHunksFromDiffs ( staged ?. contents , unstaged ?. contents ) ;
328
+ this . _hunks = hunks ;
312
329
313
330
const baseCommit = getSettledValue ( commitResult ) ;
314
331
if ( baseCommit == null ) {
@@ -359,6 +376,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
359
376
360
377
// Create safety state snapshot for validation
361
378
const safetyState = await createSafetyState ( repo , diffs , baseCommit . sha ) ;
379
+ this . _safetyState = safetyState ;
362
380
363
381
const aiEnabled = this . getAiEnabled ( ) ;
364
382
const aiModel = await this . container . ai . getModel (
@@ -395,15 +413,13 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
395
413
return {
396
414
...this . initialState ,
397
415
hunks : hunks ,
398
- hunkMap : hunkMap ,
399
416
baseCommit : {
400
417
sha : baseCommit . sha ,
401
418
message : baseCommit . message ?? '' ,
402
419
repoName : repo . name ,
403
420
branchName : currentBranch . name ,
404
421
} ,
405
422
commits : commits ,
406
- safetyState : safetyState ,
407
423
aiEnabled : aiEnabled ,
408
424
ai : {
409
425
model : aiModel ,
@@ -533,9 +549,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
533
549
await this . host . notify ( DidReloadComposerNotification , {
534
550
hunks : composerData . hunks ,
535
551
commits : composerData . commits ,
536
- hunkMap : composerData . hunkMap ,
537
552
baseCommit : composerData . baseCommit ,
538
- safetyState : composerData . safetyState ,
539
553
loadingError : composerData . loadingError ,
540
554
hasChanges : composerData . hasChanges ,
541
555
repositoryState : composerData . repositoryState ,
@@ -788,14 +802,10 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
788
802
await this . host . notify ( DidStartGeneratingNotification , undefined ) ;
789
803
790
804
// 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
+ }
799
809
800
810
const existingCommits = params . commits . map ( commit => ( {
801
811
id : commit . id ,
@@ -808,7 +818,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
808
818
const result = await this . container . ai . generateCommits (
809
819
hunks ,
810
820
existingCommits ,
811
- params . hunkMap ,
821
+ this . _hunks . map ( m => ( { index : m . index , hunkHeader : m . hunkHeader } ) ) ,
812
822
{ source : 'composer' , correlationId : this . host . instanceId } ,
813
823
{
814
824
cancellation : this . _generateCommitsCancellation . token ,
@@ -942,9 +952,28 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
942
952
// Notify webview that commit message generation is starting
943
953
await this . host . notify ( DidStartGeneratingCommitMessageNotification , { commitId : params . commitId } ) ;
944
954
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
+
945
974
// Call the AI service to generate commit message
946
975
const result = await this . container . ai . generateCommitMessage (
947
- params . diff ,
976
+ patch ,
948
977
{ source : 'composer' , correlationId : this . host . instanceId } ,
949
978
{
950
979
cancellation : this . _generateCommitMessageCancellation . token ,
@@ -1032,7 +1061,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
1032
1061
await this . host . notify ( DidStartCommittingNotification , undefined ) ;
1033
1062
1034
1063
// 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 ) ;
1036
1065
if ( ! repo ) {
1037
1066
// Clear loading state and show safety error
1038
1067
await this . host . notify ( DidFinishCommittingNotification , undefined ) ;
@@ -1049,13 +1078,18 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
1049
1078
return ;
1050
1079
}
1051
1080
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 =>
1054
1088
params . commits . some ( c => c . hunkIndices . includes ( hunk . index ) ) ,
1055
1089
) ;
1056
1090
1057
1091
// Validate repository safety state before proceeding
1058
- const validation = await validateSafetyState ( repo , params . safetyState , hunksBeingCommitted ) ;
1092
+ const validation = await validateSafetyState ( repo , this . _safetyState , hunksBeingCommitted ) ;
1059
1093
if ( ! validation . isValid ) {
1060
1094
// Clear loading state and show safety error
1061
1095
await this . host . notify ( DidFinishCommittingNotification , undefined ) ;
@@ -1073,8 +1107,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
1073
1107
return ;
1074
1108
}
1075
1109
1076
- // Convert composer data to ComposerDiffInfo format
1077
- const diffInfo = convertToComposerDiffInfo ( params . commits , params . hunks ) ;
1110
+ const diffInfo = convertToComposerDiffInfo ( params . commits , hunks ) ;
1078
1111
const svc = this . container . git . getRepositoryService ( repo . path ) ;
1079
1112
if ( ! svc ) {
1080
1113
this . _context . errors . operation . count ++ ;
@@ -1120,7 +1153,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
1120
1153
1121
1154
if (
1122
1155
! validateResultingDiff (
1123
- params . safetyState ,
1156
+ this . _safetyState ,
1124
1157
await sha256 ( resultingDiff ) ,
1125
1158
this . _context . diff . unstagedIncluded ,
1126
1159
)
0 commit comments