A TUI tool that helps navigating terminal scrollback buffer by folding command outputs. Navigate between commands, view their outputs, yank full or partial results to clipboard via line selection.
Supports tmux, WezTerm, kitty, and GNU screen with auto-detection.
Warning: Mostly vibe coded in 30 minutes for fun. Expect rough edges and quirks. Contributions welcome!
Terminal is mostly flat stream of text: prompt, output, another prompt, another output...
When you have a long session, finding a specific command's output means scrolling through everything. fold-er parses your scrollback into discrete command blocks and lets you browse them like a folding editor, one command at a time.
# Auto-detect multiplexer, capture scrollback, open viewer in new window/tab
fold-er
# Open in a horizontal split instead of new window
fold-er --split h
# Open in a vertical split
fold-er --split v
Running fold-er with no arguments detects your terminal multiplexer, captures scrollback from the current pane, and spawns the viewer in a new window (or split). No external scripts needed.
When an explicit input source is given, fold-er opens the TUI directly in the current terminal:
# Read from specific wezterm pane
fold-er --pane-id 3
# Read scrollback text from file
fold-er --text-file /tmp/scrollback.txt
# Pipe from stdin
some-command | fold-er --stdin
Normal mode
| Key | Action |
|---|---|
| j/k, Up/Down | Navigate between commands |
| PgUp/PgDn | Scroll output pane |
| Ctrl+u/d | Scroll output pane (vim-style) |
| g/Home | Jump to first command |
| G/End | Jump to last command |
| y | Yank full output to clipboard |
| v | Enter line select mode |
| q, Esc | Quit |
Line select mode
Press v to enter line select mode. The output pane shows a cursor on a single line.
- Browse phase: Navigate to the start of your selection using
j/kor arrow keys. - Mark start: Press
EnterorSpaceto mark the beginning of the selection. - Extend: Move with
j/kor arrow keys to extend the selection to the end. - Yank: Press
yto copy the selected lines to clipboard.
| Key | Action |
|---|---|
| j/k, Up/Down | Browse (phase 1) / Extend (phase 2) |
| PgUp/PgDn | Jump cursor by one page |
| Ctrl+u/d | Jump cursor by one page (vim-style) |
| Enter, Space | Mark start of selection |
| g/Home | Jump cursor to first line |
| G/End | Jump cursor to last line |
| y | Yank selected lines (after marking) |
| v, Esc, q | Cancel and return to normal mode |
fold-er uses three strategies to identify command boundaries, in priority order:
- OSC 133 semantic zones (via
--zones-file): Pre-extracted JSON from wezterm's Lua API. Most reliable but currently broken with wezterm's mux mode (see known issues). - OSC 133 markers in text: If the captured scrollback contains OSC 133 escape sequences (tmux and wezterm preserve these), fold-er parses them directly.
- Heuristic auto-detection: Looks at the last line of scrollback (your current prompt), extracts the hostname prefix and prompt ending (e.g.
~>,$,#), then matches all lines with the same pattern.
Heuristic detection works well in most cases but is not bulletproof. It can misfire when scrollback is dominated by log output (e.g. dmesg inside GNU screen with a small scrollback buffer). Use --prompt to override:
# Tell fold-er your prompt contains "~>"
fold-er --prompt "~>"
# Also works in direct mode
fold-er --text-file /tmp/sb.txt --prompt "myhost $"
The --prompt value is matched as a substring against each line. Any line containing it is treated as a command prompt.
fold-er auto-detects which multiplexer you're running in. Detection order:
| Multiplexer | Detection | Capture method | Split support |
|---|---|---|---|
| tmux | $TMUX set |
tmux capture-pane -p -e |
Yes |
| WezTerm | $WEZTERM_PANE set |
wezterm cli get-text --escapes |
Yes |
| kitty | $KITTY_PID + $TERM=xterm-kitty |
kitty @ get-text --ansi |
Yes |
| GNU screen | $STY + $TERM=screen* |
screen -X hardcopy -h |
No (new window only) |
Detection ignores inherited env vars from parent terminals. For example, launching alacritty from within WezTerm won't falsely detect WezTerm as the active multiplexer.
Standalone terminals (alacritty, foot, ghostty, etc.) don't expose their scrollback buffer programmatically. If you use one of these, run tmux or screen inside it and fold-er will work through that. Alternatively, use --text-file or --stdin to feed scrollback manually.
GNU screen note: hardcopy -h can only capture what screen keeps in its scrollback buffer. The default is 100 lines, which is very small. Add defscrollback 20000 to your ~/.screenrc for useful results.
# In .tmux.conf
bind-key f run-shell "fold-er"
bind-key F run-shell "fold-er --split h"
Option A (type in terminal or bind to shell alias):
fold-er
Option B (keybind with callback, passes correct pane ID):
{
key = 'f',
mods = 'ALT',
action = wezterm.action_callback(function(win, pane)
wezterm.background_child_process({
'fold-er', '--source-pane', tostring(pane:pane_id())
})
end),
},Note: --source-pane is needed for WezTerm keybinds because $WEZTERM_PANE in a
SpawnCommand context points to the new pane, not the source.
Requires allow_remote_control yes (or allow_remote_control socket-only) in kitty.conf.
# In kitty.conf
map ctrl+shift+f launch --type=background fold-er
Pressing y pipes the selected command's output (ANSI-stripped) to a clipboard command via stdin. In line select mode (v), only the selected lines are yanked. The clipboard command is auto-detected from your environment:
| Environment | Command |
|---|---|
Wayland ($WAYLAND_DISPLAY set) |
wl-copy |
X11 ($DISPLAY set) |
xclip -selection clipboard, or xsel --clipboard --input |
| macOS | pbcopy |
| SSH/tmux (fallback) | oscyank |
Override with --yank-cmd:
fold-er --text-file /tmp/sb.txt --yank-cmd 'xclip -selection clipboard'
When using wezterm with unix_domains (mux mode), panes in the GUI process are ClientPane instances that proxy to LocalPane in the mux server. The ClientPane type does not override get_semantic_zones(), so it falls through to the trait default which returns an empty vec. The shell integration hooks emit OSC 133 correctly and the mux server's LocalPane has the zones, but there is no RPC call to retrieve them from the client side.
This is a missing feature in wezterm's mux client, not a configuration issue. As a workaround, fold-er falls back to wezterm cli get-text with heuristic prompt detection, which works reliably.
Relevant code path: wezterm-client/src/pane/clientpane.rs has impl Pane for ClientPane but does not implement get_semantic_zones, inheriting the empty default from mux/src/pane.rs:303.
Relevant bug report: wezterm/wezterm#4229
cargo build --release