diff --git a/crates/goose-mcp/src/developer/rmcp_developer.rs b/crates/goose-mcp/src/developer/rmcp_developer.rs index bbd91d96ae..9547d3d86b 100644 --- a/crates/goose-mcp/src/developer/rmcp_developer.rs +++ b/crates/goose-mcp/src/developer/rmcp_developer.rs @@ -193,55 +193,30 @@ impl ServerHandler for DeveloperServer { let base_instructions = match os { "windows" => formatdoc! {r#" - The developer extension gives you the capabilities to edit code files and run shell commands, - and can be used to solve a wide range of problems. - - You can use the shell tool to run Windows commands (PowerShell or CMD). - When using paths, you can use either backslashes or forward slashes. - - Use the shell tool as needed to locate files or interact with the project. - - Leverage `analyze` through `return_last_only=true` subagents for deep codebase understanding with lean context - - delegate analysis, retain summaries - - Your windows/screen tools can be used for visual debugging. You should not use these tools unless - prompted to, but you can mention they are available if they are relevant. - - operating system: {os} - current directory: {cwd} - {container_info} + Developer: edit code, run shell, analyze codebases. + OS: {os} | CWD: {cwd}{container_info} + Shell: PowerShell/CMD. Use forward slashes in paths. + Analyze: delegate via return_last_only=true subagents. + Screen/windows tools available for debugging. "#, os=os, cwd=cwd.to_string_lossy(), - container_info=if in_container { "container: true" } else { "" }, + container_info=if in_container { " | container" } else { "" }, }, _ => { let shell_info = std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string()); formatdoc! {r#" - The developer extension gives you the capabilities to edit code files and run shell commands, - and can be used to solve a wide range of problems. - - You can use the shell tool to run any command that would work on the relevant operating system. - Use the shell tool as needed to locate files or interact with the project. - - Leverage `analyze` through `return_last_only=true` subagents for deep codebase understanding with lean context - - delegate analysis, retain summaries - - Your windows/screen tools can be used for visual debugging. You should not use these tools unless - prompted to, but you can mention they are available if they are relevant. - - Always prefer ripgrep (rg -C 3) to grep. - - operating system: {os} - current directory: {cwd} - shell: {shell} - {container_info} + Developer: edit code, run shell, analyze codebases. + OS: {os} | CWD: {cwd} | Shell: {shell}{container_info} + Analyze: delegate via return_last_only=true subagents. + Screen/windows tools available for debugging. + Use rg only, not grep/find/ls -r. "#, os=os, cwd=cwd.to_string_lossy(), shell=shell_info, - container_info=if in_container { "container: true" } else { "" }, + container_info=if in_container { " | container" } else { "" }, } } }; @@ -260,117 +235,40 @@ impl ServerHandler for DeveloperServer { // Check if editor model exists and augment with custom llm editor tool description let editor_description = if let Some(ref editor) = self.editor_model { formatdoc! {r#" - - Additional Text Editor Tool Instructions: - - Perform text editing operations on files. - The `command` parameter specifies the operation to perform. Allowed options are: - - `view`: View the content of a file. - - `write`: Create or overwrite a file with the given content - - `str_replace`: Replace text in one or more files. - - `insert`: Insert text at a specific line location in the file. - - `undo_edit`: Undo the last edit made to a file. - - To use the write command, you must specify `file_text` which will become the new content of the file. Be careful with - existing files! This is a full overwrite, so you must include everything - not just sections you are modifying. - - To use the insert command, you must specify both `insert_line` (the line number after which to insert, 0 for beginning, -1 for end) - and `new_str` (the text to insert). - - To use the str_replace command to edit multiple files, use the `diff` parameter with a unified diff. - To use the str_replace command to edit one file, you must specify both `old_str` and `new_str` - the `old_str` needs to exactly match one - unique section of the original file, including any whitespace. Make sure to include enough context that the match is not - ambiguous. The entire original string will be replaced with `new_str` - - When possible, batch file edits together by using a multi-file unified `diff` within a single str_replace tool call. - - {} - +TEXT_EDITOR: +Commands: view|write|str_replace|insert|undo_edit +⚠️ write=full overwrite (include all content) +str_replace: diff for multi-file, or old_str+new_str (exact match incl. whitespace, avoid ambiguous) +insert: insert_line (0=start, -1=end) + new_str +Batch edits: use unified diff +{} "#, editor.get_str_replace_description()} } else { formatdoc! {r#" - - Additional Text Editor Tool Instructions: - - Perform text editing operations on files. - - The `command` parameter specifies the operation to perform. Allowed options are: - - `view`: View the content of a file. - - `write`: Create or overwrite a file with the given content - - `str_replace`: Replace text in one or more files. - - `insert`: Insert text at a specific line location in the file. - - `undo_edit`: Undo the last edit made to a file. - - To use the write command, you must specify `file_text` which will become the new content of the file. Be careful with - existing files! This is a full overwrite, so you must include everything - not just sections you are modifying. - - To use the str_replace command to edit multiple files, use the `diff` parameter with a unified diff. - To use the str_replace command to edit one file, you must specify both `old_str` and `new_str` - the `old_str` needs to exactly match one - unique section of the original file, including any whitespace. Make sure to include enough context that the match is not - ambiguous. The entire original string will be replaced with `new_str` - - When possible, batch file edits together by using a multi-file unified `diff` within a single str_replace tool call. - - To use the insert command, you must specify both `insert_line` (the line number after which to insert, 0 for beginning, -1 for end) - and `new_str` (the text to insert). - - +TEXT_EDITOR: +Commands: view|write|str_replace|insert|undo_edit +⚠️ write=full overwrite (include all content) +str_replace: diff for multi-file, or old_str+new_str (exact match incl. whitespace, avoid ambiguous) +insert: insert_line (0=start, -1=end) + new_str +Batch edits: use unified diff "#} }; - // Create comprehensive shell tool instructions - let common_shell_instructions = indoc! {r#" - Additional Shell Tool Instructions: - Execute a command in the shell. - - This will return the output and error concatenated into a single string, as - you would see from running on the command line. There will also be an indication - of if the command succeeded or failed. - - Avoid commands that produce a large amount of output, and consider piping those outputs to files. - - **Important**: Each shell command runs in its own process. Things like directory changes or - sourcing files do not persist between tool calls. So you may need to repeat them each time by - stringing together commands. - "#}; - - let windows_specific = indoc! {r#" - **Important**: For searching files and code: - - Preferred: Use ripgrep (`rg`) when available - it respects .gitignore and is fast: - - To locate a file by name: `rg --files | rg example.py` - - To locate content inside files: `rg 'class Example'` - - Alternative Windows commands (if ripgrep is not installed): - - To locate a file by name: `dir /s /b example.py` - - To locate content inside files: `findstr /s /i "class Example" *.py` - - Note: Alternative commands may show ignored/hidden files that should be excluded. - - - Multiple commands: Use && to chain commands, avoid newlines - - Example: `cd example && dir` or `activate.bat && pip install numpy` - - **Important**: Use forward slashes in paths (e.g., `C:/Users/name`) to avoid - escape character issues with backslashes, i.e. \n in a path could be - mistaken for a newline. - "#}; - - let unix_specific = indoc! {r#" - If you need to run a long lived command, background it - e.g. `uvicorn main:app &` so that - this tool does not run indefinitely. - - **Important**: Use ripgrep - `rg` - exclusively when you need to locate a file or a code reference, - other solutions may produce too large output because of hidden files! For example *do not* use `find` or `ls -r` - - List files by name: `rg --files | rg ` - - List files that contain a regex: `rg '' -l` - - - Multiple commands: Use && to chain commands, avoid newlines - - Example: `cd example && ls` or `source env/bin/activate && pip install numpy` - "#}; - + // Condensed shell tool instructions let shell_tool_desc = match os { - "windows" => format!("{}{}", common_shell_instructions, windows_specific), - _ => format!("{}{}", common_shell_instructions, unix_specific), + "windows" => indoc! {r#" +SHELL: stdout+stderr string | New process each call (no persist) | Max 400KB +Chain: && | Large output: pipe to files +Search (prefer rg): `rg --files|rg name`, `rg 'pattern'` +Alt Win: `dir /s /b file`, `findstr /s /i "text" *.py` (may show hidden) +⚠️ Use forward slashes (C:/Users) to avoid \n escape issues + "#}, + _ => indoc! {r#" +SHELL: stdout+stderr string | New process each call (no persist) | Max 400KB +Chain: && | Background: cmd & | Large output: pipe to files +Search (rg ONLY, no find/ls -r): `rg --files|rg name`, `rg 'pattern' -l` +Examples: `cd dir && ls`, `source venv && pip install` + "#}, }; // Return base instructions directly when no hints are found @@ -574,7 +472,7 @@ impl DeveloperServer { /// of the screen_capture tool. #[tool( name = "list_windows", - description = "List all available window titles that can be used with screen_capture. Returns a list of window titles that can be used with the window_title parameter of the screen_capture tool." + description = "Get available window titles for screen_capture." )] pub async fn list_windows(&self) -> Result { let windows = Window::all().map_err(|_| { @@ -606,7 +504,7 @@ impl DeveloperServer { /// Only one of display or window_title should be specified. #[tool( name = "screen_capture", - description = "Capture a screenshot of a specified display or window. You can capture either: 1. A full display (monitor) using the display parameter 2. A specific window by its title using the window_title parameter. Only one of display or window_title should be specified." + description = "Screenshot: display# XOR window_title (use only one)." )] pub async fn screen_capture( &self, diff --git a/crates/goose/src/agents/platform_tools.rs b/crates/goose/src/agents/platform_tools.rs index bdebac8ed9..1038a75f2e 100644 --- a/crates/goose/src/agents/platform_tools.rs +++ b/crates/goose/src/agents/platform_tools.rs @@ -12,14 +12,7 @@ pub const PLATFORM_MANAGE_SCHEDULE_TOOL_NAME: &str = "platform__manage_schedule" pub fn read_resource_tool() -> Tool { Tool::new( PLATFORM_READ_RESOURCE_TOOL_NAME.to_string(), - indoc! {r#" - Read a resource from an extension. - - Resources allow extensions to share data that provide context to LLMs, such as - files, database schemas, or application-specific information. This tool searches for the - resource URI in the provided extension, and reads in the resource content. If no extension - is provided, the tool will search all extensions for the resource. - "#}.to_string(), + "Read extension resource (files/schemas/data). Searches URI in extension or all if none specified.".to_string(), object!({ "type": "object", "required": ["uri"], @@ -40,15 +33,7 @@ pub fn read_resource_tool() -> Tool { pub fn list_resources_tool() -> Tool { Tool::new( PLATFORM_LIST_RESOURCES_TOOL_NAME.to_string(), - indoc! {r#" - List resources from an extension(s). - - Resources allow extensions to share data that provide context to LLMs, such as - files, database schemas, or application-specific information. This tool lists resources - in the provided extension, and returns a list for the user to browse. If no extension - is provided, the tool will search all extensions for the resource. - "#} - .to_string(), + "List extension resources. Returns browsable list. Searches all if no extension specified.".to_string(), object!({ "type": "object", "properties": { @@ -68,10 +53,7 @@ pub fn list_resources_tool() -> Tool { pub fn search_available_extensions_tool() -> Tool { Tool::new( PLATFORM_SEARCH_AVAILABLE_EXTENSIONS_TOOL_NAME.to_string(), - "Searches for additional extensions available to help complete tasks. - Use this tool when you're unable to find a specific feature or functionality you need to complete your task, or when standard approaches aren't working. - These extensions might provide the exact tools needed to solve your problem. - If you find a relevant one, consider using your tools to enable it.".to_string(), + "Find missing functionality/extensions. Enable found items via manage_extensions.".to_string(), object!({ "type": "object", "required": [], @@ -89,11 +71,7 @@ pub fn search_available_extensions_tool() -> Tool { pub fn manage_extensions_tool() -> Tool { Tool::new( PLATFORM_MANAGE_EXTENSIONS_TOOL_NAME.to_string(), - "Tool to manage extensions and tools in goose context. - Enable or disable extensions to help complete tasks. - Enable or disable an extension by providing the extension name. - " - .to_string(), + "Enable or disable extensions. Actions: enable|disable.".to_string(), object!({ "type": "object", "required": ["action", "extension_name"], @@ -114,22 +92,7 @@ pub fn manage_extensions_tool() -> Tool { pub fn manage_schedule_tool() -> Tool { Tool::new( PLATFORM_MANAGE_SCHEDULE_TOOL_NAME.to_string(), - indoc! {r#" - Manage scheduled recipe execution for this goose instance. - - Actions: - - "list": List all scheduled jobs - - "create": Create a new scheduled job from a recipe file - - "run_now": Execute a scheduled job immediately - - "pause": Pause a scheduled job - - "unpause": Resume a paused job - - "delete": Remove a scheduled job - - "kill": Terminate a currently running job - - "inspect": Get details about a running job - - "sessions": List execution history for a job - - "session_content": Get the full content (messages) of a specific session - "#} - .to_string(), + "Schedule recipes (this goose). Jobs: list|create[file]|delete. Control: run_now|pause|unpause|kill[proc]. View: inspect[job]|sessions|content.".to_string(), object!({ "type": "object", "required": ["action"], diff --git a/crates/goose/src/agents/recipe_tools/dynamic_task_tools.rs b/crates/goose/src/agents/recipe_tools/dynamic_task_tools.rs index 4174fc1055..e8fba02558 100644 --- a/crates/goose/src/agents/recipe_tools/dynamic_task_tools.rs +++ b/crates/goose/src/agents/recipe_tools/dynamic_task_tools.rs @@ -21,7 +21,7 @@ pub const DYNAMIC_TASK_TOOL_NAME_PREFIX: &str = "dynamic_task__create_task"; pub fn create_dynamic_task_tool() -> Tool { Tool::new( DYNAMIC_TASK_TOOL_NAME_PREFIX.to_string(), - "Create tasks with instructions or prompt. For simple tasks, only include the instructions field. Extensions control: omit field = use all current extensions; empty array [] = no extensions; array with names = only those extensions. Specify extensions as shortnames (the prefixes for your tools). Specify return_last_only as true and have your subagent summarize its work in its last message to conserve your own context. Optional: title, description, extensions, settings, retry, response schema, context, activities. Arrays for multiple tasks.".to_string(), + "Create tasks: req instructions|prompt. Ext: omit=all []=none [names]=only. return_last_only→summary. Opt: title/desc/settings/retry/response/context/activities. Multi→array.".to_string(), object!({ "type": "object", "properties": { diff --git a/crates/goose/src/agents/router_tools.rs b/crates/goose/src/agents/router_tools.rs index 22c29a2213..55efa7b60e 100644 --- a/crates/goose/src/agents/router_tools.rs +++ b/crates/goose/src/agents/router_tools.rs @@ -2,7 +2,6 @@ use super::platform_tools::{ PLATFORM_LIST_RESOURCES_TOOL_NAME, PLATFORM_MANAGE_EXTENSIONS_TOOL_NAME, PLATFORM_READ_RESOURCE_TOOL_NAME, PLATFORM_SEARCH_AVAILABLE_EXTENSIONS_TOOL_NAME, }; -use indoc::indoc; use rmcp::model::{Tool, ToolAnnotations}; use rmcp::object; @@ -11,18 +10,7 @@ pub const ROUTER_LLM_SEARCH_TOOL_NAME: &str = "router__llm_search"; pub fn llm_search_tool() -> Tool { Tool::new( ROUTER_LLM_SEARCH_TOOL_NAME.to_string(), - indoc! {r#" - Searches for relevant tools based on the user's messages. - Format a query to search for the most relevant tools based on the user's messages. - Pay attention to the keywords in the user's messages, especially the last message and potential tools they are asking for. - This tool should be invoked when the user's messages suggest they are asking for a tool to be run. - Use the extension_name parameter to filter tools by the appropriate extension. - For example, if the user is asking to list the files in the current directory, you filter for the "developer" extension. - Example: {"User": "list the files in the current directory", "Query": "list files in current directory", "Extension Name": "developer", "k": 5} - Extension name is not optional, it is required. - The returned result will be a list of tool names, descriptions, and schemas from which you, the agent can select the most relevant tool to invoke. - "#} - .to_string(), + "Search tools by user intent. Focus on keywords, especially last message. Required: extension_name to filter (e.g., \"developer\" for file ops). Returns tool names/descriptions/schemas for selection.".to_string(), object!({ "type": "object", "required": ["query", "extension_name"], diff --git a/crates/goose/src/agents/subagent_execution_tool/subagent_execute_task_tool.rs b/crates/goose/src/agents/subagent_execution_tool/subagent_execute_task_tool.rs index b70f71d3f2..9ddf5da6a6 100644 --- a/crates/goose/src/agents/subagent_execution_tool/subagent_execute_task_tool.rs +++ b/crates/goose/src/agents/subagent_execution_tool/subagent_execute_task_tool.rs @@ -16,21 +16,7 @@ pub const SUBAGENT_EXECUTE_TASK_TOOL_NAME: &str = "subagent__execute_task"; pub fn create_subagent_execute_task_tool() -> Tool { Tool::new( SUBAGENT_EXECUTE_TASK_TOOL_NAME, - "Only use the subagent__execute_task tool when you execute sub recipe task or dynamic task. - EXECUTION STRATEGY DECISION: - 1. If the tasks are created with execution_mode, use the execution_mode. - 2. Execute tasks sequentially unless user explicitly requests parallel execution. PARALLEL: User uses keywords like 'parallel', 'simultaneously', 'at the same time', 'concurrently' - - IMPLEMENTATION: - - Sequential execution: Call this tool multiple times, passing exactly ONE task per call - - Parallel execution: Call this tool once, passing an ARRAY of all tasks - - EXAMPLES: - User Intent Based: - - User: 'get weather and tell me a joke' → Sequential (2 separate tool calls, 1 task each) - - User: 'get weather and joke in parallel' → Parallel (1 tool call with array of 2 tasks) - - User: 'run these simultaneously' → Parallel (1 tool call with task array) - - User: 'do task A then task B' → Sequential (2 separate tool calls)", + "Execute tasks. Mode: task's mode>SEQ(default)>PAR(if parallel/simultaneous/concurrent). SEQ: multi-call/1-task. PAR: 1-call/array.", object!({ "type": "object", "properties": { diff --git a/crates/goose/src/agents/todo_extension.rs b/crates/goose/src/agents/todo_extension.rs index ac29a96c6e..92e659af6c 100644 --- a/crates/goose/src/agents/todo_extension.rs +++ b/crates/goose/src/agents/todo_extension.rs @@ -47,16 +47,17 @@ impl TodoClient { instructions: Some(indoc! {r#" Task Management - Use todo_read and todo_write for tasks with 2+ steps, multiple files/components, or uncertain scope. + Use todo_write for tasks with 2+ steps, multiple files/components, or uncertain scope. + Your TODO content is automatically available in your context. Workflow: - - Start: read → write checklist - - During: read → update progress + - Start: write initial checklist + - During: update progress - End: verify all complete - Warning: todo_write overwrites entirely; always todo_read first (skipping is an error) + Warning: todo_write overwrites entirely; always include ALL content you want to keep - Keep items short, specific, action-oriented. Not using the todo tools for complex tasks is an error. + Keep items short, specific, action-oriented. Not using the todo tool for complex tasks is an error. Template: - [ ] Implement feature X @@ -157,14 +158,7 @@ impl TodoClient { vec![ Tool::new( "todo_read".to_string(), - indoc! {r#" - Read the entire TODO file content. - - This tool reads the complete TODO file and returns its content as a string. - Use this to view current tasks, notes, and any other information stored in the TODO file. - - The tool will return an error if the TODO file doesn't exist or cannot be read. - "#}.to_string(), + "Read TODO content. Returns as string.".to_string(), object!({ "type": "object", "properties": {}, @@ -179,18 +173,7 @@ impl TodoClient { }), Tool::new( "todo_write".to_string(), - indoc! {r#" - Write or overwrite the entire TODO file content. - - This tool replaces the complete TODO file content with the provided string. - Use this to update tasks, add new items, or reorganize the TODO file. - - WARNING: This operation completely replaces the file content. Make sure to include - all content you want to keep, not just the changes. - - The tool will create the TODO file if it doesn't exist, or overwrite it if it does. - Returns an error if the file cannot be written due to permissions or other I/O issues. - "#}.to_string(), + "Overwrite TODO. WARNING: Complete replacement - include ALL content to keep.".to_string(), object!({ "type": "object", "properties": { diff --git a/crates/goose/src/prompts/system.md b/crates/goose/src/prompts/system.md index d5200172c4..de3242c541 100644 --- a/crates/goose/src/prompts/system.md +++ b/crates/goose/src/prompts/system.md @@ -1,74 +1,53 @@ -You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal. -goose is being developed as an open-source software project. +You are goose, an AI agent created by Block (Square, CashApp, Tidal). Open-source project. -The current date is {{current_date_time}}. +Current date: {{current_date_time}} -goose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o, -claude-sonnet-4, o1, llama-3.2, deepseek-r1, etc). -These models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10 -months prior to the current date. +Compatible with tool-calling LLMs: gpt-4o, claude-sonnet-4, o1, llama-3.2, deepseek-r1. +Knowledge cutoff: typically 5-10 months prior. -# Extensions +## Extensions -Extensions allow other applications to provide context to goose. Extensions connect goose to different data sources and -tools. -You are capable of dynamically plugging into new extensions and learning how to use them. You solve higher level -problems using the tools in these extensions, and can interact with multiple at once. -Use the search_available_extensions tool to find additional extensions to enable to help with your task. To enable -extensions, use the enable_extension tool and provide the extension_name. You should only enable extensions found from -the search_available_extensions tool. +Extensions connect goose to data sources and tools. Load multiple simultaneously. +To add: use `search_available_extensions`, then `enable_extension` with names from search results only. {% if (extensions is defined) and extensions %} -Because you dynamically load extensions, your conversation history may refer -to interactions with extensions that are not currently active. The currently -active extensions are below. Each of these extensions provides tools that are -in your tool specification. - +### Active Extensions {% for extension in extensions %} - -## {{extension.name}} - +#### {{extension.name}} {% if extension.has_resources %} -{{extension.name}} supports resources, you can use platform__read_resource, -and platform__list_resources on this extension. +Resources: `platform__read_resource`, `platform__list_resources` +{% endif %} +{% if extension.instructions %} +{{extension.instructions}} {% endif %} -{% if extension.instructions %}### Instructions -{{extension.instructions}}{% endif %} {% endfor %} - {% else %} -No extensions are defined. You should let the user know that they should add extensions. +No extensions defined. Inform user to add extensions. {% endif %} {% if suggest_disable is defined %} - -# Suggestion - +## Suggestion {{suggest_disable}} {% endif %} {{tool_selection_strategy}} -# sub agents +## Subagents -Execute self contained tasks where step-by-step visibility is not important through subagents. +Execute self-contained tasks via `dynamic_task__create_task` when step-by-step visibility isn't needed. -- Delegate via `dynamic_task__create_task` for: result-only operations, parallelizable work, multi-part requests, - verification, exploration -- Parallel subagents for multiple operations, single subagents for independent work -- Explore solutions in parallel — launch parallel subagents with different approaches (if non-interfering) -- Provide all needed context — subagents cannot see your context -- Use extension filters to limit resource access -- Use return_last_only when only a summary or simple answer is required — inform subagent of this choice. +Use for: result-only operations, parallelizable work, multi-part requests, verification, exploration. -# Response Guidelines +Guidelines: +- Provide all context (subagents cannot access your conversation) +- Run parallel for non-interfering approaches +- Use `return_last_only=true` for summaries +- Apply extension filters to limit resource access -- Use Markdown formatting for all responses. -- Follow best practices for Markdown, including: - - Using headers for organization. - - Bullet points for lists. - - Links formatted correctly, either as linked text (e.g., [this is linked text](https://example.com)) or automatic - links using angle brackets (e.g., ). -- For code examples, use fenced code blocks by placing triple backticks (` ``` `) before and after the code. Include the - language identifier after the opening backticks (e.g., ` ```python `) to enable syntax highlighting. -- Ensure clarity, conciseness, and proper formatting to enhance readability and usability. +## Response Format + +Use Markdown formatting: +- Headers for structure +- Bullet points for lists +- Links: `[text](url)` or `` +- Code blocks: ` ```language ` with syntax highlighting