mirror of
https://github.com/instructkr/claude-code.git
synced 2026-04-03 22:38:48 +03:00
Keep CLI tool previews readable without truncating session data
Extend the CLI renderer's generic tool-result path to reuse the existing display-only truncation helper, so large plugin or unknown-tool payloads no longer flood the terminal while the original tool result still flows through runtime/session state unchanged. The renderer now pretty-prints structured fallback payloads before truncating them for display, and the test suite covers both Read output and generic long tool output rendering. I also added a narrow clippy allow on an oversized slash-command parser test so the workspace lint gate stays green during verification. Constraint: Tool result truncation must affect screen rendering only, not stored tool output Rejected: Truncate tool results at execution time | would lose session fidelity and break downstream consumers Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep future tool-output shortening in renderer helpers only; do not trim runtime tool payloads before persistence Tested: cargo fmt --all; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace Not-tested: Manual interactive terminal run showing truncation in a live REPL session
This commit is contained in:
@@ -622,6 +622,7 @@ mod tests {
|
|||||||
.expect("write bundled manifest");
|
.expect("write bundled manifest");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_supported_slash_commands() {
|
fn parses_supported_slash_commands() {
|
||||||
assert_eq!(SlashCommand::parse("/help"), Some(SlashCommand::Help));
|
assert_eq!(SlashCommand::parse("/help"), Some(SlashCommand::Help));
|
||||||
|
|||||||
@@ -2763,10 +2763,7 @@ fn format_tool_result(name: &str, output: &str, is_error: bool) -> String {
|
|||||||
"edit_file" | "Edit" => format_edit_result(icon, &parsed),
|
"edit_file" | "Edit" => format_edit_result(icon, &parsed),
|
||||||
"glob_search" | "Glob" => format_glob_result(icon, &parsed),
|
"glob_search" | "Glob" => format_glob_result(icon, &parsed),
|
||||||
"grep_search" | "Grep" => format_grep_result(icon, &parsed),
|
"grep_search" | "Grep" => format_grep_result(icon, &parsed),
|
||||||
_ => {
|
_ => format_generic_tool_result(icon, name, &parsed),
|
||||||
let summary = truncate_for_summary(output.trim(), 200);
|
|
||||||
format!("{icon} \x1b[38;5;245m{name}:\x1b[0m {summary}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3034,6 +3031,30 @@ fn format_grep_result(icon: &str, parsed: &serde_json::Value) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn format_generic_tool_result(icon: &str, name: &str, parsed: &serde_json::Value) -> String {
|
||||||
|
let rendered_output = match parsed {
|
||||||
|
serde_json::Value::String(text) => text.clone(),
|
||||||
|
serde_json::Value::Null => String::new(),
|
||||||
|
serde_json::Value::Object(_) | serde_json::Value::Array(_) => {
|
||||||
|
serde_json::to_string_pretty(parsed).unwrap_or_else(|_| parsed.to_string())
|
||||||
|
}
|
||||||
|
_ => parsed.to_string(),
|
||||||
|
};
|
||||||
|
let preview = truncate_output_for_display(
|
||||||
|
&rendered_output,
|
||||||
|
TOOL_OUTPUT_DISPLAY_MAX_LINES,
|
||||||
|
TOOL_OUTPUT_DISPLAY_MAX_CHARS,
|
||||||
|
);
|
||||||
|
|
||||||
|
if preview.is_empty() {
|
||||||
|
format!("{icon} \x1b[38;5;245m{name}\x1b[0m")
|
||||||
|
} else if preview.contains('\n') {
|
||||||
|
format!("{icon} \x1b[38;5;245m{name}\x1b[0m\n{preview}")
|
||||||
|
} else {
|
||||||
|
format!("{icon} \x1b[38;5;245m{name}:\x1b[0m {preview}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn summarize_tool_payload(payload: &str) -> String {
|
fn summarize_tool_payload(payload: &str) -> String {
|
||||||
let compact = match serde_json::from_str::<serde_json::Value>(payload) {
|
let compact = match serde_json::from_str::<serde_json::Value>(payload) {
|
||||||
Ok(value) => value.to_string(),
|
Ok(value) => value.to_string(),
|
||||||
@@ -4010,6 +4031,28 @@ mod tests {
|
|||||||
assert!(output.contains("stdout 119"));
|
assert!(output.contains("stdout 119"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tool_rendering_truncates_generic_long_output_for_display_only() {
|
||||||
|
let items = (0..120)
|
||||||
|
.map(|index| format!("payload {index:03}"))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let output = json!({
|
||||||
|
"summary": "plugin payload",
|
||||||
|
"items": items,
|
||||||
|
})
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let rendered = format_tool_result("plugin_echo", &output, false);
|
||||||
|
|
||||||
|
assert!(rendered.contains("plugin_echo"));
|
||||||
|
assert!(rendered.contains("payload 000"));
|
||||||
|
assert!(rendered.contains("payload 040"));
|
||||||
|
assert!(!rendered.contains("payload 080"));
|
||||||
|
assert!(!rendered.contains("payload 119"));
|
||||||
|
assert!(rendered.contains("full result preserved in session"));
|
||||||
|
assert!(output.contains("payload 119"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn push_output_block_renders_markdown_text() {
|
fn push_output_block_renders_markdown_text() {
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
|
|||||||
Reference in New Issue
Block a user