1use std::path::PathBuf;
4
5use crate::datetime::current_date;
6use crate::tool::Tool;
7
8const DEFAULT_PROMPT: &str = include_str!("../prompts/default.md");
11
12pub struct PromptBuilder {
13 custom_prompt: Option<String>,
14}
15
16impl PromptBuilder {
17 pub fn new() -> Self {
18 let custom_path = Self::custom_prompt_path();
20 let custom_prompt = if let Some(path) = custom_path {
21 std::fs::read_to_string(&path).ok()
22 } else {
23 None
24 };
25
26 Self { custom_prompt }
27 }
28
29 pub fn build_system_prompt(
33 &self,
34 tools: &[&dyn Tool],
35 skills: &[(&str, &str)],
36 working_dir: &std::path::Path,
37 ) -> String {
38 let tools_section = Self::build_tools_section(tools);
39 let skills_section = Self::build_skills_section(skills);
40 let os = std::env::consts::OS;
41 let cwd = working_dir.display().to_string();
42 let date = current_date();
43
44 if let Some(custom) = &self.custom_prompt {
45 custom
47 .replace("{os}", os)
48 .replace("{cwd}", &cwd)
49 .replace("{date}", &date)
50 .replace("{tools_section}", &tools_section)
51 .replace("{skills_section}", &skills_section)
52 } else {
53 DEFAULT_PROMPT
54 .replace("{os}", os)
55 .replace("{cwd}", &cwd)
56 .replace("{date}", &date)
57 .replace("{tools_section}", &tools_section)
58 .replace("{skills_section}", &skills_section)
59 }
60 }
61
62 fn build_tools_section(tools: &[&dyn Tool]) -> String {
64 if tools.is_empty() {
65 return "(no available tools)".to_string();
66 }
67
68 let mut section = String::new();
69 for tool in tools {
70 section.push_str(&format!(
71 "- **{}**: {}{}\n",
72 tool.name(),
73 tool.description(),
74 if tool.requires_confirmation() {
75 " (requires user confirmation)"
76 } else {
77 ""
78 }
79 ));
80 }
81 section
82 }
83
84 fn build_skills_section(skills: &[(&str, &str)]) -> String {
86 if skills.is_empty() {
87 return "(no available skills)".to_string();
88 }
89
90 let mut section = String::new();
91 for (name, description) in skills {
92 section.push_str(&format!("- **{}**: {}\n", name, description));
93 }
94 section
95 }
96
97 fn custom_prompt_path() -> Option<PathBuf> {
98 dirs::home_dir().map(|home| home.join(".robit/prompts/system.txt"))
99 }
100}
101
102impl Default for PromptBuilder {
103 fn default() -> Self {
104 Self::new()
105 }
106}