chore(beans): triage session bugs, agent discovery bug, and TUI epic

Session list & dump epic (claudbg-tci9):
- Table overflows terminal / project path truncation (claudbg-6dgc)
- JSON output is array-of-arrays not array-of-objects (claudbg-pfa5)
- --include help text missing valid values (claudbg-a532)
- Collapse consecutive identical rows in dump (claudbg-d9ev)
- Add sub-agent count column to sessions list (claudbg-xpzp)

Transcription output improvements epic (claudbg-8vpb):
- Tools header shows debug HashMap not readable list (claudbg-f4ot)
- Duration always 0ms due to durationMs/duration_ms field mismatch (claudbg-nqxz)
- Tool results hidden by default — should show with truncation (claudbg-zy1p)
- Tool use input hard-capped at 120 chars ignoring --verbose (claudbg-kg0v)

Agent discovery bug (claudbg-33n0):
- discover_agents_for_session looks in <project>/subagents/ but actual
  disk structure is <project>/<session-uuid>/subagents/

TUI epic (claudbg-i6l2) + 8 child tasks:
- ratatui/crossterm deps, app state, terminal/event loop,
  session list screen, transcript screen, sub-agents panel,
  quit dialog, help modal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
Elijah Voigt 2 months ago
parent 7f321060f5
commit 66c1cf5c6e

@ -0,0 +1,48 @@
---
# claudbg-1e1c
title: 'TUI: quit confirmation dialog'
status: todo
type: task
priority: normal
created_at: 2026-03-30T04:47:46Z
updated_at: 2026-03-30T04:49:03Z
parent: claudbg-i6l2
blocked_by:
- claudbg-ut9q
---
Implement a modal quit confirmation dialog.
## Trigger
Press `q` or `Q` from any screen (except when another modal is open).
## Appearance
```
┌──────────────────────┐
│ Quit claudbg? │
│ │
│ q = yes Esc = no │
└──────────────────────┘
```
Centered overlay on top of the current screen (using `ratatui::widgets::Clear` to clear the background area).
## State
`AppState.show_quit_dialog: bool`
- `q`/`Q` → `show_quit_dialog = true`
- While dialog is shown: `q``should_quit = true` (exit loop), `Escape``show_quit_dialog = false`
- Dialog intercepts all other key input while open.
## Implementation notes
- Use `ratatui::layout::Rect` centered calculation to position the dialog.
- Render with `Block::bordered().title("Quit?")` and a `Paragraph` inside.
- Use `Clear` widget behind the dialog block to prevent bleed-through.
## Blocked by
- event loop (claudbg-ut9q)

@ -0,0 +1,55 @@
---
# claudbg-1tlk
title: 'TUI: help modal listing all keyboard shortcuts'
status: todo
type: task
priority: normal
created_at: 2026-03-30T04:48:38Z
updated_at: 2026-03-30T04:49:03Z
parent: claudbg-i6l2
blocked_by:
- claudbg-ut9q
---
Implement a `?` help overlay that lists all keyboard shortcuts.
## Trigger
Press `?` (or `Shift+/`) from any screen (except when another modal is open).
## Content
```
┌─────────── Keyboard Shortcuts ────────────┐
│ Navigation │
│ ↑/↓ or k/j Scroll up/down │
│ ←/→ or h/l Scroll left/right │
│ Tab Cycle panes │
│ Enter Open / select │
│ Escape Go back │
│ │
│ Global │
│ q / Q Quit (with confirmation) │
│ ? Show this help │
└───────────────────────────────────────────┘
```
Press `Escape` to close.
## State
`AppState.show_help: bool`
- `?``show_help = true`
- `Escape``show_help = false`
- Help modal intercepts all key input while open.
## Implementation notes
- Same centered overlay pattern as quit dialog (Clear + Block + Paragraph).
- Content is static — hardcoded list of shortcuts.
- Render on top of whatever screen is active.
## Blocked by
- event loop (claudbg-ut9q)

@ -0,0 +1,63 @@
---
# claudbg-33n0
title: Agent discovery looks in wrong subagents directory — sessions show 0 agents
status: todo
type: bug
priority: high
created_at: 2026-03-30T04:43:21Z
updated_at: 2026-03-30T04:43:21Z
---
## Problem
`agents list <session-id>` always returns empty results. The subagent directory path used in discovery is wrong.
## Root cause
`discover_agents_for_session(session_file)` in `src/parser/discovery.rs:198-205` does:
```rust
let parent = session_file.parent(); // ~/.claude/projects/<project-dir>/
let subagents_dir = parent.join("subagents"); // ~/.claude/projects/<project-dir>/subagents/
```
That directory never exists.
`discover_all_agents()` has the same bug: it iterates project dirs and looks at `proj_path.join("subagents")` directly — also wrong.
## Actual disk structure
Confirmed across multiple sessions in the live `~/.claude/projects/` dir:
```
~/.claude/projects/<project-dir>/
<session-uuid>.jsonl ← session file
<session-uuid>/ ← directory with same name as UUID (no .jsonl)
subagents/
agent-<id>.jsonl
agent-<id>.meta.json
```
Example path that should work but currently returns empty:
`~/.claude/projects/.../def4776b-25a6-4eca-99e7-f222926c297a/subagents/agent-a8e741de240089eef.jsonl`
## Required fix
`discover_agents_for_session(session_file)` should construct:
```rust
let stem = session_file.file_stem().unwrap(); // "def4776b-...-f222926c297a"
let session_dir = session_file.parent().unwrap().join(stem);
let subagents_dir = session_dir.join("subagents");
```
`discover_all_agents()` must also be updated to walk UUID subdirectories inside each project dir instead of looking at `<project-dir>/subagents/` directly.
## Secondary issue
The existing unit tests in `src/commands/agents.rs` (`dump_with_real_agent_returns_ok`, `transcribe_with_real_agent_returns_ok`) create subagents at the old wrong path (`projects/subagents/`) and must be updated alongside the fix.
## Relevant files
- `src/parser/discovery.rs``discover_agents_for_session` (lines 198205), `discover_all_agents` (lines 211266), `collect_agents_in_dir`
- `src/commands/agents.rs` — integration tests

@ -0,0 +1,30 @@
---
# claudbg-6dgc
title: Table overflows terminal — truncate long columns and respect terminal width
status: todo
type: bug
priority: normal
created_at: 2026-03-30T04:36:31Z
updated_at: 2026-03-30T04:41:14Z
parent: claudbg-tci9
---
## Problem
`sessions list` table overflows the terminal width. The project path column is the main culprit — full paths like `/run/media/pop/4e1ca4f1-.../Projects/src/github.com/pop/claudbg` make rows far too wide.
## Current behavior
`src/output/table.rs` hardcodes `FALLBACK_WIDTH = 200` and applies `.max(FALLBACK_WIDTH)`, so the table is *always at least 200 chars wide* regardless of the actual terminal. No `ColumnConstraint` is applied to any column.
## Expected behavior
- The table should never exceed the actual terminal width (use `terminal_size` or `comfy_table`'s built-in terminal detection).
- The project path column should be truncated to show the **last** N characters (tail of the path), since the trailing project name is more meaningful than the leading `/run/media/...` prefix. E.g. display `…/pop/claudbg` instead of `/run/media/pop/4e1ca4f1-…`.
- Other columns (ID, date, model, message count) should not be truncated.
- comfy-table supports `ColumnConstraint::MaxWidth` and `ColumnConstraint::LowerBoundary` — use these to cap the project column.
## Relevant files
- `src/output/table.rs``render_table`, `FALLBACK_WIDTH` constant
- `src/commands/sessions.rs``list()` function that calls `render_table`

@ -0,0 +1,34 @@
---
# claudbg-78xt
title: 'TUI: add ratatui + crossterm dependencies'
status: todo
type: task
created_at: 2026-03-30T04:45:06Z
updated_at: 2026-03-30T04:45:06Z
parent: claudbg-i6l2
---
Add `ratatui` and `crossterm` to `Cargo.toml`.
## Changes
```toml
ratatui = "0.29" # TUI framework
crossterm = "0.28" # terminal backend (raw mode, event reading)
```
Ratatui ships its own `crossterm` backend feature; use `ratatui = { version = "0.29", features = ["crossterm"] }` to pull both together.
## Verify
Run `cargo check` to confirm the dependency resolves and there are no conflicts with existing deps (`tokio`, `libsql`).
## Notes
- Do NOT add `termion` — crossterm is cross-platform and the right choice.
- The `ratatui` crate bundles the backend integration; no need for a separate `tui-crossterm` crate.
- Check that `ratatui 0.29` is available; if not use the latest stable.
## Relevant files
- `Cargo.toml`

@ -0,0 +1,10 @@
---
# claudbg-8vpb
title: Transcription output improvements
status: todo
type: epic
created_at: 2026-03-30T04:43:30Z
updated_at: 2026-03-30T04:43:30Z
---
Groups bugs in `sessions transcribe` and `agents transcribe` output: tools display format, duration miscalculation, tool result visibility defaults, and tool input truncation.

@ -0,0 +1,44 @@
---
# claudbg-9c8r
title: 'TUI: sub-agents panel in transcript view'
status: todo
type: feature
priority: normal
created_at: 2026-03-30T04:47:15Z
updated_at: 2026-03-30T04:49:03Z
parent: claudbg-i6l2
blocked_by:
- claudbg-rudq
---
Add a sub-agents panel to the right side of the transcript screen, showing all sub-agent runs for the current session.
## Layout
```
┌── Transcript ──────────────────────┬─── Sub-agents ──┐
│ [user]: … │ a8e741de agent │
│ [assistant]: … │ > c3f920aa agent │◄ selected
│ [tool: Bash] … │ │
│ [tool_result]: … │ │
└────────────────────────────────────┴──────────────────┘
```
The panel is ~25% of the frame width (or a fixed ~30 chars).
## Implementation
- Use `ratatui::layout::Layout` with `Constraint::Min` for the chat area and `Constraint::Length(30)` for the panel.
- Panel shows: short agent ID + agent type. If the session has no sub-agents, the panel can be hidden or show "No sub-agents".
- Panel is focusable via Tab (Tab cycles: chat log → sub-agents panel → chat log).
- When panel is focused, j/k moves selection. Enter navigates to that sub-agent's transcript (sets `screen` to `SubagentTranscript`).
- Highlight the focused pane's border (e.g. with a colored `Block` border).
## Data source
`AppState.subagents: Vec<AgentRef>` populated via `discover_agents_for_session` when entering transcript screen.
## Blocked by
- transcript screen (claudbg-rudq)
- agent discovery fix (claudbg-33n0)

@ -0,0 +1,37 @@
---
# claudbg-a532
title: '`--include` help text should enumerate valid values'
status: todo
type: bug
priority: low
created_at: 2026-03-30T04:38:03Z
updated_at: 2026-03-30T04:41:14Z
parent: claudbg-tci9
---
## Problem
The `--include` flag help text says "Comma-separated content to include: thinking, output." but clap does not enumerate the valid tokens in `--help` output. Users cannot discover valid values from `--help` alone.
## Current declaration (`src/cli.rs`)
```rust
/// Comma-separated content to include: thinking, output.
#[arg(long, global = true, default_value = "")]
pub include: IncludeList,
```
The valid tokens are `thinking` and `output` (parsed in `IncludeList::from_str`).
## Expected behavior
The help output should clearly show: `[possible values: thinking, output]` or equivalent. Options:
1. Update the `#[arg(help = ...)]` string to explicitly state the valid values and format.
2. Or use clap's `value_parser` with a custom validator that also shows valid choices.
Since `IncludeList` is a comma-separated composite (not a single enum value), option 1 (explicit help string) is simplest.
## Relevant files
- `src/cli.rs``IncludeList`, `GlobalOpts`, the `include` field annotation

@ -0,0 +1,43 @@
---
# claudbg-d9ev
title: '`sessions dump` should collapse consecutive identical rows'
status: todo
type: bug
priority: normal
created_at: 2026-03-30T04:38:42Z
updated_at: 2026-03-30T04:41:14Z
parent: claudbg-tci9
---
## Problem
`sessions dump` produces hundreds of identical consecutive rows (e.g. repeated `progress` entries) that differ only in timestamp, making the output very noisy.
## Example
Rows 555661 in session `21fae0a8` are all `progress` entries with the same type, role, and content preview. They currently appear as 107 separate table rows.
## Expected behavior
Consecutive rows that are identical in all fields *except* the timestamp should be collapsed into a single row with a range indicator:
```
555-661 | progress | <role> | <content preview>
```
Only collapse truly identical rows (same `type`, `role`, and `content` preview). Do not collapse if any of these differ.
## Scope
- Applies to `sessions dump` table output only.
- Does **not** apply to `sessions transcribe`.
- Does **not** apply to `--follow` mode (streaming).
- JSON/XML output should also collapse (the range can be represented as `{"seq_start": 555, "seq_end": 661, ...}`).
## Implementation hint
After building the `rows` vector in `dump()`, run a post-processing pass: iterate rows and merge consecutive runs where `row[2]` (type), `row[3]` (role), and `row[4]` (content) are all equal. Replace the merged run with a single row whose `#` column shows `N-M` and whose timestamp is dropped or shows the first timestamp.
## Relevant files
- `src/commands/sessions.rs``dump()` function, row-building loop

@ -0,0 +1,48 @@
---
# claudbg-f4ot
title: Transcription header shows tools as debug HashMap, not readable list
status: todo
type: bug
created_at: 2026-03-30T04:43:50Z
updated_at: 2026-03-30T04:43:50Z
parent: claudbg-8vpb
---
## Problem
The transcription header line `Tools:` prints a raw Rust debug-format `HashMap`, e.g.:
```
Tools: {"Bash": 3, "Read": 5, "Write": 2}
```
## Expected behavior
A human-readable comma-separated summary, e.g.:
```
Tools: Bash×3, Read×5, Write×2
```
(or similar format — `Name(n)`, `Name: n`, etc. — sorted by count descending)
## Root cause
`src/commands/sessions.rs:427`:
```rust
println!("Tools: {:?}", stats.tool_calls);
```
`stats.tool_calls` is `HashMap<String, u64>`. The `:?` debug format is not user-friendly.
Same issue exists in `src/commands/agents.rs` transcribe header (prints same stats).
## Fix
Format `stats.tool_calls` by iterating entries, sorting by count descending, and joining as `"Name×N"`. The `×` (multiplication sign) or `(N)` notation both work.
## Relevant files
- `src/commands/sessions.rs` — transcribe function, `Tools:` println
- `src/commands/agents.rs` — transcribe function (same issue)
- `src/models/stats.rs``tool_calls: HashMap<String, u64>`

@ -0,0 +1,41 @@
---
# claudbg-i6l2
title: 'TUI: interactive session browser'
status: todo
type: epic
created_at: 2026-03-30T04:44:52Z
updated_at: 2026-03-30T04:44:52Z
---
Implement a full terminal UI using Ratatui for browsing Claude Code sessions.
## Design
**Navigation model:** drill-down (not split-pane)
- Screen 1: full-screen session list → Enter opens transcript
- Screen 2: full-screen transcript with embedded sub-agents panel → Tab cycles panes, Escape returns to list
- Screen 3: sub-agent transcript (same layout as Screen 2, Escape returns to parent transcript)
**Session list screen**
Columns: short ID, datetime, project (truncated tail), model, message count, sub-agent count.
Sorted most-recent-first.
**Transcript screen**
- Header: session ID, model, tokens (in/out/cache), tool call summary, duration
- Scrollable chat log: user messages, assistant text, tool use blocks (truncated), tool results (truncated)
- Long lines truncated with horizontal scroll
- Sub-agents panel (Tab to focus): list of sub-agent runs for this session; Enter navigates to sub-agent transcript
- Thinking blocks hidden by default
**Keyboard bindings**
- `Up`/`Down`/`k`/`j` — navigate within pane
- `Left`/`Right`/`h`/`l` — horizontal scroll in transcript
- `Tab` — cycle focus between panes
- `Enter` — drill into selection
- `Escape` — go back
- `q`/`Q` — quit confirmation dialog (q=confirm, Esc=cancel)
- `?` — help modal listing all shortcuts (Esc to close)
**Non-goals for this milestone**
- Live refresh / follow mode (future feature)
- Color themes / configuration

@ -0,0 +1,49 @@
---
# claudbg-kg0v
title: Tool use input truncated at 120 chars regardless of --verbose
status: todo
type: bug
created_at: 2026-03-30T04:44:31Z
updated_at: 2026-03-30T04:44:31Z
parent: claudbg-8vpb
---
## Problem
Tool use input (arguments passed to a tool) is hard-capped at 120 characters in transcriptions, cutting off commands mid-content:
```
[tool: Bash] {"command":"nix develop --command bash -c \"./target/debug/claudbg --help && echo '===' && ./target/debug/claudbg sessio
```
## Expected behavior
- Default: truncate at 120 chars (or a slightly higher value like 200) with a `…` indicator.
- `--verbose`: show full input, no truncation.
## Current behavior
Both `render_entry_text` implementations use a hardcoded 120-char cap regardless of `opts.verbose`:
```rust
// src/commands/sessions.rs:108-110
let input_preview = serde_json::to_string(input).unwrap_or_default();
let boundary = input_preview.floor_char_boundary(120);
let input_short = &input_preview[..boundary];
```
The `opts.verbose` flag is checked nowhere in the tool use branch.
## Fix
```rust
let cap = if opts.verbose { usize::MAX } else { 120 };
let boundary = input_preview.floor_char_boundary(cap);
```
Same change needed in `agents.rs`.
## Relevant files
- `src/commands/sessions.rs``render_entry_text`, `ToolUse` branch (~lines 107111)
- `src/commands/agents.rs``render_entry_text`, `ToolUse` branch (~lines 6872)

@ -0,0 +1,10 @@
---
# claudbg-kxii
title: 'TUI: interactive session browser'
status: todo
type: epic
created_at: 2026-03-30T04:34:21Z
updated_at: 2026-03-30T04:34:21Z
---
Implement a full terminal UI for claudbg using ratatui + crossterm. The TUI provides drill-down navigation: a full-screen session list, a transcript view with a sub-agents side panel, and nested sub-agent transcript views. Data is loaded once on screen entry (no live refresh in this iteration). Navigation is keyboard-driven (vim-style + arrow keys). Modals for quit confirmation and a help screen are included.

@ -0,0 +1,56 @@
---
# claudbg-nq36
title: 'TUI: app state model and screen enum'
status: todo
type: task
priority: normal
created_at: 2026-03-30T04:45:19Z
updated_at: 2026-03-30T04:49:03Z
parent: claudbg-i6l2
blocked_by:
- claudbg-78xt
---
Define the central `AppState` struct and `Screen` enum that drive the entire TUI.
## Types to define (`src/tui/state.rs` or `src/commands/tui/state.rs`)
```rust
pub enum Screen {
SessionList,
Transcript { session_id: String },
SubagentTranscript { parent_session_id: String, agent_id: String },
}
pub enum Focus {
ChatLog,
SubagentsPanel,
}
pub struct AppState {
pub screen: Screen,
// Session list
pub sessions: Vec<SessionListItem>, // loaded once
pub list_selected: usize,
// Transcript
pub transcript_entries: Vec<RawEntry>,
pub transcript_scroll: usize, // vertical scroll offset
pub transcript_h_scroll: usize, // horizontal scroll offset
pub subagents: Vec<AgentRef>,
pub subagent_selected: usize,
pub focus: Focus,
// Modals
pub show_quit_dialog: bool,
pub show_help: bool,
}
```
## Notes
- `SessionListItem` is a lightweight struct with the display fields (short_id, date, project, model, msg_count, agent_count).
- State transitions: `AppState::enter_transcript(session_id)` loads entries + subagents. `AppState::go_back()` pops the screen stack (or just switches back to `SessionList`).
- Keep all data loading in state transitions so the render function is pure.
## Blocked by
- ratatui dependency task (claudbg-78xt)

@ -0,0 +1,51 @@
---
# claudbg-nqxz
title: Duration shows 0ms — RawEntry.duration_ms field name mismatch (durationMs vs duration_ms)
status: todo
type: bug
priority: high
created_at: 2026-03-30T04:44:06Z
updated_at: 2026-03-30T04:44:06Z
parent: claudbg-8vpb
---
## Problem
`sessions transcribe` shows `Duration: 0ms` even for long sessions.
## Root cause
The JSONL file uses camelCase `durationMs` at the entry level:
```json
{"type": "system", "durationMs": 3446227, ...}
```
But `RawEntry` declares the field as `duration_ms: Option<u64>` with no serde rename:
```rust
// src/models/session.rs:33
pub duration_ms: Option<u64>,
```
Serde looks for `duration_ms` (snake_case), finds nothing, and the value falls into the `extra` catch-all map instead. `compute_stats` then sums zeros.
## Fix
Add `#[serde(rename = "durationMs")]` to the field:
```rust
#[serde(rename = "durationMs")]
pub duration_ms: Option<u64>,
```
Alternatively use `#[serde(alias = "durationMs")]` to accept both forms.
## Confirmed by
Running against session `21fae0a8`: `grep '"durationMs":[0-9]'` finds entries like `{"type":"system","durationMs":3446227,...}` confirming the field name is camelCase.
## Relevant files
- `src/models/session.rs``RawEntry`, `duration_ms` field (line 33)
- `src/models/stats.rs``compute_stats`, accumulates `entry.duration_ms`

@ -0,0 +1,40 @@
---
# claudbg-pfa5
title: JSON output is array-of-arrays instead of array-of-objects
status: todo
type: bug
priority: normal
created_at: 2026-03-30T04:37:05Z
updated_at: 2026-03-30T04:41:14Z
parent: claudbg-tci9
---
## Problem
`sessions list --output json` and `sessions dump --output json` serialize rows as `Vec<Vec<String>>`, producing an array of arrays with no field names:
```json
[["21fae0a8", "2024-01-01", "/path", "claude-3", "42"], ...]
```
## Expected behavior
Output should be an array of objects with named keys:
```json
[{"id": "21fae0a8", "date": "2024-01-01", "project": "/path", "model": "claude-3", "messages": 42, "subagents": 0}, ...]
```
## Affected commands
- `sessions list --output json`: keys should be `id`, `date`, `project`, `model`, `messages`, `subagents`
- `sessions dump --output json`: keys should be `seq`, `timestamp`, `type`, `role`, `content`
## Root cause
`src/commands/sessions.rs` passes `&rows: &Vec<Vec<String>>` directly to `render_json`. The fix is to build a typed struct or `serde_json::Value` map per row before serializing.
## Relevant files
- `src/commands/sessions.rs``list()` and `dump()` JSON output branches
- `src/output/json.rs` — generic `render_json`

@ -0,0 +1,40 @@
---
# claudbg-pta8
title: 'TUI: session list screen widget'
status: todo
type: feature
priority: normal
created_at: 2026-03-30T04:45:53Z
updated_at: 2026-03-30T04:49:03Z
parent: claudbg-i6l2
blocked_by:
- claudbg-nq36
---
Render the full-screen session list as the TUI's home screen.
## Layout
```
┌─ Sessions ──────────────────────────────────────────────────────┐
│ ID Date Project Model Msgs Agents │
│ ────────────────────────────────────────────────────────────────────── │
│ 21fae0a8 2026-03-29 14:32:01 …/pop/claudbg claude-3 42 3 │◄ selected (highlighted)
│ def4776b 2026-03-28 10:15:44 …/pop/claudbg claude-3 128 1 │
│ ... │
└─────────────────────────────────────────────────────────────────┘
```
## Implementation
- Use `ratatui::widgets::Table` with `TableState` for selection tracking.
- Highlight the selected row with a `Style` that inverts colors or uses a distinct background.
- Columns: ID (8), Date (20), Project (dynamic, truncated to tail), Model (20), Msgs (6), Agents (7).
- Project path: display only the last 30 or so characters (prefix with `…`).
- Populate from `AppState.sessions` (loaded on TUI start from `discover_sessions` + DB).
- Scroll with arrow keys / j/k; selected index tracked in `AppState.list_selected`.
## Blocked by
- app state model (claudbg-nq36)
- agent discovery fix (claudbg-33n0) — for accurate agent counts

@ -0,0 +1,46 @@
---
# claudbg-rudq
title: 'TUI: transcript screen — chat log and stats header'
status: todo
type: feature
priority: normal
created_at: 2026-03-30T04:46:56Z
updated_at: 2026-03-30T04:49:03Z
parent: claudbg-i6l2
blocked_by:
- claudbg-ut9q
---
Render the full-screen transcript view for a session or sub-agent.
## Layout
```
Session: 21fae0a8 Model: claude-3-opus Tokens: in=12k out=4k Duration: 57min
Tools: Bash×12, Read×8, Write×3
────────────────────────────────────────────────────────────────────────────
[user]: Please implement the sessions list command…
[assistant]: I'll start by reading the existing code structure…
[tool: Read] {"file_path": "/path/to/file.rs"}
[tool_result]: pub struct SessionRef {\n pub session_id: String,\n pub project…
[assistant]: Here is the implementation:
```
## Implementation
- Split the frame: top block = stats header (fixed ~3 lines), middle = scrollable chat log, right panel = sub-agents (see sub-agents task).
- Use `ratatui::widgets::Paragraph` with `Wrap { trim: false }` for the header.
- Chat log: build as a `Vec<Line>` (ratatui spans/lines). Each entry becomes one or more lines. Use `Paragraph::new(text).scroll((v_offset, h_offset))` for scroll.
- Horizontal scroll for long lines: track `transcript_h_scroll` in state, increment/decrement with left/right keys.
- Vertical scroll: `transcript_scroll` in state, up/down keys.
- Thinking blocks: hidden by default (filter from `entries` before rendering).
- Tool results: truncated to ~200 chars by default.
## Data source
Load `RawEntry` list from raw JSONL via `read_session_file` when entering transcript screen. Compute `SessionStats` for the header.
## Blocked by
- app state (claudbg-nq36), event loop (claudbg-ut9q)

@ -0,0 +1,10 @@
---
# claudbg-tci9
title: Session list & dump improvements
status: todo
type: epic
created_at: 2026-03-30T04:34:16Z
updated_at: 2026-03-30T04:34:16Z
---
Groups bugs and improvements to the `sessions list` and `sessions dump` commands: table overflow, JSON output format, --include help text, duplicate row collapsing, and sub-agent count.

@ -0,0 +1,38 @@
---
# claudbg-ut9q
title: 'TUI: terminal setup, event loop, and teardown'
status: todo
type: task
priority: normal
created_at: 2026-03-30T04:45:32Z
updated_at: 2026-03-30T04:49:03Z
parent: claudbg-i6l2
blocked_by:
- claudbg-nq36
---
Implement terminal initialization, the main event loop, and graceful teardown.
## Responsibilities
1. **Setup:** enable raw mode (`crossterm::terminal::enable_raw_mode`), switch to alternate screen (`EnterAlternateScreen`), create `CrosstermBackend`, instantiate `ratatui::Terminal`.
2. **Panic hook:** install a panic hook that restores the terminal before printing the panic message (prevents garbled terminal on crash).
3. **Event loop:** `loop { terminal.draw(|f| render(f, &state)); if event::poll(Duration::from_millis(50))? { handle_event(event::read()?, &mut state); } if state.should_quit { break; } }`
4. **Teardown:** disable raw mode, leave alternate screen.
## Entry point
Replaces the stub in `src/commands/stubs.rs` (the `Tui` command handler). The `tui()` async function should call into a synchronous `run_tui()` function since ratatui is sync.
## Notes
- Use `crossterm::event::Event` for keyboard input — no mouse needed for this milestone.
- The 50 ms poll timeout gives ~20 fps without busy-spinning.
- Teardown must run even on error: use a RAII guard or wrap in a closure.
## Blocked by
- ratatui deps (claudbg-78xt), app state (claudbg-nq36)

@ -0,0 +1,42 @@
---
# claudbg-xpzp
title: '`sessions list` should show sub-agent run count per session'
status: todo
type: bug
priority: normal
created_at: 2026-03-30T04:40:53Z
updated_at: 2026-03-30T04:43:26Z
parent: claudbg-tci9
blocked_by:
- claudbg-33n0
---
## Problem
`sessions list` has no column showing how many sub-agent runs a session spawned, making it impossible to identify sessions that used the Agent tool.
## Expected behavior
Add a `Sub-agents` column to `sessions list` showing the count of sub-agent JSONL files associated with each session. Zero for sessions with no sub-agents.
## Disk structure (important)
The actual subagent files live at:
```
~/.claude/projects/<project-dir>/<session-uuid>/subagents/agent-<id>.jsonl
```
Note: there is a *directory* named `<session-uuid>` (without `.jsonl`) alongside the session file that contains the `subagents/` folder.
The existing `discover_agents_for_session(session_file)` function currently looks in the *wrong place* (see separate bug). Once that is fixed, the agent count can be obtained by calling `discover_agents_for_session` for each session and taking `.len()`.
## Implementation notes
- In `sessions list`, after syncing sessions, call `discover_agents_for_session(&session_ref.file_path)` for each session to get the count.
- Add the count as the last column in the table row.
- Update JSON output to include `subagents` field.
- This is blocked on the subagent path discovery bug being fixed first.
## Relevant files
- `src/commands/sessions.rs``list()` function
- `src/parser/discovery.rs``discover_agents_for_session`

@ -0,0 +1,43 @@
---
# claudbg-zy1p
title: Tool results hidden by default in transcriptions — should show with truncation
status: todo
type: bug
created_at: 2026-03-30T04:44:18Z
updated_at: 2026-03-30T04:44:18Z
parent: claudbg-8vpb
---
## Problem
Tool results (the output of tool calls) are hidden unless the user passes `--include output`. Since tool outputs are a core part of understanding what happened in a session, they should be visible by default.
## Expected behavior
- Tool results shown by default (no flag needed).
- Truncated to ~200 chars by default (current limit, keep it).
- `--verbose` removes the truncation cap and shows full output.
- The `--include output` flag / `opts.include.output` gate should be removed (or inverted).
## Current behavior
`render_entry_text` in both `sessions.rs` and `agents.rs` gates `ToolResult` on `opts.include.output`:
```rust
if opts.include.output {
// show tool result
}
```
With the default `--include ""`, `opts.include.output` is `false`, so results are never shown.
## Fix
Remove the `if opts.include.output` guard (or replace it with the inverse: hide only when an explicit `--include no-output` flag is passed, but that's over-engineering — just always show with truncation).
Also apply the same change to the `--verbose` path: when `opts.verbose` is true, show the full content uncapped.
## Relevant files
- `src/commands/sessions.rs``render_entry_text`, `ToolResult` branch (~line 113131)
- `src/commands/agents.rs``render_entry_text`, same branch (~line 7492)
Loading…
Cancel
Save