fix: show error messages when oh-my-opencode.json config fails to load (#242)

* fix: show error messages when oh-my-opencode.json config fails to load

- Add console.error output for config parse errors (syntax errors)
- Add console.error output for config validation errors (schema violations)
- Display helpful hints for JSON syntax errors
- List all validation errors clearly with proper formatting
- Errors now shown immediately regardless of hook configuration

Fixes #241

* refactor: replace console.error with toast notifications for config errors

- Replace console.error with ctx.client.tui.showToast() for better UX
- Show toast notifications for both syntax errors and validation errors
- Toast notifications persist for 10 seconds for visibility
- Display error details with bullet points for validation errors
- Include helpful hints for JSON syntax errors

This provides a more user-friendly notification system that integrates
with OpenCode's UI instead of just logging to console.

---------

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
This commit is contained in:
Sisyphus
2025-12-26 02:04:56 +09:00
committed by GitHub
parent 57ef5df932
commit ad2bd673c4

View File

@@ -116,7 +116,7 @@ function migrateConfigFile(configPath: string, rawConfig: Record<string, unknown
return needsWrite; return needsWrite;
} }
function loadConfigFromPath(configPath: string): OhMyOpenCodeConfig | null { function loadConfigFromPath(configPath: string, ctx: any): OhMyOpenCodeConfig | null {
try { try {
if (fs.existsSync(configPath)) { if (fs.existsSync(configPath)) {
const content = fs.readFileSync(configPath, "utf-8"); const content = fs.readFileSync(configPath, "utf-8");
@@ -130,6 +130,20 @@ function loadConfigFromPath(configPath: string): OhMyOpenCodeConfig | null {
const errorMsg = result.error.issues.map(i => `${i.path.join(".")}: ${i.message}`).join(", "); const errorMsg = result.error.issues.map(i => `${i.path.join(".")}: ${i.message}`).join(", ");
log(`Config validation error in ${configPath}:`, result.error.issues); log(`Config validation error in ${configPath}:`, result.error.issues);
addConfigLoadError({ path: configPath, error: `Validation error: ${errorMsg}` }); addConfigLoadError({ path: configPath, error: `Validation error: ${errorMsg}` });
const errorList = result.error.issues
.map(issue => `${issue.path.join(".")}: ${issue.message}`)
.join("\n");
ctx.client.tui.showToast({
body: {
title: "❌ OhMyOpenCode: Config Validation Failed",
message: `Failed to load ${configPath}\n\nValidation errors:\n${errorList}\n\nConfig will be ignored. Please fix the errors above.`,
variant: "error" as const,
duration: 10000,
},
}).catch(() => {});
return null; return null;
} }
@@ -140,6 +154,19 @@ function loadConfigFromPath(configPath: string): OhMyOpenCodeConfig | null {
const errorMsg = err instanceof Error ? err.message : String(err); const errorMsg = err instanceof Error ? err.message : String(err);
log(`Error loading config from ${configPath}:`, err); log(`Error loading config from ${configPath}:`, err);
addConfigLoadError({ path: configPath, error: errorMsg }); addConfigLoadError({ path: configPath, error: errorMsg });
const hint = err instanceof SyntaxError
? "\n\nHint: Check for syntax errors in your JSON file (missing commas, quotes, brackets, etc.)"
: "";
ctx.client.tui.showToast({
body: {
title: "❌ OhMyOpenCode: Config Load Failed",
message: `Failed to load ${configPath}\n\nError: ${errorMsg}${hint}\n\nConfig will be ignored. Please fix the error above.`,
variant: "error" as const,
duration: 10000,
},
}).catch(() => {});
} }
return null; return null;
} }
@@ -174,7 +201,7 @@ function mergeConfigs(
}; };
} }
function loadPluginConfig(directory: string): OhMyOpenCodeConfig { function loadPluginConfig(directory: string, ctx: any): OhMyOpenCodeConfig {
// User-level config path (OS-specific) // User-level config path (OS-specific)
const userConfigPath = path.join( const userConfigPath = path.join(
getUserConfigDir(), getUserConfigDir(),
@@ -190,10 +217,10 @@ function loadPluginConfig(directory: string): OhMyOpenCodeConfig {
); );
// Load user config first (base) // Load user config first (base)
let config: OhMyOpenCodeConfig = loadConfigFromPath(userConfigPath) ?? {}; let config: OhMyOpenCodeConfig = loadConfigFromPath(userConfigPath, ctx) ?? {};
// Override with project config // Override with project config
const projectConfig = loadConfigFromPath(projectConfigPath); const projectConfig = loadConfigFromPath(projectConfigPath, ctx);
if (projectConfig) { if (projectConfig) {
config = mergeConfigs(config, projectConfig); config = mergeConfigs(config, projectConfig);
} }
@@ -209,7 +236,7 @@ function loadPluginConfig(directory: string): OhMyOpenCodeConfig {
} }
const OhMyOpenCodePlugin: Plugin = async (ctx) => { const OhMyOpenCodePlugin: Plugin = async (ctx) => {
const pluginConfig = loadPluginConfig(ctx.directory); const pluginConfig = loadPluginConfig(ctx.directory, ctx);
const disabledHooks = new Set(pluginConfig.disabled_hooks ?? []); const disabledHooks = new Set(pluginConfig.disabled_hooks ?? []);
const isHookEnabled = (hookName: HookName) => !disabledHooks.has(hookName); const isHookEnabled = (hookName: HookName) => !disabledHooks.has(hookName);