use std::path::{Path, PathBuf};
const MANIFEST_FILENAME: &str = "agents-cli-manifest.yaml";
const MAX_WALK_LEVELS: usize = 8;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AgentsCliLayout {
pub manifest_dir: PathBuf,
pub agent_dir: PathBuf,
}
#[must_use]
pub fn find_agents_cli_layout(project: &Path) -> Option<AgentsCliLayout> {
let mut dir = project;
for _ in 0..=MAX_WALK_LEVELS {
let candidate = dir.join(MANIFEST_FILENAME);
if candidate.is_file() {
let text = std::fs::read_to_string(&candidate).ok()?;
let agent_directory = parse_agent_directory(&text)?;
if Path::new(&agent_directory)
.components()
.any(|c| matches!(c, std::path::Component::ParentDir))
{
return None;
}
let agent_dir = dir.join(agent_directory);
return agent_dir.is_dir().then(|| AgentsCliLayout {
manifest_dir: dir.to_owned(),
agent_dir,
});
}
dir = dir.parent()?;
}
None
}
fn parse_agent_directory(text: &str) -> Option<String> {
for line in text.lines() {
let Some(rest) = line.strip_prefix("agent_directory:") else {
continue;
};
let rest = rest.trim_start();
if rest.is_empty() {
return None;
}
let value = extract_scalar(rest)?;
return (!value.is_empty()).then_some(value);
}
None
}
fn extract_scalar(rest: &str) -> Option<String> {
for quote in ['\'', '"'] {
if let Some(inner) = rest.strip_prefix(quote) {
return inner.find(quote).map(|end| inner[..end].to_owned());
}
}
rest.split_whitespace().next().map(str::to_owned)
}
#[cfg(test)]
mod tests {
use tempfile::TempDir;
use super::*;
fn write_manifest(dir: &Path, body: &str) {
std::fs::write(dir.join(MANIFEST_FILENAME), body).unwrap();
}
#[test]
fn found_at_project_root() {
let dir = TempDir::new().unwrap();
std::fs::create_dir(dir.path().join("app")).unwrap();
write_manifest(dir.path(), "schema_version: 1\nagent_directory: app\n");
let layout = find_agents_cli_layout(dir.path()).expect("layout found");
assert_eq!(layout.manifest_dir, dir.path());
assert_eq!(layout.agent_dir, dir.path().join("app"));
}
#[test]
fn found_by_walking_upward_from_a_nested_directory() {
let dir = TempDir::new().unwrap();
std::fs::create_dir(dir.path().join("app")).unwrap();
write_manifest(dir.path(), "agent_directory: app\n");
let nested = dir.path().join("app").join("sub").join("deeper");
std::fs::create_dir_all(&nested).unwrap();
let layout = find_agents_cli_layout(&nested).expect("layout found by walking up");
assert_eq!(layout.manifest_dir, dir.path());
assert_eq!(layout.agent_dir, dir.path().join("app"));
}
#[test]
fn not_found_when_no_manifest_exists() {
let dir = TempDir::new().unwrap();
assert!(find_agents_cli_layout(dir.path()).is_none());
}
#[test]
fn quoted_value_is_unquoted() {
let dir = TempDir::new().unwrap();
std::fs::create_dir(dir.path().join("my_agent")).unwrap();
write_manifest(dir.path(), "agent_directory: \"my_agent\"\n");
let layout = find_agents_cli_layout(dir.path()).expect("layout found");
assert_eq!(layout.agent_dir, dir.path().join("my_agent"));
}
#[test]
fn single_quoted_value_is_unquoted() {
let dir = TempDir::new().unwrap();
std::fs::create_dir(dir.path().join("my_agent")).unwrap();
write_manifest(dir.path(), "agent_directory: 'my_agent'\n");
let layout = find_agents_cli_layout(dir.path()).expect("layout found");
assert_eq!(layout.agent_dir, dir.path().join("my_agent"));
}
#[test]
fn missing_key_yields_none() {
let dir = TempDir::new().unwrap();
write_manifest(dir.path(), "schema_version: 1\n");
assert!(find_agents_cli_layout(dir.path()).is_none());
}
#[test]
fn nonexistent_agent_dir_yields_none() {
let dir = TempDir::new().unwrap();
write_manifest(dir.path(), "agent_directory: does_not_exist\n");
assert!(find_agents_cli_layout(dir.path()).is_none());
}
#[test]
fn manifest_within_the_walk_bound_is_found() {
let dir = TempDir::new().unwrap();
std::fs::create_dir(dir.path().join("app")).unwrap();
write_manifest(dir.path(), "agent_directory: app\n");
let mut nested = dir.path().to_owned();
for i in 0..MAX_WALK_LEVELS {
nested = nested.join(format!("d{i}"));
}
std::fs::create_dir_all(&nested).unwrap();
let layout =
find_agents_cli_layout(&nested).expect("manifest exactly at the bound is found");
assert_eq!(layout.manifest_dir, dir.path());
}
#[test]
fn manifest_beyond_the_walk_bound_is_not_found() {
let dir = TempDir::new().unwrap();
std::fs::create_dir(dir.path().join("app")).unwrap();
write_manifest(dir.path(), "agent_directory: app\n");
let mut nested = dir.path().to_owned();
for i in 0..=MAX_WALK_LEVELS {
nested = nested.join(format!("d{i}"));
}
std::fs::create_dir_all(&nested).unwrap();
assert!(find_agents_cli_layout(&nested).is_none());
}
#[test]
fn inline_comment_after_an_unquoted_value_is_discarded() {
let dir = TempDir::new().unwrap();
std::fs::create_dir(dir.path().join("app")).unwrap();
write_manifest(dir.path(), "agent_directory: app # ships in prod\n");
let layout = find_agents_cli_layout(dir.path()).expect("layout found");
assert_eq!(layout.agent_dir, dir.path().join("app"));
}
#[test]
fn quoted_value_may_contain_spaces() {
let dir = TempDir::new().unwrap();
std::fs::create_dir(dir.path().join("my app")).unwrap();
write_manifest(dir.path(), "agent_directory: \"my app\"\n");
let layout = find_agents_cli_layout(dir.path()).expect("layout found");
assert_eq!(layout.agent_dir, dir.path().join("my app"));
}
#[test]
fn parent_dir_component_in_agent_directory_yields_none() {
let root = TempDir::new().unwrap();
let project = root.path().join("project");
std::fs::create_dir(&project).unwrap();
std::fs::create_dir(root.path().join("elsewhere")).unwrap();
write_manifest(&project, "agent_directory: ../elsewhere\n");
assert!(find_agents_cli_layout(&project).is_none());
}
}