use std::path::{Path, PathBuf};
#[derive(Debug, Clone)]
pub struct AgentsMdFile {
pub path: PathBuf,
pub content: String,
}
pub struct AgentsMdRegistry {
files: Vec<AgentsMdFile>,
}
impl AgentsMdRegistry {
pub fn new() -> Self {
Self { files: Vec::new() }
}
pub fn discover(&mut self) {
self.files.clear();
if let Ok(cwd) = std::env::current_dir() {
let git_root = find_git_root(&cwd);
let stop_at = git_root.as_deref();
let mut dir = Some(cwd.as_path());
while let Some(current) = dir {
let agents_path = current.join("AGENTS.md");
if agents_path.is_file() {
if let Ok(content) = std::fs::read_to_string(&agents_path) {
self.files.push(AgentsMdFile {
path: agents_path,
content,
});
}
}
if let Some(root) = stop_at {
if current == root {
break;
}
}
dir = current.parent();
}
}
if let Some(home) = dirs::home_dir() {
let global_path = home.join(".llama-agent").join("AGENTS.md");
if global_path.is_file() {
if !self.files.iter().any(|f| f.path == global_path) {
if let Ok(content) = std::fs::read_to_string(&global_path) {
self.files.push(AgentsMdFile {
path: global_path,
content,
});
}
}
}
}
}
pub fn files(&self) -> &[AgentsMdFile] {
&self.files
}
pub fn agents_md_prompt(&self) -> String {
if self.files.is_empty() {
return String::new();
}
let mut lines = Vec::new();
lines.push("# Project Guidelines (from AGENTS.md)\n".to_string());
for file in &self.files {
let path_display = file.path.display().to_string();
lines.push(format!("<!-- Source: {} -->\n", path_display));
lines.push(file.content.clone());
lines.push(String::new());
}
lines.join("\n")
}
pub fn len(&self) -> usize {
self.files.len()
}
pub fn is_empty(&self) -> bool {
self.files.is_empty()
}
}
impl Default for AgentsMdRegistry {
fn default() -> Self {
Self::new()
}
}
fn find_git_root(start: &Path) -> Option<PathBuf> {
let mut dir = Some(start);
while let Some(current) = dir {
if current.join(".git").exists() {
return Some(current.to_path_buf());
}
dir = current.parent();
}
None
}