feat: add mode completion (#1506)
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user