Skip to main content

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.
29///
30/// # Errors
31///
32/// Returns error if the operation fails.
33pub fn run(args: Args, executor: std::sync::Arc<dyn ProcessExecutor>) -> anyhow::Result<()> {
34    let colors = Colors::new();
35    let logger = Logger::new(colors);
36
37    // Set working directory first if override is provided
38    // This ensures all subsequent operations (including config init) use the correct directory
39    if let Some(ref override_dir) = args.working_dir_override {
40        std::env::set_current_dir(override_dir)?;
41    }
42
43    // Initialize configuration and agent registry
44    let Some(init_result) = initialize_config(&args, colors, &logger)? else {
45        return Ok(()); // Early exit (--init/--init-global)
46    };
47
48    let config_init::ConfigInitResult {
49        config,
50        registry,
51        config_path,
52        config_sources,
53        agent_resolution_sources,
54    } = init_result;
55
56    // Resolve required agent names
57    let validated = resolve_required_agents(&config, &agent_resolution_sources)?;
58    let developer_agent = validated.developer_agent;
59    let reviewer_agent = validated.reviewer_agent;
60
61    // Handle listing commands (these can run without git repo)
62    if handle_listing_commands(&args, &registry, colors) {
63        return Ok(());
64    }
65
66    // Handle --diagnose
67    if args.recovery.diagnose {
68        let diagnose_workspace = crate::workspace::WorkspaceFs::new(
69            std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from(".")),
70        );
71        handle_diagnose(
72            colors,
73            &config,
74            &registry,
75            &config_path,
76            &config_sources,
77            &*executor,
78            &diagnose_workspace,
79        );
80        return Ok(());
81    }
82
83    // Validate agent chains
84    validate_agent_chains(&registry, &agent_resolution_sources, colors);
85
86    // Create effect handler for production operations
87    let mut handler = effect_handler::RealAppEffectHandler::new();
88
89    // Get repo root early for workspace creation (needed by plumbing commands)
90    // This uses the same logic as setup_working_dir_via_handler but captures the repo_root.
91    let early_repo_root =
92        discover_repo_root_for_workspace(args.working_dir_override.as_deref(), &mut handler)?;
93
94    // Create workspace for plumbing commands (and later for the full pipeline)
95    let workspace: std::sync::Arc<dyn crate::workspace::Workspace> =
96        std::sync::Arc::new(crate::workspace::WorkspaceFs::new(early_repo_root));
97
98    // Handle plumbing commands with workspace support
99    if handle_plumbing_commands(
100        &args,
101        &logger,
102        colors,
103        &mut handler,
104        Some(workspace.as_ref()),
105    )? {
106        return Ok(());
107    }
108
109    if !command_requires_prompt_setup(&args)
110        && handle_repo_commands_without_prompt_setup(RepoCommandParams {
111            args: &args,
112            config: &config,
113            registry: &registry,
114            developer_agent: &developer_agent,
115            reviewer_agent: &reviewer_agent,
116            logger: &logger,
117            colors,
118            executor: &executor,
119            repo_root: workspace.root(),
120            workspace: &workspace,
121        })?
122    {
123        return Ok(());
124    }
125
126    if args.recovery.inspect_checkpoint {
127        crate::app::resume::inspect_checkpoint(workspace.as_ref(), &logger)?;
128        return Ok(());
129    }
130
131    // Validate agents and set up git repo and PROMPT.md
132    // Note: repo_root is discovered again here (same as early_repo_root) but also
133    // does additional setup like PROMPT.md creation that plumbing commands don't need
134    let Some(repo_root) = validate_and_setup_agents(
135        &AgentSetupParams {
136            config: &config,
137            registry: &registry,
138            developer_agent: &developer_agent,
139            reviewer_agent: &reviewer_agent,
140            config_path: &config_path,
141            colors,
142            logger: &logger,
143            working_dir_override: args.working_dir_override.as_deref(),
144        },
145        &mut handler,
146    )?
147    else {
148        return Ok(());
149    };
150
151    // Prepare pipeline context or exit early
152    // Note: Reuse workspace created earlier (same repo root)
153    (prepare_pipeline_or_exit(PipelinePreparationParams {
154        args,
155        config,
156        registry,
157        developer_agent,
158        reviewer_agent,
159        repo_root,
160        logger,
161        colors,
162        executor,
163        handler: &mut handler,
164        workspace,
165    })?)
166    .map_or_else(|| Ok(()), |ctx| run_pipeline(&ctx))
167}