diff --git a/Cargo.lock b/Cargo.lock index 18bd13d9ca124d69c3d856f56d56e7c5e4f3ada3..043b3289c331041e7cf731b4b53eff5e3c14341d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,6 +114,7 @@ dependencies = [ "async-trait", "chrono", "collections", + "env_logger 0.11.8", "futures 0.3.31", "gpui", "language", @@ -246,6 +247,7 @@ dependencies = [ "async-trait", "chrono", "futures 0.3.31", + "log", "parking_lot", "schemars 1.0.1", "serde", diff --git a/crates/agent2/Cargo.toml b/crates/agent2/Cargo.toml index 2b2404171619ecfe6a0e68626dfaaf8678f6db44..c3e55405e2218c7bf40e1e07bc51a553ef627a39 100644 --- a/crates/agent2/Cargo.toml +++ b/crates/agent2/Cargo.toml @@ -34,6 +34,7 @@ util.workspace = true agentic-coding-protocol = { path = "../../../agentic-coding-protocol" } [dev-dependencies] +env_logger.workspace = true gpui = { workspace = true, "features" = ["test-support"] } project = { workspace = true, "features" = ["test-support"] } serde_json.workspace = true diff --git a/crates/agent2/src/acp.rs b/crates/agent2/src/acp.rs index 6ee02fd0d1971748551dba717d966458095108c2..a9fbc7ac28b49e4e815eed273945580bf2fc2eba 100644 --- a/crates/agent2/src/acp.rs +++ b/crates/agent2/src/acp.rs @@ -1,3 +1,5 @@ +use std::path::Path; + use crate::{Agent, AgentThread, AgentThreadEntry, AgentThreadSummary, ResponseEvent, ThreadId}; use agentic_coding_protocol as acp; use anyhow::{Context as _, Result}; @@ -9,7 +11,7 @@ use smol::process::Child; use util::ResultExt; pub struct AcpAgent { - connection: acp::Connection, + connection: acp::AgentConnection, _handler_task: Task<()>, _io_task: Task<()>, } @@ -20,7 +22,7 @@ struct AcpClientDelegate { // sent_buffer_versions: HashMap, HashMap>, } -#[async_trait] +#[async_trait(?Send)] impl acp::Client for AcpClientDelegate { async fn read_file(&self, request: acp::ReadFileParams) -> Result { let cx = &mut self.cx.clone(); @@ -28,30 +30,40 @@ impl acp::Client for AcpClientDelegate { .project .update(cx, |project, cx| { let path = project - .project_path_for_absolute_path(request.path, cx) + .project_path_for_absolute_path(Path::new(&request.path), cx) .context("Failed to get project path")?; - project.open_buffer(path, cx) - })? + anyhow::Ok(project.open_buffer(path, cx)) + })?? .await?; - anyhow::Ok(buffer.update(cx, |buffer, cx| acp::ReadFileResponse { + buffer.update(cx, |buffer, _| acp::ReadFileResponse { content: buffer.text(), - // todo! - version: 0, - })) + version: acp::FileVersion(0), + }) + } + + async fn glob_search(&self, request: acp::GlobSearchParams) -> Result { + todo!() } } impl AcpAgent { - pub fn stdio(process: Child, project: Entity, cx: AsyncApp) -> Self { - let stdin = process.stdin.expect("process didn't have stdin"); - let stdout = process.stdout.expect("process didn't have stdout"); + pub fn stdio(mut process: Child, project: Entity, cx: AsyncApp) -> Self { + let stdin = process.stdin.take().expect("process didn't have stdin"); + let stdout = process.stdout.take().expect("process didn't have stdout"); - let (connection, handler_fut, io_fut) = - acp::Connection::client_to_agent(AcpClientDelegate { project, cx }, stdin, stdout); + let (connection, handler_fut, io_fut) = acp::AgentConnection::connect_to_agent( + AcpClientDelegate { + project, + cx: cx.clone(), + }, + stdin, + stdout, + ); let io_task = cx.background_spawn(async move { io_fut.await.log_err(); + process.status().await.log_err(); }); Self { @@ -89,7 +101,7 @@ impl Agent for AcpAgent { } } -struct AcpAgentThread {} +pub struct AcpAgentThread {} impl AgentThread for AcpAgentThread { async fn entries(&self) -> Result> { diff --git a/crates/agent2/src/agent2.rs b/crates/agent2/src/agent2.rs index 188411bbad8e8f1dda7995d26464e3777551f653..1f968bfe2e76194cd53480f193dc10755e30e639 100644 --- a/crates/agent2/src/agent2.rs +++ b/crates/agent2/src/agent2.rs @@ -364,16 +364,16 @@ impl Thread { #[cfg(test)] mod tests { use super::*; - use agentic_coding_protocol::Client; - use gpui::{BackgroundExecutor, TestAppContext}; + use crate::acp::AcpAgent; + use gpui::TestAppContext; use project::FakeFs; use serde_json::json; use settings::SettingsStore; - use smol::process::Child; - use std::env; + use std::{env, process::Stdio}; use util::path; fn init_test(cx: &mut TestAppContext) { + env_logger::init(); cx.update(|cx| { let settings_store = SettingsStore::test(cx); cx.set_global(settings_store); @@ -394,74 +394,24 @@ mod tests { ) .await; let project = Project::test(fs, [path!("/test").as_ref()], cx).await; - let agent = GeminiAgent::start(&cx.executor()).await.unwrap(); + let agent = gemini_agent(project.clone(), cx.to_async()).unwrap(); let thread_store = ThreadStore::load(Arc::new(agent), project, &mut cx.to_async()) .await .unwrap(); } - struct TestClient; - - #[async_trait] - impl Client for TestClient { - async fn read_file(&self, _request: ReadFileParams) -> Result { - Ok(ReadFileResponse { - version: FileVersion(0), - content: "the content".into(), - }) - } - } - - struct GeminiAgent { - child: Child, - _task: Task<()>, - } - - impl GeminiAgent { - pub fn start(executor: &BackgroundExecutor) -> Task> { - executor.spawn(async move { - // todo! - let child = util::command::new_smol_command("node") - .arg("../gemini-cli/packages/cli") - .arg("--acp") - .env("GEMINI_API_KEY", env::var("GEMINI_API_KEY").unwrap()) - .kill_on_drop(true) - .spawn() - .unwrap(); - - Ok(GeminiAgent { child }) - }) - } - } - - impl Agent for GeminiAgent { - type Thread = GeminiAgentThread; - - async fn threads(&self) -> Result> { - todo!() - } - - async fn create_thread(&self) -> Result { - todo!() - } - - async fn open_thread(&self, id: ThreadId) -> Result { - todo!() - } - } - - struct GeminiAgentThread {} - - impl AgentThread for GeminiAgentThread { - async fn entries(&self) -> Result> { - todo!() - } + pub fn gemini_agent(project: Entity, cx: AsyncApp) -> Result { + let child = util::command::new_smol_command("node") + .arg("../../../gemini-cli/packages/cli") + .arg("--acp") + .env("GEMINI_API_KEY", env::var("GEMINI_API_KEY").unwrap()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .kill_on_drop(true) + .spawn() + .unwrap(); - async fn send( - &self, - _message: Message, - ) -> Result>> { - todo!() - } + Ok(AcpAgent::stdio(child, project, cx)) } }