Walk a freshly extracted source looking for a SKILL.md root.
(src: string)
| 118 | |
| 119 | /** Walk a freshly extracted source looking for a SKILL.md root. */ |
| 120 | async function findSkillRoot(src: string): Promise<string> { |
| 121 | // Track the last non-ENOENT stat failure so a real IO error (e.g. EACCES on a |
| 122 | // candidate path) isn't masked behind a generic "couldn't find SKILL.md". |
| 123 | let lastStatErr: any; |
| 124 | const tryStat = async (p: string): Promise<boolean> => { |
| 125 | try { |
| 126 | await fs.stat(p); |
| 127 | return true; |
| 128 | } catch (e: any) { |
| 129 | if (e?.code !== 'ENOENT') lastStatErr = e; |
| 130 | return false; |
| 131 | } |
| 132 | }; |
| 133 | |
| 134 | // Direct hit: src/SKILL.md |
| 135 | if (await tryStat(path.join(src, 'SKILL.md'))) return src; |
| 136 | |
| 137 | // Single child wrapper (common with tar extracts and git clones) |
| 138 | const entries = await fs.readdir(src, { withFileTypes: true }); |
| 139 | const dirs = entries.filter(e => e.isDirectory() && !e.name.startsWith('.')); |
| 140 | if (dirs.length === 1) { |
| 141 | const inner = path.join(src, dirs[0]!.name); |
| 142 | if (await tryStat(path.join(inner, 'SKILL.md'))) return inner; |
| 143 | } |
| 144 | |
| 145 | // skills/<name>/SKILL.md layout (multi-skill repos pick the first) |
| 146 | const skillsParent = path.join(src, 'skills'); |
| 147 | try { |
| 148 | const sub = await fs.readdir(skillsParent, { withFileTypes: true }); |
| 149 | for (const ent of sub) { |
| 150 | if (!ent.isDirectory()) continue; |
| 151 | const cand = path.join(skillsParent, ent.name); |
| 152 | if (await tryStat(path.join(cand, 'SKILL.md'))) return cand; |
| 153 | } |
| 154 | } catch {} |
| 155 | |
| 156 | const ioNote = lastStatErr ? ` (note: a filesystem error occurred while looking: ${lastStatErr?.message ?? lastStatErr})` : ''; |
| 157 | throw new Error(`Couldn't find a SKILL.md in ${src}. Skills must ship a top-level SKILL.md or skills/<name>/SKILL.md.${ioNote}`); |
| 158 | } |
| 159 | |
| 160 | async function copyInto(srcRoot: string, originalSource: string, opts: { force?: boolean }): Promise<InstallResult> { |
| 161 | const raw = await fs.readFile(path.join(srcRoot, 'SKILL.md'), 'utf-8'); |
no test coverage detected