From b5274fcb63ec818be92baaace1c75298a500a776 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 5 Dec 2025 02:53:44 +0900 Subject: [PATCH] feat(config): add Zod schema validation and JSON Schema generation - Add Zod schema for oh-my-opencode.json configuration validation - Generate JSON Schema at build time for IDE autocompletion - Add safeParse validation with error reporting on config load - Export OhMyOpenCodeConfigSchema for programmatic usage - Add build:schema script and ./schema.json export - Update README with $schema usage documentation --- script/build-schema.ts | 28 +++++++++++++++++++++ src/config/index.ts | 15 +++++++++++ src/config/schema.ts | 57 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 script/build-schema.ts create mode 100644 src/config/index.ts create mode 100644 src/config/schema.ts diff --git a/script/build-schema.ts b/script/build-schema.ts new file mode 100644 index 0000000..43a5353 --- /dev/null +++ b/script/build-schema.ts @@ -0,0 +1,28 @@ +#!/usr/bin/env bun +import * as z from "zod" +import { OhMyOpenCodeConfigSchema } from "../src/config/schema" + +const SCHEMA_OUTPUT_PATH = "dist/oh-my-opencode.schema.json" + +async function main() { + console.log("Generating JSON Schema...") + + const jsonSchema = z.toJSONSchema(OhMyOpenCodeConfigSchema, { + io: "input", + target: "draft-7", + }) + + const finalSchema = { + $schema: "http://json-schema.org/draft-07/schema#", + $id: "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/dist/oh-my-opencode.schema.json", + title: "Oh My OpenCode Configuration", + description: "Configuration schema for oh-my-opencode plugin", + ...jsonSchema, + } + + await Bun.write(SCHEMA_OUTPUT_PATH, JSON.stringify(finalSchema, null, 2)) + + console.log(`✓ JSON Schema generated: ${SCHEMA_OUTPUT_PATH}`) +} + +main() diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 0000000..b6e2ab9 --- /dev/null +++ b/src/config/index.ts @@ -0,0 +1,15 @@ +export { + OhMyOpenCodeConfigSchema, + AgentOverrideConfigSchema, + AgentOverridesSchema, + McpNameSchema, + AgentNameSchema, +} from "./schema" + +export type { + OhMyOpenCodeConfig, + AgentOverrideConfig, + AgentOverrides, + McpName, + AgentName, +} from "./schema" diff --git a/src/config/schema.ts b/src/config/schema.ts new file mode 100644 index 0000000..e4237a0 --- /dev/null +++ b/src/config/schema.ts @@ -0,0 +1,57 @@ +import { z } from "zod" + +const PermissionValue = z.enum(["ask", "allow", "deny"]) + +const BashPermission = z.union([ + PermissionValue, + z.record(z.string(), PermissionValue), +]) + +const AgentPermissionSchema = z.object({ + edit: PermissionValue.optional(), + bash: BashPermission.optional(), + webfetch: PermissionValue.optional(), + doom_loop: PermissionValue.optional(), + external_directory: PermissionValue.optional(), +}) + +export const AgentNameSchema = z.enum([ + "oracle", + "librarian", + "explore", + "frontend-ui-ux-engineer", + "document-writer", +]) + +export const McpNameSchema = z.enum(["websearch_exa", "context7"]) + +export const AgentOverrideConfigSchema = z.object({ + model: z.string().optional(), + temperature: z.number().min(0).max(2).optional(), + top_p: z.number().min(0).max(1).optional(), + prompt: z.string().optional(), + tools: z.record(z.string(), z.boolean()).optional(), + disable: z.boolean().optional(), + description: z.string().optional(), + mode: z.enum(["subagent", "primary", "all"]).optional(), + color: z + .string() + .regex(/^#[0-9A-Fa-f]{6}$/) + .optional(), + permission: AgentPermissionSchema.optional(), +}) + +export const AgentOverridesSchema = z.record(AgentNameSchema, AgentOverrideConfigSchema) + +export const OhMyOpenCodeConfigSchema = z.object({ + $schema: z.string().optional(), + disabled_mcps: z.array(McpNameSchema).optional(), + disabled_agents: z.array(AgentNameSchema).optional(), + agents: AgentOverridesSchema.optional(), +}) + +export type OhMyOpenCodeConfig = z.infer +export type AgentOverrideConfig = z.infer +export type AgentOverrides = z.infer +export type McpName = z.infer +export type AgentName = z.infer