Skip to content

Commit 9878714

Browse files
Stores hunks and safety state on webview host, uses for ai operations and safety checks
1 parent e5d3241 commit 9878714

File tree

6 files changed

+81
-120
lines changed

6 files changed

+81
-120
lines changed

src/webviews/apps/plus/composer/components/app.ts

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {
2626
OpenOnboardingCommand,
2727
ReloadComposerCommand,
2828
} from '../../../../plus/composer/protocol';
29-
import { createCombinedDiffForCommit, updateHunkAssignments } from '../../../../plus/composer/utils';
29+
import { updateHunkAssignments } from '../../../../plus/composer/utils';
3030
import type { RepoButtonGroupClickEvent } from '../../../shared/components/repo-button-group';
3131
import { focusableBaseStyles } from '../../../shared/components/styles/lit/a11y.css';
3232
import { boxSizingBase } from '../../../shared/components/styles/lit/base.css';
@@ -1256,9 +1256,7 @@ export class ComposerApp extends LitElement {
12561256
private finishAndCommit() {
12571257
this._ipc.sendCommand(FinishAndCommitCommand, {
12581258
commits: this.state.commits,
1259-
hunks: this.hunksWithAssignments,
12601259
baseCommit: this.state.baseCommit,
1261-
safetyState: this.state.safetyState,
12621260
});
12631261
}
12641262

@@ -1273,7 +1271,6 @@ export class ComposerApp extends LitElement {
12731271
private handleReloadComposer() {
12741272
this.resetHistory();
12751273
this._ipc.sendCommand(ReloadComposerCommand, {
1276-
repoPath: this.state.safetyState.repoPath,
12771274
mode: this.state.mode,
12781275
});
12791276
}
@@ -1466,11 +1463,8 @@ export class ComposerApp extends LitElement {
14661463
this.saveToHistory();
14671464

14681465
this._ipc.sendCommand(GenerateCommitsCommand, {
1469-
hunks: hunksToGenerate,
1470-
// In preview mode, send empty commits array to overwrite existing commits
1471-
// In interactive mode, send existing commits to preserve them
1466+
hunkIndices: hunksToGenerate.map(hunk => hunk.index),
14721467
commits: this.isPreviewMode ? [] : this.state.commits,
1473-
hunkMap: this.state.hunkMap,
14741468
baseCommit: this.state.baseCommit,
14751469
customInstructions: customInstructions || undefined,
14761470
});
@@ -1485,15 +1479,9 @@ export class ComposerApp extends LitElement {
14851479
return;
14861480
}
14871481

1488-
// Create combined diff for the commit
1489-
const { patch } = createCombinedDiffForCommit(commit, this.hunksWithAssignments);
1490-
if (!patch) {
1491-
return;
1492-
}
1493-
14941482
this._ipc.sendCommand(GenerateCommitMessageCommand, {
14951483
commitId: commitId,
1496-
diff: patch,
1484+
commitHunkIndices: commit.hunkIndices,
14971485
overwriteExistingMessage: commit.message.trim() !== '',
14981486
});
14991487
}

src/webviews/apps/plus/composer/stateProvider.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,7 @@ export class ComposerStateProvider implements StateProvider<State> {
136136
...this._state,
137137
hunks: msg.params.hunks,
138138
commits: msg.params.commits,
139-
hunkMap: msg.params.hunkMap,
140139
baseCommit: msg.params.baseCommit,
141-
safetyState: msg.params.safetyState,
142140
loadingError: msg.params.loadingError,
143141
hasChanges: msg.params.hasChanges,
144142
safetyError: null, // Clear any existing safety errors

src/webviews/plus/composer/composerWebview.ts

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -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 {
7880
import type { ComposerWebviewShowingArgs } from './registration';
7981
import {
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,
@@ -924,9 +934,28 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
924934
// Notify webview that commit message generation is starting
925935
await this.host.notify(DidStartGeneratingCommitMessageNotification, { commitId: params.commitId });
926936

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+
927956
// Call the AI service to generate commit message
928957
const result = await this.container.ai.generateCommitMessage(
929-
params.diff,
958+
patch,
930959
{ source: 'composer', correlationId: this.host.instanceId },
931960
{
932961
cancellation: this._generateCommitMessageCancellation.token,
@@ -1014,7 +1043,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
10141043
await this.host.notify(DidStartCommittingNotification, undefined);
10151044

10161045
// 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);
10181047
if (!repo) {
10191048
// Clear loading state and show safety error
10201049
await this.host.notify(DidFinishCommittingNotification, undefined);
@@ -1031,13 +1060,18 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
10311060
return;
10321061
}
10331062

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 =>
10361070
params.commits.some(c => c.hunkIndices.includes(hunk.index)),
10371071
);
10381072

10391073
// Validate repository safety state before proceeding
1040-
const validation = await validateSafetyState(repo, params.safetyState, hunksBeingCommitted);
1074+
const validation = await validateSafetyState(repo, this._safetyState, hunksBeingCommitted);
10411075
if (!validation.isValid) {
10421076
// Clear loading state and show safety error
10431077
await this.host.notify(DidFinishCommittingNotification, undefined);
@@ -1055,8 +1089,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
10551089
return;
10561090
}
10571091

1058-
// Convert composer data to ComposerDiffInfo format
1059-
const diffInfo = convertToComposerDiffInfo(params.commits, params.hunks);
1092+
const diffInfo = convertToComposerDiffInfo(params.commits, hunks);
10601093
const svc = this.container.git.getRepositoryService(repo.path);
10611094
if (!svc) {
10621095
this._context.errors.operation.count++;
@@ -1102,7 +1135,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
11021135

11031136
if (
11041137
!validateResultingDiff(
1105-
params.safetyState,
1138+
this._safetyState,
11061139
await sha256(resultingDiff),
11071140
this._context.diff.unstagedIncluded,
11081141
)

src/webviews/plus/composer/mockData.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Mock data for the composer following the proper data model
22
// This represents the structure that would come from AI rebase results
33

4-
import type { ComposerCommit, ComposerHunk, ComposerHunkMap } from './protocol';
4+
import type { ComposerCommit, ComposerHunk } from './protocol';
55

66
// Mock hunks following the AI rebase result structure
77
export const mockHunks: ComposerHunk[] = [
@@ -350,20 +350,6 @@ export const mockCommits: ComposerCommit[] = [
350350

351351
// Callbacks are no longer used - replaced with IPC commands
352352

353-
// Mock hunk map (maps hunk indices to hunk headers for combined diff)
354-
export const mockHunkMap: ComposerHunkMap[] = [
355-
{ index: 1, hunkHeader: '@@ -0,0 +1,15 @@' },
356-
{ index: 2, hunkHeader: '@@ -0,0 +1,12 @@' },
357-
{ index: 3, hunkHeader: '@@ -0,0 +1,18 @@' },
358-
{ index: 4, hunkHeader: '@@ -0,0 +1,20 @@' },
359-
{ index: 5, hunkHeader: '@@ -0,0 +1,10 @@' },
360-
{ index: 6, hunkHeader: '@@ -0,0 +1,25 @@' },
361-
{ index: 7, hunkHeader: '@@ -0,0 +1,30 @@' },
362-
{ index: 8, hunkHeader: '@@ -0,0 +1,8 @@' },
363-
{ index: 9, hunkHeader: '@@ -0,0 +1,15 @@' },
364-
{ index: 10, hunkHeader: '@@ -0,0 +1,12 @@' },
365-
];
366-
367353
// Mock base commit
368354
export const mockBaseCommit = {
369355
sha: 'abc123def456789',

0 commit comments

Comments
 (0)