1use crate::system::SystemInfo;
2use handlebars::Handlebars;
3use rust_embed::RustEmbed;
4use serde_json::json;
5
6#[derive(RustEmbed)]
8#[folder = "static/prompts/"]
9struct PromptAssets;
10
11pub struct PromptBuilder {
13 system_info: SystemInfo,
14 handlebars: Handlebars<'static>,
15}
16
17impl PromptBuilder {
18 pub fn new(system_info: SystemInfo) -> Self {
20 Self {
21 system_info,
22 handlebars: Handlebars::new(),
23 }
24 }
25
26 fn load_prompt(name: &str) -> String {
28 PromptAssets::get(name)
29 .and_then(|file| {
30 std::str::from_utf8(file.data.as_ref())
31 .ok()
32 .map(|s| s.to_string())
33 })
34 .unwrap_or_else(|| {
35 eprintln!("Warning: Failed to load prompt file: {}", name);
36 tracing::warn!("Failed to load prompt file: {}", name);
37 String::new()
38 })
39 }
40
41 fn build_common_prompt(&self) -> String {
43 let template = Self::load_prompt("common.md");
44
45 let data = json!({
46 "os": self.system_info.os.as_str(),
47 "shell": self.system_info.shell.as_str(),
48 "current_dir": self.system_info.current_dir.display().to_string(),
49 "username": self.system_info.username.as_deref().unwrap_or("unknown"),
50 "hostname": self.system_info.hostname.as_deref().unwrap_or("unknown"),
51 });
52
53 self.handlebars
54 .render_template(&template, &data)
55 .unwrap_or(template)
56 }
57
58 pub fn build_auto_mode(&self) -> String {
60 let common_prompt = self.build_common_prompt();
61 let mode_select_template = Self::load_prompt("mode_select.md");
62
63 Self::concat_prompts(vec![&common_prompt, &mode_select_template])
64 }
65
66 pub fn build_ask(&self) -> String {
68 let common_prompt = self.build_common_prompt();
69 let ask_prompt = Self::load_prompt("ask.md");
70
71 Self::concat_prompts(vec![&common_prompt, &ask_prompt])
72 }
73
74 pub fn build_suggest(&self) -> String {
76 let common_prompt = self.build_common_prompt();
77 let suggest_template = Self::load_prompt("suggest.md");
78
79 let data = json!({
80 "os": self.system_info.os.as_str(),
81 "shell": self.system_info.shell.as_str(),
82 });
83
84 let suggest_prompt = self
85 .handlebars
86 .render_template(&suggest_template, &data)
87 .unwrap_or(suggest_template);
88
89 Self::concat_prompts(vec![&common_prompt, &suggest_prompt])
90 }
91
92 fn concat_prompts(prompts: Vec<&str>) -> String {
94 prompts.join("\n\n---\n\n")
95 }
96}