@@ -421,10 +421,13 @@ impl CollaborativeOrchestrator {
421421 . map ( |alt| alt. description . clone ( ) )
422422 . collect ( ) ;
423423
424+ // Generate code changes diff if applicable
425+ let code_changes = self . generate_code_diff ( action_plan) . await ?;
426+
424427 Ok ( ApprovalContext {
425428 affected_files : self . extract_affected_files ( action_plan) ,
426429 command : self . extract_command ( action_plan) ,
427- code_changes : None , // TODO: Implement diff generation
430+ code_changes,
428431 reasoning : action_plan. description . clone ( ) ,
429432 alternatives,
430433 agent_recommendation : format ! (
@@ -434,6 +437,101 @@ impl CollaborativeOrchestrator {
434437 } )
435438 }
436439
440+ /// Generate code diff from action plan
441+ async fn generate_code_diff ( & self , action_plan : & ActionPlan ) -> Result < Option < CodeDiff > > {
442+ // Only generate diff for file operations and code generation
443+ if action_plan. action_type != ActionType :: FileOperation
444+ && action_plan. action_type != ActionType :: CodeGeneration
445+ {
446+ return Ok ( None ) ;
447+ }
448+
449+ // Extract file path from parameters
450+ let file_path = if let Some ( path) = action_plan. parameters . get ( "path" ) {
451+ path. as_str ( ) . map ( |s| s. to_string ( ) )
452+ } else if let Some ( file) = action_plan. parameters . get ( "file" ) {
453+ file. as_str ( ) . map ( |s| s. to_string ( ) )
454+ } else {
455+ None
456+ } ;
457+
458+ let Some ( file_path) = file_path else {
459+ return Ok ( None ) ;
460+ } ;
461+
462+ // Extract old and new content
463+ let old_content = if let Ok ( content) = tokio:: fs:: read_to_string ( & file_path) . await {
464+ content
465+ } else {
466+ String :: new ( ) // File doesn't exist yet
467+ } ;
468+
469+ let new_content = if let Some ( content) = action_plan. parameters . get ( "content" ) {
470+ content. as_str ( ) . unwrap_or ( "" ) . to_string ( )
471+ } else if let Some ( content) = action_plan. parameters . get ( "new_content" ) {
472+ content. as_str ( ) . unwrap_or ( "" ) . to_string ( )
473+ } else {
474+ return Ok ( None ) ;
475+ } ;
476+
477+ // Generate diff lines
478+ let diff_lines = self . compute_diff_lines ( & old_content, & new_content) ;
479+
480+ Ok ( Some ( CodeDiff {
481+ file_path,
482+ old_content,
483+ new_content,
484+ diff_lines,
485+ } ) )
486+ }
487+
488+ /// Compute diff lines between old and new content
489+ fn compute_diff_lines ( & self , old_content : & str , new_content : & str ) -> Vec < DiffLine > {
490+ let mut diff_lines = Vec :: new ( ) ;
491+ let old_lines: Vec < & str > = old_content. lines ( ) . collect ( ) ;
492+ let new_lines: Vec < & str > = new_content. lines ( ) . collect ( ) ;
493+
494+ // Simple line-by-line diff algorithm (can be enhanced with LCS or Myers diff)
495+ let max_len = old_lines. len ( ) . max ( new_lines. len ( ) ) ;
496+
497+ for i in 0 ..max_len {
498+ let old_line = old_lines. get ( i) . copied ( ) ;
499+ let new_line = new_lines. get ( i) . copied ( ) ;
500+
501+ match ( old_line, new_line) {
502+ ( Some ( old) , Some ( new) ) => {
503+ let change_type = if old == new {
504+ DiffChangeType :: Unchanged
505+ } else {
506+ DiffChangeType :: Modified
507+ } ;
508+ diff_lines. push ( DiffLine {
509+ line_number : i + 1 ,
510+ change_type,
511+ content : new. to_string ( ) ,
512+ } ) ;
513+ }
514+ ( None , Some ( new) ) => {
515+ diff_lines. push ( DiffLine {
516+ line_number : i + 1 ,
517+ change_type : DiffChangeType :: Added ,
518+ content : new. to_string ( ) ,
519+ } ) ;
520+ }
521+ ( Some ( old) , None ) => {
522+ diff_lines. push ( DiffLine {
523+ line_number : i + 1 ,
524+ change_type : DiffChangeType :: Removed ,
525+ content : old. to_string ( ) ,
526+ } ) ;
527+ }
528+ ( None , None ) => break ,
529+ }
530+ }
531+
532+ diff_lines
533+ }
534+
437535 /// Extract affected files from action plan
438536 fn extract_affected_files ( & self , action_plan : & ActionPlan ) -> Vec < String > {
439537 // Simple extraction - can be enhanced
0 commit comments