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
25 changes: 25 additions & 0 deletions crates/goose-cli/src/commands/configure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,27 @@ use serde_json::{json, Value};
use std::collections::HashMap;
use std::error::Error;

fn get_display_name(extension_id: &str) -> String {
match extension_id {
"developer" => "Developer Tools".to_string(),
"computercontroller" => "Computer Controller".to_string(),
"googledrive" => "Google Drive".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 @@ -39,6 +60,7 @@ pub async fn handle_configure() -> Result<(), Box<dyn Error>> {
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),
},
})?;
Expand Down Expand Up @@ -464,10 +486,13 @@ pub fn configure_extensions_dialog() -> Result<(), Box<dyn Error>> {
})
.interact()?;

let display_name = get_display_name(&extension);

ExtensionManager::set(ExtensionEntry {
enabled: true,
config: ExtensionConfig::Builtin {
name: extension.clone(),
display_name: Some(display_name),
timeout: Some(timeout),
},
})?;
Expand Down
1 change: 1 addition & 0 deletions crates/goose-cli/src/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ impl Session {
for name in builtin_name.split(',') {
let config = ExtensionConfig::Builtin {
name: name.trim().to_string(),
display_name: None,
// TODO: should set a timeout
timeout: Some(goose::config::DEFAULT_EXTENSION_TIMEOUT),
};
Expand Down
13 changes: 10 additions & 3 deletions crates/goose-server/src/routes/extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ enum ExtensionConfigRequest {
Builtin {
/// The name of the built-in extension.
name: String,
display_name: Option<String>,
timeout: Option<u64>,
},
}
Expand Down Expand Up @@ -157,9 +158,15 @@ async fn add_extension(
timeout,
}
}
ExtensionConfigRequest::Builtin { name, timeout } => {
ExtensionConfig::Builtin { name, timeout }
}
ExtensionConfigRequest::Builtin {
name,
display_name,
timeout,
} => ExtensionConfig::Builtin {
name,
display_name,
timeout,
},
};

// Acquire a lock on the agent and attempt to add the extension.
Expand Down
7 changes: 6 additions & 1 deletion crates/goose/src/agents/capabilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,12 @@ impl Capabilities {
);
Box::new(McpClient::new(service))
}
ExtensionConfig::Builtin { name, timeout } => {
#[allow(unused_variables)]
ExtensionConfig::Builtin {
name,
display_name,
timeout,
} => {
// For builtin extensions, we run the current executable with mcp and extension name
let cmd = std::env::current_exe()
.expect("should find the current executable")
Expand Down
2 changes: 2 additions & 0 deletions crates/goose/src/agents/extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ pub enum ExtensionConfig {
Builtin {
/// The name used to identify this extension
name: String,
display_name: Option<String>, // needed for the UI
timeout: Option<u64>,
},
}
Expand All @@ -86,6 +87,7 @@ impl Default for ExtensionConfig {
fn default() -> Self {
Self::Builtin {
name: config::DEFAULT_EXTENSION.to_string(),
display_name: Some(config::DEFAULT_DISPLAY_NAME.to_string()),
timeout: Some(config::DEFAULT_EXTENSION_TIMEOUT),
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/goose/src/config/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use utoipa::ToSchema;
pub const DEFAULT_EXTENSION: &str = "developer";
pub const DEFAULT_EXTENSION_TIMEOUT: u64 = 300;
pub const DEFAULT_EXTENSION_DESCRIPTION: &str = "";
pub const DEFAULT_DISPLAY_NAME: &str = "Developer";

#[derive(Debug, Deserialize, Serialize, Clone, ToSchema)]
pub struct ExtensionEntry {
Expand Down Expand Up @@ -42,6 +43,7 @@ impl ExtensionManager {
enabled: true,
config: ExtensionConfig::Builtin {
name: DEFAULT_EXTENSION.to_string(),
display_name: Some(DEFAULT_DISPLAY_NAME.to_string()),
timeout: Some(DEFAULT_EXTENSION_TIMEOUT),
},
},
Expand Down
1 change: 1 addition & 0 deletions crates/goose/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub use base::{Config, ConfigError, APP_STRATEGY};
pub use experiments::ExperimentManager;
pub use extensions::{ExtensionEntry, ExtensionManager};

pub use extensions::DEFAULT_DISPLAY_NAME;
pub use extensions::DEFAULT_EXTENSION;
pub use extensions::DEFAULT_EXTENSION_DESCRIPTION;
pub use extensions::DEFAULT_EXTENSION_TIMEOUT;
6 changes: 5 additions & 1 deletion ui/desktop/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"license": {
"name": "Apache-2.0"
},
"version": "1.0.14"
"version": "1.0.15"
},
"paths": {
"/config": {
Expand Down Expand Up @@ -392,6 +392,10 @@
"type"
],
"properties": {
"display_name": {
"type": "string",
"nullable": true
},
"name": {
"type": "string",
"description": "The name used to identify this extension"
Expand Down
1 change: 1 addition & 0 deletions ui/desktop/src/api/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export type ExtensionConfig = {
timeout?: number | null;
type: 'stdio';
} | {
display_name?: string | null;
/**
* The name used to identify this extension
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export default function ExtensionsSection() {

<div className="flex gap-4 pt-4 w-full">
<Button
className="flex items-center gap-2 flex-1 justify-center text-textSubtle bg-black dark:bg-white hover:bg-subtle"
className="flex items-center gap-2 flex-1 justify-center text-white dark:text-textSubtle bg-black dark:bg-white hover:bg-subtle"
onClick={() => setIsAddModalOpen(true)}
>
<Plus className="h-4 w-4" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type { ExtensionConfig } from '../../../api/types.gen';
import builtInExtensionsData from '../../../built-in-extensions.json';
import { FixedExtensionEntry } from '@/src/components/ConfigContext';
import builtInExtensionsData from './built-in-extensions.json';
import { FixedExtensionEntry } from '../../ConfigContext';

// Type definition for built-in extensions from JSON
type BuiltinExtension = {
id: string;
name: string;
display_name: string;
description: string;
enabled: boolean;
type: 'builtin';
Expand Down Expand Up @@ -50,13 +51,14 @@ export async function syncBuiltInExtensions(

// Check each built-in extension
for (const builtinExt of builtinExtensions) {
// Only add if the extension doesn't already exist
// Only add if the extension doesn't already exist -- use the id
if (!existingExtensionKeys.has(builtinExt.id)) {
console.log(`Adding built-in extension: ${builtinExt.id}`);

// Convert to the ExtensionConfig format
const extConfig: ExtensionConfig = {
name: builtinExt.name,
display_name: builtinExt.display_name,
type: 'builtin',
timeout: builtinExt.timeout ?? 300,
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
[
{
"id": "developer",
"name": "developer",
"display_name": "Developer",
"description": "General development tools useful for software engineering.",
"enabled": true,
"type": "builtin",
"env_keys": [],
"timeout": 300
},
{
"id": "computercontroller",
"name": "computercontroller",
"display_name": "Computer Controller",
"description": "General computer control tools that don't require you to be a developer or engineer.",
"enabled": false,
"type": "builtin",
"env_keys": [],
"timeout": 300
},
{
"id": "memory",
"name": "memory",
"display_name": "Memory",
"description": "Teach goose your preferences as you go.",
"enabled": false,
"type": "builtin",
"env_keys": [],
"timeout": 300
},
{
"id": "jetbrains",
"display_name": "Jetbrains",
"name": "jetbrains",
"description": "Integration with any Jetbrains IDE",
"enabled": false,
"type": "builtin",
"env_keys": [],
"timeout": 300
},
{
"id": "tutorial",
"name": "tutorial",
"display_name": "Tutorial",
"description": "Access interactive tutorials and guides",
"enabled": false,
"type": "builtin",
"env_keys": []
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default function ExtensionItem({ extension, onToggle, onConfigure }: Exte
return (
<div className="rounded-lg border border-borderSubtle p-4 mb-2">
<div className="flex items-center justify-between mb-2">
<h3 className="font-medium text-textStandard">{getFriendlyTitle(extension.name)}</h3>
<h3 className="font-medium text-textStandard">{getFriendlyTitle(extension)}</h3>
<div className="flex items-center gap-2">
{/* Only show config button for non-builtin extensions */}
{extension.type !== 'builtin' && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,21 @@ export default function ExtensionList({ extensions, onToggle, onConfigure }: Ext

// Helper functions
// Helper function to get a friendly title from extension name
export function getFriendlyTitle(name: string): string {
export function getFriendlyTitle(extension: FixedExtensionEntry): string {
let name = '';

// if it's a builtin, check if there's a display_name (old configs didn't have this field)
if (extension.type === 'builtin' && 'display_name' in extension && extension.display_name) {
// If we have a display_name for a builtin, use it directly
return extension.display_name;
} else {
// For non-builtins or builtins without display_name
name = extension.name;
}

// Format the name to be more readable
return name
.split('-')
.split(/[-_]/) // Split on hyphens and underscores
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
Expand Down
Loading