Skip to content

Commit 3829fa2

Browse files
committed
add the file summary when list and expand
1 parent a44f814 commit 3829fa2

File tree

2 files changed

+64
-14
lines changed

2 files changed

+64
-14
lines changed

crates/chat-cli/src/cli/chat/capture.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,29 @@ impl CaptureManager {
156156
Ok(stats)
157157
}
158158

159+
pub fn get_file_changes_between(&self, base: &str, head: &str) -> Result<FileChangeStats> {
160+
let git_dir_arg = format!("--git-dir={}", self.shadow_repo_path.display());
161+
let output = Command::new("git")
162+
.args([&git_dir_arg, "diff", "--name-status", base, head])
163+
.output()?;
164+
if !output.status.success() {
165+
bail!("Failed to get diff stats: {}", String::from_utf8_lossy(&output.stderr));
166+
}
167+
let mut stats = FileChangeStats::default();
168+
for line in String::from_utf8_lossy(&output.stdout).lines() {
169+
// Handle A/M/D and R/C as modified
170+
let code = line.split('\t').next().unwrap_or("");
171+
match code.chars().next().unwrap_or('M') {
172+
'A' => stats.added += 1,
173+
'M' => stats.modified += 1,
174+
'D' => stats.deleted += 1,
175+
'R' | 'C' => stats.modified += 1,
176+
_ => {},
177+
}
178+
}
179+
Ok(stats)
180+
}
181+
159182
fn get_previous_tag(&self, tag: &str) -> Result<String> {
160183
// Parse tag format "X" or "X.Y" to get previous
161184
if let Ok(turn) = tag.parse::<usize>() {

crates/chat-cli/src/cli/chat/cli/capture.rs

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -264,13 +264,19 @@ impl CaptureDisplayEntry {
264264
fn with_file_stats(capture: &Capture, manager: &CaptureManager) -> Result<Self> {
265265
let mut entry = Self::try_from(capture)?;
266266

267-
if let Some(stats) = manager.file_changes.get(&capture.tag) {
267+
// Prefer cached stats; if absent, compute on the fly (no mutation of manager needed).
268+
let stats_opt = manager
269+
.file_changes
270+
.get(&capture.tag)
271+
.cloned()
272+
.or_else(|| manager.get_file_changes(&capture.tag).ok());
273+
274+
if let Some(stats) = stats_opt.as_ref() {
268275
let stats_str = format_file_stats(stats);
269276
if !stats_str.is_empty() {
270277
entry.display_parts.push(format!(" ({})", stats_str).dark_grey());
271278
}
272279
}
273-
274280
Ok(entry)
275281
}
276282
}
@@ -336,29 +342,50 @@ fn expand_capture(manager: &CaptureManager, output: &mut impl Write, tag: String
336342
},
337343
};
338344
let capture = &manager.captures[*capture_index];
339-
let display_entry = CaptureDisplayEntry::with_file_stats(capture, manager)?;
345+
// Turn header: do NOT show file stats here
346+
let display_entry = CaptureDisplayEntry::try_from(capture)?;
340347
execute!(output, style::Print(display_entry), style::Print("\n"))?;
341348

342349
// If the user tries to expand a tool-level checkpoint, return early
343350
if !capture.is_turn {
344351
return Ok(());
345352
} else {
346-
let mut display_vec = Vec::new();
353+
// Collect tool-level entries with their indices so we can diff against the previous capture.
354+
let mut items: Vec<(usize, CaptureDisplayEntry)> = Vec::new();
347355
for i in (0..*capture_index).rev() {
348-
let capture = &manager.captures[i];
349-
if capture.is_turn {
356+
let c = &manager.captures[i];
357+
if c.is_turn {
350358
break;
351359
}
352-
display_vec.push(CaptureDisplayEntry::with_file_stats(&manager.captures[i], manager)?);
360+
items.push((i, CaptureDisplayEntry::try_from(c)?));
353361
}
354362

355-
for entry in display_vec.iter().rev() {
356-
execute!(
357-
output,
358-
style::Print(" └─ ".blue()),
359-
style::Print(entry),
360-
style::Print("\n")
361-
)?;
363+
for (idx, entry) in items.iter().rev() {
364+
// previous capture in creation order (or itself if 0)
365+
let base_idx = idx.saturating_sub(1);
366+
let base_tag = &manager.captures[base_idx].tag;
367+
let curr_tag = &manager.captures[*idx].tag;
368+
// compute stats between previous capture -> this tool capture
369+
let badge = manager
370+
.get_file_changes_between(base_tag, curr_tag)
371+
.map_or_else(|_| String::new(), |s| format_file_stats(&s));
372+
373+
if badge.is_empty() {
374+
execute!(
375+
output,
376+
style::Print(" └─ ".blue()),
377+
style::Print(entry),
378+
style::Print("\n")
379+
)?;
380+
} else {
381+
execute!(
382+
output,
383+
style::Print(" └─ ".blue()),
384+
style::Print(entry),
385+
style::Print(format!(" ({})", badge).dark_grey()),
386+
style::Print("\n")
387+
)?;
388+
}
362389
}
363390
}
364391

0 commit comments

Comments
 (0)