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
10 changes: 9 additions & 1 deletion assets/settings/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -1107,13 +1107,18 @@
"create_directory": true,
"delete_path": true,
"diagnostics": true,
"apply_code_action": true,
"edit_file": true,
"fetch": true,
"find_path": true,
"find_references": true,
"get_code_actions": true,
"go_to_definition": true,
"list_directory": true,
"project_notifications": false,
"move_path": true,
"now": true,
"find_path": true,
"rename_symbol": true,
"read_file": true,
"restore_file_from_disk": true,
"save_file": true,
Expand All @@ -1137,6 +1142,9 @@
"project_notifications": false,
"now": true,
"find_path": true,
"find_references": true,
"get_code_actions": true,
"go_to_definition": true,
"read_file": true,
"open": true,
"grep": true,
Expand Down
26 changes: 21 additions & 5 deletions crates/agent/src/thread.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use crate::{
ContextServerRegistry, CopyPathTool, CreateDirectoryTool, DbLanguageModel, DbThread,
DeletePathTool, DiagnosticsTool, EditFileTool, FetchTool, FindPathTool, GrepTool,
ListDirectoryTool, MovePathTool, NowTool, OpenTool, ProjectSnapshot, ReadFileTool,
ApplyCodeActionTool, CodeActionStore, ContextServerRegistry, CopyPathTool, CreateDirectoryTool,
DbLanguageModel, DbThread, DeletePathTool, DiagnosticsTool, EditFileTool, FetchTool,
FindPathTool, FindReferencesTool, GetCodeActionsTool, GoToDefinitionTool, GrepTool,
ListDirectoryTool, MovePathTool, NowTool, OpenTool, ProjectSnapshot, ReadFileTool, RenameTool,
RestoreFileFromDiskTool, SaveFileTool, SpawnAgentTool, SystemPromptTemplate, Template,
Templates, TerminalTool, ToolPermissionDecision, UpdatePlanTool, WebSearchTool,
decide_permission_from_settings,
};
use acp_thread::{MentionUri, UserMessageId};
use action_log::ActionLog;
use feature_flags::{FeatureFlagAppExt as _, UpdatePlanToolFeatureFlag};
use feature_flags::{FeatureFlagAppExt as _, LspToolFeatureFlag, UpdatePlanToolFeatureFlag};

use agent_client_protocol::schema as acp;
use agent_settings::{
Expand Down Expand Up @@ -1542,7 +1543,6 @@ impl Thread {
self.project.clone(),
self.action_log.clone(),
));
self.add_tool(DiagnosticsTool::new(self.project.clone()));
self.add_tool(EditFileTool::new(
self.project.clone(),
cx.weak_entity(),
Expand All @@ -1569,6 +1569,22 @@ impl Thread {
self.add_tool(TerminalTool::new(self.project.clone(), environment.clone()));
self.add_tool(WebSearchTool);

self.add_tool(DiagnosticsTool::new(self.project.clone()));
if cx.has_flag::<LspToolFeatureFlag>() {
let code_action_store: CodeActionStore = cx.new(|_cx| None);
self.add_tool(FindReferencesTool::new(self.project.clone()));
self.add_tool(GetCodeActionsTool::new(
self.project.clone(),
code_action_store.clone(),
));
self.add_tool(ApplyCodeActionTool::new(
self.project.clone(),
code_action_store,
));
self.add_tool(GoToDefinitionTool::new(self.project.clone()));
self.add_tool(RenameTool::new(self.project.clone()));
}

if self.depth() < MAX_SUBAGENT_DEPTH {
self.add_tool(SpawnAgentTool::new(environment));
}
Expand Down
17 changes: 17 additions & 0 deletions crates/agent/src/tools.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod apply_code_action_tool;
mod context_server_registry;
mod copy_path_tool;
mod create_directory_tool;
Expand All @@ -8,15 +9,20 @@ mod edit_file_tool;
mod evals;
mod fetch_tool;
mod find_path_tool;
mod find_references_tool;
mod get_code_actions_tool;
mod go_to_definition_tool;
mod grep_tool;
mod list_directory_tool;
mod move_path_tool;
mod now_tool;
mod open_tool;
mod read_file_tool;
mod rename_tool;
mod restore_file_from_disk_tool;
mod save_file_tool;
mod spawn_agent_tool;
mod symbol_locator;
mod terminal_tool;
mod tool_edit_parser;
mod tool_permissions;
Expand All @@ -26,6 +32,7 @@ mod web_search_tool;
use crate::AgentTool;
use language_model::{LanguageModelRequestTool, LanguageModelToolSchemaFormat};

pub use apply_code_action_tool::*;
pub use context_server_registry::*;
pub use copy_path_tool::*;
pub use create_directory_tool::*;
Expand All @@ -34,15 +41,20 @@ pub use diagnostics_tool::*;
pub use edit_file_tool::*;
pub use fetch_tool::*;
pub use find_path_tool::*;
pub use find_references_tool::*;
pub use get_code_actions_tool::*;
pub use go_to_definition_tool::*;
pub use grep_tool::*;
pub use list_directory_tool::*;
pub use move_path_tool::*;
pub use now_tool::*;
pub use open_tool::*;
pub use read_file_tool::*;
pub use rename_tool::*;
pub use restore_file_from_disk_tool::*;
pub use save_file_tool::*;
pub use spawn_agent_tool::*;
pub use symbol_locator::*;
pub use terminal_tool::*;
pub use tool_permissions::*;
pub use update_plan_tool::*;
Expand Down Expand Up @@ -117,19 +129,24 @@ macro_rules! tools {
}

tools! {
ApplyCodeActionTool,
CopyPathTool,
CreateDirectoryTool,
DeletePathTool,
DiagnosticsTool,
EditFileTool,
FetchTool,
FindPathTool,
FindReferencesTool,
GetCodeActionsTool,
GoToDefinitionTool,
GrepTool,
ListDirectoryTool,
MovePathTool,
NowTool,
OpenTool,
ReadFileTool,
RenameTool,
RestoreFileFromDiskTool,
SaveFileTool,
SpawnAgentTool,
Expand Down
145 changes: 145 additions & 0 deletions crates/agent/src/tools/apply_code_action_tool.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use std::fmt::Write;
use std::sync::Arc;

use agent_client_protocol::schema as acp;
use gpui::{App, Entity, SharedString, Task};
use project::Project;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use super::symbol_locator::CodeActionStore;
use crate::{AgentTool, ToolCallEventStream, ToolInput};

/// Applies a code action previously retrieved by get_code_actions.
///
/// You must call get_code_actions first to get the list of available actions,
/// then use the number from that list to choose which action to apply.
///
/// After applying a code action, the list is cleared. If you want to apply
/// another action, call get_code_actions again.
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct ApplyCodeActionToolInput {
/// The 1-based index of the code action to apply, from the list
/// returned by get_code_actions.
pub index: u32,
}

pub struct ApplyCodeActionTool {
project: Entity<Project>,
code_action_store: CodeActionStore,
}

impl ApplyCodeActionTool {
pub fn new(project: Entity<Project>, code_action_store: CodeActionStore) -> Self {
Self {
project,
code_action_store,
}
}
}

impl AgentTool for ApplyCodeActionTool {
type Input = ApplyCodeActionToolInput;
type Output = String;

const NAME: &'static str = "apply_code_action";

fn kind() -> acp::ToolKind {
acp::ToolKind::Other
}

fn initial_title(
&self,
input: Result<Self::Input, serde_json::Value>,
cx: &mut App,
) -> SharedString {
if let Ok(input) = input {
let title = self
.code_action_store
.read(cx)
.as_ref()
.and_then(|pending| {
let index = input.index.checked_sub(1)? as usize;
Some(pending.actions.get(index)?.lsp_action.title().to_string())
});
if let Some(title) = title {
format!("Apply code action: {title}").into()
} else {
format!("Apply code action #{}", input.index).into()
}
} else {
"Apply code action".into()
}
}

fn run(
self: Arc<Self>,
input: ToolInput<Self::Input>,
_event_stream: ToolCallEventStream,
cx: &mut App,
) -> Task<Result<String, String>> {
let project = self.project.clone();
let store = self.code_action_store.clone();
cx.spawn(async move |cx| {
let input = input
.recv()
.await
.map_err(|e| format!("Failed to receive tool input: {e}"))?;

let pending = store.update(cx, |store, _cx| store.take()).ok_or_else(|| {
"No code actions available. Call get_code_actions first.".to_string()
})?;

let zero_based_index = input
.index
.checked_sub(1)
.ok_or_else(|| "Index must be 1 or greater.".to_string())?;

let action = pending
.actions
.get(zero_based_index as usize)
.cloned()
.ok_or_else(|| {
format!(
"Index {} is out of range. There were {} code action(s) available.",
input.index,
pending.actions.len()
)
})?;

let title = action.lsp_action.title().to_string();
let buffer = pending.buffer.clone();

let apply_task = project.update(cx, |project, cx| {
project.apply_code_action(buffer, action, true, cx)
});

let transaction = apply_task
.await
.map_err(|e| format!("Failed to apply code action '{title}': {e}"))?;

if transaction.0.is_empty() {
return Ok(format!(
"Code action '{title}' was applied but made no changes.",
));
}

let mut output = format!(
"Applied code action '{title}'. Modified {} file(s):\n",
transaction.0.len()
);

for (buffer, _) in &transaction.0 {
buffer.read_with(cx, |buffer, cx| {
let path = buffer
.file()
.map(|f| f.full_path(cx).display().to_string())
.unwrap_or_else(|| "<untitled>".to_string());
writeln!(output, "- {path}").ok();
});
}

Ok(output)
})
}
}
Loading
Loading