(skillPath: string, skillName: string, agents: string[] = [])
| 140 | * Parse a single skill from its directory |
| 141 | */ |
| 142 | export function parseSkill(skillPath: string, skillName: string, agents: string[] = []): SkillInfo | null { |
| 143 | const skillMdPath = path.join(skillPath, 'SKILL.md'); |
| 144 | |
| 145 | if (!fs.existsSync(skillMdPath)) { |
| 146 | return null; |
| 147 | } |
| 148 | |
| 149 | try { |
| 150 | const content = fs.readFileSync(skillMdPath, 'utf-8'); |
| 151 | const { frontMatter, body } = parseFrontMatter(content); |
| 152 | const { count, files } = countFiles(skillPath); |
| 153 | |
| 154 | const source = skillPath.includes('/workspace') ? 'workspace' : 'system'; |
| 155 | |
| 156 | return { |
| 157 | id: skillName, |
| 158 | name: frontMatter.name || skillName, |
| 159 | description: frontMatter.description || extractFirstParagraph(body), |
| 160 | location: skillPath, |
| 161 | source, |
| 162 | homepage: frontMatter.homepage, |
| 163 | emoji: frontMatter.metadata?.openclaw?.emoji, |
| 164 | fileCount: count, |
| 165 | fullContent: content, |
| 166 | files, |
| 167 | agents, |
| 168 | }; |
| 169 | } catch { |
| 170 | return null; |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | /** |
| 175 | * Build a map of skill-name -> [agentId] by scanning all workspace skill dirs |
no test coverage detected