(skillRoot, options = {})
| 40 | } |
| 41 | |
| 42 | export async function evaluateSkill(skillRoot, options = {}) { |
| 43 | const prefix = options.prefix ? `${options.prefix}:` : ""; |
| 44 | const skillPath = path.join(skillRoot, "SKILL.md"); |
| 45 | const targetPath = relativePath(process.cwd(), skillRoot); |
| 46 | const checks = []; |
| 47 | const metrics = []; |
| 48 | const artifacts = []; |
| 49 | |
| 50 | if (!(await pathExists(skillPath))) { |
| 51 | checks.push( |
| 52 | createCheck({ |
| 53 | id: `${prefix}skill-file-missing`, |
| 54 | category: "skill-structure", |
| 55 | severity: "error", |
| 56 | status: "fail", |
| 57 | message: "The target skill directory is missing SKILL.md.", |
| 58 | evidence: [targetPath], |
| 59 | remediation: ["Add SKILL.md to the skill root."], |
| 60 | targetPath, |
| 61 | }), |
| 62 | ); |
| 63 | return { checks, metrics, artifacts }; |
| 64 | } |
| 65 | |
| 66 | const content = await readText(skillPath); |
| 67 | const lines = content.replace(/\r\n/g, "\n").split("\n"); |
| 68 | const parsed = parseFrontmatter(content); |
| 69 | const relativeLinks = findRelativeLinks(content); |
| 70 | const supportFiles = (await walkFiles(skillRoot)).filter((filePath) => filePath !== skillPath); |
| 71 | |
| 72 | if (parsed.errors.length > 0) { |
| 73 | checks.push( |
| 74 | createCheck({ |
| 75 | id: `${prefix}frontmatter-invalid`, |
| 76 | category: "skill-structure", |
| 77 | severity: "error", |
| 78 | status: "fail", |
| 79 | message: "The skill frontmatter could not be parsed.", |
| 80 | evidence: parsed.errors, |
| 81 | remediation: ["Fix the YAML frontmatter at the top of SKILL.md."], |
| 82 | targetPath, |
| 83 | }), |
| 84 | ); |
| 85 | } |
| 86 | |
| 87 | const frontmatter = parsed.data || {}; |
| 88 | const unexpectedKeys = Object.keys(frontmatter).filter((key) => !ALLOWED_FRONTMATTER_KEYS.has(key)); |
| 89 | if (unexpectedKeys.length > 0) { |
| 90 | checks.push( |
| 91 | createCheck({ |
| 92 | id: `${prefix}frontmatter-extra-keys`, |
| 93 | category: "best-practice", |
| 94 | severity: "warning", |
| 95 | status: "warn", |
| 96 | message: "The skill frontmatter contains keys outside the common Codex skill conventions.", |
| 97 | evidence: unexpectedKeys.map((key) => `Unexpected key: ${key}`), |
| 98 | remediation: ["Remove non-standard frontmatter keys or move the metadata into references."], |
| 99 | targetPath, |
no test coverage detected