1pub fn handle_init_global_with<R: ConfigEnvironment>(
21 colors: Colors,
22 env: &R,
23) -> anyhow::Result<bool> {
24 let global_path = env
25 .unified_config_path()
26 .ok_or_else(|| anyhow::anyhow!("Cannot determine config directory (no home directory)"))?;
27
28 if env.file_exists(&global_path) {
30 println!(
31 "{}Unified config already exists:{} {}",
32 colors.yellow(),
33 colors.reset(),
34 global_path.display()
35 );
36 println!("Edit the file to customize, or delete it to regenerate from defaults.");
37 println!();
38 println!("Next steps:");
39 println!(" 1. Create a PROMPT.md for your task:");
40 println!(" ralph --init <work-guide>");
41 println!(" ralph --list-work-guides # Show all Work Guides");
42 println!(" 2. Or run ralph directly with default settings:");
43 println!(" ralph \"your commit message\"");
44 return Ok(true);
45 }
46
47 env.write_file(&global_path, crate::config::unified::DEFAULT_UNIFIED_CONFIG)
49 .map_err(|e| {
50 anyhow::anyhow!(
51 "Failed to create config file {}: {}",
52 global_path.display(),
53 e
54 )
55 })?;
56
57 println!(
58 "{}Created unified config: {}{}{}\n",
59 colors.green(),
60 colors.bold(),
61 global_path.display(),
62 colors.reset()
63 );
64 println!("This is the primary configuration file for Ralph.");
65 println!();
66 println!("Features:");
67 println!(" - General settings (verbosity, iterations, etc.)");
68 println!(" - CCS aliases for Claude Code Switch integration");
69 println!(" - Custom agent definitions");
70 println!(" - Agent chain configuration with fallbacks");
71 println!();
72 println!("Environment variables (RALPH_*) override these settings.");
73 println!();
74 println!("Next steps:");
75 println!(" 1. Create a PROMPT.md for your task:");
76 println!(" ralph --init <work-guide>");
77 println!(" ralph --list-work-guides # Show all Work Guides");
78 println!(" 2. Or run ralph directly with default settings:");
79 println!(" ralph \"your commit message\"");
80 Ok(true)
81}
82
83pub fn handle_init_global(colors: Colors) -> anyhow::Result<bool> {
97 handle_init_global_with(colors, &RealConfigEnvironment)
98}
99
100fn create_minimal_prompt_md() -> String {
102 "# Task Description
103
104Describe what you want the AI agents to implement.
105
106## Example
107
108\"Fix the typo in the README file\"
109
110## Context
111
112Provide any relevant context about the task:
113- What problem are you trying to solve?
114- What are the acceptance criteria?
115- Are there any specific requirements or constraints?
116
117## Notes
118
119- This is a minimal PROMPT.md created by `ralph --init`
120- You can edit this file directly or use `ralph --init <work-guide>` to start from a Work Guide
121- Run `ralph --list-work-guides` to see all available Work Guides
122"
123 .to_string()
124}
125
126fn create_prompt_from_template<R: ConfigEnvironment>(
134 template_name: &str,
135 prompt_path: &Path,
136 force: bool,
137 colors: Colors,
138 env: &R,
139) -> anyhow::Result<bool> {
140 let Some(template) = get_template(template_name) else {
142 println!(
143 "{}Unknown Work Guide: '{}'{}",
144 colors.red(),
145 template_name,
146 colors.reset()
147 );
148 println!();
149 let similar = find_similar_templates(template_name);
150 if !similar.is_empty() {
151 println!("{}Did you mean?{}", colors.yellow(), colors.reset());
152 for (name, score) in similar {
153 println!(
154 " {}{}{} ({}% similar)",
155 colors.cyan(),
156 name,
157 colors.reset(),
158 score
159 );
160 }
161 println!();
162 }
163 println!("Commonly used Work Guides:");
164 print_common_work_guides(colors);
165 println!("Usage: ralph --init <work-guide>");
166 return Ok(true);
167 };
168
169 let content = template.content();
170
171 let file_exists = env.file_exists(prompt_path);
173
174 if force || !file_exists {
175 env.write_file(prompt_path, content)?;
177 } else {
178 if can_prompt_user() {
180 if !prompt_overwrite_confirmation(prompt_path, colors)? {
181 return Ok(true);
182 }
183 env.write_file(prompt_path, content)?;
184 } else {
185 return Err(anyhow::anyhow!(
186 "PROMPT.md already exists: {}\nRefusing to overwrite in non-interactive mode. Use --force-overwrite to overwrite, or delete/backup the existing file.",
187 prompt_path.display()
188 ));
189 }
190 }
191
192 println!(
193 "{}Created PROMPT.md from template: {}{}{}",
194 colors.green(),
195 colors.bold(),
196 template_name,
197 colors.reset()
198 );
199 println!();
200 println!(
201 "Template: {}{}{} {}",
202 colors.cyan(),
203 template.name(),
204 colors.reset(),
205 template.description()
206 );
207 println!();
208 println!("Next steps:");
209 println!(" 1. Edit PROMPT.md with your task details");
210 println!(" 2. Run: ralph \"your commit message\"");
211 println!();
212 println!("Tip: Use --list-work-guides to see all available Work Guides.");
213
214 Ok(true)
215}
216
217fn handle_init_template_arg_at_path_with_env<R: ConfigEnvironment>(
219 template_name: &str,
220 prompt_path: &Path,
221 force: bool,
222 colors: Colors,
223 env: &R,
224) -> anyhow::Result<bool> {
225 if get_template(template_name).is_some() {
226 return create_prompt_from_template(template_name, prompt_path, force, colors, env);
227 }
228
229 println!(
231 "{}Unknown Work Guide: '{}'{}",
232 colors.red(),
233 template_name,
234 colors.reset()
235 );
236 println!();
237
238 let similar = find_similar_templates(template_name);
240 if !similar.is_empty() {
241 println!("{}Did you mean?{}", colors.yellow(), colors.reset());
242 for (name, score) in similar {
243 println!(
244 " {}{}{} ({}% similar)",
245 colors.cyan(),
246 name,
247 colors.reset(),
248 score
249 );
250 }
251 println!();
252 }
253
254 println!("Commonly used Work Guides:");
255 print_common_work_guides(colors);
256 println!("Usage: ralph --init=<work-guide>");
257 println!(" ralph --init # Smart init (infers intent)");
258 Ok(true)
259}
260
261fn handle_init_state_inference_with_env<R: ConfigEnvironment>(
263 config_path: &std::path::Path,
264 prompt_path: &Path,
265 config_exists: bool,
266 prompt_exists: bool,
267 force: bool,
268 colors: Colors,
269 env: &R,
270) -> anyhow::Result<bool> {
271 match (config_exists, prompt_exists) {
272 (false, false) => handle_init_none_exist_with_env(config_path, colors, env),
273 (true, false) => Ok(handle_init_only_config_exists_with_env(
274 config_path,
275 prompt_path,
276 force,
277 colors,
278 env,
279 )),
280 (false, true) => handle_init_only_prompt_exists_with_env(colors, env),
281 (true, true) => Ok(handle_init_both_exist(
282 config_path,
283 prompt_path,
284 force,
285 colors,
286 )),
287 }
288}
289
290fn handle_init_none_exist_with_env<R: ConfigEnvironment>(
292 _config_path: &std::path::Path,
293 colors: Colors,
294 env: &R,
295) -> anyhow::Result<bool> {
296 println!(
297 "{}No config found. Creating unified config...{}",
298 colors.dim(),
299 colors.reset()
300 );
301 println!();
302 handle_init_global_with(colors, env)?;
303 Ok(true)
304}
305
306fn handle_init_only_config_exists_with_env<R: ConfigEnvironment>(
308 config_path: &std::path::Path,
309 prompt_path: &Path,
310 force: bool,
311 colors: Colors,
312 env: &R,
313) -> bool {
314 println!(
315 "{}Config found at:{} {}",
316 colors.green(),
317 colors.reset(),
318 config_path.display()
319 );
320 println!(
321 "{}PROMPT.md not found in current directory.{}",
322 colors.yellow(),
323 colors.reset()
324 );
325 println!();
326
327 print_common_work_guides(colors);
329
330 if can_prompt_user() {
332 if let Some(template_name) = prompt_for_template(colors) {
334 match create_prompt_from_template(&template_name, prompt_path, force, colors, env) {
335 Ok(_) => return true,
336 Err(e) => {
337 println!(
338 "{}Failed to create PROMPT.md: {}{}",
339 colors.red(),
340 e,
341 colors.reset()
342 );
343 return true;
344 }
345 }
346 }
347 } else {
349 let default_content = create_minimal_prompt_md();
351
352 if env.file_exists(prompt_path) {
354 println!(
355 "{}PROMPT.md already exists:{} {}",
356 colors.yellow(),
357 colors.reset(),
358 prompt_path.display()
359 );
360 println!("Use --force-overwrite to overwrite, or delete/backup the existing file.");
361 return true;
362 }
363
364 match env.write_file(prompt_path, &default_content) {
366 Ok(()) => {
367 println!(
368 "{}Created minimal PROMPT.md{}",
369 colors.green(),
370 colors.reset()
371 );
372 println!();
373 println!("Next steps:");
374 println!(" 1. Edit PROMPT.md with your task details");
375 println!(" 2. Run: ralph \"your commit message\"");
376 println!();
377 println!("Tip: Use ralph --list-work-guides to see all available Work Guides.");
378 return true;
379 }
380 Err(e) => {
381 println!(
382 "{}Failed to create PROMPT.md: {}{}",
383 colors.red(),
384 e,
385 colors.reset()
386 );
387 return true;
388 }
389 }
390 }
391
392 println!("Create a PROMPT.md from a Work Guide to get started:");
394 println!();
395
396 for (name, description) in list_templates() {
397 println!(
398 " {}{}{} {}{}{}",
399 colors.cyan(),
400 name,
401 colors.reset(),
402 colors.dim(),
403 description,
404 colors.reset()
405 );
406 }
407
408 println!();
409 println!("Usage: ralph --init <work-guide>");
410 println!();
411 println!("Example:");
412 println!(" ralph --init bug-fix");
413 println!(" ralph --init feature-spec");
414 true
415}
416
417fn handle_init_only_prompt_exists_with_env<R: ConfigEnvironment>(
419 colors: Colors,
420 env: &R,
421) -> anyhow::Result<bool> {
422 println!(
423 "{}PROMPT.md found in current directory.{}",
424 colors.green(),
425 colors.reset()
426 );
427 println!(
428 "{}No config found. Creating unified config...{}",
429 colors.dim(),
430 colors.reset()
431 );
432 println!();
433 handle_init_global_with(colors, env)?;
434 Ok(true)
435}