@@ -200,6 +200,28 @@ var (
200200
201201)
202202
203+func readConfigFile(path string) (*Config, error) {
204+ var cfg *Config
205+ if _, err := os.Stat(path); err != nil && !os.IsNotExist(err) {
206+ // some other error occurred while checking the file
207+ return nil, err
208+ } else if err == nil {
209+ // config file exists, read it
210+ file, err := os.ReadFile(path)
211+ if err != nil {
212+ return nil, err
213+ }
214+ cfg = &Config{}
215+ if err := json.Unmarshal(file, cfg); err != nil {
216+ return nil, err
217+ }
218+ } else {
219+ // config file does not exist, create a new one
220+ cfg = &Config{}
221+ }
222+ return cfg, nil
223+}
224+
203225func loadConfig(cwd string, debug bool) (*Config, error) {
204226 // First read the global config file
205227 cfgPath := ConfigPath()
@@ -248,47 +270,29 @@ func loadConfig(cwd string, debug bool) (*Config, error) {
248270 }))
249271 slog.SetDefault(logger)
250272 }
251− var globalCfg *Config
252− if _, err := os.Stat(cfgPath); err != nil && !os.IsNotExist(err) {
253− // some other error occurred while checking the file
254− return nil, err
255− } else if err == nil {
256− // config file exists, read it
257− file, err := os.ReadFile(cfgPath)
258− if err != nil {
259− return nil, err
260− }
261− globalCfg = &Config{}
262− if err := json.Unmarshal(file, globalCfg); err != nil {
263− return nil, err
264− }
265− } else {
266− // config file does not exist, create a new one
267− globalCfg = &Config{}
273+
274+ priorityOrderedConfigFiles := []string{
275+ cfgPath, // Global config file
276+ filepath.Join(cwd, "crush.json"), // Local config file
277+ filepath.Join(cwd, ".crush.json"), // Local config file
268278 }
269279
270− var localConfig *Config
271− // Global config loaded, now read the local config file
272− localConfigPath := filepath.Join(cwd, "crush.json")
273− if _, err := os.Stat(localConfigPath); err != nil && !os.IsNotExist(err) {
274− // some other error occurred while checking the file
275− return nil, err
276− } else if err == nil {
277− // local config file exists, read it
278− file, err := os.ReadFile(localConfigPath)
280+ configs := make([]*Config, 0)
281+ for _, path := range priorityOrderedConfigFiles {
282+ localConfig, err := readConfigFile(path)
279283 if err != nil {
280− return nil, err
284+ return nil, fmt.Errorf("failed to read config file %s: %w", path, err)
281285 }
282− localConfig = &Config{}
283− if err := json.Unmarshal(file, localConfig); err != nil {
284− return nil, err
286+ if localConfig != nil {
287+ // If the config file was read successfully, add it to the list
288+ configs = append(configs, localConfig)
285289 }
286290 }
287291
288292 // merge options
289− mergeOptions(cfg, globalCfg, localConfig)
293+ mergeOptions(cfg, configs...)
290294
291− mergeProviderConfigs(cfg, globalCfg, localConfig)
295+ mergeProviderConfigs(cfg, configs...)
292296 // no providers found the app is not initialized yet
293297 if len(cfg.Providers) == 0 {
294298 return cfg, nil
@@ -310,7 +314,7 @@ func loadConfig(cwd string, debug bool) (*Config, error) {
310314 cfg.Models = PreferredModels{}
311315 }
312316
313− mergeModels(cfg, globalCfg, localConfig)
317+ mergeModels(cfg, configs...)
314318
315319 agents := map[AgentID]Agent{
316320 AgentCoder: {
@@ -340,9 +344,9 @@ func loadConfig(cwd string, debug bool) (*Config, error) {
340344 },
341345 }
342346 cfg.Agents = agents
343− mergeAgents(cfg, globalCfg, localConfig)
344− mergeMCPs(cfg, globalCfg, localConfig)
345− mergeLSPs(cfg, globalCfg, localConfig)
347+ mergeAgents(cfg, configs...)
348+ mergeMCPs(cfg, configs...)
349+ mergeLSPs(cfg, configs...)
346350
347351 // Validate the final configuration
348352 if err := cfg.Validate(); err != nil {
@@ -457,8 +461,8 @@ func validateProvider(p provider.InferenceProvider, providerConfig ProviderConfi
457461 return nil
458462}
459463
460−func mergeModels(base, global, local *Config) {
461− for _, cfg := range []*Config{global, local} {
464+func mergeModels(base *Config, others ...*Config) {
465+ for _, cfg := range others {
462466 if cfg == nil {
463467 continue
464468 }
@@ -472,8 +476,8 @@ func mergeModels(base, global, local *Config) {
472476 }
473477}
474478
475−func mergeOptions(base, global, local *Config) {
476− for _, cfg := range []*Config{global, local} {
479+func mergeOptions(base *Config, others ...*Config) {
480+ for _, cfg := range others {
477481 if cfg == nil {
478482 continue
479483 }
@@ -506,8 +510,8 @@ func mergeOptions(base, global, local *Config) {
506510 }
507511}
508512
509−func mergeAgents(base, global, local *Config) {
510− for _, cfg := range []*Config{global, local} {
513+func mergeAgents(base *Config, others ...*Config) {
514+ for _, cfg := range others {
511515 if cfg == nil {
512516 continue
513517 }
@@ -575,8 +579,8 @@ func mergeAgents(base, global, local *Config) {
575579 }
576580}
577581
578−func mergeMCPs(base, global, local *Config) {
579− for _, cfg := range []*Config{global, local} {
582+func mergeMCPs(base *Config, others ...*Config) {
583+ for _, cfg := range others {
580584 if cfg == nil {
581585 continue
582586 }
@@ -584,8 +588,8 @@ func mergeMCPs(base, global, local *Config) {
584588 }
585589}
586590
587−func mergeLSPs(base, global, local *Config) {
588− for _, cfg := range []*Config{global, local} {
591+func mergeLSPs(base *Config, others ...*Config) {
592+ for _, cfg := range others {
589593 if cfg == nil {
590594 continue
591595 }
@@ -593,15 +597,27 @@ func mergeLSPs(base, global, local *Config) {
593597 }
594598}
595599
596−func mergeProviderConfigs(base, global, local *Config) {
597− for _, cfg := range []*Config{global, local} {
600+func mergeProviderConfigs(base *Config, others ...*Config) {
601+ for _, cfg := range others {
598602 if cfg == nil {
599603 continue
600604 }
601605 for providerName, p := range cfg.Providers {
602606 p.ID = providerName
603607 if _, ok := base.Providers[providerName]; !ok {
604− base.Providers[providerName] = p
608+ if slices.Contains(provider.KnownProviders(), providerName) {
609+ providers := Providers()
610+ for _, providerDef := range providers {
611+ if providerDef.ID == providerName {
612+ logging.Info("Using default provider config for", "provider", providerName)
613+ baseProvider := getDefaultProviderConfig(providerDef, providerDef.APIKey)
614+ base.Providers[providerName] = mergeProviderConfig(providerName, baseProvider, p)
615+ break
616+ }
617+ }
618+ } else {
619+ base.Providers[providerName] = p
620+ }
605621 } else {
606622 base.Providers[providerName] = mergeProviderConfig(providerName, base.Providers[providerName], p)
607623 }
@@ -676,6 +692,40 @@ func providerDefaultConfig(providerID provider.InferenceProvider) ProviderConfig
676692 }
677693}
678694
695+func getDefaultProviderConfig(p provider.Provider, apiKey string) ProviderConfig {
696+ providerConfig := providerDefaultConfig(p.ID)
697+ providerConfig.APIKey = apiKey
698+ providerConfig.DefaultLargeModel = p.DefaultLargeModelID
699+ providerConfig.DefaultSmallModel = p.DefaultSmallModelID
700+ baseURL := p.APIEndpoint
701+ if strings.HasPrefix(baseURL, "$") {
702+ envVar := strings.TrimPrefix(baseURL, "$")
703+ baseURL = os.Getenv(envVar)
704+ }
705+ providerConfig.BaseURL = baseURL
706+ for _, model := range p.Models {
707+ configModel := Model{
708+ ID: model.ID,
709+ Name: model.Name,
710+ CostPer1MIn: model.CostPer1MIn,
711+ CostPer1MOut: model.CostPer1MOut,
712+ CostPer1MInCached: model.CostPer1MInCached,
713+ CostPer1MOutCached: model.CostPer1MOutCached,
714+ ContextWindow: model.ContextWindow,
715+ DefaultMaxTokens: model.DefaultMaxTokens,
716+ CanReason: model.CanReason,
717+ SupportsImages: model.SupportsImages,
718+ }
719+ // Set reasoning effort for reasoning models
720+ if model.HasReasoningEffort && model.DefaultReasoningEffort != "" {
721+ configModel.HasReasoningEffort = model.HasReasoningEffort
722+ configModel.ReasoningEffort = model.DefaultReasoningEffort
723+ }
724+ providerConfig.Models = append(providerConfig.Models, configModel)
725+ }
726+ return providerConfig
727+}
728+
679729func defaultConfigBasedOnEnv() *Config {
680730 cfg := &Config{
681731 Options: Options{
@@ -694,37 +744,7 @@ func defaultConfigBasedOnEnv() *Config {
694744 if strings.HasPrefix(p.APIKey, "$") {
695745 envVar := strings.TrimPrefix(p.APIKey, "$")
696746 if apiKey := os.Getenv(envVar); apiKey != "" {
697− providerConfig := providerDefaultConfig(p.ID)
698− providerConfig.APIKey = apiKey
699− providerConfig.DefaultLargeModel = p.DefaultLargeModelID
700− providerConfig.DefaultSmallModel = p.DefaultSmallModelID
701− baseURL := p.APIEndpoint
702− if strings.HasPrefix(baseURL, "$") {
703− envVar := strings.TrimPrefix(baseURL, "$")
704− baseURL = os.Getenv(envVar)
705− }
706− providerConfig.BaseURL = baseURL
707− for _, model := range p.Models {
708− configModel := Model{
709− ID: model.ID,
710− Name: model.Name,
711− CostPer1MIn: model.CostPer1MIn,
712− CostPer1MOut: model.CostPer1MOut,
713− CostPer1MInCached: model.CostPer1MInCached,
714− CostPer1MOutCached: model.CostPer1MOutCached,
715− ContextWindow: model.ContextWindow,
716− DefaultMaxTokens: model.DefaultMaxTokens,
717− CanReason: model.CanReason,
718− SupportsImages: model.SupportsImages,
719− }
720− // Set reasoning effort for reasoning models
721− if model.HasReasoningEffort && model.DefaultReasoningEffort != "" {
722− configModel.HasReasoningEffort = model.HasReasoningEffort
723− configModel.ReasoningEffort = model.DefaultReasoningEffort
724− }
725− providerConfig.Models = append(providerConfig.Models, configModel)
726− }
727− cfg.Providers[p.ID] = providerConfig
747+ cfg.Providers[p.ID] = getDefaultProviderConfig(p, apiKey)
728748 }
729749 }
730750 }