Skip to content
This repository was archived by the owner on Sep 18, 2025. It is now read-only.

Commit a8d5787

Browse files
committed
config validation
1 parent 9ae6af8 commit a8d5787

File tree

11 files changed

+911
-17
lines changed

11 files changed

+911
-17
lines changed

.opencode.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"$schema": "./opencode-schema.json",
23
"lsp": {
34
"gopls": {
45
"command": "gopls"

cmd/schema/README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# OpenCode Configuration Schema Generator
2+
3+
This tool generates a JSON Schema for the OpenCode configuration file. The schema can be used to validate configuration files and provide autocompletion in editors that support JSON Schema.
4+
5+
## Usage
6+
7+
```bash
8+
go run cmd/schema/main.go > opencode-schema.json
9+
```
10+
11+
This will generate a JSON Schema file that can be used to validate configuration files.
12+
13+
## Schema Features
14+
15+
The generated schema includes:
16+
17+
- All configuration options with descriptions
18+
- Default values where applicable
19+
- Validation for enum values (e.g., model IDs, provider types)
20+
- Required fields
21+
- Type checking
22+
23+
## Using the Schema
24+
25+
You can use the generated schema in several ways:
26+
27+
1. **Editor Integration**: Many editors (VS Code, JetBrains IDEs, etc.) support JSON Schema for validation and autocompletion. You can configure your editor to use the generated schema for `.opencode.json` files.
28+
29+
2. **Validation Tools**: You can use tools like [jsonschema](https://github.com/Julian/jsonschema) to validate your configuration files against the schema.
30+
31+
3. **Documentation**: The schema serves as documentation for the configuration options.
32+
33+
## Example Configuration
34+
35+
Here's an example configuration that conforms to the schema:
36+
37+
```json
38+
{
39+
"data": {
40+
"directory": ".opencode"
41+
},
42+
"debug": false,
43+
"providers": {
44+
"anthropic": {
45+
"apiKey": "your-api-key"
46+
}
47+
},
48+
"agents": {
49+
"coder": {
50+
"model": "claude-3.7-sonnet",
51+
"maxTokens": 5000,
52+
"reasoningEffort": "medium"
53+
},
54+
"task": {
55+
"model": "claude-3.7-sonnet",
56+
"maxTokens": 5000
57+
},
58+
"title": {
59+
"model": "claude-3.7-sonnet",
60+
"maxTokens": 80
61+
}
62+
}
63+
}
64+
```

cmd/schema/main.go

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
8+
"github.com/kujtimiihoxha/opencode/internal/config"
9+
"github.com/kujtimiihoxha/opencode/internal/llm/models"
10+
)
11+
12+
// JSONSchemaType represents a JSON Schema type
13+
type 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+
26+
func 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+
38+
func generateSchema() map[string]any {
39+
schema := map[string]any{
40+
"$schema": "http://json-schema.org/draft-07/schema#",
41+
"title": "OpenCode Configuration",
42+
"description": "Configuration schema for the OpenCode 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": ".opencode",
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+
// Add MCP servers
81+
schema["properties"].(map[string]any)["mcpServers"] = map[string]any{
82+
"type": "object",
83+
"description": "Model Control Protocol server configurations",
84+
"additionalProperties": map[string]any{
85+
"type": "object",
86+
"description": "MCP server configuration",
87+
"properties": map[string]any{
88+
"command": map[string]any{
89+
"type": "string",
90+
"description": "Command to execute for the MCP server",
91+
},
92+
"env": map[string]any{
93+
"type": "array",
94+
"description": "Environment variables for the MCP server",
95+
"items": map[string]any{
96+
"type": "string",
97+
},
98+
},
99+
"args": map[string]any{
100+
"type": "array",
101+
"description": "Command arguments for the MCP server",
102+
"items": map[string]any{
103+
"type": "string",
104+
},
105+
},
106+
"type": map[string]any{
107+
"type": "string",
108+
"description": "Type of MCP server",
109+
"enum": []string{"stdio", "sse"},
110+
"default": "stdio",
111+
},
112+
"url": map[string]any{
113+
"type": "string",
114+
"description": "URL for SSE type MCP servers",
115+
},
116+
"headers": map[string]any{
117+
"type": "object",
118+
"description": "HTTP headers for SSE type MCP servers",
119+
"additionalProperties": map[string]any{
120+
"type": "string",
121+
},
122+
},
123+
},
124+
"required": []string{"command"},
125+
},
126+
}
127+
128+
// Add providers
129+
providerSchema := map[string]any{
130+
"type": "object",
131+
"description": "LLM provider configurations",
132+
"additionalProperties": map[string]any{
133+
"type": "object",
134+
"description": "Provider configuration",
135+
"properties": map[string]any{
136+
"apiKey": map[string]any{
137+
"type": "string",
138+
"description": "API key for the provider",
139+
},
140+
"disabled": map[string]any{
141+
"type": "boolean",
142+
"description": "Whether the provider is disabled",
143+
"default": false,
144+
},
145+
},
146+
},
147+
}
148+
149+
// Add known providers
150+
knownProviders := []string{
151+
string(models.ProviderAnthropic),
152+
string(models.ProviderOpenAI),
153+
string(models.ProviderGemini),
154+
string(models.ProviderGROQ),
155+
string(models.ProviderBedrock),
156+
}
157+
158+
providerSchema["additionalProperties"].(map[string]any)["properties"].(map[string]any)["provider"] = map[string]any{
159+
"type": "string",
160+
"description": "Provider type",
161+
"enum": knownProviders,
162+
}
163+
164+
schema["properties"].(map[string]any)["providers"] = providerSchema
165+
166+
// Add agents
167+
agentSchema := map[string]any{
168+
"type": "object",
169+
"description": "Agent configurations",
170+
"additionalProperties": map[string]any{
171+
"type": "object",
172+
"description": "Agent configuration",
173+
"properties": map[string]any{
174+
"model": map[string]any{
175+
"type": "string",
176+
"description": "Model ID for the agent",
177+
},
178+
"maxTokens": map[string]any{
179+
"type": "integer",
180+
"description": "Maximum tokens for the agent",
181+
"minimum": 1,
182+
},
183+
"reasoningEffort": map[string]any{
184+
"type": "string",
185+
"description": "Reasoning effort for models that support it (OpenAI, Anthropic)",
186+
"enum": []string{"low", "medium", "high"},
187+
},
188+
},
189+
"required": []string{"model"},
190+
},
191+
}
192+
193+
// Add model enum
194+
modelEnum := []string{}
195+
for modelID := range models.SupportedModels {
196+
modelEnum = append(modelEnum, string(modelID))
197+
}
198+
agentSchema["additionalProperties"].(map[string]any)["properties"].(map[string]any)["model"].(map[string]any)["enum"] = modelEnum
199+
200+
// Add specific agent properties
201+
agentProperties := map[string]any{}
202+
knownAgents := []string{
203+
string(config.AgentCoder),
204+
string(config.AgentTask),
205+
string(config.AgentTitle),
206+
}
207+
208+
for _, agentName := range knownAgents {
209+
agentProperties[agentName] = map[string]any{
210+
"$ref": "#/definitions/agent",
211+
}
212+
}
213+
214+
// Create a combined schema that allows both specific agents and additional ones
215+
combinedAgentSchema := map[string]any{
216+
"type": "object",
217+
"description": "Agent configurations",
218+
"properties": agentProperties,
219+
"additionalProperties": agentSchema["additionalProperties"],
220+
}
221+
222+
schema["properties"].(map[string]any)["agents"] = combinedAgentSchema
223+
schema["definitions"] = map[string]any{
224+
"agent": agentSchema["additionalProperties"],
225+
}
226+
227+
// Add LSP configuration
228+
schema["properties"].(map[string]any)["lsp"] = map[string]any{
229+
"type": "object",
230+
"description": "Language Server Protocol configurations",
231+
"additionalProperties": map[string]any{
232+
"type": "object",
233+
"description": "LSP configuration for a language",
234+
"properties": map[string]any{
235+
"disabled": map[string]any{
236+
"type": "boolean",
237+
"description": "Whether the LSP is disabled",
238+
"default": false,
239+
},
240+
"command": map[string]any{
241+
"type": "string",
242+
"description": "Command to execute for the LSP server",
243+
},
244+
"args": map[string]any{
245+
"type": "array",
246+
"description": "Command arguments for the LSP server",
247+
"items": map[string]any{
248+
"type": "string",
249+
},
250+
},
251+
"options": map[string]any{
252+
"type": "object",
253+
"description": "Additional options for the LSP server",
254+
},
255+
},
256+
"required": []string{"command"},
257+
},
258+
}
259+
260+
return schema
261+
}
262+

0 commit comments

Comments
 (0)