Cargo.lock 🔗
@@ -724,6 +724,7 @@ dependencies = [
"itertools 0.14.0",
"language",
"language_model",
+ "open",
"project",
"rand 0.8.5",
"release_channel",
Richard Feldman created
I've seen models try to run `open` in Bash. This is a cross-platform
version of that.
<img width="634" alt="Screenshot 2025-03-26 at 10 27 40 AM"
src="https://github.com/user-attachments/assets/b18cb50f-6e2f-4770-b15c-1040916a420a"
/>
Release Notes:
- N/A
Cargo.lock | 1
Cargo.toml | 1
crates/assistant_tools/Cargo.toml | 1
crates/assistant_tools/src/assistant_tools.rs | 3
crates/assistant_tools/src/open_tool.rs | 68 +++++++++++++++
crates/assistant_tools/src/open_tool/description.md | 6 +
6 files changed, 80 insertions(+)
@@ -724,6 +724,7 @@ dependencies = [
"itertools 0.14.0",
"language",
"language_model",
+ "open",
"project",
"rand 0.8.5",
"release_channel",
@@ -470,6 +470,7 @@ mlua = { version = "0.10", features = ["lua54", "vendored", "async", "send"] }
nanoid = "0.4"
nbformat = { version = "0.10.0" }
nix = "0.29"
+open = "5.0.0"
num-format = "0.4.4"
ordered-float = "2.1.1"
palette = { version = "0.7.5", default-features = false, features = ["std"] }
@@ -35,6 +35,7 @@ ui.workspace = true
util.workspace = true
workspace.workspace = true
worktree.workspace = true
+open = { workspace = true }
[dev-dependencies]
collections = { workspace = true, features = ["test-support"] }
@@ -10,6 +10,7 @@ mod find_replace_file_tool;
mod list_directory_tool;
mod move_path_tool;
mod now_tool;
+mod open_tool;
mod path_search_tool;
mod read_file_tool;
mod regex_search_tool;
@@ -34,6 +35,7 @@ use crate::fetch_tool::FetchTool;
use crate::find_replace_file_tool::FindReplaceFileTool;
use crate::list_directory_tool::ListDirectoryTool;
use crate::now_tool::NowTool;
+use crate::open_tool::OpenTool;
use crate::path_search_tool::PathSearchTool;
use crate::read_file_tool::ReadFileTool;
use crate::regex_search_tool::RegexSearchTool;
@@ -55,6 +57,7 @@ pub fn init(http_client: Arc<HttpClientWithUrl>, cx: &mut App) {
registry.register_tool(EditFilesTool);
registry.register_tool(ListDirectoryTool);
registry.register_tool(NowTool);
+ registry.register_tool(OpenTool);
registry.register_tool(PathSearchTool);
registry.register_tool(ReadFileTool);
registry.register_tool(RegexSearchTool);
@@ -0,0 +1,68 @@
+use anyhow::{anyhow, Context as _, Result};
+use assistant_tool::{ActionLog, Tool};
+use gpui::{App, AppContext, Entity, Task};
+use language_model::LanguageModelRequestMessage;
+use project::Project;
+use schemars::JsonSchema;
+use serde::{Deserialize, Serialize};
+use std::sync::Arc;
+use ui::IconName;
+use util::markdown::MarkdownString;
+
+#[derive(Debug, Serialize, Deserialize, JsonSchema)]
+pub struct OpenToolInput {
+ /// The path or URL to open with the default application.
+ path_or_url: String,
+}
+
+pub struct OpenTool;
+
+impl Tool for OpenTool {
+ fn name(&self) -> String {
+ "open".to_string()
+ }
+
+ fn needs_confirmation(&self) -> bool {
+ true
+ }
+
+ fn description(&self) -> String {
+ include_str!("./open_tool/description.md").to_string()
+ }
+
+ fn icon(&self) -> IconName {
+ IconName::ExternalLink
+ }
+
+ fn input_schema(&self) -> serde_json::Value {
+ let schema = schemars::schema_for!(OpenToolInput);
+ serde_json::to_value(&schema).unwrap()
+ }
+
+ fn ui_text(&self, input: &serde_json::Value) -> String {
+ match serde_json::from_value::<OpenToolInput>(input.clone()) {
+ Ok(input) => format!("Open `{}`", MarkdownString::escape(&input.path_or_url)),
+ Err(_) => "Open file or URL".to_string(),
+ }
+ }
+
+ fn run(
+ self: Arc<Self>,
+ input: serde_json::Value,
+ _messages: &[LanguageModelRequestMessage],
+ _project: Entity<Project>,
+ _action_log: Entity<ActionLog>,
+ cx: &mut App,
+ ) -> Task<Result<String>> {
+ let input: OpenToolInput = match serde_json::from_value(input) {
+ Ok(input) => input,
+ Err(err) => return Task::ready(Err(anyhow!(err))),
+ };
+
+ cx.background_spawn(async move {
+ open::that(&input.path_or_url).context("Failed to open URL or file path")?;
+
+ Ok(format!("Successfully opened {}", input.path_or_url))
+ })
+ }
+}
@@ -0,0 +1,6 @@
+This tool opens a file or URL with the default application associated with it on the user's operating system:
+- On macOS, it's equivalent to the `open` command
+- On Windows, it's equivalent to `start`
+- On Linux, it uses something like `xdg-open`, `gio open`, `gnome-open`, `kde-open`, `wslview` as appropriate
+
+For example, it can open a web browser with a URL, open a PDF file with the default PDF viewer, etc.