Imported from prdlk/dotfiles master (efdd827), which supersedes the omarchy branch (3031416). Drops the Umbriel, Noctalia, niri, CachyOS, macOS, and Blume docs-site layers; docs/ is plain Markdown.
23 KiB
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 is a git submodule (prdlk/fish). Edit inside the submodule, commit there, then commit the updated pointer in the chezmoi repo. 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 submodule directly. ~/.config/fish/fish_variables (universal variables, including the selected theme) is listed in .chezmoiignore.tmpl and stays local to each machine. ~/.config/fish/conf.d/secrets.fish is local-only: it is listed in .chezmoiignore.tmpl and in the fish repo's .gitignore, and never committed.
Installation
fish is in .chezmoidata/packages.yaml under packages.arch.pacman; .chezmoiscripts/run_onchange_before_10-packages.sh.tmpl installs it with pacman. The tools that fish initializes (starship, zoxide, atuin, fzf, eza, gum, yazi, television, mise) come from the same manifest; Bun is a global mise tool (packages.shared.mise) and rustup comes from 15-tools. hishtory, envman, and the 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:
~/.config/fish/fish_plugins
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 |
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 |
__EGL_VENDOR_LIBRARY_FILENAMES, VK_DRIVER_FILES |
Mesa's 50_mesa.json and RADV's radeon_icd.json, only when /dev/dri/by-path/pci-0000:c4:00.0-render (the Strix Halo iGPU) exists. Keeps the session off the compute-only eGPU |
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 |
GITHUB_TOKEN |
gh auth token, unless already set and only if gh is logged in |
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. ~/.config/go/env holds the same values for any go run outside a shell.
~/.config/fish/conf.d/10-env.fish
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-pastewhenwl-copyexists.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.
Warning
Put your own values in
~/.config/fish/conf.d/secrets.fishon each machine. Do not reuse credentials from another clone.
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 | 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 fishused to emit (atuin 18.21 removed that command), 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 | _tv_inline git-files, then repaint: pick a file in the repo and open it in nvim (television) |
| ctrl-f | yy: yazi, then cd to the directory yazi exited in |
| 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_bind: gho (pick a ghq repo or Obsidian vault from the ghq-repos cable and cd into it), then repaint |
| ctrl-p | _pj_bind: pj, the task runner on the pj cable (Scripts), then repaint |
| ctrl-a | omp-session, then repaint: resume or start omp for the current repo by branch and session age, opening the omp-sessions cable only when nothing qualifies (Scripts) |
| ctrl-n | omp, then repaint: always a new omp session |
| ctrl-w | atuin ai inline: natural-language command overlay |
| ctrl-z | _jobs_bind: with one background job, fg it; with several, pick one from the jobs cable (television) and fg that; with none, nothing. While a job holds the terminal, ctrl-z is still the tty's suspend, so the key toggles suspend and resume |
| ctrl-g | lazygit |
| down | _pj_bind_down: opens the pj cable 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_bind --quiet (PJ_QUIET=1), so pressing down outside a project does nothing and prints no error.
The pickers run tv --inline, which opens on the line below the prompt and leaves the cursor there when it exits. The wrappers put the prompt back before commandline -f repaint, writing the cursor moves to /dev/tty:
| Wrapper | Runs | Then |
|---|---|---|
_tv_inline ARGS |
tv ARGS --inline |
\033[A (cursor up), returns tv's status |
_gho_bind |
gho |
\033[A if the cwd did not change; \n after a cd, so the prompt lands below onefetch |
_pj_bind [--quiet] |
pj with PJ_STATE set to a temp file |
\033[A for open (cancelled) or empty (no task sources); \n for ran (the task printed output) |
_jobs_bind |
fg of the only job, or jobs with TV_JOBS set to a temp file holding the job table |
\033[A (via _tv_inline) when the picker is cancelled |
_jobs_bind runs fg by replacing the command line with __jobs_bind_fg <id> (leading space: kept out of history) and executing it, not from inside the binding. A binding runs while fish's reader still owns the terminal with extended keys on, so a job started with & that had never held the terminal got ctrl-z and ctrl-c as escape sequences instead of signals. __jobs_bind_fg erases the echoed prompt line (\033[A\033[2K on /dev/tty) and runs fg with its "Send job … to foreground" message sent to /dev/null, so resuming leaves nothing on screen. Text already typed is put back at the next prompt by a one-shot fish_prompt handler.
omp-session (ctrl-a) does its own \033[A when its omp-sessions picker is cancelled, so the binding only repaints.
zsh and bash bind ctrl-o, ctrl-e, ctrl-p, down, and ctrl-t the same way (bash and zsh). Inside television, ctrl-e runs the channel's edit action and ctrl-h its goto_parent_dir action (~/.config/television/config.toml). The config and cables are on the television page.
Note
Under kitty, ctrl-g (lazygit overlay), ctrl-y (yazi split), and ctrl-h/j/k/l (split navigation) are mapped in
kitty.confand never reach fish. The fish binds for those keys only apply in other terminals. See kitty.
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) |
mise.fish |
mise |
mise completion fish | source (generated at load time) |
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 |
_gho_bind |
ctrl-o handler: gho, then cursor fix and repaint (see Key bindings) |
_pj_bind |
ctrl-p handler: pj with PJ_STATE, then cursor fix and repaint. --quiet sets PJ_QUIET=1 |
_jobs_bind |
ctrl-z handler: fg the only job or pick one from the jobs cable (see Key bindings) |
_pj_bind_down |
Down-arrow handler: _pj_bind --quiet on an empty idle prompt, else down-or-search |
_tv_inline |
tv $argv --inline, then moves the cursor up one line on /dev/tty |
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 in ~/.local/bin) |
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 |
tv ghq-repos --inline (repo/vault picker); returns if nothing was picked, else 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-f bind |
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).
Warning
gh-sonr-org-migrate
functions/gh-sonr-org-migrate.fishis a script, not a function definition. Runninggh-sonr-org-migratein fish autoloads the file, which runs the whole fish migration flow. Because the file defines no function of that name, fish then falls through to the bash port at~/.local/bin/gh-sonr-org-migrateand starts a second run. Cancel the second run at its firstgumprompt, or call the bash script by its full path.