use std::env;
use std::ffi::OsStr;
use std::fs;
use std::path::{Path, PathBuf};
fn main() {
let manifest_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").expect("manifest dir"));
let repo_root = manifest_dir
.ancestors()
.nth(3)
.expect("repo root")
.to_path_buf();
let source_root = if repo_root.join(".mcp.json").is_file() {
repo_root
} else {
manifest_dir.join("assets/root")
};
let mut entries = Vec::new();
rerun_if_changed(&source_root.join(".mcp.json"));
rerun_if_changed(&source_root.join("AGENTS.md"));
rerun_if_changed(&source_root.join("CLAUDE.md"));
rerun_if_changed(&source_root.join(".agents/settings.json"));
rerun_if_changed(&source_root.join("prompts"));
rerun_if_changed(&source_root.join(".agents/skills"));
rerun_if_changed(&source_root.join("scripts"));
entries.push((
".mcp.json".to_string(),
fs::read_to_string(source_root.join(".mcp.json")).expect("read .mcp.json"),
));
entries.push((
"AGENTS.md".to_string(),
fs::read_to_string(source_root.join("AGENTS.md")).expect("read AGENTS.md"),
));
entries.push((
"CLAUDE.md".to_string(),
fs::read_to_string(source_root.join("CLAUDE.md")).expect("read CLAUDE.md"),
));
entries.push((
".agents/settings.json".to_string(),
fs::read_to_string(source_root.join(".agents/settings.json"))
.expect("read .agents/settings.json"),
));
entries.extend(collect_prompts(&source_root));
entries.extend(collect_skills(&source_root));
entries.extend(collect_scripts(&source_root));
entries.sort_by(|a, b| a.0.cmp(&b.0));
let out_dir = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR"));
let out_file = out_dir.join("embedded_files.rs");
let mut rendered = String::new();
rendered.push_str("pub const EMBEDDED_FILES: &[(&str, &str)] = &[\n");
for (path, content) in entries {
rendered.push_str(" (");
rendered.push_str(&rust_string(&path));
rendered.push_str(", ");
rendered.push_str(&rust_string(&content));
rendered.push_str("),\n");
}
rendered.push_str("];\n");
fs::write(out_file, rendered).expect("write embedded registry");
}
fn collect_prompts(repo_root: &Path) -> Vec<(String, String)> {
let dir = repo_root.join("prompts");
let mut entries = Vec::new();
if let Ok(read_dir) = fs::read_dir(&dir) {
for entry in read_dir.flatten() {
let path = entry.path();
if path.extension() != Some(OsStr::new("md")) {
continue;
}
if !path.is_file() {
continue;
}
let rel = path
.strip_prefix(repo_root)
.expect("prompt under repo root")
.to_string_lossy()
.replace('\\', "/");
entries.push((rel, fs::read_to_string(&path).expect("read prompt")));
}
}
entries.sort_by(|a, b| a.0.cmp(&b.0));
entries
}
fn collect_skills(repo_root: &Path) -> Vec<(String, String)> {
let dir = repo_root.join(".agents/skills");
let mut entries = Vec::new();
walk_skills(&dir, repo_root, &mut entries);
entries.sort_by(|a, b| a.0.cmp(&b.0));
entries
}
fn collect_scripts(repo_root: &Path) -> Vec<(String, String)> {
let dir = repo_root.join("scripts");
let mut entries = Vec::new();
let Ok(read_dir) = fs::read_dir(&dir) else {
return entries;
};
for entry in read_dir.flatten() {
let path = entry.path();
if !path.is_file() {
continue;
}
let rel = path
.strip_prefix(repo_root)
.expect("script under repo root")
.to_string_lossy()
.replace('\\', "/");
entries.push((rel, fs::read_to_string(&path).expect("read script")));
}
entries.sort_by(|a, b| a.0.cmp(&b.0));
entries
}
fn walk_skills(dir: &Path, repo_root: &Path, entries: &mut Vec<(String, String)>) {
let Ok(read_dir) = fs::read_dir(dir) else {
return;
};
for entry in read_dir.flatten() {
let path = entry.path();
if path.is_dir() {
walk_skills(&path, repo_root, entries);
continue;
}
if path.file_name() != Some(OsStr::new("SKILL.md")) {
continue;
}
let rel = path
.strip_prefix(repo_root)
.expect("skill under repo root")
.to_string_lossy()
.replace('\\', "/");
entries.push((rel, fs::read_to_string(&path).expect("read skill")));
}
}
fn rerun_if_changed(path: &Path) {
println!("cargo:rerun-if-changed={}", path.display());
}
fn rust_string(s: &str) -> String {
let mut out = String::with_capacity(s.len() + 2);
out.push('"');
for ch in s.chars() {
for esc in ch.escape_default() {
out.push(esc);
}
}
out.push('"');
out
}