Hardware
My hardware philosophy is simple: invest in things you touch all day, every day. A great keyboard, a sharp monitor, and a machine with enough headroom that you never wait for it.
Keyboard Mod
I use the Nulea split ergonomic keyboard, but with a twist: I rip out the numpad keys and velcro-attach an Apple Magic Trackpad in their place. This keeps the trackpad centered between the split halves, right where your thumbs naturally rest. No more reaching for a mouse — and the ergonomic split keeps your wrists happy during long sessions.
Computer
If you use your laptop as your primary machine — especially for mobile development, client builds, or running multiple dev environments — max out the specs. The M4 Max with 128GB RAM and 8TB storage handles everything I throw at it without breaking a sweat. I also keep a cloud desktop (EC2) for heavier workloads and remote pairing.
Headphones
I use Apple wired USB-C EarPods. I lost too many AirPods Pro to the laundry. These work fine — good microphone, easy to switch between phone and laptop, and they just work. Sometimes the simplest tool is the best tool.
macOS Configuration
A few small macOS tweaks that make a huge difference for keyboard-heavy workflows.
System Preferences
- Caps Lock → Control — The single most impactful remap. Ctrl is used constantly in terminals and Vim, and Caps Lock is in the perfect position for it. System Preferences → Keyboard → Modifier Keys.
- Key Repeat: fastest / Delay: shortest — Makes navigating text feel instant. Holding down
jin Vim should feel like flying.
defaults write
These commands disable the press-and-hold character picker in favor of key repeat — essential for Vim-style editing in VS Code and across macOS.
defaults write com.microsoft.VSCode ApplePressAndHoldEnabled -bool false defaults write -g ApplePressAndHoldEnabled -bool false defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool false
Why three commands? The first targets VS Code specifically, the second sets the global default for the current user, and the third writes to NSGlobalDomain as a catch-all. Belt and suspenders — some apps read one, some read another.
Chrome Extensions
- ARIA DevTools — Inspect accessibility tree directly in the browser. Essential for building accessible UIs.
- Tampermonkey — Run custom userscripts on any page. Useful for tweaking internal tools, adding shortcuts, or automating repetitive web tasks.
Terminal & Shell
I use Ghostty as my terminal emulator with tmux running inside it for session management and pane splitting. The shell is zsh with vi-mode keybindings.
Ghostty Config
window-decoration = true shell-integration-features = no-title title = " " command = /opt/homebrew/bin/tmux new-session
Auto-launch tmux. Ghostty launches directly into a tmux session — no extra step. The title = " " keeps the title bar clean since tmux manages its own status. Shell integration title is disabled to avoid conflicts with tmux's pane labels.
Dotfiles Deep Dive
These are the configuration files I carry across machines. Each one is annotated below. Full backups live in a dedicated directory and are periodically snapshotted.
.zshrc
The shell configuration — aliases, history, vi-mode, path setup, and tool integrations. This is the longest file and the one that evolves most.
alias l='CLICOLOR_FORCE=1 ls -altrh' alias gloa='git log --all --oneline --graph --decorate' alias gloar='git log --all --oneline --graph --decorate --remotes' alias gs='git status' alias gfa='git fetch --all --prune'
Short aliases, big wins. l gives a colorized, reverse-time-sorted listing — the most recent files at the bottom where your cursor is. gloa is a compact graph view of all branches. gfa fetches everything and prunes stale remotes in one shot. These save hundreds of keystrokes per day.
HISTFILE=$HOME/.zsh_history HISTSIZE=100000 SAVEHIST=100000 setopt appendhistory # Append history to the history file (no overwriting) setopt sharehistory # Share history across terminals setopt incappendhistory # Write immediately, don't wait for shell exit
100K lines of history, shared everywhere. With sharehistory and incappendhistory, every terminal session can see commands from every other session in real time. Combined with Ctrl-R reverse search, this becomes a personal command database.
set -o vi bindkey -v # Vi-mode keybindings bindkey fd vi-cmd-mode # Escape to normal mode with 'fd' PROMPT='%n@%m %* %~ %# ' alias h1='history 1' alias vi='nvim' export VISUAL="vim" export EDITOR="$VISUAL"
Vi everywhere. The shell runs in vi-mode, and fd is mapped to Escape — the same mapping used in the .vimrc and nvim configs. This muscle memory carries across every tool. vi is aliased to nvim so the modern editor is always the default.
The prompt includes username, hostname, time, and full path — useful when you're SSH'd into remote machines and need context at a glance.
# Cache brew shellenv (static output, no need to fork every time)
export HOMEBREW_PREFIX="/opt/homebrew"
export HOMEBREW_CELLAR="/opt/homebrew/Cellar"
export HOMEBREW_REPOSITORY="/opt/homebrew"
export PATH="/opt/homebrew/bin:/opt/homebrew/sbin${PATH+:$PATH}"
# Hardcoded path (was: code --locate-shell-integration-path zsh — took 5s!)
[[ "$TERM_PROGRAM" == "vscode" ]] && . "/Applications/Visual Studio Code.app/..."
# Ruby / rbenv (lazy-loaded — only runs rbenv init when you first use ruby)
_rbenv_lazy_init() {
unfunction ruby gem irb bundle rake 2>/dev/null
eval "$(rbenv init - --no-rehash)"
}
for cmd in ruby gem irb bundle rake; do
eval "$cmd() { _rbenv_lazy_init; $cmd \"\$@\" }"
done
# fnm - Fast Node Manager (instead of nvm)
eval "$(fnm env --use-on-cd --shell zsh)"
# Starship prompt
eval "$(starship init zsh)"Shell startup speed matters. Several optimizations here: Homebrew's shellenv output is hardcoded instead of forked every launch. VS Code's shell integration path is hardcoded (the code --locate-... command took 5 seconds!). Ruby's rbenv is lazy-loaded — it only initializes when you first run ruby, gem, etc. And fnm replaces nvm as the Node version manager because it's dramatically faster.
.tmux.conf
Tmux is the session manager that ties everything together. This config focuses on seamless pane navigation and a minimal status bar.
# Mouse support (scroll, click panes, resize)
set -g mouse on
# Generous scroll history
set -g history-limit 500000
# Active pane border - subtle gold highlight with top status
set -g pane-border-style 'fg=colour250'
set -g pane-active-border-style 'fg=colour136'
set -g pane-border-status top
set -g pane-border-format ' #{pane_index}: #{pane_current_command} '
# ── Pane navigation / auto-split ──
# Ctrl-hjkl: move to adjacent pane, or SPLIT a new one at the edge
bind -n C-h if -F '#{pane_at_left}' 'split-window -hb ...' 'select-pane -L'
bind -n C-j if -F '#{pane_at_bottom}' 'split-window -v ...' 'select-pane -D'
bind -n C-k if -F '#{pane_at_top}' 'split-window -vb ...' 'select-pane -U'
bind -n C-l if -F '#{pane_at_right}' 'split-window -h ...' 'select-pane -R'
# ── Pane management ──
bind -n C-x confirm-before -p "Kill pane? (y/n)" kill-pane
bind -n C-z resize-pane -Z # Toggle zoom
bind -n C-= select-layout even-horizontal
# Transparent status bar (inherits terminal background)
set -g status-style 'fg=default,bg=default'
set -g window-status-current-style 'fg=colour136,bg=default,bold'
# Passthrough for image protocols, true color
set -g allow-passthrough on
set -g default-terminal "tmux-256color"
set -ga terminal-overrides ',xterm-ghostty:Tc'Ctrl-HJKL with auto-split is the killer feature. Press Ctrl-H/J/K/L to navigate between panes — but if you're already at the edge, it automatically creates a new pane in that direction. No need to remember separate split and navigate keybindings. Want a new pane to the right? Just Ctrl-L when you're on the rightmost pane. It even inherits your current working directory.
500K scroll history ensures you never lose output from long-running builds. The transparent status bar means tmux visually disappears into Ghostty — no jarring green bar. The gold highlight on the active pane border is subtle but enough to always know where you are.
.vimrc / nvim init.lua
The Vim config is kept minimal — no plugin manager, no 200-line statusline. Just keybindings and sensible defaults. The nvim init.lua is a Lua translation of the same config.
inoremap fd <Esc> vnoremap fd <Esc> " Swap ; and : — enter command mode without Shift nnoremap ; : nnoremap : ; vnoremap ; : vnoremap : ; " Split navigation with Ctrl-HJKL set splitbelow set splitright noremap <C-h> <C-w>h noremap <C-j> <C-w>j noremap <C-k> <C-w>k noremap <C-l> <C-w>l " Same in terminal mode tnoremap <C-h> <C-\><C-n><C-w>h tnoremap <C-j> <C-\><C-n><C-w>j tnoremap <C-k> <C-\><C-n><C-w>k tnoremap <C-l> <C-\><C-n><C-w>l
The same movement everywhere. fd for Escape, Ctrl-HJKL for pane navigation — identical to the tmux and shell configs. The ;/: swap is a classic Vim optimization: you enter command mode with ; (no Shift key), and the rarely-used repeat-find moves to :. Over a day of editing, this saves thousands of Shift keypresses.
syntax on filetype plugin indent on set encoding=utf-8 set tabstop=4 set softtabstop=4 set shiftwidth=4 set expandtab " Spaces, not tabs set autoindent set fileformat=unix set incsearch " Search as you type set hlsearch " Highlight results set ignorecase " Case-insensitive search... set smartcase " ...unless uppercase used set backspace=indent,eol,start set clipboard=unnamed " System clipboard integration " Remove trailing whitespace on save (Python files) autocmd BufWritePre *.py :%s/\s\+$//e " Persistent undo, swap, and backup set undofile set undodir=~/.vim/undo// set swapfile set directory=~/.vim/swap// set backup set backupdir=~/.vim/backup// silent !mkdir -p ~/.vim/swap ~/.vim/backup ~/.vim/undo
Persistent undo is the unsung hero here. Close a file, reopen it days later, and you can still undo. The // suffix on directory paths tells Vim to use the full file path in the swap/backup name, avoiding collisions. Trailing whitespace is auto-stripped on Python files because PEP 8.
Coding Agents & Pi
I use Pi as my primary coding agent. I run a fork of pi-mono that adds a wrapper script for loading and using Pi with AWS Bedrock as the model provider.
The pc Script
I keep a pc (pi commit) script in ~/bin that runs Pi in non-interactive mode for quick tasks — especially auto-generating commit messages from staged changes.
#!/usr/bin/env bash
# pc: run p (pi with bedrock) in non-interactive mode
# Uses pi's -p (--print): process prompt and exit. No TUI.
# Usage: pc [--haiku|--micro|--lite] [prompt...]
# With no args (besides flags), generates a commit message from staged changes.
P_FLAGS=()
PROMPT_ARGS=()
while [[ $# -gt 0 ]]; do
case "$1" in
--haiku|--micro|--lite)
P_FLAGS+=("$1"); shift ;;
*)
PROMPT_ARGS+=("$1"); shift ;;
esac
done
if [[ ${#PROMPT_ARGS[@]} -ge 1 ]]; then
p "${P_FLAGS[@]}" -p "${PROMPT_ARGS[*]}"
else
DIFF=$(git diff --cached)
if [[ -z "$DIFF" ]]; then
echo "No staged changes." >&2; exit 1
fi
PROMPT="Given the following git diff of staged changes, write a good
commit message using conventional commit format. Only output the
commit message, nothing else.
$DIFF"
MSG=$(echo "$PROMPT" | p "${P_FLAGS[@]}" -p \
| perl -0pe 's/<thinking>.*?<\/thinking>\n?//gs')
if [[ -z "$MSG" ]]; then
echo "Failed to generate commit message." >&2; exit 1
fi
git commit -m "$MSG"
fiOne command to commit. pc with no arguments reads git diff --cached, sends it to Pi (backed by Bedrock), strips any thinking tags, and commits with the generated message. Pass --haiku or --lite for cheaper/faster models on trivial commits. Pass a prompt directly (pc "explain this codebase") and it works as a general-purpose CLI.
The p alias (defined elsewhere) points to the Bedrock-configured Pi binary. This keeps the cloud provider config in one place.
Pi Setup
The fork at p10q/pi-mono wraps the upstream Pi with a Bedrock provider integration, along with several custom agents and skills.
Key environment variable in .zshrc:
export PI_SPAWN_CMD=~/bin/p
Agent spawning. PI_SPAWN_CMD tells Pi which binary to use when spawning subagents. By pointing it at the Bedrock-wrapped p script, all spawned agents — including the ones below — automatically inherit the Bedrock provider config.
Custom Agents
I've built a few specialized agents that extend Pi's capabilities:
- tmux-spawn — Spawns Pi subagents in tmux panes, enabling parallel agent workflows. One pane runs your main task while others handle subtasks — code review, test generation, documentation — all visible side by side.
- chrome-devtools — Connects Pi to Chrome's DevTools Protocol for browser automation, visual testing, and interactive debugging. The agent can navigate pages, take screenshots, inspect the DOM, and execute JavaScript — all driven by natural language.
- figma-mcp — Integrates Figma's design data via MCP (Model Context Protocol). Lets Pi read design specs, extract colors and spacing tokens, and generate code that matches the design system. Bridges the gap between design files and implementation.
Composable agent architecture. The beauty of Pi's agent system is that each of these is just a configuration file — a system prompt, a set of tools, and optional skills. They compose together: the tmux-spawn agent can invoke the chrome-devtools agent in a separate pane, which can reference the figma-mcp agent for design specs. It's agents all the way down.
Combined Dotfiles
Complete, copy-ready versions of every config file discussed above. Copy to clipboard or tap to view the full file.