Generates `$OUT_DIR/plugin_bundle_generated.rs`: a recursive manifest of every file under `plugin/skills/` (SKILL.md *and* any `references/`, `scripts/`, `assets/` support files), embedded via `include_str!` at compile time. Moving embedding off a hand-maintained flat list lets skills ship support files without a matching table edit; the coverage tests assert the generated set equals the on-disk t
()
| 362 | /// generated slice serves Claude, Codex, and Cursor (Cursor filters out the |
| 363 | /// `tracedecay-*` dispatcher skills at compose time). |
| 364 | fn generate_skill_bundle() { |
| 365 | let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"); |
| 366 | let skills_root = Path::new(&manifest_dir).join("plugin/skills"); |
| 367 | println!("cargo::rerun-if-changed=plugin/skills"); |
| 368 | |
| 369 | let mut code = String::new(); |
| 370 | code.push_str( |
| 371 | "// @generated by build.rs (generate_skill_bundle). Do not edit.\n\ |
| 372 | /// Every file under `plugin/skills/` (SKILL.md + support files), embedded\n\ |
| 373 | /// recursively at compile time. `relative` is the deploy path (identical to\n\ |
| 374 | /// the `plugin/`-relative source path).\n\ |
| 375 | pub const GENERATED_SKILL_FILES: &[PluginFile] = &[\n", |
| 376 | ); |
| 377 | for relative in collect_files_relative(&skills_root) { |
| 378 | // rerun on each individual file so edits re-trigger codegen even if the |
| 379 | // directory mtime does not change. |
| 380 | println!("cargo::rerun-if-changed=plugin/skills/{relative}"); |
| 381 | // Every embedded file goes through `include_str!`, which only accepts |
| 382 | // UTF-8. A binary support file (e.g. `assets/*.png`) would otherwise |
| 383 | // fail to compile with an opaque "stream did not contain valid UTF-8" |
| 384 | // error pointing at the generated file, not the offending asset. Guard |
| 385 | // it here with a clear message; binary support files are not embeddable |
| 386 | // yet (add `include_bytes!` handling when that becomes a requirement). |
| 387 | let abs = skills_root.join(&relative); |
| 388 | if !is_probably_utf8_text(&abs) { |
| 389 | panic!( |
| 390 | "plugin/skills/{relative} is not a UTF-8 text file. The skill bundle codegen \ |
| 391 | embeds every file via include_str!, so binary support files (e.g. images) are \ |
| 392 | not embeddable yet. Remove the binary file or add include_bytes! support to \ |
| 393 | build.rs (generate_skill_bundle)." |
| 394 | ); |
| 395 | } |
| 396 | let deploy = format!("skills/{relative}"); |
| 397 | let source = format!("skills/{relative}"); |
| 398 | code.push_str(&format!( |
| 399 | " PluginFile {{ relative: {deploy:?}, contents: include_str!(concat!(env!(\"CARGO_MANIFEST_DIR\"), \"/plugin/{source}\")) }},\n" |
| 400 | )); |
| 401 | } |
| 402 | code.push_str("];\n"); |
| 403 | |
| 404 | let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR"); |
| 405 | let out_path = Path::new(&out_dir).join("plugin_bundle_generated.rs"); |
| 406 | if let Err(e) = fs::write(&out_path, code) { |
| 407 | panic!("failed to write {}: {e}", out_path.display()); |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | fn main() { |
| 412 | generate_skill_bundle(); |
no test coverage detected