diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index ead3a0149b4..449877aab98 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -19,6 +19,8 @@ import { BunProc } from "@/bun" import { Installation } from "@/installation" import { ConfigMarkdown } from "./markdown" import { existsSync } from "fs" +import { Bus } from "@/bus" +import { TuiEvent } from "@/cli/cmd/tui/event" export namespace Config { const log = Log.create({ service: "config" }) @@ -218,8 +220,25 @@ export namespace Config { dot: true, cwd: dir, })) { - const md = await ConfigMarkdown.parse(item) - if (!md.data) continue + const md = await ConfigMarkdown.parse(item).catch((err) => { + if (!ConfigMarkdown.FrontmatterError.isInstance(err)) { + log.error("failed to load command", { path: item, error: err }) + return undefined + } + log.error("failed to parse command frontmatter", { + path: err.data.path, + message: err.data.message, + }) + const relativePath = path.relative(dir, item) + Bus.publish(TuiEvent.ToastShow, { + title: "Command Error", + message: `Failed to parse ${relativePath}: ${err.data.message.split(":")[0]}`, + variant: "error", + duration: 8000, + }) + return undefined + }) + if (!md?.data) continue const name = (() => { const patterns = ["/.opencode/command/", "/command/"] @@ -257,8 +276,25 @@ export namespace Config { dot: true, cwd: dir, })) { - const md = await ConfigMarkdown.parse(item) - if (!md.data) continue + const md = await ConfigMarkdown.parse(item).catch((err) => { + if (!ConfigMarkdown.FrontmatterError.isInstance(err)) { + log.error("failed to load agent", { path: item, error: err }) + return undefined + } + log.error("failed to parse agent frontmatter", { + path: err.data.path, + message: err.data.message, + }) + const relativePath = path.relative(dir, item) + Bus.publish(TuiEvent.ToastShow, { + title: "Agent Error", + message: `Failed to parse ${relativePath}: ${err.data.message.split(":")[0]}`, + variant: "error", + duration: 8000, + }) + return undefined + }) + if (!md?.data) continue // Extract relative path from agent folder for nested agents let agentName = path.basename(item, ".md") @@ -299,8 +335,25 @@ export namespace Config { dot: true, cwd: dir, })) { - const md = await ConfigMarkdown.parse(item) - if (!md.data) continue + const md = await ConfigMarkdown.parse(item).catch((err) => { + if (!ConfigMarkdown.FrontmatterError.isInstance(err)) { + log.error("failed to load mode", { path: item, error: err }) + return undefined + } + log.error("failed to parse mode frontmatter", { + path: err.data.path, + message: err.data.message, + }) + const relativePath = path.relative(dir, item) + Bus.publish(TuiEvent.ToastShow, { + title: "Mode Error", + message: `Failed to parse ${relativePath}: ${err.data.message.split(":")[0]}`, + variant: "error", + duration: 8000, + }) + return undefined + }) + if (!md?.data) continue const config = { name: path.basename(item, ".md"), diff --git a/packages/opencode/src/skill/skill.ts b/packages/opencode/src/skill/skill.ts index cbc042d1e96..92c775797d1 100644 --- a/packages/opencode/src/skill/skill.ts +++ b/packages/opencode/src/skill/skill.ts @@ -1,4 +1,5 @@ import z from "zod" +import path from "path" import { Config } from "../config/config" import { Instance } from "../project/instance" import { NamedError } from "@opencode-ai/util/error" @@ -8,6 +9,8 @@ import { Global } from "@/global" import { Filesystem } from "@/util/filesystem" import { exists } from "fs/promises" import { Flag } from "@/flag/flag" +import { Bus } from "@/bus" +import { TuiEvent } from "@/cli/cmd/tui/event" export namespace Skill { const log = Log.create({ service: "skill" }) @@ -43,7 +46,25 @@ export namespace Skill { const skills: Record = {} const addSkill = async (match: string) => { - const md = await ConfigMarkdown.parse(match) + const md = await ConfigMarkdown.parse(match).catch((err) => { + if (!ConfigMarkdown.FrontmatterError.isInstance(err)) { + log.error("failed to load skill", { path: match, error: err }) + return undefined + } + log.error("failed to parse skill frontmatter", { + path: err.data.path, + message: err.data.message, + }) + const relativePath = path.relative(Instance.directory, match) + Bus.publish(TuiEvent.ToastShow, { + title: "Skill Error", + message: `Failed to parse ${relativePath}: ${err.data.message.split(":")[0]}`, + variant: "error", + duration: 8000, + }) + return undefined + }) + if (!md) { return }