@@ -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 ,
@@ -924,9 +934,28 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
924
934
// Notify webview that commit message generation is starting
925
935
await this . host . notify ( DidStartGeneratingCommitMessageNotification , { commitId : params . commitId } ) ;
926
936
937
+ // Create combined diff for the commit
938
+ const { patch } = createCombinedDiffForCommit (
939
+ this . _hunks . filter ( h => params . commitHunkIndices . includes ( h . index ) ) ,
940
+ ) ;
941
+ if ( ! patch ) {
942
+ this . _context . operations . generateCommitMessage . errorCount ++ ;
943
+ this . _context . errors . operation . count ++ ;
944
+ // Send error notification for failure (not cancellation)
945
+ this . sendTelemetryEvent ( 'composer/action/generateCommitMessage/failed' , {
946
+ ...eventData ,
947
+ 'failure.reason' : 'error' ,
948
+ 'failure.error.message' : 'Failed to create diff for commit' ,
949
+ } ) ;
950
+ await this . host . notify ( DidErrorAIOperationNotification , {
951
+ operation : 'generate commit message' ,
952
+ error : 'Failed to create diff for commit' ,
953
+ } ) ;
954
+ }
955
+
927
956
// Call the AI service to generate commit message
928
957
const result = await this . container . ai . generateCommitMessage (
929
- params . diff ,
958
+ patch ,
930
959
{ source : 'composer' , correlationId : this . host . instanceId } ,
931
960
{
932
961
cancellation : this . _generateCommitMessageCancellation . token ,
@@ -1014,7 +1043,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
1014
1043
await this . host . notify ( DidStartCommittingNotification , undefined ) ;
1015
1044
1016
1045
// Get the specific repository from the safety state
1017
- const repo = this . container . git . getRepository ( params . safetyState . repoPath ) ;
1046
+ const repo = this . container . git . getRepository ( this . _safetyState . repoPath ) ;
1018
1047
if ( ! repo ) {
1019
1048
// Clear loading state and show safety error
1020
1049
await this . host . notify ( DidFinishCommittingNotification , undefined ) ;
@@ -1031,13 +1060,18 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
1031
1060
return ;
1032
1061
}
1033
1062
1034
- // Extract hunk sources for smart validation
1035
- const hunksBeingCommitted = params . hunks . filter ( hunk =>
1063
+ const commitHunkIndices = params . commits . flatMap ( c => c . hunkIndices ) ;
1064
+ const hunks : ComposerHunk [ ] = [ ] ;
1065
+ for ( const hunk of commitHunkIndices ) {
1066
+ hunks . push ( { ...this . _hunks . find ( m => m . index === hunk ) ! , assigned : true } ) ;
1067
+ }
1068
+
1069
+ const hunksBeingCommitted = hunks . filter ( hunk =>
1036
1070
params . commits . some ( c => c . hunkIndices . includes ( hunk . index ) ) ,
1037
1071
) ;
1038
1072
1039
1073
// Validate repository safety state before proceeding
1040
- const validation = await validateSafetyState ( repo , params . safetyState , hunksBeingCommitted ) ;
1074
+ const validation = await validateSafetyState ( repo , this . _safetyState , hunksBeingCommitted ) ;
1041
1075
if ( ! validation . isValid ) {
1042
1076
// Clear loading state and show safety error
1043
1077
await this . host . notify ( DidFinishCommittingNotification , undefined ) ;
@@ -1055,8 +1089,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
1055
1089
return ;
1056
1090
}
1057
1091
1058
- // Convert composer data to ComposerDiffInfo format
1059
- const diffInfo = convertToComposerDiffInfo ( params . commits , params . hunks ) ;
1092
+ const diffInfo = convertToComposerDiffInfo ( params . commits , hunks ) ;
1060
1093
const svc = this . container . git . getRepositoryService ( repo . path ) ;
1061
1094
if ( ! svc ) {
1062
1095
this . _context . errors . operation . count ++ ;
@@ -1102,7 +1135,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
1102
1135
1103
1136
if (
1104
1137
! validateResultingDiff (
1105
- params . safetyState ,
1138
+ this . _safetyState ,
1106
1139
await sha256 ( resultingDiff ) ,
1107
1140
this . _context . diff . unstagedIncluded ,
1108
1141
)
0 commit comments