Skip to main content

a_agent/context/
agents.rs

1use std::collections::BTreeMap;
2use std::fs;
3use std::path::{Path, PathBuf};
4
5use anyhow::{Context, Result};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct AgentsFile {
9    pub path: PathBuf,
10    pub content: String,
11}
12
13pub fn discover_agents(cwd: &Path, global: Option<&Path>) -> Result<Vec<AgentsFile>> {
14    let mut files = Vec::new();
15    if let Some(path) = global.filter(|path| path.is_file()) {
16        files.push(read_agents(path)?);
17    }
18
19    let boundary = repository_boundary(cwd);
20    let mut candidates = Vec::new();
21    let mut current = Some(cwd);
22    while let Some(path) = current {
23        candidates.push(path.join("AGENTS.md"));
24        if path == boundary {
25            break;
26        }
27        current = path.parent();
28    }
29    candidates.reverse();
30    for path in candidates.into_iter().filter(|path| path.is_file()) {
31        files.push(read_agents(&path)?);
32    }
33    Ok(files)
34}
35
36pub fn discover_agents_for_targets(
37    cwd: &Path,
38    global: Option<&Path>,
39    targets: &[PathBuf],
40) -> Result<Vec<AgentsFile>> {
41    let global_path = global.map(Path::to_path_buf);
42    let mut unique = BTreeMap::new();
43    for item in discover_agents(cwd, global)? {
44        unique.insert(item.path.clone(), item);
45    }
46    for target in targets {
47        let directory = if target.is_dir() {
48            target.as_path()
49        } else {
50            target.parent().unwrap_or(cwd)
51        };
52        for item in discover_agents(directory, None)? {
53            unique.insert(item.path.clone(), item);
54        }
55    }
56    let mut files = unique.into_values().collect::<Vec<_>>();
57    files.sort_by_key(|item| {
58        (
59            usize::from(global_path.as_ref() != Some(&item.path)),
60            item.path.components().count(),
61            item.path.clone(),
62        )
63    });
64    Ok(files)
65}
66
67fn repository_boundary(cwd: &Path) -> &Path {
68    cwd.ancestors()
69        .find(|path| path.join(".git").exists())
70        .unwrap_or(cwd)
71}
72
73fn read_agents(path: &Path) -> Result<AgentsFile> {
74    Ok(AgentsFile {
75        path: path.to_path_buf(),
76        content: fs::read_to_string(path)
77            .with_context(|| format!("read AGENTS.md {}", path.display()))?,
78    })
79}