33import { z } from "zod"
44import { Config } from "@/config/config"
55import { zodObject } from "@opencode-ai/core/effect-zod"
6- import { TuiConfig } from "../src/cli/cmd/tui/config/tui"
6+ import { TuiJsonSchema } from "../src/cli/cmd/tui/config/tui-json-schema"
7+ import { Schema } from "effect"
8+
9+ type JsonSchema = Record < string , unknown >
710
811function generate ( schema : z . ZodType ) {
912 const result = z . toJSONSchema ( schema , {
@@ -34,7 +37,7 @@ function generate(schema: z.ZodType) {
3437 schema . examples = [ schema . default ]
3538 }
3639
37- schema . description = [ schema . description || "" , `default: \`${ String ( schema . default ) } \`` ]
40+ schema . description = [ schema . description || "" , `default: \`${ formatDefault ( schema . default ) } \`` ]
3841 . filter ( Boolean )
3942 . join ( "\n\n" )
4043 . trim ( )
@@ -52,6 +55,55 @@ function generate(schema: z.ZodType) {
5255 return result
5356}
5457
58+ function formatDefault ( value : unknown ) {
59+ if ( typeof value !== "object" || value === null ) return String ( value )
60+ return JSON . stringify ( value )
61+ }
62+
63+ function generateEffect ( schema : Schema . Top ) {
64+ const document = Schema . toJsonSchemaDocument ( schema )
65+ const normalized = normalize ( {
66+ $schema : "https://json-schema.org/draft/2020-12/schema" ,
67+ ...document . schema ,
68+ $defs : document . definitions ,
69+ } )
70+ if ( ! isRecord ( normalized ) ) throw new Error ( "schema generator produced a non-object schema" )
71+ normalized . allowComments = true
72+ normalized . allowTrailingCommas = true
73+ return normalized
74+ }
75+
76+ function normalize ( value : unknown ) : unknown {
77+ if ( Array . isArray ( value ) ) return value . map ( normalize )
78+ if ( ! isRecord ( value ) ) return value
79+
80+ const schema = Object . fromEntries ( Object . entries ( value ) . map ( ( [ key , item ] ) => [ key , normalize ( item ) ] ) )
81+
82+ if ( Array . isArray ( schema . anyOf ) ) {
83+ const anyOf = schema . anyOf . filter ( ( item ) => ! isRecord ( item ) || item . type !== "null" )
84+ if ( anyOf . length !== schema . anyOf . length ) {
85+ const { anyOf : _ , ...rest } = schema
86+ if ( anyOf . length === 1 && isRecord ( anyOf [ 0 ] ) ) return normalize ( { ...anyOf [ 0 ] , ...rest } )
87+ return { ...rest , anyOf }
88+ }
89+ }
90+
91+ if ( Array . isArray ( schema . allOf ) && schema . allOf . length === 1 && isRecord ( schema . allOf [ 0 ] ) ) {
92+ const { allOf : _ , ...rest } = schema
93+ return normalize ( { ...schema . allOf [ 0 ] , ...rest } )
94+ }
95+
96+ if ( schema . type === "integer" && schema . maximum === undefined ) {
97+ return { ...schema , maximum : Number . MAX_SAFE_INTEGER }
98+ }
99+
100+ return schema
101+ }
102+
103+ function isRecord ( value : unknown ) : value is JsonSchema {
104+ return typeof value === "object" && value !== null && ! Array . isArray ( value )
105+ }
106+
55107const configFile = process . argv [ 2 ]
56108const tuiFile = process . argv [ 3 ]
57109
@@ -60,5 +112,5 @@ await Bun.write(configFile, JSON.stringify(generate(zodObject(Config.Info).stric
60112
61113if ( tuiFile ) {
62114 console . log ( tuiFile )
63- await Bun . write ( tuiFile , JSON . stringify ( generate ( TuiConfig . JsonSchemaInfo ) , null , 2 ) )
115+ await Bun . write ( tuiFile , JSON . stringify ( generateEffect ( TuiJsonSchema . Info ) , null , 2 ) )
64116}
0 commit comments