diff --git a/codex-rs/core/Cargo.toml b/codex-rs/core/Cargo.toml index 66ed20a43..a857da8b3 100644 --- a/codex-rs/core/Cargo.toml +++ b/codex-rs/core/Cargo.toml @@ -3,6 +3,7 @@ edition.workspace = true license.workspace = true name = "codex-core" version.workspace = true +build = "build.rs" [lib] doctest = false diff --git a/codex-rs/core/build.rs b/codex-rs/core/build.rs new file mode 100644 index 000000000..587415a3f --- /dev/null +++ b/codex-rs/core/build.rs @@ -0,0 +1,27 @@ +use std::fs; +use std::path::Path; + +fn main() { + let samples_dir = Path::new("src/skills/assets/samples"); + if !samples_dir.exists() { + return; + } + + println!("cargo:rerun-if-changed={}", samples_dir.display()); + visit_dir(samples_dir); +} + +fn visit_dir(dir: &Path) { + let entries = match fs::read_dir(dir) { + Ok(entries) => entries, + Err(_) => return, + }; + + for entry in entries.flatten() { + let path = entry.path(); + println!("cargo:rerun-if-changed={}", path.display()); + if path.is_dir() { + visit_dir(&path); + } + } +} diff --git a/codex-rs/core/src/skills/system.rs b/codex-rs/core/src/skills/system.rs index cfa20045a..cf8404096 100644 --- a/codex-rs/core/src/skills/system.rs +++ b/codex-rs/core/src/skills/system.rs @@ -86,21 +86,8 @@ fn read_marker(path: &AbsolutePathBuf) -> Result { } fn embedded_system_skills_fingerprint() -> String { - let mut items: Vec<(String, Option)> = SYSTEM_SKILLS_DIR - .entries() - .iter() - .map(|entry| match entry { - include_dir::DirEntry::Dir(dir) => (dir.path().to_string_lossy().to_string(), None), - include_dir::DirEntry::File(file) => { - let mut file_hasher = DefaultHasher::new(); - file.contents().hash(&mut file_hasher); - ( - file.path().to_string_lossy().to_string(), - Some(file_hasher.finish()), - ) - } - }) - .collect(); + let mut items = Vec::new(); + collect_fingerprint_items(&SYSTEM_SKILLS_DIR, &mut items); items.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); let mut hasher = DefaultHasher::new(); @@ -112,6 +99,25 @@ fn embedded_system_skills_fingerprint() -> String { format!("{:x}", hasher.finish()) } +fn collect_fingerprint_items(dir: &Dir<'_>, items: &mut Vec<(String, Option)>) { + for entry in dir.entries() { + match entry { + include_dir::DirEntry::Dir(subdir) => { + items.push((subdir.path().to_string_lossy().to_string(), None)); + collect_fingerprint_items(subdir, items); + } + include_dir::DirEntry::File(file) => { + let mut file_hasher = DefaultHasher::new(); + file.contents().hash(&mut file_hasher); + items.push(( + file.path().to_string_lossy().to_string(), + Some(file_hasher.finish()), + )); + } + } + } +} + /// Writes the embedded `include_dir::Dir` to disk under `dest`. /// /// Preserves the embedded directory structure. @@ -163,3 +169,28 @@ impl SystemSkillsError { Self::Io { action, source } } } + +#[cfg(test)] +mod tests { + use super::SYSTEM_SKILLS_DIR; + use super::collect_fingerprint_items; + + #[test] + fn fingerprint_traverses_nested_entries() { + let mut items = Vec::new(); + collect_fingerprint_items(&SYSTEM_SKILLS_DIR, &mut items); + let mut paths: Vec = items.into_iter().map(|(path, _)| path).collect(); + paths.sort_unstable(); + + assert!( + paths + .binary_search_by(|probe| probe.as_str().cmp("skill-creator/SKILL.md")) + .is_ok() + ); + assert!( + paths + .binary_search_by(|probe| probe.as_str().cmp("skill-creator/scripts/init_skill.py")) + .is_ok() + ); + } +}