Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 56ac9a7

Browse files
committed
refactor: extract revision logic & utils
- Extract common revision logic into reusable RevisionConfig interface - Create shared reviewAndRevise function to eliminate duplication - Modularize commit utilities (handleEditorRevision, displayCommitMessage, etc.) - Improve error handling in revision process - Make agent mode consistent with streaming flow
1 parent d675d11 commit 56ac9a7

File tree

2 files changed

+116
-111
lines changed

2 files changed

+116
-111
lines changed

src/commands/aicommits-utils.ts

Lines changed: 116 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,23 @@ const openInEditor = (
4242
}
4343
};
4444

45-
export const streamingReviewAndRevise = async ({
46-
aiCommitMessageService,
47-
promptUI,
48-
message,
49-
body,
50-
diff,
51-
}: {
52-
aiCommitMessageService: AICommitMessageService;
53-
promptUI: ClackPromptService;
54-
message: string;
55-
body: string;
56-
diff: string;
57-
}): Promise<{ accepted: boolean; message?: string; body?: string }> => {
45+
interface RevisionConfig {
46+
reviseLabel: string;
47+
promptMessage: string;
48+
promptPlaceholder: string;
49+
revise: (
50+
userPrompt: string,
51+
currentMessage: string,
52+
currentBody: string,
53+
) => Promise<{ message: string; body: string }>;
54+
}
55+
56+
const reviewAndRevise = async (
57+
promptUI: ClackPromptService,
58+
message: string,
59+
body: string,
60+
config: RevisionConfig,
61+
): Promise<{ accepted: boolean; message?: string; body?: string }> => {
5862
let currentMessage = message;
5963
let currentBody = body;
6064

@@ -65,7 +69,7 @@ export const streamingReviewAndRevise = async ({
6569
)}\n\n${cyan(currentBody)}\n\nWhat would you like to do?`,
6670
options: [
6771
{ label: 'Accept and commit', value: 'accept' },
68-
{ label: 'Revise with a prompt', value: 'revise' },
72+
{ label: config.reviseLabel, value: 'revise' },
6973
{ label: 'Edit in $EDITOR', value: 'edit' },
7074
{ label: 'Cancel', value: 'cancel' },
7175
],
@@ -78,21 +82,80 @@ export const streamingReviewAndRevise = async ({
7882
return { accepted: false };
7983
} else if (confirmed === 'revise') {
8084
const userPrompt = await promptUI.text({
81-
message:
82-
'Describe how you want to revise the commit message (e.g. "make it more descriptive", "use imperative mood", etc):',
83-
placeholder: 'Enter revision prompt',
85+
message: config.promptMessage,
86+
placeholder: config.promptPlaceholder,
8487
});
8588
if (!userPrompt || promptUI.isCancel(userPrompt)) {
8689
promptUI.outro('Commit cancelled');
8790
return { accepted: false };
8891
}
8992

93+
try {
94+
const result = await config.revise(userPrompt, currentMessage, currentBody);
95+
currentMessage = result.message;
96+
currentBody = result.body;
97+
} catch (error) {
98+
promptUI.outro(`Failed to revise: ${error instanceof Error ? error.message : 'Unknown error'}`);
99+
return { accepted: false };
100+
}
101+
} else if (confirmed === 'edit') {
102+
const result = handleEditorRevision(promptUI, currentMessage, currentBody);
103+
if (!result) {
104+
return { accepted: false };
105+
}
106+
currentMessage = result.message;
107+
currentBody = result.body;
108+
}
109+
}
110+
promptUI.outro('Too many revisions requested, commit cancelled.');
111+
return { accepted: false };
112+
};
113+
114+
const handleEditorRevision = (
115+
promptUI: ClackPromptService,
116+
currentMessage: string,
117+
currentBody: string,
118+
): { message: string; body: string } | null => {
119+
const initial = `${currentMessage}\n\n${currentBody}`.trim();
120+
const edited = openInEditor(initial, promptUI);
121+
if (edited === null) {
122+
promptUI.outro('Commit cancelled');
123+
return null;
124+
}
125+
// Split edited message into subject and body (first line = subject, rest = body)
126+
const [firstLine, ...rest] = edited.split('\n');
127+
return {
128+
message: firstLine.trim(),
129+
body: rest.join('\n').trim(),
130+
};
131+
};
132+
133+
export const streamingReviewAndRevise = async ({
134+
aiCommitMessageService,
135+
promptUI,
136+
message,
137+
body,
138+
diff,
139+
}: {
140+
aiCommitMessageService: AICommitMessageService;
141+
promptUI: ClackPromptService;
142+
message: string;
143+
body: string;
144+
diff: string;
145+
}): Promise<{ accepted: boolean; message?: string; body?: string }> => {
146+
const config: RevisionConfig = {
147+
reviseLabel: 'Revise with a prompt',
148+
promptMessage:
149+
'Describe how you want to revise the commit message (e.g. "make it more descriptive", "use imperative mood", etc):',
150+
promptPlaceholder: 'Enter revision prompt',
151+
revise: async (userPrompt: string) => {
90152
const reviseSpinner = promptUI.spinner();
91153
reviseSpinner.start('The AI is revising your commit message');
92154

93155
let messageBuffer = '';
156+
let updatedMessage = '';
157+
let updatedBody = '';
94158

95-
// Use streaming to show revision in real-time
96159
await aiCommitMessageService.reviseStreamingCommitMessage({
97160
diff,
98161
userPrompt,
@@ -105,36 +168,27 @@ export const streamingReviewAndRevise = async ({
105168
onBodyUpdate: () => {
106169
// Don't show body updates in real-time
107170
},
108-
onComplete: (updatedMessage, updatedBody) => {
109-
currentMessage = updatedMessage;
110-
currentBody = updatedBody;
171+
onComplete: (message, body) => {
172+
updatedMessage = message;
173+
updatedBody = body;
111174
reviseSpinner.stop('Revision complete');
112175

113176
// Display the updated message and body
114177
promptUI.log.step('Updated commit message:');
115-
promptUI.log.message(green(updatedMessage));
178+
promptUI.log.message(green(message));
116179

117-
if (updatedBody) {
180+
if (body) {
118181
promptUI.log.step('Updated commit body:');
119-
promptUI.log.message(updatedBody);
182+
promptUI.log.message(body);
120183
}
121184
},
122185
});
123-
} else if (confirmed === 'edit') {
124-
const initial = `${currentMessage}\n\n${currentBody}`.trim();
125-
const edited = openInEditor(initial, promptUI);
126-
if (edited === null) {
127-
promptUI.outro('Commit cancelled');
128-
return { accepted: false };
129-
}
130-
// Split edited message into subject and body (first line = subject, rest = body)
131-
const [firstLine, ...rest] = edited.split('\n');
132-
currentMessage = firstLine.trim();
133-
currentBody = rest.join('\n').trim();
134-
}
135-
}
136-
promptUI.outro('Too many revisions requested, commit cancelled.');
137-
return { accepted: false };
186+
187+
return { message: updatedMessage, body: updatedBody };
188+
},
189+
};
190+
191+
return reviewAndRevise(promptUI, message, body, config);
138192
};
139193

140194
export const agentStreamingReviewAndRevise = async ({
@@ -148,81 +202,35 @@ export const agentStreamingReviewAndRevise = async ({
148202
message: string;
149203
body: string;
150204
}): Promise<{ accepted: boolean; message?: string; body?: string }> => {
151-
let currentMessage = message;
152-
let currentBody = body;
205+
const config: RevisionConfig = {
206+
reviseLabel: 'Revise with AI agent',
207+
promptMessage:
208+
'Describe how you want the AI agent to revise the commit message (e.g. "make it more descriptive", "use imperative mood", "check related files for context"):',
209+
promptPlaceholder: 'Enter revision prompt for the AI agent',
210+
revise: async (userPrompt: string, currentMessage: string, currentBody: string) => {
211+
const reviseSpinner = promptUI.spinner();
212+
reviseSpinner.start('AI agent is revising your commit message...');
153213

154-
for (let i = 0; i < 10; i++) {
155-
const confirmed = await promptUI.select({
156-
message: `Proposed commit message:\n\n${cyan(
214+
const result = await aiAgentService.reviseCommitWithAgent({
157215
currentMessage,
158-
)}\n\n${cyan(currentBody)}\n\nWhat would you like to do?`,
159-
options: [
160-
{ label: 'Accept and commit', value: 'accept' },
161-
{ label: 'Revise with AI agent', value: 'revise' },
162-
{ label: 'Edit in $EDITOR', value: 'edit' },
163-
{ label: 'Cancel', value: 'cancel' },
164-
],
165-
});
166-
167-
if (confirmed === 'accept') {
168-
return { accepted: true, message: currentMessage, body: currentBody };
169-
} else if (confirmed === 'cancel' || promptUI.isCancel(confirmed)) {
170-
promptUI.outro('Commit cancelled');
171-
return { accepted: false };
172-
} else if (confirmed === 'revise') {
173-
const userPrompt = await promptUI.text({
174-
message:
175-
'Describe how you want the AI agent to revise the commit message (e.g. "make it more descriptive", "use imperative mood", "check related files for context"):',
176-
placeholder: 'Enter revision prompt for the AI agent',
216+
currentBody,
217+
userRevisionPrompt: userPrompt,
177218
});
178-
if (!userPrompt || promptUI.isCancel(userPrompt)) {
179-
promptUI.outro('Commit cancelled');
180-
return { accepted: false };
181-
}
182219

183-
const reviseSpinner = promptUI.spinner();
184-
reviseSpinner.start('AI agent is revising your commit message...');
185-
186-
try {
187-
const result = await aiAgentService.reviseCommitWithAgent({
188-
currentMessage,
189-
currentBody,
190-
userRevisionPrompt: userPrompt,
191-
});
220+
reviseSpinner.stop('Agent revision complete');
192221

193-
reviseSpinner.stop('Agent revision complete');
222+
// Display the updated message and body
223+
promptUI.log.step('Agent-revised commit message:');
224+
promptUI.log.message(green(result.commitMessage));
194225

195-
currentMessage = result.commitMessage;
196-
currentBody = result.body;
226+
if (result.body) {
227+
promptUI.log.step('Agent-revised commit body:');
228+
promptUI.log.message(result.body);
229+
}
197230

198-
// Display the updated message and body
199-
promptUI.log.step('Agent-revised commit message:');
200-
promptUI.log.message(green(result.commitMessage));
231+
return { message: result.commitMessage, body: result.body };
232+
},
233+
};
201234

202-
if (result.body) {
203-
promptUI.log.step('Agent-revised commit body:');
204-
promptUI.log.message(result.body);
205-
}
206-
} catch (error) {
207-
reviseSpinner.stop('Agent revision failed');
208-
promptUI.outro(
209-
`Failed to revise with agent: ${error instanceof Error ? error.message : 'Unknown error'}`,
210-
);
211-
return { accepted: false };
212-
}
213-
} else if (confirmed === 'edit') {
214-
const initial = `${currentMessage}\n\n${currentBody}`.trim();
215-
const edited = openInEditor(initial, promptUI);
216-
if (edited === null) {
217-
promptUI.outro('Commit cancelled');
218-
return { accepted: false };
219-
}
220-
// Split edited message into subject and body (first line = subject, rest = body)
221-
const [firstLine, ...rest] = edited.split('\n');
222-
currentMessage = firstLine.trim();
223-
currentBody = rest.join('\n').trim();
224-
}
225-
}
226-
promptUI.outro('Too many revisions requested, commit cancelled.');
227-
return { accepted: false };
235+
return reviewAndRevise(promptUI, message, body, config);
228236
};

src/commands/aicommits.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ export const aiCommits = async ({
7878
stagingSpinner.stop('All files staged');
7979
}
8080

81-
// Handle agent mode
8281
if (agentMode) {
8382
return await handleAgentMode(aiAgentService, gitService, promptUI);
8483
}
@@ -107,7 +106,6 @@ export const aiCommits = async ({
107106
let commitBody = '';
108107
let messageBuffer = '';
109108

110-
// Use streaming API to generate and display commit message in real-time
111109
await aiCommitMessageService.generateStreamingCommitMessage({
112110
diff: staged.diff,
113111
onMessageUpdate: (content) => {
@@ -180,7 +178,6 @@ async function handleAgentMode(
180178

181179
try {
182180
const result = await aiAgentService.generateCommitWithAgent();
183-
184181
analyzeSpinner.stop('Repository analysis complete');
185182

186183
// Display the generated commit message

0 commit comments

Comments
 (0)