feat: add mode completion (#1506)

This commit is contained in:
Wendy Tang
2025-03-04 13:56:09 -08:00
committed by GitHub
parent 389e8059f4
commit e30f50a92e
3 changed files with 91 additions and 9 deletions
@@ -75,6 +75,46 @@ impl GooseCompleter {
Ok((line.len(), vec![])) Ok((line.len(), vec![]))
} }
/// Complete flags for the /mode command
fn complete_mode_flags(&self, line: &str) -> Result<(usize, Vec<Pair>)> {
let modes = ["auto", "approve", "chat"];
let parts: Vec<&str> = line.split_whitespace().collect();
// If we're just after "/mode" with a space, show all options
if line == "/mode " {
return Ok((
line.len(),
modes
.iter()
.map(|mode| Pair {
display: mode.to_string(),
replacement: format!("{} ", mode),
})
.collect(),
));
}
// If we're typing a mode name, show the flags for that mode
if parts.len() == 2 {
let partial = parts[1].to_lowercase();
return Ok((
line.len() - partial.len(),
modes
.iter()
.filter(|mode| mode.to_lowercase().starts_with(&partial.to_lowercase()))
.map(|mode| Pair {
display: mode.to_string(),
replacement: format!("{} ", mode),
})
.collect(),
));
}
// No completions available
Ok((line.len(), vec![]))
}
/// Complete slash commands /// Complete slash commands
fn complete_slash_commands(&self, line: &str) -> Result<(usize, Vec<Pair>)> { fn complete_slash_commands(&self, line: &str) -> Result<(usize, Vec<Pair>)> {
// Define available slash commands // Define available slash commands
@@ -88,6 +128,7 @@ impl GooseCompleter {
"/builtin", "/builtin",
"/prompts", "/prompts",
"/prompt", "/prompt",
"/mode",
]; ];
// Find commands that match the prefix // Find commands that match the prefix
@@ -292,6 +333,10 @@ impl Completer for GooseCompleter {
)); ));
} }
} }
if line.starts_with("/mode") {
return self.complete_mode_flags(line);
}
} }
// Default: no completions // Default: no completions
+24 -9
View File
@@ -1,10 +1,9 @@
use super::completion::GooseCompleter;
use anyhow::Result; use anyhow::Result;
use rustyline::Editor; use rustyline::Editor;
use shlex; use shlex;
use std::collections::HashMap; use std::collections::HashMap;
use super::completion::GooseCompleter;
#[derive(Debug)] #[derive(Debug)]
pub enum InputResult { pub enum InputResult {
Message(String), Message(String),
@@ -15,6 +14,7 @@ pub enum InputResult {
Retry, Retry,
ListPrompts(Option<String>), ListPrompts(Option<String>),
PromptCommand(PromptCommandOptions), PromptCommand(PromptCommandOptions),
GooseMode(String),
} }
#[derive(Debug)] #[derive(Debug)]
@@ -65,6 +65,14 @@ pub fn get_input(
fn handle_slash_command(input: &str) -> Option<InputResult> { fn handle_slash_command(input: &str) -> Option<InputResult> {
let input = input.trim(); let input = input.trim();
// Command prefix constants
const CMD_PROMPTS: &str = "/prompts ";
const CMD_PROMPT: &str = "/prompt";
const CMD_PROMPT_WITH_SPACE: &str = "/prompt ";
const CMD_EXTENSION: &str = "/extension ";
const CMD_BUILTIN: &str = "/builtin ";
const CMD_MODE: &str = "/mode ";
match input { match input {
"/exit" | "/quit" => Some(InputResult::Exit), "/exit" | "/quit" => Some(InputResult::Exit),
"/?" | "/help" => { "/?" | "/help" => {
@@ -73,20 +81,20 @@ fn handle_slash_command(input: &str) -> Option<InputResult> {
} }
"/t" => Some(InputResult::ToggleTheme), "/t" => Some(InputResult::ToggleTheme),
"/prompts" => Some(InputResult::ListPrompts(None)), "/prompts" => Some(InputResult::ListPrompts(None)),
s if s.starts_with("/prompts ") => { s if s.starts_with(CMD_PROMPTS) => {
// Parse arguments for /prompts command // Parse arguments for /prompts command
let args = s.strip_prefix("/prompts ").unwrap_or_default(); let args = s.strip_prefix(CMD_PROMPTS).unwrap_or_default();
parse_prompts_command(args) parse_prompts_command(args)
} }
s if s.starts_with("/prompt") => { s if s.starts_with(CMD_PROMPT) => {
if s == "/prompt" { if s == CMD_PROMPT {
// No arguments case // No arguments case
Some(InputResult::PromptCommand(PromptCommandOptions { Some(InputResult::PromptCommand(PromptCommandOptions {
name: String::new(), // Empty name will trigger the error message in the rendering name: String::new(), // Empty name will trigger the error message in the rendering
info: false, info: false,
arguments: HashMap::new(), arguments: HashMap::new(),
})) }))
} else if let Some(stripped) = s.strip_prefix("/prompt ") { } else if let Some(stripped) = s.strip_prefix(CMD_PROMPT_WITH_SPACE) {
// Has arguments case // Has arguments case
parse_prompt_command(stripped) parse_prompt_command(stripped)
} else { } else {
@@ -94,8 +102,15 @@ fn handle_slash_command(input: &str) -> Option<InputResult> {
None None
} }
} }
s if s.starts_with("/extension ") => Some(InputResult::AddExtension(s[11..].to_string())), s if s.starts_with(CMD_EXTENSION) => Some(InputResult::AddExtension(
s if s.starts_with("/builtin ") => Some(InputResult::AddBuiltin(s[9..].to_string())), s[CMD_EXTENSION.len()..].to_string(),
)),
s if s.starts_with(CMD_BUILTIN) => {
Some(InputResult::AddBuiltin(s[CMD_BUILTIN.len()..].to_string()))
}
s if s.starts_with(CMD_MODE) => {
Some(InputResult::GooseMode(s[CMD_MODE.len()..].to_string()))
}
_ => None, _ => None,
} }
} }
+22
View File
@@ -14,6 +14,7 @@ use etcetera::choose_app_strategy;
use etcetera::AppStrategy; use etcetera::AppStrategy;
use goose::agents::extension::{Envs, ExtensionConfig}; use goose::agents::extension::{Envs, ExtensionConfig};
use goose::agents::Agent; use goose::agents::Agent;
use goose::config::Config;
use goose::message::{Message, MessageContent}; use goose::message::{Message, MessageContent};
use goose::session; use goose::session;
use mcp_core::handler::ToolError; use mcp_core::handler::ToolError;
@@ -325,6 +326,27 @@ impl Session {
Err(e) => output::render_error(&e.to_string()), Err(e) => output::render_error(&e.to_string()),
} }
} }
input::InputResult::GooseMode(mode) => {
save_history(&mut editor);
let config = Config::global();
let mode = mode.to_lowercase();
// Check if mode is valid
if !["auto", "approve", "chat"].contains(&mode.as_str()) {
output::render_error(&format!(
"Invalid mode '{}'. Mode must be one of: auto, approve, chat",
mode
));
continue;
}
config
.set("GOOSE_MODE", Value::String(mode.to_string()))
.unwrap();
println!("Goose mode set to '{}'", mode);
continue;
}
input::InputResult::PromptCommand(opts) => { input::InputResult::PromptCommand(opts) => {
save_history(&mut editor); save_history(&mut editor);