diff --git a/.gitignore b/.gitignore index 44f8e03..3e9e5d0 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,11 @@ opinionated_catalog.md docs/superpowers +# ──────────────────── Runtime State Files ─────────────────── +# Sentinel file written/removed at runtime to coordinate logging state +# across the fish config and the Kitty Python watcher. Never committed. +.logging_disabled + # ──────────────── Fisher-Managed Plugin Files ─────────────── # Fisher writes these into the repo directory on install/update. # They are owned by Fisher — do not commit them. diff --git a/README.md b/README.md index 82fb34b..10135a5 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ end ## Minimal Mode -Everything opinionated in this config — command shadows, startup side-effects, key and environment overrides, and terminal integrations — is active by default but can be switched off with universal variables. Four category toggles and one master switch are available: +Everything opinionated in this config — command shadows, startup side-effects, key and environment overrides, terminal integrations, and logging — is active by default but can be switched off with universal variables. Five category toggles and one master switch are available: | Variable | Disables | |---|---| @@ -174,7 +174,8 @@ Everything opinionated in this config — command shadows, startup side-effects, | `__fish_config_op_autoexec` | Startup side-effects: Fisher bootstrap, theme apply, `paru`/`yay` wrapper generation, auto venv activation, WakaTime hook | | `__fish_config_op_overrides` | Vi mode, `exit`→`smart_exit`, `$PAGER`/`$MANPAGER`/`$CDPATH`, bang-bang history expansion, autopair, puffer, Starship prompt, theme colors | | `__fish_config_op_integrations` | Kitty/WezTerm window abbreviations, `done` notifications, `spwin`/`tab`/`split`, `hist`, `logs`, `upgrade`, WakaTime | -| `__fish_config_opinionated` | Master switch — all four categories at once | +| `__fish_config_op_logging` | Scrollback capture on exit, `paru`/`yay` AUR log wrappers, Kitty watcher capture (sentinel-file coordinated) | +| `__fish_config_opinionated` | Master switch — all five categories at once | Set any of them to a falsy value (`0`, `false`, `no`, `off`, `n`) to disable; erase the variable to re-enable: diff --git a/conf.d/logging-events.fish b/conf.d/logging-events.fish new file mode 100644 index 0000000..892f518 --- /dev/null +++ b/conf.d/logging-events.fish @@ -0,0 +1,27 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# C5 — Logging & Capture: registers --on-variable event handlers at shell +# startup so that changes to __fish_config_op_logging or the master +# __fish_config_opinionated take effect immediately in every running shell. +# Also calls __fish_config_sync_logging once to reconcile sentinel-file and +# wrapper state with any variable values that were pre-set before this shell +# started. +# +# These functions must be defined in conf.d (not functions/) because fish +# only autoloads from functions/ on explicit call — event handlers that live +# solely in functions/ are never registered and their --on-variable triggers +# never fire. + +function __fish_config_logging_changed --on-variable __fish_config_op_logging \ + --description 'C5 event handler: sync logging state when __fish_config_op_logging changes' + __fish_config_sync_logging +end + +function __fish_config_opinionated_changed --on-variable __fish_config_opinionated \ + --description 'C5 event handler: sync logging state when master opinionated switch changes' + __fish_config_sync_logging +end + +# Sync once at startup so pre-set variable values take effect without a re-set +__fish_config_sync_logging diff --git a/conf.d/paru-wrapper.fish b/conf.d/paru-wrapper.fish index da9db45..2bb7de5 100644 --- a/conf.d/paru-wrapper.fish +++ b/conf.d/paru-wrapper.fish @@ -6,9 +6,18 @@ # timestamped log file and prunes old logs, mirroring smart_exit behavior. # Auto-generating a wrapper in ~/.local/bin is opinionated (C2 auto-exec). -# Task #4's __fish_config_enable_logging will additionally gate this wrapper. +# Wrapper generation is also gated by C5 (Logging & Capture). __fish_config_op_enabled __fish_config_op_autoexec; or return +# C5 — Logging & Capture: remove generated wrapper and skip when logging is off +if not __fish_config_op_enabled __fish_config_op_logging + if test -f "$HOME/.local/bin/paru" + and grep -q "# paru-wrapper-version:" "$HOME/.local/bin/paru" 2>/dev/null + rm -f "$HOME/.local/bin/paru" + end + return +end + set -l _paru_real /usr/bin/paru set -l _paru_wrapper "$HOME/.local/bin/paru" set -l _paru_wrapper_version 1 diff --git a/conf.d/yay-wrapper.fish b/conf.d/yay-wrapper.fish index 258f1ec..4e16e05 100644 --- a/conf.d/yay-wrapper.fish +++ b/conf.d/yay-wrapper.fish @@ -6,9 +6,18 @@ # timestamped log file and prunes old logs, mirroring smart_exit behavior. # Auto-generating a wrapper in ~/.local/bin is opinionated (C2 auto-exec). -# Task #4's __fish_config_enable_logging will additionally gate this wrapper. +# Wrapper generation is also gated by C5 (Logging & Capture). __fish_config_op_enabled __fish_config_op_autoexec; or return +# C5 — Logging & Capture: remove generated wrapper and skip when logging is off +if not __fish_config_op_enabled __fish_config_op_logging + if test -f "$HOME/.local/bin/yay" + and grep -q "# yay-wrapper-version:" "$HOME/.local/bin/yay" 2>/dev/null + rm -f "$HOME/.local/bin/yay" + end + return +end + set -l _yay_real /usr/bin/yay set -l _yay_wrapper "$HOME/.local/bin/yay" set -l _yay_wrapper_version 1 diff --git a/docs/fish-config.md b/docs/fish-config.md index fa53b22..d8b3938 100644 --- a/docs/fish-config.md +++ b/docs/fish-config.md @@ -1382,7 +1382,7 @@ fish_variables. ## Opinionated Components (Minimal Mode) Every opinionated piece of this config is active by default but can be -switched off through four category opt-out variables, each evaluated via +switched off through five category opt-out variables, each evaluated via __fish_variable_check. Set a variable to any falsy value (0, false, no, off, n) to disable its category; erase it or set a truthy value (1, true, yes, on, y) to re-enable. Unset means enabled. @@ -1410,13 +1410,18 @@ yes, on, y) to re-enable. Unset means enabled. WezTerm window abbreviations, done notifications, spwin/tab/split, hist, logs, upgrade, WakaTime + __fish_config_op_logging Logging & capture: scrollback + capture on exit, paru/yay AUR log + wrappers, Kitty watcher capture; + sentinel file coordinates + cross-process state Examples: # Disable command shadows only (rm becomes plain rm again): set -U __fish_config_op_aliases off - # Full minimal mode — disable all four categories at once: + # Full minimal mode — disable all five categories at once: set -U __fish_config_opinionated 0 # Re-enable everything: diff --git a/functions/__fish_config_sync_logging.fish b/functions/__fish_config_sync_logging.fish new file mode 100644 index 0000000..c41c4af --- /dev/null +++ b/functions/__fish_config_sync_logging.fish @@ -0,0 +1,99 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# SYNOPSIS +# __fish_config_sync_logging +# +# DESCRIPTION +# Synchronises C5 logging state: creates or removes the Kitty sentinel +# file ($XDG_CONFIG_HOME/fish/.logging_disabled) and generates or removes +# the paru/yay AUR-helper log wrappers based on the combined value of +# __fish_config_opinionated (master) and __fish_config_op_logging (C5). +# Called by --on-variable event handlers whenever either variable changes. +# Safe to call at any time; wrapper removal only affects files bearing +# the generated version-marker comment. +# +# RETURNS +# 0 Always +# +# EXAMPLE +# __fish_config_sync_logging +function __fish_config_sync_logging --description 'Sync C5 logging state: sentinel file and paru/yay wrappers' + set -l config_home $XDG_CONFIG_HOME + if test -z "$config_home" + set config_home "$HOME/.config" + end + set -l sentinel "$config_home/fish/.logging_disabled" + set -l paru_wrapper "$HOME/.local/bin/paru" + set -l yay_wrapper "$HOME/.local/bin/yay" + set -l wrapper_version 1 + + if __fish_config_op_enabled __fish_config_op_logging + # Logging enabled: remove sentinel and regenerate wrappers if binaries exist + rm -f $sentinel + + if test -x /usr/bin/paru + mkdir -p (dirname $paru_wrapper) + printf '%s\n' \ + '#!/usr/bin/env bash' \ + "# paru-wrapper-version: $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 + end + + if test -x /usr/bin/yay + mkdir -p (dirname $yay_wrapper) + printf '%s\n' \ + '#!/usr/bin/env bash' \ + "# yay-wrapper-version: $wrapper_version" \ + '# Auto-generated by conf.d/yay-wrapper.fish — do not edit by hand.' \ + '# Tees yay 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/yay_$(date +%Y-%m-%d_%H-%M-%S).log"' \ + '' \ + '/usr/bin/yay "$@" 2>&1 | tee "$log_file"' \ + '' \ + 'max_files="${SCROLLBACK_HISTORY_MAX_FILES:-100}"' \ + 'mapfile -t logs < <(ls -1t "$log_dir"/yay_*.log 2>/dev/null)' \ + 'excess=$(( ${#logs[@]} - max_files ))' \ + 'for (( i = ${#logs[@]} - 1; i >= ${#logs[@]} - excess && i >= 0; i-- )); do' \ + ' rm -f "${logs[$i]}"' \ + 'done' \ + >$yay_wrapper + chmod +x $yay_wrapper + end + else + # Logging disabled: create sentinel and remove any generated wrappers + mkdir -p (dirname $sentinel) + touch $sentinel + + if test -f $paru_wrapper + and grep -q "# paru-wrapper-version:" $paru_wrapper 2>/dev/null + rm -f $paru_wrapper + end + + if test -f $yay_wrapper + and grep -q "# yay-wrapper-version:" $yay_wrapper 2>/dev/null + rm -f $yay_wrapper + end + end +end diff --git a/functions/smart_exit.fish b/functions/smart_exit.fish index eef2fff..1a66819 100644 --- a/functions/smart_exit.fish +++ b/functions/smart_exit.fish @@ -48,6 +48,16 @@ function smart_exit --description 'Capture colorized scrollback before exiting, return 0 end + # C5 — Logging & Capture: skip all capture when logging is disabled. + # When disabled, tell Kitty the window is handled so its watcher doesn't + # capture either — belt-and-suspenders alongside the sentinel file. + if not __fish_config_op_enabled __fish_config_op_logging + if test -n "$KITTY_WINDOW_ID" + kitty @ set-user-vars "logged_by_shell=true" 2>/dev/null + end + builtin exit + 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) @@ -61,8 +71,8 @@ function smart_exit --description 'Capture colorized scrollback before exiting, 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, + # 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"