43 lines
1.8 KiB
TypeScript
43 lines
1.8 KiB
TypeScript
// Minimal YAML-subset frontmatter parser for upstream SKILL.md files.
|
|
// Supports exactly what mattpocock/skills uses (census 2026-10-14, @49dd158): top-level
|
|
// `key: value` scalars, double/single-quoted strings with \" escapes, and nested blocks
|
|
// (e.g. `metadata:`) which are skipped. test/generate.test.ts asserts every vendored skill
|
|
// parses with non-empty name+description, so an upstream format change fails the build
|
|
// instead of silently producing a broken manifest.
|
|
|
|
export type Frontmatter = Record<string, string>
|
|
|
|
export function parseFrontmatter(content: string): { frontmatter: Frontmatter; body: string } {
|
|
const text = content.replace(/^\uFEFF/, "")
|
|
if (!text.startsWith("---\n") && !text.startsWith("---\r\n")) {
|
|
return { frontmatter: {}, body: text }
|
|
}
|
|
const end = text.indexOf("\n---", 3)
|
|
if (end === -1) return { frontmatter: {}, body: text }
|
|
const raw = text.slice(text.indexOf("\n") + 1, end + 1)
|
|
const body = text.slice(text.indexOf("\n", end + 1) + 1)
|
|
|
|
const frontmatter: Frontmatter = {}
|
|
for (const line of raw.split(/\r?\n/)) {
|
|
if (!line || /^\s/.test(line) || line.trim() === "---") continue // nested block or separator
|
|
const m = /^([A-Za-z][\w-]*):\s*(.*)$/.exec(line)
|
|
if (!m) continue
|
|
frontmatter[m[1]] = unquote(m[2].trim())
|
|
}
|
|
return { frontmatter, body }
|
|
}
|
|
|
|
function unquote(value: string): string {
|
|
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
|
|
return value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, "\\")
|
|
}
|
|
if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) {
|
|
return value.slice(1, -1).replace(/''/g, "'")
|
|
}
|
|
return value
|
|
}
|
|
|
|
export function isUserInvoked(frontmatter: Frontmatter): boolean {
|
|
return frontmatter["disable-model-invocation"] === "true"
|
|
}
|