use proc_macro::TokenStream;
use quote::quote;
use syn::LitStr;
pub fn generate_include_skill(input: TokenStream) -> TokenStream {
let path_lit = syn::parse_macro_input!(input as LitStr);
let path_str = path_lit.value();
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")
.expect("CARGO_MANIFEST_DIR is always set by cargo during proc-macro expansion");
let skill_dir = std::path::Path::new(&manifest_dir).join(&path_str);
let skill_md_path = skill_dir.join("SKILL.md");
let skill_md_content = match std::fs::read_to_string(&skill_md_path) {
Ok(c) => c,
Err(e) => {
return syn::Error::new(
path_lit.span(),
format!(
"include_skill!: cannot read {}: {e}",
skill_md_path.display()
),
)
.to_compile_error()
.into();
}
};
if !skill_md_content.starts_with("---") {
return syn::Error::new(
path_lit.span(),
format!(
"include_skill!: {} must begin with YAML frontmatter (---)",
skill_md_path.display()
),
)
.to_compile_error()
.into();
}
if !skill_md_content.contains("\n---") {
return syn::Error::new(
path_lit.span(),
format!(
"include_skill!: {} frontmatter is not closed with ---",
skill_md_path.display()
),
)
.to_compile_error()
.into();
}
let dir_name = skill_dir
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("")
.to_string();
let skill_md_path_str = skill_md_path.to_string_lossy().to_string();
let expanded = quote! {
{
const SKILL_MD_CONTENT: &str = include_str!(#skill_md_path_str);
::radkit::agent::AgentSkillDef::from_skill_md_str(SKILL_MD_CONTENT, #dir_name)
.unwrap_or_else(|e| panic!(
"include_skill!(\"{}\") — invalid SKILL.md: {}",
#path_str, e
))
}
};
TokenStream::from(expanded)
}