* Extract key-value pairs from a YAML-like frontmatter block. * Handles: string values, booleans, multiline (>-), and simple lists. * * @param {string} content - Full SKILL.md content * @returns {{ frontmatter: object|null, body: string }}
(content)
| 193 | * @returns {{ frontmatter: object|null, body: string }} |
| 194 | */ |
| 195 | function parseFrontmatter(content) { |
| 196 | const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/); |
| 197 | if (!match) return { frontmatter: null, body: content }; |
| 198 | |
| 199 | const raw = match[1]; |
| 200 | const body = match[2]; |
| 201 | const fm = {}; |
| 202 | |
| 203 | const lines = raw.split(/\r?\n/); |
| 204 | let i = 0; |
| 205 | while (i < lines.length) { |
| 206 | const line = lines[i]; |
| 207 | |
| 208 | // Multiline value (>- or |) |
| 209 | const multilineMatch = line.match(/^(\S[\w-]*):\s*[>|]-?\s*$/); |
| 210 | if (multilineMatch) { |
| 211 | const key = multilineMatch[1]; |
| 212 | const parts = []; |
| 213 | i++; |
| 214 | while (i < lines.length && /^\s+/.test(lines[i])) { |
| 215 | parts.push(lines[i].trim()); |
| 216 | i++; |
| 217 | } |
| 218 | fm[key] = parts.join(' '); |
| 219 | continue; |
| 220 | } |
| 221 | |
| 222 | // Simple key: value |
| 223 | const kvMatch = line.match(/^(\S[\w-]*):\s*(.*)/); |
| 224 | if (kvMatch) { |
| 225 | const key = kvMatch[1]; |
| 226 | const val = kvMatch[2].trim(); |
| 227 | if (val === 'true') fm[key] = true; |
| 228 | else if (val === 'false') fm[key] = false; |
| 229 | else if (val.startsWith('[') && val.endsWith(']')) { |
| 230 | fm[key] = val.slice(1, -1).split(',').map(s => s.trim()).filter(Boolean); |
| 231 | } |
| 232 | else if (val === '') fm[key] = null; |
| 233 | else fm[key] = val; |
| 234 | } |
| 235 | i++; |
| 236 | } |
| 237 | |
| 238 | return { frontmatter: fm, body }; |
| 239 | } |
| 240 | |
| 241 | // ── Skill discovery ─────────────────────────────────────────────────────────── |
| 242 |
no outgoing calls
no test coverage detected