Skip to main content

codetether_agent/agent/builtin/
agents_md.rs

1//! AGENTS.md discovery helpers for built-in prompts.
2//!
3//! This module searches for project instruction files while respecting the git
4//! repository boundary.
5//!
6//! # Examples
7//!
8//! ```ignore
9//! let all = load_all_agents_md(std::path::Path::new("."));
10//! ```
11
12use std::path::{Path, PathBuf};
13
14/// Loads the closest `AGENTS.md` at or above `start_dir`, stopping at the git root.
15///
16/// # Examples
17///
18/// ```ignore
19/// let loaded = load_agents_md(std::path::Path::new("."));
20/// ```
21pub fn load_agents_md(start_dir: &Path) -> Option<(String, PathBuf)> {
22    load_all_agents_md(start_dir).into_iter().next()
23}
24
25/// Loads all `AGENTS.md` files between `start_dir` and the git root.
26///
27/// # Examples
28///
29/// ```ignore
30/// let files = load_all_agents_md(std::path::Path::new("."));
31/// ```
32pub fn load_all_agents_md(start_dir: &Path) -> Vec<(String, PathBuf)> {
33    let mut results = Vec::new();
34    let mut current = start_dir.to_path_buf();
35    let repo_root = find_git_root(start_dir);
36    loop {
37        if let Some(found) = load_file(&current) {
38            results.push(found);
39        }
40        if repo_root.as_ref() == Some(&current) || !current.pop() {
41            break;
42        }
43    }
44    results
45}
46
47fn load_file(dir: &Path) -> Option<(String, PathBuf)> {
48    let agents_path = dir.join("AGENTS.md");
49    std::fs::read_to_string(&agents_path)
50        .ok()
51        .map(|content| (content, agents_path))
52}
53
54fn find_git_root(start_dir: &Path) -> Option<PathBuf> {
55    let mut current = start_dir.to_path_buf();
56    loop {
57        if current.join(".git").exists() {
58            return Some(current);
59        }
60        if !current.pop() {
61            return None;
62        }
63    }
64}