Cargo.lock 🔗
@@ -416,6 +416,7 @@ dependencies = [
"serde_json",
"serde_json_lenient",
"settings",
+ "shlex",
"smol",
"streaming_diff",
"task",
Cole Miller created
Reverts #38175, which is not correct, since in fact we do need to
pre-quote the command and arguments for the shell when using
`SpawnInTerminal` (although we should probably change the API so that
this isn't necessary). Then, applies the same fix as #38565 to fix the
root cause of being unable to spawn the login task on macOS, or in any
case where the command/args contain spaces.
Release Notes:
- Fixed being unable to login with Claude Code or Gemini using the
terminal.
Cargo.lock | 1
crates/agent_ui/Cargo.toml | 1
crates/agent_ui/src/acp/thread_view.rs | 34 ++++++++++++---------------
3 files changed, 17 insertions(+), 19 deletions(-)
@@ -416,6 +416,7 @@ dependencies = [
"serde_json",
"serde_json_lenient",
"settings",
+ "shlex",
"smol",
"streaming_diff",
"task",
@@ -80,6 +80,7 @@ serde.workspace = true
serde_json.workspace = true
serde_json_lenient.workspace = true
settings.workspace = true
+shlex.workspace = true
smol.workspace = true
streaming_diff.workspace = true
task.workspace = true
@@ -9,7 +9,7 @@ use agent_client_protocol::{self as acp, PromptCapabilities};
use agent_servers::{AgentServer, AgentServerDelegate};
use agent_settings::{AgentProfileId, AgentSettings, CompletionMode, NotifyWhenAgentWaiting};
use agent2::{DbThreadMetadata, HistoryEntry, HistoryEntryId, HistoryStore, NativeAgentServer};
-use anyhow::{Result, anyhow, bail};
+use anyhow::{Context as _, Result, anyhow, bail};
use arrayvec::ArrayVec;
use audio::{Audio, Sound};
use buffer_diff::BufferDiff;
@@ -1582,6 +1582,19 @@ impl AcpThreadView {
window.spawn(cx, async move |cx| {
let mut task = login.clone();
+ task.command = task
+ .command
+ .map(|command| anyhow::Ok(shlex::try_quote(&command)?.to_string()))
+ .transpose()?;
+ task.args = task
+ .args
+ .iter()
+ .map(|arg| {
+ Ok(shlex::try_quote(arg)
+ .context("Failed to quote argument")?
+ .to_string())
+ })
+ .collect::<Result<Vec<_>>>()?;
task.full_label = task.label.clone();
task.id = task::TaskId(format!("external-agent-{}-login", task.label));
task.command_label = task.label.clone();
@@ -1591,7 +1604,7 @@ impl AcpThreadView {
task.shell = shell;
let terminal = terminal_panel.update_in(cx, |terminal_panel, window, cx| {
- terminal_panel.spawn_task(&login, window, cx)
+ terminal_panel.spawn_task(&task, window, cx)
})?;
let terminal = terminal.await?;
@@ -5669,23 +5682,6 @@ pub(crate) mod tests {
});
}
- #[gpui::test]
- async fn test_spawn_external_agent_login_handles_spaces(cx: &mut TestAppContext) {
- init_test(cx);
-
- // Verify paths with spaces aren't pre-quoted
- let path_with_spaces = "/Users/test/Library/Application Support/Zed/cli.js";
- let login_task = task::SpawnInTerminal {
- command: Some("node".to_string()),
- args: vec![path_with_spaces.to_string(), "/login".to_string()],
- ..Default::default()
- };
-
- // Args should be passed as-is, not pre-quoted
- assert!(!login_task.args[0].starts_with('"'));
- assert!(!login_task.args[0].starts_with('\''));
- }
-
#[gpui::test]
async fn test_notification_for_tool_authorization(cx: &mut TestAppContext) {
init_test(cx);