ralph_workflow/agents/ccs/
agent_config.rs1fn build_ccs_config_from_flags(
3 alias_config: &CcsAliasConfig,
4 defaults: &CcsConfig,
5 cmd: String,
6 env_vars: HashMap<String, String>,
7 display_name: String,
8) -> AgentConfig {
9 let output_flag = alias_config
10 .output_flag
11 .clone()
12 .unwrap_or_else(|| defaults.output_flag.clone());
13 let yolo_flag = alias_config
14 .yolo_flag
15 .clone()
16 .unwrap_or_else(|| defaults.yolo_flag.clone());
17 let verbose_flag = alias_config
18 .verbose_flag
19 .clone()
20 .unwrap_or_else(|| defaults.verbose_flag.clone());
21 let print_flag = alias_config.print_flag.clone().unwrap_or_else(|| {
26 let pf = defaults.print_flag.clone();
27 if pf.is_empty() {
28 "--print".to_string()
30 } else {
31 pf
32 }
33 });
34
35 let json_parser = alias_config
39 .json_parser
40 .as_deref()
41 .unwrap_or(&defaults.json_parser);
42 let can_commit = alias_config.can_commit.unwrap_or(defaults.can_commit);
43
44 let streaming_flag = alias_config
46 .streaming_flag
47 .clone()
48 .unwrap_or_else(|| defaults.streaming_flag.clone());
49
50 let session_flag = alias_config
53 .session_flag
54 .clone()
55 .unwrap_or_else(|| defaults.session_flag.clone());
56
57 AgentConfig {
58 cmd, output_flag,
60 yolo_flag,
61 verbose_flag,
62 can_commit,
63 json_parser: JsonParserType::parse(json_parser),
64 model_flag: alias_config.model_flag.clone(),
65 print_flag, streaming_flag, session_flag, env_vars, display_name: Some(display_name),
70 }
71}
72
73#[cfg(any(test, feature = "test-utils"))]
78#[must_use]
79pub fn build_ccs_agent_config(
80 alias_config: &CcsAliasConfig,
81 defaults: &CcsConfig,
82 display_name: String,
83 alias_name: &str,
84) -> AgentConfig {
85 build_ccs_agent_config_impl(alias_config, defaults, display_name, alias_name, |key| {
86 crate::agents::runtime::get_env_var(key)
87 })
88}
89
90#[cfg(not(any(test, feature = "test-utils")))]
91pub fn build_ccs_agent_config(
92 alias_config: &CcsAliasConfig,
93 defaults: &CcsConfig,
94 display_name: String,
95 alias_name: &str,
96) -> AgentConfig {
97 build_ccs_agent_config_impl(alias_config, defaults, display_name, alias_name, |key| {
98 crate::agents::runtime::get_env_var(key)
99 })
100}
101
102#[expect(
103 clippy::print_stderr,
104 reason = "user-facing informative messages for CCS configuration"
105)]
106fn build_ccs_agent_config_impl(
107 alias_config: &CcsAliasConfig,
108 defaults: &CcsConfig,
109 display_name: String,
110 alias_name: &str,
111 get_env_var: impl Fn(&str) -> Option<String>,
112) -> AgentConfig {
113 let debug_mode = get_env_var("RALPH_CCS_DEBUG").is_some();
115
116 let ((env_vars, env_vars_loaded), profile_used_for_env) = if alias_name.is_empty() {
118 ((HashMap::new(), false), None)
119 } else if is_glm_alias(alias_name) {
120 let original_cmd = alias_config.cmd.as_str();
121 let profile =
122 ccs_profile_from_command(original_cmd).unwrap_or_else(|| alias_name.to_string());
123 let result = match load_ccs_env_vars_with_guess(&profile) {
124 Ok((vars, guessed)) => {
125 if let Some(guessed) = guessed {
126 eprintln!("Info: CCS profile '{profile}' not found; using '{guessed}'");
127 }
128 let loaded = !vars.is_empty();
129 (vars, loaded)
130 }
131 Err(err) => {
132 let suggestions = find_ccs_profile_suggestions(&profile);
133 eprintln!("Warning: failed to load CCS env vars for profile '{profile}': {err}");
134 if !suggestions.is_empty() {
135 eprintln!("Tip: available/nearby CCS profiles:");
136 suggestions.iter().for_each(|s| {
137 eprintln!(" - {s}");
138 });
139 }
140 (HashMap::new(), false)
141 }
142 };
143 (result, Some(profile))
144 } else {
145 ((HashMap::new(), false), None)
148 };
149
150 let cmd = resolve_ccs_command(
152 alias_config,
153 alias_name,
154 env_vars_loaded,
155 profile_used_for_env.as_ref(),
156 debug_mode,
157 );
158
159 build_ccs_config_from_flags(alias_config, defaults, cmd.command, env_vars, display_name)
161}
162
163#[derive(Debug, Clone, Default)]
165pub struct CcsAliasResolver {
166 aliases: HashMap<String, CcsAliasConfig>,
167 defaults: CcsConfig,
168}
169
170impl CcsAliasResolver {
171 #[must_use]
173 pub const fn new(aliases: HashMap<String, CcsAliasConfig>, defaults: CcsConfig) -> Self {
174 Self { aliases, defaults }
175 }
176
177 #[must_use]
179 pub fn empty() -> Self {
180 Self::default()
181 }
182
183 #[must_use]
191 pub fn try_resolve(&self, agent_name: &str) -> Option<AgentConfig> {
192 let alias = parse_ccs_ref(agent_name)?;
193 if let Some(config) = resolve_ccs_agent(alias, &self.aliases, &self.defaults) {
195 return Some(config);
196 }
197 let cmd = CcsAliasConfig {
200 cmd: format!("ccs {alias}"),
201 ..CcsAliasConfig::default()
202 };
203 let display_name = format!("ccs-{alias}");
204 Some(build_ccs_agent_config(
205 &cmd,
206 &self.defaults,
207 display_name,
208 alias,
209 ))
210 }
211}