mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(skills): warn on unterminated frontmatter (#110479)
This commit is contained in:
@@ -201,6 +201,16 @@ metadata:
|
||||
expect(parseFrontmatterBlock(content)).toStrictEqual({});
|
||||
});
|
||||
|
||||
it("reports an unterminated frontmatter block", () => {
|
||||
const result = parseFrontmatterBlockResult(`---
|
||||
name: broken
|
||||
description: Missing the closing delimiter
|
||||
`);
|
||||
|
||||
expect(result.frontmatter).toEqual({});
|
||||
expect(result.issues).toEqual([expect.objectContaining({ code: "UNTERMINATED_FRONTMATTER" })]);
|
||||
});
|
||||
|
||||
it("ignores non-delimiter opening prefixes", () => {
|
||||
for (const prefix of ["---not", "----", "--- name"]) {
|
||||
const content = `${prefix}
|
||||
|
||||
@@ -200,11 +200,12 @@ function normalizeFrontmatterContent(content: string): string {
|
||||
}
|
||||
|
||||
const FRONTMATTER_CLOSING_DELIMITER = /(?:^|\n)---[^\S\n]*(?:\n|(?![\s\S]))/;
|
||||
const FRONTMATTER_OPENING_DELIMITER = /^---[^\S\n]*\n/;
|
||||
|
||||
function extractFrontmatterBlockFromNormalized(
|
||||
normalized: string,
|
||||
): ExtractedFrontmatterBlock | undefined {
|
||||
const opening = /^---[^\S\n]*\n/.exec(normalized);
|
||||
const opening = FRONTMATTER_OPENING_DELIMITER.exec(normalized);
|
||||
if (!opening) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -239,6 +240,20 @@ export function parseFrontmatterBlock(content: string): ParsedFrontmatter {
|
||||
|
||||
/** Parses frontmatter once while retaining recoverable YAML parser issues for owning loaders. */
|
||||
export function parseFrontmatterBlockResult(content: string): ParsedFrontmatterBlockResult {
|
||||
const block = extractFrontmatterBlock(content)?.block;
|
||||
return block ? parseYamlFrontmatter(block) : { frontmatter: {}, issues: [] };
|
||||
const normalized = normalizeFrontmatterContent(content);
|
||||
const block = extractFrontmatterBlockFromNormalized(normalized)?.block;
|
||||
if (block !== undefined) {
|
||||
return block ? parseYamlFrontmatter(block) : { frontmatter: {}, issues: [] };
|
||||
}
|
||||
return FRONTMATTER_OPENING_DELIMITER.test(normalized)
|
||||
? {
|
||||
frontmatter: {},
|
||||
issues: [
|
||||
{
|
||||
code: "UNTERMINATED_FRONTMATTER",
|
||||
message: "missing closing --- delimiter",
|
||||
},
|
||||
],
|
||||
}
|
||||
: { frontmatter: {}, issues: [] };
|
||||
}
|
||||
|
||||
@@ -423,11 +423,14 @@ metadata:
|
||||
expect(entry?.metadata?.requires?.env).toEqual(["EXAMPLE_VAR"]);
|
||||
});
|
||||
|
||||
it("warns with the malformed file and keeps loading sibling workspace skills", async () => {
|
||||
it("warns for malformed files and keeps loading sibling workspace skills", async () => {
|
||||
const workspaceDir = await createTempWorkspaceDir();
|
||||
const brokenDir = path.join(workspaceDir, "skills", "broken");
|
||||
const brokenFile = path.join(brokenDir, "SKILL.md");
|
||||
const unterminatedDir = path.join(workspaceDir, "skills", "unterminated");
|
||||
const unterminatedFile = path.join(unterminatedDir, "SKILL.md");
|
||||
await fs.mkdir(brokenDir, { recursive: true });
|
||||
await fs.mkdir(unterminatedDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
brokenFile,
|
||||
`---
|
||||
@@ -437,6 +440,11 @@ description: Broken skill
|
||||
`,
|
||||
"utf8",
|
||||
);
|
||||
await fs.writeFile(
|
||||
unterminatedFile,
|
||||
"---\nname: unterminated\ndescription: Missing closing delimiter\n",
|
||||
"utf8",
|
||||
);
|
||||
await writeSkill({
|
||||
dir: path.join(workspaceDir, "skills", "valid"),
|
||||
name: "valid",
|
||||
@@ -451,6 +459,11 @@ description: Broken skill
|
||||
expect(entries.map((entry) => entry.skill.name)).not.toContain("broken");
|
||||
expect(warningText).toContain(brokenFile);
|
||||
expect(warningText).toContain("invalid frontmatter: BAD_INDENT");
|
||||
expect(entries.map((entry) => entry.skill.name)).not.toContain("unterminated");
|
||||
expect(warningText).toContain(unterminatedFile);
|
||||
expect(warningText).toContain(
|
||||
"invalid frontmatter: UNTERMINATED_FRONTMATTER: missing closing --- delimiter",
|
||||
);
|
||||
});
|
||||
|
||||
it("applies agent skill filters and replacement semantics", async () => {
|
||||
|
||||
Reference in New Issue
Block a user