diff --git a/scripts/lib/skill-lint-test.js b/scripts/lib/skill-lint-test.js index ba08cb2a..f09fbacb 100644 --- a/scripts/lib/skill-lint-test.js +++ b/scripts/lib/skill-lint-test.js @@ -9,7 +9,7 @@ const fs = require('node:fs'); const os = require('node:os'); const path = require('node:path'); -const { lintSkillContent, lintSkillLayout, topLevelFrontmatterKeys } = require('./skill-lint.js'); +const { lintSkillContent, lintSkillLayout, topLevelFrontmatterKeys, parseFrontmatter, frontmatterYamlErrors } = require('./skill-lint.js'); const KNOWN = new Set(['alpha', 'beta']); @@ -648,3 +648,50 @@ test('the spec-key check tolerates CRLF frontmatter', () => { const { errors } = lintSkillContent('alpha', content, KNOWN); assert.equal(errors.filter(e => /key 'temperature'/.test(e)).length, 1); }); + +test('parseFrontmatter ignores comments and nested metadata keys', () => { + const content = [ + '---', + 'name: alpha', + '# note: this is a comment', + 'description: Designs alphas. Use when building one.', + 'metadata:', + ' name: beta', + ' description: Nested details without trigger', + '---', + '', + ].join('\n'); + assert.deepEqual(parseFrontmatter(content), { + name: 'alpha', + description: 'Designs alphas. Use when building one.', + metadata: '', + }); +}); + +test('frontmatterYamlErrors ignores comment lines containing colons or quotes', () => { + const content = [ + '---', + 'name: alpha', + '# note: a comment: with a colon and "unclosed quote', + 'description: "Designs alphas. Use when building one."', + '---', + '', + ].join('\n'); + assert.deepEqual(frontmatterYamlErrors(content), []); +}); + +test('a skill with frontmatter comments and nested metadata passes validation', () => { + const content = withAllSections([ + '---', + 'name: alpha', + '# Note: keep description up to date: check docs', + 'description: Designs alphas. Use when building one.', + 'metadata:', + ' name: beta', + ' description: Internal notes', + '---', + ].join('\n')); + const { errors } = lintSkillContent('alpha', content, KNOWN); + assert.deepEqual(errors, []); +}); + diff --git a/scripts/lib/skill-lint.js b/scripts/lib/skill-lint.js index 8779c9c0..7d30feec 100644 --- a/scripts/lib/skill-lint.js +++ b/scripts/lib/skill-lint.js @@ -184,11 +184,12 @@ function parseFrontmatter(content) { const result = {}; for (const line of match[1].split(/\r?\n/)) { - const colonIdx = line.indexOf(':'); - if (colonIdx === -1) continue; - const key = line.slice(0, colonIdx).trim(); - const value = line.slice(colonIdx + 1).trim().replace(/^['"]|['"]$/g, ''); - if (key) result[key] = value; + if (/^\s*#/.test(line)) continue; + const m = line.match(/^([A-Za-z0-9_-]+)\s*:\s*(.*)$/); + if (!m) continue; + const key = m[1]; + const value = m[2].trim().replace(/^['"]|['"]$/g, ''); + result[key] = value; } return result; } @@ -234,7 +235,7 @@ function frontmatterYamlErrors(content) { const lines = match[1].split(/\r?\n/); lines.forEach((line, i) => { const lineNo = i + 2; // the opening `---` is line 1 - if (!line.trim()) return; + if (!line.trim() || /^\s*#/.test(line)) return; if (/^[ ]*\t/.test(line)) { errors.push(