From 621d67e89e30fb72b53353558f20949e91615569 Mon Sep 17 00:00:00 2001 From: rootiest Date: Wed, 10 Jun 2026 21:20:54 -0400 Subject: [PATCH 01/10] chore(logging): add .logging_disabled sentinel to .gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) 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. From f17229d0b83e4983c26497b939894fd6a94a8f54 Mon Sep 17 00:00:00 2001 From: rootiest Date: Wed, 10 Jun 2026 21:26:40 -0400 Subject: [PATCH 02/10] feat(logging): add __fish_config_sync_logging shared C5 state sync --- functions/__fish_config_sync_logging.fish | 99 +++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 functions/__fish_config_sync_logging.fish 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 From 0382d6ab9c2a519bb2c0aa328b54ed052362a1c2 Mon Sep 17 00:00:00 2001 From: rootiest Date: Wed, 10 Jun 2026 21:28:13 -0400 Subject: [PATCH 03/10] feat(logging): add C5 --on-variable event handlers for real-time sync --- functions/__fish_config_logging_changed.fish | 21 ++++++++++++++++ .../__fish_config_opinionated_changed.fish | 24 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 functions/__fish_config_logging_changed.fish create mode 100644 functions/__fish_config_opinionated_changed.fish diff --git a/functions/__fish_config_logging_changed.fish b/functions/__fish_config_logging_changed.fish new file mode 100644 index 0000000..4ac19fb --- /dev/null +++ b/functions/__fish_config_logging_changed.fish @@ -0,0 +1,21 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# SYNOPSIS +# __fish_config_logging_changed +# +# DESCRIPTION +# Event handler that fires in every running shell when +# __fish_config_op_logging is set or erased. Delegates to +# __fish_config_sync_logging to update the Kitty sentinel file and +# paru/yay wrappers immediately without requiring a shell restart. +# +# RETURNS +# 0 Always +# +# EXAMPLE +# set -U __fish_config_op_logging 0 # triggers this automatically +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 diff --git a/functions/__fish_config_opinionated_changed.fish b/functions/__fish_config_opinionated_changed.fish new file mode 100644 index 0000000..40edac2 --- /dev/null +++ b/functions/__fish_config_opinionated_changed.fish @@ -0,0 +1,24 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# SYNOPSIS +# __fish_config_opinionated_changed +# +# DESCRIPTION +# Event handler that fires in every running shell when +# __fish_config_opinionated (the master opinionated switch) is set or +# erased. Delegates to __fish_config_sync_logging to keep C5 logging +# state in sync. Logging is the only category with external on-disk +# state (sentinel file, wrapper scripts) that requires real-time +# synchronisation when the master variable changes; C1-C4 guards +# evaluate at call-time and need no event handler. +# +# RETURNS +# 0 Always +# +# EXAMPLE +# set -U __fish_config_opinionated 0 # triggers this automatically +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 From be3dd7494e00084ce03b0e7702fa3f22809a344c Mon Sep 17 00:00:00 2001 From: rootiest Date: Wed, 10 Jun 2026 21:29:52 -0400 Subject: [PATCH 04/10] feat(logging): gate scrollback capture in smart_exit under C5 guard --- functions/smart_exit.fish | 69 ++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/functions/smart_exit.fish b/functions/smart_exit.fish index eef2fff..e956388 100644 --- a/functions/smart_exit.fish +++ b/functions/smart_exit.fish @@ -48,49 +48,52 @@ function smart_exit --description 'Capture colorized scrollback before exiting, 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) + # C5 — Logging & Capture: skip all capture when logging is disabled + if __fish_config_op_enabled __fish_config_op_logging + 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" + # 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) + # 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" - # 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 + 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" + # 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 - end - # 4. Prune junk logs before counting toward the max - _scrollback_prune_junk $snapshot_dir + # 4. Prune junk logs before counting toward the max + _scrollback_prune_junk $snapshot_dir - # 5. 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] + # 5. 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 - 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 From 7e266cb61a139a6f652bedc95f13a2cb69fdefcd Mon Sep 17 00:00:00 2001 From: rootiest Date: Wed, 10 Jun 2026 21:33:54 -0400 Subject: [PATCH 05/10] feat(logging): gate paru/yay wrapper generation under C5 guard (Tasks 5-6) Add C5 (Logging & Capture) guard to wrapper generation in both conf.d/paru-wrapper.fish and conf.d/yay-wrapper.fish. When C5 logging is disabled, the guard removes any generated wrapper and returns early, allowing the system to fall back to the bare binary. Also replaces stale Task #4 comment references with C5 classification. --- conf.d/paru-wrapper.fish | 11 ++++++++++- conf.d/yay-wrapper.fish | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) 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 From e30ccbc633945bb58f32d13cbb43cbd6940e016f Mon Sep 17 00:00:00 2001 From: rootiest Date: Wed, 10 Jun 2026 21:43:46 -0400 Subject: [PATCH 06/10] docs(logging): document C5 logging guard in fish-config.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add __fish_config_op_logging (C5 — Logging & Capture) to the opinionated components variable table and update the master-disable example comment from "four categories" to "five categories". AGENTS.md and opinionated_catalog.md are git-ignored per project policy; those files were updated on disk but cannot be tracked in this repo. --- docs/fish-config.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/fish-config.md b/docs/fish-config.md index fa53b22..f0101ce 100644 --- a/docs/fish-config.md +++ b/docs/fish-config.md @@ -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: From 2ce41d0c55cb576bb60927cbfd8c86acd2c826c7 Mon Sep 17 00:00:00 2001 From: rootiest Date: Wed, 10 Jun 2026 21:46:14 -0400 Subject: [PATCH 07/10] =?UTF-8?q?docs(logging):=20fix=20'four'=20=E2=86=92?= =?UTF-8?q?=20'five'=20category=20count=20in=20fish-config.md=20prose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/fish-config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fish-config.md b/docs/fish-config.md index f0101ce..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. From eb1b780a805af3aa2188e72bb6819c471ac00cbe Mon Sep 17 00:00:00 2001 From: rootiest Date: Wed, 10 Jun 2026 21:49:47 -0400 Subject: [PATCH 08/10] docs(logging): add C5 logging toggle to Minimal Mode in README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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: From 76349ad94b7a50003d4eeaca48d3de7971929a9e Mon Sep 17 00:00:00 2001 From: rootiest Date: Wed, 10 Jun 2026 21:54:56 -0400 Subject: [PATCH 09/10] fix(logging): register C5 event handlers in conf.d for reliable startup --on-variable handlers in functions/ are only autoloaded on explicit call, so they never fire when a universal variable changes. Moving the definitions to conf.d/logging-events.fish ensures they are registered at shell init. Also adds a startup __fish_config_sync_logging call so pre-set variable values (e.g. set before this shell was opened) take effect immediately without requiring a re-set. --- conf.d/logging-events.fish | 27 +++++++++++++++++++ functions/__fish_config_logging_changed.fish | 21 --------------- .../__fish_config_opinionated_changed.fish | 24 ----------------- 3 files changed, 27 insertions(+), 45 deletions(-) create mode 100644 conf.d/logging-events.fish delete mode 100644 functions/__fish_config_logging_changed.fish delete mode 100644 functions/__fish_config_opinionated_changed.fish 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/functions/__fish_config_logging_changed.fish b/functions/__fish_config_logging_changed.fish deleted file mode 100644 index 4ac19fb..0000000 --- a/functions/__fish_config_logging_changed.fish +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (C) 2026 Rootiest -# SPDX-License-Identifier: AGPL-3.0-or-later - -# SYNOPSIS -# __fish_config_logging_changed -# -# DESCRIPTION -# Event handler that fires in every running shell when -# __fish_config_op_logging is set or erased. Delegates to -# __fish_config_sync_logging to update the Kitty sentinel file and -# paru/yay wrappers immediately without requiring a shell restart. -# -# RETURNS -# 0 Always -# -# EXAMPLE -# set -U __fish_config_op_logging 0 # triggers this automatically -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 diff --git a/functions/__fish_config_opinionated_changed.fish b/functions/__fish_config_opinionated_changed.fish deleted file mode 100644 index 40edac2..0000000 --- a/functions/__fish_config_opinionated_changed.fish +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) 2026 Rootiest -# SPDX-License-Identifier: AGPL-3.0-or-later - -# SYNOPSIS -# __fish_config_opinionated_changed -# -# DESCRIPTION -# Event handler that fires in every running shell when -# __fish_config_opinionated (the master opinionated switch) is set or -# erased. Delegates to __fish_config_sync_logging to keep C5 logging -# state in sync. Logging is the only category with external on-disk -# state (sentinel file, wrapper scripts) that requires real-time -# synchronisation when the master variable changes; C1-C4 guards -# evaluate at call-time and need no event handler. -# -# RETURNS -# 0 Always -# -# EXAMPLE -# set -U __fish_config_opinionated 0 # triggers this automatically -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 From 7fcf268fc7515aae58983b54b2ac1b83ad84b0ca Mon Sep 17 00:00:00 2001 From: rootiest Date: Wed, 10 Jun 2026 21:59:52 -0400 Subject: [PATCH 10/10] fix(logging): suppress Kitty watcher capture when C5 logging is disabled When smart_exit skips fish-side capture due to the C5 guard, it now sets logged_by_shell=true on the Kitty window before calling builtin exit. This prevents watcher.py's on_close handler from capturing the scrollback even if the sentinel file is absent, providing a second layer of protection. Also restructures the capture block to be flat (no wrapper if-true) after the early-exit guard. --- functions/smart_exit.fish | 97 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 45 deletions(-) diff --git a/functions/smart_exit.fish b/functions/smart_exit.fish index e956388..1a66819 100644 --- a/functions/smart_exit.fish +++ b/functions/smart_exit.fish @@ -48,52 +48,59 @@ function smart_exit --description 'Capture colorized scrollback before exiting, return 0 end - # C5 — Logging & Capture: skip all capture when logging is disabled - if __fish_config_op_enabled __fish_config_op_logging - 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" - # 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 - - # 4. Prune junk logs before counting toward the max - _scrollback_prune_junk $snapshot_dir - - # 5. 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 + # 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) + + # 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" + # 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 + + # 4. Prune junk logs before counting toward the max + _scrollback_prune_junk $snapshot_dir + + # 5. 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