Просмотр исходного кода

fix: better frontmatter errors

Aiden Cline 3 месяцев назад
Родитель
Сommit
42e0b47a7d

+ 10 - 3
packages/opencode/src/cli/error.ts

@@ -1,3 +1,4 @@
+import { ConfigMarkdown } from "@/config/markdown"
 import { Config } from "../config/config"
 import { MCP } from "../mcp"
 import { UI } from "./ui"
@@ -7,16 +8,22 @@ export function FormatError(input: unknown) {
     return `MCP server "${input.data.name}" failed. Note, opencode does not support MCP authentication yet.`
   if (Config.JsonError.isInstance(input)) {
     return (
-      `Config file at ${input.data.path} is not valid JSON(C)` + (input.data.message ? `: ${input.data.message}` : "")
+      `Config file at ${input.data.path} is not valid JSON(C)` +
+      (input.data.message ? `: ${input.data.message}` : "")
     )
   }
   if (Config.ConfigDirectoryTypoError.isInstance(input)) {
     return `Directory "${input.data.dir}" in ${input.data.path} is not valid. Use "${input.data.suggestion}" instead. This is a common typo.`
   }
+  if (ConfigMarkdown.FrontmatterError.isInstance(input)) {
+    return `Failed to parse frontmatter in ${input.data.path}:\n${input.data.message}`
+  }
   if (Config.InvalidError.isInstance(input))
     return [
-      `Config file at ${input.data.path} is invalid` + (input.data.message ? `: ${input.data.message}` : ""),
-      ...(input.data.issues?.map((issue) => "↳ " + issue.message + " " + issue.path.join(".")) ?? []),
+      `Config file at ${input.data.path} is invalid` +
+        (input.data.message ? `: ${input.data.message}` : ""),
+      ...(input.data.issues?.map((issue) => "↳ " + issue.message + " " + issue.path.join(".")) ??
+        []),
     ].join("\n")
 
   if (UI.CancelledError.isInstance(input)) return ""

+ 4 - 7
packages/opencode/src/config/config.ts

@@ -9,7 +9,6 @@ import { Global } from "../global"
 import fs from "fs/promises"
 import { lazy } from "../util/lazy"
 import { NamedError } from "../util/error"
-import matter from "gray-matter"
 import { Flag } from "../flag/flag"
 import { Auth } from "../auth"
 import {
@@ -21,6 +20,7 @@ import { Instance } from "../project/instance"
 import { LSPServer } from "../lsp/server"
 import { BunProc } from "@/bun"
 import { Installation } from "@/installation"
+import { ConfigMarkdown } from "./markdown"
 
 export namespace Config {
   const log = Log.create({ service: "config" })
@@ -191,8 +191,7 @@ export namespace Config {
       dot: true,
       cwd: dir,
     })) {
-      const content = await Bun.file(item).text()
-      const md = matter(content)
+      const md = await ConfigMarkdown.parse(item)
       if (!md.data) continue
 
       const name = (() => {
@@ -231,8 +230,7 @@ export namespace Config {
       dot: true,
       cwd: dir,
     })) {
-      const content = await Bun.file(item).text()
-      const md = matter(content)
+      const md = await ConfigMarkdown.parse(item)
       if (!md.data) continue
 
       // Extract relative path from agent folder for nested agents
@@ -274,8 +272,7 @@ export namespace Config {
       dot: true,
       cwd: dir,
     })) {
-      const content = await Bun.file(item).text()
-      const md = matter(content)
+      const md = await ConfigMarkdown.parse(item)
       if (!md.data) continue
 
       const config = {

+ 29 - 0
packages/opencode/src/config/markdown.ts

@@ -1,3 +1,7 @@
+import { NamedError } from "@/util/error"
+import matter from "gray-matter"
+import { z } from "zod"
+
 export namespace ConfigMarkdown {
   export const FILE_REGEX = /(?<![\w`])@(\.?[^\s`,.]*(?:\.[^\s`,.]+)*)/g
   export const SHELL_REGEX = /!`([^`]+)`/g
@@ -9,4 +13,29 @@ export namespace ConfigMarkdown {
   export function shell(template: string) {
     return Array.from(template.matchAll(SHELL_REGEX))
   }
+
+  export async function parse(filePath: string) {
+    const template = await Bun.file(filePath).text()
+
+    try {
+      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 },
+      )
+    }
+  }
+
+  export const FrontmatterError = NamedError.create(
+    "ConfigFrontmatterError",
+    z.object({
+      path: z.string(),
+      message: z.string(),
+    }),
+  )
 }