Skip to content

Commit b06d192

Browse files
committed
refactor: dynamically generate command list
1 parent b3fa226 commit b06d192

File tree

1 file changed

+54
-51
lines changed

1 file changed

+54
-51
lines changed

crates/chat-cli/src/cli/chat/prompt.rs

Lines changed: 54 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::borrow::Cow;
22
use std::cell::RefCell;
33
use std::path::PathBuf;
4+
use std::sync::OnceLock;
45

56
use eyre::Result;
67
use rustyline::completion::{
@@ -54,58 +55,60 @@ use crate::database::settings::Setting;
5455
use crate::os::Os;
5556
use 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
107110
pub 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>;
115118
pub 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

Comments
 (0)