fix(zellij): repair scrollback logging on shell exit #55

Merged
rootiest merged 3 commits from fix/zellij-logging-dump-screen into main 2026-06-16 19:15:23 +00:00
5 changed files with 83 additions and 37 deletions
+6 -1
View File
@@ -66,13 +66,18 @@ This config captures terminal output to `~/.terminal_history` (override with
|---|---|---| |---|---|---|
| Kitty scrollback | When a Kitty window/tab closes | `scrollback_<timestamp>.log` | | Kitty scrollback | When a Kitty window/tab closes | `scrollback_<timestamp>.log` |
| tmux pane | Continuously while the pane is open (`pipe-pane`) | `tmux_<session>-w<win>-p<pane>_<timestamp>.log` | | tmux pane | Continuously while the pane is open (`pipe-pane`) | `tmux_<session>-w<win>-p<pane>_<timestamp>.log` |
| zellij pane | Snapshot taken on shell exit (`dump-screen`) | `zellij_<session>-p<pane>_<timestamp>.log` | | zellij pane | Snapshot taken on **clean** shell exit (`dump-screen`) | `zellij_<session>-p<pane>_<timestamp>.log` |
| `paru` wrapper | Every `paru` invocation | `paru_<timestamp>.log` | | `paru` wrapper | Every `paru` invocation | `paru_<timestamp>.log` |
| `yay` wrapper | Every `yay` invocation | `yay_<timestamp>.log` | | `yay` wrapper | Every `yay` invocation | `yay_<timestamp>.log` |
Old logs are pruned automatically to stay within `$SCROLLBACK_HISTORY_MAX_FILES` Old logs are pruned automatically to stay within `$SCROLLBACK_HISTORY_MAX_FILES`
(default 100) per source, and empty/trivial captures are discarded. (default 100) per source, and empty/trivial captures are discarded.
> **zellij caveat:** zellij has no continuous pipe like tmux, so its pane is
> snapshotted only on a clean shell exit (`exit`/Ctrl-D). Closing a pane or
> quitting zellij directly tears down the pane/server before it can be dumped,
> so those sessions are not logged. End with `exit` to guarantee a log.
**These logs can contain secrets** — anything printed to your terminal (command **These logs can contain secrets** — anything printed to your terminal (command
output, file dumps, tokens echoed to stdout) ends up in them. They never leave output, file dumps, tokens echoed to stdout) ends up in them. They never leave
your machine, but treat `~/.terminal_history` as sensitive. your machine, but treat `~/.terminal_history` as sensitive.
+45 -5
View File
@@ -133,7 +133,9 @@ logging=## 5.11 Pager and Logging
logs=### logs logs=### logs
smart-exit=### smart_exit smart-exit=### smart_exit
ai=## 5.12 AI and Developer Tools ai=## 5.12 AI and Developer Tools
antigravity=### antigravity antigravity=### antigravity-resume
antigravity-ide=### antigravity-ide
agy=### agy
claude-cli=### claude claude-cli=### claude
claude=### claude-resume claude=### claude-resume
claude-docs=### claude-docs claude-docs=### claude-docs
@@ -155,7 +157,37 @@ joplin=### joplin
replay=### replay replay=### replay
tmux=### tmux-clean tmux=### tmux-clean
wake-lock=### wake-lock wake-lock=### wake-lock
zellij=### zellij
# ── Tool-name synonyms (replacement → shadowed command) ──────
# These tools don't appear in any heading, so the fallback scan can't
# find them; map each to the function that wraps it.
eza=### ls
exa=### ls
lsd=### ls
trash=### rm
btop=### top
prettyping=### ping
duf=### du
dust=### du
kitten=### ssh
bat=### cat
# ── Tool integrations (Section: Integrations) ────────────────
zoxide=### Zoxide
z=### Zoxide
direnv=### DirEnv
venv=### Auto Python Venv
virtualenv=### Auto Python Venv
python=### Auto Python Venv
wakatime=### WakaTime
tailscale=### Tailscale
done=### Done Notifications
notifications=### Done Notifications
notify=### Done Notifications
pager-hierarchy=## Pager Hierarchy
shell-aliases=## 4.11 Shell Aliases
kitty-logging=### kitty-logging
watcher=### kitty-logging
# ── Section 6: Dependency Catalog ──────────────────────────── # ── Section 6: Dependency Catalog ────────────────────────────
catalog=# 6. DEPENDENCY CATALOG catalog=# 6. DEPENDENCY CATALOG
@@ -193,6 +225,11 @@ integrations-detail=#### C4 — Terminal and Tool Integration
c5=#### C5 — Logging and Capture c5=#### C5 — Logging and Capture
logging-detail=#### C5 — Logging and Capture logging-detail=#### C5 — Logging and Capture
logging-sentinel=#### C5 — Logging and Capture logging-sentinel=#### C5 — Logging and Capture
zellij=#### C5 — Logging and Capture
zellij-logging=#### C5 — Logging and Capture
tmux-logging=#### C5 — Logging and Capture
pipe-pane=#### C5 — Logging and Capture
dump-screen=#### C5 — Logging and Capture
c6=#### C6 — Greeting and First-Run UI c6=#### C6 — Greeting and First-Run UI
greeting=#### C6 — Greeting and First-Run UI greeting=#### C6 — Greeting and First-Run UI
@@ -225,13 +262,16 @@ updating=## Updating
# ── Section 10: Personalization ─────────────────────────────── # ── Section 10: Personalization ───────────────────────────────
personalization=# 10. PERSONALIZATION personalization=# 10. PERSONALIZATION
personalize=# 10. PERSONALIZATION personalize=# 10. PERSONALIZATION
secrets=## secrets.fish secrets-file=## secrets.fish
local-config=## local.fish local-config=## local.fish
# ── Section 11: Viewing This Manual ────────────────────────── # ── Section 11: Viewing This Manual ──────────────────────────
viewing=# 11. VIEWING THIS MANUAL viewing=# 11. VIEWING THIS MANUAL
manual=# 11. VIEWING THIS MANUAL manual=# 11. VIEWING THIS MANUAL
ov=## With ov (recommended) ov=## With ov (recommended)
man-page=## As a man page (if compiled) man-page=## As a man page
manpage=## As a man page (if compiled) manpage=## As a man page
jump=## Jumping to a section jump=## Jumping to a section
html=## In the browser (HTML)
browser=## In the browser (HTML)
wiki=## As a wiki
+26 -11
View File
@@ -1423,11 +1423,6 @@ Add -i (interactive confirmation) to destructive commands:
wake-lock rsync -avz src/ dest/ wake-lock rsync -avz src/ dest/
### zellij
Synopsis: zellij [args...]
Launches zellij with the Catppuccin Mocha theme applied.
--- ---
# 6. DEPENDENCY CATALOG # 6. DEPENDENCY CATALOG
@@ -1750,12 +1745,32 @@ SCROLLBACK_HISTORY_MAX_FILES, matching the paru/yay wrapper behaviour.
The zellij capture works differently: Zellij has no live output-streaming The zellij capture works differently: Zellij has no live output-streaming
facility like pipe-pane, so the log is taken as a one-shot snapshot when the facility like pipe-pane, so the log is taken as a one-shot snapshot when the
shell exits, via `zellij action dump-screen --full`. A fish_exit handler shell exits, via `zellij action dump-screen --full --ansi` (the --ansi flag
(registered whenever $ZELLIJ is set) writes the pane's full scrollback and preserves color). The dump is captured on the fish process's stdout and
then prunes old zellij_*.log files the same way. Because the capture happens written to the log file by fish itself (not via `--path`, which would make the
at exit, toggling __fish_config_op_logging takes effect on the next exit with zellij server write the file). A fish_exit handler (registered whenever
no restart or sentinel coordination needed — the C5 guard is re-checked when $ZELLIJ is set) writes the pane's full scrollback and then prunes old
the handler fires. zellij_*.log files the same way. Because the capture happens at exit, toggling
__fish_config_op_logging takes effect on the next exit with no restart or
sentinel coordination needed — the C5 guard is re-checked when the handler
fires.
LIMITATION — zellij capture only fires on a clean shell exit (typing `exit`,
Ctrl-D, or a logout), because that is when the fish_exit handler runs. It does
NOT capture when you close a pane or quit zellij through zellij itself:
- Closing a pane signals the shell and tears the pane down concurrently, so
even if the handler runs, `dump-screen` may find the pane buffer already
gone.
- Quitting zellij kills the zellij server, and `dump-screen` needs a live
server to read from — there is nothing left to snapshot.
This is a structural difference from tmux, NOT a bug. tmux streams pane output
to disk continuously via pipe-pane, so whatever was printed is already saved
no matter how the pane dies. Zellij can only snapshot, and the only reliable
snapshot point from the shell is a clean exit. To guarantee a zellij pane is
logged, end the session with `exit` or Ctrl-D rather than zellij's close-pane
or quit actions.
The Kitty watcher is managed by the kitty-logging command: it installs a The Kitty watcher is managed by the kitty-logging command: it installs a
version-marked watcher (fish-config-watcher.py) into the Kitty config directory version-marked watcher (fish-config-watcher.py) into the Kitty config directory
+6 -1
View File
@@ -34,7 +34,12 @@ function _zellij_dump_log --description 'Dump the current Zellij pane scrollback
set -l log_file "$log_dir/zellij_"$session"-p"$pane_id"_"$timestamp".log" set -l log_file "$log_dir/zellij_"$session"-p"$pane_id"_"$timestamp".log"
mkdir -p $log_dir mkdir -p $log_dir
zellij action dump-screen --full "$log_file" 2>/dev/null # Dump to STDOUT and let this fish process write the file. `--path` makes the
# zellij *server* write the file (its CWD/permissions, historically flaky);
# capturing STDOUT keeps the write client-side and reliable. Drop the empty
# file if the dump produced nothing (e.g. pane already torn down on exit).
zellij action dump-screen --full --ansi >"$log_file" 2>/dev/null
test -s "$log_file"; or command rm -f "$log_file"
_prune_terminal_logs zellij _prune_terminal_logs zellij
end end
-19
View File
@@ -1,19 +0,0 @@
# Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later
# SYNOPSIS
# zellij [args...]
#
# DESCRIPTION
# Launches zellij with the Catppuccin Mocha theme applied via
# --theme catppuccin-mocha.
#
# ARGUMENTS
# args... Arguments forwarded to zellij
#
# EXAMPLE
# zellij
function zellij --wraps='zellij' --description 'alias zellij=zellij options --theme catppuccin-mocha'
command zellij options --theme catppuccin-mocha $argv
end