Remote Work Tools

Remote Team fish Shell Setup Guide

fish (the Friendly Interactive SHell) has autosuggestions, syntax highlighting, and a sane scripting language without the POSIX baggage. For remote teams that work heavily in terminals, sharing a consistent fish config means everyone benefits from the same abbreviations for your internal tools, the same project navigation functions, and the same prompt without anyone spending a day configuring it.


Why fish for Team Config Sharing


Repo Structure

dotfiles/
├── fish/
│   ├── config.fish            -- main config, sourced on startup
│   ├── fish_plugins           -- Fisher plugin lock file (committed)
│   ├── functions/
│   │   ├── mkcd.fish
│   │   ├── proj.fish          -- project switcher
│   │   ├── kdeploy.fish       -- kubectl deploy helper
│   │   └── ghpr.fish          -- open current branch PR
│   ├── conf.d/
│   │   ├── abbreviations.fish -- shared abbreviations
│   │   └── env.fish           -- non-secret env vars
│   └── completions/           -- custom completions for internal tools
└── install.sh

Core Config

fish/config.fish

# ─── PATH ──────────────────────────────────────────────────────────
fish_add_path /usr/local/bin
fish_add_path $HOME/.local/bin
fish_add_path $HOME/go/bin
fish_add_path $HOME/.cargo/bin

# ─── Options ───────────────────────────────────────────────────────
set -g fish_greeting ""           # no welcome message
set -g EDITOR nvim
set -g VISUAL nvim
set -g PAGER "less -R"

# ─── Colors (Catppuccin Mocha) ─────────────────────────────────────
set -g fish_color_command cba6f7
set -g fish_color_param cdd6f4
set -g fish_color_error f38ba8
set -g fish_color_autosuggestion 585b70
set -g fish_color_comment 6c7086

# ─── History ───────────────────────────────────────────────────────
set -g fish_history_max 10000

# ─── Local config (gitignored) ─────────────────────────────────────
if test -f $HOME/.config/fish/config.local.fish
    source $HOME/.config/fish/config.local.fish
end

Shared Abbreviations

fish/conf.d/abbreviations.fish

# ─── Git ───────────────────────────────────────────────────────────
abbr -a g    git
abbr -a gs   git status
abbr -a gd   git diff
abbr -a ga   git add
abbr -a gaa  git add -A
abbr -a gc   git commit -m
abbr -a gca  git commit --amend --no-edit
abbr -a gp   git push
abbr -a gpf  git push --force-with-lease
abbr -a gl   git pull --rebase
abbr -a glo  "git log --oneline --graph --decorate -20"
abbr -a gco  git checkout
abbr -a gcb  git checkout -b
abbr -a gst  git stash
abbr -a gsp  git stash pop

# ─── Docker ────────────────────────────────────────────────────────
abbr -a d    docker
abbr -a dc   docker compose
abbr -a dcu  docker compose up -d
abbr -a dcd  docker compose down
abbr -a dcl  docker compose logs -f
abbr -a dps  docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

# ─── Kubernetes ────────────────────────────────────────────────────
abbr -a k    kubectl
abbr -a kgp  kubectl get pods
abbr -a kgpa "kubectl get pods -A"
abbr -a kl   kubectl logs -f
abbr -a kex  kubectl exec -it
abbr -a kns  kubectl config set-context --current --namespace
abbr -a kdp  kubectl describe pod

# ─── Misc ──────────────────────────────────────────────────────────
abbr -a ll   "ls -lah"
abbr -a la   "ls -A"
abbr -a ..   "cd .."
abbr -a ...  "cd ../.."
abbr -a h    history
abbr -a v    nvim
abbr -a tf   terraform
abbr -a awsl "aws --profile"

Shared Functions

fish/functions/proj.fish — jump to a project directory:

function proj --description "Jump to project directory"
    set projects_dir $HOME/projects

    if test (count $argv) -eq 0
        # Interactive picker if fzf is available
        if command -q fzf
            set target (ls $projects_dir | fzf --prompt="project> " --height=40%)
            if test -n "$target"
                cd $projects_dir/$target
                # Auto-activate virtualenv if present
                if test -f .venv/bin/activate.fish
                    source .venv/bin/activate.fish
                end
            end
        else
            ls $projects_dir
        end
    else
        cd $projects_dir/$argv[1]
    end
end

fish/functions/ghpr.fish — open the PR for the current branch:

function ghpr --description "Open or create PR for current branch"
    set branch (git rev-parse --abbrev-ref HEAD 2>/dev/null)
    if test -z "$branch"
        echo "Not in a git repository"
        return 1
    end

    if test "$branch" = "main" -o "$branch" = "master"
        echo "Already on $branch — check out a feature branch first"
        return 1
    end

    # Use gh CLI to open or create PR
    if command -q gh
        gh pr view --web 2>/dev/null
        or gh pr create --web
    else
        echo "Install gh: brew install gh"
    end
end

fish/functions/kdeploy.fish — deploy to Kubernetes with confirmation:

function kdeploy --description "Deploy image to Kubernetes deployment"
    # Usage: kdeploy <deployment> <image> [namespace]
    if test (count $argv) -lt 2
        echo "Usage: kdeploy <deployment> <image> [namespace]"
        return 1
    end

    set deployment $argv[1]
    set image $argv[2]
    set ns (if test (count $argv) -ge 3; echo $argv[3]; else; echo "production"; end)

    echo "Deploy $image to $deployment in namespace $ns? [y/N]"
    read confirm
    if test "$confirm" != "y" -a "$confirm" != "Y"
        echo "Aborted"
        return 1
    end

    kubectl set image deployment/$deployment \
        $deployment=$image \
        -n $ns

    kubectl rollout status deployment/$deployment -n $ns
end

Fisher Plugin Lock File

fish/fish_plugins (committed — locks exact plugin versions):

jorgebucaran/fisher
PatrickF1/fzf.fish
IlanCosman/tide@v6.1.1
jethrokuan/z
nickeb96/puffer-fish
jorgebucaran/autopair.fish

Install plugins from the lock file headlessly:

fish -c "fisher update"

Bootstrap Script

install.sh

#!/bin/bash
set -euo pipefail

DOTFILES_DIR="$(cd "$(dirname "$0")" && pwd)"
FISH_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/fish"

# Install fish if not present
if ! command -v fish &>/dev/null; then
  if [[ "$(uname)" == "Darwin" ]]; then
    brew install fish
  else
    sudo apt-add-repository ppa:fish-shell/release-3
    sudo apt update && sudo apt install fish
  fi
fi

# Create config dir and symlink
mkdir -p "$FISH_CONFIG/functions" "$FISH_CONFIG/conf.d" "$FISH_CONFIG/completions"

ln -sf "$DOTFILES_DIR/fish/config.fish"            "$FISH_CONFIG/config.fish"
ln -sf "$DOTFILES_DIR/fish/fish_plugins"            "$FISH_CONFIG/fish_plugins"
ln -sf "$DOTFILES_DIR/fish/conf.d/abbreviations.fish" "$FISH_CONFIG/conf.d/abbreviations.fish"
ln -sf "$DOTFILES_DIR/fish/conf.d/env.fish"         "$FISH_CONFIG/conf.d/env.fish"

for fn in "$DOTFILES_DIR/fish/functions/"*.fish; do
  ln -sf "$fn" "$FISH_CONFIG/functions/$(basename "$fn")"
done

# Install Fisher and plugins
fish -c "
  if not functions -q fisher
    curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source
    fisher install jorgebucaran/fisher
  end
  fisher update
"

# Set fish as default shell
if ! grep -q "$(which fish)" /etc/shells; then
  which fish | sudo tee -a /etc/shells
fi
chsh -s "$(which fish)"

echo "fish setup complete. Open a new terminal or run: exec fish"


Built by theluckystrike — More at zovo.one