use crate::config::ConfigEnvironment;
use crate::logger::Colors;
use crate::templates::{get_template, list_templates};
use std::io::Write;
use std::path::Path;
use super::global::handle_init_global_with;
use super::super::{
can_prompt_user, find_similar_templates, print_common_work_guides, prompt_for_template,
prompt_overwrite_confirmation,
};
fn create_minimal_prompt_md() -> String {
"# Task Description
Describe what you want the AI agents to implement.
## Example
\"Fix the typo in the README file\"
## Context
Provide any relevant context about the task:
- What problem are you trying to solve?
- What are the acceptance criteria?
- Are there any specific requirements or constraints?
## Notes
- This is a minimal PROMPT.md created by `ralph --init`
- You can edit this file directly or use `ralph --init <work-guide>` to start from a Work Guide
- Run `ralph --list-work-guides` to see all available Work Guides
"
.to_string()
}
pub fn create_prompt_from_template<R: ConfigEnvironment>(
template_name: &str,
prompt_path: &Path,
force: bool,
colors: Colors,
env: &R,
) -> anyhow::Result<bool> {
let Some(template) = get_template(template_name) else {
let _ = writeln!(
std::io::stdout(),
"{}Unknown Work Guide: '{}'{}",
colors.red(),
template_name,
colors.reset()
);
let similar = find_similar_templates(template_name);
if !similar.is_empty() {
let _ = writeln!(
std::io::stdout(),
"{}Did you mean:?{}",
colors.yellow(),
colors.reset()
);
similar.into_iter().for_each(|(name, score)| {
let _ = writeln!(std::io::stdout(), " - {} ({}% match)", name, score);
});
}
return Ok(false);
};
if env.file_exists(prompt_path) && !force {
let response =
prompt_overwrite_confirmation(prompt_path.to_string_lossy().as_ref(), colors)?;
if !response {
let _ = writeln!(
std::io::stdout(),
"{}Aborted.{}",
colors.yellow(),
colors.reset()
);
return Ok(false);
}
}
env.write_file(prompt_path, template.content().as_bytes())?;
let _ = writeln!(
std::io::stdout(),
"{}Created PROMPT.md from '{}' work guide{}",
colors.green(),
template.name(),
colors.reset()
);
Ok(true)
}
pub fn infer_init_action<R: ConfigEnvironment>(
config_path: &Path,
prompt_path: &Path,
colors: Colors,
env: &R,
) -> InitInferredAction {
let has_config = env.file_exists(config_path);
let has_prompt = env.file_exists(prompt_path);
match (has_config, has_prompt) {
(true, true) => InitInferredAction::BothExist,
(true, false) => InitInferredAction::ConfigOnly,
(false, true) => InitInferredAction::PromptOnly,
(false, false) => InitInferredAction::NeitherExists,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InitInferredAction {
BothExists,
ConfigOnly,
PromptOnly,
NeitherExists,
}
pub fn handle_init_state_inference_with_env<R: ConfigEnvironment>(
config_path: &Path,
prompt_path: &Path,
template_arg: Option<&str>,
colors: Colors,
env: &R,
) -> anyhow::Result<bool> {
let action = infer_init_action(config_path, prompt_path, colors, env);
match (action, template_arg) {
(_, Some(template_name)) => {
create_prompt_from_template(template_name, prompt_path, false, colors, env)
}
(InitInferredAction::BothExists, None) => {
let _ = writeln!(
std::io::stdout(),
"{}Found existing config and PROMPT.md{}",
colors.yellow(),
colors.reset()
);
let response = prompt_overwrite_confirmation("Reinitialize both?", colors)?;
if response {
handle_init_global_with(config_path, true, colors, env)?;
create_prompt_from_template("blank", prompt_path, true, colors, env)?;
return Ok(true);
}
Ok(false)
}
(InitInferredAction::ConfigOnly, None) => {
let _ = writeln!(
std::io::stdout(),
"{}Found existing config but no PROMPT.md{}",
colors.yellow(),
colors.reset()
);
let response = prompt_overwrite_confirmation("Create PROMPT.md?", colors)?;
if response {
let _ = writeln!(
std::io::stdout(),
"\n{}Available Work Guides:{}",
colors.blue(),
colors.reset()
);
list_templates(colors);
if can_prompt_user() {
if let Some(template_name) = prompt_for_template(colors) {
return create_prompt_from_template(
&template_name,
prompt_path,
false,
colors,
env,
);
}
} else {
let content = create_minimal_prompt_md();
env.write_file(prompt_path, content.as_bytes())?;
let _ = writeln!(
std::io::stdout(),
"{}Created minimal PROMPT.md (non-interactive mode){}",
colors.green(),
colors.reset()
);
}
}
Ok(false)
}
(InitInferredAction::PromptOnly, None) => {
let _ = writeln!(
std::io::stdout(),
"{}Found existing PROMPT.md but no config{}",
colors.yellow(),
colors.reset()
);
let response = prompt_overwrite_confirmation("Create config?", colors)?;
if response {
handle_init_global_with(config_path, false, colors, env)?;
return Ok(true);
}
Ok(false)
}
(InitInferredAction::NeitherExists, None) => {
let _ = writeln!(
std::io::stdout(),
"\n{}Available Work Guides:{}",
colors.blue(),
colors.reset()
);
list_templates(colors);
let _ = writeln!(std::io::stdout());
print_common_work_guides(colors);
if can_prompt_user() {
if let Some(template_name) = prompt_for_template(colors) {
match create_prompt_from_template(
&template_name,
prompt_path,
false,
colors,
env,
) {
Ok(true) => return Ok(true),
Ok(false) => {
}
Err(e) => {
let _ = writeln!(
std::io::stdout(),
"{}Failed to create PROMPT.md: {}{}",
colors.red(),
e,
colors.reset()
);
return Ok(false);
}
}
}
}
let content = create_minimal_prompt_md();
env.write_file(prompt_path, content.as_bytes())?;
let _ = writeln!(
std::io::stdout(),
"{}Created minimal PROMPT.md (non-interactive mode){}",
colors.green(),
colors.reset()
);
super::super::handle_extended_help();
Ok(true)
}
}
}
pub fn handle_init_template_arg_at_path_with_env<R: ConfigEnvironment>(
template_name: &str,
prompt_path: &Path,
colors: Colors,
env: &R,
) -> anyhow::Result<bool> {
create_prompt_from_template(template_name, prompt_path, false, colors, env)
}