@@ -3,6 +3,7 @@ package commands
33import (
44 "context"
55 "fmt"
6+ "slices"
67 "strings"
78
89 tea "charm.land/bubbletea/v2"
@@ -37,69 +38,13 @@ type Item struct {
3738func builtInSessionCommands () []Item {
3839 cmds := []Item {
3940 {
40- ID : "session.exit" ,
41- Label : "Exit" ,
42- SlashCommand : "/exit" ,
43- Description : "Exit the application" ,
44- Category : "Session" ,
45- Execute : func (string ) tea.Cmd {
46- return core .CmdHandler (messages.ExitSessionMsg {})
47- },
48- },
49- {
50- ID : "session.new" ,
51- Label : "New" ,
52- SlashCommand : "/new" ,
53- Description : "Start a new conversation" ,
54- Category : "Session" ,
55- Execute : func (string ) tea.Cmd {
56- return core .CmdHandler (messages.NewSessionMsg {})
57- },
58- },
59- {
60- ID : "session.history" ,
61- Label : "Sessions" ,
62- SlashCommand : "/sessions" ,
63- Description : "Browse and load past sessions" ,
64- Category : "Session" ,
65- Execute : func (string ) tea.Cmd {
66- return core .CmdHandler (messages.OpenSessionBrowserMsg {})
67- },
68- },
69- {
70- ID : "session.star" ,
71- Label : "Star" ,
72- SlashCommand : "/star" ,
73- Description : "Toggle star on current session" ,
74- Category : "Session" ,
75- Execute : func (string ) tea.Cmd {
76- return core .CmdHandler (messages.ToggleSessionStarMsg {})
77- },
78- },
79- {
80- ID : "session.title" ,
81- Label : "Title" ,
82- SlashCommand : "/title" ,
83- Description : "Set or regenerate session title (usage: /title [new title])" ,
41+ ID : "session.attach" ,
42+ Label : "Attach" ,
43+ SlashCommand : "/attach" ,
44+ Description : "Attach a file to your message (usage: /attach [path])" ,
8445 Category : "Session" ,
8546 Execute : func (arg string ) tea.Cmd {
86- arg = strings .TrimSpace (arg )
87- if arg == "" {
88- // No argument: regenerate title
89- return core .CmdHandler (messages.RegenerateTitleMsg {})
90- }
91- // With argument: set title
92- return core .CmdHandler (messages.SetSessionTitleMsg {Title : arg })
93- },
94- },
95- {
96- ID : "session.model" ,
97- Label : "Model" ,
98- SlashCommand : "/model" ,
99- Description : "Change the model for the current agent" ,
100- Category : "Session" ,
101- Execute : func (string ) tea.Cmd {
102- return core .CmdHandler (messages.OpenModelPickerMsg {})
47+ return core .CmdHandler (messages.AttachFileMsg {FilePath : arg })
10348 },
10449 },
10550 {
@@ -132,6 +77,16 @@ func builtInSessionCommands() []Item {
13277 return core .CmdHandler (messages.CopyLastResponseToClipboardMsg {})
13378 },
13479 },
80+ {
81+ ID : "session.cost" ,
82+ Label : "Cost" ,
83+ SlashCommand : "/cost" ,
84+ Description : "Show detailed cost breakdown for this session" ,
85+ Category : "Session" ,
86+ Execute : func (string ) tea.Cmd {
87+ return core .CmdHandler (messages.ShowCostDialogMsg {})
88+ },
89+ },
13590 {
13691 ID : "session.eval" ,
13792 Label : "Eval" ,
@@ -142,6 +97,16 @@ func builtInSessionCommands() []Item {
14297 return core .CmdHandler (messages.EvalSessionMsg {Filename : arg })
14398 },
14499 },
100+ {
101+ ID : "session.exit" ,
102+ Label : "Exit" ,
103+ SlashCommand : "/exit" ,
104+ Description : "Exit the application" ,
105+ Category : "Session" ,
106+ Execute : func (string ) tea.Cmd {
107+ return core .CmdHandler (messages.ExitSessionMsg {})
108+ },
109+ },
145110 {
146111 ID : "session.export" ,
147112 Label : "Export" ,
@@ -153,23 +118,43 @@ func builtInSessionCommands() []Item {
153118 },
154119 },
155120 {
156- ID : "session.yolo " ,
157- Label : "Yolo " ,
158- SlashCommand : "/yolo " ,
159- Description : "Toggle automatic approval of tool calls " ,
121+ ID : "session.model " ,
122+ Label : "Model " ,
123+ SlashCommand : "/model " ,
124+ Description : "Change the model for the current agent " ,
160125 Category : "Session" ,
161126 Execute : func (string ) tea.Cmd {
162- return core .CmdHandler (messages.ToggleYoloMsg {})
127+ return core .CmdHandler (messages.OpenModelPickerMsg {})
163128 },
164129 },
165130 {
166- ID : "session.think " ,
167- Label : "Think " ,
168- SlashCommand : "/think " ,
169- Description : "Toggle thinking/reasoning mode " ,
131+ ID : "session.new " ,
132+ Label : "New " ,
133+ SlashCommand : "/new " ,
134+ Description : "Start a new conversation " ,
170135 Category : "Session" ,
171136 Execute : func (string ) tea.Cmd {
172- return core .CmdHandler (messages.ToggleThinkingMsg {})
137+ return core .CmdHandler (messages.NewSessionMsg {})
138+ },
139+ },
140+ {
141+ ID : "session.permissions" ,
142+ Label : "Permissions" ,
143+ SlashCommand : "/permissions" ,
144+ Description : "Show tool permission rules for this session" ,
145+ Category : "Session" ,
146+ Execute : func (string ) tea.Cmd {
147+ return core .CmdHandler (messages.ShowPermissionsDialogMsg {})
148+ },
149+ },
150+ {
151+ ID : "session.history" ,
152+ Label : "Sessions" ,
153+ SlashCommand : "/sessions" ,
154+ Description : "Browse and load past sessions" ,
155+ Category : "Session" ,
156+ Execute : func (string ) tea.Cmd {
157+ return core .CmdHandler (messages.OpenSessionBrowserMsg {})
173158 },
174159 },
175160 {
@@ -183,45 +168,63 @@ func builtInSessionCommands() []Item {
183168 },
184169 },
185170 {
186- ID : "session.cost " ,
187- Label : "Cost " ,
188- SlashCommand : "/cost " ,
189- Description : "Show detailed cost breakdown for this session" ,
171+ ID : "session.star " ,
172+ Label : "Star " ,
173+ SlashCommand : "/star " ,
174+ Description : "Toggle star on current session" ,
190175 Category : "Session" ,
191176 Execute : func (string ) tea.Cmd {
192- return core .CmdHandler (messages.ShowCostDialogMsg {})
177+ return core .CmdHandler (messages.ToggleSessionStarMsg {})
193178 },
194179 },
195180 {
196- ID : "session.permissions " ,
197- Label : "Permissions " ,
198- SlashCommand : "/permissions " ,
199- Description : "Show tool permission rules for this session " ,
181+ ID : "session.think " ,
182+ Label : "Think " ,
183+ SlashCommand : "/think " ,
184+ Description : "Toggle thinking/reasoning mode " ,
200185 Category : "Session" ,
201186 Execute : func (string ) tea.Cmd {
202- return core .CmdHandler (messages.ShowPermissionsDialogMsg {})
187+ return core .CmdHandler (messages.ToggleThinkingMsg {})
203188 },
204189 },
205190 {
206- ID : "session.attach " ,
207- Label : "Attach " ,
208- SlashCommand : "/attach " ,
209- Description : "Attach a file to your message (usage: /attach [path ])" ,
191+ ID : "session.title " ,
192+ Label : "Title " ,
193+ SlashCommand : "/title " ,
194+ Description : "Set or regenerate session title (usage: /title [new title ])" ,
210195 Category : "Session" ,
211196 Execute : func (arg string ) tea.Cmd {
212- return core .CmdHandler (messages.AttachFileMsg {FilePath : arg })
197+ arg = strings .TrimSpace (arg )
198+ if arg == "" {
199+ // No argument: regenerate title
200+ return core .CmdHandler (messages.RegenerateTitleMsg {})
201+ }
202+ // With argument: set title
203+ return core .CmdHandler (messages.SetSessionTitleMsg {Title : arg })
213204 },
214205 },
215206 {
216- ID : "settings.theme " ,
217- Label : "Theme " ,
218- SlashCommand : "/theme " ,
219- Description : "Change the color theme " ,
220- Category : "Settings " ,
207+ ID : "session.yolo " ,
208+ Label : "Yolo " ,
209+ SlashCommand : "/yolo " ,
210+ Description : "Toggle automatic approval of tool calls " ,
211+ Category : "Session " ,
221212 Execute : func (string ) tea.Cmd {
222- return core .CmdHandler (messages.OpenThemePickerMsg {})
213+ return core .CmdHandler (messages.ToggleYoloMsg {})
223214 },
224215 },
216+ }
217+
218+ // Add speak command on supported platforms (macOS only)
219+ if speak := speakCommand (); speak != nil {
220+ cmds = append (cmds , * speak )
221+ }
222+
223+ return cmds
224+ }
225+
226+ func builtInSettingsCommands () []Item {
227+ return []Item {
225228 {
226229 ID : "settings.split-diff" ,
227230 Label : "Split Diff" ,
@@ -232,39 +235,50 @@ func builtInSessionCommands() []Item {
232235 return core .CmdHandler (messages.ToggleSplitDiffMsg {})
233236 },
234237 },
238+ {
239+ ID : "settings.theme" ,
240+ Label : "Theme" ,
241+ SlashCommand : "/theme" ,
242+ Description : "Change the color theme" ,
243+ Category : "Settings" ,
244+ Execute : func (string ) tea.Cmd {
245+ return core .CmdHandler (messages.OpenThemePickerMsg {})
246+ },
247+ },
235248 }
236-
237- // Add speak command on supported platforms (macOS only)
238- if speak := speakCommand (); speak != nil {
239- cmds = append (cmds , * speak )
240- }
241-
242- return cmds
243249}
244250
245251func builtInFeedbackCommands () []Item {
246252 return []Item {
247253 {
248- ID : "feedback.bug " ,
249- Label : "Report Bug " ,
250- Description : "Report a bug or issue " ,
254+ ID : "feedback.feedback " ,
255+ Label : "Give Feedback " ,
256+ Description : "Provide feedback about cagent " ,
251257 Category : "Feedback" ,
252258 Execute : func (string ) tea.Cmd {
253- return core .CmdHandler (messages.OpenURLMsg {URL : "https://github.com/docker/cagent/issues/new/choose" })
259+ return core .CmdHandler (messages.OpenURLMsg {URL : feedback . Link })
254260 },
255261 },
256262 {
257- ID : "feedback.feedback " ,
258- Label : "Give Feedback " ,
259- Description : "Provide feedback about cagent " ,
263+ ID : "feedback.bug " ,
264+ Label : "Report Bug " ,
265+ Description : "Report a bug or issue " ,
260266 Category : "Feedback" ,
261267 Execute : func (string ) tea.Cmd {
262- return core .CmdHandler (messages.OpenURLMsg {URL : feedback . Link })
268+ return core .CmdHandler (messages.OpenURLMsg {URL : "https://github.com/docker/cagent/issues/new/choose" })
263269 },
264270 },
265271 }
266272}
267273
274+ // sortByLabel returns items sorted alphabetically by label.
275+ func sortByLabel (items []Item ) []Item {
276+ slices .SortFunc (items , func (a , b Item ) int {
277+ return strings .Compare (strings .ToLower (a .Label ), strings .ToLower (b .Label ))
278+ })
279+ return items
280+ }
281+
268282// BuildCommandCategories builds the list of command categories for the command palette
269283func BuildCommandCategories (ctx context.Context , application * app.App ) []Category {
270284 // Get session commands and filter based on model capabilities
@@ -298,10 +312,6 @@ func BuildCommandCategories(ctx context.Context, application *app.App) []Categor
298312 Name : "Session" ,
299313 Commands : sessionCommands ,
300314 },
301- {
302- Name : "Feedback" ,
303- Commands : builtInFeedbackCommands (),
304- },
305315 }
306316
307317 agentCommands := application .CurrentAgentCommands (ctx )
@@ -322,7 +332,7 @@ func BuildCommandCategories(ctx context.Context, application *app.App) []Categor
322332
323333 categories = append (categories , Category {
324334 Name : "Agent Commands" ,
325- Commands : commands ,
335+ Commands : sortByLabel ( commands ) ,
326336 })
327337 }
328338
@@ -394,7 +404,7 @@ func BuildCommandCategories(ctx context.Context, application *app.App) []Categor
394404
395405 categories = append (categories , Category {
396406 Name : "MCP Prompts" ,
397- Commands : mcpCommands ,
407+ Commands : sortByLabel ( mcpCommands ) ,
398408 })
399409 }
400410
@@ -424,10 +434,22 @@ func BuildCommandCategories(ctx context.Context, application *app.App) []Categor
424434
425435 categories = append (categories , Category {
426436 Name : "Skills" ,
427- Commands : skillCommands ,
437+ Commands : sortByLabel ( skillCommands ) ,
428438 })
429439 }
430440
441+ // Settings and Feedback are always last, in that order.
442+ categories = append (categories ,
443+ Category {
444+ Name : "Settings" ,
445+ Commands : builtInSettingsCommands (),
446+ },
447+ Category {
448+ Name : "Feedback" ,
449+ Commands : builtInFeedbackCommands (),
450+ },
451+ )
452+
431453 return categories
432454}
433455
@@ -449,5 +471,11 @@ func ParseSlashCommand(input string) tea.Cmd {
449471 }
450472 }
451473
474+ for _ , item := range builtInSettingsCommands () {
475+ if item .SlashCommand == cmd {
476+ return item .Execute (arg )
477+ }
478+ }
479+
452480 return nil
453481}
0 commit comments