(plugin)
| 78 | } |
| 79 | |
| 80 | function validateSpecPaths(plugin) { |
| 81 | const errors = []; |
| 82 | const specs = { |
| 83 | agents: { prefix: "./agents/", suffix: ".md", repoDir: "agents", repoSuffix: ".agent.md" }, |
| 84 | skills: { prefix: "./skills/", suffix: "/", repoDir: "skills", repoFile: "SKILL.md" }, |
| 85 | }; |
| 86 | |
| 87 | for (const [field, spec] of Object.entries(specs)) { |
| 88 | const arr = plugin[field]; |
| 89 | if (arr === undefined) continue; |
| 90 | if (!Array.isArray(arr)) { |
| 91 | errors.push(`${field} must be an array`); |
| 92 | continue; |
| 93 | } |
| 94 | if (!arraysEqual(arr, sortPluginEntries(arr))) { |
| 95 | errors.push(`${field} must be sorted alphabetically`); |
| 96 | } |
| 97 | for (let i = 0; i < arr.length; i++) { |
| 98 | const p = arr[i]; |
| 99 | if (typeof p !== "string") { |
| 100 | errors.push(`${field}[${i}] must be a string`); |
| 101 | continue; |
| 102 | } |
| 103 | if (!p.startsWith("./")) { |
| 104 | errors.push(`${field}[${i}] must start with "./"`); |
| 105 | continue; |
| 106 | } |
| 107 | if (!p.startsWith(spec.prefix)) { |
| 108 | errors.push(`${field}[${i}] must start with "${spec.prefix}"`); |
| 109 | continue; |
| 110 | } |
| 111 | if (!p.endsWith(spec.suffix)) { |
| 112 | errors.push(`${field}[${i}] must end with "${spec.suffix}"`); |
| 113 | continue; |
| 114 | } |
| 115 | // Validate the source file exists at repo root |
| 116 | const basename = p.slice(spec.prefix.length, p.length - spec.suffix.length); |
| 117 | if (field === "skills") { |
| 118 | const skillDir = path.join(ROOT_FOLDER, spec.repoDir, basename); |
| 119 | const skillFile = path.join(skillDir, spec.repoFile); |
| 120 | if (!fs.existsSync(skillFile)) { |
| 121 | errors.push(`${field}[${i}] source not found: ${spec.repoDir}/${basename}/SKILL.md`); |
| 122 | } |
| 123 | } else { |
| 124 | const srcFile = path.join(ROOT_FOLDER, spec.repoDir, basename + spec.repoSuffix); |
| 125 | if (!fs.existsSync(srcFile)) { |
| 126 | errors.push(`${field}[${i}] source not found: ${spec.repoDir}/${basename}${spec.repoSuffix}`); |
| 127 | } |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | return errors; |
| 132 | } |
| 133 | |
| 134 | function validatePlugin(folderName) { |
| 135 | const pluginDir = path.join(PLUGINS_DIR, folderName); |
no test coverage detected