( pluginDir: string, )
| 761 | * plugin returns an empty array. |
| 762 | */ |
| 763 | export async function validatePluginContents( |
| 764 | pluginDir: string, |
| 765 | ): Promise<ValidationResult[]> { |
| 766 | const results: ValidationResult[] = [] |
| 767 | |
| 768 | const dirs: Array<['skill' | 'agent' | 'command', string]> = [ |
| 769 | ['skill', path.join(pluginDir, 'skills')], |
| 770 | ['agent', path.join(pluginDir, 'agents')], |
| 771 | ['command', path.join(pluginDir, 'commands')], |
| 772 | ] |
| 773 | |
| 774 | for (const [fileType, dir] of dirs) { |
| 775 | const files = await collectMarkdown(dir, fileType === 'skill') |
| 776 | for (const filePath of files) { |
| 777 | let content: string |
| 778 | try { |
| 779 | content = await readFile(filePath, { encoding: 'utf-8' }) |
| 780 | } catch (e: unknown) { |
| 781 | // ENOENT is expected for speculative skill paths (subdirs without SKILL.md) |
| 782 | if (isENOENT(e)) continue |
| 783 | results.push({ |
| 784 | success: false, |
| 785 | errors: [ |
| 786 | { path: 'file', message: `Failed to read: ${errorMessage(e)}` }, |
| 787 | ], |
| 788 | warnings: [], |
| 789 | filePath, |
| 790 | fileType, |
| 791 | }) |
| 792 | continue |
| 793 | } |
| 794 | const r = validateComponentFile(filePath, content, fileType) |
| 795 | if (r.errors.length > 0 || r.warnings.length > 0) { |
| 796 | results.push(r) |
| 797 | } |
| 798 | } |
| 799 | } |
| 800 | |
| 801 | const hooksResult = await validateHooksJson( |
| 802 | path.join(pluginDir, 'hooks', 'hooks.json'), |
| 803 | ) |
| 804 | if (hooksResult.errors.length > 0 || hooksResult.warnings.length > 0) { |
| 805 | results.push(hooksResult) |
| 806 | } |
| 807 | |
| 808 | return results |
| 809 | } |
| 810 | |
| 811 | /** |
| 812 | * Validate a manifest file or directory (auto-detects type) |
no test coverage detected