Display what the tool is doing (#27120)
<img width="639" alt="Screenshot 2025-03-19 at 4 56 47 PM" src="https://github.com/user-attachments/assets/b997f04d-4aff-4070-87b1-ffdb61019bd1" /> Release Notes: - N/A --------- Co-authored-by: Agus Zubiaga <hi@aguz.me>
This commit is contained in:
parent
aae81fd54c
commit
e3578fc44a
18 changed files with 349 additions and 132 deletions
|
@ -32,6 +32,13 @@ impl Tool for BashTool {
|
|||
serde_json::to_value(&schema).unwrap()
|
||||
}
|
||||
|
||||
fn ui_text(&self, input: &serde_json::Value) -> String {
|
||||
match serde_json::from_value::<BashToolInput>(input.clone()) {
|
||||
Ok(input) => format!("`$ {}`", input.command),
|
||||
Err(_) => "Run bash command".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
input: serde_json::Value,
|
||||
|
|
|
@ -39,6 +39,13 @@ impl Tool for DeletePathTool {
|
|||
serde_json::to_value(&schema).unwrap()
|
||||
}
|
||||
|
||||
fn ui_text(&self, input: &serde_json::Value) -> String {
|
||||
match serde_json::from_value::<DeletePathToolInput>(input.clone()) {
|
||||
Ok(input) => format!("Delete “`{}`”", input.path),
|
||||
Err(_) => "Delete path".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
input: serde_json::Value,
|
||||
|
@ -59,13 +66,12 @@ impl Tool for DeletePathTool {
|
|||
{
|
||||
Some(deletion_task) => cx.background_spawn(async move {
|
||||
match deletion_task.await {
|
||||
Ok(()) => Ok(format!("Deleted {}", &path_str)),
|
||||
Err(err) => Err(anyhow!("Failed to delete {}: {}", &path_str, err)),
|
||||
Ok(()) => Ok(format!("Deleted {path_str}")),
|
||||
Err(err) => Err(anyhow!("Failed to delete {path_str}: {err}")),
|
||||
}
|
||||
}),
|
||||
None => Task::ready(Err(anyhow!(
|
||||
"Couldn't delete {} because that path isn't in this project.",
|
||||
path_str
|
||||
"Couldn't delete {path_str} because that path isn't in this project."
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,17 @@ impl Tool for DiagnosticsTool {
|
|||
serde_json::to_value(&schema).unwrap()
|
||||
}
|
||||
|
||||
fn ui_text(&self, input: &serde_json::Value) -> String {
|
||||
if let Some(path) = serde_json::from_value::<DiagnosticsToolInput>(input.clone())
|
||||
.ok()
|
||||
.and_then(|input| input.path)
|
||||
{
|
||||
format!("Check diagnostics for “`{}`”", path.display())
|
||||
} else {
|
||||
"Check project diagnostics".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
input: serde_json::Value,
|
||||
|
@ -54,14 +65,15 @@ impl Tool for DiagnosticsTool {
|
|||
_action_log: Entity<ActionLog>,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<String>> {
|
||||
let input = match serde_json::from_value::<DiagnosticsToolInput>(input) {
|
||||
Ok(input) => input,
|
||||
Err(err) => return Task::ready(Err(anyhow!(err))),
|
||||
};
|
||||
|
||||
if let Some(path) = input.path {
|
||||
if let Some(path) = serde_json::from_value::<DiagnosticsToolInput>(input)
|
||||
.ok()
|
||||
.and_then(|input| input.path)
|
||||
{
|
||||
let Some(project_path) = project.read(cx).find_project_path(&path, cx) else {
|
||||
return Task::ready(Err(anyhow!("Could not find path in project")));
|
||||
return Task::ready(Err(anyhow!(
|
||||
"Could not find path {} in project",
|
||||
path.display()
|
||||
)));
|
||||
};
|
||||
let buffer = project.update(cx, |project, cx| project.open_buffer(project_path, cx));
|
||||
|
||||
|
|
|
@ -24,10 +24,7 @@ use util::ResultExt;
|
|||
pub struct EditFilesToolInput {
|
||||
/// High-level edit instructions. These will be interpreted by a smaller
|
||||
/// model, so explain the changes you want that model to make and which
|
||||
/// file paths need changing.
|
||||
///
|
||||
/// The description should be concise and clear. We will show this
|
||||
/// description to the user as well.
|
||||
/// file paths need changing. The description should be concise and clear.
|
||||
///
|
||||
/// WARNING: When specifying which file paths need changing, you MUST
|
||||
/// start each path with one of the project's root directories.
|
||||
|
@ -58,6 +55,21 @@ pub struct EditFilesToolInput {
|
|||
/// Notice how we never specify code snippets in the instructions!
|
||||
/// </example>
|
||||
pub edit_instructions: String,
|
||||
|
||||
/// A user-friendly description of what changes are being made.
|
||||
/// This will be shown to the user in the UI to describe the edit operation. The screen real estate for this UI will be extremely
|
||||
/// constrained, so make the description extremely terse.
|
||||
///
|
||||
/// <example>
|
||||
/// For fixing a broken authentication system:
|
||||
/// "Fix auth bug in login flow"
|
||||
/// </example>
|
||||
///
|
||||
/// <example>
|
||||
/// For adding unit tests to a module:
|
||||
/// "Add tests for user profile logic"
|
||||
/// </example>
|
||||
pub display_description: String,
|
||||
}
|
||||
|
||||
pub struct EditFilesTool;
|
||||
|
@ -76,6 +88,13 @@ impl Tool for EditFilesTool {
|
|||
serde_json::to_value(&schema).unwrap()
|
||||
}
|
||||
|
||||
fn ui_text(&self, input: &serde_json::Value) -> String {
|
||||
match serde_json::from_value::<EditFilesToolInput>(input.clone()) {
|
||||
Ok(input) => input.display_description,
|
||||
Err(_) => "Edit files".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
input: serde_json::Value,
|
||||
|
|
|
@ -122,6 +122,13 @@ impl Tool for FetchTool {
|
|||
serde_json::to_value(&schema).unwrap()
|
||||
}
|
||||
|
||||
fn ui_text(&self, input: &serde_json::Value) -> String {
|
||||
match serde_json::from_value::<FetchToolInput>(input.clone()) {
|
||||
Ok(input) => format!("Fetch `{}`", input.url),
|
||||
Err(_) => "Fetch URL".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
input: serde_json::Value,
|
||||
|
|
|
@ -50,6 +50,13 @@ impl Tool for ListDirectoryTool {
|
|||
serde_json::to_value(&schema).unwrap()
|
||||
}
|
||||
|
||||
fn ui_text(&self, input: &serde_json::Value) -> String {
|
||||
match serde_json::from_value::<ListDirectoryToolInput>(input.clone()) {
|
||||
Ok(input) => format!("List the `{}` directory's contents", input.path.display()),
|
||||
Err(_) => "List directory".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
input: serde_json::Value,
|
||||
|
@ -64,7 +71,10 @@ impl Tool for ListDirectoryTool {
|
|||
};
|
||||
|
||||
let Some(project_path) = project.read(cx).find_project_path(&input.path, cx) else {
|
||||
return Task::ready(Err(anyhow!("Path not found in project")));
|
||||
return Task::ready(Err(anyhow!(
|
||||
"Path {} not found in project",
|
||||
input.path.display()
|
||||
)));
|
||||
};
|
||||
let Some(worktree) = project
|
||||
.read(cx)
|
||||
|
@ -79,7 +89,7 @@ impl Tool for ListDirectoryTool {
|
|||
};
|
||||
|
||||
if !entry.is_dir() {
|
||||
return Task::ready(Err(anyhow!("{} is a file.", input.path.display())));
|
||||
return Task::ready(Err(anyhow!("{} is not a directory.", input.path.display())));
|
||||
}
|
||||
|
||||
let mut output = String::new();
|
||||
|
|
|
@ -40,6 +40,10 @@ impl Tool for NowTool {
|
|||
serde_json::to_value(&schema).unwrap()
|
||||
}
|
||||
|
||||
fn ui_text(&self, _input: &serde_json::Value) -> String {
|
||||
"Get current time".to_string()
|
||||
}
|
||||
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
input: serde_json::Value,
|
||||
|
|
|
@ -48,6 +48,13 @@ impl Tool for PathSearchTool {
|
|||
serde_json::to_value(&schema).unwrap()
|
||||
}
|
||||
|
||||
fn ui_text(&self, input: &serde_json::Value) -> String {
|
||||
match serde_json::from_value::<PathSearchToolInput>(input.clone()) {
|
||||
Ok(input) => format!("Find paths matching “`{}`”", input.glob),
|
||||
Err(_) => "Search paths".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
input: serde_json::Value,
|
||||
|
@ -62,7 +69,7 @@ impl Tool for PathSearchTool {
|
|||
};
|
||||
let path_matcher = match PathMatcher::new(&[glob.clone()]) {
|
||||
Ok(matcher) => matcher,
|
||||
Err(err) => return Task::ready(Err(anyhow!("Invalid glob: {}", err))),
|
||||
Err(err) => return Task::ready(Err(anyhow!("Invalid glob: {err}"))),
|
||||
};
|
||||
let snapshots: Vec<Snapshot> = project
|
||||
.read(cx)
|
||||
|
|
|
@ -53,6 +53,13 @@ impl Tool for ReadFileTool {
|
|||
serde_json::to_value(&schema).unwrap()
|
||||
}
|
||||
|
||||
fn ui_text(&self, input: &serde_json::Value) -> String {
|
||||
match serde_json::from_value::<ReadFileToolInput>(input.clone()) {
|
||||
Ok(input) => format!("Read file `{}`", input.path.display()),
|
||||
Err(_) => "Read file".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
input: serde_json::Value,
|
||||
|
@ -67,7 +74,10 @@ impl Tool for ReadFileTool {
|
|||
};
|
||||
|
||||
let Some(project_path) = project.read(cx).find_project_path(&input.path, cx) else {
|
||||
return Task::ready(Err(anyhow!("Path not found in project")));
|
||||
return Task::ready(Err(anyhow!(
|
||||
"Path {} not found in project",
|
||||
&input.path.display()
|
||||
)));
|
||||
};
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
|
|
|
@ -22,10 +22,17 @@ pub struct RegexSearchToolInput {
|
|||
/// Optional starting position for paginated results (0-based).
|
||||
/// When not provided, starts from the beginning.
|
||||
#[serde(default)]
|
||||
pub offset: Option<usize>,
|
||||
pub offset: Option<u32>,
|
||||
}
|
||||
|
||||
const RESULTS_PER_PAGE: usize = 20;
|
||||
impl RegexSearchToolInput {
|
||||
/// Which page of search results this is.
|
||||
pub fn page(&self) -> u32 {
|
||||
1 + (self.offset.unwrap_or(0) / RESULTS_PER_PAGE)
|
||||
}
|
||||
}
|
||||
|
||||
const RESULTS_PER_PAGE: u32 = 20;
|
||||
|
||||
pub struct RegexSearchTool;
|
||||
|
||||
|
@ -43,6 +50,24 @@ impl Tool for RegexSearchTool {
|
|||
serde_json::to_value(&schema).unwrap()
|
||||
}
|
||||
|
||||
fn ui_text(&self, input: &serde_json::Value) -> String {
|
||||
match serde_json::from_value::<RegexSearchToolInput>(input.clone()) {
|
||||
Ok(input) => {
|
||||
let page = input.page();
|
||||
|
||||
if page > 1 {
|
||||
format!(
|
||||
"Get page {page} of search results for regex “`{}`”",
|
||||
input.regex
|
||||
)
|
||||
} else {
|
||||
format!("Search files for regex “`{}`”", input.regex)
|
||||
}
|
||||
}
|
||||
Err(_) => "Search with regex".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
input: serde_json::Value,
|
||||
|
@ -154,7 +179,7 @@ impl Tool for RegexSearchTool {
|
|||
offset + matches_found,
|
||||
offset + RESULTS_PER_PAGE,
|
||||
))
|
||||
} else {
|
||||
} else {
|
||||
Ok(format!("Found {matches_found} matches:\n{output}"))
|
||||
}
|
||||
})
|
||||
|
|
|
@ -31,6 +31,10 @@ impl Tool for ThinkingTool {
|
|||
serde_json::to_value(&schema).unwrap()
|
||||
}
|
||||
|
||||
fn ui_text(&self, _input: &serde_json::Value) -> String {
|
||||
"Thinking".to_string()
|
||||
}
|
||||
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
input: serde_json::Value,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue