* Loads skills from plugin skills directories * Skills are directories containing SKILL.md files
( skillsPath: string, pluginName: string, sourceName: string, pluginManifest: PluginManifest, pluginPath: string, loadedPaths: Set<string>, )
| 685 | * Skills are directories containing SKILL.md files |
| 686 | */ |
| 687 | async function loadSkillsFromDirectory( |
| 688 | skillsPath: string, |
| 689 | pluginName: string, |
| 690 | sourceName: string, |
| 691 | pluginManifest: PluginManifest, |
| 692 | pluginPath: string, |
| 693 | loadedPaths: Set<string>, |
| 694 | ): Promise<Command[]> { |
| 695 | const fs = getFsImplementation() |
| 696 | const skills: Command[] = [] |
| 697 | |
| 698 | // First, check if skillsPath itself contains SKILL.md (direct skill directory) |
| 699 | const directSkillPath = join(skillsPath, 'SKILL.md') |
| 700 | let directSkillContent: string | null = null |
| 701 | try { |
| 702 | directSkillContent = await fs.readFile(directSkillPath, { |
| 703 | encoding: 'utf-8', |
| 704 | }) |
| 705 | } catch (e: unknown) { |
| 706 | if (!isENOENT(e)) { |
| 707 | logForDebugging(`Failed to load skill from ${directSkillPath}: ${e}`, { |
| 708 | level: 'error', |
| 709 | }) |
| 710 | return skills |
| 711 | } |
| 712 | // ENOENT: no direct SKILL.md, fall through to scan subdirectories |
| 713 | } |
| 714 | |
| 715 | if (directSkillContent !== null) { |
| 716 | // This is a direct skill directory, load the skill from here |
| 717 | if (isDuplicatePath(fs, directSkillPath, loadedPaths)) { |
| 718 | return skills |
| 719 | } |
| 720 | try { |
| 721 | const { frontmatter, content: markdownContent } = parseFrontmatter( |
| 722 | directSkillContent, |
| 723 | directSkillPath, |
| 724 | ) |
| 725 | |
| 726 | const skillName = `${pluginName}:${basename(skillsPath)}` |
| 727 | |
| 728 | const file: PluginMarkdownFile = { |
| 729 | filePath: directSkillPath, |
| 730 | baseDir: dirname(directSkillPath), |
| 731 | frontmatter, |
| 732 | content: markdownContent, |
| 733 | } |
| 734 | |
| 735 | const skill = createPluginCommand( |
| 736 | skillName, |
| 737 | file, |
| 738 | sourceName, |
| 739 | pluginManifest, |
| 740 | pluginPath, |
| 741 | true, // isSkill |
| 742 | { isSkillMode: true }, // config |
| 743 | ) |
| 744 |
no test coverage detected