1use std::path::Path;
5
6const DEFAULT_PROMPT: &str = "You are a helpful AI assistant.";
7
8const ROUTING_GUIDANCE: &str = "## Routing guidance
9In group chats, respond to questions about the assistant, its plugins, settings, commands, upgrades, or transport even without a direct mention. Ignore unrelated ambient chatter unless the message clearly addresses the assistant or requests help.";
10
11const PROMPT_FILES: [(&str, &str, usize); 6] = [
12 ("IDENTITY.md", "## Identity", 12_000),
13 ("SOUL.md", "## Personality & Tone", 12_000),
14 ("USER.md", "## About the User", 12_000),
15 ("AGENTS.md", "## Workspace Rules", 16_000),
16 ("TOOLS.md", "## Tool Notes", 12_000),
17 ("MEMORY.md", "## Long-Term Memory", 8_000),
18];
19
20pub async fn build_system_prompt(workspace: &Path) -> String {
22 let body = load_workspace_sections(workspace, &PROMPT_FILES).await;
23 let mut prompt = if body.is_empty() {
24 DEFAULT_PROMPT.to_string()
25 } else {
26 body
27 };
28 prompt.push_str("\n\n");
29 prompt.push_str(ROUTING_GUIDANCE);
30 prompt
31}
32
33async fn load_workspace_sections(workspace: &Path, files: &[(&str, &str, usize)]) -> String {
34 let mut parts = Vec::new();
35 for &(filename, header, limit) in files {
36 if let Some(content) = read_file(workspace, filename, limit).await {
37 parts.push(format!("{header}\n{content}"));
38 }
39 }
40 parts.join("\n\n---\n\n")
41}
42
43async fn read_file(workspace: &Path, filename: &str, limit: usize) -> Option<String> {
45 let path = workspace.join(filename);
46 let content = tokio::fs::read_to_string(&path).await.ok()?;
47 let trimmed = content.trim();
48 if trimmed.is_empty() {
49 return None;
50 }
51 if trimmed.len() > limit {
52 Some(format!("{}...\n(truncated)", &trimmed[..limit]))
53 } else {
54 Some(trimmed.to_string())
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use std::path::PathBuf;
62
63 #[tokio::test]
64 async fn test_build_system_prompt_empty_workspace() {
65 let prompt = build_system_prompt(&PathBuf::from("/nonexistent")).await;
66 assert!(prompt.contains(DEFAULT_PROMPT));
67 assert!(prompt.contains("Routing guidance"));
68 }
69}