on main help sign in
fix(cli): show all providers in crush models, not just configured ones
6292bfaChristian Rocha
internal/cmd/models.go+74−29
@@ -7,7 +7,6 @@ import (
7 "sort"
7 "sort"
8 "strings"
8 "strings"
9
9
10− "charm.land/catwalk/pkg/catwalk"
11 "charm.land/lipgloss/v2/tree"
10 "charm.land/lipgloss/v2/tree"
12 "github.com/charmbracelet/crush/internal/config"
11 "github.com/charmbracelet/crush/internal/config"
13 "github.com/mattn/go-isatty"
12 "github.com/mattn/go-isatty"
@@ -16,8 +15,8 @@ import (
16
15
17var modelsCmd = &cobra.Command{
16var modelsCmd = &cobra.Command{
18 Use: "models",
17 Use: "models",
19− Short: "List all available models from configured providers",
18+ Short: "List all available models from known providers",
20− Long: `List all available models from configured providers. Shows provider name and model IDs.`,
19+ Long: `List all available models from known providers. Shows provider name and model IDs. Unconfigured providers are marked with (not configured).`,
21 Example: `# List all available models
20 Example: `# List all available models
22crush models
21crush models
23
22
@@ -38,53 +37,94 @@ crush models gpt5`,
38 return err
37 return err
39 }
38 }
40
39
41− if !cfg.Config().IsConfigured() {
42− return fmt.Errorf("no providers configured - please run 'crush' to set up a provider interactively")
43− }
44−
45 term := strings.ToLower(strings.Join(args, " "))
40 term := strings.ToLower(strings.Join(args, " "))
46− filter := func(p config.ProviderConfig, m catwalk.Model) bool {
41+
47− for _, s := range []string{p.ID, p.Name, m.ID, m.Name} {
42+ type providerEntry struct {
48− if term == "" || strings.Contains(strings.ToLower(s), term) {
43+ name string
49− return true
44+ models []string
50− }
45+ configured bool
51− }
52− return false
53 }
46 }
54
47
55− var providerIDs []string
48+ entries := make(map[string]*providerEntry)
56− providerModels := make(map[string][]string)
57
49
50+ // Add configured providers first.
58 for providerID, provider := range cfg.Config().Providers.Seq2() {
51 for providerID, provider := range cfg.Config().Providers.Seq2() {
59 if provider.Disable {
52 if provider.Disable {
60 continue
53 continue
61 }
54 }
62− var found bool
55+ entry := &providerEntry{
56+ name: provider.Name,
57+ configured: true,
58+ }
63 for _, model := range provider.Models {
59 for _, model := range provider.Models {
64− if !filter(provider, model) {
60+ if term != "" {
61+ matched := false
62+ for _, s := range []string{provider.ID, provider.Name, model.ID, model.Name} {
63+ if strings.Contains(strings.ToLower(s), term) {
64+ matched = true
65+ break
66+ }
67+ }
68+ if !matched {
65− continue
69+ continue
70+ }
66 }
71 }
67− providerModels[providerID] = append(providerModels[providerID], model.ID)
72+ entry.models = append(entry.models, model.ID)
68− found = true
69 }
73 }
70− if !found {
74+ if len(entry.models) > 0 {
75+ slices.Sort(entry.models)
76+ entries[providerID] = entry
77+ }
78+ }
79+
80+ // Add known but unconfigured providers from catwalk.
81+ for _, kp := range cfg.KnownProviders() {
82+ providerID := string(kp.ID)
83+ if _, exists := entries[providerID]; exists {
71 continue
84 continue
72 }
85 }
73− slices.Sort(providerModels[providerID])
86+ entry := &providerEntry{
87+ name: kp.Name,
88+ configured: false,
89+ }
90+ for _, model := range kp.Models {
91+ if term != "" {
92+ matched := false
93+ for _, s := range []string{providerID, kp.Name, model.ID, model.Name} {
94+ if strings.Contains(strings.ToLower(s), term) {
95+ matched = true
96+ break
97+ }
98+ }
99+ if !matched {
100+ continue
101+ }
102+ }
103+ entry.models = append(entry.models, model.ID)
104+ }
105+ if len(entry.models) > 0 {
106+ slices.Sort(entry.models)
107+ entries[providerID] = entry
108+ }
109+ }
110+
111+ var providerIDs []string
112+ for id := range entries {
74− providerIDs = append(providerIDs, providerID)
113+ providerIDs = append(providerIDs, id)
75 }
114 }
76 sort.Strings(providerIDs)
115 sort.Strings(providerIDs)
77
116
78 if len(providerIDs) == 0 && len(args) == 0 {
117 if len(providerIDs) == 0 && len(args) == 0 {
79− return fmt.Errorf("no enabled providers found")
118+ return fmt.Errorf("no providers found")
80 }
119 }
81 if len(providerIDs) == 0 {
120 if len(providerIDs) == 0 {
82− return fmt.Errorf("no enabled providers found matching %q", term)
121+ return fmt.Errorf("no providers found matching %q", term)
83 }
122 }
84
123
85 if !isatty.IsTerminal(os.Stdout.Fd()) {
124 if !isatty.IsTerminal(os.Stdout.Fd()) {
86 for _, providerID := range providerIDs {
125 for _, providerID := range providerIDs {
126+ entry := entries[providerID]
87− for _, modelID := range providerModels[providerID] {
127+ for _, modelID := range entry.models {
88 fmt.Println(providerID + "/" + modelID)
128 fmt.Println(providerID + "/" + modelID)
89 }
129 }
90 }
130 }
@@ -93,8 +133,13 @@ crush models gpt5`,
93
133
94 t := tree.New()
134 t := tree.New()
95 for _, providerID := range providerIDs {
135 for _, providerID := range providerIDs {
136+ entry := entries[providerID]
137+ label := providerID
138+ if !entry.configured {
139+ label += " (not configured)"
140+ }
96− providerNode := tree.Root(providerID)
141+ providerNode := tree.Root(label)
97− for _, modelID := range providerModels[providerID] {
142+ for _, modelID := range entry.models {
98 providerNode.Child(modelID)
143 providerNode.Child(modelID)
99 }
144 }
100 t.Child(providerNode)
145 t.Child(providerNode)