1package main
2
3import (
4 "encoding/json"
5 "fmt"
6 "os"
7
8 "github.com/charmbracelet/crush/internal/config"
9 "github.com/charmbracelet/crush/internal/llm/models"
10)
11
12// JSONSchemaType represents a JSON Schema type
13type JSONSchemaType struct {
14 Type string `json:"type,omitempty"`
15 Description string `json:"description,omitempty"`
16 Properties map[string]any `json:"properties,omitempty"`
17 Required []string `json:"required,omitempty"`
18 AdditionalProperties any `json:"additionalProperties,omitempty"`
19 Enum []any `json:"enum,omitempty"`
20 Items map[string]any `json:"items,omitempty"`
21 OneOf []map[string]any `json:"oneOf,omitempty"`
22 AnyOf []map[string]any `json:"anyOf,omitempty"`
23 Default any `json:"default,omitempty"`
24}
25
26func main() {
27 schema := generateSchema()
28
29 // Pretty print the schema
30 encoder := json.NewEncoder(os.Stdout)
31 encoder.SetIndent("", " ")
32 if err := encoder.Encode(schema); err != nil {
33 fmt.Fprintf(os.Stderr, "Error encoding schema: %v\n", err)
34 os.Exit(1)
35 }
36}
37
38func generateSchema() map[string]any {
39 schema := map[string]any{
40 "$schema": "http://json-schema.org/draft-07/schema#",
41 "title": "Crush Configuration",
42 "description": "Configuration schema for the Crush application",
43 "type": "object",
44 "properties": map[string]any{},
45 }
46
47 // Add Data configuration
48 schema["properties"].(map[string]any)["data"] = map[string]any{
49 "type": "object",
50 "description": "Storage configuration",
51 "properties": map[string]any{
52 "directory": map[string]any{
53 "type": "string",
54 "description": "Directory where application data is stored",
55 "default": ".crush",
56 },
57 },
58 "required": []string{"directory"},
59 }
60
61 // Add working directory
62 schema["properties"].(map[string]any)["wd"] = map[string]any{
63 "type": "string",
64 "description": "Working directory for the application",
65 }
66
67 // Add debug flags
68 schema["properties"].(map[string]any)["debug"] = map[string]any{
69 "type": "boolean",
70 "description": "Enable debug mode",
71 "default": false,
72 }
73
74 schema["properties"].(map[string]any)["debugLSP"] = map[string]any{
75 "type": "boolean",
76 "description": "Enable LSP debug mode",
77 "default": false,
78 }
79
80 schema["properties"].(map[string]any)["contextPaths"] = map[string]any{
81 "type": "array",
82 "description": "Context paths for the application",
83 "items": map[string]any{
84 "type": "string",
85 },
86 "default": []string{
87 ".github/copilot-instructions.md",
88 ".cursorrules",
89 ".cursor/rules/",
90 "CLAUDE.md",
91 "CLAUDE.local.md",
92 "GEMINI.md",
93 "gemini.md",
94 "crush.md",
95 "crush.local.md",
96 "Crush.md",
97 "Crush.local.md",
98 "CRUSH.md",
99 "CRUSH.local.md",
100 },
101 }
102
103 schema["properties"].(map[string]any)["tui"] = map[string]any{
104 "type": "object",
105 "description": "Terminal User Interface configuration",
106 "properties": map[string]any{
107 "theme": map[string]any{
108 "type": "string",
109 "description": "TUI theme name",
110 "default": "crush",
111 "enum": []string{
112 "crush",
113 "catppuccin",
114 "dracula",
115 "flexoki",
116 "gruvbox",
117 "monokai",
118 "onedark",
119 "tokyonight",
120 "tron",
121 },
122 },
123 },
124 }
125
126 // Add MCP servers
127 schema["properties"].(map[string]any)["mcpServers"] = map[string]any{
128 "type": "object",
129 "description": "Model Control Protocol server configurations",
130 "additionalProperties": map[string]any{
131 "type": "object",
132 "description": "MCP server configuration",
133 "properties": map[string]any{
134 "command": map[string]any{
135 "type": "string",
136 "description": "Command to execute for the MCP server",
137 },
138 "env": map[string]any{
139 "type": "array",
140 "description": "Environment variables for the MCP server",
141 "items": map[string]any{
142 "type": "string",
143 },
144 },
145 "args": map[string]any{
146 "type": "array",
147 "description": "Command arguments for the MCP server",
148 "items": map[string]any{
149 "type": "string",
150 },
151 },
152 "type": map[string]any{
153 "type": "string",
154 "description": "Type of MCP server",
155 "enum": []string{"stdio", "sse"},
156 "default": "stdio",
157 },
158 "url": map[string]any{
159 "type": "string",
160 "description": "URL for SSE type MCP servers",
161 },
162 "headers": map[string]any{
163 "type": "object",
164 "description": "HTTP headers for SSE type MCP servers",
165 "additionalProperties": map[string]any{
166 "type": "string",
167 },
168 },
169 },
170 "required": []string{"command"},
171 },
172 }
173
174 // Add providers
175 providerSchema := map[string]any{
176 "type": "object",
177 "description": "LLM provider configurations",
178 "additionalProperties": map[string]any{
179 "type": "object",
180 "description": "Provider configuration",
181 "properties": map[string]any{
182 "apiKey": map[string]any{
183 "type": "string",
184 "description": "API key for the provider",
185 },
186 "disabled": map[string]any{
187 "type": "boolean",
188 "description": "Whether the provider is disabled",
189 "default": false,
190 },
191 },
192 },
193 }
194
195 // Add known providers
196 knownProviders := []string{
197 string(models.ProviderAnthropic),
198 string(models.ProviderOpenAI),
199 string(models.ProviderGemini),
200 string(models.ProviderGROQ),
201 string(models.ProviderOpenRouter),
202 string(models.ProviderBedrock),
203 string(models.ProviderAzure),
204 string(models.ProviderVertexAI),
205 }
206
207 providerSchema["additionalProperties"].(map[string]any)["properties"].(map[string]any)["provider"] = map[string]any{
208 "type": "string",
209 "description": "Provider type",
210 "enum": knownProviders,
211 }
212
213 schema["properties"].(map[string]any)["providers"] = providerSchema
214
215 // Add agents
216 agentSchema := map[string]any{
217 "type": "object",
218 "description": "Agent configurations",
219 "additionalProperties": map[string]any{
220 "type": "object",
221 "description": "Agent configuration",
222 "properties": map[string]any{
223 "model": map[string]any{
224 "type": "string",
225 "description": "Model ID for the agent",
226 },
227 "maxTokens": map[string]any{
228 "type": "integer",
229 "description": "Maximum tokens for the agent",
230 "minimum": 1,
231 },
232 "reasoningEffort": map[string]any{
233 "type": "string",
234 "description": "Reasoning effort for models that support it (OpenAI, Anthropic)",
235 "enum": []string{"low", "medium", "high"},
236 },
237 },
238 "required": []string{"model"},
239 },
240 }
241
242 // Add model enum
243 modelEnum := []string{}
244 for modelID := range models.SupportedModels {
245 modelEnum = append(modelEnum, string(modelID))
246 }
247 agentSchema["additionalProperties"].(map[string]any)["properties"].(map[string]any)["model"].(map[string]any)["enum"] = modelEnum
248
249 // Add specific agent properties
250 agentProperties := map[string]any{}
251 knownAgents := []string{
252 string(config.AgentCoder),
253 string(config.AgentTask),
254 string(config.AgentTitle),
255 }
256
257 for _, agentName := range knownAgents {
258 agentProperties[agentName] = map[string]any{
259 "$ref": "#/definitions/agent",
260 }
261 }
262
263 // Create a combined schema that allows both specific agents and additional ones
264 combinedAgentSchema := map[string]any{
265 "type": "object",
266 "description": "Agent configurations",
267 "properties": agentProperties,
268 "additionalProperties": agentSchema["additionalProperties"],
269 }
270
271 schema["properties"].(map[string]any)["agents"] = combinedAgentSchema
272 schema["definitions"] = map[string]any{
273 "agent": agentSchema["additionalProperties"],
274 }
275
276 // Add LSP configuration
277 schema["properties"].(map[string]any)["lsp"] = map[string]any{
278 "type": "object",
279 "description": "Language Server Protocol configurations",
280 "additionalProperties": map[string]any{
281 "type": "object",
282 "description": "LSP configuration for a language",
283 "properties": map[string]any{
284 "disabled": map[string]any{
285 "type": "boolean",
286 "description": "Whether the LSP is disabled",
287 "default": false,
288 },
289 "command": map[string]any{
290 "type": "string",
291 "description": "Command to execute for the LSP server",
292 },
293 "args": map[string]any{
294 "type": "array",
295 "description": "Command arguments for the LSP server",
296 "items": map[string]any{
297 "type": "string",
298 },
299 },
300 "options": map[string]any{
301 "type": "object",
302 "description": "Additional options for the LSP server",
303 },
304 },
305 "required": []string{"command"},
306 },
307 }
308
309 return schema
310}