main.go

  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}