Files
dotfiles/docs/shell/fish.mdx
T

324 lines
24 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: fish
description: 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 setup and the [iris](/shell/iris) launch.
| 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, fzf, eza, gum, yazi) come from the same manifest. mise, television, hishtory, envman, iris, 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](https://github.com/jorgebucaran/fisher) manifest with one entry:
```text ~/.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` |
| `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`.
```fish ~/.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-paste` if `wl-copy` exists, else `pbcopy`/`pbpaste`.
- `q`: exits the shell. If `$YAZI_ID` is set and `ya` exists, it first runs `ya emit quit` so the yazi that spawned the shell quits too.
- Interactive only: `celar` and `claer` → `clear`, and `ls` → `eza --icons` when eza is installed. `clear` itself is not aliased, because an alias would shadow `functions/clear.fish`.
The full list is on the [Aliases](/shell/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 fish` runs for every shell, so scripts see mise shims too.
- Interactive only, in order:
1. Print `~/.local/state/quickshell/user/generated/terminal/sequences.txt` if present. These are escape sequences that apply wallpaper-generated Material You terminal colors from quickshell.
2. `tv init fish` (television), if installed.
3. `starship init fish`, `zoxide init fish`, and `entire completion fish`, each only if installed.
4. `tv init fish` a second time. The comment says it runs before hishtory so hishtory keeps <kbd>ctrl-r</kbd>.
5. Source `~/.hishtory/config.fish` if present.
6. Activate the unsloth studio venv (`~/.unsloth/studio/unsloth_studio/bin/activate.fish`) if present.
Under [iris](/shell/iris), <kbd>ctrl-r</kbd> never reaches fish: iris takes it to switch its menu between spec and history mode.
### 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.fish` on 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:
1. **Starship with transient prompt.** `starship_transient_prompt_func` renders `starship module character` and `starship_transient_rprompt_func` renders `starship module time`. Then `starship init fish | source` and `enable_transience` run, so past prompts collapse to `❯` with the time on the right. See [Starship](/shell/starship).
2. **Bottom-pinned prompt.** In interactive shells with `FISH_BOTTOM_PROMPT=1`, `tput cup $LINES 0` moves the cursor to the last row. The comment calls `1` the default, but `10-env.fish` sets `0`, so the prompt stays at the top unless you export `FISH_BOTTOM_PROMPT=1`.
3. `fish_add_path ~/.grok/bin` (grok installer block).
4. Sources `~/.config/envman/load.fish` if it is non-empty.
5. `fish_add_path ~/.cmux/bin` and `~/.cache/.bun/bin`.
6. Sources the first readable `~/.config/herdr/plugins/github/herdr-automatic-rename-*/shell/hook.fish`. This is herdr's automatic pane-rename hook ([herdr](/terminal/herdr)).
7. `brew shellenv fish` for `/home/linuxbrew/.linuxbrew`.
8. **iris.** If iris is installed: in an interactive fish that iris is not already running (no `IRIS_PID`, no `IRIS_RESCUE`), export `fish_features=no-query-term`, then `iris init fish | source`. The first half of the init `exec`s iris from an interactive fish; iris starts a second fish inside its pty, which runs this whole config again, and there the second half installs iris's cwd and command hooks and turns off fish autosuggestions. `IRIS_RESCUE=1 fish` starts a plain fish. See [iris](/shell/iris).
`no-query-term` turns off fish's terminal capability query. In kitty that query switches on kitty's keyboard protocol, which sends keys as `CSI u` escape sequences iris cannot parse, so its menu never opened. Feature flags are read at startup, so the export has to happen before `exec iris`; the fish iris starts inherits it.
atuin is not used in fish: it fought iris for <kbd>up</kbd> and <kbd>ctrl-r</kbd>. bash still initializes it ([atuin](/shell/atuin)).
## 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 |
| --- | --- |
| <kbd>ctrl-e</kbd> | `_tv_inline git-files`, then repaint: pick a file in the repo and open it in nvim ([television](/tools/television#git-files)) |
| <kbd>ctrl-f</kbd> | `_bind_run yy`: yazi, then cd to the directory yazi exited in |
| <kbd>ctrl-b</kbd> | `_bind_run purple` (an external command; this repo does not provide it) |
| <kbd>ctrl-y</kbd> | `_bind_run clip-last`: re-run the last command and copy its output |
| <kbd>ctrl-o</kbd> | `_gho_bind`: `gho` (pick a ghq repo or Obsidian vault from the `ghq-repos` cable and cd into it), then repaint |
| <kbd>ctrl-p</kbd> | `_pj_bind`: `pj`, the task runner on the `pj` cable ([Scripts](/shell/scripts#pj)), then repaint |
| <kbd>ctrl-a</kbd> | `_bind_run omp-session`: resume or start omp for the current repo by branch and session age, opening the `omp-sessions` cable only when nothing qualifies ([Scripts](/shell/scripts#omp-session)) |
| <kbd>ctrl-z</kbd> | `_jobs_bind`: with one background job, `fg` it; with several, pick one from the `jobs` cable ([television](/tools/television#jobs)) 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 |
| <kbd>ctrl-g</kbd> | `_bind_run lazygit` |
| <kbd>ctrl-c</kbd> | Clears the command line, runs `clear` (screen and kitty scrollback), repaints |
| <kbd>ctrl-q</kbd> | `exit` |
Bindings added by tool init scripts:
| Key | Mode | Action | Source |
| --- | --- | --- | --- |
| <kbd>up</kbd> / <kbd>down</kbd> | default and insert | fish presets (`up-or-search`, `down-or-search`); under iris the keys never reach fish | fish |
| <kbd>ctrl-t</kbd> | insert | `tv_smart_autocomplete` | `tv init fish` |
Under iris, <kbd>tab</kbd>, <kbd>shift-tab</kbd>, <kbd>ctrl-r</kbd>, <kbd>up</kbd>, and <kbd>down</kbd> belong to iris and never reach fish, so fish's tab completion, history keys, and the `tv init fish` <kbd>ctrl-r</kbd> history binding are shadowed. Every other key above reaches fish.
A program started by a key binding never fires `fish_preexec`/`fish_postexec`, so iris would keep handling keys as if the prompt were idle while tv, yazi, or lazygit owned the screen: it swallowed arrows and <kbd>tab</kbd> and could draw its menu over them. Every binding that runs a program therefore tells iris with `_iris_busy start` / `_iris_busy stop`, the same `IRIS_CMD_START` / `IRIS_CMD_STOP` messages iris's own hooks send (a no-op outside iris). `_bind_run CMD` wraps a command that way and repaints; `_tv_inline`, `_gho_bind`, and `_pj_bind` call `_iris_busy` themselves.
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` | `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` (<kbd>ctrl-a</kbd>) does its own `\033[A` when its `omp-sessions` picker is cancelled, so the binding only repaints.
zsh and bash bind <kbd>ctrl-o</kbd>, <kbd>ctrl-e</kbd>, <kbd>ctrl-p</kbd>, and <kbd>ctrl-t</kbd> the same way, and zsh also <kbd>ctrl-z</kbd> ([bash and zsh](/shell/bash-zsh)). Inside television, <kbd>ctrl-e</kbd> runs the channel's `edit` action and <kbd>ctrl-h</kbd> its `goto_parent_dir` action (`television/config.toml`). The config and cables are on the [television](/tools/television) page.
:::note
Under kitty, <kbd>ctrl-g</kbd> (lazygit overlay), <kbd>ctrl-y</kbd> (yazi split), and <kbd>ctrl-h/j/k/l</kbd> (split navigation) are mapped in `kitty.conf` and never reach fish. The fish binds for those keys only apply in other terminals. See [kitty](/terminal/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](https://github.com/scottmckendry/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](/shell/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` | <kbd>ctrl-o</kbd> handler: `gho`, then cursor fix and repaint (see [Key bindings](#key-bindings)) |
| `_pj_bind` | <kbd>ctrl-p</kbd> handler: `pj` with `PJ_STATE`, then cursor fix and repaint |
| `_jobs_bind` | <kbd>ctrl-z</kbd> handler: `fg` the only job or pick one from the `jobs` cable (see [Key bindings](#key-bindings)) |
| `_bind_run` | Runs `$argv` between `_iris_busy start` and `stop`, then repaints. Used by the <kbd>ctrl-a</kbd>, <kbd>ctrl-b</kbd>, <kbd>ctrl-f</kbd>, <kbd>ctrl-g</kbd>, and <kbd>ctrl-y</kbd> bindings |
| `_iris_busy` | `start`/`stop`: writes `IRIS_CMD_START` / `IRIS_CMD_STOP:0` to `$IRIS_FD` so iris stands down while a binding's program runs. No-op outside iris |
| `_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, see [niri](/desktop/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, <kbd>ctrl-/</kbd> 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 <kbd>ctrl-f</kbd> 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` and `starship_transient_rprompt_func` (config.fish). Under iris, `iris init fish` adds `_iris_fish_prompt`, `_iris_fish_preexec`, and `_iris_fish_postexec`.
:::warning[gh-sonr-org-migrate]
`functions/gh-sonr-org-migrate.fish` is a script, not a function definition. Running `gh-sonr-org-migrate` in 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-migrate` and starts a second run. Cancel the second run at its first `gum` prompt, or call the bash script by its full path.
:::
## auto-Niri.fish
```fish ~/.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](/desktop/niri).