Download CV
← Back to All Blogs
The only shell (terminal) config you need
The only shell (terminal) config you need

The only shell (terminal) config you need

5/27/202650 Reads

I Spent Months Building My Shell Config So You Don’t Have To

I used to be a Linux user and now I'm using MacOS, you know both are similar in terms of terminal environment, Unix tooling, and shell workflows.

If your shell setup is bad, your workflow probably is too

TBH I used to tweak my shell every week.

Now I haven’t touched the core setup in 6 months, which probably means I finally got it right.

My plugin manager free config includes:

  • Fast and minimal Zsh setup
  • Smart directory jumping using zoxid
  • Fuzzy search everywhere with fzf
  • Git-aware custom prompt
  • Custom widgets for notes, diary, and MDN browsing
  • Instant note-taking workflow inside Neovim
  • Syntax highlighting and command autosuggestions
  • Fast tab completion with fuzzy matching
  • Docker utility aliases and cleanup helpers
  • Large shared shell history across sessions
  • Cross-platform workflow between Linux and macOS
  • Terminal-first productivity setup
  • Minimal plugins with low maintenance
  • Keyboard-driven workflow with custom keybindings
  • Reproducible and stable daily-driver configuration

Assuming you already have Zsh installed


First thing: Installing the plugins

I’m not using any plugin manager because, WHY?

create a file named .zsh_functions in your home dir and paste it:

zsh
ZDOTDIR="$HOME/.zsh"function zsh_add_file() {  [[ -f "$ZDOTDIR/$1" ]] && source "$ZDOTDIR/$1"}
function zsh_add_plugin() {  local repo="$1"  local plugin_name="${repo##*/}"
  local plugin_dir="$ZDOTDIR/plugins/$plugin_name"
  if [[ -d "$plugin_dir" ]]; then    zsh_add_file "plugins/$plugin_name/$plugin_name.zsh"    return  fi
  # Otherwise → clone from GitHub  echo "Installing $plugin_name..."  git clone "https://github.com/$repo.git" "$plugin_dir"
  # After cloning → load it  zsh_add_file "plugins/$plugin_name/$plugin_name.plugin.zsh"  zsh_add_file "plugins/$plugin_name/$plugin_name.zsh"}
# yazifunction y() {	local tmp="$(mktemp -t "yazi-cwd.XXXXXX")" cwd	yazi "$@" --cwd-file="$tmp"	if cwd="$(command cat -- "$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then		builtin cd -- "$cwd"	fi	rm -f -- "$tmp"}

git_status() {  git rev-parse --is-inside-work-tree &>/dev/null || return  user=$(git config user.name)  branch=$(git rev-parse --abbrev-ref HEAD)  echo "($user | $branch)"}

And your plugin manager is ready 😂
the zsh_add_plugin function will download all the plugins we will specify.

You can find the full configuration here

To make the shell powerful and beautiful, you only need to work with two files:

  1. .zshrc
  2. .zsh_functions

Both files should be located in your home directory.


Autosuggestions and Better Completion

One of the biggest quality-of-life improvements in my shell workflow comes from autosuggestions and fuzzy completions.

First inatall fzf if you haven't already

zsh
source "$HOME/.zsh_functions"
zsh_add_plugin "zsh-users/zsh-autosuggestions"zsh_add_plugin "zsh-users/zsh-syntax-highlighting"zsh_add_plugin "Aloxaf/fzf-tab"
source <(fzf --zsh)

I use:

"zsh-users/zsh-autosuggestions" for inline command suggestions

auto-suggetion

"zsh-users/zsh-syntax-highlighting" for syntax highlighting

pasted-image-1779951212881

"Aloxaf/fzf-tab" for fuzzy tab completion

tab-completion

Custom Keybinds

I added a few custom keybindings to make navigation and history searching feel more natural.

zsh
# Keybindingsbindkey '^E' autosuggest-acceptbindkey '^[[A' history-beginning-search-backwardbindkey '^[[B' history-beginning-search-forwardbindkey '^A' beginning-of-linebindkey '^?' backward-delete-charbindkey "^[[3~" delete-charbindkey '^[[D' backward-charbindkey '^[[C' forward-char

Mimimal but Powerfull PROMPT

if you look at the .zsh_functions file you can see there is a function named git_status

zsh
# .zsh_functionsgit_status() {  git rev-parse --is-inside-work-tree &>/dev/null || return  user=$(git config user.name)  branch=$(git rev-parse --abbrev-ref HEAD)  echo "($user | $branch)"}

Here I use that function directly inside the shell prompt to display useful git information.

zsh
setopt PROMPT_SUBSTPROMPT='%F{#66FF7E}%n%f@%m %F{#43C0FE}%~%f %F{#C39AFF}$(git_status)%f %B%F{#FF00E4}%f%b '
git-status

This is very useful when you are working on a repository that has multiple branches or when you have multiple Git users.


Storing the command history for future auto suggetion

zsh
HISTFILE="$HOME/.zsh_history"HISTSIZE=500000SAVEHIST=500000setopt appendhistorysetopt INC_APPEND_HISTORY  setopt SHARE_HISTORY

The autosuggestions mainly rely on shell history, so keeping shared history file makes suggestions significantly more useful over time.

you can modify the size based on your needs.


The easy CD

I use zoxide to make directory navigation much faste

zsh
eval "$(zoxide init zsh)"alias cd=z
cd-zoxide

Note Taking

Terminal is enough to take notes for me 👀

zsh
note_logic(){	cd ~/notes || return	nvim -c "set wrap linebreak" "$(date +%Y/%b-%d | tr 'A-Z' 'a-z').md"	cd - > /dev/null}
notes_logic() { 	cd ~/notes || return 	local count=$(ls -1 **/*.md | wc -l | tr -d ' ')	fileName=$(fzf --prompt="~/notes [$count]> " --border=rounded \		--margin=1,2 \		--no-sort \		--preview-window="right:60%:border-left" \		--preview="bat --color=always --style=numbers,changes --line-range :500 {}" < /dev/tty)		if [ -n "$fileName" ]; then		nvim $fileName		fi		cd - > /dev/null}alias notes=notes_logicalias note=note_logic

This will open Neovim when you run note, and when you save, it will store your note using the current date as the filename.

Run notes to get a list of your notes, then press Enter to view or modify them using Neovim.

notes

ENV management

Keeping environment variables directly inside the config file is not a good idea.

This is not for vibe coders

pasted-image-1779954896834

So I keep my environment variables inside a ~/.secrets.sh file.
Then, inside my config file, I simply source it:

zsh
relaod(){	source "$HOME/.secrets.sh"}
alias reload=relaodsource "$HOME/.secrets.sh"

when i modify the .secrets.sh file i just run reload command instead reloding the full shell

some aliases you can use

zsh
# for stopping and removing all Docker containersalias dsa='docker stop $(docker ps -q)'alias dra='docker rm $(docker ps -a -q)'

you are done!

Thanks for reading... ❤️

Share this post