(skillName)
| 254 | // ── Lint one skill ──────────────────────────────────────────────────────────── |
| 255 | |
| 256 | function lintSkill(skillName) { |
| 257 | const filePath = path.join(SKILLS_DIR, skillName, 'SKILL.md'); |
| 258 | const content = fs.readFileSync(filePath, 'utf8'); |
| 259 | const { frontmatter, body } = parseFrontmatter(content); |
| 260 | |
| 261 | const results = []; |
| 262 | |
| 263 | for (const checkDef of CHECKS) { |
| 264 | let outcome; |
| 265 | try { |
| 266 | outcome = checkDef.check(frontmatter, body, skillName); |
| 267 | } catch (err) { |
| 268 | outcome = `check threw: ${err.message}`; |
| 269 | } |
| 270 | |
| 271 | if (outcome === null) continue; // not applicable — skip |
| 272 | |
| 273 | const passed = outcome === true || outcome === undefined; |
| 274 | const detail = typeof outcome === 'string' ? outcome : null; |
| 275 | |
| 276 | results.push({ |
| 277 | id: checkDef.id, |
| 278 | level: checkDef.level, |
| 279 | description: checkDef.description, |
| 280 | passed, |
| 281 | detail, |
| 282 | fix: passed ? null : checkDef.fix, |
| 283 | }); |
| 284 | } |
| 285 | |
| 286 | const failCount = results.filter(r => !r.passed && r.level === 'FAIL').length; |
| 287 | const warnCount = results.filter(r => !r.passed && r.level === 'WARN').length; |
| 288 | const passCount = results.filter(r => r.passed).length; |
| 289 | const skipCount = CHECKS.length - results.length; |
| 290 | |
| 291 | return { skillName, filePath, results, failCount, warnCount, passCount, skipCount }; |
| 292 | } |
| 293 | |
| 294 | // ── Reporting ───────────────────────────────────────────────────────────────── |
| 295 |
nothing calls this directly
no test coverage detected