use std::path::Path;
pub fn resolve_prompt(root: &str, prompt_ref: &str) -> Result<String, String> {
let resolved_root = Path::new(root)
.canonicalize()
.map_err(|e| format!("Cannot resolve project root: {}", e))?;
if prompt_ref.contains('/') {
let full_path = resolved_root.join(prompt_ref);
if let Ok(canonical) = full_path.canonicalize() {
if !canonical.starts_with(&resolved_root) {
return Err(format!("Prompt path escapes project root: {}", prompt_ref));
}
return Ok(canonical.to_string_lossy().to_string());
}
let normalized = resolved_root.join(prompt_ref);
if normalized.exists() {
return Ok(normalized.to_string_lossy().to_string());
}
return Err(format!("Prompt file not found: {}", full_path.display()));
}
let candidates = [
resolved_root.join("prompts").join(prompt_ref),
resolved_root.join(".7").join("prompts").join(prompt_ref),
];
for candidate in &candidates {
if candidate.exists() {
return Ok(candidate.to_string_lossy().to_string());
}
}
Err(format!(
"Prompt \"{}\" not found in: {}",
prompt_ref,
candidates
.iter()
.map(|c| c.display().to_string())
.collect::<Vec<_>>()
.join(", ")
))
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::TempDir;
#[test]
fn test_resolve_prompt_relative_path() {
let dir = TempDir::new().unwrap();
let prompts_dir = dir.path().join("prompts");
fs::create_dir_all(&prompts_dir).unwrap();
fs::write(prompts_dir.join("test.md"), "prompt content").unwrap();
let result = resolve_prompt(dir.path().to_str().unwrap(), "prompts/test.md");
assert!(result.is_ok());
}
#[test]
fn test_resolve_prompt_bare_name_primary() {
let dir = TempDir::new().unwrap();
let prompts_dir = dir.path().join("prompts");
fs::create_dir_all(&prompts_dir).unwrap();
fs::write(prompts_dir.join("test.md"), "content").unwrap();
let result = resolve_prompt(dir.path().to_str().unwrap(), "test.md");
assert!(result.is_ok());
}
#[test]
fn test_resolve_prompt_bare_name_secondary() {
let dir = TempDir::new().unwrap();
let dot7_prompts = dir.path().join(".7").join("prompts");
fs::create_dir_all(&dot7_prompts).unwrap();
fs::write(dot7_prompts.join("test.md"), "content").unwrap();
let result = resolve_prompt(dir.path().to_str().unwrap(), "test.md");
assert!(result.is_ok());
}
#[test]
fn test_resolve_prompt_not_found() {
let dir = TempDir::new().unwrap();
let result = resolve_prompt(dir.path().to_str().unwrap(), "nonexistent.md");
assert!(result.is_err());
}
}