ralph_workflow/app/
config_init.rs1use crate::agents::{global_agents_config_path, AgentRegistry, AgentRole, ConfigSource};
10use crate::cli::{
11 apply_args_to_config, handle_generate_completion, handle_init_global, handle_init_legacy,
12 handle_init_prompt, handle_list_templates, 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.template_list.list_templates && handle_list_templates(colors) {
91 return Ok(None);
92 }
93
94 if let Some(ref template_name) = args.init_prompt {
96 if handle_init_prompt(template_name, colors)? {
97 return Ok(None);
98 }
99 }
100
101 if args.unified_init.init.is_some()
103 && handle_smart_init(args.unified_init.init.as_deref(), colors)?
104 {
105 return Ok(None);
106 }
107
108 if args.unified_init.init_config && handle_init_global(colors)? {
110 return Ok(None);
111 }
112
113 if args.unified_init.init_global && handle_init_global(colors)? {
115 return Ok(None);
116 }
117
118 if args.legacy_init.init_legacy {
120 let repo_root = get_repo_root().ok();
121 let legacy_path = repo_root.map_or_else(
122 || PathBuf::from(".agent/agents.toml"),
123 |root| root.join(".agent/agents.toml"),
124 );
125 if handle_init_legacy(colors, &legacy_path)? {
126 return Ok(None);
127 }
128 }
129
130 let (registry, config_sources) = load_agent_registry(unified.as_ref(), config_path.as_path())?;
132
133 apply_default_agents(&mut config, ®istry);
135
136 Ok(Some(ConfigInitResult {
137 config,
138 registry,
139 config_path,
140 config_sources,
141 }))
142}
143
144fn load_agent_registry(
145 unified: Option<&UnifiedConfig>,
146 config_path: &std::path::Path,
147) -> anyhow::Result<(AgentRegistry, Vec<ConfigSource>)> {
148 let mut registry = AgentRegistry::new().map_err(|e| {
149 anyhow::anyhow!("Failed to load built-in default agents config (examples/agents.toml): {e}")
150 })?;
151
152 let mut sources = Vec::new();
153
154 if unified.is_none() {
157 if let Some(global_path) = global_agents_config_path() {
158 if global_path.exists() {
159 let loaded = registry.load_from_file(&global_path).map_err(|e| {
160 anyhow::anyhow!(
161 "Failed to load legacy global agent config {}: {}",
162 global_path.display(),
163 e
164 )
165 })?;
166 sources.push(ConfigSource {
167 path: global_path,
168 agents_loaded: loaded,
169 });
170 }
171 }
172
173 let repo_root = get_repo_root().ok();
174 let project_path = repo_root.map_or_else(
175 || PathBuf::from(".agent/agents.toml"),
176 |root| root.join(".agent/agents.toml"),
177 );
178 if project_path.exists() {
179 let loaded = registry.load_from_file(&project_path).map_err(|e| {
180 anyhow::anyhow!(
181 "Failed to load legacy per-repo agent config {}: {}",
182 project_path.display(),
183 e
184 )
185 })?;
186 sources.push(ConfigSource {
187 path: project_path,
188 agents_loaded: loaded,
189 });
190 }
191 }
192
193 if let Some(unified_cfg) = unified {
194 let loaded = registry.apply_unified_config(unified_cfg);
195 if loaded > 0 || unified_cfg.agent_chain.is_some() {
196 sources.push(ConfigSource {
197 path: config_path.to_path_buf(),
198 agents_loaded: loaded,
199 });
200 }
201 }
202
203 Ok((registry, sources))
204}
205
206fn apply_default_agents(config: &mut Config, registry: &AgentRegistry) {
211 if config.developer_agent.is_none() {
212 config.developer_agent = registry
213 .fallback_config()
214 .get_fallbacks(AgentRole::Developer)
215 .first()
216 .cloned();
217 }
218 if config.reviewer_agent.is_none() {
219 config.reviewer_agent = registry
220 .fallback_config()
221 .get_fallbacks(AgentRole::Reviewer)
222 .first()
223 .cloned();
224 }
225}