"Your terminal is where you live. Make it beautiful."
Every developer has a dotfiles repo. Most of them are a pile of Gists half-remembered from 2019, a .bashrc that's been copy-pasted between machines for years, and a VS Code settings file that's one merge conflict away from catastrophe.
This one is different. I want to walk you through every real, working piece of gourangadassamrat/dotfiles — not just the highlights reel, but the actual code, the design decisions, and the parts that took the most iteration to get right.
⚡ The Bootstrap: One Command, Full Environment
git clone https://github.com/GourangaDasSamrat/dotfiles.git ~/dotfiles
cd ~/dotfiles/scripts && ./install.sh
install.sh calls every module in lib/ in order. If you want to pick and choose:
./setup.sh
This drops you into a numbered menu of every .sh file found recursively in scripts/ (excluding itself and utils/). You pick by number — or type all. No YAML, no external tooling, just bash.
The brains behind cross-platform compatibility live in utils/detect.sh, which determines your OS, available package manager (apt, brew, pacman), and sudo access. Every script in lib/ sources this. The result: the same lib/packages.sh that runs apt install on Ubuntu runs brew install on macOS, transparently.
Everything is symlinked into place using GNU Stow — one call to stow <module> and the directory structure inside that module folder maps directly onto $HOME. Zero manual path management.
🐚 The Shell: Nothing Is Left Unquestioned
The zsh config lives under zsh/.config/zsh/ and is split into core/, functions/, plugins/, and user/. Here's what's actually in each.
core/
colors.zsh — Defines COLOR_HEADER, COLOR_SUCCESS, COLOR_ERROR, COLOR_WARNING, COLOR_TEXT, COLOR_CURSOR, COLOR_BORDER, COLOR_NORMAL, COLOR_RESET as ANSI escape sequences. Every function and alias in the repo uses these — the whole terminal has a consistent color palette, not just a theme.
env.zsh — DOTFILES path, GOPATH, editor set to nvim, XDG_* base dirs.
history.zsh — HISTSIZE, SAVEHIST, setopt SHARE_HISTORY. Done correctly.
user/aliases.zsh
A few highlights that deserve explanation:
eza aliases are conditional. They only activate if eza is actually in $PATH:
if (($+commands[eza])); then
alias ls='eza --color=always --long --git --no-filesize --icons=always --no-time --no-user --no-permissions'
alias lt='eza --tree -a -I "node_modules|.git"'
fi
VS Code profile switching is handled gracefully across code, code-oss, and code-insiders. It detects which binary exists and aliases it correctly, then layers profile shortcuts on top — only if code resolves at all:
alias code-b='code --profile "Backend Dev"'
alias code-f='code --profile "Frontend Dev"'
alias code-g='code --profile "Go Dev"'
alias code-c='code --profile "C/C++ Dev"'
alias code-d='code --profile "Database Dev"'
alias code-r='code --profile "Rust Dev"'
alias code-l='code --profile "Lua Dev"'
alias code-p='code --profile "Python Dev"'
alias code-w='code --profile "Wiki Dev"'
Nine profiles. One alias each. Termux support is included too — for those of us who develop on Android:
if [[ -d /data/data/com.termux ]]; then
alias debian="TERM='xterm-256color' proot-distro login debian --user gouranga"
fi
🔧 user/overrides.zsh: mkdir and rm Reimagined
These two rewrites are among my favorite parts of the whole repo.
mkdir — Git-aware directory creation
When you create a single directory, an interactive prompt appears — no fzf, just pure terminal keyboard input with read -k1 and ANSI escape sequences for arrow key detection:
◆ Initialize git repository in 'myproject'?
Yes › No
Use left/right arrows to toggle, Enter to confirm. If you say Yes:
cd "$1"
git init -q
echo "# $1" > README.md
git add .
git commit -m "chore: initialize repository with README" -q
It quietly creates the repo, writes a README, and makes the first conventional commit — all in one mkdir call. Creating multiple directories skips the prompt entirely.
rm — Confirm before you destroy
Overrides rm to first show you exactly what you're deleting (with 📁/📄 icons for directories vs files), then presents the same arrow-key Yes/No interface. Flags like -rf still pass through after confirmation. Both overrides use tput civis to hide the cursor during interaction and trap 'tput cnorm' EXIT to restore it on exit or Ctrl+C.
🔌 plugins/fzf.zsh: fzf Done Properly
The fzf config ships two themes — Dracula and Catppuccin — as named functions that can be swapped by calling them:
_fzf_theme_dracula() # active by default
_fzf_theme_catppuccin() # call to switch
Each sets the full 8-color FZF_DEFAULT_OPTS palette: fg, bg, hl, info, prompt, pointer, marker, spinner, header, border.
Search uses fd instead of find:
export FZF_DEFAULT_COMMAND="fd --hidden --strip-cwd-prefix --exclude .git"
Tab completion previews use eza for directories and bat for files. Context-aware completion via _fzf_comprun routes different commands to appropriate previews — cd gets a directory tree, ssh gets dig {}, export/unset evaluates the variable value.
📡 functions/ — Custom Commands Written From Scratch
apireq — A Full API Client in Your Terminal
apireq is the most complex function in the repo. It's a complete interactive HTTP client powered by fzf, written entirely in zsh. No dependencies beyond curl, fzf, bat, jq, and optionally python3 for JSON validation.
Flow:
- Choose: Create new request or Load saved
.httpfile - Pick HTTP method via fzf
- Enter URL
- Multi-select with Space: Auth · Custom Headers · Query Params · Request Body
- Auth: Bearer Token or Basic Auth (password via
read -rs, no echo to terminal) - Body: JSON (validated via
python3 -m json.tool), Raw Text, Form Data (urlencoded), or Multipart (supports file upload viakey=@/path/to/file) - Output:
auto(bat for headers + jq for JSON body),bat,jq, orraw - Save as
.httpfile with an fzf folder picker that browses$HOME
The .http format is real — saved requests can be loaded, inspected in a preview pane, and optionally modified field-by-field (method, URL, headers, body) before re-sending.
archive.zsh — Universal Pack/Unpack
extract() handles tar.*, tgz, tbz2, txz, and zip via a clean case statement with colorized output using the colors.zsh palette.
compress() presents an fzf menu of 7 formats with human descriptions:
tar.gz → Good balance, common
tar.bz2 → Better compression, slower
tar.xz → Best compression, slowest
zip → Cross-platform
7z → High compression
gz → Single file only
bz2 → Single file only
.gz and .bz2 correctly bail out with an error if you try to compress a directory.
network.zsh — Three Genuinely Useful Web Commands
All three are conditionally defined — they only load if their dependencies exist at shell startup.
isup [url] — Uses httpie with -F (follow redirects) and -p=h (headers only) to check site status. Displays ✔ ONLINE [200 OK], ⚠ ISSUE [301], or ✘ OFFLINE. Extracts and shows the Server: header value.
myip — Fetches ipinfo.io via httpie, parses the JSON with grep -Po, and prints IP address, city + region, and ISP — all styled with the color palette.
inspect [url] — Fetches response headers filtered to security-relevant ones (Server, Content-Type, X-Powered-By, Cache-Control, Strict-Transport-Security), then runs openssl s_client to show SSL cert validity dates. Only defined when both http and openssl are available.
utils.zsh — The Everyday Toolkit
serve [port] — Python HTTP server with a full validation loop: ensures the port is numeric, 1–65535, and not already bound (lsof -Pi :$port -sTCP:LISTEN). Prompts for another port if occupied.
timer <duration> — Accepts raw seconds, 1h30m, 45m20s, or any combination. Renders a full-screen countdown: color-coded time display (green → yellow → red as it runs out), a 40-char block progress bar (█░), a braille spinner, and elapsed time. On completion: terminal bell (\a), notify-send desktop notification, and system audio via paplay or aplay if available.
backup <path> — One command creates folder_backup_20260429_142301.tar.gz with a precise timestamp.
weather [city] — Fetches wttr.in/<city>?0mFq&format=v2 — the v2 format gives a full readable weather card. Defaults to Khulna.
t <command> — Prefixes every output line from any command with [YYYY-MM-DD HH:MM:SS] via moreutils' ts. Uses stdbuf -oL -eL for real-time line-buffered output and FORCE_COLOR=3 to preserve colors in piped output. Defined only if ts is installed.
expose [port] [ttl] — Wraps slim share with the same port validation as serve. Exposes localhost to the internet. Optional ttl argument passed through. Defined only if slim is installed.
security.zsh — Auto-Locking GPG
Spawns a background daemon named gpg-auto-lock-loop via exec -a (so it's grep-able by name) that fires gpg-connect-agent reloadagent /bye every 900 seconds — locking your GPG key cache. Checks for an existing instance before spawning so sourcing your config multiple times never creates duplicates.
whois.zsh — Filtered WHOIS Lookup
Defines a server map with keys dp (whois.digitalplat.org), iana (whois.iana.org), and com (whois.verisign-grs.com):
dzw example.com # default server
dzw dp example.com # via digitalplat.org
dzw iana example.com # via IANA
Output is filtered with grep -E to useful fields only — Domain Name, Registrar, dates, Name Servers, Status — stripping all legal boilerplate.
chpwd.zsh — Smart Directory Change Hooks
Three functions registered via add-zsh-hook chpwd:
_manage_python_venv — Checks for .venv, venv, or .env directories. Auto-activates on cd in, auto-deactivates on cd out.
_list_project_tools — On every cd, detects and lists available automation targets. justfile → just --list. Makefile → awk-parsed targets. package.json → jq -r '.scripts | keys[]'. docker-compose.yml → docker compose ps --services.
_auto_nvm_use — Reads .nvmrc and calls nvm use automatically.
plugins/pass.zsh — Encrypted Secret Management
env-save <file> <pass-path> — Reads a .env file and pipes it into pass insert -m at any path in the password store. Multi-line safe.
env-load <pass-path> [filename] — Pulls secrets out of pass into a local file (defaults to .env). Verifies the path exists before writing. Prints line count on success.
🪟 tmux: Live Weather in the Status Bar
The tmux config uses Catppuccin macchiato with rounded window separators and status bar at the top, updating every second.
The live weather widget:
set -ag status-right "#[fg=#b7bdf8,bg=#24273a] #[fg=#24273a,bg=#b7bdf8,bold]#(curl -s 'wttr.in/Khulna?format=%%c%%t' | tr -d '+')#[fg=#b7bdf8,bg=#24273a] "
This fetches the current condition icon and temperature from wttr.in every second. tr -d '+' strips the leading + from positive temperatures.
Key bindings:
Prefix: Ctrl+A
Splits: | horizontal · - vertical (opens at current path)
Pane nav: h·j·k·l (Vim) or Alt+Arrow (no prefix)
Window nav: Alt+1–5 (no prefix needed)
Window reorder: < and > to swap left/right
Resize: Prefix + H/J/K/L (repeatable with -r)
Zoom pane: Prefix + m
Persistence: tmux-resurrect + tmux-continuum auto-save every 5 minutes (@continuum-save-interval '5'), restore on server start (@continuum-restore 'on'), and capture pane contents (@resurrect-capture-pane-contents 'on').
🖥️ VS Code: Nine Profiles, Every Extension Documented
The docs/vscode/vscode-extensions.md documents every extension per profile:
| Profile | Formatter | Key Extensions |
|---|---|---|
| Default | — | Dracula, Error Lens, GitLens, Code Spell Checker, Commit Sage, Todo Tree |
| Frontend | Prettier + ESLint | Live Server, Pretty TS Errors, Tailwind IntelliSense |
| Backend | Prettier + ESLint | Thunder Client, Prisma, SQLTools |
| C/C++ | clangd | clangd, Code Runner |
| Go | gopls + goimports | Go (official), Code Runner |
| Database | prettier-sql | Prettier SQL VSCode, SQLTools |
| Rust | rust-analyzer | rust-analyzer, Code Runner |
| Lua | sumneko | Lua, Code Runner |
| Wiki | Prettier | Prettier |
Font stack: Operator Mono (italic keywords + ligatures) → Cartograph CF → JetBrains Mono.
Custom snippets shipped: cpp.json (competitive programming main, LeetCode template), go.json (main, package, iferr, interface), react-components.code-snippets, clang-format.code-snippets.
✏️ Neovim: lazy.nvim + Custom Dashboard
Managed by lazy.nvim. Plugins:
-
Mofiqul/dracula.nvim— Dracula withitalic_comment = true -
nvim-lualine/lualine.nvim— statusline withdracula-nvimtheme -
windwp/nvim-autopairs— auto-close brackets onInsertEnter -
folke/todo-comments.nvim— highlightsTODO:,FIXME:,NOTE:etc. -
rhysd/committia.vim— splits the commit window into diff + message panes -
lewis6991/gitsigns.nvim— git diff signs in the gutter -
goolord/alpha-nvim— ASCIINEOVIMbanner dashboard with New File, File Browser, and Quit shortcuts
Options set in one compact idiomatic block: line numbers, cursorline, 2-space tabs, smart case search, clipboard = "unnamedplus", persistent undo, no swapfile, trailing space and tab indicators.
A gitcommit FileType autocmd enables spell and sets textwidth=72 — matching the 72-char limit the git hook warns about.
🔧 Git: GPG Signing + Hooks That Apply Everywhere
.gitconfig
All commits are GPG-signed by default. core.hooksPath = ~/.git-hooks redirects hooks globally — every repository on the machine gets these hooks automatically without any per-repo setup.
Useful aliases:
git lg # color graph log with relative time
git today # commits since midnight with [HH:MM AM/PM] timestamps
git yesterday # yesterday's commits
git mine # your commits only across all branches
git last # full detail: hash, author, date, full message
git undo # soft reset HEAD~1
git unstage # reset HEAD --
git el # dump log to git_history.txt
commit-msg hook
Validates the Conventional Commits spec: type(scope): subject. Skips merge commits (^Merge) and revert commits (^Revert). Valid types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert. Warns (non-blocking) if the subject line exceeds 72 characters.
pre-push hook
Sources ~/.zsh_secrets and checks for GIT_PUSH_PASS. If set, prompts for a passphrase via /dev/tty with stty -echo (no terminal echo). Push proceeds only on match. A lightweight push authorization layer without external tooling.
📊 GitHub CLI: Contribution Analytics in the Terminal
gh/.config/gh/config.yml ships a full suite of GraphQL-powered gh aliases with ANSI color formatting baked into the --jq expressions:
Current activity:
gh today # every commit with [HH:MM] timestamps, all repos
gh today-summary # repo-by-repo breakdown + total count
gh today-stats # commits, PRs, issues, private contributions
gh this-month-summary # monthly breakdown via GraphQL (handles 1000+ commits)
gh this-year-summary # year breakdown sorted by repo
gh this-year-languages # top 5 languages by bytes committed this year
gh streak # current and longest commit streak
Historical lookups:
gh yesterday # prior day commits with timestamps
gh last-month-summary # prior month breakdown
gh last-year-languages # top 5 languages in previous year
Utilities:
gh prs # your PRs in the current repo (all states)
gh open # open current repo in browser
gh co # interactive PR checkout
All cross-platform — macOS date flags handled separately from Linux via [[ "$OSTYPE" == "darwin"* ]] checks in the shell snippets.
🎨 Starship: A Two-Line Prompt Showing Everything
The starship.toml config uses a two-line layout with $fill for right-alignment. Line 1 (left): hostname (SSH only), directory, language version modules. Line 1 (right): git branch, git status, command duration, time, OS. Line 2: ❯ (green on success, red on error).
Notable details:
-
Directory truncated to 1 level (
truncation_length = 1) - Git status shows ahead/behind counts (⇡/⇣), untracked (?), modified (!), staged (+), renamed (»), deleted ( )
- Command duration shown after just 1ms
-
Custom VS Code version —
[custom.vscode]runs a shell script that finds the installed binary, reads itspackage.json, and shows the version inline in the prompt whenever VS Code is available. Works acrosscode,code-oss, andcode-insiders. - Language icons for Node, Go, Python, Rust, Java, Kotlin, Lua, Dart, Swift, Zig, Deno, Bun, and more — all Nerd Font.
🖱️ Rofi: Dracula App Launcher (Linux)
rofi/config.rasi uses the Dracula palette as CSS variables: MonoLisa 13 font, rounded 8px corners, 95% opaque background (#282a36ee), bd93f9 purple border and selection highlight, MacTahoe-dark icon theme. 7 items visible, 1 column, no scrollbar.
🎵 ytm-player: YouTube Music in the Terminal
High-quality audio, 80% default volume, prefetch_next enabled, block-style progress bar, album art in UI. MPRIS enabled for Linux media key integration. LastFM and Discord Rich Presence configured but disabled by default — just uncomment to enable.
🏗️ Full Architecture
scripts/
├── install.sh # runs lib/* in order
├── setup.sh # interactive numbered menu
├── config/wallpapers.url # remote wallpapers repo URL
├── utils/detect.sh # OS, pkg manager, sudo detection
└── lib/
├── packages.sh # all tools via apt/brew/pacman
├── plugins.sh # oh-my-zsh, zsh plugins, tpm
├── wallpapers.sh # clones wallpapers repo
├── dotfiles.sh # stow + chmod git hooks
├── go_tools.sh # gopls, goimports, golangci-lint, air,
│ # gotests, govulncheck, shfmt, usql, slim
│ # (--update flag supported)
└── python_tools.sh # httpie, ytm-player via uv
# (--update flag supported)
usql is installed with custom build tags (mysql postgres sqlite3 moderncsqlite) — one universal SQL client for four databases. Both go_tools.sh and python_tools.sh call go clean -modcache or equivalent cleanup after installation.
✅ CI/CD: Dotfiles That Are Actually Tested
shell-validation.yml — Runs shellcheck on every .sh file in scripts/ to catch undefined variables, unsafe patterns, and portability issues.
scripts-functionality.yml — Integration tests that run the core install scripts end-to-end in a CI environment.
Full GitHub issue template system: bug reports, feature requests, and config update requests — each with structured fields. PR template with a checklist.
🔒 Secret Templates
docs/templates/.zsh_secrets.template — Documents exactly which variables to set (including GIT_PUSH_PASS) in ~/.zsh_secrets, sourced by the pre-push hook.
docs/templates/.connections.usql.template — Ready-to-fill database connection string template for usql.
Getting Started
# Full auto-bootstrap
git clone https://github.com/GourangaDasSamrat/dotfiles.git ~/dotfiles
cd ~/dotfiles/scripts && ./install.sh
# Interactive — pick exactly what you want
cd ~/dotfiles/scripts && ./setup.sh
# Just Go tools
bash ~/dotfiles/scripts/lib/go_tools.sh
# Update all Go tools
bash ~/dotfiles/scripts/lib/go_tools.sh --update
# Just Python tools
bash ~/dotfiles/scripts/lib/python_tools.sh
Why This Is Different From Most Dotfiles Repos
Conditional loading everywhere. Nothing breaks if a dependency isn't installed. Every alias, function, and plugin checks for its binary before defining itself. Works on a minimal server and a fully loaded workstation.
The functions are genuinely useful replacements. apireq isn't a thin curl wrapper — it's a workflow with saved state. timer isn't a sleep alias — it's a full TUI. chpwd hooks aren't novelties — they save real keystrokes every day.
Git hygiene enforced at the machine level. core.hooksPath in .gitconfig points to a global location. Every repository you clone or create automatically gets conventional commit validation and push authorization — not just repos where you remembered to install hooks.
Modular by design. You can take exactly one piece — the fzf config, the git aliases, the apireq function, the starship prompt — and drop it into your own setup without the rest.
The repo is at github.com/GourangaDasSamrat/dotfiles. Everything described here is real, working code — read the source.
If it made your terminal feel like home — drop a ⭐
Built by Gouranga Das Samrat · MIT License · Issues and PRs welcome
Top comments (0)