diff --git a/packages/cli/src/commands/skills.test.ts b/packages/cli/src/commands/skills.test.ts index 83847e7a..18f7755c 100644 --- a/packages/cli/src/commands/skills.test.ts +++ b/packages/cli/src/commands/skills.test.ts @@ -211,6 +211,33 @@ describe('skills new command', () => { expect(stdout.join('\n')).toContain('wrote'); }); + it('does not infer metadata from frontmatter-shaped text in the Markdown body', async () => { + const skillDir = join(tempDir, 'plain-skill'); + mkdirSync(skillDir, { recursive: true }); + const skillFile = join(skillDir, 'SKILL.md'); + writeFileSync(skillFile, [ + '# Plain Skill', + '', + 'Example configuration:', + '', + 'name: body-example', + 'description: This belongs to the example, not skill metadata', + '', + ].join('\n')); + const out = join(tempDir, 'plain-skill.json'); + const newCmd = skillsCmd.commands.find((c) => c.name() === 'new')!; + + await newCmd.parseAsync([ + '--skill-file', skillFile, + '--out', out, + ], { from: 'user' }); + + const manifest = JSON.parse(readFileSync(out, 'utf8')); + expect(manifest.name).toBe('plain-skill'); + expect(manifest.title).toBe('plain-skill'); + expect(manifest.description).toBe('Agent skill: plain-skill'); + }); + it.each(['-5', '1.9', '5abc', '1e2', '0x10', '+5', `${Number.MAX_SAFE_INTEGER + 1}`])( 'rejects invalid listing price %s before writing a manifest', async (price) => { diff --git a/packages/cli/src/commands/skills.ts b/packages/cli/src/commands/skills.ts index b4203b2a..a34fb7de 100644 --- a/packages/cli/src/commands/skills.ts +++ b/packages/cli/src/commands/skills.ts @@ -92,7 +92,9 @@ export function titleFromSlug(slug: string): string { function q(s: string): string { return JSON.stringify(s); } async function exists(path: string): Promise { try { await access(path); return true; } catch { return false; } } function frontmatterValue(text: string, key: string): string | undefined { - const m = text.match(new RegExp(`^${key}:\\s*["']?([^"'\\n]+)["']?\\s*$`, 'm')); + const frontmatter = text.match(/^(?:\uFEFF)?---[ \t]*(?:\r?\n|\r)([\s\S]*?)(?:\r?\n|\r)---[ \t]*(?:(?:\r?\n|\r)|$)/)?.[1]; + if (frontmatter === undefined) return undefined; + const m = frontmatter.match(new RegExp(`^${key}:\\s*["']?([^"'\\n]+)["']?\\s*$`, 'm')); return m?.[1]?.trim(); } async function inferFromSkill(file: string): Promise> {