Personal configs for Neovim, tmux, zsh, git, Ghostty, and Claude Code — with deploy scripts for local macOS and remote Linux VMs.
| File | Deploys to | Description |
|---|---|---|
zshrc |
~/.zshrc |
Zsh: history, completion, autosuggestions, fzf, zoxide, aliases, functions |
starship.toml |
~/.config/starship.toml |
Starship prompt: directory, git status, language versions |
ghostty.conf |
~/.config/ghostty/config |
Ghostty: font, catppuccin-mocha theme, zsh integration, Option-as-Alt |
gitconfig |
~/.gitconfig |
Git: delta diff pager, aliases, sensible defaults |
gitconfig.local |
~/.gitconfig.local |
Default identity (LinkedIn) + includeIf for ~/perso/ |
gitconfig.personal |
~/.gitconfig.personal |
Personal identity (Apache) for ~/perso/ repos |
gitignore_global |
~/.gitignore_global |
Global gitignore: macOS, editors, secrets, build artifacts |
ssh_config.custom |
~/.ssh/config.custom |
SSH: route git@github.com to personal key for ~/perso/ |
nvim_init.lua |
~/.config/nvim/init.lua |
Neovim: markdown editing, folding, TOC sidebar, render-markdown |
tmux.conf |
~/.tmux.conf |
Tmux: Ctrl+Space prefix, mouse, vim navigation, 50k scrollback |
statusline-command.sh |
~/.claude/statusline-command.sh |
Claude Code statusline: git branch, token usage, cost, model |
claude-settings.json |
~/.claude/settings.json |
Claude Code: statusline config + allow/deny permissions |
Brewfile |
— | All Homebrew packages, installed via brew bundle |
macos-defaults.sh |
— | macOS system settings: keyboard, trackpad, Finder, Dock, screenshots |
bin/tmux-sessionizer |
~/.local/bin/tmux-sessionizer |
Fuzzy-pick a repo/worktree under ~/work or ~/perso → tmux session |
deploy-dotfiles-local |
~/bin/deploy-dotfiles-local |
Deploy all configs to local macOS |
deploy-dotfiles |
~/bin/deploy-dotfiles |
Deploy configs to remote Linux VMs via SSH |
git clone git@github.com:b-slim/.dotfiles.git ~/.dotfiles && ~/.dotfiles/deploy-dotfiles-localThis clones the repo and immediately deploys everything: installs all packages via Homebrew, symlinks all configs, installs Neovim plugins.
Or step by step:
git clone git@github.com:b-slim/.dotfiles.git ~/.dotfiles
# Preview what will happen without touching anything
~/.dotfiles/deploy-dotfiles-local --dry-run
# Deploy (symlinks configs, installs packages via Brewfile)
~/.dotfiles/deploy-dotfiles-local
# Also apply macOS system settings (keyboard, Dock, Finder)
~/.dotfiles/deploy-dotfiles-local --macos-defaultsConfigs are symlinked by default —
git pullin~/.dotfileskeeps everything up to date automatically.
| Flag | Description |
|---|---|
| (none) | Symlink all configs — stays in sync with git pull |
--copy |
Copy files instead of symlinking |
--dry-run |
Preview all actions without making any changes |
--macos-defaults |
Also run macos-defaults.sh (keyboard, Dock, Finder, screenshots) |
--help |
Show usage |
- Homebrew — installs if missing
Brewfile—brew bundle --no-upgrade(all packages at once)zshrc→~/.zshrcstarship.toml→~/.config/starship.tomlghostty.conf→~/.config/ghostty/configgitconfig→~/.gitconfiggitignore_global→~/.gitignore_globalgitconfig.local→~/.gitconfig.local(LinkedIn default +includeIf ~/perso/)gitconfig.personal→~/.gitconfig.personal(bslim@apache.org)ssh_config.custom→~/.ssh/config.custom(chmod 600)nvim_init.lua→~/.config/nvim/init.luatmux.conf→~/.tmux.conf+ reload if tmux is runningstatusline-command.sh→~/.claude/statusline-command.sh(chmod +x)claude-settings.json→ merged into~/.claude/settings.jsonvia jqbin/*→~/.local/bin/(e.g.tmux-sessionizer)- Headless Neovim —
nvim --headless "+Lazy! sync" +qa ~/bin/— symlinks for both deploy scripts
Existing regular files are backed up as .bak before being replaced.
source ~/.zshrc # load zsh config in current session
deploy-dotfiles-local --macos-defaults # optional: apply system settings| Feature | Details |
|---|---|
| History | 100k entries, deduplication, shared across sessions, timestamps |
| Completion | Case-insensitive, colored, cached, interactive menu |
| autosuggestions | Fish-like inline suggestions — → to accept |
| syntax-highlighting | Command coloring as you type (must be sourced last) |
| fzf | Ctrl+R history, Ctrl+T files, Alt+C dirs — with bat previews |
| zoxide | z <partial> jumps to frequent dirs, replaces cd |
| Starship | Single-line prompt: dir + git branch/status + language versions |
| eza | Better ls with icons, git status, tree view |
| bat | Better cat with syntax highlighting, used as fzf preview |
| Alias | Expands to |
|---|---|
v / vi / vim |
nvim |
ll |
eza -lah --icons --git |
lt |
eza --tree --icons -L 2 |
cat |
bat --paging=never |
gs |
git status -s |
ga / gaa |
git add / git add --all |
gc / gcm |
git commit / git commit -m |
gp / gpl |
git push / git pull |
gd / gds |
git diff / git diff --staged |
glog |
git lg (pretty graph log) |
lg |
lazygit |
j <dir> |
zoxide jump |
.. / ... / .... |
cd up 1 / 2 / 3 levels |
brewup |
brew update && brew upgrade && brew cleanup |
path |
Print $PATH one entry per line |
cleanup |
Remove .DS_Store and ._* files recursively |
flushdns |
Flush macOS DNS cache |
| Function | Description |
|---|---|
fv |
fzf → pick file → open in nvim (with bat preview) |
fcd |
fzf → pick directory → cd into it (with eza tree preview) |
fgl |
fzf → browse git log → show diff for selected commit |
wt |
fzf → pick a worktree of the current repo → cd into it |
wtnew <branch> [path] |
Create worktree for branch (existing or new) as sibling of repo → cd into it |
wtrm |
fzf → pick a worktree → remove (refuses main, prompts to confirm) |
mkcd <dir> |
mkdir -p + cd in one step |
extract <file> |
Extract any archive (tar.gz, zip, bz2, 7z, …) |
serve [port] |
Start a Python HTTP server in the current dir (default: 8000) |
topcmds |
Show the 10 most used shell commands |
Machine-specific config (work proxies, private env vars, etc.) goes in ~/.zshrc.local — sourced at the end of .zshrc, not tracked in this repo.
zshrc.local.example contains nag wrappers that print a hint when you reach for an old command, then run it anyway so nothing breaks:
cp ~/.dotfiles/zshrc.local.example ~/.zshrc.local
source ~/.zshrc| Old habit | Nag fires | New habit |
|---|---|---|
cd <path> |
yes | z <partial> |
find |
yes | fd |
grep |
yes | rg |
man <tool> |
yes | tldr <tool> |
Remove each wrapper once it feels automatic. Delete ~/.zshrc.local when done.
| Setting | Value |
|---|---|
| Diff pager | delta — syntax-highlighted, side-by-side, line numbers |
| Editor | nvim |
| Default branch | main |
| Push default | current + auto setup remote |
| Fetch | Prune deleted remote branches automatically |
| Rebase | Auto-stash before rebase |
| Rerere | Remember and reuse conflict resolutions |
| Branch sort | Most recently active first |
| Diff algorithm | histogram (better than default Myers) |
| Alias | Command |
|---|---|
git lg |
Pretty graph log (all branches) |
git lgs |
Pretty graph log (current branch) |
git st |
git status -s |
git aa |
git add --all |
git cm |
git commit -m |
git amend |
git commit --amend --no-edit |
git undo |
Reset last commit, keep changes staged |
git new |
git checkout -b |
git brd |
git branch -d |
git sa |
Stash including untracked files |
git sp |
git stash pop |
git changed |
Files changed in last commit |
git file-log |
Full log for a specific file (git file-log -- path) |
git aliases |
List all configured aliases |
Two identities switch automatically based on directory:
| Directory | Name | |
|---|---|---|
| Everywhere (default) | Slim Bouguerra | sbouguerra@linkedin.com |
~/perso/** |
Slim Bouguerra | bslim@apache.org |
Wired up via includeIf "gitdir:~/perso/" in ~/.gitconfig.local. Verify the active identity in any repo:
git config user.email~/.ssh/config is managed by LinkedIn and cannot be modified directly. Personal SSH config lives in ~/.ssh/config.custom (included by the managed config).
ssh_config.custom configures:
Match host github.com user git
IdentityFile ~/.ssh/personal_github_b-slim
IdentitiesOnly yes
IdentityAgent none
This routes git@github.com connections to the personal SSH key when working in ~/perso/, keeping it separate from LinkedIn's SSH agent.
| Setting | Value |
|---|---|
| Font | JetBrainsMono Nerd Font Mono, size 14, thickened |
| Theme | catppuccin-mocha |
| Shell integration | zsh — cursor shape per mode, prompt marks, title updates |
| Option as Alt | Enabled — required for zsh word nav (Alt+.) and fzf (Alt+C) |
| Scrollback | 100,000 lines |
| Window padding | 10px horizontal, 8px vertical |
| Titlebar style | Tabs |
| Cursor | Block, no blink |
| Copy on select | Disabled |
| Mouse hide | While typing |
Run via deploy-dotfiles-local --macos-defaults or directly:
~/.dotfiles/macos-defaults.sh| Setting | Value | Default |
|---|---|---|
| Key repeat rate | 2 | 6 |
| Initial repeat delay | 15 | 25 |
| Press-and-hold accent menu | Disabled | Enabled |
| Auto-correct | Disabled | Enabled |
| Smart quotes / dashes | Disabled | Enabled |
| Auto-capitalize | Disabled | Enabled |
| Auto-period on double-space | Disabled | Enabled |
| Full keyboard access (Tab in dialogs) | All controls | Text fields only |
| Setting | Value |
|---|---|
| Tap to click | Enabled |
| Setting | Value |
|---|---|
| Show hidden files | Yes |
| Show all file extensions | Yes |
| Show path bar | Yes |
| Show status bar | Yes |
| Default view | List |
| New window target | Home folder |
| Folders on top when sorting | Yes |
| Warn on extension change | No |
.DS_Store on network volumes |
Disabled |
.DS_Store on USB volumes |
Disabled |
| Disk image verification | Disabled (faster mounting) |
| Setting | Value |
|---|---|
| Auto-hide | Enabled |
| Auto-hide delay | 0s |
| Auto-hide animation | 0.2s |
| Show recent apps | No |
| Icon size | 48px |
| Minimize into app icon | Yes |
| Mission Control animation | 0.1s |
| Setting | Value |
|---|---|
| Save location | ~/Desktop/Screenshots/ |
| Format | PNG |
| Drop shadow | Disabled |
| App | Setting |
|---|---|
| Activity Monitor | Show all processes, sort by CPU |
| TextEdit | Default to plain text, UTF-8 |
Some settings (keyboard, trackpad) require a logout/restart to take full effect.
All packages are defined in Brewfile. Install everything:
brew bundle --file=~/.dotfiles/Brewfile| Package | Description |
|---|---|
zsh-autosuggestions |
Fish-like inline suggestions |
zsh-syntax-highlighting |
Command coloring as you type |
fzf |
Fuzzy finder |
starship |
Shell prompt |
zoxide |
Smarter cd |
git |
Version control |
git-delta |
Syntax-highlighted diffs |
lazygit |
TUI git client |
gh |
GitHub CLI |
neovim |
Text editor |
tmux |
Terminal multiplexer |
bat |
Better cat |
eza |
Better ls |
fd |
Better find |
ripgrep |
Better grep |
sd |
Better sed |
jq / yq |
JSON / YAML processors |
bc |
Calculator (Claude statusline) |
tldr |
Concise man pages |
htop |
Process monitor |
watch |
Run command repeatedly |
font-jetbrains-mono-nerd-font |
Terminal font with icons |
ghostty |
Terminal emulator |
mosh |
Better SSH — survives sleep/wake and network roaming |
colima |
Lightweight Docker daemon (replaces Docker Desktop) |
docker |
Docker CLI |
docker-compose |
Multi-container apps |
lazydocker |
TUI for Docker |
dive |
Inspect Docker image layers |
kubectl |
Kubernetes CLI |
k9s |
Kubernetes TUI |
kubectx |
Switch cluster contexts and namespaces fast |
jenv |
Java version manager (per-directory via .java-version) |
maven |
Build tool |
temurin@21 |
Eclipse Temurin JDK 21 (current LTS) |
temurin@17 |
Eclipse Temurin JDK 17 (previous LTS) |
~/.ssh/config.custom configures:
| Setting | Value | Effect |
|---|---|---|
ControlMaster auto |
All hosts | Reuses existing TCP connection — subsequent SSH to same host is instant |
ControlPersist 10m |
All hosts | Keeps the master connection alive 10 min after last session |
ServerAliveInterval 60 |
All hosts | Sends keepalive every 60s — prevents idle disconnects |
| Personal key | github.com git |
Routes to ~/.ssh/personal_github_b-slim |
mosh — use instead of ssh for remote VMs. Survives sleep/wake, network switches, and high latency:
mosh user@vm1 # instead of ssh user@vm1Colima provides the Docker daemon without Docker Desktop.
colstart # start Docker daemon
colstop # stop it| Alias | Command |
|---|---|
dps / dpsa |
docker ps / docker ps -a |
di |
docker images |
dex <ctr> <cmd> |
docker exec -it |
dlf <ctr> |
docker logs -f |
drm / drmi |
docker rm / docker rmi |
dcup / dcdown |
docker-compose up -d / down |
dclogs |
docker-compose logs -f |
lzd |
lazydocker TUI |
dive <image> |
Inspect image layers |
| Alias | Command |
|---|---|
k |
kubectl |
k9 / k9s |
Kubernetes TUI |
kctx <ctx> |
kubectx — switch cluster context |
kns <ns> |
kubens — switch namespace |
kgp / kgpa |
get pods / get pods --all-namespaces |
kgs / kgd / kgn |
get services / deployments / nodes |
klf <pod> |
kubectl logs -f |
kex <pod> <cmd> |
kubectl exec -it |
kdp / kds |
describe pod / describe service |
jenv manages which JDK is active globally or per directory.
| Plugin | What it does |
|---|---|
export |
Sets JAVA_HOME automatically when JDK switches |
maven |
Makes mvn use the jenv-selected JDK |
gradle |
Makes gradle use the jenv-selected JDK |
Drop a .java-version file in any repo to pin its JDK:
jvl 17 # writes .java-version = 17 in current dir
jvl 21 # switch to 21 for this project
jvs # list all registered JDKs
jvg 21 # set global defaultCommit .java-version to the repo so every developer gets the same JDK automatically.
| Alias | Command |
|---|---|
mvnci |
mvn clean install -T4 (4 threads) |
mvncp |
mvn clean package |
mvnt |
mvn test |
mvnst |
mvn install -DskipTests |
mvntree |
mvn dependency:tree |
Markdown-focused configuration with lazy.nvim plugin manager.
| Plugin | Purpose |
|---|---|
vim-markdown |
Folding, syntax |
render-markdown |
Rich in-buffer rendering |
outline.nvim |
TOC sidebar panel |
nvim-treesitter |
Syntax parsing |
Leader key is Space.
| Key | Action |
|---|---|
<Space>t |
TOC in quickfix |
<Space>o |
TOC sidebar panel |
<Space>ff |
Toggle fold under cursor |
<Space>fu |
Open one fold under cursor |
<Space>fU |
Open all nested folds under cursor |
<Space>fa |
Fold all |
<Space>fo |
Unfold all |
<Space>f1 / f2 / f3 |
Fold to level 1 / 2 / 3 |
]] |
Jump to next heading |
[[ |
Jump to previous heading |
gx |
Open URL under cursor in browser |
<Space>mr |
Toggle render-markdown |
| Setting | Value |
|---|---|
| Prefix | Ctrl+Space |
| Mouse support | Enabled |
| Scrollback | 50,000 lines |
| Pane navigation | Prefix + h/j/k/l (vim-style) |
| Alt navigation | Alt+h/j/k/l (no prefix needed) |
| Sessionizer | Prefix + f — fuzzy-pick repo/worktree → switch session |
| Color | 24-bit (true color) |
bin/tmux-sessionizer turns "switch to project X" into one keystroke. Each repo / worktree under ~/work or ~/perso becomes its own persistent tmux session — keeping shell history, running dev servers, and editor state isolated per project.
| Key | Where | Action |
|---|---|---|
Prefix+f |
Inside tmux | Open picker → switch-client to the chosen session (creates it if needed) |
Ctrl+f |
Plain shell | Open picker → attach to the chosen session (creates it if needed) |
Picker scope: every git worktree list of every git repo found as a direct child of ~/work or ~/perso. Non-repo dirs (heap dumps, archives, etc.) are filtered out. Roots are configured at the top of bin/tmux-sessionizer.
Ctrl+f shadows zsh's default forward-char — right-arrow does the same thing.
Session names come from basename of the worktree path, with ., :, and spaces replaced by _ (tmux name restrictions). So ~/work/trino-fix-LTS-bug/ becomes the session trino-fix-LTS-bug.
The wt* functions and tmux-sessionizer are designed to work as one system: one git worktree = one directory = one tmux session. No context-switching cost, no stash dance, no re-running dev servers.
| Command | What it does |
|---|---|
wtnew <branch> |
Create a worktree as a sibling of the current repo (../<repo>-<branch>) and cd into it. Creates the branch if it doesn't exist. |
wtnew <branch> <path> |
Same, but at an explicit path. |
wt |
fzf-pick any worktree of the current repo → cd into it. |
wtrm |
fzf-pick a worktree → confirm → git worktree remove it (refuses the main worktree). |
Ctrl+f / Prefix+f |
Picker across all repos+worktrees under ~/work and ~/perso → tmux session. |
You're in ~/work/trino on main with uncommitted exploratory work you don't want to disturb. You need to start a clean branch for fix-LTS-cache-miss:
cd ~/work/trino
wtnew fix-LTS-cache-miss
# → creates branch + worktree at ~/work/trino-fix-LTS-cache-miss
# → cd's you there
# Hit Prefix+f (or Ctrl+f from a plain shell), pick the new worktree
# → you're now in a dedicated tmux session named `trino-fix-LTS-cache-miss`The main ~/work/trino worktree is untouched — its dev server keeps running in its own session. Switch between them with Prefix+f any time.
A PR review request lands while you're mid-feature. Don't stash; don't switch branches in place — make a throwaway worktree:
cd ~/work/trino
git fetch origin pull/8421/head:pr-8421 # fetch the PR ref as a local branch
wtnew pr-8421 # worktree + cd
# Prefix+f → pick the new session, build/test there
# When done:
wtrm # fzf-pick pr-8421 → confirm → gone
git branch -D pr-8421 # drop the branch tooYour original feature session is exactly where you left it — same editor state, same dev server, same shell history.
You're deep in a Trino debugging session. Slack pings about a claude-assistant doc fix:
Prefix+f → type "assist" → Enter
Lands you in the claude-assistant session (creates it on first jump). Fix, commit, push. Prefix+f → "trino" → Enter, and you're back exactly where you were.
This is the main reason the sessionizer exists: context-switch cost = one fuzzy match.
cd ~/work/trino # back in the main worktree
git worktree list # see what's still around
wtrm # fzf-pick the merged worktree → removewtrm refuses to remove the main worktree, and if you happen to be standing in the worktree you're removing, it cds you back to main first. Branches stick around after wtrm — delete them separately with git branch -d <name> if you're done with them.
- Naming:
wtnew feature/foo-barsanitizes the slash → directory is../<repo>-feature-foo-bar. The branch keeps its slash. - First-time setup on a new VM: ensure
~/work(and/or~/perso) exists before the picker has anything to show. The sessionizer silently produces nothing if both roots are missing. - Adding more roots: edit
ROOTS=(...)at the top ofbin/tmux-sessionizerand redeploy. - Existing branch:
wtnew <existing-branch>checks out the existing branch instead of creating a new one — useful for resuming abandoned work.
Displays in Claude Code's terminal:
- Directory (robbyrussell-style green arrow)
- Git branch with dirty indicator
- Token usage — color-coded: green
<50%, yellow50–79%, red≥80% - Estimated session cost (based on per-model pricing)
- Active model name
- Agent name (when running a subagent)
- Keyboard shortcuts reference line
Installed to ~/.claude/statusline-command.sh. Settings in ~/.claude/settings.json.
- File operations:
Read,Edit,Write - Shell utilities:
ls,cat,grep,find,head,tail,diff,sort,awk,sed,cut,tr,xargs,tree, etc. - Git: all git commands
- Build tools:
gradle,./gradlew,npm - Languages:
python,python3,java - GitHub CLI:
gh pr,gh api,gh issue,gh search,gh auth - Kubernetes:
kubectl get,kubectl describe,kubectl logs - MCP tools:
mcp__captain__*,mcp__glean_default__*
- Sensitive files:
.env,.pem,.key,.p12,.pfx,.keystore,.netrc,.pgpass - Credential dirs:
~/.datavault,~/.azure,~/.azure-devops,~/dev.src - Destructive git:
git push --force,git reset --hard,git clean -f - Destructive shell:
rm -rf / - Network tools:
ssh,scp,nc,netcat,telnet WebSearch
# Single host
deploy-dotfiles user@vm1
# Multiple hosts
deploy-dotfiles user@vm1 user@vm2 user@vm3- Installs/updates Neovim if missing or below v0.8.0 (user-local to
~/.local/, no sudo) - Deploys
nvim_init.lua→~/.config/nvim/init.lua - Deploys
tmux.conf→~/.tmux.conf+ reloads if running - Deploys
statusline-command.sh→~/.claude/(chmod +x) - Merges
claude-settings.json→~/.claude/settings.jsonvia jq - Installs Ghostty terminfo (
xterm-ghostty) from local machine if available - Runs headless Neovim to auto-install plugins via lazy.nvim
git,curl(Neovim install),jq(settings merge),bc(statusline cost calc)- A Nerd Font in your terminal (for icons)
