← Back to selected work

How I Work: Everything Is Text

Ghostty terminal with tmux showing agent status in the statusline and multiple panes

A walkthrough of my terminal-first, keyboard-driven setup I use every day.

Principles

  1. Everything is text
  2. Everything is version-controlled
  3. Everything needs to work natively with AI agents.

Dotfiles Repo

Everything lives in my ~/dotfiles repo.

  • One bootstraps a fresh machine builds a clean Ubuntu container, runs from scratch, and verifies the result. If a new symlink or dependency breaks the install, it fails in Docker instead of on a real machine.
  • Every config file is symlinked from there.
  • When a setting changes, it changes in one place, in a file, with a commit.

Package Management

I try to follow the same principles with package management

  • Homebrew dependencies are declared in a — one file that captures every CLI tool and macOS app.
  • Runtime versions (Node, Python, etc.) are managed by mise and pinned in .

What I Gain (Other Than Being Fun)

  1. I can get a new machine fully configured by running two commands.
  2. I can experiment with my environment
  3. I'm forced to learn how my tools work and I believe that knowledge compounds.

My Workflow

Window and session management

I use AeroSpace for window layout and tmux for project sessions. Both are configured in plain text files — and .

AeroSpace, an i3-like tiling window manager, makes moving between windows quick with my keyboard.

tmux handles the project layer. I switch between sessions/repos with fzf.

fzf session picker with live preview — jumping between projects without touching the mouse.
AeroSpace workspace switching across monitors, keyboard only.

Neovim

Neovim is my text editor. All configuration lives in Lua files under ~/dotfiles/nvim/.

Same keys, different context — pane navigation in tmux, window focus in AeroSpace, moving in Neovim.

Agent Memory and Skills

I believe the agent you choose is less important than the infrastructure underneath them. Models improve and get replaced. The goal is a system I can route any agent through without rebuilding my workflow.

I switch between three agents

  1. Claude Code
  2. Pi
  3. Devin.

All three pull from the same ~/dotfiles/agent/ directory.

text
~/dotfiles/agent/
  commands/   ← slash commands shared across agents
  hooks/      ← lifecycle hooks
  skills/     ← reusable task instructions

commands · hooks · skills

Tmux

tmux does more than session management. A few specific pieces worth calling out:

The session picker (Prefix + s) is a custom binding that replaces tmux's default choose-tree with an fzf popup.

This also helps with remote SSH development. tmux sessions persist on the server so even when the connection drops you can ssh back in, reattach, and everything is exactly where you left it.

I also have a statusline showing active agent state — idle, running, or waiting for input.

I also have notifications fire when an agent session finishes.

Neovim Plugins

A few plugins I use:

Diffview is a full-screen git diff interface. Side-by-side layout, file panel on the left, keybindings consistent with everything else in the setup. The part I reach for most is DiffviewFileHistory on a single file — the fastest way to trace how a piece of code changed and understand why it looks the way it does.

Telescope with the frecency extension (<leader>ff) ranks files by how often and how recently you've opened them.

Persisted saves and restores your workspace — open files, splits, layout — per git branch.

Bufferin (<leader>b) opens a buffer list — every open file, with icons and modified state.

Scripts

~/dotfiles/scripts/ holds the one-off utilities I reuse: theme switching, dev environment startup, notes sync. Since they are shell scripts, agents can read and run them. If something breaks, it's easy to diagnose and fix.