(content: string)
| 18 | |
| 19 | // 解析 SKILL.cat.md 内容:YAML frontmatter + markdown body |
| 20 | export function parseSkillMd(content: string): { metadata: SkillMetadata; prompt: string } | null { |
| 21 | const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/); |
| 22 | if (!match) return null; |
| 23 | |
| 24 | const [, frontmatter, body] = match; |
| 25 | |
| 26 | let parsed: Record<string, unknown>; |
| 27 | try { |
| 28 | parsed = parseYaml(frontmatter); |
| 29 | } catch { |
| 30 | return null; |
| 31 | } |
| 32 | |
| 33 | if (!parsed || typeof parsed !== "object") return null; |
| 34 | |
| 35 | const name = typeof parsed.name === "string" ? parsed.name : ""; |
| 36 | if (!name) return null; |
| 37 | |
| 38 | const description = typeof parsed.description === "string" ? parsed.description : ""; |
| 39 | const version = typeof parsed.version === "string" ? parsed.version : undefined; |
| 40 | |
| 41 | // 解析 scripts 文件名列表(URL 安装时使用) |
| 42 | const scripts = Array.isArray(parsed.scripts) |
| 43 | ? parsed.scripts.filter((s): s is string => typeof s === "string") |
| 44 | : undefined; |
| 45 | |
| 46 | // 解析 references 文件名列表(URL 安装时使用) |
| 47 | const references = Array.isArray(parsed.references) |
| 48 | ? parsed.references.filter((r): r is string => typeof r === "string") |
| 49 | : undefined; |
| 50 | |
| 51 | // 解析 config 块 |
| 52 | let config: Record<string, SkillConfigField> | undefined; |
| 53 | if (parsed.config && typeof parsed.config === "object" && !Array.isArray(parsed.config)) { |
| 54 | const rawConfig = parsed.config as Record<string, unknown>; |
| 55 | const entries = Object.entries(rawConfig); |
| 56 | if (entries.length > 0) { |
| 57 | config = {}; |
| 58 | for (const [key, value] of entries) { |
| 59 | if (value && typeof value === "object" && !Array.isArray(value)) { |
| 60 | config[key] = normalizeConfigField(value as Record<string, unknown>); |
| 61 | } |
| 62 | } |
| 63 | // 如果解析后没有有效字段,置为 undefined |
| 64 | if (Object.keys(config).length === 0) config = undefined; |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | return { |
| 69 | metadata: { |
| 70 | name, |
| 71 | description, |
| 72 | ...(version ? { version } : {}), |
| 73 | ...(scripts?.length ? { scripts } : {}), |
| 74 | ...(references?.length ? { references } : {}), |
| 75 | ...(config ? { config } : {}), |
| 76 | }, |
| 77 | prompt: body.trim(), |
no test coverage detected