ralph_workflow/app/
config_init.rs1use crate::agents::{global_agents_config_path, AgentRegistry, AgentRole, ConfigSource};
10use crate::cli::{
11 apply_args_to_config, handle_extended_help, handle_generate_completion, handle_init_global,
12 handle_init_legacy, handle_init_prompt, handle_list_work_guides, handle_smart_init, Args,
13};
14use crate::config::{loader, unified_config_path, Config, UnifiedConfig};
15use crate::git_helpers::get_repo_root;
16use crate::logger::Colors;
17use crate::logger::Logger;
18use std::path::PathBuf;
19
20pub struct ConfigInitResult {
22 pub config: Config,
24 pub registry: AgentRegistry,
26 pub config_path: PathBuf,
28 pub config_sources: Vec<ConfigSource>,
30}
31
32pub fn initialize_config(
53 args: &Args,
54 colors: Colors,
55 logger: &Logger,
56) -> anyhow::Result<Option<ConfigInitResult>> {
57 let (mut config, unified, warnings) = args
59 .config
60 .as_ref()
61 .map_or_else(loader::load_config, |config_path| {
62 loader::load_config_from_path(Some(config_path.as_path()))
63 });
64
65 for warning in warnings {
67 logger.warn(&warning);
68 }
69
70 let config_path = args
71 .config
72 .clone()
73 .or_else(unified_config_path)
74 .unwrap_or_else(|| PathBuf::from("~/.config/ralph-workflow.toml"));
75
76 config = config.with_commit_msg(args.commit_msg.clone());
78
79 apply_args_to_config(args, &mut config, colors);
81
82 if let Some(shell) = args.completion.generate_completion {
84 if handle_generate_completion(shell) {
85 return Ok(None);
86 }
87 }
88
89 if args.recovery.extended_help {
92 handle_extended_help();
93 if args.work_guide_list.list_work_guides {
94 println!();
95 handle_list_work_guides(colors);
96 }
97 return Ok(None);
98 }
99
100 if args.work_guide_list.list_work_guides && handle_list_work_guides(colors) {
102 return Ok(None);
103 }
104
105 if let Some(ref template_name) = args.init_prompt {
107 if handle_init_prompt(template_name, args.unified_init.force_init, colors)? {
108 return Ok(None);
109 }
110 }
111
112 if args.unified_init.init.is_some()
114 && handle_smart_init(
115 args.unified_init.init.as_deref(),
116 args.unified_init.force_init,
117 colors,
118 )?
119 {
120 return Ok(None);
121 }
122
123 if args.unified_init.init_config && handle_init_global(colors)? {
125 return Ok(None);
126 }
127
128 if args.unified_init.init_global && handle_init_global(colors)? {
130 return Ok(None);
131 }
132
133 if args.legacy_init.init_legacy {
135 let repo_root = get_repo_root().ok();
136 let legacy_path = repo_root.map_or_else(
137 || PathBuf::from(".agent/agents.toml"),
138 |root| root.join(".agent/agents.toml"),
139 );
140 if handle_init_legacy(colors, &legacy_path)? {
141 return Ok(None);
142 }
143 }
144
145 let (registry, config_sources) = load_agent_registry(unified.as_ref(), config_path.as_path())?;
147
148 apply_default_agents(&mut config, ®istry);
150
151 Ok(Some(ConfigInitResult {
152 config,
153 registry,
154 config_path,
155 config_sources,
156 }))
157}
158
159fn load_agent_registry(
160 unified: Option<&UnifiedConfig>,
161 config_path: &std::path::Path,
162) -> anyhow::Result<(AgentRegistry, Vec<ConfigSource>)> {
163 let mut registry = AgentRegistry::new().map_err(|e| {
164 anyhow::anyhow!("Failed to load built-in default agents config (examples/agents.toml): {e}")
165 })?;
166
167 let mut sources = Vec::new();
168
169 if unified.is_none() {
172 if let Some(global_path) = global_agents_config_path() {
173 if global_path.exists() {
174 let loaded = registry.load_from_file(&global_path).map_err(|e| {
175 anyhow::anyhow!(
176 "Failed to load legacy global agent config {}: {}",
177 global_path.display(),
178 e
179 )
180 })?;
181 sources.push(ConfigSource {
182 path: global_path,
183 agents_loaded: loaded,
184 });
185 }
186 }
187
188 let repo_root = get_repo_root().ok();
189 let project_path = repo_root.map_or_else(
190 || PathBuf::from(".agent/agents.toml"),
191 |root| root.join(".agent/agents.toml"),
192 );
193 if project_path.exists() {
194 let loaded = registry.load_from_file(&project_path).map_err(|e| {
195 anyhow::anyhow!(
196 "Failed to load legacy per-repo agent config {}: {}",
197 project_path.display(),
198 e
199 )
200 })?;
201 sources.push(ConfigSource {
202 path: project_path,
203 agents_loaded: loaded,
204 });
205 }
206 }
207
208 if let Some(unified_cfg) = unified {
209 let loaded = registry.apply_unified_config(unified_cfg);
210 if loaded > 0 || unified_cfg.agent_chain.is_some() {
211 sources.push(ConfigSource {
212 path: config_path.to_path_buf(),
213 agents_loaded: loaded,
214 });
215 }
216 }
217
218 Ok((registry, sources))
219}
220
221fn apply_default_agents(config: &mut Config, registry: &AgentRegistry) {
226 if config.developer_agent.is_none() {
227 config.developer_agent = registry
228 .fallback_config()
229 .get_fallbacks(AgentRole::Developer)
230 .first()
231 .cloned();
232 }
233 if config.reviewer_agent.is_none() {
234 config.reviewer_agent = registry
235 .fallback_config()
236 .get_fallbacks(AgentRole::Reviewer)
237 .first()
238 .cloned();
239 }
240}