ralph_workflow/app/runner/pipeline_execution/entry_points/main.rs
1// Main entry point functions for the pipeline.
2//
3// This module contains:
4// - run: Main application entry point
5// - run_with_config: Test-only entry point with pre-built Config
6// - run_with_config_and_resolver: Test-only entry point with custom path resolver
7// - run_with_config_and_handlers: Test-only entry point with both handlers
8// - RunWithHandlersParams: Parameters for test entry points
9
10/// Main application entry point.
11///
12/// Orchestrates the entire Ralph pipeline:
13/// 1. Configuration initialization
14/// 2. Agent validation
15/// 3. Plumbing commands (if requested)
16/// 4. Development phase
17/// 5. Review & fix phase
18/// 6. Final validation
19/// 7. Commit phase
20///
21/// # Arguments
22///
23/// * `args` - The parsed CLI arguments
24/// * `executor` - Process executor for external process execution
25///
26/// # Returns
27///
28/// Returns `Ok(())` on success or an error if any phase fails.
29pub fn run(args: Args, executor: std::sync::Arc<dyn ProcessExecutor>) -> anyhow::Result<()> {
30 let colors = Colors::new();
31 let logger = Logger::new(colors);
32
33 // Set working directory first if override is provided
34 // This ensures all subsequent operations (including config init) use the correct directory
35 if let Some(ref override_dir) = args.working_dir_override {
36 std::env::set_current_dir(override_dir)?;
37 }
38
39 // Initialize configuration and agent registry
40 let Some(init_result) = initialize_config(&args, colors, &logger)? else {
41 return Ok(()); // Early exit (--init/--init-global)
42 };
43
44 let config_init::ConfigInitResult {
45 config,
46 registry,
47 config_path,
48 config_sources,
49 } = init_result;
50
51 // Resolve required agent names
52 let validated = resolve_required_agents(&config)?;
53 let developer_agent = validated.developer_agent;
54 let reviewer_agent = validated.reviewer_agent;
55
56 // Handle listing commands (these can run without git repo)
57 if handle_listing_commands(&args, ®istry, colors) {
58 return Ok(());
59 }
60
61 // Handle --diagnose
62 if args.recovery.diagnose {
63 let diagnose_workspace = crate::workspace::WorkspaceFs::new(
64 std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from(".")),
65 );
66 handle_diagnose(
67 colors,
68 &config,
69 ®istry,
70 &config_path,
71 &config_sources,
72 &*executor,
73 &diagnose_workspace,
74 );
75 return Ok(());
76 }
77
78 // Validate agent chains
79 validate_agent_chains(®istry, colors);
80
81 // Create effect handler for production operations
82 let mut handler = effect_handler::RealAppEffectHandler::new();
83
84 // Get repo root early for workspace creation (needed by plumbing commands)
85 // This uses the same logic as setup_working_dir_via_handler but captures the repo_root.
86 let early_repo_root =
87 discover_repo_root_for_workspace(args.working_dir_override.as_deref(), &mut handler)?;
88
89 // Create workspace for plumbing commands (and later for the full pipeline)
90 let workspace: std::sync::Arc<dyn crate::workspace::Workspace> =
91 std::sync::Arc::new(crate::workspace::WorkspaceFs::new(early_repo_root));
92
93 // Handle plumbing commands with workspace support
94 if handle_plumbing_commands(
95 &args,
96 &logger,
97 colors,
98 &mut handler,
99 Some(workspace.as_ref()),
100 )? {
101 return Ok(());
102 }
103
104 // Validate agents and set up git repo and PROMPT.md
105 // Note: repo_root is discovered again here (same as early_repo_root) but also
106 // does additional setup like PROMPT.md creation that plumbing commands don't need
107 let Some(repo_root) = validate_and_setup_agents(
108 AgentSetupParams {
109 config: &config,
110 registry: ®istry,
111 developer_agent: &developer_agent,
112 reviewer_agent: &reviewer_agent,
113 config_path: &config_path,
114 colors,
115 logger: &logger,
116 working_dir_override: args.working_dir_override.as_deref(),
117 },
118 &mut handler,
119 )?
120 else {
121 return Ok(());
122 };
123
124 // Prepare pipeline context or exit early
125 // Note: Reuse workspace created earlier (same repo root)
126 (prepare_pipeline_or_exit(PipelinePreparationParams {
127 args,
128 config,
129 registry,
130 developer_agent,
131 reviewer_agent,
132 repo_root,
133 logger,
134 colors,
135 executor,
136 handler: &mut handler,
137 workspace,
138 })?)
139 .map_or_else(|| Ok(()), |ctx| run_pipeline(&ctx))
140}
141