Skip to content

Commit d40ac31

Browse files
authored
fix(agent): Parse EDITOR environment variable to agent edit/create co… (#3104)
* fix(agent): Parse EDITOR environment variable to agent edit/create commands Fix bug where 'q agent edit', '/agent edit', 'q agent create' and '/agent create' commands failed when EDITOR contained arguments (e.g., 'emacsclient -nw'). The commands were passing the entire string to Command::new() which expects only the executable name. Now using shlex::split() to properly parse the command and arguments, matching the implementation in the /editor chat command. 🤖 Assisted by Amazon Q Developer * Fixed formatting issues
1 parent 38622d7 commit d40ac31

File tree

4 files changed

+46
-26
lines changed

4 files changed

+46
-26
lines changed

crates/chat-cli/src/cli/agent/root_command_args.rs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,8 @@ impl AgentArgs {
116116
Some(AgentSubcommands::Create { name, directory, from }) => {
117117
let mut agents = Agents::load(os, None, true, &mut stderr, mcp_enabled).await.0;
118118
let path_with_file_name = create_agent(os, &mut agents, name.clone(), directory, from).await?;
119-
let editor_cmd = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string());
120-
let mut cmd = std::process::Command::new(editor_cmd);
121119

122-
let status = cmd.arg(&path_with_file_name).status()?;
123-
if !status.success() {
124-
bail!("Editor process did not exit with success");
125-
}
120+
crate::util::editor::launch_editor(&path_with_file_name)?;
126121

127122
let Ok(content) = os.fs.read(&path_with_file_name).await else {
128123
bail!(
@@ -148,13 +143,7 @@ impl AgentArgs {
148143
let _agents = Agents::load(os, None, true, &mut stderr, mcp_enabled).await.0;
149144
let (_agent, path_with_file_name) = Agent::get_agent_by_name(os, &name).await?;
150145

151-
let editor_cmd = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string());
152-
let mut cmd = std::process::Command::new(editor_cmd);
153-
154-
let status = cmd.arg(&path_with_file_name).status()?;
155-
if !status.success() {
156-
bail!("Editor process did not exit with success");
157-
}
146+
crate::util::editor::launch_editor(&path_with_file_name)?;
158147

159148
let Ok(content) = os.fs.read(&path_with_file_name).await else {
160149
bail!(

crates/chat-cli/src/cli/chat/cli/profile.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,9 @@ impl AgentSubcommand {
195195
let path_with_file_name = create_agent(os, &mut agents, name.clone(), directory, from)
196196
.await
197197
.map_err(|e| ChatError::Custom(Cow::Owned(e.to_string())))?;
198-
let editor_cmd = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string());
199-
let mut cmd = std::process::Command::new(editor_cmd);
200198

201-
let status = cmd.arg(&path_with_file_name).status()?;
202-
if !status.success() {
203-
return Err(ChatError::Custom("Editor process did not exit with success".into()));
204-
}
199+
crate::util::editor::launch_editor(&path_with_file_name)
200+
.map_err(|e| ChatError::Custom(Cow::Owned(e.to_string())))?;
205201

206202
let new_agent = Agent::load(
207203
os,
@@ -253,13 +249,8 @@ impl AgentSubcommand {
253249
.await
254250
.map_err(|e| ChatError::Custom(Cow::Owned(e.to_string())))?;
255251

256-
let editor_cmd = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string());
257-
let mut cmd = std::process::Command::new(editor_cmd);
258-
259-
let status = cmd.arg(&path_with_file_name).status()?;
260-
if !status.success() {
261-
return Err(ChatError::Custom("Editor process did not exit with success".into()));
262-
}
252+
crate::util::editor::launch_editor(&path_with_file_name)
253+
.map_err(|e| ChatError::Custom(Cow::Owned(e.to_string())))?;
263254

264255
let updated_agent = Agent::load(
265256
os,

crates/chat-cli/src/util/editor.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
use std::path::Path;
2+
use std::process::Command;
3+
4+
/// Launch the user's preferred editor with the given file path.
5+
///
6+
/// This function properly parses the EDITOR environment variable to handle
7+
/// editors that require arguments (e.g., "emacsclient -nw").
8+
///
9+
/// # Arguments
10+
/// * `file_path` - Path to the file to open in the editor
11+
///
12+
/// # Returns
13+
/// * `Ok(())` if the editor was launched successfully and exited with success
14+
/// * `Err` if the editor failed to launch or exited with an error
15+
pub fn launch_editor(file_path: &Path) -> eyre::Result<()> {
16+
let editor_cmd = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string());
17+
18+
// Parse the editor command to handle arguments
19+
let mut parts = shlex::split(&editor_cmd).ok_or_else(|| eyre::eyre!("Failed to parse EDITOR command"))?;
20+
21+
if parts.is_empty() {
22+
eyre::bail!("EDITOR environment variable is empty");
23+
}
24+
25+
let editor_bin = parts.remove(0);
26+
27+
let mut cmd = Command::new(editor_bin);
28+
for arg in parts {
29+
cmd.arg(arg);
30+
}
31+
32+
let status = cmd.arg(file_path).status()?;
33+
34+
if !status.success() {
35+
eyre::bail!("Editor process did not exit with success");
36+
}
37+
38+
Ok(())
39+
}

crates/chat-cli/src/util/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod consts;
22
pub mod directories;
3+
pub mod editor;
34
pub mod knowledge_store;
45
pub mod open;
56
pub mod pattern_matching;

0 commit comments

Comments
 (0)