-- subagents/frontmatter.lua: splitting a profile into YAML header and body. -- -- The body is the part a broken header must never cost the user, so every -- degraded case is checked for "body preserved verbatim" as well as for the -- warning. Cases that actually parse YAML need tinyyaml and skip without it. local frontmatter = require("subagents.frontmatter") local function yaml_or_skip() return pcall(require, "tinyyaml") end return { { "fenced header parses and keeps the body verbatim", function() if not yaml_or_skip() then return "skip", "tinyyaml is not installed" end local body = "You are a reviewer.\n\n indented line \n" local data, parsed_body, warning = frontmatter.parse( "---\nname: reviewer\ndescription: Reviews changes\nmodel: anthropic:sonnet\n---\n" .. body) assert(warning == nil, "unexpected warning: " .. tostring(warning)) assert(type(data) == "table", "expected a mapping") assert(data.name == "reviewer", tostring(data.name)) assert(data.description == "Reviews changes") assert(data.model == "anthropic:sonnet", tostring(data.model)) assert(parsed_body == body, string.format("body was rewritten: %q", parsed_body)) end }, { "CRLF fences are tolerated", function() if not yaml_or_skip() then return "skip", "tinyyaml is not installed" end local data, body, warning = frontmatter.parse("---\r\nname: crlf\r\n---\r\nbody\r\n") assert(warning == nil, tostring(warning)) assert(type(data) == "table" and data.name == "crlf", "header did not parse") assert(body == "body\r\n", string.format("%q", body)) end }, { "no fence means the whole file is the body", function() local text = "You are a reviewer.\n\n---\n\nNot a header.\n" local data, body, warning = frontmatter.parse(text) assert(data == nil, "expected no metadata") assert(body == text, "body was rewritten") assert(warning == nil, tostring(warning)) end }, { "an unterminated fence is treated as prose", function() local text = "---\nname: never closed\n\nstill prose\n" local data, body, warning = frontmatter.parse(text) assert(data == nil, "expected no metadata") assert(body == text, "body was rewritten") assert(warning == nil, "a lone rule is not an error") end }, { "an empty fenced block is an empty mapping", function() local data, body, warning = frontmatter.parse("---\n---\nbody\n") assert(type(data) == "table" and next(data) == nil, "expected an empty mapping") assert(body == "body\n", string.format("%q", body)) assert(warning == nil, tostring(warning)) end }, { "a header the parser rejects warns and keeps the body", function() if not yaml_or_skip() then return "skip", "tinyyaml is not installed" end local data, body, warning = frontmatter.parse("---\njust a string\n---\nbody\n") assert(data == nil, "a rejected header must not produce metadata") assert(body == "body\n", string.format("%q", body)) assert(type(warning) == "string" and warning:find("did not parse", 1, true), "expected a parse warning, got " .. tostring(warning)) end }, { "an unresolved alias is dropped rather than read literally", function() if not yaml_or_skip() then return "skip", "tinyyaml is not installed" end -- tinyyaml does not resolve aliases: without the guard in parse(), the -- `*d` token would arrive as the literal string "*d" and become this -- profile's name. local data, body, warning = frontmatter.parse( "---\ndefs: &d my-agent\nname: *d\n---\nbody\n") assert(type(data) == "table", "the mapping itself should survive") assert(data.name == nil, "an alias must not become a value, got " .. tostring(data.name)) assert(data.defs == nil, "an anchor must not become a value, got " .. tostring(data.defs)) assert(body == "body\n", string.format("%q", body)) assert(type(warning) == "string" and warning:find("anchors/aliases", 1, true), "expected an alias warning, got " .. tostring(warning)) end }, { "empty input is empty output", function() local data, body, warning = frontmatter.parse("") assert(data == nil and body == "" and warning == nil) end }, }