normalize tool display; remove backticks (#1482)

This commit is contained in:
Gabriel Gordon-Hall
2025-12-09 15:50:17 +00:00
committed by GitHub
parent d0392e6d5e
commit 84d80659b3
9 changed files with 42 additions and 44 deletions

1
.gitignore vendored
View File

@@ -69,6 +69,7 @@ crates/executors/bindings
crates/utils/bindings crates/utils/bindings
crates/services/bindings crates/services/bindings
crates/server/bindings crates/server/bindings
crates/remote/bindings
build-npm-package-codesign.sh build-npm-package-codesign.sh

View File

@@ -8,7 +8,6 @@ use agent_client_protocol::{self as acp, SessionNotification};
use futures::StreamExt; use futures::StreamExt;
use regex::Regex; use regex::Regex;
use serde::Deserialize; use serde::Deserialize;
use tracing::{debug, trace};
use workspace_utils::msg_store::MsgStore; use workspace_utils::msg_store::MsgStore;
pub use super::AcpAgentHarness; pub use super::AcpAgentHarness;
@@ -38,7 +37,7 @@ pub fn normalize_logs(msg_store: Arc<MsgStore>, worktree_path: &Path) {
let mut stdout_lines = msg_store.stdout_lines_stream(); let mut stdout_lines = msg_store.stdout_lines_stream();
while let Some(Ok(line)) = stdout_lines.next().await { while let Some(Ok(line)) = stdout_lines.next().await {
if let Some(parsed) = AcpEventParser::parse_line(&line) { if let Some(parsed) = AcpEventParser::parse_line(&line) {
trace!("Parsed ACP line: {:?}", parsed); tracing::trace!("Parsed ACP line: {:?}", parsed);
match parsed { match parsed {
AcpEvent::SessionStart(id) => { AcpEvent::SessionStart(id) => {
if !stored_session_id { if !stored_session_id {
@@ -207,7 +206,7 @@ pub fn normalize_logs(msg_store: Arc<MsgStore>, worktree_path: &Path) {
.map(|s| s.title.clone()) .map(|s| s.title.clone())
.or_else(|| Some("".to_string())); .or_else(|| Some("".to_string()));
} }
trace!("Got tool call update: {:?}", update); tracing::trace!("Got tool call update: {:?}", update);
if let Ok(tc) = agent_client_protocol::ToolCall::try_from(update.clone()) { if let Ok(tc) = agent_client_protocol::ToolCall::try_from(update.clone()) {
handle_tool_call( handle_tool_call(
&tc, &tc,
@@ -218,7 +217,7 @@ pub fn normalize_logs(msg_store: Arc<MsgStore>, worktree_path: &Path) {
&msg_store, &msg_store,
); );
} else { } else {
debug!("Failed to convert tool call update to ToolCall"); tracing::debug!("Failed to convert tool call update to ToolCall");
} }
} }
AcpEvent::User(_) | AcpEvent::Other(_) => (), AcpEvent::User(_) | AcpEvent::Other(_) => (),
@@ -303,9 +302,10 @@ pub fn normalize_logs(msg_store: Arc<MsgStore>, worktree_path: &Path) {
// Prefer structured raw_output, else fallback to aggregated text content // Prefer structured raw_output, else fallback to aggregated text content
let completed = let completed =
matches!(tc.status, agent_client_protocol::ToolCallStatus::Completed); matches!(tc.status, agent_client_protocol::ToolCallStatus::Completed);
trace!( tracing::trace!(
"Mapping execute tool call, completed: {}, command: {}", "Mapping execute tool call, completed: {}, command: {}",
completed, command completed,
command
); );
let tc_exit_status = match tc.status { let tc_exit_status = match tc.status {
agent_client_protocol::ToolCallStatus::Completed => { agent_client_protocol::ToolCallStatus::Completed => {
@@ -617,7 +617,7 @@ impl AcpEventParser {
return Some(acp_event); return Some(acp_event);
} }
debug!("Failed to parse ACP raw log {trimmed}"); tracing::debug!("Failed to parse ACP raw log {trimmed}");
None None
} }

View File

@@ -1196,11 +1196,11 @@ impl ClaudeLogProcessor {
worktree_path: &str, worktree_path: &str,
) -> String { ) -> String {
match action_type { match action_type {
ActionType::FileRead { path } => format!("`{path}`"), ActionType::FileRead { path } => path.to_string(),
ActionType::FileEdit { path, .. } => format!("`{path}`"), ActionType::FileEdit { path, .. } => path.to_string(),
ActionType::CommandRun { command, .. } => format!("`{command}`"), ActionType::CommandRun { command, .. } => command.to_string(),
ActionType::Search { query } => format!("`{query}`"), ActionType::Search { query } => query.to_string(),
ActionType::WebFetch { url } => format!("`{url}`"), ActionType::WebFetch { url } => url.to_string(),
ActionType::TaskCreate { description } => { ActionType::TaskCreate { description } => {
if description.is_empty() { if description.is_empty() {
"Task".to_string() "Task".to_string()
@@ -1232,13 +1232,13 @@ impl ClaudeLogProcessor {
if relative_path.is_empty() { if relative_path.is_empty() {
"List directory".to_string() "List directory".to_string()
} else { } else {
format!("List directory: `{relative_path}`") format!("List directory: {relative_path}")
} }
} }
ClaudeToolData::Glob { pattern, path, .. } => { ClaudeToolData::Glob { pattern, path, .. } => {
if let Some(search_path) = path { if let Some(search_path) = path {
format!( format!(
"Find files: `{}` in `{}`", "Find files: `{}` in {}",
pattern, pattern,
make_path_relative(search_path, worktree_path) make_path_relative(search_path, worktree_path)
) )
@@ -1257,7 +1257,7 @@ impl ClaudeLogProcessor {
ClaudeToolData::CodebaseSearchAgent { query, path, .. } => { ClaudeToolData::CodebaseSearchAgent { query, path, .. } => {
match (query.as_ref(), path.as_ref()) { match (query.as_ref(), path.as_ref()) {
(Some(q), Some(p)) if !q.is_empty() && !p.is_empty() => format!( (Some(q), Some(p)) if !q.is_empty() && !p.is_empty() => format!(
"Codebase search: `{}` in `{}`", "Codebase search: `{}` in {}",
q, q,
make_path_relative(p, worktree_path) make_path_relative(p, worktree_path)
), ),
@@ -1948,7 +1948,7 @@ mod tests {
"/tmp/test-worktree", "/tmp/test-worktree",
); );
assert_eq!(result, "`**/*.ts`"); assert_eq!(result, "**/*.ts");
} }
#[test] #[test]
@@ -1967,7 +1967,7 @@ mod tests {
"/tmp/test-worktree", "/tmp/test-worktree",
); );
assert_eq!(result, "`*.js`"); assert_eq!(result, "*.js");
} }
#[test] #[test]
@@ -1984,7 +1984,7 @@ mod tests {
"/tmp/test-worktree", "/tmp/test-worktree",
); );
assert_eq!(result, "List directory: `components`"); assert_eq!(result, "List directory: components");
} }
#[test] #[test]
@@ -2190,8 +2190,8 @@ mod tests {
let parsed: ClaudeJson = serde_json::from_str(bash_json).unwrap(); let parsed: ClaudeJson = serde_json::from_str(bash_json).unwrap();
let entries = normalize(&parsed, "/tmp/work"); let entries = normalize(&parsed, "/tmp/work");
assert_eq!(entries.len(), 1); assert_eq!(entries.len(), 1);
// Content should display the command in backticks // Content should display the command
assert_eq!(entries[0].content, "`echo hello`"); assert_eq!(entries[0].content, "echo hello");
// Task content should include description/prompt wrapped in backticks // Task content should include description/prompt wrapped in backticks
let task_json = r#"{ let task_json = r#"{

View File

@@ -82,7 +82,7 @@ struct CommandState {
impl ToNormalizedEntry for CommandState { impl ToNormalizedEntry for CommandState {
fn to_normalized_entry(&self) -> NormalizedEntry { fn to_normalized_entry(&self) -> NormalizedEntry {
let content = format!("`{}`", self.command); let content = self.command.to_string();
NormalizedEntry { NormalizedEntry {
timestamp: None, timestamp: None,
@@ -894,7 +894,7 @@ pub fn normalize_logs(msg_store: Arc<MsgStore>, worktree_path: &Path) {
}, },
status: ToolStatus::Success, status: ToolStatus::Success,
}, },
content: format!("`{relative_path}`"), content: relative_path.to_string(),
metadata: None, metadata: None,
}, },
); );

View File

@@ -717,10 +717,7 @@ impl CursorToolCall {
match self { match self {
CursorToolCall::Read { args, .. } => { CursorToolCall::Read { args, .. } => {
let path = make_path_relative(&args.path, worktree_path); let path = make_path_relative(&args.path, worktree_path);
( (ActionType::FileRead { path: path.clone() }, path)
ActionType::FileRead { path: path.clone() },
format!("`{path}`"),
)
} }
CursorToolCall::Write { args, .. } => { CursorToolCall::Write { args, .. } => {
let path = make_path_relative(&args.path, worktree_path); let path = make_path_relative(&args.path, worktree_path);
@@ -729,7 +726,7 @@ impl CursorToolCall {
path: path.clone(), path: path.clone(),
changes: vec![], changes: vec![],
}, },
format!("`{path}`"), path,
) )
} }
CursorToolCall::Edit { args, result, .. } => { CursorToolCall::Edit { args, result, .. } => {
@@ -789,7 +786,7 @@ impl CursorToolCall {
path: path.clone(), path: path.clone(),
changes, changes,
}, },
format!("`{path}`"), path,
) )
} }
CursorToolCall::Delete { args, .. } => { CursorToolCall::Delete { args, .. } => {
@@ -799,7 +796,7 @@ impl CursorToolCall {
path: path.clone(), path: path.clone(),
changes: vec![FileChange::Delete], changes: vec![FileChange::Delete],
}, },
format!("`{path}`"), path.to_string(),
) )
} }
CursorToolCall::Shell { args, .. } => { CursorToolCall::Shell { args, .. } => {
@@ -809,7 +806,7 @@ impl CursorToolCall {
command: cmd.clone(), command: cmd.clone(),
result: None, result: None,
}, },
format!("`{cmd}`"), cmd.to_string(),
) )
} }
CursorToolCall::Grep { args, .. } => { CursorToolCall::Grep { args, .. } => {
@@ -818,7 +815,7 @@ impl CursorToolCall {
ActionType::Search { ActionType::Search {
query: pattern.clone(), query: pattern.clone(),
}, },
format!("`{pattern}`"), pattern.to_string(),
) )
} }
CursorToolCall::SemSearch { args, .. } => { CursorToolCall::SemSearch { args, .. } => {
@@ -827,7 +824,7 @@ impl CursorToolCall {
ActionType::Search { ActionType::Search {
query: query.clone(), query: query.clone(),
}, },
format!("`{query}`"), query.to_string(),
) )
} }
CursorToolCall::Glob { args, .. } => { CursorToolCall::Glob { args, .. } => {
@@ -838,7 +835,7 @@ impl CursorToolCall {
ActionType::Search { ActionType::Search {
query: pattern.clone(), query: pattern.clone(),
}, },
format!("Find files: `{pattern}` in `{path}`"), format!("Find files: `{pattern}` in {path}"),
) )
} else { } else {
( (
@@ -854,7 +851,7 @@ impl CursorToolCall {
let content = if path.is_empty() { let content = if path.is_empty() {
"List directory".to_string() "List directory".to_string()
} else { } else {
format!("List directory: `{path}`") format!("List directory: {path}")
}; };
( (
ActionType::Other { ActionType::Other {

View File

@@ -1026,7 +1026,7 @@ impl ToNormalizedEntry for FileReadState {
}, },
status: self.status.clone(), status: self.status.clone(),
}, },
content: format!("`{}`", self.path), content: self.path.clone(),
metadata: None, metadata: None,
} }
} }
@@ -1052,7 +1052,7 @@ impl ToNormalizedEntry for FileEditState {
}, },
status: self.status.clone(), status: self.status.clone(),
}, },
content: format!("`{}`", self.path), content: self.path.clone(),
metadata: None, metadata: None,
} }
} }
@@ -1094,7 +1094,7 @@ impl ToNormalizedEntry for CommandRunState {
}, },
status: self.status.clone(), status: self.status.clone(),
}, },
content: format!("`{}`", self.command), content: self.command.clone(),
metadata: None, metadata: None,
} }
} }
@@ -1149,7 +1149,7 @@ impl ToNormalizedEntry for SearchState {
}, },
status: self.status.clone(), status: self.status.clone(),
}, },
content: format!("`{}`", self.query), content: self.query.clone(),
metadata: None, metadata: None,
} }
} }
@@ -1173,7 +1173,7 @@ impl ToNormalizedEntry for WebFetchState {
}, },
status: self.status.clone(), status: self.status.clone(),
}, },
content: format!("`{}`", self.url), content: self.url.clone(),
metadata: None, metadata: None,
} }
} }

View File

@@ -215,7 +215,7 @@ export default function DiffCard({
// Title row // Title row
const title = ( const title = (
<p <p
className="text-xs font-mono overflow-x-auto flex-1" className="text-sm font-mono overflow-x-auto flex-1"
style={{ color: 'hsl(var(--muted-foreground) / 0.7)' }} style={{ color: 'hsl(var(--muted-foreground) / 0.7)' }}
> >
<Icon className="h-3 w-3 inline mr-2" aria-hidden /> <Icon className="h-3 w-3 inline mr-2" aria-hidden />

View File

@@ -516,9 +516,9 @@ const ToolCallCard: React.FC<{
{entryType && getEntryIcon(entryType)} {entryType && getEntryIcon(entryType)}
</span> </span>
{showInlineSummary ? ( {showInlineSummary ? (
<span className="font-light">{inlineText}</span> <span className="text-sm font-mono">{inlineText}</span>
) : ( ) : (
<span className="font-normal">{label}</span> <span className="text-sm font-mono">{label}</span>
)} )}
</span> </span>
</HeaderWrapper> </HeaderWrapper>

View File

@@ -136,7 +136,7 @@ const FileChangeRenderer = ({
{icon} {icon}
<p <p
onClick={() => expandable && setExpanded()} onClick={() => expandable && setExpanded()}
className="text-sm font-light overflow-x-auto flex-1 cursor-pointer" className="text-sm font-mono overflow-x-auto flex-1 cursor-pointer"
> >
{titleNode} {titleNode}
</p> </p>