From a6ddca66e2e2a93cde275ff7c30a3b5a50cc3106 Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Thu, 1 Oct 2026 04:54:22 +0800 Subject: [PATCH] fix(terminal): spell a mention with a space the way Claude and Codex read it A file picked from the composer's @ menu went in raw for Claude Code and Codex, so "my notes.md" came out as "@my notes.md": - Claude Code ends a bare mention at the first whitespace, so it looked for "my" and the file never reached the turn. A path with a space now goes in double quotes, @"my notes.md", the form Claude's own list puts in and its parser reads back as one file. - Codex hands mentions to the model as plain text, and its own list puts in the path alone: without the @, in double quotes when it has a space in it. The composer now does the same. Checked for real: Claude 2.1.286 with no tools answers from @"my notes.md" and not from @my notes.md or @my\ notes.md; Codex 0.158's list inserts "my notes.md". Attachments are unchanged; both agents read one with a space in its path. --- src/terminal/composer.rs | 49 ++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/src/terminal/composer.rs b/src/terminal/composer.rs index cdb08943..a87c0e73 100644 --- a/src/terminal/composer.rs +++ b/src/terminal/composer.rs @@ -581,15 +581,27 @@ pub(super) fn command_items(commands: &[(String, String)], query: &str) -> Vec, path: &str, shell: Option<&str>) -> String { - if agent != Some(CLIAgent::Gemini) { - return format!("@{path}"); + let spaced = path.chars().any(char::is_whitespace); + match agent { + Some(CLIAgent::Gemini) => {} + Some(CLIAgent::Claude) if spaced => return format!("@\"{path}\""), + Some(CLIAgent::Codex) if spaced && !path.contains('"') => return format!("\"{path}\""), + Some(CLIAgent::Codex) => return path.to_string(), + _ => return format!("@{path}"), } let spelled = match quoting_for(shell) { Quoting::Posix => { @@ -2297,10 +2309,29 @@ mod tests { mention(Some(CLIAgent::Gemini), r"C:\src\a.rs", Some("pwsh")), r"@C:\src\a.rs" ); + } + + #[test] + fn a_mention_with_a_space_is_spelled_the_way_each_agent_reads_it() { + let zsh = Some("zsh"); assert_eq!( - mention(Some(CLIAgent::Claude), "my notes.md", Some("zsh")), - "@my notes.md" + mention(Some(CLIAgent::Claude), "my notes.md", zsh), + r#"@"my notes.md""# ); + assert_eq!( + mention(Some(CLIAgent::Claude), "src/a.rs", zsh), + "@src/a.rs" + ); + assert_eq!( + mention(Some(CLIAgent::Codex), "my notes.md", zsh), + r#""my notes.md""# + ); + assert_eq!(mention(Some(CLIAgent::Codex), "src/a.rs", zsh), "src/a.rs"); + assert_eq!( + mention(Some(CLIAgent::Gemini), "my notes.md", zsh), + r"@my\ notes.md" + ); + assert_eq!(mention(None, "my notes.md", zsh), "@my notes.md"); } fn screen(lines: &[&str]) -> Vec {