Files
mattpocock-skills-opencode/scripts/frontmatter.ts
T

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"
}