fix(cli): sync slash help with builtins (#10173)
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
use goose::agents::execute_commands::list_commands;
|
||||
use goose::config::GooseMode;
|
||||
use rustyline::completion::{Completer, FilenameCompleter, Pair};
|
||||
use rustyline::highlight::{CmdKind, Highlighter};
|
||||
@@ -152,23 +153,25 @@ impl GooseCompleter {
|
||||
|
||||
/// Complete slash commands
|
||||
fn complete_slash_commands(&self, line: &str) -> Result<(usize, Vec<Pair>)> {
|
||||
// Define available slash commands
|
||||
let commands = [
|
||||
"/exit",
|
||||
"/quit",
|
||||
"/help",
|
||||
"/?",
|
||||
"/t",
|
||||
"/extension",
|
||||
"/builtin",
|
||||
"/prompts",
|
||||
"/prompt",
|
||||
"/mode",
|
||||
"/model",
|
||||
"/recipe",
|
||||
"/skills",
|
||||
"/status",
|
||||
let mut commands = vec![
|
||||
"/exit".to_string(),
|
||||
"/quit".to_string(),
|
||||
"/help".to_string(),
|
||||
"/?".to_string(),
|
||||
"/t".to_string(),
|
||||
"/extension".to_string(),
|
||||
"/builtin".to_string(),
|
||||
"/mode".to_string(),
|
||||
"/model".to_string(),
|
||||
"/recipe".to_string(),
|
||||
];
|
||||
commands.extend(
|
||||
list_commands()
|
||||
.iter()
|
||||
.map(|command| format!("/{}", command.name)),
|
||||
);
|
||||
commands.sort();
|
||||
commands.dedup();
|
||||
|
||||
// Find commands that match the prefix
|
||||
let matching_commands: Vec<Pair> = commands
|
||||
@@ -578,6 +581,15 @@ mod tests {
|
||||
let (pos, candidates) = completer.complete_slash_commands("/").unwrap();
|
||||
assert_eq!(pos, 0);
|
||||
assert!(candidates.len() > 1);
|
||||
for command in list_commands() {
|
||||
assert!(
|
||||
candidates
|
||||
.iter()
|
||||
.any(|candidate| candidate.display == format!("/{}", command.name)),
|
||||
"slash completion should list /{}",
|
||||
command.name
|
||||
);
|
||||
}
|
||||
|
||||
// Test no match
|
||||
let (_pos, candidates) = completer.complete_slash_commands("/nonexistent").unwrap();
|
||||
|
||||
@@ -401,10 +401,17 @@ fn parse_plan_command(input: String) -> Option<InputResult> {
|
||||
Some(InputResult::Plan(options))
|
||||
}
|
||||
|
||||
fn print_help() {
|
||||
fn help_text() -> String {
|
||||
let newline_key = get_newline_key().to_ascii_uppercase();
|
||||
let modes = GooseMode::VARIANTS.join(", ");
|
||||
println!(
|
||||
let additional_builtin_help = additional_builtin_help();
|
||||
let additional_builtin_help = if additional_builtin_help.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!("{additional_builtin_help}\n")
|
||||
};
|
||||
|
||||
format!(
|
||||
"Available commands:
|
||||
/exit or /quit - Exit the session
|
||||
/t - Toggle Light/Dark/Ansi theme
|
||||
@@ -425,7 +432,7 @@ fn print_help() {
|
||||
/recipe [filepath] - Generate a recipe from the current conversation and save it to the specified filepath (must end with .yaml).
|
||||
If no filepath is provided, it will be saved to ./recipe.yaml.
|
||||
/compact - Compact the current conversation to reduce context length while preserving key information.
|
||||
/status - Show session status: model, provider, mode, and token usage.
|
||||
{additional_builtin_help}/status - Show session status: model, provider, mode, and token usage.
|
||||
/edit [text] - Open your prompt editor to compose a message. Optionally pre-fill with text.
|
||||
Uses $GOOSE_PROMPT_EDITOR, $VISUAL, or $EDITOR (in that order).
|
||||
/skills - List available skills or enable skills by name (usage: /skills [<name>...])
|
||||
@@ -436,7 +443,23 @@ Navigation:
|
||||
Ctrl+C - Clear current line if text is entered, otherwise exit the session
|
||||
Ctrl+{newline_key} - Add a newline (configurable via GOOSE_CLI_NEWLINE_KEY)
|
||||
Up/Down arrows - Navigate through command history"
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
fn additional_builtin_help() -> String {
|
||||
const DOCUMENTED_BUILTINS: &[&str] =
|
||||
&["prompts", "prompt", "compact", "clear", "skills", "status"];
|
||||
|
||||
goose::agents::execute_commands::list_commands()
|
||||
.iter()
|
||||
.filter(|command| !DOCUMENTED_BUILTINS.contains(&command.name))
|
||||
.map(|command| format!("/{} - {}", command.name, command.description))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
fn print_help() {
|
||||
println!("{}", help_text());
|
||||
}
|
||||
|
||||
/// Extract recent messages for editor context
|
||||
@@ -535,6 +558,19 @@ mod tests {
|
||||
assert!(handle_slash_command("/unknown").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn help_lists_builtin_agent_commands() {
|
||||
let help = help_text();
|
||||
|
||||
for command in goose::agents::execute_commands::list_commands() {
|
||||
assert!(
|
||||
help.contains(&format!("/{}", command.name)),
|
||||
"help output should list /{}",
|
||||
command.name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prompts_command() {
|
||||
// Test basic prompts command
|
||||
|
||||
Reference in New Issue
Block a user