tricks.fish is sourced twice per shell on CachyOS: once by the conf.d
autoload, once forced by config.fish to re-win over the distro's own
tricks.fish (measured 4.55ms for the second pass, see
startup-latency-JOB-BRIEF-FINDINGS.md §1). The bang-bang functions,
aliases, and history override further down need to re-run on both
passes since those are what re-assert over the distro config, but the
PATH/MANPAGER setup does not.
Gates fish_add_path and the type -q bat probe behind a once-per-session
global, guarding only that block. Everything else in the file, and the
coupling with config.fish's own cachyos-tricks guard, is unchanged.
Neither file defines a function or sets a global; their only effect is
writing ~/.local/bin/<tool>, which every interactive session does anyway.
Combined 10.7 ms off every fish -c.
__fish_config_sync_logging ran on every fish -c, mkdir+touching the C5
sentinel on disk from every subshell. Its consumers — the Kitty watcher and
the paru/yay wrappers — are interactive-context, and every interactive shell
still reconciles the state.
The --on-variable PWD handler backgrounds a git fast-forward. A script that
cd's was firing it, which is also where AGENTS.md Task #4's credential
prompt could surface from a background job.
fish_color_* is consumed only by the syntax highlighter. The guard sits
below the existing cleanup branch so stale-FZF_DEFAULT_OPTS cleanup keeps
running where it does today; the FZF value itself is a persisted universal
and survives regardless.
Defines fish_prompt only. The guard precedes the op-guard and the type -q
PATH scan so both are skipped in scripts. Scripts fall back to the repo's
autoloadable functions/fish_prompt.fish, which nothing invokes anyway.
Thirteen lines, every one a `complete -c cheat` registration, sourced on
every shell start from the wrong directory. Startup cost was already ~0
because its command substitutions are lazy, but completions/ is where fish
expects the file and the move is free.
conf.d/tailscale.fish is 252 lines of Cobra-generated completion that fish
sourced on every shell start, and its self-priming block executed the
tailscale binary to warm the completion cache. completions/ is the directory
fish autoloads on first <TAB>.
Measured: 19.2 ms off both interactive and non-interactive startup.
Binding the raw @@ chord made a lone @ an ambiguous prefix, so fish
(with fish_sequence_key_delay_ms unset) held every typed @ indefinitely
until a disambiguating keystroke arrived, breaking things like
`ssh user@host`. Binding the single @ key instead and checking whether
the current token is already a bare @ removes the ambiguity entirely:
plain @ always self-inserts instantly, and a second consecutive @
triggers the picker in place, with no forced delimiter before you can
keep typing. Vi's normal/visual modes are left unbound, matching their
existing (no-op) @ behavior.
- Quote both keys and values in the generated __fish_config_op_registry.fish
keys array so a future identity/tag containing a glob-special character
(*, ?, [) can't be silently dropped by fish's set, desyncing the
positionally-paired keys/values arrays.
- Deduplicate the tag list per site in build_registry() so an identity
collision across sources (e.g. auto-pull tagged autoexec/sync in both
functions/auto-pull.fish and conf.d/auto-pull.fish) no longer produces a
duplicated tag in the committed registry.
- Add a comment to the generated file's header noting it must sort first
among conf.d/*.fish guard-callers, since that ordering is currently
implicit in the filename rather than stated anywhere.
- CI's auto-commit step now also stages conf.d/__fish_config_op_registry.fish
alongside the docs it already regenerates, so a # COMPONENT header change
pushed without a manual __fish_config_op_registry_rebuild self-heals
instead of silently drifting from what CI just regenerated.
overrides-tricks conflated two distinct C3 concerns under one shared site
(overrides/environment), so __fish_config_op_overrides_key_bindings off
left tricks.fish's bang-bang bindings active while abbr.fish/puffer.fish/
config.fish's equivalent sites correctly disabled -- a half-dismantled
bang-bang system per docs/manual's own atomic-gating claim. Split into
tricks-manpager (overrides/environment, matches PAGER/EDITOR/CDPATH) and
tricks-bang (overrides/key-bindings, matches abbr.fish/puffer.fish/
config.fish's bang-related sites, per
docs/manual/08-components-reference/03-c3-key-and-environment-overrides.md).
Also fixes stale prose in config.fish's top-of-file comment: the guard
signature is now <identity> [<site>], not <category>.
The site was mistagged aliases/filesystem in the prior commit. The original
code's own comment above the guarded block ("Surgically overriding the
distro config is opinionated (C3 overrides): skip it entirely when
overrides are disabled") documents this as an overrides decision, and it
gated __fish_config_op_overrides pre-migration. Retag to
overrides/environment (matching tricks.fish's own sibling site for the
equivalent action) so aliases=on/overrides=off keeps skipping
tricks.fish/ls/lt/cleanup/copy, as originally documented -- no silent
user-facing behavior change.
Adds multi-site `# COMPONENT` headers and converts every guard call site in
conf.d/tricks.fish, conf.d/abbr.fish, functions/smart_exit.fish,
conf.d/yay-wrapper.fish, conf.d/paru-wrapper.fish, conf.d/wakatime.fish,
conf.d/first_run.fish, and config.fish (9 sites across 3 categories) to the
self-identifying `(status current-function|basename) <site>` calling
convention. Regenerates conf.d/__fish_config_op_registry.fish.
Fish 4.x ships the native `help` function embedded in the binary
(embedded:functions/help.fish) with no on-disk file. The old
functions/help.fish tried to `source $__fish_data_dir/functions/help.fish`
to back up the native help before shadowing it — that path no longer exists,
so the first `help config ...` call errored:
source: No such file or directory
functions: Function 'help' does not exist
The backup cannot happen from functions/help.fish at all: an autoload shadow
of that name makes `help` resolve to our own wrapper (or nothing, mid-load),
so `functions -c help` can never capture the embedded original.
Move the wrapper to conf.d/help.fish, sourced at startup before any shadow
exists, where `functions -c help __original_help` copies the embedded native
help. Behavior is otherwise unchanged: 'help config' delegates to config-help,
all other topics fall through to __original_help, and the C1 opinionated guard
still applies. Delete the obsolete functions/help.fish.
Extract the browser-detection and launch logic out of config-help into a
reusable open-url function, then build repo-open on top of it to open the
current repo's origin remote in a browser.
- open-url: resolve the best graphical browser (fish_help_browser -> BROWSER
-> xdg-mime https handler -> known binaries -> xdg-open) and launch it
backgrounded. Silent by default; -v/--verbose reports the browser, -s/--silent
is accepted explicitly. Browser console chatter is discarded.
- repo-open: normalize the origin URL from HTTPS and SSH/scp forms, deep-link
to the current branch (falling back to the remote default) and sub-directory.
Provider layout resolved via git config browse.provider, hostname heuristic
(github/gitlab/gitea/bitbucket, codeberg->gitea), then github default.
-p/--print emits the URL, -r/--root ignores the sub-directory.
- config-help: replace the inlined browser block with an open-url call.
- abbr: add open-repo and url-open abbreviations that expand to the canonical
command names on space/enter.
- docs: document both functions in the SSoT (fish-config.md + index) and add a
repo-open row to the README doc-browsing table.
Both the auto-pull function and the PWD event handler hardcoded
$XDG_CONFIG_HOME/.user-dots/fish as the registry directory. They now
resolve $__fish_user_dots_path first, falling back to the same default,
so a custom path set via config-settings is honoured.
The generated PTY-logging wrappers hardcoded /usr/bin/{paru,yay} both for
the presence check and as the command the wrapper shells out to, breaking
on any host where the binary lives elsewhere. Resolve the real path via
__fish_real_command (which skips our own shim) and embed that. Bump the
wrapper version 5 -> 6 so existing installs regenerate.
Add a C2-guarded PWD handler that background fast-forwards the fish-config
repo (baseline) plus any repos in a machine-local registry. Safety is
guaranteed by _auto_pull_sync: clean tree + has-upstream + --ff-only, else
no-op. Never rebases, merges, or overwrites work.
- conf.d/auto-pull.fish: --on-variable PWD handler, throttled to fire once
per repo entry; spawns a --no-config child to run the worker
- functions/_auto_pull_sync.fish: the ff-only worker (safe on any branch)
- functions/auto-pull.fish: add/remove/list/status registry management,
usable even when C2 is disabled
- completions/auto-pull.fish: subcommand + registered-repo completions
Registry lives at ~/.config/.user-dots/fish/auto-pull.list (machine-local,
never committed).
Zellij has no live-stream facility like tmux pipe-pane, so capture is a
one-shot 'zellij action dump-screen --full' run from a fish_exit handler
(conf.d/zellij-logging.fish -> functions/_zellij_dump_log.fish). The C5
guard is checked inside the helper at exit time, so toggling logging
needs no sync_logging coordination — there is no persistent stream.
Extract pruning into functions/_prune_terminal_logs.fish, shared by both
the tmux and zellij helpers. This also fixes a latent bug in the tmux
helper: globbing tmux_*.log directly errored with 'No matches for
wildcard' on the first log in an empty dir. The shared helper globs via
'set' (tolerates no-match) and uses command ls/rm to bypass the eza and
trash C1 shadows.
Extract the tmux pipe-pane setup into functions/_tmux_pipe_log.fish,
called by both conf.d/tmux-logging.fish (startup) and
__fish_config_sync_logging (C5 re-enable). The helper prunes the oldest
tmux_*.log files by mtime to stay within SCROLLBACK_HISTORY_MAX_FILES,
matching the paru/yay wrappers.
Uses 'command ls' to bypass the eza ls shadow, which injects OSC-8
hyperlink escapes into paths and corrupted the filenames passed to rm.
Starts tmux pipe-pane capture for the current pane whenever fish
launches inside a tmux session. Each shell gets a timestamped log
in SCROLLBACK_HISTORY_DIR. Toggling __fish_config_op_logging stops
or restarts the pipe in all open fish shells via the existing
--on-variable event handlers.
Startup side-effects that modify the filesystem belong in first-run, not
in a recurring conf.d fragment. The symlink is now created once (like a
real install step) instead of being checked on every shell launch.
MANPATH addition stays in tricks.fish — that's an env var that must be
set each session.
pacman/paru download progress is a multi-line terminal animation: it
repaints lines in place using ANSI cursor-movement (ESC[<n>A) and
erase-line (ESC[K) sequences, not just carriage returns. The previous
line-wise regex approaches could not reconstruct this — frames either
concatenated into one giant line (CR removed) or collapsed to blanks
(CR kept), discarding the final 100% frame.
Add scripts/clean_progress_log.py, a small dependency-free terminal
screen-buffer emulator that replays the cursor movements against an
in-memory grid and dumps the final static frame, preserving SGR color
so logs still render with color in ov/bat/less -R. It also drops the
script(1) header/footer.
Both wrappers now pipe the raw PTY capture through this cleaner (bumped
to version 5), falling back to stripping only the script(1) header when
python3 is unavailable. Document the scripts/ dir and mechanism in
AGENTS.md and docs/fish-config.md.
col -bp is designed for backspace-overstriking (man pages), not ANSI
terminal animations; it mangled escape sequences and garbled overprinted
progress bar text. Replace with a perl -ne one-liner that:
1. Skips Script started/done header lines (next if matches, with -ne
so next truly suppresses print, unlike -pe which still auto-prints)
2. Strips trailing \r from PTY \r\n line endings
3. Collapses CR-redrawn progress bar frames to their final state
4. Preserves all ANSI color codes so ov renders them correctly
Bump wrapper version to 4 to force regeneration.
script(1) captures every \r-redrawn frame of the pacman progress bar
as raw bytes. The previous sed pass only stripped ANSI codes, leaving
all intermediate frames concatenated in the log. col -bp semantically
replays the carriage-return overwrites and retains only the final state
of each line; the sed pass then strips residual ANSI codes and the
"Script started/done" header that script(1) writes to the file even
under -q. Bump wrapper version to 3.
Piping through tee caused libalpm to detect a non-TTY stdout and
suppress download progress bars. Switch to script(1) with -q -e -c
which allocates a pseudo-TTY so paru/yay see a real terminal, then
strip ANSI escape sequences from the captured log for readability.
Bump wrapper version to 2 to force regeneration on next shell start.
--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.
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.