Aggregate tool usage from condensed entries.
(entries: &[CondensedEntry])
| 221 | |
| 222 | /// Aggregate tool usage from condensed entries. |
| 223 | pub fn aggregate_tool_usage(entries: &[CondensedEntry]) -> Vec<ToolUseSummary> { |
| 224 | let mut tools: HashMap<String, ToolUseSummary> = HashMap::new(); |
| 225 | |
| 226 | for entry in entries.iter().filter(|e| e.is_tool()) { |
| 227 | let name = entry.tool_name.as_deref().unwrap_or("Unknown"); |
| 228 | |
| 229 | let summary = tools |
| 230 | .entry(name.to_string()) |
| 231 | .or_insert_with(|| ToolUseSummary::new(name, 0, Vec::new())); |
| 232 | |
| 233 | summary.invocation_count += 1; |
| 234 | |
| 235 | // Track files affected by file-modifying tools |
| 236 | if let Some(detail) = &entry.tool_detail { |
| 237 | if matches!(name, "Edit" | "Write" | "MultiEdit" | "NotebookEdit") |
| 238 | && !summary.files_affected.contains(detail) |
| 239 | { |
| 240 | summary.files_affected.push(detail.clone()); |
| 241 | } |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | let mut result: Vec<_> = tools.into_values().collect(); |
| 246 | result.sort_by(|a, b| a.tool_name.cmp(&b.tool_name)); |
| 247 | result |
| 248 | } |
| 249 | |
| 250 | /// Extract user content from a transcript message value. |
| 251 | fn extract_user_content(message: &serde_json::Value) -> Option<String> { |