From 114d95dab2eb0d386a80489ee2fb3d8ea666e8aa Mon Sep 17 00:00:00 2001 From: Maciek Szczesniak Date: Mon, 12 Jan 2026 19:46:10 +0100 Subject: [PATCH 1/2] fix: show toast error message on ConfigMarkdown parse error --- packages/opencode/src/config/config.ts | 68 ++++++++++++++++++++++++-- packages/opencode/src/skill/skill.ts | 26 +++++++++- 2 files changed, 90 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index ead3a0149b4..7cba8b347ce 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,7 +220,27 @@ export namespace Config { dot: true, cwd: dir, })) { - const md = await ConfigMarkdown.parse(item) + let md + try { + md = await ConfigMarkdown.parse(item) + } catch (err) { + if (ConfigMarkdown.FrontmatterError.isInstance(err)) { + 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, + }) + } else { + log.error("failed to load command", { path: item, error: err }) + } + continue + } if (!md.data) continue const name = (() => { @@ -257,7 +279,27 @@ export namespace Config { dot: true, cwd: dir, })) { - const md = await ConfigMarkdown.parse(item) + let md + try { + md = await ConfigMarkdown.parse(item) + } catch (err) { + if (ConfigMarkdown.FrontmatterError.isInstance(err)) { + 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, + }) + } else { + log.error("failed to load agent", { path: item, error: err }) + } + continue + } if (!md.data) continue // Extract relative path from agent folder for nested agents @@ -299,7 +341,27 @@ export namespace Config { dot: true, cwd: dir, })) { - const md = await ConfigMarkdown.parse(item) + let md + try { + md = await ConfigMarkdown.parse(item) + } catch (err) { + if (ConfigMarkdown.FrontmatterError.isInstance(err)) { + 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, + }) + } else { + log.error("failed to load mode", { path: item, error: err }) + } + continue + } if (!md.data) continue const config = { diff --git a/packages/opencode/src/skill/skill.ts b/packages/opencode/src/skill/skill.ts index cbc042d1e96..db1db55dc76 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,28 @@ export namespace Skill { const skills: Record = {} const addSkill = async (match: string) => { - const md = await ConfigMarkdown.parse(match) + let md + try { + md = await ConfigMarkdown.parse(match) + } catch (err) { + if (ConfigMarkdown.FrontmatterError.isInstance(err)) { + 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, + }) + } else { + log.error("failed to load skill", { path: match, error: err }) + } + return + } + if (!md) { return } From 686a9fce4a708869ae4cf3635c1042f53ac94554 Mon Sep 17 00:00:00 2001 From: Maciek Szczesniak Date: Mon, 12 Jan 2026 20:06:07 +0100 Subject: [PATCH 2/2] style guide adjustments --- packages/opencode/src/config/config.ts | 111 ++++++++++++------------- packages/opencode/src/skill/skill.ts | 35 ++++---- 2 files changed, 67 insertions(+), 79 deletions(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 7cba8b347ce..449877aab98 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -220,28 +220,25 @@ export namespace Config { dot: true, cwd: dir, })) { - let md - try { - md = await ConfigMarkdown.parse(item) - } catch (err) { - if (ConfigMarkdown.FrontmatterError.isInstance(err)) { - 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, - }) - } else { + 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 } - continue - } - if (!md.data) continue + 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/"] @@ -279,28 +276,25 @@ export namespace Config { dot: true, cwd: dir, })) { - let md - try { - md = await ConfigMarkdown.parse(item) - } catch (err) { - if (ConfigMarkdown.FrontmatterError.isInstance(err)) { - 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, - }) - } else { + 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 } - continue - } - if (!md.data) continue + 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") @@ -341,28 +335,25 @@ export namespace Config { dot: true, cwd: dir, })) { - let md - try { - md = await ConfigMarkdown.parse(item) - } catch (err) { - if (ConfigMarkdown.FrontmatterError.isInstance(err)) { - 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, - }) - } else { + 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 } - continue - } - if (!md.data) continue + 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 db1db55dc76..92c775797d1 100644 --- a/packages/opencode/src/skill/skill.ts +++ b/packages/opencode/src/skill/skill.ts @@ -46,27 +46,24 @@ export namespace Skill { const skills: Record = {} const addSkill = async (match: string) => { - let md - try { - md = await ConfigMarkdown.parse(match) - } catch (err) { - if (ConfigMarkdown.FrontmatterError.isInstance(err)) { - 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, - }) - } else { + 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 } - return - } + 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