Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ tokenizer_files/
*.log
tmp/

logs/
# Generated by Cargo
# will have compiled files and executables
debug/
Expand Down
145 changes: 52 additions & 93 deletions crates/goose-cli/src/commands/configure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,27 +28,6 @@ use std::error::Error;
// cursor-selected and cursor-unselected items.
const MULTISELECT_VISIBILITY_HINT: &str = "<";

fn get_display_name(extension_id: &str) -> String {
match extension_id {
"developer" => "Developer Tools".to_string(),
"computercontroller" => "Computer Controller".to_string(),
"autovisualiser" => "Auto Visualiser".to_string(),
"memory" => "Memory".to_string(),
"tutorial" => "Tutorial".to_string(),
"jetbrains" => "JetBrains".to_string(),
// Add other extensions as needed
_ => {
extension_id
.chars()
.next()
.unwrap_or_default()
.to_uppercase()
.collect::<String>()
+ &extension_id[1..]
}
}
}

pub async fn handle_configure() -> Result<(), Box<dyn Error>> {
let config = Config::global();

Expand Down Expand Up @@ -128,14 +107,7 @@ pub async fn handle_configure() -> Result<(), Box<dyn Error>> {
// This operation is best-effort and errors are ignored
ExtensionConfigManager::set(ExtensionEntry {
enabled: true,
config: ExtensionConfig::Builtin {
name: "developer".to_string(),
display_name: Some(goose::config::DEFAULT_DISPLAY_NAME.to_string()),
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
bundled: Some(true),
description: None,
available_tools: Vec::new(),
},
config: ExtensionConfig::default(),
})?;
}
Ok(false) => {
Expand Down Expand Up @@ -747,35 +719,40 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
match extension_type {
// TODO we'll want a place to collect all these options, maybe just an enum in goose-mcp
"built-in" => {
let extension = cliclack::select("Which built-in extension would you like to enable?")
.item(
let extensions = vec![
(
"autovisualiser",
"Auto Visualiser",
"Data visualisation and UI generation tools",
)
.item(
),
(
"computercontroller",
"Computer Controller",
"controls for webscraping, file caching, and automations",
)
.item(
),
(
"developer",
"Developer Tools",
"Code editing and shell access",
)
.item("jetbrains", "JetBrains", "Connect to jetbrains IDEs")
.item(
),
("jetbrains", "JetBrains", "Connect to jetbrains IDEs"),
(
"memory",
"Memory",
"Tools to save and retrieve durable memories",
)
.item(
),
(
"tutorial",
"Tutorial",
"Access interactive tutorials and guides",
)
.interact()?
.to_string();
),
];

let mut select = cliclack::select("Which built-in extension would you like to enable?");
for (id, name, desc) in &extensions {
select = select.item(id, name, desc);
}
let extension = select.interact()?.to_string();

let timeout: u64 = cliclack::input("Please set the timeout for this tool (in secs):")
.placeholder(&goose::config::DEFAULT_EXTENSION_TIMEOUT.to_string())
Expand All @@ -785,7 +762,11 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
})
.interact()?;

let display_name = get_display_name(&extension);
let (display_name, description) = extensions
.iter()
.find(|(id, _, _)| id == &extension)
.map(|(_, name, desc)| (name.to_string(), desc.to_string()))
.unwrap_or_else(|| (extension.clone(), extension.clone()));

ExtensionConfigManager::set(ExtensionEntry {
enabled: true,
Expand All @@ -794,7 +775,7 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
display_name: Some(display_name),
timeout: Some(timeout),
bundled: Some(true),
description: None,
description,
available_tools: Vec::new(),
},
})?;
Expand Down Expand Up @@ -841,20 +822,13 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
let cmd = parts.next().unwrap_or("").to_string();
let args: Vec<String> = parts.map(String::from).collect();

let add_desc = cliclack::confirm("Would you like to add a description?").interact()?;

let description = if add_desc {
let desc = cliclack::input("Enter a description for this extension:")
.placeholder("Description")
.validate(|input: &String| match input.parse::<String>() {
Ok(_) => Ok(()),
Err(_) => Err("Please enter a valid description"),
})
.interact()?;
Some(desc)
} else {
None
};
let description = cliclack::input("Enter a description for this extension:")
.placeholder("Description")
.validate(|input: &String| match input.parse::<String>() {
Ok(_) => Ok(()),
Err(_) => Err("Please enter a valid description"),
})
.interact()?;

let add_env =
cliclack::confirm("Would you like to add environment variables?").interact()?;
Expand Down Expand Up @@ -945,21 +919,13 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
})
.interact()?;

let add_desc = cliclack::confirm("Would you like to add a description?").interact()?;

let description = if add_desc {
let desc = cliclack::input("Enter a description for this extension:")
.placeholder("Description")
.validate(|input: &String| match input.parse::<String>() {
Ok(_) => Ok(()),
Err(_) => Err("Please enter a valid description"),
})
.interact()?;
Some(desc)
} else {
None
};

let description = cliclack::input("Enter a description for this extension:")
.placeholder("Description")
.validate(|input: &String| match input.parse::<String>() {
Ok(_) => Ok(()),
Err(_) => Err("Please enter a valid description"),
})
.interact()?;
let add_env =
cliclack::confirm("Would you like to add environment variables?").interact()?;

Expand Down Expand Up @@ -1048,23 +1014,16 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
})
.interact()?;

let add_desc = cliclack::confirm("Would you like to add a description?").interact()?;

let description = if add_desc {
let desc = cliclack::input("Enter a description for this extension:")
.placeholder("Description")
.validate(|input: &String| {
if input.trim().is_empty() {
Err("Please enter a valid description")
} else {
Ok(())
}
})
.interact()?;
Some(desc)
} else {
None
};
let description = cliclack::input("Enter a description for this extension:")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so descriptions are required now - but not often used, how come mandatory now effectively?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, we were using them all over the place, just for when we didn't have them we would try to put something together ourselves. this is just an effort to make it all look a bit more like each other.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is ok - just wanted to make sure deliberate and won't cause problems loading older configurations

.placeholder("Description")
.validate(|input: &String| {
if input.trim().is_empty() {
Err("Please enter a valid description")
} else {
Ok(())
}
})
.interact()?;

let add_headers =
cliclack::confirm("Would you like to add custom headers?").interact()?;
Expand Down Expand Up @@ -1762,7 +1721,7 @@ pub async fn handle_openrouter_auth() -> Result<(), Box<dyn Error>> {
),
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
bundled: Some(true),
description: None,
description: "Developer extension".to_string(),
available_tools: Vec::new(),
},
}) {
Expand Down Expand Up @@ -1865,7 +1824,7 @@ pub async fn handle_tetrate_auth() -> Result<(), Box<dyn Error>> {
),
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
bundled: Some(true),
description: None,
description: "Developer extension".to_string(),
available_tools: Vec::new(),
},
}) {
Expand Down
13 changes: 7 additions & 6 deletions crates/goose-cli/src/recipes/secret_discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ fn extract_secrets_from_extensions(
ExtensionConfig::Stdio { name, env_keys, .. } => (name, env_keys),
ExtensionConfig::StreamableHttp { name, env_keys, .. } => (name, env_keys),
ExtensionConfig::Builtin { name, .. } => (name, &Vec::new()),
ExtensionConfig::Platform { name, .. } => (name, &Vec::new()),
ExtensionConfig::Frontend { name, .. } => (name, &Vec::new()),
ExtensionConfig::InlinePython { name, .. } => (name, &Vec::new()),
};
Expand Down Expand Up @@ -140,7 +141,7 @@ mod tests {
uri: "sse://example.com".to_string(),
envs: Envs::new(HashMap::new()),
env_keys: vec!["GITHUB_TOKEN".to_string(), "GITHUB_API_URL".to_string()],
description: None,
description: "github-mcp".to_string(),
timeout: None,
bundled: None,
available_tools: Vec::new(),
Expand All @@ -152,14 +153,14 @@ mod tests {
envs: Envs::new(HashMap::new()),
env_keys: vec!["SLACK_TOKEN".to_string()],
timeout: None,
description: None,
description: "slack-mcp".to_string(),
bundled: None,
available_tools: Vec::new(),
},
ExtensionConfig::Builtin {
name: "builtin-ext".to_string(),
display_name: None,
description: None,
description: "builtin-ext".to_string(),
timeout: None,
bundled: None,
available_tools: Vec::new(),
Expand Down Expand Up @@ -237,7 +238,7 @@ mod tests {
uri: "sse://example.com".to_string(),
envs: Envs::new(HashMap::new()),
env_keys: vec!["API_KEY".to_string()],
description: None,
description: "service-a".to_string(),
timeout: None,
bundled: None,
available_tools: Vec::new(),
Expand All @@ -249,7 +250,7 @@ mod tests {
envs: Envs::new(HashMap::new()),
env_keys: vec!["API_KEY".to_string()], // Same original key, different extension
timeout: None,
description: None,
description: "service-b".to_string(),
bundled: None,
available_tools: Vec::new(),
},
Expand Down Expand Up @@ -296,7 +297,7 @@ mod tests {
uri: "sse://parent.com".to_string(),
envs: Envs::new(HashMap::new()),
env_keys: vec!["PARENT_TOKEN".to_string()],
description: None,
description: "parent-ext".to_string(),
timeout: None,
bundled: None,
available_tools: Vec::new(),
Expand Down
2 changes: 1 addition & 1 deletion crates/goose-cli/src/scenario_tests/scenario_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ where
ExtensionConfig::Builtin {
name: "".to_string(),
display_name: None,
description: None,
description: "".to_string(),
timeout: None,
bundled: None,
available_tools: vec![],
Expand Down
8 changes: 8 additions & 0 deletions crates/goose-cli/src/session/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use goose::config::{Config, ExtensionConfig, ExtensionConfigManager};
use goose::providers::create;
use goose::recipe::{Response, SubRecipe};

use goose::agents::extension::PlatformExtensionContext;
use goose::session::SessionManager;
use rustyline::EditMode;
use std::collections::HashSet;
Expand Down Expand Up @@ -281,6 +282,13 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> CliSession {
Some(session.id)
};

agent
.extension_manager
.set_context(PlatformExtensionContext {
session_id: session_id.clone(),
})
.await;

if session_config.resume {
if let Some(session_id) = session_id.as_ref() {
// Read the session metadata from database
Expand Down
8 changes: 4 additions & 4 deletions crates/goose-cli/src/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ impl CliSession {
args: parts.iter().map(|s| s.to_string()).collect(),
envs: Envs::new(envs),
env_keys: Vec::new(),
description: Some(goose::config::DEFAULT_EXTENSION_DESCRIPTION.to_string()),
description: goose::config::DEFAULT_EXTENSION_DESCRIPTION.to_string(),
// TODO: should set timeout
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
bundled: None,
Expand Down Expand Up @@ -241,7 +241,7 @@ impl CliSession {
uri: extension_url,
envs: Envs::new(HashMap::new()),
env_keys: Vec::new(),
description: Some(goose::config::DEFAULT_EXTENSION_DESCRIPTION.to_string()),
description: goose::config::DEFAULT_EXTENSION_DESCRIPTION.to_string(),
// TODO: should set timeout
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
bundled: None,
Expand Down Expand Up @@ -276,7 +276,7 @@ impl CliSession {
envs: Envs::new(HashMap::new()),
env_keys: Vec::new(),
headers: HashMap::new(),
description: Some(goose::config::DEFAULT_EXTENSION_DESCRIPTION.to_string()),
description: goose::config::DEFAULT_EXTENSION_DESCRIPTION.to_string(),
// TODO: should set timeout
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
bundled: None,
Expand Down Expand Up @@ -306,7 +306,7 @@ impl CliSession {
// TODO: should set a timeout
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
bundled: None,
description: None,
description: name.trim().to_string(),
available_tools: Vec::new(),
};
self.agent
Expand Down
Loading
Loading