diff --git a/.beans/claudbg-1e1c--tui-quit-confirmation-dialog.md b/.beans/claudbg-1e1c--tui-quit-confirmation-dialog.md new file mode 100644 index 0000000..9061838 --- /dev/null +++ b/.beans/claudbg-1e1c--tui-quit-confirmation-dialog.md @@ -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) diff --git a/.beans/claudbg-1tlk--tui-help-modal-listing-all-keyboard-shortcuts.md b/.beans/claudbg-1tlk--tui-help-modal-listing-all-keyboard-shortcuts.md new file mode 100644 index 0000000..3b32507 --- /dev/null +++ b/.beans/claudbg-1tlk--tui-help-modal-listing-all-keyboard-shortcuts.md @@ -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) diff --git a/.beans/claudbg-33n0--agent-discovery-looks-in-wrong-subagents-directory.md b/.beans/claudbg-33n0--agent-discovery-looks-in-wrong-subagents-directory.md new file mode 100644 index 0000000..cbaa9ac --- /dev/null +++ b/.beans/claudbg-33n0--agent-discovery-looks-in-wrong-subagents-directory.md @@ -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 ` 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// +let subagents_dir = parent.join("subagents"); // ~/.claude/projects//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// + .jsonl ← session file + / ← directory with same name as UUID (no .jsonl) + subagents/ + agent-.jsonl + agent-.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 `/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 198–205), `discover_all_agents` (lines 211–266), `collect_agents_in_dir` +- `src/commands/agents.rs` — integration tests diff --git a/.beans/claudbg-6dgc--table-overflows-terminal-truncate-long-columns-and.md b/.beans/claudbg-6dgc--table-overflows-terminal-truncate-long-columns-and.md new file mode 100644 index 0000000..55ae6b2 --- /dev/null +++ b/.beans/claudbg-6dgc--table-overflows-terminal-truncate-long-columns-and.md @@ -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` diff --git a/.beans/claudbg-78xt--tui-add-ratatui-crossterm-dependencies.md b/.beans/claudbg-78xt--tui-add-ratatui-crossterm-dependencies.md new file mode 100644 index 0000000..2473134 --- /dev/null +++ b/.beans/claudbg-78xt--tui-add-ratatui-crossterm-dependencies.md @@ -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` diff --git a/.beans/claudbg-8vpb--transcription-output-improvements.md b/.beans/claudbg-8vpb--transcription-output-improvements.md new file mode 100644 index 0000000..b3bc531 --- /dev/null +++ b/.beans/claudbg-8vpb--transcription-output-improvements.md @@ -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. diff --git a/.beans/claudbg-9c8r--tui-sub-agents-panel-in-transcript-view.md b/.beans/claudbg-9c8r--tui-sub-agents-panel-in-transcript-view.md new file mode 100644 index 0000000..6a0156d --- /dev/null +++ b/.beans/claudbg-9c8r--tui-sub-agents-panel-in-transcript-view.md @@ -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` populated via `discover_agents_for_session` when entering transcript screen. + +## Blocked by + +- transcript screen (claudbg-rudq) +- agent discovery fix (claudbg-33n0) diff --git a/.beans/claudbg-a532--include-help-text-should-enumerate-valid-values.md b/.beans/claudbg-a532--include-help-text-should-enumerate-valid-values.md new file mode 100644 index 0000000..6be9b6e --- /dev/null +++ b/.beans/claudbg-a532--include-help-text-should-enumerate-valid-values.md @@ -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 diff --git a/.beans/claudbg-d9ev--sessions-dump-should-collapse-consecutive-identica.md b/.beans/claudbg-d9ev--sessions-dump-should-collapse-consecutive-identica.md new file mode 100644 index 0000000..512d3b6 --- /dev/null +++ b/.beans/claudbg-d9ev--sessions-dump-should-collapse-consecutive-identica.md @@ -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 555–661 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 | | +``` + +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 diff --git a/.beans/claudbg-f4ot--transcription-header-shows-tools-as-debug-hashmap.md b/.beans/claudbg-f4ot--transcription-header-shows-tools-as-debug-hashmap.md new file mode 100644 index 0000000..e5ff5e7 --- /dev/null +++ b/.beans/claudbg-f4ot--transcription-header-shows-tools-as-debug-hashmap.md @@ -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`. 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` diff --git a/.beans/claudbg-i6l2--tui-interactive-session-browser.md b/.beans/claudbg-i6l2--tui-interactive-session-browser.md new file mode 100644 index 0000000..fa2c739 --- /dev/null +++ b/.beans/claudbg-i6l2--tui-interactive-session-browser.md @@ -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 diff --git a/.beans/claudbg-kg0v--tool-use-input-truncated-at-120-chars-regardless-o.md b/.beans/claudbg-kg0v--tool-use-input-truncated-at-120-chars-regardless-o.md new file mode 100644 index 0000000..dfc6f7b --- /dev/null +++ b/.beans/claudbg-kg0v--tool-use-input-truncated-at-120-chars-regardless-o.md @@ -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 107–111) +- `src/commands/agents.rs` — `render_entry_text`, `ToolUse` branch (~lines 68–72) diff --git a/.beans/claudbg-kxii--tui-interactive-session-browser.md b/.beans/claudbg-kxii--tui-interactive-session-browser.md new file mode 100644 index 0000000..ec13f67 --- /dev/null +++ b/.beans/claudbg-kxii--tui-interactive-session-browser.md @@ -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. diff --git a/.beans/claudbg-nq36--tui-app-state-model-and-screen-enum.md b/.beans/claudbg-nq36--tui-app-state-model-and-screen-enum.md new file mode 100644 index 0000000..53259ac --- /dev/null +++ b/.beans/claudbg-nq36--tui-app-state-model-and-screen-enum.md @@ -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, // loaded once + pub list_selected: usize, + // Transcript + pub transcript_entries: Vec, + pub transcript_scroll: usize, // vertical scroll offset + pub transcript_h_scroll: usize, // horizontal scroll offset + pub subagents: Vec, + 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) diff --git a/.beans/claudbg-nqxz--duration-shows-0ms-rawentryduration-ms-field-name.md b/.beans/claudbg-nqxz--duration-shows-0ms-rawentryduration-ms-field-name.md new file mode 100644 index 0000000..74ee6bd --- /dev/null +++ b/.beans/claudbg-nqxz--duration-shows-0ms-rawentryduration-ms-field-name.md @@ -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` with no serde rename: + +```rust +// src/models/session.rs:33 +pub duration_ms: Option, +``` + +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, +``` + +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` diff --git a/.beans/claudbg-pfa5--json-output-is-array-of-arrays-instead-of-array-of.md b/.beans/claudbg-pfa5--json-output-is-array-of-arrays-instead-of-array-of.md new file mode 100644 index 0000000..10bfdb8 --- /dev/null +++ b/.beans/claudbg-pfa5--json-output-is-array-of-arrays-instead-of-array-of.md @@ -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>`, 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>` 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` diff --git a/.beans/claudbg-pta8--tui-session-list-screen-widget.md b/.beans/claudbg-pta8--tui-session-list-screen-widget.md new file mode 100644 index 0000000..df390ef --- /dev/null +++ b/.beans/claudbg-pta8--tui-session-list-screen-widget.md @@ -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 diff --git a/.beans/claudbg-rudq--tui-transcript-screen-chat-log-and-stats-header.md b/.beans/claudbg-rudq--tui-transcript-screen-chat-log-and-stats-header.md new file mode 100644 index 0000000..66b8a66 --- /dev/null +++ b/.beans/claudbg-rudq--tui-transcript-screen-chat-log-and-stats-header.md @@ -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` (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) diff --git a/.beans/claudbg-tci9--session-list-dump-improvements.md b/.beans/claudbg-tci9--session-list-dump-improvements.md new file mode 100644 index 0000000..03e6f7b --- /dev/null +++ b/.beans/claudbg-tci9--session-list-dump-improvements.md @@ -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. diff --git a/.beans/claudbg-ut9q--tui-terminal-setup-event-loop-and-teardown.md b/.beans/claudbg-ut9q--tui-terminal-setup-event-loop-and-teardown.md new file mode 100644 index 0000000..9fd6431 --- /dev/null +++ b/.beans/claudbg-ut9q--tui-terminal-setup-event-loop-and-teardown.md @@ -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) diff --git a/.beans/claudbg-xpzp--sessions-list-should-show-sub-agent-run-count-per.md b/.beans/claudbg-xpzp--sessions-list-should-show-sub-agent-run-count-per.md new file mode 100644 index 0000000..af39bdf --- /dev/null +++ b/.beans/claudbg-xpzp--sessions-list-should-show-sub-agent-run-count-per.md @@ -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///subagents/agent-.jsonl +``` +Note: there is a *directory* named `` (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` diff --git a/.beans/claudbg-zy1p--tool-results-hidden-by-default-in-transcriptions-s.md b/.beans/claudbg-zy1p--tool-results-hidden-by-default-in-transcriptions-s.md new file mode 100644 index 0000000..f13e2f3 --- /dev/null +++ b/.beans/claudbg-zy1p--tool-results-hidden-by-default-in-transcriptions-s.md @@ -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 113–131) +- `src/commands/agents.rs` — `render_entry_text`, same branch (~line 74–92)