diff --git a/packages/opencode/src/config/markdown.ts b/packages/opencode/src/config/markdown.ts index f20842c41a9..01eaf7123e8 100644 --- a/packages/opencode/src/config/markdown.ts +++ b/packages/opencode/src/config/markdown.ts @@ -14,6 +14,74 @@ export namespace ConfigMarkdown { return Array.from(template.matchAll(SHELL_REGEX)) } + function fixYamlFrontmatter(content: string): string { + const lines = content.split("\n") + const result: string[] = [] + let inFrontmatter = false + let frontmatterEnd = false + + for (const line of lines) { + if (line.trim() === "---" && !inFrontmatter) { + inFrontmatter = true + result.push(line) + continue + } + + if (line.trim() === "---" && inFrontmatter && !frontmatterEnd) { + frontmatterEnd = true + inFrontmatter = false + result.push(line) + continue + } + + if (!inFrontmatter) { + result.push(line) + continue + } + + const colonIndex = line.indexOf(":") + if (colonIndex === -1) { + result.push(line) + continue + } + + const key = line.slice(0, colonIndex).trim() + const value = line.slice(colonIndex + 1).trim() + + if (!value || value.startsWith("#") || value.startsWith("|") || value.startsWith(">")) { + result.push(line) + continue + } + + const alreadyQuoted = + (value.startsWith("'") && value.endsWith("'")) || (value.startsWith('"') && value.endsWith('"')) + + if (alreadyQuoted) { + result.push(line) + continue + } + + const specialChars = /[:#{}\[\],>|\*&@%`]/ + const needsQuoting = specialChars.test(value) + + if (!needsQuoting) { + result.push(line) + continue + } + + let quotedValue: string + if (value.includes("'")) { + quotedValue = `"${value.replace(/"/g, '\\"')}"` + } else { + quotedValue = `'${value}'` + } + + result.push(`${key}: ${quotedValue}`) + } + + return result.join("\n") + } + export async function parse(filePath: string) { const template = await Bun.file(filePath).text() @@ -21,13 +89,19 @@ export namespace ConfigMarkdown { const md = matter(template) return md } catch (err) { - throw new FrontmatterError( - { - path: filePath, - message: `Failed to parse YAML frontmatter: ${err instanceof Error ? err.message : String(err)}`, - }, - { cause: err }, - ) + const fixed = fixYamlFrontmatter(template) + try { + const md = matter(fixed) + return md + } catch { + throw new FrontmatterError( + { + path: filePath, + message: `Failed to parse YAML frontmatter: ${err instanceof Error ? err.message : String(err)}`, + }, + { cause: err }, + ) + } } }