#!/usr/bin/env node import fs from "fs"; import path from "path"; import { ROOT_FOLDER } from "./constants.mjs"; const PLUGINS_DIR = path.join(ROOT_FOLDER, "plugins"); /** * Recursively copy a directory. */ function copyDirRecursive(src, dest) { fs.mkdirSync(dest, { recursive: true }); for (const entry of fs.readdirSync(src, { withFileTypes: true })) { const srcPath = path.join(src, entry.name); const destPath = path.join(dest, entry.name); if (entry.isDirectory()) { copyDirRecursive(srcPath, destPath); } else { fs.copyFileSync(srcPath, destPath); } } } /** * Resolve a plugin-relative path to the repo-root source file. * * ./agents/foo.md → ROOT/agents/foo.agent.md * ./commands/bar.md → ROOT/prompts/bar.prompt.md * ./skills/baz/ → ROOT/skills/baz/ */ function resolveSource(relPath) { const basename = path.basename(relPath, ".md"); if (relPath.startsWith("./agents/")) { return path.join(ROOT_FOLDER, "agents", `${basename}.agent.md`); } if (relPath.startsWith("./commands/")) { return path.join(ROOT_FOLDER, "prompts", `${basename}.prompt.md`); } if (relPath.startsWith("./skills/")) { // Strip trailing slash and get the skill folder name const skillName = relPath.replace(/^\.\/skills\//, "").replace(/\/$/, ""); return path.join(ROOT_FOLDER, "skills", skillName); } return null; } function materializePlugins() { console.log("Materializing plugin files...\n"); if (!fs.existsSync(PLUGINS_DIR)) { console.error(`Error: Plugins directory not found at ${PLUGINS_DIR}`); process.exit(1); } const pluginDirs = fs.readdirSync(PLUGINS_DIR, { withFileTypes: true }) .filter(entry => entry.isDirectory()) .map(entry => entry.name) .sort(); let totalAgents = 0; let totalCommands = 0; let totalSkills = 0; let warnings = 0; let errors = 0; for (const dirName of pluginDirs) { const pluginPath = path.join(PLUGINS_DIR, dirName); const pluginJsonPath = path.join(pluginPath, ".github/plugin", "plugin.json"); if (!fs.existsSync(pluginJsonPath)) { continue; } let metadata; try { metadata = JSON.parse(fs.readFileSync(pluginJsonPath, "utf8")); } catch (err) { console.error(`Error: Failed to parse ${pluginJsonPath}: ${err.message}`); errors++; continue; } const pluginName = metadata.name || dirName; // Process agents if (Array.isArray(metadata.agents)) { for (const relPath of metadata.agents) { const src = resolveSource(relPath); if (!src) { console.warn(` ⚠ ${pluginName}: Unknown path format: ${relPath}`); warnings++; continue; } if (!fs.existsSync(src)) { console.warn(` ⚠ ${pluginName}: Source not found: ${src}`); warnings++; continue; } const dest = path.join(pluginPath, relPath.replace(/^\.\//, "")); fs.mkdirSync(path.dirname(dest), { recursive: true }); fs.copyFileSync(src, dest); totalAgents++; } } // Process commands if (Array.isArray(metadata.commands)) { for (const relPath of metadata.commands) { const src = resolveSource(relPath); if (!src) { console.warn(` ⚠ ${pluginName}: Unknown path format: ${relPath}`); warnings++; continue; } if (!fs.existsSync(src)) { console.warn(` ⚠ ${pluginName}: Source not found: ${src}`); warnings++; continue; } const dest = path.join(pluginPath, relPath.replace(/^\.\//, "")); fs.mkdirSync(path.dirname(dest), { recursive: true }); fs.copyFileSync(src, dest); totalCommands++; } } // Process skills if (Array.isArray(metadata.skills)) { for (const relPath of metadata.skills) { const src = resolveSource(relPath); if (!src) { console.warn(` ⚠ ${pluginName}: Unknown path format: ${relPath}`); warnings++; continue; } if (!fs.existsSync(src) || !fs.statSync(src).isDirectory()) { console.warn(` ⚠ ${pluginName}: Source directory not found: ${src}`); warnings++; continue; } const dest = path.join(pluginPath, relPath.replace(/^\.\//, "").replace(/\/$/, "")); copyDirRecursive(src, dest); totalSkills++; } } const counts = []; if (metadata.agents?.length) counts.push(`${metadata.agents.length} agents`); if (metadata.commands?.length) counts.push(`${metadata.commands.length} commands`); if (metadata.skills?.length) counts.push(`${metadata.skills.length} skills`); if (counts.length) { console.log(`✓ ${pluginName}: ${counts.join(", ")}`); } } console.log(`\nDone. Copied ${totalAgents} agents, ${totalCommands} commands, ${totalSkills} skills.`); if (warnings > 0) { console.log(`${warnings} warning(s).`); } if (errors > 0) { console.error(`${errors} error(s).`); process.exit(1); } } materializePlugins();