1package main
2
3import (
4 "encoding/json"
5 "fmt"
6 "os"
7
8 "github.com/charmbracelet/crush/internal/config"
9 "github.com/invopop/jsonschema"
10)
11
12func main() {
13 // Create a new reflector
14 r := &jsonschema.Reflector{
15 // Use anonymous schemas to avoid ID conflicts
16 Anonymous: true,
17 // Expand the root struct instead of referencing it
18 ExpandedStruct: true,
19 AllowAdditionalProperties: true,
20 }
21
22 // Generate schema for the main Config struct
23 schema := r.Reflect(&config.Config{})
24
25 // Enhance the schema with additional information
26 enhanceSchema(schema)
27
28 // Set the schema metadata
29 schema.Version = "https://json-schema.org/draft/2020-12/schema"
30 schema.Title = "Crush Configuration"
31 schema.Description = "Configuration schema for the Crush application"
32
33 // Pretty print the schema
34 encoder := json.NewEncoder(os.Stdout)
35 encoder.SetIndent("", " ")
36 if err := encoder.Encode(schema); err != nil {
37 fmt.Fprintf(os.Stderr, "Error encoding schema: %v\n", err)
38 os.Exit(1)
39 }
40}
41
42// enhanceSchema adds additional enhancements to the generated schema
43func enhanceSchema(schema *jsonschema.Schema) {
44 // Add provider enums
45 addProviderEnums(schema)
46
47 // Add model enums
48 addModelEnums(schema)
49
50 // Add tool enums
51 addToolEnums(schema)
52
53 // Add default context paths
54 addDefaultContextPaths(schema)
55}
56
57// addProviderEnums adds provider enums to the schema
58func addProviderEnums(schema *jsonschema.Schema) {
59 providers := config.Providers()
60 var providerIDs []any
61 for _, p := range providers {
62 providerIDs = append(providerIDs, string(p.ID))
63 }
64
65 // Add to PreferredModel provider field
66 if schema.Definitions != nil {
67 if preferredModelDef, exists := schema.Definitions["PreferredModel"]; exists {
68 if providerProp, exists := preferredModelDef.Properties.Get("provider"); exists {
69 providerProp.Enum = providerIDs
70 }
71 }
72
73 // Add to ProviderConfig ID field
74 if providerConfigDef, exists := schema.Definitions["ProviderConfig"]; exists {
75 if idProp, exists := providerConfigDef.Properties.Get("id"); exists {
76 idProp.Enum = providerIDs
77 }
78 }
79 }
80}
81
82// addModelEnums adds model enums to the schema
83func addModelEnums(schema *jsonschema.Schema) {
84 providers := config.Providers()
85 var modelIDs []any
86 for _, p := range providers {
87 for _, m := range p.Models {
88 modelIDs = append(modelIDs, m.ID)
89 }
90 }
91
92 // Add to PreferredModel model_id field
93 if schema.Definitions != nil {
94 if preferredModelDef, exists := schema.Definitions["PreferredModel"]; exists {
95 if modelIDProp, exists := preferredModelDef.Properties.Get("model_id"); exists {
96 modelIDProp.Enum = modelIDs
97 }
98 }
99 }
100}
101
102// addToolEnums adds tool enums to the schema
103func addToolEnums(schema *jsonschema.Schema) {
104 tools := []any{
105 "bash", "edit", "fetch", "glob", "grep", "ls", "sourcegraph", "view", "write", "agent",
106 }
107
108 if schema.Definitions != nil {
109 if agentDef, exists := schema.Definitions["Agent"]; exists {
110 if allowedToolsProp, exists := agentDef.Properties.Get("allowed_tools"); exists {
111 if allowedToolsProp.Items != nil {
112 allowedToolsProp.Items.Enum = tools
113 }
114 }
115 }
116 }
117}
118
119// addDefaultContextPaths adds default context paths to the schema
120func addDefaultContextPaths(schema *jsonschema.Schema) {
121 defaultContextPaths := []any{
122 ".github/copilot-instructions.md",
123 ".cursorrules",
124 ".cursor/rules/",
125 "CLAUDE.md",
126 "CLAUDE.local.md",
127 "GEMINI.md",
128 "gemini.md",
129 "crush.md",
130 "crush.local.md",
131 "Crush.md",
132 "Crush.local.md",
133 "CRUSH.md",
134 "CRUSH.local.md",
135 }
136
137 if schema.Definitions != nil {
138 if optionsDef, exists := schema.Definitions["Options"]; exists {
139 if contextPathsProp, exists := optionsDef.Properties.Get("context_paths"); exists {
140 contextPathsProp.Default = defaultContextPaths
141 }
142 }
143 }
144
145 // Also add to root properties if they exist
146 if schema.Properties != nil {
147 if optionsProp, exists := schema.Properties.Get("options"); exists {
148 if optionsProp.Properties != nil {
149 if contextPathsProp, exists := optionsProp.Properties.Get("context_paths"); exists {
150 contextPathsProp.Default = defaultContextPaths
151 }
152 }
153 }
154 }
155}