11use std:: borrow:: Cow ;
22use std:: cell:: RefCell ;
33use std:: path:: PathBuf ;
4+ use std:: sync:: OnceLock ;
45
56use eyre:: Result ;
67use rustyline:: completion:: {
@@ -54,58 +55,60 @@ use crate::database::settings::Setting;
5455use crate :: os:: Os ;
5556use crate :: util:: directories:: chat_cli_bash_history_path;
5657
57- pub const COMMANDS : & [ & str ] = & [
58- "/clear" ,
59- "/help" ,
60- "/editor" ,
61- "/reply" ,
62- "/issue" ,
63- "/quit" ,
64- "/tools" ,
65- "/tools trust" ,
66- "/tools untrust" ,
67- "/tools trust-all" ,
68- "/tools reset" ,
69- "/mcp" ,
70- "/model" ,
71- "/experiment" ,
72- "/agent" ,
73- "/agent help" ,
74- "/agent list" ,
75- "/agent create" ,
76- "/agent delete" ,
77- "/agent rename" ,
78- "/agent set" ,
79- "/agent schema" ,
80- "/agent generate" ,
81- "/prompts" ,
82- "/context" ,
83- "/context help" ,
84- "/context show" ,
85- "/context show --expand" ,
86- "/context add" ,
87- "/context rm" ,
88- "/context clear" ,
89- "/hooks" ,
90- "/hooks help" ,
91- "/hooks add" ,
92- "/hooks rm" ,
93- "/hooks enable" ,
94- "/hooks disable" ,
95- "/hooks enable-all" ,
96- "/hooks disable-all" ,
97- "/compact" ,
98- "/compact help" ,
99- "/usage" ,
100- "/changelog" ,
101- "/save" ,
102- "/load" ,
103- "/subscribe" ,
104- ] ;
58+ /// Cache for dynamically generated commands
59+ static COMMANDS_CACHE : OnceLock < Vec < String > > = OnceLock :: new ( ) ;
60+
61+ /// Generate command list from SlashCommand enum using clap introspection
62+ fn generate_commands ( ) -> & ' static Vec < String > {
63+ COMMANDS_CACHE . get_or_init ( || {
64+ use clap:: CommandFactory ;
65+
66+ use super :: cli:: SlashCommand ;
67+
68+ let mut commands = Vec :: new ( ) ;
69+ let slash_command = SlashCommand :: command ( ) ;
70+
71+ for subcommand in slash_command. get_subcommands ( ) {
72+ let name = subcommand. get_name ( ) ;
73+
74+ // Skip hidden commands
75+ if subcommand. is_hide_set ( ) {
76+ continue ;
77+ }
78+
79+ // Add the main command
80+ commands. push ( format ! ( "/{}" , name) ) ;
81+
82+ // Add aliases
83+ for alias in subcommand. get_all_aliases ( ) {
84+ commands. push ( format ! ( "/{}" , alias) ) ;
85+ }
86+
87+ // If this command has subcommands, add them too
88+ if subcommand. has_subcommands ( ) {
89+ for nested in subcommand. get_subcommands ( ) {
90+ if !nested. is_hide_set ( ) {
91+ commands. push ( format ! ( "/{} {}" , name, nested. get_name( ) ) ) ;
92+
93+ // Add nested aliases
94+ for alias in nested. get_all_aliases ( ) {
95+ commands. push ( format ! ( "/{} {}" , name, alias) ) ;
96+ }
97+ }
98+ }
99+ }
100+ }
101+
102+ // Add /help explicitly since it's not a SlashCommand variant but is handled by clap.
103+ commands. push ( "/help" . to_string ( ) ) ;
104+ commands. sort ( ) ;
105+ commands
106+ } )
107+ }
105108
106109/// Generate dynamic command list including experiment-based commands when enabled
107110pub fn get_available_commands ( os : & Os ) -> Vec < & ' static str > {
108- let mut commands = COMMANDS . to_vec ( ) ;
111+ let mut commands: Vec < & ' static str > = generate_commands ( ) . iter ( ) . map ( |s| s . as_str ( ) ) . collect ( ) ;
109112 commands. extend ( ExperimentManager :: get_commands ( os) ) ;
110113 commands. sort ( ) ;
111114 commands
@@ -115,13 +118,13 @@ pub type PromptQuerySender = tokio::sync::broadcast::Sender<PromptQuery>;
115118pub type PromptQueryResponseReceiver = tokio:: sync:: broadcast:: Receiver < PromptQueryResult > ;
116119
117120/// Complete commands that start with a slash
118- fn complete_command ( commands : Vec < & ' static str > , word : & str , start : usize ) -> ( usize , Vec < String > ) {
121+ fn complete_command ( commands : Vec < & str > , word : & str , start : usize ) -> ( usize , Vec < String > ) {
119122 (
120123 start,
121124 commands
122125 . iter ( )
123126 . filter ( |p| p. starts_with ( word) )
124- . map ( |s| ( * s ) . to_owned ( ) )
127+ . map ( |s| s . to_string ( ) )
125128 . collect ( ) ,
126129 )
127130}
0 commit comments