From 9be7934f1245f608aed3b3c0b692c416e03ba3db Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 12 Mar 2025 16:51:29 -0400 Subject: [PATCH] Add Bash tool (#26597) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Screenshot 2025-03-12 at 4 24 18 PM Screenshot 2025-03-12 at 4 24 36 PM Release Notes: - N/A --- crates/assistant_tools/src/assistant_tools.rs | 3 + crates/assistant_tools/src/bash_tool.rs | 70 +++++++++++++++++++ .../src/bash_tool/description.md | 1 + 3 files changed, 74 insertions(+) create mode 100644 crates/assistant_tools/src/bash_tool.rs create mode 100644 crates/assistant_tools/src/bash_tool/description.md diff --git a/crates/assistant_tools/src/assistant_tools.rs b/crates/assistant_tools/src/assistant_tools.rs index 22ae7a0a3a3953848f97614d3167b8df5aac7c13..a790398580617d41596861febf0425cb6f6e9a30 100644 --- a/crates/assistant_tools/src/assistant_tools.rs +++ b/crates/assistant_tools/src/assistant_tools.rs @@ -1,3 +1,4 @@ +mod bash_tool; mod delete_path_tool; mod edit_files_tool; mod list_directory_tool; @@ -8,6 +9,7 @@ mod regex_search; use assistant_tool::ToolRegistry; use gpui::App; +use crate::bash_tool::BashTool; use crate::delete_path_tool::DeletePathTool; use crate::edit_files_tool::EditFilesTool; use crate::list_directory_tool::ListDirectoryTool; @@ -25,4 +27,5 @@ pub fn init(cx: &mut App) { registry.register_tool(EditFilesTool); registry.register_tool(RegexSearchTool); registry.register_tool(DeletePathTool); + registry.register_tool(BashTool); } diff --git a/crates/assistant_tools/src/bash_tool.rs b/crates/assistant_tools/src/bash_tool.rs new file mode 100644 index 0000000000000000000000000000000000000000..bac4b49c863256afd9a495674cd53c51f4517ae6 --- /dev/null +++ b/crates/assistant_tools/src/bash_tool.rs @@ -0,0 +1,70 @@ +use anyhow::{anyhow, Result}; +use assistant_tool::Tool; +use gpui::{App, Entity, Task}; +use language_model::LanguageModelRequestMessage; +use project::Project; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; + +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +pub struct BashToolInput { + /// The bash command to execute as a one-liner. + command: String, +} + +pub struct BashTool; + +impl Tool for BashTool { + fn name(&self) -> String { + "bash".to_string() + } + + fn description(&self) -> String { + include_str!("./bash_tool/description.md").to_string() + } + + fn input_schema(&self) -> serde_json::Value { + let schema = schemars::schema_for!(BashToolInput); + serde_json::to_value(&schema).unwrap() + } + + fn run( + self: Arc, + input: serde_json::Value, + _messages: &[LanguageModelRequestMessage], + _project: Entity, + cx: &mut App, + ) -> Task> { + let input: BashToolInput = match serde_json::from_value(input) { + Ok(input) => input, + Err(err) => return Task::ready(Err(anyhow!(err))), + }; + + cx.spawn(|_| async move { + // Add 2>&1 to merge stderr into stdout for proper interleaving + let command = format!("{} 2>&1", input.command); + + // Spawn a blocking task to execute the command + let output = futures::executor::block_on(async { + std::process::Command::new("bash") + .arg("-c") + .arg(&command) + .output() + .map_err(|err| anyhow!("Failed to execute bash command: {}", err)) + })?; + + let output_string = String::from_utf8_lossy(&output.stdout).to_string(); + + if output.status.success() { + Ok(output_string) + } else { + Ok(format!( + "Command failed with exit code {}\n{}", + output.status.code().unwrap_or(-1), + &output_string + )) + } + }) + } +} diff --git a/crates/assistant_tools/src/bash_tool/description.md b/crates/assistant_tools/src/bash_tool/description.md new file mode 100644 index 0000000000000000000000000000000000000000..596bbceae0d276f684660639df2404ced032ecaa --- /dev/null +++ b/crates/assistant_tools/src/bash_tool/description.md @@ -0,0 +1 @@ +Executes a bash one-liner and returns the combined output. This tool spawns a bash process, combines stdout and stderr into one interleaved stream as they are produced (preserving the order of writes), and captures that stream into a string which is returned. Use this tool when you need to run shell commands to get information about the system or process files.