use crate::RewriteProfile; use serde_json::Value as JsonValue; use std::fs; use std::io; use std::path::Path; use std::path::PathBuf; pub(super) fn display_source_paths(paths: &[PathBuf]) -> String { paths .iter() .map(|path| path.display().to_string()) .collect::>() .join(", ") } pub(crate) fn read_json_file(path: &Path) -> io::Result> { if !path.is_file() { return Ok(None); } let raw = fs::read_to_string(path)?; serde_json::from_str(&raw) .map(Some) .map_err(|err| invalid_data_error(err.to_string())) } pub(super) fn is_missing_or_empty_text_file(path: &Path) -> io::Result { if !path.exists() { return Ok(true); } if !path.is_file() { return Ok(false); } Ok(fs::read_to_string(path)?.trim().is_empty()) } pub(super) fn invalid_data_error(message: impl Into) -> io::Error { io::Error::new(io::ErrorKind::InvalidData, message.into()) } pub(super) fn copy_dir_recursive( source: &Path, target: &Path, rewrite_profile: RewriteProfile, ) -> io::Result<()> { fs::create_dir_all(target)?; for entry in fs::read_dir(source)? { let entry = entry?; let source_path = entry.path(); let target_path = target.join(entry.file_name()); let file_type = entry.file_type()?; if file_type.is_dir() { copy_dir_recursive(&source_path, &target_path, rewrite_profile)?; continue; } if file_type.is_file() { if is_skill_md(&source_path) { rewrite_and_copy_text_file(&source_path, &target_path, rewrite_profile)?; } else { fs::copy(source_path, target_path)?; } } } Ok(()) } pub(super) fn rewrite_external_agent_terms( content: &str, rewrite_profile: RewriteProfile, ) -> String { rewrite_profile.rewrite(content) } fn is_skill_md(path: &Path) -> bool { path.file_name() .and_then(|name| name.to_str()) .is_some_and(|name| name.eq_ignore_ascii_case("SKILL.md")) } fn rewrite_and_copy_text_file( source: &Path, target: &Path, rewrite_profile: RewriteProfile, ) -> io::Result<()> { let source_contents = fs::read_to_string(source)?; let rewritten = rewrite_external_agent_terms(&source_contents, rewrite_profile); fs::write(target, rewritten) }