diff --git a/packages/api/src/skills/__tests__/import.test.ts b/packages/api/src/skills/__tests__/import.test.ts index eb35f92de6..9aa1ebf620 100644 --- a/packages/api/src/skills/__tests__/import.test.ts +++ b/packages/api/src/skills/__tests__/import.test.ts @@ -188,6 +188,16 @@ describe('parseFrontmatter', () => { }); }); + it('does not treat frontmatter scalar lines that start with --- text as closing fences', () => { + const raw = `---\nname: marker\ndescription: 'first\n---not a closing fence\nlast'\nalways-apply: false\n---\n\nbody`; + expect(parseFrontmatter(raw)).toEqual({ + name: 'marker', + description: 'first ---not a closing fence last', + alwaysApply: false, + invalidBooleans: [], + }); + }); + it('returns empty fields when frontmatter is unterminated', () => { const raw = `---\nname: incomplete\n`; expect(parseFrontmatter(raw)).toEqual({ diff --git a/packages/api/src/skills/parse.ts b/packages/api/src/skills/parse.ts index 5261824706..ca640c63d3 100644 --- a/packages/api/src/skills/parse.ts +++ b/packages/api/src/skills/parse.ts @@ -23,11 +23,12 @@ function extractFrontmatterBlock(raw: string): string | null { if (!content.startsWith('---\n')) { return null; } - const closingIdx = content.indexOf('\n---', 4); - if (closingIdx === -1) { + const body = content.slice(4); + const closingFence = /(?:^|\n)---[ \t]*(?:\n|$)/.exec(body); + if (!closingFence) { return null; } - return content.slice(4, closingIdx); + return body.slice(0, closingFence.index); } function getCaseInsensitive(frontmatter: Record, key: string): unknown {