From f97272d0ec009700737dc6f485edb3d1a1cea081 Mon Sep 17 00:00:00 2001 From: rootiest Date: Sun, 31 May 2026 20:39:52 -0400 Subject: [PATCH] feat(shell): replace pager with ov, add logs browser, and auto-generate paru wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Set $PAGER to ov in config.fish (falls back to less); remove LESS=-R - Rewrite less wrapper with full hierarchy: $PAGER → ov → less → more → cat - Simplify view.fish fallback to delegate to the less wrapper - Add logs function: fzf browser for scrollback and paru logs, opens in $PAGER - Add claude wrapper: auto-injects --remote-control unless already present - Add conf.d/paru-wrapper.fish: generates ~/.local/bin/paru on first run, versioned so future template changes auto-propagate on next shell start - Sync README.md and requirements.md to document all of the above --- README.md | 21 +++++++- conf.d/paru-wrapper.fish | 47 +++++++++++++++++ config.fish | 7 ++- functions/claude.fish | 10 ++++ functions/less.fish | 15 ++++-- functions/logs.fish | 105 ++++++++++++++++++++++++++++++++++++++ functions/smart_exit.fish | 3 ++ functions/view.fish | 4 +- requirements.md | 2 +- 9 files changed, 202 insertions(+), 12 deletions(-) create mode 100644 conf.d/paru-wrapper.fish create mode 100644 functions/claude.fish create mode 100644 functions/logs.fish diff --git a/README.md b/README.md index 10206f5..33740ea 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ This config layers on top of the CachyOS base Fish configuration and adds: │ ├── theme.fish # Theme syntax highlighting colors │ ├── done.fish # Done plugin (desktop notifications for long commands) │ ├── tricks.fish # PATH, bang-bang helpers, bat man pages, system aliases, history/backup utilities +│ ├── paru-wrapper.fish # Auto-generates ~/.local/bin/paru logging wrapper on first run │ ├── wakatime.fish # WakaTime shell hook │ └── zoxide.fish # Zoxide z/zi aliases ├── functions/ # Custom functions (one per file) @@ -138,6 +139,20 @@ Every shell command is reported to WakaTime for time-tracking. Disable by settin Full tab completion for the `tailscale` CLI is provided via `conf.d/tailscale.fish`. +### Pager + +`$PAGER` is set in `config.fish` to `ov` when available, falling back to `less`. This ensures any application that honours `$PAGER` (git, man, etc.) uses the configured pager automatically. + +The `less` wrapper function implements a full pager hierarchy, so anything that calls `less` directly also benefits: + +``` +$PAGER → ov → less → more → cat +``` + +| Variable | Default | Description | +|---|---|---| +| `PAGER` | `ov` (if installed), else `less` | System-wide pager used by `less`, `logs`, and any tool that honours `$PAGER` | + ### bat Man Pages When `bat` is installed, man pages are automatically rendered through it for syntax highlighting. `MANPAGER` and `MANROFFOPT` are set in `conf.d/tricks.fish`: @@ -212,7 +227,7 @@ These functions wrap modern alternatives with graceful fallbacks to standard too |---|---|---| | `ls` | `ls` | `eza` (falls back to `lsd`, then system `ls`) | | `cat` | `cat` | `bat` (plain, no pager); falls back to `command cat` for ANSI log files and scrollback snapshots | -| `less` | `less` | `most` | +| `less` | `less` | `ov` (modern pager); falls back to `less` → `more` → `cat` | | `ping` | `ping` | `prettyping --nolegend` | | `top` | `top` | `btop` | | `rg` | `rg` | ripgrep with `--hyperlink-format=kitty` | @@ -342,6 +357,7 @@ Install method priority: **git+cargo source build** (fish) → **cargo** (other | `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` | +| `logs` | Browse terminal log files (scrollback and paru) interactively with fzf; `Enter` opens in `$PAGER`, `Ctrl-E` opens in `$EDITOR`; `-c ` filters to `scrollback` or `paru` | | `psmem` | List all processes sorted by memory usage (descending) | | `psmem10` | Top 10 processes by memory usage | | `tmux-clean` | Kill all detached tmux sessions | @@ -369,6 +385,7 @@ Install method priority: **git+cargo source build** (fish) → **cargo** (other | Function | Description | |---|---| +| `claude` | Wrapper for `claude` that always injects `--remote-control` (skips if already present) | | `claude-resume` | Resume Claude Code session from `.claude_session` in CWD | | `antigravity-resume` | Resume antigravity-cli session from `.antigravity_session` in CWD | | `code-resume` | Smart resume — tries Claude then antigravity-cli, falls back to picker | @@ -618,7 +635,7 @@ Named context shortcuts (e.g. `dcr`, `dck`) live in `~/.config/.user-dots/fish/l | [dust](https://github.com/bootandy/dust) | `du` (directories) | | [duf](https://github.com/muesli/duf) | `du` (disks) | | [prettyping](https://github.com/denilsonsa/prettyping) | `ping` | -| [most](https://www.jedsoft.org/most/) | `less` | +| [ov](https://github.com/noborus/ov) | `less` / `$PAGER` | | [ripgrep](https://github.com/BurntSushi/ripgrep) | `grep` | | [lazygit](https://github.com/jesseduffield/lazygit) | git TUI | | [lazydocker](https://github.com/jesseduffield/lazydocker) | Docker TUI | diff --git a/conf.d/paru-wrapper.fish b/conf.d/paru-wrapper.fish new file mode 100644 index 0000000..b388062 --- /dev/null +++ b/conf.d/paru-wrapper.fish @@ -0,0 +1,47 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# Generates ~/.local/bin/paru on first run (and on version bump) when +# /usr/bin/paru is installed. The wrapper tees paru output to a +# timestamped log file and prunes old logs, mirroring smart_exit behavior. + +set -l _paru_real /usr/bin/paru +set -l _paru_wrapper "$HOME/.local/bin/paru" +set -l _paru_wrapper_version 1 + +# Skip entirely if the real paru binary isn't present +test -x $_paru_real; or return + +# Check if wrapper already exists at the expected version +if test -f $_paru_wrapper + and grep -q "# paru-wrapper-version: $_paru_wrapper_version" $_paru_wrapper 2>/dev/null + set --erase _paru_real _paru_wrapper _paru_wrapper_version + return +end + +mkdir -p (dirname $_paru_wrapper) + +printf '%s\n' \ + '#!/usr/bin/env bash' \ + "# paru-wrapper-version: $_paru_wrapper_version" \ + '# Auto-generated by conf.d/paru-wrapper.fish — do not edit by hand.' \ + '# Tees paru output to a timestamped log file and prunes old ones.' \ + 'set -o pipefail' \ + '' \ + 'log_dir="${SCROLLBACK_HISTORY_DIR:-$HOME/.terminal_history}"' \ + 'mkdir -p "$log_dir"' \ + 'log_file="$log_dir/paru_$(date +%Y-%m-%d_%H-%M-%S).log"' \ + '' \ + '/usr/bin/paru "$@" 2>&1 | tee "$log_file"' \ + '' \ + 'max_files="${SCROLLBACK_HISTORY_MAX_FILES:-100}"' \ + 'mapfile -t logs < <(ls -1t "$log_dir"/paru_*.log 2>/dev/null)' \ + 'excess=$(( ${#logs[@]} - max_files ))' \ + 'for (( i = ${#logs[@]} - 1; i >= ${#logs[@]} - excess && i >= 0; i-- )); do' \ + ' rm -f "${logs[$i]}"' \ + 'done' \ + > $_paru_wrapper + +chmod +x $_paru_wrapper + +set --erase _paru_real _paru_wrapper _paru_wrapper_version diff --git a/config.fish b/config.fish index 7e56922..001a6fd 100644 --- a/config.fish +++ b/config.fish @@ -51,7 +51,12 @@ set -gx CODEIUM_HOME "$XDG_CONFIG_HOME/codeium" set -gx WORDLIST "$XDG_CONFIG_HOME/hunspell_en_US" # ───────────────────── Misc Configuration variables ───────────────────── -set -gx LESS -R # Ensures colors render correctly in less pager +# ─────────────────────────── Pager variables ──────────────────────────── +if type -q ov + set -gx PAGER ov +else if type -q less + set -gx PAGER less +end # ─────────────────────────── Editor variables ─────────────────────────── # Set Editor variables with fallback to vi if nvim isn't available. This ensures that diff --git a/functions/claude.fish b/functions/claude.fish new file mode 100644 index 0000000..a26d4aa --- /dev/null +++ b/functions/claude.fish @@ -0,0 +1,10 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function claude --wraps='claude --remote-control' --description 'claude with --remote-control always enabled' + if contains -- --remote-control $argv + command claude $argv + else + command claude --remote-control $argv + end +end diff --git a/functions/less.fish b/functions/less.fish index 0e5997d..9eb41f1 100644 --- a/functions/less.fish +++ b/functions/less.fish @@ -1,11 +1,16 @@ # Copyright (C) 2026 Rootiest # SPDX-License-Identifier: AGPL-3.0-or-later -# alias less=most -function less --wraps='most' --description 'alias less=most' - if which most >/dev/null 2>&1 - most $argv - else +function less --wraps='ov' --description 'Pager wrapper: $PAGER → ov → less → more → cat' + if set -q PAGER; and command -q $PAGER + command $PAGER $argv + else if command -q ov + command ov $argv + else if command -q less command less $argv + else if command -q more + command more $argv + else + command cat $argv end end diff --git a/functions/logs.fish b/functions/logs.fish new file mode 100644 index 0000000..b7cd8bb --- /dev/null +++ b/functions/logs.fish @@ -0,0 +1,105 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function logs --description 'Browse terminal log files interactively with fzf' + set -l options h/help c/category= + argparse $options -- $argv + or return 1 + + if set -q _flag_help + set -l c_accent (set_color green) + set -l c_primary (set_color cyan) + set -l c_bold (set_color --bold) + set -l c_reset (set_color normal) + echo "Usage: "$c_accent"logs"$c_reset" ["$c_primary"OPTIONS"$c_reset"]" + echo "" + echo "Browse and open terminal log files interactively." + echo "Logs sorted newest-first. Type to fuzzy-filter by date or category." + echo "" + echo "Options:" + echo " "$c_primary"-h, --help"$c_reset" Show this help" + echo " "$c_primary"-c, --category"$c_reset" Limit to one category: scrollback, paru" + echo "" + echo "Keys in fzf:" + echo " "$c_primary"Enter"$c_reset" Open in \$PAGER" + echo " "$c_primary"Ctrl-E"$c_reset" Open in \$EDITOR" + echo " "$c_primary"Ctrl-C"$c_reset" Quit" + return 0 + end + + set -l log_dir (set -q SCROLLBACK_HISTORY_DIR; and echo $SCROLLBACK_HISTORY_DIR; or echo "$HOME/.terminal_history") + set -l editor (set -q EDITOR; and echo $EDITOR; or echo nvim) + + # Scan primary dir and the legacy kitty dir (deduped) + set -l scan_dirs $log_dir + set -l legacy_dir "$HOME/.terminal_history" + if test -d $legacy_dir; and not contains $legacy_dir $scan_dirs + set -a scan_dirs $legacy_dir + end + + # Collect entries: "SORTKEY|FILEPATH|CATEGORY" + set -l entries + for dir in $scan_dirs + test -d $dir || continue + for f in $dir/*.log $dir/*.txt + test -f $f || continue + set -l base (string replace -r '\.(log|txt)$' '' (basename $f)) + set -l ts (string match -r '[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2}' $base) + test -n "$ts" || continue + set -l cat (string replace "_$ts" '' $base) + if set -q _flag_category; and test $cat != $_flag_category + continue + end + set -a entries "$ts|$f|$cat" + end + end + + if test (count $entries) -eq 0 + echo "No log files found" + return 1 + end + + # Sort newest-first (ISO timestamp prefix is lexicographically correct) + set -l sorted (printf '%s\n' $entries | sort -r) + + # Build fzf input lines: "FILEPATHDATETIME [CATEGORY]" + # Field 1 (hidden via --with-nth 2) = filepath for use in {1} placeholders + set -l fzf_lines + for entry in $sorted + set -l parts (string split '|' $entry) + set -l ts_parts (string split '_' $parts[1]) + set -l date $ts_parts[1] + set -l time (string replace -ra '-' ':' $ts_parts[2]) + + # Declare before switch so the variable survives past the block + set -l label "[$parts[3]]" + switch $parts[3] + case scrollback + set label (printf '\033[36m[scrollback]\033[0m') + case paru + set label (printf '\033[32m[paru ]\033[0m') + end + + set -a fzf_lines (printf '%s\t%s %s %s' $parts[2] $date $time $label) + end + + set -l selected (printf '%s\n' $fzf_lines | fzf \ + --ansi \ + --delimiter '\t' \ + --with-nth 2 \ + --preview 'command cat {1}' \ + --preview-window 'right:60%:wrap' \ + --prompt 'Terminal Logs -> ' \ + --header 'Enter: View Ctrl-E: Edit Ctrl-C: Quit (type to filter by date or category)' \ + --bind "ctrl-e:execute($editor {1})") + + test -n "$selected" || return 0 + + set -l file (echo $selected | cut -f1) + test -f $file || return 1 + if command -q $PAGER + command cat $file | command $PAGER + else + command cat $file + end +end diff --git a/functions/smart_exit.fish b/functions/smart_exit.fish index ae5e4bc..c2002d6 100644 --- a/functions/smart_exit.fish +++ b/functions/smart_exit.fish @@ -42,7 +42,10 @@ function smart_exit --description 'Capture colorized scrollback before exiting, # 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" + # Capture the log via the shell kitty @ get-text --match id:$KITTY_WINDOW_ID --extent all --ansi | sed 's/^\[38;2;[0-9;]*m//g' >$filename 2>/dev/null + # Broadcast a window variable flag telling Kitty the log is handled + kitty @ set-user-vars "logged_by_shell=true" 2>/dev/null end end end diff --git a/functions/view.fish b/functions/view.fish index 8a9885e..dfd8bd1 100644 --- a/functions/view.fish +++ b/functions/view.fish @@ -5,10 +5,8 @@ function view --wraps='nvim -R' --description 'alias view=nvim -R' if type -q nvim nvim -R $argv - else if type -q less - less $argv else - command cat $argv + less $argv end end diff --git a/requirements.md b/requirements.md index 44b432a..0462373 100644 --- a/requirements.md +++ b/requirements.md @@ -101,7 +101,7 @@ Non-standard applications required by this fish shell configuration. | Tool | Description | Notes | |------|-------------|-------| -| `most` | Advanced pager | Wraps `less` | +| `ov` | Modern pager | Sets `$PAGER`; wraps `less` with hierarchy fallback | | `prettyping` | Pretty `ping` wrapper | Wraps `ping` | | `sudo-rs` | Rust rewrite of sudo | Wraps `sudo` |