ralph_workflow/cli/init/smart_init.rs
1/// Handle the smart `--init` flag with a custom path resolver.
2///
3/// This function intelligently determines what the user wants to initialize:
4/// - If a value is provided and matches a known template name -> create PROMPT.md
5/// - If config doesn't exist and no template specified -> create config
6/// - If config exists but PROMPT.md doesn't -> prompt to create PROMPT.md
7/// - If both exist -> show helpful message about what's already set up
8///
9/// # Arguments
10///
11/// * `template_arg` - Optional template name from `--init=TEMPLATE`
12/// * `force` - If true, overwrite existing PROMPT.md without prompting
13/// * `colors` - Terminal color configuration for output
14/// * `env` - Path resolver for determining config file locations
15///
16/// # Returns
17///
18/// Returns `Ok(true)` if the flag was handled (program should exit after),
19/// or `Ok(false)` if not handled, or an error if initialization failed.
20///
21/// # Errors
22///
23/// Returns error if the operation fails.
24pub fn handle_smart_init_with<R: ConfigEnvironment>(
25 template_arg: Option<&str>,
26 force: bool,
27 colors: Colors,
28 env: &R,
29) -> anyhow::Result<bool> {
30 let config_path = env
31 .unified_config_path()
32 .ok_or_else(|| anyhow::anyhow!("Cannot determine config directory (no home directory)"))?;
33 let prompt_path = env.prompt_path();
34 handle_smart_init_at_paths_with_env(
35 template_arg,
36 force,
37 colors,
38 &config_path,
39 &prompt_path,
40 env,
41 )
42}
43
44/// Handle the smart `--init` flag using the default path resolver.
45///
46/// This is a convenience wrapper that uses [`RealConfigEnvironment`] internally.
47///
48/// # Errors
49///
50/// Returns error if the operation fails.
51pub fn handle_smart_init(
52 template_arg: Option<&str>,
53 force: bool,
54 colors: Colors,
55) -> anyhow::Result<bool> {
56 handle_smart_init_with(template_arg, force, colors, &RealConfigEnvironment)
57}
58
59fn handle_smart_init_at_paths_with_env<R: ConfigEnvironment>(
60 template_arg: Option<&str>,
61 force: bool,
62 colors: Colors,
63 config_path: &std::path::Path,
64 prompt_path: &Path,
65 env: &R,
66) -> anyhow::Result<bool> {
67 let config_exists = env.file_exists(config_path);
68 let prompt_exists = env.file_exists(prompt_path);
69
70 // If a template name is provided (non-empty), treat it as --init <template>
71 if let Some(template_name) = template_arg {
72 if !template_name.is_empty() {
73 return handle_init_template_arg_at_path_with_env(
74 template_name,
75 prompt_path,
76 force,
77 colors,
78 env,
79 );
80 }
81 // Empty string means --init was used without a value, fall through to smart inference
82 }
83
84 // No template provided - use smart inference based on current state
85 handle_init_state_inference_with_env(
86 config_path,
87 prompt_path,
88 config_exists,
89 prompt_exists,
90 force,
91 colors,
92 env,
93 )
94}