feat(tui): label collapsed command activity
This commit is contained in:
+2
-1
@@ -15,7 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
`moonshotai/kimi-k2.7-code` registry row.
|
||||
- **Cursor-style activity metadata rows (#3146).** Dense successful tool-run
|
||||
summaries now render as a single muted `Explored ...` / `Updated metadata`
|
||||
row while keeping keyboard/mouse expansion and detail inspection intact.
|
||||
row, include short command-family labels for successful generic verifier
|
||||
groups, and keep keyboard/mouse expansion and detail inspection intact.
|
||||
- **Provider-wait observability (#3095).** Footer stall reasons now name the
|
||||
active provider/model route, idle seconds vs stream budget, and whether a
|
||||
fanout plan is still at `0 running` or dispatch is pending. Structured
|
||||
|
||||
@@ -15,7 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
`moonshotai/kimi-k2.7-code` registry row.
|
||||
- **Cursor-style activity metadata rows (#3146).** Dense successful tool-run
|
||||
summaries now render as a single muted `Explored ...` / `Updated metadata`
|
||||
row while keeping keyboard/mouse expansion and detail inspection intact.
|
||||
row, include short command-family labels for successful generic verifier
|
||||
groups, and keep keyboard/mouse expansion and detail inspection intact.
|
||||
- **Provider-wait observability (#3095).** Footer stall reasons now name the
|
||||
active provider/model route, idle seconds vs stream budget, and whether a
|
||||
fanout plan is still at `0 running` or dispatch is pending. Structured
|
||||
|
||||
@@ -908,6 +908,10 @@ fn tool_display_name(tool: &ToolCell) -> &str {
|
||||
|
||||
fn classify_tool_run_activity(tool: &ToolCell) -> ToolRunActivity {
|
||||
let name = tool_display_name(tool);
|
||||
classify_tool_name_activity(name)
|
||||
}
|
||||
|
||||
fn classify_tool_name_activity(name: &str) -> ToolRunActivity {
|
||||
let normalized = name.trim().to_ascii_lowercase();
|
||||
match normalized.as_str() {
|
||||
"read_file" | "list_dir" | "view_image" | "explore" => ToolRunActivity::File,
|
||||
@@ -991,10 +995,13 @@ pub fn tool_run_summary(run: &ToolRun) -> String {
|
||||
clauses.push(format!("Explored {}", parts.join(", ")));
|
||||
}
|
||||
if activity.commands > 0 {
|
||||
clauses.push(format!(
|
||||
"ran {}",
|
||||
counted(activity.commands, "command", "commands")
|
||||
));
|
||||
let mut command_clause =
|
||||
format!("ran {}", counted(activity.commands, "command", "commands"));
|
||||
if let Some(families) = command_family_summary(run) {
|
||||
command_clause.push_str(": ");
|
||||
command_clause.push_str(&families);
|
||||
}
|
||||
clauses.push(command_clause);
|
||||
}
|
||||
if activity.edits > 0 {
|
||||
clauses.push(format!(
|
||||
@@ -1020,6 +1027,23 @@ pub fn tool_run_summary(run: &ToolRun) -> String {
|
||||
sentence_case_activity(summary)
|
||||
}
|
||||
|
||||
fn command_family_summary(run: &ToolRun) -> Option<String> {
|
||||
if run.activity.commands == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut families = Vec::new();
|
||||
for family in &run.tool_families {
|
||||
if classify_tool_name_activity(family) == ToolRunActivity::Command
|
||||
&& !families.iter().any(|existing| existing == family)
|
||||
{
|
||||
families.push(family.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
(!families.is_empty()).then(|| families.join(", "))
|
||||
}
|
||||
|
||||
fn counted(count: usize, singular: &str, plural: &str) -> String {
|
||||
let noun = if count == 1 { singular } else { plural };
|
||||
format!("{count} {noun}")
|
||||
@@ -5979,6 +6003,30 @@ mod tests {
|
||||
assert_eq!(runs[0].count, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detect_tool_runs_summarizes_safe_command_tools() {
|
||||
let history = vec![
|
||||
success_generic_tool("run_tests"),
|
||||
success_generic_tool("run_verifiers"),
|
||||
success_generic_tool("validate_data"),
|
||||
];
|
||||
|
||||
let runs = super::detect_tool_runs(&history, 3);
|
||||
|
||||
assert_eq!(runs.len(), 1);
|
||||
assert_eq!(runs[0].start, 0);
|
||||
assert_eq!(runs[0].count, 3);
|
||||
assert_eq!(runs[0].activity.commands, 3);
|
||||
assert_eq!(
|
||||
runs[0].tool_families,
|
||||
vec!["run_tests", "run_verifiers", "validate_data"]
|
||||
);
|
||||
assert_eq!(
|
||||
super::tool_run_summary(&runs[0]),
|
||||
"Ran 3 commands: run_tests, run_verifiers, validate_data"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tool_run_summary_reports_compact_success_group() {
|
||||
let run = super::ToolRun {
|
||||
@@ -5997,6 +6045,29 @@ mod tests {
|
||||
assert_eq!(summary, "Explored 4 files, 1 search");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tool_run_summary_lists_only_command_families_for_command_clause() {
|
||||
let run = super::ToolRun {
|
||||
start: 4,
|
||||
count: 4,
|
||||
tool_families: vec![
|
||||
"read_file".to_string(),
|
||||
"run_tests".to_string(),
|
||||
"validate_data".to_string(),
|
||||
],
|
||||
activity: super::ToolRunActivitySummary {
|
||||
files: 2,
|
||||
commands: 2,
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
super::tool_run_summary(&run),
|
||||
"Explored 2 files, ran 2 commands: run_tests, validate_data"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tool_run_summary_uses_metadata_fallback_for_unknown_groups() {
|
||||
let run = super::ToolRun {
|
||||
|
||||
Reference in New Issue
Block a user