63 lines
2.3 KiB
C#
63 lines
2.3 KiB
C#
namespace ClaudeDo.Worker.Skills;
|
|
|
|
internal static class SkillFrontmatter
|
|
{
|
|
public sealed record ParsedFrontmatter(string Name, string Description);
|
|
|
|
/// <summary>
|
|
/// Parses the YAML frontmatter block (between the first two "---" lines) of a SKILL.md
|
|
/// file, extracting "name" (required) and "description" (optional). The description may
|
|
/// be a folded scalar ("description: >"), whose indented continuation lines are joined
|
|
/// with spaces into a single string.
|
|
/// </summary>
|
|
public static ParsedFrontmatter Parse(string skillMdPath)
|
|
{
|
|
var lines = File.ReadAllLines(skillMdPath);
|
|
if (lines.Length == 0 || lines[0].Trim() != "---")
|
|
throw new InvalidOperationException($"'{skillMdPath}' has no YAML frontmatter block.");
|
|
|
|
string? name = null;
|
|
var description = "";
|
|
|
|
var i = 1;
|
|
for (; i < lines.Length; i++)
|
|
{
|
|
var line = lines[i];
|
|
if (line.Trim() == "---") break;
|
|
|
|
if (line.StartsWith("name:", StringComparison.Ordinal))
|
|
{
|
|
name = line["name:".Length..].Trim().Trim('"', '\'');
|
|
}
|
|
else if (line.StartsWith("description:", StringComparison.Ordinal))
|
|
{
|
|
var rest = line["description:".Length..].Trim();
|
|
if (rest == ">" || rest == ">-" || rest == "|" || rest == "|-")
|
|
{
|
|
var folded = new List<string>();
|
|
var j = i + 1;
|
|
for (; j < lines.Length; j++)
|
|
{
|
|
var contLine = lines[j];
|
|
if (contLine.Trim() == "---") break;
|
|
if (contLine.Length == 0) continue;
|
|
if (!char.IsWhiteSpace(contLine[0])) break;
|
|
folded.Add(contLine.Trim());
|
|
}
|
|
description = string.Join(" ", folded);
|
|
i = j - 1;
|
|
}
|
|
else
|
|
{
|
|
description = rest.Trim('"', '\'');
|
|
}
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(name))
|
|
throw new InvalidOperationException($"'{skillMdPath}' frontmatter is missing required 'name'.");
|
|
|
|
return new ParsedFrontmatter(name, description);
|
|
}
|
|
}
|