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