From 378cb6b2218aeedfe84f4d8ff4645deef4fda44b Mon Sep 17 00:00:00 2001 From: rootiest Date: Sat, 30 May 2026 01:20:40 -0400 Subject: [PATCH] feat(shell): add scrollback history capture on shell exit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add smart_exit function: captures Kitty scrollback to a timestamped log file on exit; --no-log/-n skips capture; auto-prunes oldest logs once count exceeds SCROLLBACK_HISTORY_MAX_FILES - Wire exit → smart_exit in config.fish for interactive sessions; export SCROLLBACK_HISTORY_DIR and SCROLLBACK_HISTORY_MAX_FILES with sane defaults (~/.terminal_history and 100 respectively) - Update cat to detect files in SCROLLBACK_HISTORY_DIR or containing raw ANSI escape sequences and pass them through command cat instead of bat, preserving color output - Remove redundant alias rm="rm -i" from tricks.fish (superseded by functions/rm.fish trash-aware wrapper) - Sync README: new Scrollback History integration section, smart_exit added to System functions table, cat description updated, stale rm Safety Wrapper entry and note removed --- README.md | 22 ++++++++++--- conf.d/tricks.fish | 1 - config.fish | 18 +++++++++++ functions/cat.fish | 12 ++++++- functions/smart_exit.fish | 68 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 functions/smart_exit.fish diff --git a/README.md b/README.md index 0b0d835..10206f5 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,21 @@ Desktop notifications for long-running commands via [franciscolourenco/done](htt | `__done_min_cmd_duration` | `10000` ms | Minimum duration before a notification is sent | | `__done_notification_urgency_level` | `low` | Desktop notification urgency | +### Scrollback History + +When running inside Kitty, closing a shell session with `exit` automatically saves a timestamped scrollback snapshot to `SCROLLBACK_HISTORY_DIR` (default: `~/.terminal_history`). Snapshots are named `scrollback_YYYY-MM-DD_HH-MM-SS.log` and the oldest files are pruned automatically once the count exceeds `SCROLLBACK_HISTORY_MAX_FILES`. + +Use `exit --no-log` (or `exit -n`) to close without saving. + +The `cat` function detects files inside `SCROLLBACK_HISTORY_DIR` (and any file containing raw ANSI escape sequences) and pipes them through `command cat` instead of `bat`, preserving color output. + +Both variables can be overridden in `local.fish`: + +| Variable | Default | Description | +|---|---|---| +| `SCROLLBACK_HISTORY_DIR` | `~/.terminal_history` | Directory where scrollback snapshots are written | +| `SCROLLBACK_HISTORY_MAX_FILES` | `100` | Maximum number of snapshots to keep before pruning | + --- ## Key Bindings @@ -196,7 +211,7 @@ These functions wrap modern alternatives with graceful fallbacks to standard too | Function | Replaces | Tool | |---|---|---| | `ls` | `ls` | `eza` (falls back to `lsd`, then system `ls`) | -| `cat` | `cat` | `bat` (plain, no pager) | +| `cat` | `cat` | `bat` (plain, no pager); falls back to `command cat` for ANSI log files and scrollback snapshots | | `less` | `less` | `most` | | `ping` | `ping` | `prettyping --nolegend` | | `top` | `top` | `btop` | @@ -326,6 +341,7 @@ Install method priority: **git+cargo source build** (fish) → **cargo** (other | `wake-lock ` | Run a command with `systemd-inhibit` to prevent sleep | | `swapstat` | Colorized zRAM compression ratio, swappiness, and swap priority report | | `sudo-toggle` | Toggle sudo password bypass — writes/clears a `NOPASSWD` rule in `/etc/sudoers.d/nofail-toggle` | +| `smart_exit` | Captures Kitty terminal scrollback to a timestamped log on exit; `--no-log` skips capture; auto-prunes oldest logs when count exceeds `SCROLLBACK_HISTORY_MAX_FILES` | | `psmem` | List all processes sorted by memory usage (descending) | | `psmem10` | Top 10 processes by memory usage | | `tmux-clean` | Kill all detached tmux sessions | @@ -416,10 +432,6 @@ These aliases add `-i` (interactive confirmation) to destructive commands: |---|---| | `cp` | `cp -i` | | `mv` | `mv -i` | -| `rm` | `rm -i` | - -> [!NOTE] -> The `rm` alias applies only when no custom `rm` function from `functions/rm.fish` takes precedence. The trash-aware `rm` wrapper in `functions/rm.fish` is the primary handler for interactive sessions. #### Archives & Networking diff --git a/conf.d/tricks.fish b/conf.d/tricks.fish index 7a3d493..1c2abf1 100644 --- a/conf.d/tricks.fish +++ b/conf.d/tricks.fish @@ -96,7 +96,6 @@ alias egrep='egrep --color=auto' # Safety aliases (Confirmation before overwriting/deleting) alias cp="cp -i" alias mv="mv -i" -alias rm="rm -i" # Archives and networking short-hands alias tarnow='tar -acf ' diff --git a/config.fish b/config.fish index f4bf052..7e56922 100644 --- a/config.fish +++ b/config.fish @@ -70,6 +70,24 @@ set -gx SUDO_EDITOR $EDITOR # Helps ensure that GPG can prompt for passphrases correctly when invoked from the terminal. set -gx GPG_TTY (tty) +# ────────────────────────── Scrollback History ────────────────────────── +# Directory where scrollback history is saved into log files. +set -gx SCROLLBACK_HISTORY_DIR "$HOME/.terminal_history" +# Maximum number of scrollback history files to keep +set -gx SCROLLBACK_HISTORY_MAX_FILES 100 +# Wire up a clean exit function that won't fire on background subshells +if status is-interactive + function exit --description 'Safe interactive exit' + # If the smart_exit file exists in our function path, invoke it explicitly + if functions -q smart_exit + smart_exit $argv + else + # Absolute fallback to protect shell mechanics + builtin exit $argv + end + end +end + # ──────────────────────────── PATH variables ──────────────────────────── # Adds common user bin directories to the PATH. The -mg --move option for cargo ensures that # the cargo bin directory is moved to the end of the PATH, which can help avoid conflicts diff --git a/functions/cat.fish b/functions/cat.fish index ab7de68..9dc6693 100644 --- a/functions/cat.fish +++ b/functions/cat.fish @@ -2,7 +2,7 @@ # SPDX-License-Identifier: AGPL-3.0-or-later # Use bat for files and ls for directories when using cat -function cat --wraps='bat' --description 'Use bat for files and ls for directories' +function cat --wraps='bat' --description 'Use bat for files, ls for directories, and raw cat for ANSI logs' # If no arguments are provided, cat usually waits for stdin. # We'll maintain that behavior by skipping the directory check if $argv is empty. if set -q argv[1] @@ -11,6 +11,16 @@ function cat --wraps='bat' --description 'Use bat for files and ls for directori ls $argv return end + + # NEW: Check if the target file lives in your scrollback snapshot directory OR + # contains raw terminal ANSI color escape sequences. + if test -f $argv[1] + if string match -q "$SCROLLBACK_HISTORY_DIR/*" $argv[1] + or string match -qr '\e\[[0-9;]*m' (head -n 5 $argv[1] 2>/dev/null) + command cat $argv + return + end + end end # Fallback to bat or standard cat diff --git a/functions/smart_exit.fish b/functions/smart_exit.fish new file mode 100644 index 0000000..ae5e4bc --- /dev/null +++ b/functions/smart_exit.fish @@ -0,0 +1,68 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# Function to capture colorized scrollback before exiting, with pruning and safe overrides +function smart_exit --description 'Capture colorized scrollback before exiting, with pruning and safe overrides' + set -l options h/help n/no-log + argparse $options -- $argv + or return 1 + + set -l c_primary (set_color cyan) + set -l c_accent (set_color green) + set -l c_warn (set_color yellow) + set -l c_bold (set_color --bold) + set -l c_reset (set_color normal) + + if set -q _flag_help + echo -e "Usage: $c_accent"exit"$c_reset [$c_primary""OPTIONS""$c_reset]" + echo "" + echo "Closes the current shell session, automatically archiving the window scrollback." + echo "" + echo "Options:" + echo -e " $c_primary""-h, --help""$c_reset Show this help message" + echo -e " $c_primary""-n, --no-log""$c_reset Exit immediately $c_bold""without""$c_reset saving a scrollback history log" + return 0 + end + + set -l snapshot_dir (set -q SCROLLBACK_HISTORY_DIR; and echo $SCROLLBACK_HISTORY_DIR; or echo "$HOME/.terminal_history") + set -l max_files (set -q SCROLLBACK_HISTORY_MAX_FILES; and echo $SCROLLBACK_HISTORY_MAX_FILES; or echo 100) + + # Handle Scrollback Capture (Skipped if -n/--no-log is used) + if not set -q _flag_no_log + mkdir -p $snapshot_dir + set -l timestamp (date "+%Y-%m-%d_%H-%M-%S") + set -l filename "$snapshot_dir/scrollback_$timestamp.log" + + # Safe child process detection + set -l active_tui (ps -o comm= --ppid $fish_pid 2>/dev/null) + + if test -n "$KITTY_WINDOW_ID" + # LIVE BUFFER CHECK: Check the active token variable $_ + # If the user typed exit, $_ will match "exit". If flags were passed, + # we check if it contains the phrase "exit" to match 'exit -n' or 'exit --help'. + if string match -qr exit "$_" + if not string match -qr '^(nvim|vim|vi|nano|emacs|tmux)$' "$active_tui" + kitty @ get-text --match id:$KITTY_WINDOW_ID --extent all --ansi | sed 's/^\[38;2;[0-9;]*m//g' >$filename 2>/dev/null + end + end + end + + # 4. Automatic Pruning Logic + set -l current_logs $snapshot_dir/scrollback_*.log + if test -f "$current_logs[1]" + set -l total_files (count $current_logs) + if test $total_files -gt $max_files + set -l num_to_delete (math $total_files - $max_files) + for i in (seq 1 $num_to_delete) + rm -f $current_logs[$i] + end + end + end + else + echo -e "$c_warn""➔""$c_reset Exiting discreetly; $c_bold""no history logs saved.""$c_reset" + sleep 0.4 + end + + # Call the true system exit directly + builtin exit +end