# How the Hindsight TUI Explorer Navigates Banks and Memories

> Explore Hindsight TUI explorer navigation for banks and memories. Discover a k9s-style hierarchical browser with async queries and auto-refresh for efficient data inspection.

- Repository: [vectorize-io/hindsight](https://github.com/vectorize-io/hindsight)
- Tags: how-to-guide
- Published: 2026-03-13

---

**The Hindsight TUI explorer provides a keyboard-driven, k9s-style hierarchical browser that lets users navigate from bank selection to memory inspection using Enter and Esc keys, with background async queries and auto-refresh capabilities powered by the ratatui crate.**

The Hindsight TUI explorer is a full-screen terminal interface built for the vectorize-io/hindsight semantic memory system. Using the Rust-based **ratatui** crate, it renders an interactive hierarchy that allows developers to browse banks, inspect stored memories, and execute recall queries without leaving the command line.

## Core Navigation Flow

### Selecting a Bank (View::Banks)

When you launch the explorer with `hindsight explore`, the interface starts in the **Banks** view. In [`hindsight-cli/src/commands/explore.rs`](https://github.com/vectorize-io/hindsight/blob/main/hindsight-cli/src/commands/explore.rs), the `App::load_banks()` method (lines 36‑44) calls `client.list_agents(false)` to fetch all available banks as `BankListItem` structs. These populate `self.banks`, and the UI selects the first entry by default using a `ListState`.

The view state is defined by the `View::Banks` variant (lines 30‑34). This initial screen presents a scrollable list where you can use arrow keys to highlight different banks before pressing **Enter** to drill down.

### Opening Bank Memories

Pressing **Enter** on a bank triggers `App::enter_view()` (lines 604‑616). This method pushes the current view onto a `view_history` stack, then sets `self.view = View::Memories(bank_id)` and calls `self.load_memories(&bank_id)` to fetch the bank's contents. The `View` enum tracks the active screen, and the `bank_id()` helper (lines 49‑53) extracts the identifier for sub-views.

### Browsing the Memory List

Once inside a bank, the **Memories** view renders a scrollable table showing each memory's type, mentioned-at timestamp, occurred-at timestamp, and a truncated text snippet. The `render_memories()` function (lines 735‑758) constructs a `ratatui::widgets::List` of `ListItem`s from `self.memories`, preserving the selected index across refreshes via `&mut app.memories_state`.

Long fields are handled with horizontal scrolling managed by `self.horizontal_scroll`, allowing you to view truncated content without leaving the list view.

### Inspecting Memory Details

To inspect a specific memory, press **Enter** on any row. The `enter_view()` method handles this via the `View::Memories` branch (lines 618‑623), which stores the selected memory in `self.viewing_memory`. The render path at lines 774‑818 then checks `if let Some(memory) = &app.viewing_memory` and draws a detailed panel displaying full metadata and the complete `text` field.

Press **Esc** to close the detail view and return to the memory list. Press **Esc** again from the list to pop the previous view from `view_history` via `App::go_back()` (lines 734‑761), returning you to the bank selection screen.

### Executing Queries

From any view, press **/** to open the **Query** view. After typing a query and hitting **Enter**, the `execute_query()` method (lines 309‑376) spawns a background thread that calls either `client.recall` or `client.reflect` depending on `self.query_mode`. Results are delivered asynchronously via an `mpsc` channel as `QueryResult` variants (enum defined at lines 71‑75), allowing the UI to remain responsive while the server processes the request. The `render_query()` function (lines 1243‑1329) displays the final results or reflection.

## Architecture and State Management

### The App Struct

The core state machine resides in the `App` struct within [`hindsight-cli/src/commands/explore.rs`](https://github.com/vectorize-io/hindsight/blob/main/hindsight-cli/src/commands/explore.rs). It holds the `ApiClient` instance, the current `View` enum, a history stack for navigation, and multiple `ListState` objects for managing selection indices across banks, memories, entities, documents, and query results. Additional flags track UI states such as `loading`, `help`, and `auto_refresh_enabled`.

### Background Query Processing

To prevent blocking the main thread during expensive recall operations, the explorer uses a multi-producer, single-consumer (`mpsc`) channel. When `execute_query()` spawns a worker thread, it sends `QueryResult` values back to the main loop, which calls `check_query_result()` to update `app.query_results` without freezing the interface.

### Auto-Refresh Mechanism

The explorer supports automatic data refreshing every five seconds. The `toggle_auto_refresh()` method (lines 214‑222) flips the `auto_refresh_enabled` flag, while `do_auto_refresh()` (lines 228‑233) checks `should_refresh()` (lines 24‑26) and calls `self.refresh()` to reload data for the current view. This ensures you are always viewing the latest memories without manual intervention.

## Practical Usage Examples

### Launching the Explorer

```bash

# Start the interactive TUI (k9s-style interface)

hindsight explore

# or use the alias

hindsight tui

```

### Typical Navigation Sequence

```rust
let mut app = App::new(client);

// Load and display all banks
app.refresh()?;  // triggers load_banks()
ui::draw(&mut terminal, &mut app);

// User selects the third bank (index 2)
app.banks_state.select(Some(2));
app.enter_view()?;  // switches to View::Memories(bank_id)

// Load memories for the selected bank
app.refresh()?;  // triggers load_memories(bank_id)
ui::draw(&mut terminal, &mut app);

// User selects a specific memory (index 5)
app.memories_state.select(Some(5));
app.enter_view()?;  // sets viewing_memory = Some(memory)

// Render detail view
ui::draw(&mut terminal, &mut app);

// Press Esc to return to memory list
app.go_back();
ui::draw(&mut terminal, &mut app);

```

### Programmatic API Access (Non-TUI)

If you need the same data without the terminal interface, use the underlying `ApiClient` directly from [`hindsight-cli/src/api.rs`](https://github.com/vectorize-io/hindsight/blob/main/hindsight-cli/src/api.rs):

```rust
let client = ApiClient::new("http://localhost:8888", None)?;
let banks = client.list_agents(false)?;  // List all banks

let memories = client.list_memories(
    "my-bank", 
    None, 
    None, 
    Some(500),  // limit
    Some(0),    // offset
    false
)?;

let recall = client.recall("my-bank", &RecallRequest {
    query: "what did we decide about the UI?".into(),
    budget: Some(Budget::Mid),
    ..Default::default()
}, false)?;

```

## Summary

- **The Hindsight TUI explorer** is implemented in [`hindsight-cli/src/commands/explore.rs`](https://github.com/vectorize-io/hindsight/blob/main/hindsight-cli/src/commands/explore.rs) using the ratatui crate for terminal rendering.
- **Navigation** relies on a view stack (`View::Banks` → `View::Memories` → detail view) managed by `enter_view()` and `go_back()`.
- **State preservation** uses `ListState` objects to maintain selection indices across banks and memories during refreshes.
- **Async queries** run in background threads via `mpsc` channels, keeping the UI responsive during recall or reflect operations.
- **Auto-refresh** occurs every five seconds when enabled, reloading data via `do_auto_refresh()`.

## Frequently Asked Questions

### How do I launch the Hindsight TUI explorer?

Run `hindsight explore` or its alias `hindsight tui` from your terminal. This command initializes the ratatui-based interface and immediately calls `App::load_banks()` to populate the initial bank selection screen.

### What keyboard shortcuts are available for navigation?

Use **↑/↓** to navigate lists, **Enter** to open a selected bank or memory, and **Esc** to go back. Press **←/→** to horizontally scroll long text fields in memory details. Press **/** to open the query view, **R** to force a manual refresh, and **Q** or **Ctrl+C** to quit.

### How does the TUI handle data refreshes?

The explorer checks `should_refresh()` every five seconds when auto-refresh is enabled (`toggle_auto_refresh()` at lines 214‑222). The `do_auto_refresh()` method (lines 228‑233) triggers `self.refresh()`, which reloads data for the current view (banks or memories) while preserving your selected index via `ListState`.

### Can I query memories without using the interactive interface?

Yes. The same API calls used by the TUI are available programmatically through `ApiClient` in [`hindsight-cli/src/api.rs`](https://github.com/vectorize-io/hindsight/blob/main/hindsight-cli/src/api.rs). You can call `client.recall()` or `client.reflect()` directly from Rust code or use the Hindsight HTTP API, bypassing the terminal interface entirely while accessing identical functionality.