#[cfg(test)]
#[must_use]
pub fn prompt_plan(prompt_content: Option<&str>) -> String {
use crate::workspace::{Workspace, WorkspaceFs};
use std::env;
let workspace = WorkspaceFs::new(env::current_dir().unwrap());
let partials = get_shared_partials();
let template_content = include_str!("../templates/planning_xml.txt");
let template = Template::new(template_content);
let prompt_md = prompt_content.unwrap_or("No requirements provided");
let variables = HashMap::from([
("PROMPT", prompt_md.to_string()),
(
"PLAN_XML_PATH",
workspace.absolute_str(".agent/tmp/plan.xml"),
),
(
"PLAN_XSD_PATH",
workspace.absolute_str(".agent/tmp/plan.xsd"),
),
]);
template
.render_with_partials(&variables, &partials)
.unwrap_or_else(|_| {
format!(
"PLANNING MODE\n\nCreate an implementation plan for:\n\n{prompt_md}\n\nIdentify critical files and implementation steps.\n\nOutput format: <ralph-plan><ralph-summary>Summary</ralph-summary><ralph-implementation-steps>Steps</ralph-implementation-steps></ralph-plan>\n"
)
})
}
pub fn prompt_plan_with_context(
context: &TemplateContext,
prompt_content: Option<&str>,
workspace: &dyn Workspace,
) -> String {
let partials = get_shared_partials();
let template_content = context
.registry()
.get_template("planning_xml")
.unwrap_or_else(|_| {
include_str!("../templates/planning_xml.txt").to_string()
});
let template = Template::new(&template_content);
let prompt_md = prompt_content.unwrap_or("No requirements provided");
let variables = HashMap::from([
("PROMPT", prompt_md.to_string()),
(
"PLAN_XML_PATH",
workspace.absolute_str(".agent/tmp/plan.xml"),
),
(
"PLAN_XSD_PATH",
workspace.absolute_str(".agent/tmp/plan.xsd"),
),
]);
template
.render_with_partials(&variables, &partials)
.unwrap_or_else(|_| {
format!(
"PLANNING MODE\n\nCreate an implementation plan for:\n\n{prompt_md}\n\nIdentify critical files and implementation steps.\n\nOutput format: <ralph-plan><ralph-summary>Summary</ralph-summary><ralph-implementation-steps>Steps</ralph-implementation-steps></ralph-plan>\n"
)
})
}
pub fn prompt_planning_xml_with_context(
context: &TemplateContext,
prompt_content: Option<&str>,
workspace: &dyn Workspace,
) -> String {
let partials = get_shared_partials();
write_planning_xsd_schema_file(workspace);
let template_content = context
.registry()
.get_template("planning_xml")
.unwrap_or_else(|_| include_str!("../templates/planning_xml.txt").to_string());
let template = Template::new(&template_content);
let prompt_md = prompt_content.unwrap_or("No requirements provided");
let variables = HashMap::from([
("PROMPT", prompt_md.to_string()),
(
"PLAN_XML_PATH",
workspace.absolute_str(".agent/tmp/plan.xml"),
),
(
"PLAN_XSD_PATH",
workspace.absolute_str(".agent/tmp/plan.xsd"),
),
]);
template
.render_with_partials(&variables, &partials)
.unwrap_or_else(|_| {
format!(
"PLANNING MODE\n\nCreate an implementation plan for:\n\n{prompt_md}\n\n\
Output format: <ralph-plan><ralph-summary>Summary</ralph-summary><ralph-implementation-steps>Steps</ralph-implementation-steps></ralph-plan>\n"
)
})
}
pub fn prompt_planning_xml_with_references_and_log(
context: &TemplateContext,
prompt_ref: &super::content_reference::PromptContentReference,
workspace: &dyn Workspace,
template_name: &str,
) -> crate::prompts::RenderedTemplate {
use crate::prompts::{
RenderedTemplate, SubstitutionEntry, SubstitutionLog, SubstitutionSource,
};
let partials = get_shared_partials();
write_planning_xsd_schema_file(workspace);
let template_content = context
.registry()
.get_template("planning_xml")
.unwrap_or_else(|_| include_str!("../templates/planning_xml.txt").to_string());
let template = Template::new(&template_content);
let variables = HashMap::from([
("PROMPT", prompt_ref.render_for_template()),
(
"PLAN_XML_PATH",
workspace.absolute_str(".agent/tmp/plan.xml"),
),
(
"PLAN_XSD_PATH",
workspace.absolute_str(".agent/tmp/plan.xsd"),
),
]);
match template.render_with_log(template_name, &variables, &partials) {
Ok(rendered) => rendered,
Err(err) => {
let unsubstituted = match &err {
crate::prompts::template_engine::TemplateError::MissingVariable(name) => {
vec![name.clone()]
}
_ => vec![],
};
let prompt = prompt_ref.render_for_template();
let prompt_content =
format!("PLANNING MODE\n\nCreate an implementation plan for:\n\n{prompt}\n");
RenderedTemplate {
content: prompt_content,
log: SubstitutionLog {
template_name: template_name.to_string(),
substituted: vec![SubstitutionEntry {
name: "PROMPT".to_string(),
source: SubstitutionSource::Value,
}],
unsubstituted,
},
}
}
}
}
pub fn prompt_planning_xml_with_references(
context: &TemplateContext,
prompt_ref: &super::content_reference::PromptContentReference,
workspace: &dyn Workspace,
) -> String {
let partials = get_shared_partials();
write_planning_xsd_schema_file(workspace);
let template_content = context
.registry()
.get_template("planning_xml")
.unwrap_or_else(|_| include_str!("../templates/planning_xml.txt").to_string());
let template = Template::new(&template_content);
let variables = HashMap::from([
("PROMPT", prompt_ref.render_for_template()),
(
"PLAN_XML_PATH",
workspace.absolute_str(".agent/tmp/plan.xml"),
),
(
"PLAN_XSD_PATH",
workspace.absolute_str(".agent/tmp/plan.xsd"),
),
]);
template
.render_with_partials(&variables, &partials)
.unwrap_or_else(|_| {
let prompt = prompt_ref.render_for_template();
format!("PLANNING MODE\n\nCreate an implementation plan for:\n\n{prompt}\n")
})
}
pub fn prompt_planning_xsd_retry_with_context_files(
context: &TemplateContext,
xsd_error: &str,
workspace: &dyn Workspace,
) -> String {
use std::path::Path;
let partials = get_shared_partials();
write_planning_xsd_retry_schema_files(workspace);
let schema_path = Path::new(".agent/tmp/plan.xsd");
let last_output_path = Path::new(".agent/tmp/last_output.xml");
let schema_exists = workspace.exists(schema_path);
let last_output_exists = workspace.exists(last_output_path);
let diagnostic_prefix = if !schema_exists || !last_output_exists {
let parts: Vec<String> =
std::iter::once("⚠️ WARNING: Required XSD retry files are missing:\n".to_string())
.chain(
if !schema_exists {
Some(format!(
" - Schema file: {} (workspace.root() = {})\n",
workspace.absolute_str(".agent/tmp/plan.xsd"),
workspace.root().display()
))
} else {
None
},
)
.chain(if !last_output_exists {
Some(format!(
" - Last output: {} (workspace.root() = {})\n",
workspace.absolute_str(".agent/tmp/last_output.xml"),
workspace.root().display()
))
} else {
None
})
.chain(std::iter::once(
"This likely indicates CWD != workspace.root() path mismatch.\n\n".to_string(),
))
.collect();
parts.concat()
} else {
String::new()
};
if !schema_exists && !last_output_exists {
return format!(
"{diagnostic_prefix}XSD VALIDATION FAILED - CREATE IMPLEMENTATION PLAN\n\n\
Error: {xsd_error}\n\n\
The schema and previous output files could not be found. \
Please create an implementation plan for the requirements in PROMPT.md.\n\n\
Output format: <ralph-plan><ralph-summary>Summary</ralph-summary><ralph-implementation-steps>Steps</ralph-implementation-steps></ralph-plan>\n"
);
}
let template_content = context
.registry()
.get_template("planning_xsd_retry")
.unwrap_or_else(|_| include_str!("../templates/planning_xsd_retry.txt").to_string());
let variables = HashMap::from([
("XSD_ERROR", xsd_error.to_string()),
(
"PLAN_XML_PATH",
workspace.absolute_str(".agent/tmp/plan.xml"),
),
(
"PLAN_XSD_PATH",
workspace.absolute_str(".agent/tmp/plan.xsd"),
),
(
"LAST_OUTPUT_XML_PATH",
workspace.absolute_str(".agent/tmp/last_output.xml"),
),
]);
let rendered_prompt = Template::new(&template_content)
.render_with_partials(&variables, &partials)
.unwrap_or_else(|_| {
format!(
"Your previous plan failed XSD validation.\n\nError: {xsd_error}\n\n\
Read .agent/tmp/plan.xsd for the schema and .agent/tmp/last_output.xml for your previous output.\n\
Please resend your plan in valid XML format conforming to the XSD schema.\n"
)
});
if diagnostic_prefix.is_empty() {
rendered_prompt
} else {
format!("{diagnostic_prefix}\n{rendered_prompt}")
}
}
pub fn prompt_planning_xsd_retry_with_context_files_and_log(
context: &TemplateContext,
xsd_error: &str,
workspace: &dyn Workspace,
template_name: &str,
) -> crate::prompts::RenderedTemplate {
use crate::prompts::{
RenderedTemplate, SubstitutionEntry, SubstitutionLog, SubstitutionSource,
};
use std::path::Path;
let partials = get_shared_partials();
write_planning_xsd_retry_schema_files(workspace);
let schema_path = Path::new(".agent/tmp/plan.xsd");
let last_output_path = Path::new(".agent/tmp/last_output.xml");
let schema_exists = workspace.exists(schema_path);
let last_output_exists = workspace.exists(last_output_path);
let diagnostic_prefix = if !schema_exists || !last_output_exists {
let parts: Vec<String> =
std::iter::once("⚠️ WARNING: Required XSD retry files are missing:\n".to_string())
.chain(
if !schema_exists {
Some(format!(
" - Schema file: {} (workspace.root() = {})\n",
workspace.absolute_str(".agent/tmp/plan.xsd"),
workspace.root().display()
))
} else {
None
},
)
.chain(if !last_output_exists {
Some(format!(
" - Last output: {} (workspace.root() = {})\n",
workspace.absolute_str(".agent/tmp/last_output.xml"),
workspace.root().display()
))
} else {
None
})
.chain(std::iter::once(
"This likely indicates CWD != workspace.root() path mismatch.\n\n".to_string(),
))
.collect();
parts.concat()
} else {
String::new()
};
if !schema_exists && !last_output_exists {
let prompt_content = format!(
"{diagnostic_prefix}XSD VALIDATION FAILED - CREATE IMPLEMENTATION PLAN\n\n\
Error: {xsd_error}\n\n\
The schema and previous output files could not be found. \
Please create an implementation plan for the requirements in PROMPT.md.\n\n\
Output format: <ralph-plan><ralph-summary>Summary</ralph-summary><ralph-implementation-steps>Steps</ralph-implementation-steps></ralph-plan>\n"
);
return RenderedTemplate {
content: prompt_content,
log: SubstitutionLog {
template_name: template_name.to_string(),
substituted: vec![SubstitutionEntry {
name: "XSD_ERROR".to_string(),
source: SubstitutionSource::Value,
}],
unsubstituted: vec![],
},
};
}
let template_content = context
.registry()
.get_template("planning_xsd_retry")
.unwrap_or_else(|_| include_str!("../templates/planning_xsd_retry.txt").to_string());
let variables = HashMap::from([
("XSD_ERROR", xsd_error.to_string()),
(
"PLAN_XML_PATH",
workspace.absolute_str(".agent/tmp/plan.xml"),
),
(
"PLAN_XSD_PATH",
workspace.absolute_str(".agent/tmp/plan.xsd"),
),
(
"LAST_OUTPUT_XML_PATH",
workspace.absolute_str(".agent/tmp/last_output.xml"),
),
]);
let template = Template::new(&template_content);
template
.render_with_log(template_name, &variables, &partials)
.map(|mut rendered| {
if !diagnostic_prefix.is_empty() {
rendered.content = format!("{}\n{}", diagnostic_prefix, rendered.content);
}
rendered
})
.unwrap_or_else(|_| {
let prompt_content = format!(
"Your previous plan failed XSD validation.\n\nError: {xsd_error}\n\n\
Read .agent/tmp/plan.xsd for the schema and .agent/tmp/last_output.xml for your previous output.\n\
Please resend your plan in valid XML format conforming to the XSD schema.\n"
);
RenderedTemplate {
content: prompt_content,
log: SubstitutionLog {
template_name: template_name.to_string(),
substituted: vec![SubstitutionEntry {
name: "XSD_ERROR".to_string(),
source: SubstitutionSource::Value,
}],
unsubstituted: vec![],
},
}
})
}
pub fn prompt_planning_xsd_retry_with_context(
context: &TemplateContext,
_prompt_content: &str,
xsd_error: &str,
last_output: &str,
workspace: &dyn Workspace,
) -> String {
write_planning_xsd_retry_files(workspace, last_output);
prompt_planning_xsd_retry_with_context_files(context, xsd_error, workspace)
}