use super::*;
pub(super) async fn render_tool_output_common(
renderer: &mut AnsiRenderer,
handle: &InlineHandle,
name: &str,
args_val: &serde_json::Value,
output: &serde_json::Value,
command_success: bool,
vt_config: Option<&VTCodeConfig>,
workspace_root: Option<&Path>,
) -> Result<()> {
let inline_run_tool = renderer.supports_inline_ui() && streams_pty_output(name, args_val);
let git_diff_payload = is_git_diff_payload(output);
let status = ToolDisplayStatus::from_command_output(output, command_success);
let has_spool_path = output.get("spool_path").is_some();
let complete_capture = if renderer.supports_inline_ui()
&& is_command_output_call(name, args_val)
&& (inline_run_tool || has_spool_path)
{
load_complete_output(output, workspace_root).await
} else {
None
};
let inline_pty_command = inline_run_tool && is_command_output_call(name, args_val);
let compact_pty_without_live_preview = inline_pty_command && renderer.is_compact_display();
if inline_pty_command && !git_diff_payload {
let viewer_id = if let Some(capture) = complete_capture.as_deref() {
let mut viewer_lines =
build_merged_command_output_lines(name, args_val, capture, workspace_root, output, status);
append_capture_status_line(&mut viewer_lines, output, complete_capture.as_deref());
append_follow_up_capture_lines(&mut viewer_lines, output, Some(capture));
Some(handle.record_tool_output(viewer_lines))
} else {
let mut viewer_lines = if has_spool_path {
build_merged_command_output_lines(name, args_val, "", workspace_root, output, status)
} else {
build_pipe_command_output_lines(name, args_val, output, workspace_root, status)
};
append_capture_status_line(&mut viewer_lines, output, complete_capture.as_deref());
append_follow_up_capture_lines(&mut viewer_lines, output, complete_capture.as_deref());
Some(handle.record_tool_output(viewer_lines))
};
let compact_success = renderer.is_compact_display()
&& is_command_output_call(name, args_val)
&& matches!(status, ToolDisplayStatus::Success)
&& !has_compact_command_artifact(output, complete_capture.as_deref());
if compact_success {
renderer.collapse_pty_block_to_compact_activity(
compact_command_text(name, args_val, workspace_root),
compact_hidden_line_count(output, complete_capture.as_deref()),
None,
viewer_id,
)?;
return Ok(());
}
renderer.flush_compact_command_group();
if complete_capture_unavailable(output, complete_capture.as_deref()) {
renderer.line(MessageStyle::Warning, "Complete command output capture unavailable.")?;
}
if let Some(message) = warning_message(output) {
renderer.line(MessageStyle::Warning, &format!("⚠ {message}"))?;
}
if compact_pty_without_live_preview {
render_command_summary(renderer, name, args_val, output, command_success, workspace_root, viewer_id, true)?;
} else {
record_summary_line(name, args_val, output, command_success);
}
if let Some(note) = output_text(output, "critical_note") {
renderer.line(MessageStyle::ToolError, note)?;
transcript::append(note);
}
if let Some(stderr) = stderr_for_inline_display(output) {
let stderr_lines = normalize_terminal_output_lines(stderr);
if !stderr_lines.is_empty() {
renderer.line(MessageStyle::ToolError, &format!("stderr: {}", stderr_lines.join("\n")))?;
}
}
if !has_renderable_stream_content(output) && matches!(status, ToolDisplayStatus::Success) {
if !renderer.is_compact_display() {
renderer.line(MessageStyle::Info, "(no output)")?;
}
return Ok(());
}
if !matches!(status, ToolDisplayStatus::Success) {
if let Some(completion) = compact_run_completion_line(output, status) {
let indented = format!(" {}", completion);
renderer.line(MessageStyle::Status, &indented)?;
transcript::append(&completion);
}
}
return Ok(());
}
let is_session_followup = is_exec_session_call(name, args_val);
let _ = renderer.take_session_expand_anchor();
renderer.set_session_body(is_session_followup);
let viewer_id = if renderer.supports_inline_ui() && (is_command_output_call(name, args_val) || is_session_followup)
{
let mut viewer_lines = if is_session_followup {
build_pipe_command_output_lines(name, args_val, output, workspace_root, status)
} else if inline_run_tool || has_spool_path {
complete_capture.as_deref().map_or_else(
|| build_merged_command_output_lines(name, args_val, "", workspace_root, output, status),
|capture| build_merged_command_output_lines(name, args_val, capture, workspace_root, output, status),
)
} else {
build_pipe_command_output_lines(name, args_val, output, workspace_root, status)
};
append_capture_status_line(&mut viewer_lines, output, complete_capture.as_deref());
append_follow_up_capture_lines(&mut viewer_lines, output, complete_capture.as_deref());
let viewer_id = handle.record_tool_output(viewer_lines);
if is_session_followup {
renderer.set_session_expand_anchor(viewer_id);
}
Some(viewer_id)
} else {
None
};
let compact_command = renderer.supports_inline_ui()
&& is_command_output_call(name, args_val)
&& renderer.is_compact_display()
&& matches!(status, ToolDisplayStatus::Success)
&& !git_diff_payload;
let compact_file_diff = renderer.supports_inline_ui()
&& renderer.is_compact_display()
&& matches!(status, ToolDisplayStatus::Success)
&& crate::agent::runloop::unified::tool_summary::is_file_modification_tool(name, args_val)
&& has_file_operation_diff(output);
let compact_artifact = has_compact_command_artifact(output, complete_capture.as_deref());
if !matches!(status, ToolDisplayStatus::Success) {
renderer.flush_compact_command_group();
}
if git_diff_payload || compact_command && compact_artifact {
renderer.flush_compact_command_group();
}
if compact_file_diff {
renderer.flush_compact_command_group();
}
if compact_command {
renderer.render_compact_command_activity(
compact_command_text(name, args_val, workspace_root),
compact_hidden_line_count(output, complete_capture.as_deref()),
None,
viewer_id,
)?;
if !compact_artifact {
return Ok(());
}
}
let skip_live_pty_summary = inline_run_tool && git_diff_payload && !compact_pty_without_live_preview;
if !(compact_command || skip_live_pty_summary || compact_file_diff) {
render_command_summary(
renderer,
name,
args_val,
output,
command_success,
workspace_root,
viewer_id,
!matches!(status, ToolDisplayStatus::Success) || git_diff_payload,
)?;
}
if complete_capture_unavailable(output, complete_capture.as_deref()) {
renderer.line(MessageStyle::Warning, "Complete command output capture unavailable.")?;
}
if let Some(message) = warning_message(output) {
renderer.line(MessageStyle::Warning, &format!("⚠ {message}"))?;
}
let result = crate::agent::runloop::tool_output::render_tool_output(renderer, Some(name), output, vt_config).await;
if result.is_ok() && compact_command && compact_artifact {
render_structured_command_context(renderer, output)?;
}
if !matches!(status, ToolDisplayStatus::Success) {
renderer.flush_compact_command_group();
}
if compact_command && compact_artifact {
renderer.flush_compact_command_group();
}
result
}