Dotfiles
My zsh + tmux setup. Copy what you need.
▶~/.zshrc
# Personal Zsh configuration file. It is strongly recommended to keep all
# shell customization and configuration (including exported environment
# variables such as PATH) in this file or in files sourced from it.
#
# Documentation: https://github.com/romkatv/zsh4humans/blob/v5/README.md.
# Periodic auto-update on Zsh startup: 'ask' or 'no'.
# You can manually run `z4h update` to update everything.
zstyle ':z4h:' auto-update 'yes'
# Ask whether to auto-update this often; has no effect if auto-update is 'no'.
zstyle ':z4h:' auto-update-days '28'
# Keyboard type: 'mac' or 'pc'.
zstyle ':z4h:bindkey' keyboard 'mac'
# Start tmux automatically.
zstyle ':z4h:' start-tmux no
# Mark up shell's output with semantic information.
zstyle ':z4h:' term-shell-integration 'yes'
# Right-arrow key accepts one character ('partial-accept') from
# command autosuggestions or the whole thing ('accept')?
zstyle ':z4h:autosuggestions' forward-char 'partial-accept'
# Recursively traverse directories when TAB-completing files.
zstyle ':z4h:fzf-complete' recurse-dirs 'yes'
# Enable direnv to automatically source .envrc files.
zstyle ':z4h:direnv' enable 'yes'
# Show "loading" and "unloading" notifications from direnv.
zstyle ':z4h:direnv:success' notify 'yes'
# Enable ('yes') or disable ('no') automatic teleportation of z4h over
# SSH when connecting to these hosts.
# zstyle ':z4h:ssh:example-hostname1' enable 'yes'
# zstyle ':z4h:ssh:*.example-hostname2' enable 'no'
zstyle ':z4h:fzf-dir-history' height '60%'
# Send these files over to the remote host when connecting over SSH to the
# enabled hosts.
# zstyle ':z4h:ssh:*' send-extra-files '~/.nanorc' '~/.env.zsh'
# The default value if none of the overrides above match the hostname.
zstyle ':z4h:ssh:*' enable 'yes'
# Clone additional Git repositories from GitHub.
# z4h install ohmyzsh/ohmyzsh || return
z4h install lukechilds/zsh-nvm || return
# Install or update core components (fzf, zsh-autosuggestions, etc.) and
# initialize Zsh. After this point console I/O is unavailable until Zsh
# is fully initialized. Everything that requires user interaction or can
# perform network I/O must be done above. Everything else is best done below.
z4h init || return
# ----------------------------
# PATH (consolidated)
# ----------------------------
export PYENV_ROOT="$HOME/.pyenv"
export PNPM_HOME="$HOME/Library/pnpm"
path=(
~/bin
/opt/homebrew/opt/postgresql@17/bin
$PYENV_ROOT/bin(N)
$PNPM_HOME
"/Applications/Sublime Text.app/Contents/SharedSupport/bin"
"/Applications/iTerm.app/Contents/Resources/utilities"
$HOME/Library/Python/3.9/bin
$path
)
fpath=($HOME/.docker/completions $fpath)
# ----------------------------
# Environment variables
# ----------------------------
export HOMEBREW_NO_AUTO_UPDATE=1
export HISTSIZE=10000000
export SAVEHIST=10000000
export NVM_LAZY_LOAD=true
export NVM_COMPLETION=true
z4h source $Z4H/lukechilds/zsh-nvm/zsh-nvm.plugin.zsh
export DISABLE_OPENCOLLECTIVE=true
export FZF_DEFAULT_OPTS="--history=$HOME/.fzf-history"
export NEO4J_HOME="$HOME/Developer/neo4j"
export EDITOR='code'
export VISUAL="$EDITOR"
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
export GPG_TTY=$(tty)
export PINENTRY_USER_DATA="USE_CURSES=1"
# Source additional local files if they exist.
source /usr/local/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
# Use additional Git repositories pulled in with `z4h install`.
#
# Define key bindings.
z4h bindkey undo Ctrl+/ Shift+Tab # undo the last command line change
z4h bindkey redo Option+/ # redo the last undone command line change
z4h bindkey z4h-fzf-history Ctrl+R # classic fzf history search over ^R
z4h bindkey z4h-cd-back Shift+Left # cd into the previous directory
z4h bindkey z4h-cd-forward Shift+Right # cd into the next directory
z4h bindkey z4h-cd-up Shift+Up # cd into the parent directory
z4h bindkey z4h-cd-down Shift+Down # cd into a child directory
# Autoload functions.
autoload -Uz zmv
# Define functions and completions.
function md() { [[ $# == 1 ]] && mkdir -p -- "$1" && cd -- "$1" }
compdef _directories md
function h() {
# check if we passed any parameters
if [ -z "$*" ]; then
# if no parameters were passed print entire history
history 1
else
# if words were passed use it as a search
history 1 | egrep --color=auto "$@"
fi
}
ts() {
command -v tmux &>/dev/null || { echo "tmux not found"; return 1; }
local sessions=(
"Blog:~/Developer/tiruma.la"
"Obsidian:~/Developer/Obsidian"
"Dev:~/Developer"
"Home:~"
)
local default="${sessions[1]%%:*}" name dir
for s in "${sessions[@]}"; do
name="${s%%:*}" dir="${s##*:}"
tmux has-session -t "$name" 2>/dev/null || \
tmux new-session -d -s "$name" -c "${dir/#\~/$HOME}"
done
tmux switch-client -t "$default" 2>/dev/null || tmux attach -t "$default"
}
# Tmux helper: `t` to pick a session, `t name` to attach-or-create
t() {
command -v tmux &>/dev/null || { echo "tmux not found"; return 1; }
if [[ -n "$1" ]]; then
tmux new-session -A -s "$1"
return
fi
local sessions
sessions=$(tmux list-sessions -F '#{session_name}' 2>/dev/null)
if [[ -z "$sessions" ]]; then
tmux new-session
else
local session
session=$(echo "$sessions" | fzf --reverse --header='Attach to session (or Esc for new)')
if [[ -n "$session" ]]; then
if [[ -n "$TMUX" ]]; then
tmux switch-client -t "$session"
else
tmux attach -t "$session"
fi
else
tmux new-session
fi
fi
}
# Set shell options: http://zsh.sourceforge.net/Doc/Release/Options.html.
setopt glob_dots # no special treatment for file names with a leading dot
setopt no_auto_menu # require an extra TAB press to open the completion menu
setopt EXTENDED_HISTORY
setopt INC_APPEND_HISTORY
setopt SHARE_HISTORY
setopt HIST_VERIFY
setopt HIST_IGNORE_SPACE # prefix command with space to exclude from history
setopt HIST_REDUCE_BLANKS # trim extra whitespace
setopt HIST_IGNORE_ALL_DUPS # remove older duplicate when new entry is added
setopt CORRECT
setopt AUTO_CD
setopt INTERACTIVE_COMMENTS
setopt HIST_FIND_NO_DUPS
# Define aliases.
alias openshell="code $HOME/.zshrc"
alias cls='clear'
alias ll="eza -alh"
alias ls="eza"
alias lt="eza --tree --level=2"
alias tree='tree -a -I .git'
alias dcl="docker compose logs -f"
alias dcd="docker compose down --remove-orphans; docker image prune -f"
alias drv="docker compose down --remove-orphans -v; docker image prune -f"
alias dcu="docker compose up -d"
alias dcb="docker compose build"
alias dni="docker network inspect"
alias dps="docker ps -a"
alias dbp="docker builder prune -a"
alias tpl="tofu plan"
alias tva="tofu validate"
alias tapp="tofu apply"
alias tfmt="tofu fmt -recursive ."
alias tfi="tofu init"
alias trs="tmux kill-server"
eval "$(pyenv init - zsh)"
eval "$(uv generate-shell-completion zsh)"
eval "$(zoxide init zsh)"
test -e "${HOME}/.iterm2_shell_integration.zsh" && source "${HOME}/.iterm2_shell_integration.zsh"
# Auto-start tmux dev session
# if command -v tmux &>/dev/null && [ -z "$TMUX" ] && [ -t 0 ]; then
# ts
# fi
▶~/.tmux.conf
# ==============================================================================
# TMUX CONFIG
# ==============================================================================
# ----------------------------
# Prefix: C-a (easier than C-b)
# ----------------------------
unbind C-b
set -g prefix C-a
bind C-a send-prefix
# ----------------------------
# General
# ----------------------------
set -sg escape-time 0 # no delay for escape (critical for vim)
set -g history-limit 100000 # big scrollback for log tailing
set -g display-time 2000 # show messages longer
set -g status-interval 5 # refresh status bar every 5s
set -g mouse on # mouse for scroll, select, resize
set -g base-index 1 # windows start at 1
set -g pane-base-index 1 # panes start at 1
set -g renumber-windows on # re-number on close
setw -g automatic-rename on # auto-rename to running command
set -g automatic-rename-format '#{b:pane_current_path}'
set -g focus-events on # for vim autoread etc
set -g set-clipboard on # OSC 52 clipboard
setw -g mode-keys vi # vi keys in copy mode
set -g detach-on-destroy off # switch to another session instead of detaching
set -g default-shell /bin/zsh
# ----------------------------
# Terminal / True Color (iTerm2)
# ----------------------------
set -g default-terminal "tmux-256color"
set -ga terminal-overrides ",xterm-256color*:Tc"
set -as terminal-overrides ',*:Smulx=\E[4::%p1%dm'
set -as terminal-overrides ',*:Setulc=\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m'
# iTerm2 title integration
set -g set-titles on
set -g set-titles-string '#S → #W'
# ----------------------------
# Splits & Windows (keep cwd)
# ----------------------------
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
bind c new-window -c "#{pane_current_path}"
unbind '"'
unbind %
# ----------------------------
# Pane Navigation (vim-style)
# ----------------------------
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
# Alt+hjkl — switch panes without prefix
bind -n M-h select-pane -L
bind -n M-l select-pane -R
bind -n M-j select-pane -D
bind -n M-k select-pane -U
# Resize with prefix + H/J/K/L (repeatable)
bind -r H resize-pane -L 5
bind -r J resize-pane -D 5
bind -r K resize-pane -U 5
bind -r L resize-pane -R 5
# Alt+, / Alt+. — prev/next window without prefix
bind -n M-, previous-window
bind -n M-. next-window
# Tab — toggle last window
bind Tab last-window
# Swap windows left/right
bind -r < swap-window -t -1\; select-window -t -1
bind -r > swap-window -t +1\; select-window -t +1
bind -r C-h previous-window
bind -r C-l next-window
# ----------------------------
# Copy Mode (vi — tmux-yank handles clipboard integration)
# ----------------------------
bind -T copy-mode-vi v send-keys -X begin-selection
bind -T copy-mode-vi C-v send-keys -X rectangle-toggle
# ----------------------------
# Utility Keybindings
# ----------------------------
bind r source-file ~/.tmux.conf \; display-message " Config reloaded!"
# Popup windows (tmux 3.2+)
bind g display-popup -E -xC -yC -w 80% -h 80% -d "#{pane_current_path}" lazygit
bind T display-popup -E -xC -yC -w 80% -h 80% -d "#{pane_current_path}"
# Session fuzzy finder via fzf
bind s display-popup -E "\
tmux list-sessions -F '#{?session_attached,*, } #{session_name}' \
| fzf --reverse --header='Switch Session' \
| sed 's/^[* ]*//' \
| xargs tmux switch-client -t"
# Window fuzzy finder
bind w display-popup -E "\
tmux list-windows -a -F '#S:#I #W' \
| fzf --reverse --header='Switch Window' \
| awk '{print \$1}' \
| xargs tmux switch-client -t"
# Quick pane toggles
bind m resize-pane -Z # zoom/unzoom current pane (maximize)
bind x confirm-before -p "Kill pane? (y/n)" kill-pane
bind X confirm-before -p "Kill window? (y/n)" kill-window
bind Q confirm-before -p "Kill session '#S'? (y/n)" kill-session
# Quick session creation with a name prompt
bind N command-prompt -p "New session:" "new-session -s '%%'"
# Rename pane title
bind t command-prompt -p "Pane title:" "select-pane -T '%%'"
# Break pane into its own window
bind B break-pane
# Join a pane from another window (inverse of break)
bind M-j command-prompt -p "Join pane from:" "join-pane -s '%%'"
# Cheatsheet popup (C-a ?)
bind ? display-popup -E -w 40% -h 80% "~/.tmux/scripts/cheatsheet.sh"
# ----------------------------
# Claude Code Integration
# ----------------------------
# C-a y — Capture visible pane content to clipboard
bind y run-shell "~/.tmux/scripts/capture-pane.sh visible"
# C-a Y — Capture last command's output to clipboard
bind Y run-shell "~/.tmux/scripts/capture-pane.sh last-cmd"
# C-a C-y — Capture the OTHER pane's visible content (grab logs/errors while in Claude)
bind C-y run-shell "~/.tmux/scripts/capture-other-pane.sh"
# C-a S — Capture full scrollback to clipboard
bind S run-shell "~/.tmux/scripts/capture-pane.sh all"
# ----------------------------
# Status Bar — Amber (shadcn Lyra)
# ----------------------------
# Palette:
# bg: #0a0a0a card: #171717 surface: #161616
# sec: #262626 accent: #404040 muted: #a1a1a1
# fg: #fafafa amber50: #fffbeb
# amber3: #ffd236 amber4: #fcbb00 amber5: #f99c00
# amber6: #dd7400 amber7: #b75000 amber8: #953d00
# amber9: #7b3306 amber95: #461901 red: #ff6568
set -g status-position top
set -g status-justify left
set -g status-style "bg=#171717,fg=#a1a1a1"
# Left: empty (windows render here via status-justify left)
set -g status-left-length 0
set -g status-left ""
# Right: indicators + session name
set -g status-right-length 80
set -g status-right "\
#{?client_prefix,#[fg=#ffd236]⌨ ,}\
#{?pane_synchronized,#[fg=#ff6568]⚏ ,}\
#[fg=#f99c00,bg=#171717]\
#[fg=#171717,bg=#f99c00,bold] #S "
# Window tabs — inactive
setw -g window-status-format "\
#[fg=#171717,bg=#0a0a0a]\
#[fg=#a1a1a1,bg=#0a0a0a] #I #W \
#[fg=#0a0a0a,bg=#171717]"
# Window tabs — active (amber glow)
setw -g window-status-current-format "\
#[fg=#171717,bg=#dd7400]\
#[fg=#fffbeb,bg=#dd7400,bold] #I #W \
#[fg=#dd7400,bg=#171717]"
setw -g window-status-separator ""
# Pane borders
set -g pane-border-style "fg=#262626"
set -g pane-active-border-style "fg=#f99c00"
# Pane title bar (shows on top border)
set -g pane-border-status top
set -g pane-border-format " #{?pane_active,#[fg=#fcbb00],#[fg=#953d00]}#T "
# Dim inactive panes, full brightness for active
set -g window-style "fg=#a1a1a1"
set -g window-active-style "fg=#fafafa"
# Message / command prompt styling
set -g message-style "bg=#262626,fg=#ffd236"
set -g message-command-style "bg=#262626,fg=#f99c00"
# Copy mode / selection highlight
# A) Warm brown + gold (amber-900 / amber-300)
# setw -g mode-style "bg=#7b3306,fg=#ffd236"
# B) Neutral gray + amber text (accent / amber-500)
setw -g mode-style "bg=#404040,fg=#f99c00"
# C) Subtle amber tint + white (dark amber / foreground)
# setw -g mode-style "bg=#453519,fg=#fafafa"
# D) Minimal + gold text (secondary / amber-300)
# setw -g mode-style "bg=#262626,fg=#ffd236"
# Clock mode
set -g clock-mode-colour "#f99c00"
# Popup border (tmux 3.3+)
set -g popup-border-style "fg=#404040"
# ----------------------------
# Plugins (managed by TPM)
# ----------------------------
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-yank'
set -g @plugin 'tmux-plugins/tmux-logging'
set -g @plugin 'akohlbecker/aw-watcher-tmux'
set -g @plugin 'jaclu/tmux-menus'
set -g @plugin 'tmux-plugins/tmux-sensible'
set -g @plugin 'sainnhe/tmux-fzf'
set -g @plugin 'Morantron/tmux-fingers'
# Logging plugin: prefix + shift+p to toggle logging a pane to file
# prefix + alt+p to save full pane history
# Logs go to ~/tmux-logs/
set -g @logging-path "$HOME/tmux-logs"
# Initialize TPM (MUST be last line)
run '~/.tmux/plugins/tpm/tpm'▶~/.tmux/scripts/capture-pane.sh
#!/bin/bash
# Capture visible pane content to clipboard
# Usage: capture-pane.sh [mode]
# visible - capture what's currently visible on screen (default)
# last-cmd - capture output of the last command (between last two prompts)
# all - capture entire scrollback
mode="${1:-visible}"
pane_id="${2:-}"
case "$mode" in
visible)
tmux capture-pane ${pane_id:+-t "$pane_id"} -p | pbcopy
tmux display-message " Visible pane content copied to clipboard"
;;
last-cmd)
# Capture full scrollback, then extract last command output
# Looks for the last two shell prompts and grabs content between them
tmux capture-pane ${pane_id:+-t "$pane_id"} -p -S -500 | \
awk '
/^[^ \t].*[\$#%>] / || /^❯ / || /^➜ / || /^\$ / {
if (buf != "") prev = buf
buf = ""
collecting = 1
next
}
collecting { buf = buf $0 "\n" }
END { printf "%s", buf }
' | pbcopy
tmux display-message " Last command output copied to clipboard"
;;
all)
tmux capture-pane ${pane_id:+-t "$pane_id"} -p -S - | pbcopy
tmux display-message " Full scrollback copied to clipboard"
;;
esac
▶~/.tmux/scripts/capture-other-pane.sh
#!/bin/bash
# Capture content from the "other" pane (not the active one) to clipboard
# Useful when Claude is in one pane and you want to grab output from the other
current=$(tmux display-message -p '#{pane_id}')
# Get the last active pane that isn't this one
other=$(tmux list-panes -F '#{pane_id}' | grep -v "^${current}$" | head -1)
if [ -z "$other" ]; then
tmux display-message " No other pane to capture from"
exit 1
fi
tmux capture-pane -t "$other" -p | pbcopy
tmux display-message " Other pane content copied to clipboard"
▶~/.tmux/scripts/cheatsheet.sh
#!/bin/bash
# Colors
b=$'\033[1m' # bold
d=$'\033[2m' # dim
a=$'\033[33m' # amber
r=$'\033[0m' # reset
h="${b}${a}" # heading
cat <<EOF
${h}TMUX CHEATSHEET${r}
${h}PANES${r}
C-a | Split side by side
C-a - Split top/bottom
Alt h j k l Move between panes ${d}(no prefix)${r}
C-a h j k l Move between panes
C-a H J K L Resize pane ${d}(repeatable)${r}
C-a x Kill pane
C-a T Shell
${h}WINDOWS${r}
C-a c New window
Alt , / . Prev / next window ${d}(no prefix)${r}
C-a Tab Toggle last window
C-a < / > Swap window left / right ${d}(repeatable)${r}
C-a X Kill window
${h}SESSIONS${r}
C-a N New named session
C-a s Session picker ${d}(fzf)${r}
C-a w Window picker ${d}(fzf)${r}
C-a Q Kill session
${h}COPY TO CLIPBOARD${r}
${b}Quick capture${r}
C-a y Copy visible pane
C-a Y Copy last command output
C-a C-y Copy the other pane
C-a S Copy full scrollback
${b}Select & copy${r} ${d}(enter copy mode first)${r}
C-a [ Enter copy mode ${d}(scroll & select)${r}
v Start selection
C-v Toggle rectangle select
y Yank to clipboard
EOF
read -rsn1