config.go

  1package config
  2
  3import (
  4	"slices"
  5	"strings"
  6
  7	"github.com/charmbracelet/crush/internal/fur/provider"
  8)
  9
 10const (
 11	appName              = "crush"
 12	defaultDataDirectory = ".crush"
 13	defaultLogLevel      = "info"
 14)
 15
 16var defaultContextPaths = []string{
 17	".github/copilot-instructions.md",
 18	".cursorrules",
 19	".cursor/rules/",
 20	"CLAUDE.md",
 21	"CLAUDE.local.md",
 22	"GEMINI.md",
 23	"gemini.md",
 24	"crush.md",
 25	"crush.local.md",
 26	"Crush.md",
 27	"Crush.local.md",
 28	"CRUSH.md",
 29	"CRUSH.local.md",
 30}
 31
 32type SelectedModel struct {
 33	// The model id as used by the provider API.
 34	// Required.
 35	Model string `json:"model"`
 36	// The model provider, same as the key/id used in the providers config.
 37	// Required.
 38	Provider string `json:"provider"`
 39
 40	// Only used by models that use the openai provider and need this set.
 41	ReasoningEffort string `json:"reasoning_effort,omitempty"`
 42
 43	// Overrides the default model configuration.
 44	MaxTokens int64 `json:"max_tokens,omitempty"`
 45
 46	// Used by anthropic models that can reason to indicate if the model should think.
 47	Think bool `json:"think,omitempty"`
 48}
 49
 50type ProviderConfig struct {
 51	// The provider's API endpoint.
 52	BaseURL string `json:"base_url,omitempty"`
 53	// The provider type, e.g. "openai", "anthropic", etc. if empty it defaults to openai.
 54	Type provider.Type `json:"type,omitempty"`
 55	// The provider's API key.
 56	APIKey string `json:"api_key,omitempty"`
 57	// Marks the provider as disabled.
 58	Disable bool `json:"disable,omitempty"`
 59
 60	// Extra headers to send with each request to the provider.
 61	ExtraHeaders map[string]string
 62
 63	// Used to pass extra parameters to the provider.
 64	ExtraParams map[string]string `json:"-"`
 65
 66	// The provider models
 67	Models []provider.Model `json:"models,omitempty"`
 68}
 69
 70type MCPType string
 71
 72const (
 73	MCPStdio MCPType = "stdio"
 74	MCPSse   MCPType = "sse"
 75	MCPHttp  MCPType = "http"
 76)
 77
 78type MCPConfig struct {
 79	Command string   `json:"command,omitempty" `
 80	Env     []string `json:"env,omitempty"`
 81	Args    []string `json:"args,omitempty"`
 82	Type    MCPType  `json:"type"`
 83	URL     string   `json:"url,omitempty"`
 84
 85	// TODO: maybe make it possible to get the value from the env
 86	Headers map[string]string `json:"headers,omitempty"`
 87}
 88
 89type LSPConfig struct {
 90	Disabled bool     `json:"enabled,omitempty"`
 91	Command  string   `json:"command"`
 92	Args     []string `json:"args,omitempty"`
 93	Options  any      `json:"options,omitempty"`
 94}
 95
 96type TUIOptions struct {
 97	CompactMode bool `json:"compact_mode,omitempty"`
 98	// Here we can add themes later or any TUI related options
 99}
100
101type Options struct {
102	ContextPaths         []string    `json:"context_paths,omitempty"`
103	TUI                  *TUIOptions `json:"tui,omitempty"`
104	Debug                bool        `json:"debug,omitempty"`
105	DebugLSP             bool        `json:"debug_lsp,omitempty"`
106	DisableAutoSummarize bool        `json:"disable_auto_summarize,omitempty"`
107	// Relative to the cwd
108	DataDirectory string `json:"data_directory,omitempty"`
109}
110
111type MCPs map[string]MCPConfig
112
113type MCP struct {
114	Name string    `json:"name"`
115	MCP  MCPConfig `json:"mcp"`
116}
117
118func (m MCPs) Sorted() []MCP {
119	sorted := make([]MCP, 0, len(m))
120	for k, v := range m {
121		sorted = append(sorted, MCP{
122			Name: k,
123			MCP:  v,
124		})
125	}
126	slices.SortFunc(sorted, func(a, b MCP) int {
127		return strings.Compare(a.Name, b.Name)
128	})
129	return sorted
130}
131
132type LSPs map[string]LSPConfig
133
134type LSP struct {
135	Name string    `json:"name"`
136	LSP  LSPConfig `json:"lsp"`
137}
138
139func (l LSPs) Sorted() []LSP {
140	sorted := make([]LSP, 0, len(l))
141	for k, v := range l {
142		sorted = append(sorted, LSP{
143			Name: k,
144			LSP:  v,
145		})
146	}
147	slices.SortFunc(sorted, func(a, b LSP) int {
148		return strings.Compare(a.Name, b.Name)
149	})
150	return sorted
151}
152
153// Config holds the configuration for crush.
154type Config struct {
155	// We currently only support large/small as values here.
156	Models map[string]SelectedModel `json:"models,omitempty"`
157
158	// The providers that are configured
159	Providers map[string]ProviderConfig `json:"providers,omitempty"`
160
161	MCP MCPs `json:"mcp,omitempty"`
162
163	LSP LSPs `json:"lsp,omitempty"`
164
165	Options *Options `json:"options,omitempty"`
166
167	// Internal
168	workingDir string `json:"-"`
169}
170
171func (c *Config) WorkingDir() string {
172	return c.workingDir
173}