@@ -6,7 +6,7 @@ use std::io::IsTerminal;
66use std:: path:: PathBuf ;
77
88use console:: style;
9- use dialoguer:: Confirm ;
9+ use dialoguer:: { Confirm , Editor , Select } ;
1010use globset:: { Glob , GlobSetBuilder } ;
1111use tokio:: signal;
1212use tokio:: sync:: mpsc;
@@ -186,10 +186,10 @@ impl App {
186186 // Finish analysis spinner before any interactive prompts
187187 progress. finish ( ) ;
188188
189+ let is_interactive = std:: io:: stdout ( ) . is_terminal ( ) && std:: io:: stdin ( ) . is_terminal ( ) ;
190+
189191 // Step 3.5: Split detection
190192 if !self . cli . no_split {
191- let is_interactive = std:: io:: stdout ( ) . is_terminal ( ) && std:: io:: stdin ( ) . is_terminal ( ) ;
192-
193193 if is_interactive && !self . cli . yes {
194194 let suggestion = CommitSplitter :: analyze ( & changes, & symbols) ;
195195
@@ -360,13 +360,45 @@ impl App {
360360 }
361361
362362 // Step 6: Select message
363- let message = if candidates. len ( ) == 1 {
363+ let mut message = if candidates. len ( ) == 1 {
364364 candidates. into_iter ( ) . next ( ) . unwrap ( )
365365 } else {
366366 self . select_candidate ( & candidates) ?
367367 } ;
368368
369- // Step 7: Clipboard / dry-run / confirm and commit
369+ // Step 6.5: Interactive Review / Edit
370+ if !self . cli . yes && is_interactive && !self . cli . dry_run && !self . cli . clipboard {
371+ loop {
372+ eprintln ! ( "\n {}" , style( "Commit message:" ) . bold( ) ) ;
373+ eprintln ! ( "{}" , style( & message) . green( ) ) ;
374+ eprintln ! ( ) ;
375+
376+ let options = & [ "Commit" , "Edit" , "Cancel" ] ;
377+ let selection = Select :: new ( )
378+ . with_prompt ( "What would you like to do?" )
379+ . items ( options)
380+ . default ( 0 )
381+ . interact ( )
382+ . map_err ( |e| Error :: Dialog ( e. to_string ( ) ) ) ?;
383+
384+ match selection {
385+ 0 => break , // Commit
386+ 1 => {
387+ if let Some ( edited) = Editor :: new ( )
388+ . edit ( & message)
389+ . map_err ( |e| Error :: Dialog ( e. to_string ( ) ) ) ?
390+ {
391+ if !edited. trim ( ) . is_empty ( ) {
392+ message = edited;
393+ }
394+ }
395+ }
396+ _ => return Err ( Error :: Cancelled ) ,
397+ }
398+ }
399+ }
400+
401+ // Step 7: Clipboard / dry-run / commit
370402 if self . cli . clipboard {
371403 Self :: copy_to_clipboard ( & message) ?;
372404 eprintln ! ( "{} Copied to clipboard!" , style( "✓" ) . green( ) . bold( ) ) ;
@@ -379,31 +411,18 @@ impl App {
379411 return Ok ( ( ) ) ;
380412 }
381413
382- let is_interactive = std:: io:: stdout ( ) . is_terminal ( ) && std:: io:: stdin ( ) . is_terminal ( ) ;
383-
384- if !self . cli . yes {
385- if !is_interactive {
386- eprintln ! ( "{}" , style( "warning:" ) . yellow( ) . bold( ) ) ;
387- eprintln ! ( " Not a terminal. Use --yes to auto-confirm in scripts/hooks." ) ;
388- println ! ( "{}" , message) ;
389- return Ok ( ( ) ) ;
390- }
391-
392- // For single candidate (already shown via streaming), just confirm
393- if num_candidates == 1 {
394- eprintln ! ( "\n {}" , style( "Generated commit message:" ) . bold( ) ) ;
395- eprintln ! ( "{}" , style( & message) . green( ) ) ;
396- eprintln ! ( ) ;
397- }
398-
399- let confirm = Confirm :: new ( )
400- . with_prompt ( "Create commit with this message?" )
401- . default ( true )
402- . interact ( ) ?;
414+ // Auto-commit if --yes is set
415+ if self . cli . yes {
416+ git. commit ( & message) . await ?;
417+ eprintln ! ( "{} Committed!" , style( "✓" ) . green( ) . bold( ) ) ;
418+ return Ok ( ( ) ) ;
419+ }
403420
404- if !confirm {
405- return Err ( Error :: Cancelled ) ;
406- }
421+ if !is_interactive {
422+ eprintln ! ( "{}" , style( "warning:" ) . yellow( ) . bold( ) ) ;
423+ eprintln ! ( " Not a terminal. Use --yes to auto-confirm in scripts/hooks." ) ;
424+ println ! ( "{}" , message) ;
425+ return Ok ( ( ) ) ;
407426 }
408427
409428 git. commit ( & message) . await ?;
0 commit comments