Files
fish-config/docs/wiki/7-customization.md
T
2026-06-24 03:09:52 +00:00

419 lines
22 KiB
Markdown

# 7. CUSTOMIZATION
**Sections:** [Index](index.md) | [1. Configuration Variables](1-configuration-variables.md) | [2. Path Setup](2-path-setup.md) | [3. Key Bindings](3-key-bindings.md) | [4. Abbreviations](4-abbreviations.md) | [5. Functions Reference](5-functions-reference.md) | [6. Dependency Catalog](6-dependency-catalog.md) | **7. Customization** | [8. Fisher Plugins](8-fisher-plugins.md) | [9. Installation](9-installation.md) | [10. Personalization](10-personalization.md) | [11. Viewing This Manual](11-viewing-this-manual.md)
---
## Machine-local Configuration
Place machine-specific settings that should not be committed to git in:
$__fish_user_dots_path/local.fish
`__fish_user_dots_path` defaults to `~/.config/.user-dots/fish`. Set a
custom location with:
set -U __fish_user_dots_path /path/to/your/dots/fish
Typical uses: additional PATH entries, local aliases, hostname-specific env
vars, work-specific tool configs.
## Secrets and API Keys
$__fish_user_dots_path/secrets.fish
Store API tokens, GPG keys, private credentials here. This file is never
committed. It is sourced by local.fish directly, not by config.fish.
`local.fish` is sourced at the end of config.fish on every interactive
session, so it and its companion secrets.fish can override anything set
earlier.
## Overriding Configuration Variables
Any variable set in local.fish after the main config loads takes effect.
Example: to increase the scrollback history limit:
# in local.fish
set -gx SCROLLBACK_HISTORY_MAX_FILES 200
## Fish Universal Variables
Some settings (fzf colors, theme) are stored in fish_variables via
`set -U`. These are machine-local and git-ignored. Do not commit
fish_variables.
## Opinionated Components (Minimal Mode)
Every opinionated piece of this config is active by default but can be
switched off through six 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.
An explicit per-category truthy value takes precedence over the master
switch: setting __fish_config_opinionated=0 disables all unset categories,
but a category with an explicit truthy value remains enabled regardless.
Variable Disables
------------------------------ ------------------------------------
__fish_config_op_aliases Command shadows and flag injection:
ls->eza, cat->bat, cd->zoxide,
rm->trash, less->ov, top->btop,
ping->prettyping, ssh->kitten,
du->duf/dust, mkdir/bash wrappers,
history timestamps, grep/cp/mv/wget
flag injection, help intercept, claude
AGENTS.md auto-link
__fish_config_op_autoexec Startup side-effects: Fisher
bootstrap, theme apply, paru/yay
wrapper generation, auto venv
activation, WakaTime hook
__fish_config_op_overrides Key and env overrides: Vi mode,
exit->smart_exit, PAGER/MANPAGER,
CDPATH, bang-bang system, autopair,
puffer, starship prompt, theme
colors, FZF_DEFAULT_OPTS, right
prompt
__fish_config_op_integrations Terminal/tool coupling: Kitty/
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
__fish_config_op_greeting Greeting & first-run UI: per-session
fish_greeting override (defines empty
function late in config.fish to
suppress distro greetings such as
CachyOS fastfetch); first-run welcome
banner in conf.d/first_run.fish
Examples:
# Disable command shadows only (rm becomes plain rm again):
set -U __fish_config_op_aliases off
# Full minimal mode — disable all six categories at once:
set -U __fish_config_opinionated 0
# Re-enable everything:
set -Ue __fish_config_opinionated
# Minimal mode but keep the greeting:
set -U __fish_config_opinionated 0
set -U __fish_config_op_greeting 1
# (erase both to go back to full-flavor defaults)
For an interactive alternative to setting these variables by hand, run
config-settings — a full-screen TUI that flips any category (including C5
logging) on or off, per session or universally. See its entry in Section 5.
Notes:
- Command shadows (rm, cat, ls, ...) react immediately; conf.d-level
components (bindings, prompt, abbreviations, hooks) take effect in
new shells.
- With aliases disabled, rm falls back to bare `command rm` — files
are deleted permanently, not trashed.
- Disabled integration commands (spwin, tab, split, hist, logs,
upgrade) print an error naming the variable that disabled them.
- On CachyOS, the distro fish config's own aliases, history override,
and bang-bang bindings are stripped per category as well.
### Component Reference
The following tables detail every component in each category. Use this
reference to understand exactly which behaviors change when you toggle a
category variable.
#### C1 — Command Shadows
Disabling __fish_config_op_aliases restores standard system behavior for
all of these commands.
Command / Alias Active behavior Disabled fallback
───────────────────────────────────────────────────────────────────────────
ls eza -l -a --icons --hyperlink system ls
cat bat syntax-highlighted; dirs → ls /usr/bin/cat
cd zoxide frecency-based navigation fish builtin cd
rm moves files to trash (recoverable) command rm (permanent)
less $PAGER → ov → less → more → cat system less
du duf (disk overview) or dust (dir tree) system du
top btop resource monitor system top
ping prettyping --nolegend animation system ping
ssh kitten ssh in Kitty terminal system ssh
rg rg --hyperlink-format=kitty system rg
mkdir verbose path-tree display on creation mkdir -p silently
bash XDG bashrc + $SHELL reset on exit system bash
history timestamps prepended to every entry fish builtin history
cp / mv forced -i confirmation prompt cp / mv unmodified
wget forced --continue (resume downloads) system wget
grep/fgrep/egrep forced --color=auto system grep variants
dir / vdir forced --color=auto system dir / vdir
help config intercepts "help config" → config-help fish builtin help
claude auto-links AGENTS.md as CLAUDE.md before launch command claude
edit multi-editor launcher (GUI/term + fallbacks) $EDITOR/nvim/nano/vi
When C1 is disabled, `rm` uses bare `command rm` with no wrapper — files
are permanently deleted, not trashed. There is no intermediate safety net.
#### C2 — Startup Side-Effects
These run automatically without any user action. Disabling
__fish_config_op_autoexec prevents all of them.
Component Trigger What it does
───────────────────────────────────────────────────────────────────────────
Fisher bootstrap First shell only Downloads and installs fisher
Fisher update After bootstrap Installs all fish_plugins entries
Catppuccin Mocha theme First shell only Applies theme via fish_config
paru wrapper Every startup Writes ~/.local/bin/paru wrapper
yay wrapper Every startup Writes ~/.local/bin/yay wrapper
Python venv activation On every cd Sources .venv/bin/activate.fish
WakaTime command hook On every command Reports to WakaTime API
Auto-pull fast-forward On entering a repo Background ff-only git pull
When C2 is disabled: no Fisher install, no theme application, no paru/yay
wrapper generation, no automatic venv activation, no WakaTime reporting, and
no auto-pull (the PWD handler is never registered).
The first-run completion marker (__fish_config_first_run_complete) is still
set so the init does not re-run on subsequent shells.
Python venv activation fires on every directory change. If a directory uses
direnv (.envrc present), direnv takes priority and auto-venv is skipped for
that directory.
Auto-pull fast-forwards opted-in repositories in the background when you cd
into them. The fish-config repo is always covered; other repos are added with
the `auto-pull` command (see its entry in the functions reference). It only
ever fast-forwards a clean repo whose branch has an upstream — never rebases,
merges, or overwrites work — so it is a no-op on dirty trees, divergent
branches, or repos without a remote. The handler fires once per repo entry
(not on every sub-directory cd). The registry is machine-local at
`$__fish_user_dots_path/auto-pull.list` (defaults to `~/.config/.user-dots/fish/auto-pull.list`) and is never committed.
#### C3 — Key and Environment Overrides
These change fundamental shell behavior: how keys work, which pager opens,
and what the prompt looks like. Disabling __fish_config_op_overrides removes
all of them.
Override What it replaces or sets
───────────────────────────────────────────────────────────────────────────
Vi mode fish_vi_key_bindings replaces default Emacs mode
exit → smart_exit exit wrapper that captures scrollback before closing
PAGER=ov ov used by git, man, and all $PAGER-aware tools
MANPAGER=bat pipeline man pages rendered with syntax highlighting
CDPATH=. ~/projects ~ bare dir names resolve against ~/projects and ~
Bang-bang system ! and $ keys expand history; !^, !*, !-N, !?str?,
^old^new abbreviations; six expand_bang_* helpers
Autopair ( [ { " ' auto-close to (), [], {}, "", ''
Puffer key intercepts . ! $ * keys intercepted for smart expansion
Starship prompt fish_prompt replaced by Starship + OSC 133 markers
Catppuccin colors 30+ fish_color_* variables set to Mocha palette
FZF_DEFAULT_OPTS FZF themed to Catppuccin Mocha colors
Right prompt fish_right_prompt: exit code (on failure) + dim timestamp; always rendered; Docker context added when starship+C3 active
The bang-bang system spans key_bindings.fish, abbr.fish, puffer.fish, and
six expand_bang_*.fish functions. All are gated together — disabling C3
removes the entire bang-expansion system at once.
When C3 is disabled, `exit` falls back to `builtin exit` with no scrollback
capture, no Kitty IPC, and no file I/O on exit. The scrollback capture block
is independently controlled by C5 (see below).
#### C4 — Terminal and Tool Integration
These features couple the shell to specific external tools. Disabling
__fish_config_op_integrations disables all of them.
Component Requires
───────────────────────────────────────────────────────────────────────────
~60 Kitty/WezTerm abbrs Active Kitty or WezTerm session
(:w, :wv, :wh, :t, etc.)
Done desktop notifications Graphical desktop with a notification daemon
spwin Kitty or WezTerm
tab Kitty, WezTerm, or Konsole
split Kitty or WezTerm
hist fzf + wl-copy (Wayland clipboard)
logs fzf + ov; reads from ~/.terminal_history/
upgrade paru or yay (Arch Linux only)
WakaTime hook wakatime CLI and a configured API key
Disabled integration commands (spwin, tab, split, hist, logs, upgrade) print
a colored error to stderr naming the variable that disabled them rather than
silently failing.
#### C5 — Logging and Capture
Five components capture shell output to disk. Disabling
__fish_config_op_logging skips all capture and removes the logging wrappers.
Component What it captures
───────────────────────────────────────────────────────────────────────────
Scrollback capture Terminal session output saved to:
~/.terminal_history/scrollback_YYYY-MM-DD_HH-MM-SS.log
tmux pane capture Continuous pane stream via pipe-pane, saved to:
~/.terminal_history/tmux_<session>-w<win>-p<pane>_YYYY-MM-DD_HH-MM-SS.log
zellij pane capture Pane scrollback snapshot on shell exit, saved to:
~/.terminal_history/zellij_<session>-p<pane>_YYYY-MM-DD_HH-MM-SS.log
paru wrapper All paru/AUR output captured to:
~/.terminal_history/paru_YYYY-MM-DD_HH-MM-SS.log
yay wrapper All yay/AUR output captured to:
~/.terminal_history/yay_YYYY-MM-DD_HH-MM-SS.log
Kitty watcher watcher.py captures scrollback when Kitty closes
The tmux capture starts automatically when fish launches inside any tmux
pane ($TMUX is set). It uses tmux's native pipe-pane to stream all pane
output directly to disk without an intermediate process. Each fish shell
session gets its own log file; a new log is created on each shell start
(including exec fish and new splits). Before each new log, the oldest
tmux_*.log files are pruned (by modification time) to keep the total within
SCROLLBACK_HISTORY_MAX_FILES, matching the paru/yay wrapper behaviour.
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
shell exits, via `zellij action dump-screen --full --ansi` (the --ansi flag
preserves color). The dump is captured on the fish process's stdout and
written to the log file by fish itself (not via `--path`, which would make the
zellij server write the file). A fish_exit handler (registered whenever
$ZELLIJ is set) writes the pane's full scrollback and then prunes old
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
version-marked watcher (fish-config-watcher.py) into the Kitty config directory
and wires it into kitty.conf via a managed block. Inside Kitty, a non-blocking
per-session reminder points first-time users at `kitty-logging install` until
they install or run `kitty-logging dismiss`. Install affects new Kitty windows
only; runtime disable is still handled by the .logging_disabled sentinel.
Logging coordination via sentinel file
C5 uses a sentinel file to synchronize state between the shell and
out-of-process components (the Kitty watcher and all running shells):
~/.config/fish/.logging_disabled
Disabling __fish_config_op_logging:
1. Creates the sentinel immediately in every open shell.
2. Removes ~/.local/bin/paru and ~/.local/bin/yay logging wrappers;
bare /usr/bin/paru and /usr/bin/yay are used instead.
3. Kitty's watcher.py reads the sentinel on each save attempt and
skips capture — no Kitty restart required.
4. smart_exit stops saving scrollback logs.
5. Stops tmux pipe-pane capture in every open fish shell inside tmux.
Re-enabling __fish_config_op_logging:
1. Removes the sentinel in every open shell.
2. Regenerates paru/yay logging wrappers in ~/.local/bin/.
3. Kitty watcher resumes capture on the next session exit.
4. Restarts tmux pipe-pane capture in every open fish shell inside tmux.
Changes propagate to all running shells through an event handler that fires
whenever __fish_config_op_logging changes — no shell restart needed.
Note: C3 and C5 compose independently. C3 controls whether the smart_exit
wrapper is active at all; C5 controls only the scrollback-capture block
inside it. With C3 disabled, exit is plain builtin exit regardless of C5.
#### C6 — Greeting and First-Run UI
Component What it shows
───────────────────────────────────────────────────────────────────────────
First-run welcome banner One-time message on first interactive session
fish_greeting override Empty function defined late in config.fish to
suppress distro greetings (e.g. CachyOS sets
fish_greeting to fastfetch by default)
When C6 is disabled, no greeting is printed by this config. Any greeting
set by the distro or other configs runs normally — this config simply does
not override it.
## Prompt and Theme
### Starship
The primary prompt is Starship, initialized by conf.d/starship.fish.
Configure it via ~/.config/starship.toml.
conf.d/starship.fish defines a fish_prompt wrapper that only activates when
starship is in PATH. It emits OSC 133;A (prompt start) immediately before
Starship renders and OSC 133;B (input start) immediately after, placing both
markers on the prompt line itself. This allows ov to use them as sticky
section headers when browsing scrollback logs. Without Starship, fish's
built-in prompt handles these markers automatically.
### Catppuccin Fallback Prompt
When Starship is absent or C3 overrides are disabled, a built-in nim-style
two-line prompt activates from functions/fish_prompt.fish. No external
dependencies — fish builtins only.
Layout:
┬─[user@host:~/path] (main)
╰─>$
Elements:
user Yellow (Catppuccin Yellow); red if root
@host Blue (local) or Teal (SSH)
~/path prompt_pwd abbreviation (Catppuccin Text)
(main) Current git branch in Catppuccin Pink; omitted outside repos
─[V:name] Active Python venv basename; omitted when none
─[N/I/R/V] Vi-mode indicator when vi bindings are active
┬─ / ╰─> Connector lines: Catppuccin Green on success, Red on failure
The right prompt (fish_right_prompt.fish) always renders, regardless of C3
state. On failure it shows a red ✘ and the exit code; on success it shows
only the dim timestamp. When starship is installed and C3 is enabled, the
active Docker context is also shown (if non-default):
✘ 1 󰡨 myctx Fri Jun 12 00:51:21 2026 ← failed, starship+C3 active
✘ 1 Fri Jun 12 00:51:21 2026 ← failed, fallback prompt
Fri Jun 12 00:51:21 2026 ← success (no ✘)
### FZF
FZF is themed to Catppuccin Mocha via FZF_DEFAULT_OPTS set in
integrations/fzf.fish. The colors applied:
Background: #1E1E2E (base) #313244 (surface0)
Foreground: #CDD6F4 (text)
Highlights: #F38BA8 (red) #CBA6F7 (mauve) #B4BEFE (lavender)
To customize, override FZF_DEFAULT_OPTS in local.fish.
### Catppuccin Mocha Syntax Highlighting
The Catppuccin Mocha theme ships with this config in themes/ and is applied
on first run via `conf.d/first_run.fish`. Colors are stored in fish_variables
(universal). To switch variants, install a different theme from themes/:
fish_config theme save "Catppuccin Latte"
---