Skip to main content

dot/
context.rs

1use std::path::Path;
2
3use crate::config::ContextConfig;
4
5pub struct AgentsContext {
6    content: Option<String>,
7}
8
9impl AgentsContext {
10    pub fn load(cwd: &str, cfg: &ContextConfig) -> Self {
11        let mut parts: Vec<String> = Vec::new();
12
13        if cfg.auto_load_global {
14            let config_dir = crate::config::Config::config_dir();
15            for name in &["AGENTS.md", "AGENT.md"] {
16                let path = config_dir.join(name);
17                if path.exists() {
18                    match std::fs::read_to_string(&path) {
19                        Ok(content) => {
20                            let trimmed = content.trim().to_string();
21                            if !trimmed.is_empty() {
22                                tracing::debug!(
23                                    "Loaded global agents context from {}",
24                                    path.display()
25                                );
26                                parts.push(trimmed);
27                            }
28                        }
29                        Err(e) => {
30                            tracing::warn!("Failed to read {}: {}", path.display(), e);
31                        }
32                    }
33                    break;
34                }
35            }
36        }
37
38        if cfg.auto_load_project {
39            let cwd_path = Path::new(cwd);
40            let candidates = [
41                cwd_path.join("AGENTS.md"),
42                cwd_path.join("AGENT.md"),
43                cwd_path.join(".dot").join("AGENTS.md"),
44                cwd_path.join(".dot").join("AGENT.md"),
45            ];
46            for path in &candidates {
47                if path.exists() {
48                    match std::fs::read_to_string(path) {
49                        Ok(content) => {
50                            let trimmed = content.trim().to_string();
51                            if !trimmed.is_empty() {
52                                tracing::debug!(
53                                    "Loaded project agents context from {}",
54                                    path.display()
55                                );
56                                parts.push(trimmed);
57                            }
58                        }
59                        Err(e) => {
60                            tracing::warn!("Failed to read {}: {}", path.display(), e);
61                        }
62                    }
63                    break;
64                }
65            }
66        }
67
68        let content = if parts.is_empty() {
69            None
70        } else {
71            Some(parts.join("\n\n---\n\n"))
72        };
73
74        AgentsContext { content }
75    }
76
77    pub fn apply_to_system_prompt(&self, base: &str) -> String {
78        match &self.content {
79            None => base.to_string(),
80            Some(ctx) => format!("{ctx}\n\n---\n\n{base}"),
81        }
82    }
83
84    pub fn is_empty(&self) -> bool {
85        self.content.is_none()
86    }
87}