fish
The primary interactive shell — conf.d layout, environment, vi-mode key bindings, the cyberdream theme, completions, and every autoloaded function.
fish is the primary interactive shell. The config is split into autoloaded files: conf.d/ for startup snippets, functions/ for one function per file, completions/ for completion scripts, and themes/ for the color theme. config.fish runs last and holds the prompt and atuin setup.
| Source (repo) | Target |
|---|---|
dot_config/fish/config.fish |
~/.config/fish/config.fish |
dot_config/fish/conf.d/*.fish |
~/.config/fish/conf.d/ |
dot_config/fish/functions/*.fish |
~/.config/fish/functions/ |
dot_config/fish/completions/*.fish |
~/.config/fish/completions/ |
dot_config/fish/themes/cyberdream.theme |
~/.config/fish/themes/cyberdream.theme |
dot_config/fish/fish_plugins |
~/.config/fish/fish_plugins |
dot_config/fish/auto-Niri.fish |
~/.config/fish/auto-Niri.fish (Linux desktop/laptop only) |
None of these files are templates. chezmoi runs in symlink mode, so each target is a symlink back into the source tree and edits to ~/.config/fish/... land in the repo directly. ~/.config/fish/fish_variables (universal variables, including the selected theme) is listed in .chezmoiignore.tmpl and stays local to each machine.
Installation
fish is in .chezmoidata/packages.yaml under packages.arch.pacman and packages.darwin.brew. .chezmoiscripts/run_onchange_before_10-packages.sh.tmpl installs it with pacman on Arch and Homebrew on macOS. The tools that fish initializes (starship, zoxide, atuin, fzf, eza, gum, yazi) come from the same manifest. mise, television, hishtory, envman, and the Bun, Go, Android, and Foundry toolchains are not in the manifest; fish initializes each one only if it is present.
Plugins
fish_plugins is a fisher manifest with one entry:
jorgebucaran/nvm.fish
The plugin’s files are committed to the repo (conf.d/nvm.fish, functions/nvm.fish, functions/_nvm_*.fish, completions/nvm.fish), so fish has nvm without fisher. The plugin stores Node builds in $XDG_DATA_HOME/nvm (default ~/.local/share/nvm) and downloads from https://nodejs.org/dist. If nvm_default_version is set, it runs nvm use --silent $nvm_default_version in each interactive shell.
Startup order
fish sources conf.d/*.fish in name order and then config.fish. The numbered files load first:
| File | Purpose |
|---|---|
00-path.fish |
PATH entries and PNPM_HOME |
10-env.fish |
Non-secret environment, greeting, fzf colors |
20-aliases.fish |
Aliases, clipboard aliases, q, typo guards |
30-ssh-agent.fish |
One ssh-agent shared across shells |
40-tools.fish |
mise, television, starship, zoxide, entire, hishtory, unsloth |
50-terminal-title.fish |
tmux/screen window name from $PWD |
atuin.env.fish |
Sources ~/.atuin/bin/env.fish (puts atuin’s install dir on PATH) |
inir-env.fish |
INIR_VENV and ILLOGICAL_IMPULSE_VIRTUAL_ENV → ~/.local/state/quickshell/.venv |
inir-path.fish |
Prepends ~/.local/bin to PATH if missing |
nvm.fish |
nvm.fish plugin events and default-version activation |
opencode-background-subagents.fish |
OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true, OPENCODE_ENABLE_EXA=1 |
pnpm-filter.fish |
pnpmf* workspace helpers |
rustup.fish |
Sources ~/.cargo/env.fish |
secrets.fish |
API credentials (names below) |
uv.env.fish |
Sources ~/.local/bin/env.fish (written by the uv installer) |
00-path.fish
Runs for every shell, including non-interactive ones, so scripts get the same PATH.
| Entry | Position |
|---|---|
~/.bun/bin, ~/.local/share/go/bin, ~/.maestro/bin, ~/.grok/bin, ~/.opencode/bin |
prepended |
~/.foundry/bin |
appended |
~/.local/share/android/platform-tools, cmdline-tools/latest/bin, emulator |
appended |
~/local/share/pnpm |
appended (note the missing dot; this path does not match PNPM_HOME) |
$PNPM_HOME/bin |
prepended; PNPM_HOME=~/.local/share/pnpm |
~/.local/bin comes from inir-path.fish and ~/.cargo/bin from rustup.fish.
10-env.fish
| Variable | Value |
|---|---|
fish_greeting |
empty (no greeting) |
FISH_BOTTOM_PROMPT |
0 unless inherited. 1 pins the prompt to the bottom row |
HSA_OVERRIDE_GFX_VERSION |
11.5.0 (ROCm) |
ROCR_VISIBLE_DEVICES, HIP_VISIBLE_DEVICES |
0 |
EGET_BIN |
~/.local/bin |
GHQ_ROOT, CODE_ROOT |
~/Code |
ANDROID_HOME, ANDROID_SDK_ROOT |
~/.local/share/android |
GITEA_LOGIN |
prad.codes |
GITLAB_HOST |
git.sonr.org |
EDITOR, VISUAL |
nvim |
BROWSER |
/usr/bin/brave-origin if that binary exists |
CHROME_EXECUTABLE |
/usr/bin/chromium if it exists |
PUPPETEER_EXECUTABLE_PATH |
~/.local/bin/chrome-headless-shell |
ZEAL_DOCSETS |
the Zeal flatpak docset directory |
SHARP_IGNORE_GLOBAL_LIBVIPS |
1 |
FZF_DEFAULT_OPTS |
cyberdream fzf theme (below) |
GOPATH/GOBIN are not set here. The comment explains why: mise’s fish hook re-applies its [env] on every prompt, so Go paths are pinned in ~/.config/mise/config.toml, and mise provides GOROOT.
set -gx FZF_DEFAULT_OPTS "
--height=~40%
--layout=default
--info=inline-right
--border=rounded
--prompt=' '
--pointer='▶'
--marker='✓'
--color=fg:#ffffff,fg+:#ffffff,bg:-1,bg+:#3c4048
--color=hl:#5ef1ff,hl+:#5ef1ff
--color=info:#7b8496,header:#7b8496,border:#7b8496
--color=prompt:#5ef1ff,pointer:#ffaecf,marker:#5eff6c,spinner:#f1ff5e
--color=gutter:-1,query:#ffffff
"
20-aliases.fish
Editor, Bun, directory-jump, and tool aliases, followed by:
cpy/pst:wl-copy/wl-pasteifwl-copyexists, elsepbcopy/pbpaste.q: exits the shell. If$YAZI_IDis set andyaexists, it first runsya emit quitso the yazi that spawned the shell quits too.- Interactive only:
celarandclaer→clear, andls→eza --iconswhen eza is installed.clearitself is not aliased, because an alias would shadowfunctions/clear.fish.
The full list is on the Aliases page. This file is plain fish, not rendered from .chezmoidata/aliases.yaml, so its alias set differs from bash/zsh in a few places.
30-ssh-agent.fish
Interactive shells without SSH_AUTH_SOCK source ~/.ssh/agent.env.fish and probe with ssh-add -l. Exit code 2 means no agent is reachable. In that case it starts ssh-agent -c, rewrites its csh setenv lines into set -gx, saves them to ~/.ssh/agent.env.fish, and sources the file. An SSH_AUTH_SOCK provided from outside (systemd unit, forwarding, keyring) is left alone.
40-tools.fish
mise activate fishruns for every shell, so scripts see mise shims too.- Interactive only, in order:
- Print
~/.local/state/quickshell/user/generated/terminal/sequences.txtif present. These are escape sequences that apply wallpaper-generated Material You terminal colors from quickshell. tv init fish(television), if installed.starship init fish,zoxide init fish, andentire completion fish, each only if installed.tv init fisha second time. The comment says it runs before hishtory so hishtory keeps ctrl-r.- Source
~/.hishtory/config.fishif present. - Activate the unsloth studio venv (
~/.unsloth/studio/unsloth_studio/bin/activate.fish) if present.
- Print
atuin is initialized later, in config.fish, and binds ctrl-r itself.
50-terminal-title.fish
Defines terminal_title, which runs on every PWD change and prints ESC k <prompt_pwd> ESC \. That sequence sets the tmux or screen window name. functions/fish_title.fish sets the regular terminal title.
pnpm-filter.fish
pnpm workspace helpers. __pnpm_pick_pkg lists workspace packages with pnpm m ls --depth -1 --json | jq, and you pick one with gum filter.
| Function | Does |
|---|---|
pnpmf <deps…> |
Pick a package, then pnpm --filter <pkg> add <deps> inside a gum spin |
pnpmfd <deps…> |
Same with add -D |
pnpmfr <deps…> |
pnpm --filter <pkg> remove <deps> |
pnpmfw |
Pick a target package and a sibling, then pnpm --filter <pkg> add <sibling> --workspace |
pnpmfx |
Pick a package, then pick one of its package.json scripts with gum filter and run it |
secrets.fish
Exports credentials as global variables. The values are machine-specific and are not documented here. Variable names:
PGDATABASE (local Postgres connection string), GITLAB_TOKEN, OPPER_API_KEY, OPENAI_API_KEY, GROQ_API_KEY, WEBAWESOME_NPM_TOKEN, TAVILY_API_KEY, FINANCIAL_DATASETS_API_KEY, PIMLICO_API_KEY, WAKATIME_API_KEY, LINEAR_CLI_PROFILE, LINEAR_API_KEY, LINEAR_TEAM_ID.
config.fish
The header comment says the file should stay empty, but it holds the following:
- Starship with transient prompt.
starship_transient_prompt_funcrendersstarship module characterandstarship_transient_rprompt_funcrendersstarship module time. Thenstarship init fish | sourceandenable_transiencerun, so past prompts collapse to❯with the time on the right. See Starship. - Bottom-pinned prompt. In interactive shells with
FISH_BOTTOM_PROMPT=1,tput cup $LINES 0moves the cursor to the last row. The comment calls1the default, but10-env.fishsets0, so the prompt stays at the top unless you exportFISH_BOTTOM_PROMPT=1. fish_add_path ~/.grok/bin(grok installer block).- Sources
~/.config/envman/load.fishif it is non-empty. - atuin. Interactive shells run
atuin init fish | sourceandatuin ai init fish | source. See atuin. fish_add_path ~/.cmux/binand~/.cache/.bun/bin.- Sources the first readable
~/.config/herdr/plugins/github/herdr-automatic-rename-*/shell/hook.fish. This is herdr’s automatic pane-rename hook (herdr). - A copy of the
_atuin_ai_question_markfunction thatatuin ai init fishemits, followed bybind "?" _atuin_ai_question_mark.
Key bindings
functions/fish_user_key_bindings.fish turns on vi mode (fish_vi_key_bindings) first, then adds custom binds in both insert and default (normal) modes, so they work in either mode.
| Key | Action |
|---|---|
| ctrl-e | yy: yazi, then cd to the directory yazi exited in |
| ctrl-f | yy (same as ctrl-e) |
| ctrl-b | purple, then repaint (an external command; this repo does not provide it) |
| ctrl-y | clip-last: re-run the last command and copy its output |
| ctrl-o | gho: pick a ghq repo or Obsidian vault and cd into it |
| ctrl-p | pj: fzf task runner (Scripts) |
| ctrl-a | omp (the coding agent, see AI agents) |
| ctrl-w | atuin ai inline: natural-language command overlay |
| ctrl-g | lazygit |
| down | _pj_bind_down: runs pj on an idle, empty prompt, otherwise fish’s down-or-search |
| ctrl-c | Clears the command line, runs clear (screen and kitty scrollback), repaints |
| ctrl-q | exit |
Bindings added by tool init scripts:
| Key | Mode | Action | Source |
|---|---|---|---|
| ctrl-r | default and insert | _atuin_search |
atuin init fish |
| up | default and insert | _atuin_bind_up (atuin history, directory filter) |
atuin init fish |
| ? | default (vi normal) | _atuin_ai_question_mark: on an empty buffer, opens atuin ai inline |
config.fish |
| ctrl-t | insert | tv_smart_autocomplete |
tv init fish |
_pj_bind_down hands the key to down-or-search when the buffer is non-empty, the completion pager is open, or history search is active. Otherwise it runs PJ_QUIET=1 pj, so pressing down outside a project does nothing and prints no error.
The prompt shows ❮ in green in vi normal mode (starship’s vimcmd_symbol).
Theme
themes/cyberdream.theme is a fish theme file with colors from cyberdream.nvim and preferred background #16181a. The repo ships the file but does not select it. The active theme is stored in fish_variables, which chezmoi ignores, so pick it once per machine with fish_config theme save cyberdream.
| Variable | Color |
|---|---|
fish_color_normal |
#ffffff |
fish_color_command |
#5ef1ff |
fish_color_param, fish_color_escape |
#ffaecf |
fish_color_keyword, fish_color_host |
#5eff6c |
fish_color_quote, fish_color_option, fish_color_host_remote |
#f1ff5e |
fish_color_redirection, fish_color_operator |
#5ea1ff |
fish_color_end |
#bd5eff |
fish_color_comment, fish_color_gray, fish_color_autosuggestion |
#7b8496 |
fish_color_error, fish_color_cancel, fish_color_status |
#ff6e5e |
fish_color_selection, fish_color_search_match |
background #3c4048 |
fish_color_cwd |
#ffbd5e |
fish_color_user |
#5ef5d2 |
fish_pager_color_progress, fish_pager_color_description |
#7b8496 |
fish_pager_color_prefix |
#5ea1ff |
fish_pager_color_completion |
#ffffff |
FZF_DEFAULT_OPTS in 10-env.fish uses the same palette.
Completions
| File | Command | Mechanism |
|---|---|---|
ccc.fish |
ccc |
Typer-generated: calls ccc with _CCC_COMPLETE=complete_fish |
grok.fish |
grok |
Static clap-style completion script (global options, subcommands) |
nvm.fish |
nvm |
From the nvm.fish plugin |
omp.fish |
omp |
omp completions fish | source (generated at load time) |
ostt.fish |
ostt |
Static clap-style completion script |
Functions
Every file in functions/ is autoloaded by name. Several also exist as POSIX scripts in ~/.local/bin for bash and zsh. In fish the function shadows the script. See Scripts for why the cd-style helpers must stay shell functions.
| Function | Purpose |
|---|---|
_nvm_index_update |
nvm.fish internal: downloads index.tab from $nvm_mirror into $nvm_data/.index |
_nvm_list |
nvm.fish internal: lists installed Node versions plus system |
_nvm_version_activate |
nvm.fish internal: sets nvm_current_version and prepends its bin to PATH |
_nvm_version_deactivate |
nvm.fish internal: removes a version’s bin from PATH |
_pj_bind_down |
Down-arrow handler: pj on an empty idle prompt, else down-or-search |
ai |
gum choose among claude, opencode, codex, hermes, fractal. claude runs with --dangerously-skip-permissions |
c |
clear with no argument, cd <dir> with one |
c2p |
fzf-pick a .hbs template under ~/.config/code2prompt and run code2prompt --template |
cd |
builtin cd. If that fails, zoxide query the argument. Every successful cd is recorded with zoxide add |
clear |
Prints ESC[2J ESC[3J ESC[1;1H to clear the screen and kitty scrollback, then re-pins the prompt if FISH_BOTTOM_PROMPT=1. Falls back to command clear when not interactive |
clip-last |
Re-runs $history[1], pipes the output to cpy, and sends a notify-send notification |
cp-appid |
fzf-pick a .desktop app ID and copy it with wl-copy (desktop helper, see niri) |
czs |
chezmoi sync: chezmoi status, chezmoi re-add of drifted files, commit the source repo, then git pull --rebase --autostash and git push if ahead. Optional targets scope it (for example czs ~/.config/fish). Messages print with a chs: prefix |
extract_agents <dir> |
Copies every AGENTS.md under the current tree into <dir>, preserving paths |
fab |
fzf-pick a Fabric pattern from ~/.config/fabric/pattern_explainations.json (description in the preview, ctrl-/ toggles it) and run it, feeding piped stdin if any |
fish_title |
Terminal title: prompt_pwd, or <host (10 chars)>:<pwd> over SSH |
fish_user_key_bindings |
Vi mode plus the custom binds above |
gdc |
gum confirm, then git reset --hard HEAD and git clean -fd |
gh-sonr-org-migrate |
Script body (see the warning below) that moves every repo from *sonr* GitHub orgs into sonr-io and deletes the emptied orgs |
ghg <repo> |
ghg-clone <repo> (ghq get), cd into the clone, clear, onefetch |
ghn <name> |
Runs ~/.local/bin/ghn to create and clone a private repo, then cds into it |
gho |
Runs ~/.local/bin/gho (repo/vault picker), cds into the result, clear, and onefetch inside git repos |
git-summary [flags] |
code2prompt over the repo root, including only AGENTS.md files, using ~/.config/code2prompt/agents-md.hbs. Extra flags pass through |
gtm [repo] |
Migrates GitHub repos to a prad.codes (Gitea) org with tea repos migrate. With no argument, fzf multi-selects from gh repo list. You pick the target org and migration options (wiki, issues, labels, pull-requests, releases, milestones, lfs, mirror) with gum, private repos stay private, and it offers to clone the results with ghg-clone. Fish only |
launch_omp_livediff_overlay |
Uses kitty @ to overlay omp on the current window with a livediff vsplit beside it. Needs KITTY_LISTEN_ON |
lc-elir [root] |
fzf multi-select numbered LeetCode solution files (1234.slug.js/.ts) and run omp -p "/leetcode-eli5 <file>" on each |
mdcp <url> |
curl.md <url>, copy the result with cpy, and preview it with mdfried |
mdp |
fzf-pick a Markdown file up to 3 levels deep (mdfried preview) and open it in mdfried |
nvm |
nvm.fish: install, use, list, list-remote, current, uninstall. Reads .nvmrc/.node-version |
y |
yazi with --cwd-file, then cd to the directory yazi exited in |
yy |
Same as y. Used by the ctrl-e/ctrl-f binds |
Functions defined inside conf.d/ and config.fish rather than functions/: q (20-aliases), terminal_title (50-terminal-title), the pnpmf* helpers and __pnpm_pick_pkg (pnpm-filter), the _nvm_install/_nvm_update/_nvm_uninstall event handlers (nvm), and starship_transient_prompt_func, starship_transient_rprompt_func, and _atuin_ai_question_mark (config.fish).
auto-Niri.fish
# Auto start Niri on tty1
if test -z "$DISPLAY" ;and test "$XDG_VTNR" -eq 1
mkdir -p ~/.cache
exec niri-session > ~/.cache/niri.log 2>&1
end
This file is applied only on Linux with role desktop or laptop. It is not in conf.d/, so fish does not load it automatically, and nothing in the repo sources it. To start niri on login to tty1, source it from a login-shell snippet. See niri.