twatch - record, rewind, inspect, and diff terminal UI screens.
twatch adds rewindable history to existing TUI applications.
Full-screen terminal apps like htop, lazygit, k9s, and nmtui constantly redraw the same screen, so normal terminal scrollback often cannot show you what happened before.
twatch runs the target app through a PTY, records its screen states, and lets you rewind, search, and diff previous frames. It can also extract selected ranges in batch mode and write them to standard output, making TUI content easier to inspect, debug, or pipe into other commands.
- Wrap a child TUI application in a PTY
- Keep
latestplus screen history with checkpoint + delta storage - Show history in an overlay pane
- Highlight changed cells in watch diff mode
- Filter history with string (
/) or regex (*) - Inspect a selected cell and compare it with the previous frame
- Record input and resize context with frame metadata
- Save and load history logs
- Replay a saved JSONL trace in read-only mode
- Save the selected snapshot as ANSI text or SVG
- Auto-save snapshots on string, regex, or change-count triggers
- Run an asynchronous experimental
aftercommandtrigger - Compress in-memory history with experimental
-C - Output the current screen in batch mode
- Propagate terminal resize to both
twatchand the child TUI
cargo install twatch$ twatch --help
watch for TUI apps
Usage: twatch [OPTIONS] [COMMAND]...
Arguments:
[COMMAND]...
Options:
-b, --batch
--batch-count <BATCH_COUNT>
Stop after emitting this many batch frames
--batch-size <WIDTH,HEIGHT>
Use a fixed PTY size such as 80,24
--batch-crop <X,Y,WIDTH,HEIGHT>
Crop batch output to a rectangle such as 10,5,40,12
--batch-diff-only
Print only added content for batch diff output
--batch-no-color
Disable ANSI color sequences in batch output
-A, --aftercommand <AFTERCOMMAND>
-K, --keymap <KEY=ACTION>
Remap twatch keys with KEY=ACTION
-k, --bind <FROM=TO>
Override child TUI keys with FROM=TO
--aftercommand-regex <AFTERCOMMAND_REGEX>
Only run aftercommand when output matches this regex
--aftercommand-change-cells <AFTERCOMMAND_CHANGE_CELLS>
Only run aftercommand when changed cell count reaches this threshold
--aftercommand-every <AFTERCOMMAND_EVERY>
Only run aftercommand on every Nth changed frame
--aftercommand-debounce-ms <AFTERCOMMAND_DEBOUNCE_MS>
Debounce aftercommand for this many milliseconds
--aftercommand-timeout-ms <AFTERCOMMAND_TIMEOUT_MS>
Kill aftercommand if it exceeds this timeout in milliseconds [default: 3000]
-C, --compress
-l, --logfile <LOGFILE>
--replay <REPLAY>
Replay a saved JSONL trace in read-only mode
--screenshot-dir <SCREENSHOT_DIR>
[default: /tmp]
--screenshot-format <SCREENSHOT_FORMAT>
[default: text] [possible values: text, svg]
--snapshot-on <SNAPSHOT_ON>
Auto-save a snapshot when the screen contains this string
--snapshot-on-regex <SNAPSHOT_ON_REGEX>
Auto-save a snapshot when the screen matches this regex
--snapshot-on-change-cells <SNAPSHOT_ON_CHANGE_CELLS>
Auto-save a snapshot when changed cell count reaches this threshold
--snapshot-once
Only trigger automatic snapshot once
-s, --shell <SHELL>
[default: "sh -c"]
-d, --differences <DIFFERENCES>
Diff mode: watch for TUI mode, list/word for batch mode [default: none] [possible values: none, watch, list, word]
-L, --limit <LIMIT>
[default: 500]
--checkpoint-interval <CHECKPOINT_INTERVAL>
[default: 12]
--debug
Show debug diagnostics in the interactive UI
-h, --help
Print help
-V, --version
Print version
| Key | Action |
|---|---|
Up, Down |
Move selected screen (history or watch) |
PageUp, PageDown |
Move selected screen (history or watch) |
Home, End |
Move selected screen (history or watch) |
Tab |
Toggle selected screen (history / watch) |
Left |
Select watch screen |
Right |
Select history screen |
Alt+Left, Alt+Right |
Scroll watch window horizontally |
q |
Open exit dialog |
Ctrl-c |
Open exit dialog |
h |
Show help |
Backspace |
Toggle history pane |
d |
Toggle diff mode |
0 |
Disable diff |
1 |
Enable watch diff |
p |
Pause/unpause capture |
Shift+P |
Pause/unpause the child process |
I |
Toggle cell inspector |
Shift+Arrow |
Move inspector cursor |
/ |
Filter history by string |
* |
Filter history by regex |
i |
Enter child app input mode |
Ctrl-g |
Leave child app input mode |
D |
Delete selected history |
X |
Clear history except selected |
s |
Cycle snapshot format (text(ANSI) / svg) |
Shift+S |
Toggle selected frame info |
Ctrl+S |
Save selected snapshot |
Remap twatch actions with -K/--keymap using KEY=ACTION.
Custom keymaps are checked before the built-in passthrough rules, so you can
override keys such as Down and use them for local history navigation.
twatch -K ctrl-p=history_pane_up -K ctrl-n=history_pane_down htop
twatch -K down=history_pane_down htopSupported actions:
| action | description |
|---|---|
up / down |
Move selected view using the current focus |
watch_pane_up / watch_pane_down |
Scroll only the watch pane |
history_pane_up / history_pane_down |
Move only the history selection |
page_up / page_down |
Page move using the current focus |
watch_pane_page_up / watch_pane_page_down |
Page scroll only the watch pane |
history_pane_page_up / history_pane_page_down |
Page move only the history pane |
move_top / move_end |
Jump using the current focus |
watch_pane_move_top / watch_pane_move_end |
Jump only the watch pane |
history_pane_move_top / history_pane_move_end |
Jump only the history selection |
toggle_focus |
Switch watch/history focus |
focus_watch_pane / focus_history_pane |
Focus a specific pane |
quit |
Open the exit dialog |
reset |
Close help/exit or clear the current filter |
delete |
Delete the selected history entry |
clear_except_selected |
Keep only the selected history entry |
cancel |
Match the built-in Ctrl-c behavior |
force_cancel |
Exit immediately |
help |
Toggle the help dialog |
toggle_view_history_pane |
Toggle the history pane |
toggle_history_summary |
Toggle selected frame details |
toggle_diff_mode |
Toggle watch diff |
set_diff_mode_none |
Disable diff |
set_diff_mode_watch |
Enable watch diff |
toggle_pause |
Pause or resume capture |
toggle_child_pause |
Pause or resume the wrapped process |
change_filter_mode |
Start plain-text history filtering |
change_regex_filter_mode |
Start regex history filtering |
enter_app_input_mode |
Enter child app input mode |
leave_app_input_mode |
Leave child app input mode |
toggle_inspector |
Toggle the cell inspector |
save_snapshot |
Save the selected snapshot |
cycle_snapshot_format |
Cycle snapshot output format |
scroll_left / scroll_right |
Scroll the watch pane horizontally |
Override keys before they reach the wrapped TUI with -k/--bind FROM=TO.
This follows the twrap style: TO accepts key names like up, down,
enter, f1, ctrl-c, comma-separated key sequences, text:..., or
screenshot.
twatch -k j=down -k k=up lazygit
twatch -k ctrl-j=text:gg -k ctrl-t=screenshot nvimCtrl-g remains reserved for leaving app input mode.
- Mouse support is implemented, but behavior still depends on the child TUI and the mouse protocol it enables.
- Snapshot save defaults to
/tmp, and text output keeps ANSI color escapes. ppausestwatchscreen capture and history updates.Shift+Psuspends or resumes the wrapped child process.
Run a TUI application inside twatch and browse previous screen states later.
This is the basic interactive mode.
twatch htopOpen plain search mode and narrow the history list to snapshots containing a matching string. Matches are highlighted on the watch screen while the filtered history entries remain selectable in the history pane.
twatch htop
# press /Open regex search mode and narrow the history list to matching snapshots. Matching entries stay selectable from the history pane while you inspect changes.
twatch htop
# press *Stream the captured TUI as plain terminal text instead of opening the interactive UI. You can combine this with count, crop, size, diff, and color controls.
twatch -b htopDisable ANSI escape sequences and print plain text only. This is useful when piping batch output into tools that do not handle colors well.
twatch -b --batch-no-color htopUse a fixed PTY size when you want stable batch output across runs. You can also crop a rectangular region to focus on one part of the captured screen.
twatch --batch-size 80,20 btoptwatch --batch-size 80,20 --batch-crop 0,10,80,10 -b htopFor text-oriented commands, you can switch to line or word diff mode to get hwatch-like highlighting.
This works best in batch mode, where twatch compares plain text snapshots instead of the live TUI screen buffer.
twatch --batch --differences line htoptwatch --batch --differences word kubectl get pods -ASave captured snapshots as JSONL so you can reload or inspect a session later. This is useful for debugging long-running screens outside the live UI.
twatch --logfile ./twatch.jsonl htopOpen a saved JSONL trace without launching a child PTY. This is useful for issue reports or postmortem debugging.
twatch --replay ./twatch.jsonlRun a shell hook asynchronously when debug-relevant trigger conditions match. You can limit it with regex, change count, cadence, debounce, and timeout options.
twatch -A 'jq -r .output <<<"$TWATCH_DATA" >/tmp/twatch.out' \
--aftercommand-regex 'panic|error' \
--aftercommand-debounce-ms 500 \
htopTWATCH_DATA includes frame metadata such as:
frame_sequnix_timestampwidthheightchanged_cell_countmatched_ruleslast_input_summary
Save snapshots automatically when a frame matches a string, regex, or change-count threshold.
twatch --snapshot-on 'panic' --snapshot-once htoptwatch --snapshot-on-regex 'panic|traceback' --snapshot-on-change-cells 100 htopCompress stored history entries in memory to reduce runtime footprint. This is still experimental. It is most helpful when the wrapped TUI updates frequently.
twatch -C htopSave the selected screen as ANSI text or SVG from the interactive view. Use this when you want a shareable artifact of one captured frame.
twatch --screenshot-dir ./shots --screenshot-format svg htopThese projects explore similar terminal wrapping and history-oriented workflows.


