ralph_workflow/app/boundary/
conflict_resolution.rs1use crate::app::rebase::ConflictResolutionResult;
8use crate::config::Config;
9use crate::logger::{Colors, Logger};
10use crate::workspace::Workspace;
11use std::path::Path;
12use std::sync::Arc;
13
14pub struct ConflictResolutionRuntimeParams<'a> {
15 pub resolution_prompt: &'a str,
16 pub config: &'a Config,
17 pub logger: &'a Logger,
18 pub colors: Colors,
19 pub executor_arc: Arc<dyn crate::executor::ProcessExecutor>,
20 pub workspace: &'a dyn Workspace,
21 pub workspace_arc: Arc<dyn Workspace>,
22 pub reviewer_agent: &'a str,
23 pub registry: &'a crate::agents::AgentRegistry,
24}
25
26pub fn run_ai_conflict_resolution_with_runtime(
27 params: ConflictResolutionRuntimeParams<'_>,
28) -> anyhow::Result<ConflictResolutionResult> {
29 let ConflictResolutionRuntimeParams {
30 resolution_prompt,
31 config,
32 logger,
33 colors,
34 executor_arc,
35 workspace,
36 workspace_arc,
37 reviewer_agent,
38 registry,
39 } = params;
40 let mut timer = crate::app::runtime_factory::create_timer();
41 let executor_ref: &dyn crate::executor::ProcessExecutor = &*executor_arc;
42 let mut runtime = crate::app::runtime_factory::create_pipeline_runtime(
43 crate::app::runtime_factory::PipelineRuntimeFactoryParams {
44 timer: &mut timer,
45 logger,
46 colors: &colors,
47 config,
48 executor: executor_ref,
49 executor_arc: Arc::clone(&executor_arc),
50 workspace,
51 workspace_arc: Arc::clone(&workspace_arc),
52 },
53 );
54 let log_dir = ".agent/logs/rebase_conflict_resolution";
55 workspace.create_dir_all(Path::new(log_dir))?;
56 run_conflict_prompt_and_check(
57 resolution_prompt,
58 reviewer_agent,
59 log_dir,
60 registry,
61 workspace,
62 &mut runtime,
63 )
64}
65
66fn run_conflict_prompt_and_check(
67 resolution_prompt: &str,
68 reviewer_agent: &str,
69 log_dir: &str,
70 registry: &crate::agents::AgentRegistry,
71 workspace: &dyn Workspace,
72 runtime: &mut crate::pipeline::PipelineRuntime<'_>,
73) -> anyhow::Result<ConflictResolutionResult> {
74 let agent_config = registry
75 .resolve_config(reviewer_agent)
76 .ok_or_else(|| anyhow::anyhow!("Agent not found: {reviewer_agent}"))?;
77 let cmd_str = agent_config.build_cmd_with_model(true, true, true, None);
78 let log_prefix = format!("{log_dir}/conflict_resolution");
79 let model_index = 0usize;
80 let attempt = crate::pipeline::logfile::next_logfile_attempt_index(
81 Path::new(&log_prefix),
82 reviewer_agent,
83 model_index,
84 workspace,
85 );
86 let logfile = crate::pipeline::logfile::build_logfile_path_with_attempt(
87 &log_prefix,
88 reviewer_agent,
89 model_index,
90 attempt,
91 );
92 let prompt_cmd = crate::pipeline::PromptCommand {
93 label: reviewer_agent,
94 display_name: reviewer_agent,
95 cmd_str: &cmd_str,
96 prompt: resolution_prompt,
97 log_prefix: &log_prefix,
98 model_index: Some(model_index),
99 attempt: Some(attempt),
100 logfile: &logfile,
101 parser_type: agent_config.json_parser,
102 env_vars: &agent_config.env_vars,
103 completion_output_path: None,
104 };
105 let result = crate::pipeline::run_with_prompt(&prompt_cmd, runtime)?;
106 check_conflict_exit_and_remaining(result.exit_code)
107}
108
109fn check_conflict_exit_and_remaining(exit_code: i32) -> anyhow::Result<ConflictResolutionResult> {
110 if exit_code != 0 {
111 return Ok(ConflictResolutionResult::Failed);
112 }
113 let remaining_conflicts = crate::git_helpers::get_conflicted_files()?;
114 if remaining_conflicts.is_empty() {
115 Ok(ConflictResolutionResult::FileEditsOnly)
116 } else {
117 Ok(ConflictResolutionResult::Failed)
118 }
119}