57inline int install(
const fs::path &skills_dir,
const fs::path &project_dir,
58 const json &data,
bool overwrite,
bool verbose =
true) {
60 if (!fs::is_directory(skills_dir)) {
61 std::cerr << fg::yellow <<
"Warning: skill sources not found in "
62 << skills_dir <<
", skipping agent documentation" << fg::reset
69 fs::create_directories(dest /
"reference", ec);
71 std::cerr << fg::red <<
"Error: cannot create " << dest <<
": "
72 << ec.message() << fg::reset << std::endl;
77 auto report = [&](
const fs::path &p,
bool skipped) {
80 std::cout <<
"==> " << style::bold << p.string() << style::reset <<
": ";
82 std::cout << fg::red <<
"already exists, skipped; use -o to overwrite"
83 << fg::reset << std::endl;
85 std::cout << fg::green <<
"created" << fg::reset << std::endl;
89 fs::path tpl = skills_dir /
"SKILL.md.tpl";
90 fs::path skill_md = dest /
"SKILL.md";
91 if (fs::exists(tpl)) {
92 if (!overwrite && fs::exists(skill_md)) {
93 report(skill_md,
true);
96 inja::Environment env;
100 env.set_line_statement(
"%%");
101 std::string rendered = env.render_file(tpl.string(), data);
102 std::ofstream ofs(skill_md);
106 report(skill_md,
false);
107 }
catch (
const std::exception &e) {
108 std::cerr << fg::red <<
"Error rendering " << tpl <<
": " << e.what()
109 << fg::reset << std::endl;
115 fs::path ref_src = skills_dir /
"reference";
116 if (fs::is_directory(ref_src)) {
117 std::vector<fs::path> refs;
118 for (
const auto &e : fs::directory_iterator(ref_src))
119 if (e.is_regular_file() && e.path().extension() ==
".md")
120 refs.push_back(e.path());
121 std::sort(refs.begin(), refs.end());
122 for (
const auto &src : refs) {
123 fs::path dst = dest /
"reference" / src.filename();
124 if (!overwrite && fs::exists(dst)) {
128 fs::copy_file(src, dst, fs::copy_options::overwrite_existing, ec);
130 std::cerr << fg::red <<
"Error: cannot copy " << src <<
": "
131 << ec.message() << fg::reset << std::endl;