fix: ensure frontmatter can process same content as other agents (#8719)

This commit is contained in:
Aiden Cline
2026-01-15 11:06:14 -08:00
committed by GitHub
parent 3f3550a16e
commit dc1c25cff5
8 changed files with 266 additions and 78 deletions

View File

@@ -28,7 +28,7 @@ export function FormatError(input: unknown) {
return `Directory "${input.data.dir}" in ${input.data.path} is not valid. Rename the directory to "${input.data.suggestion}" or remove it. This is a common typo.`
}
if (ConfigMarkdown.FrontmatterError.isInstance(input)) {
return `Failed to parse frontmatter in ${input.data.path}:\n${input.data.message}`
return input.data.message
}
if (Config.InvalidError.isInstance(input))
return [

View File

@@ -235,7 +235,7 @@ export namespace Config {
})) {
const md = await ConfigMarkdown.parse(item).catch((err) => {
const message = ConfigMarkdown.FrontmatterError.isInstance(err)
? `${err.data.path}: ${err.data.message}`
? err.data.message
: `Failed to parse command ${item}`
Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() })
log.error("failed to load command", { command: item, err })
@@ -274,7 +274,7 @@ export namespace Config {
})) {
const md = await ConfigMarkdown.parse(item).catch((err) => {
const message = ConfigMarkdown.FrontmatterError.isInstance(err)
? `${err.data.path}: ${err.data.message}`
? err.data.message
: `Failed to parse agent ${item}`
Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() })
log.error("failed to load agent", { agent: item, err })
@@ -312,7 +312,7 @@ export namespace Config {
})) {
const md = await ConfigMarkdown.parse(item).catch((err) => {
const message = ConfigMarkdown.FrontmatterError.isInstance(err)
? `${err.data.path}: ${err.data.message}`
? err.data.message
: `Failed to parse mode ${item}`
Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() })
log.error("failed to load mode", { mode: item, err })

View File

@@ -14,8 +14,60 @@ export namespace ConfigMarkdown {
return Array.from(template.matchAll(SHELL_REGEX))
}
export function preprocessFrontmatter(content: string): string {
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/)
if (!match) return content
const frontmatter = match[1]
const lines = frontmatter.split("\n")
const result: string[] = []
for (const line of lines) {
// skip comments and empty lines
if (line.trim().startsWith("#") || line.trim() === "") {
result.push(line)
continue
}
// skip lines that are continuations (indented)
if (line.match(/^\s+/)) {
result.push(line)
continue
}
// match key: value pattern
const kvMatch = line.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.*)$/)
if (!kvMatch) {
result.push(line)
continue
}
const key = kvMatch[1]
const value = kvMatch[2].trim()
// skip if value is empty, already quoted, or uses block scalar
if (value === "" || value === ">" || value === "|" || value.startsWith('"') || value.startsWith("'")) {
result.push(line)
continue
}
// if value contains a colon, convert to block scalar
if (value.includes(":")) {
result.push(`${key}: |`)
result.push(` ${value}`)
continue
}
result.push(line)
}
const processed = result.join("\n")
return content.replace(frontmatter, () => processed)
}
export async function parse(filePath: string) {
const template = await Bun.file(filePath).text()
const raw = await Bun.file(filePath).text()
const template = preprocessFrontmatter(raw)
try {
const md = matter(template)
@@ -24,7 +76,7 @@ export namespace ConfigMarkdown {
throw new FrontmatterError(
{
path: filePath,
message: `Failed to parse YAML frontmatter: ${err instanceof Error ? err.message : String(err)}`,
message: `${filePath}: Failed to parse YAML frontmatter: ${err instanceof Error ? err.message : String(err)}`,
},
{ cause: err },
)

View File

@@ -48,7 +48,7 @@ export namespace Skill {
const addSkill = async (match: string) => {
const md = await ConfigMarkdown.parse(match).catch((err) => {
const message = ConfigMarkdown.FrontmatterError.isInstance(err)
? `${err.data.path}: ${err.data.message}`
? err.data.message
: `Failed to parse skill ${match}`
Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() })
log.error("failed to load skill", { skill: match, err })