Building a tmux Toolkit by Auditing Your Own Git History
TL;DR
Most developer tooling advice tells you what to install. None of it asks when you actually code. We ran
git log --format="%aI"across 15+ projects, discovered that 35% of our commits land between midnight and 4am, and built a tmux toolkit tuned to that reality: auto-checkpoint scripts for deep-night sessions, agent layout shortcuts for the 20-agent swarms we actually run, and project aliases ordered by real commit activity. Six files installed. Every keybinding justified by data.
1. Technical — The Configs
~/.tmux.conf
The base config rewires tmux around three principles: prefix key within reach, vim muscle memory for navigation, and instant access to agent layouts.
Prefix key: C-a instead of the default C-b. The a key sits under the left pinky’s home position. You will press this thousands of times. One key closer matters.
Pane navigation: vim-style with h/j/k/l bound via bind -r. The -r flag makes them repeatable — hold prefix, tap l l l to move right three panes without re-pressing C-a each time. This is critical when you have four Claude Code agents open and you’re bouncing between them.
bind -r h select-pane -L
bind -r j select-pane -D
bind -r k select-pane -U
bind -r l select-pane -R
Splits: | for vertical, - for horizontal. The default % and " bindings are nonsensical. A pipe character looks like a vertical split. A dash looks like a horizontal one. Both inherit the current pane’s working directory via -c "#{pane_current_path}", so new panes open where you already are, not in ~.
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
Agent layout shortcuts: These are the most opinionated bindings.
| Keybinding | Layout | Use Case |
|---|---|---|
C-a g | Two vertical panes, 50/50 | Dual Claude Code agents |
C-a G | Three vertical panes, 33/33/33 | Triple agent swarm |
C-a 4 | 2x2 grid | Quad layout for parallel work |
Tab | Switch between sessions | Jump between project contexts |
The dual and triple layouts exist because our sprint.json data shows ~20 agents spawned per sprint. Running two or three Claude Code instances side by side in the same tmux session is faster than switching terminals. The quad layout handles the “four agents, four files, zero conflicts” pattern we hit regularly in larger sessions.
Copy mode: vim keybindings (setw -g mode-keys vi), v to begin selection, y to yank to system clipboard via pbcopy. On macOS this means you can select text in any tmux pane and paste it anywhere — clipboard integration that works.
bind -T copy-mode-vi v send -X begin-selection
bind -T copy-mode-vi y send -X copy-pipe-and-cancel "pbcopy"
Status bar: Dark cyan background, minimal. Left side shows session name. Right side shows hostname and time. No plugins, no weather widgets, no spotify integration. The status bar exists to tell you which session you’re in. Nothing else.
Scrollback: 50,000 lines. The default 2,000 is useless for Claude Code output. A single agent session can produce thousands of lines of reasoning, diffs, and tool calls. 50K covers even the longest midnight sessions without losing context.
Mouse: On. Purists will disagree. But when you have four panes and need to resize one at 2am, dragging the border is faster than remembering the resize keybindings. Mouse support also enables scroll-to-read in any pane, which matters when reviewing agent output.
~/scripts/cc-session.sh
Creates a tmux session named cc-<project> with a specific three-pane layout:
- Left pane (65% width): Claude Code, launched automatically. This is where you spend 80% of your time.
- Top-right pane: A regular shell. For running builds, checking logs, tailing processes.
- Bottom-right pane:
git log --oneline --graph -20, refreshable. Persistent commit history so you always know the current state.
The script validates the project path before creating anything. If the directory doesn’t exist, it errors with a message instead of silently creating a broken session. It also prints verbose output with [cc-session] prefixes so you can see exactly what it’s doing.
Usage:
cc-session ~/Local_Dev/projects/cinder
# Creates session "cc-cinder" with 3 panes
The session naming strips the path to the project basename. Running cc-session on ~/Local_Dev/projects/soma-chat-ai creates cc-soma-chat-ai. This keeps tmux ls output readable.
~/scripts/cc-dual.sh
Creates a tmux session named dual-<project> with two side-by-side Claude Code agents. No shell pane, no git log. Just two agents, ready to work.
This matches the parallel agent pattern: give each agent a clear scope (e.g., “you handle the API routes” and “you handle the tests”), let them run simultaneously. The dual layout means you can watch both agents work in real time, catch when one is about to step on the other’s files, and intervene before conflicts happen.
cc-dual ~/Local_Dev/projects/soma-chat-ai
# Creates session "dual-soma-chat-ai" with 2 Claude Code panes
~/scripts/cc-checkpoint.sh
Auto-commits at a configurable interval during long sessions. This script exists because of the midnight-to-4am data.
When 35% of your commits happen in the dead of night, you will forget to commit. You’ll be three hours deep in a refactor, the code works, and you’ll close the laptop. The next morning you have a massive uncommitted diff that’s hard to reason about. Or worse, something crashes and you lose work.
The checkpoint script runs in a loop:
- Check if the current directory is a git repo. Exit if not.
- Stage all changes (
git add -A). - If there are staged changes, commit with a message like
checkpoint #3 (cc-checkpoint). - Sleep for N minutes (default: 15).
- Repeat.
The commit messages include a counter so you can see how many checkpoints a session produced. They’re easy to squash later with git rebase -i if you want clean history.
Usage:
# In your project directory, start auto-checkpoint every 10 minutes
cc-checkpoint 10
Run it in one of the right-side panes from cc-session. It stays quiet when there are no changes and only commits when there’s actual work to save.
Enhanced ~/bin/tmux-stats
The original tmux-stats script was a session dashboard with an ASCII art header and system load. We kept the header and added three capabilities:
Project awareness: For each tmux session, the script checks if the session’s working directory is inside ~/Local_Dev/projects/. If so, it shows the project name, current git branch, dirty/clean status, and the latest commit message. This turns tmux-stats from a generic session list into a project status board.
Claude Code process detection: Counts running Claude Code processes and displays the total. When you’re running a swarm of 4 agents across 2 sessions, this tells you at a glance how many are active. The count comes from pgrep -f "claude" filtered to exclude the stats script itself.
Listening ports: Lists all ports currently in use with their process names. Cross-references against ~/Local_Dev/ports.yml when available. This matters because the workspace runs multiple dev servers (Astro on 4397, life-dashboard on various ports, Docker containers on assigned ports) and port collisions are a recurring annoyance.
Quick reference card: A block at the bottom listing the most-used keybindings and aliases. When you forget whether the fuzzy picker is tf or ts, the answer is right there.
.zshrc Additions
All additions are grouped under a # --- tmux toolkit --- comment block for easy identification.
tmux aliases:
| Alias | Command | Purpose |
|---|---|---|
tls | tmux list-sessions | List active sessions |
ta | tmux attach -t | Attach to named session |
tk | tmux kill-session -t | Kill named session |
tn | tmux new-session -s | New named session |
Fuzzy session picker (tf): Uses fzf to select from active tmux sessions and attaches to the chosen one. When you have 5 sessions running (cc-cinder, cc-soma, dual-narssr, cc-relay, cc-life-dashboard), typing tf and a few characters is faster than remembering exact names.
cc-clean function: Kills all tmux sessions matching the cc-* or dual-* pattern. End-of-session cleanup when you’re done with a sprint.
Project aliases (cc and ccr): cc opens Claude Code in the current directory. ccr opens it with the --resume flag to continue a previous conversation.
Project jump function (ccp): Takes a project name, cds to ~/Local_Dev/projects/<name>. Tab-completes against the projects directory. Pairs with cc — type ccp soma-chat-ai then cc and you’re in a Claude Code session for that project.
Top 10 project cd aliases: Ordered by actual commit activity, not alphabetical:
alias soma='cd ~/Local_Dev/projects/soma-chat-ai'
alias relay='cd ~/Local_Dev/projects/claude-relay'
alias narssr='cd ~/Local_Dev/projects/NARSSR'
alias btw='cd ~/Local_Dev/projects/bythewei'
alias threads='cd ~/Local_Dev/projects/threads-analysis'
alias btwco='cd ~/Local_Dev/projects/ByTheWeiCo'
alias lawyer='cd ~/Local_Dev/projects/fire-my-lawyer'
alias cinder='cd ~/Local_Dev/projects/cinder'
alias babel='cd ~/Local_Dev/projects/babel-palace'
alias lifedash='cd ~/Local_Dev/projects/life-dashboard'
The ordering matters. soma and relay are at the top because they have the most commits since March. Alphabetical ordering would bury them.
Port management:
port <number>— shows what’s listening on a port vialsof.killport <number>— kills the process on that port. For when a dev server zombies after a crash.ports— lists all listening ports. Quick audit.
Process cleanup:
kill-orphans— finds and kills orphaned node/python/uvicorn processes. After a crash, dev server child processes sometimes survive the parent. This catches them.
Git shortcut:
gcheck—git log --oneline -10followed bygit status. The two commands you run most after switching to a project, combined into one.
All aliases were checked against the existing .zshrc (which already had gs, gl, gd, ga, gc, gp) to avoid conflicts.
~/scripts was added to $PATH so cc-session, cc-dual, and cc-checkpoint are callable from anywhere.
Keybinding Reference Table
| Context | Key | Action |
|---|---|---|
| tmux prefix | C-a | Prefix (replaces C-b) |
| tmux | C-a | | Vertical split |
| tmux | C-a - | Horizontal split |
| tmux | C-a h/j/k/l | Navigate panes (vim) |
| tmux | C-a g | Dual agent layout |
| tmux | C-a G | Triple agent layout |
| tmux | C-a 4 | Quad agent layout |
| tmux | Tab | Switch sessions |
| tmux copy | v | Begin selection |
| tmux copy | y | Yank to clipboard |
| shell | tf | Fuzzy session picker |
| shell | tls | List sessions |
| shell | ta <name> | Attach to session |
| shell | cc | Claude Code here |
| shell | ccr | Claude Code —resume |
| shell | ccp <proj> | Jump to project dir |
2. Praxis — The Audit Methodology
The Premise
Every tmux tutorial starts with the same keybindings. Every “developer workflow” blog post recommends the same plugins. None of them know when you code, how long your sessions run, how many tools you use simultaneously, or what your project switching patterns look like.
The premise: instead of copying someone else’s config, audit your own git history and build for the patterns that actually exist.
The Methodology
The core command:
git log --format="%aI" --all
This outputs every commit timestamp in ISO 8601 format across all branches. Run it in every project directory, concatenate the output, and you have a complete record of when you’ve shipped code.
We ran this across 15+ projects in ~/Local_Dev/projects/. The pipeline:
for dir in ~/Local_Dev/projects/*/; do
if [ -d "$dir/.git" ]; then
git -C "$dir" log --format="%aI" --all 2>/dev/null
fi
done | cut -dT -f2 | cut -d: -f1 | sort | uniq -c | sort -rn
This extracts the hour from each timestamp, counts occurrences, and sorts by frequency. The output is a histogram of your coding hours.
The Hour Distribution
| Time Block | Commits | Share |
|---|---|---|
| Midnight - 4am | 321 | 35% |
| 1pm - 5pm | 230 | 25% |
| 5pm - 11pm | 255 | 28% |
| 5am - 12pm | 71 | 8% |
| Other | 40 | 4% |
35% of all commits happen between midnight and 4am. Morning commits barely register. This is a night-owl builder pattern — two distinct work windows (afternoon burst and deep-night session) with mornings essentially offline.
This has direct implications for tooling. A midnight-to-4am session is long, uninterrupted, and high-risk for forgetting to commit. Auto-checkpoint exists because of this data.
Project Activity Ranking
Commit counts since March 2026, sorted by activity:
| Project | Commits |
|---|---|
| soma-chat-ai | 154 |
| claude-relay | 101 |
| NARSSR | 61 |
| bythewei | 49 |
| threads-analysis | 48 |
| ByTheWeiCo | 34 |
| fire-my-lawyer | 26 |
| cinder | 13 |
This ranking drove the project alias ordering in .zshrc. It also informed the session scripts — the most active projects need the fastest access paths.
Sprint-Burst Pattern
Commits don’t arrive evenly. They cluster: 10-20 commits in a burst, then quiet for days. A typical sprint looks like:
- Open a Claude Code session.
- Spawn 2-4 agents with clear scopes.
- Build for 2-6 hours straight.
- Ship 10-20 commits.
- Write up the session (blackboard + whiteboard post).
- Go quiet for 1-3 days.
The sprint.json files across projects confirmed this. The ~20 agents spawned stat wasn’t a guess — it was counted from session logs and co-author lines in git history. Commits tagged with Co-Authored-By: Claude lines map directly to agent sessions.
This burst pattern means the toolkit needs to support rapid session startup (not “let me configure my environment for 10 minutes”) and graceful session teardown (cc-clean, kill-orphans). You want to go from zero to four agents in under 30 seconds.
Conventional Commits and Kanban
Soma-chat-ai stood out: consistent feat:, docs:, security:, fix: prefixes on every commit. Most other projects use informal messages. This didn’t change the toolkit design, but it confirmed that the most active project has the most discipline — which makes sense. When you’re committing 154 times in two months, you need structure to keep the log readable.
The kanban-driven workflow (sprint.json with columns, scope_creep counter, archive array) is bythewei-specific but the pattern extends. The sprint board is the source of truth for what happened. The git log is the source of truth for when.
The Scope Creep Counter
The scope_creep.days_without_incident field in sprint.json tracks how many consecutive sessions stayed on-scope. It resets to 0 frequently. The incidents array is a confession log of sessions that started as “fix one bug” and ended as “shipped an entire platform.”
This data point didn’t change the toolkit, but it did validate the checkpoint script. If you’re going to scope-creep a 3-bug fix into a full telemetry platform, you should at least have auto-commits running so the intermediate states are recoverable.
The Philosophy
Don’t configure tools based on defaults or blog posts. Audit your own behavior and build for that.
The data said: you’re a night owl who builds in bursts with multiple agents. So: deep-night auto-checkpoint, instant agent layouts, burst-optimized session startup, and cleanup scripts for when the sprint ends.
If the data had said something different — morning coder, single-agent, long-running sessions — the toolkit would look completely different. The point isn’t the specific keybindings. The point is deriving them from evidence.
3. Functional — Day-to-Day Usage
Starting a Session
You sit down at 10pm. The project is cinder. Here’s the startup sequence:
cc-session ~/Local_Dev/projects/cinder
This creates tmux session cc-cinder with three panes. Claude Code is already running in the left pane. The top-right shell is ready for builds. The bottom-right shows the last 20 commits.
If you want to attach from another terminal:
ta cc-cinder
Or use the fuzzy picker:
tf
# Type "cin", hit enter
Running Dual Agents
You’re working on soma-chat-ai and need two agents — one for the API layer, one for the frontend.
cc-dual ~/Local_Dev/projects/soma-chat-ai
Two side-by-side Claude Code instances. Tell the left agent to handle backend routes. Tell the right agent to handle React components. Watch them work in parallel. If one starts touching a file the other needs, you see it immediately and can redirect.
For triple or quad layouts within an existing session, use the shortcuts:
# Inside a tmux session:
C-a g # splits into dual
C-a G # splits into triple
C-a 4 # splits into quad
Auto-Checkpoint for Late Night Sessions
It’s 1am. You’ve been building for three hours. Start the checkpoint script in a spare pane:
cc-checkpoint 15
Every 15 minutes, if there are uncommitted changes, it auto-commits with checkpoint #N (cc-checkpoint). You’ll see output like:
[cc-checkpoint] Starting auto-checkpoint every 15 minutes
[cc-checkpoint] Checkpoint #1 committed
[cc-checkpoint] No changes to commit
[cc-checkpoint] Checkpoint #2 committed
When you’re done, Ctrl-C the script. If you want clean history, squash the checkpoints:
git log --oneline # count how many checkpoints
git rebase -i HEAD~5 # squash the last 5 into one real commit
Or leave them. Checkpoint commits are cheap. Lost work is expensive.
Checking Status
Run tmux-stats from any shell. You get:
- ASCII art header (preserved from the original script)
- Per-session breakdown: name, pane count, working directory
- Project details for each session: branch, dirty/clean, latest commit
- Claude Code process count: “3 Claude agents active”
- Listening ports with process names
- Quick reference card with keybindings
This is your command center view. One command, full situational awareness.
The Fuzzy Picker
tf pipes tmux list-sessions through fzf. With 5 sessions running:
tf
> cc-cinder
cc-soma-chat-ai
dual-narssr
cc-relay
cc-life-dashboard
Type a few characters, press Enter, attached. Faster than remembering session names. Faster than tmux ls followed by ta cc-whatever.
Cleaning Up
End of sprint. You have 6 tmux sessions and some orphaned processes.
cc-clean # kills all cc-* and dual-* sessions
kill-orphans # finds and kills orphaned node/python processes
ports # verify nothing is still listening
cc-clean is selective — it only kills sessions matching the Claude Code naming pattern. Your personal tmux sessions (if any) survive.
kill-orphans catches the processes that outlive their parent. A dev server started inside a tmux pane that you killed doesn’t always die. kill-orphans finds them by process name (node, python, uvicorn) and sends SIGTERM.
Port Management
You’re about to start a dev server. Which ports are taken?
ports # list all listening ports
port 4397 # check if 4397 is in use
killport 3000 # kill whatever is on port 3000
Port collisions are the most annoying class of “it works on my machine” bug. These three commands eliminate the guessing.
Project Jumping
You’re in cinder but need to check something in soma:
ccp soma-chat-ai # cd to ~/Local_Dev/projects/soma-chat-ai
gl # git log (existing alias)
gs # git status (existing alias)
Or use the direct alias:
soma # cd to soma-chat-ai
relay # cd to claude-relay
narssr # cd to NARSSR
btw # cd to bythewei
A Typical Evening Session
Here’s the full flow for a real evening coding session:
9:45pm — Sit down. Check what’s running:
tmux-stats
9:46pm — Start a session for the night’s project:
cc-session ~/Local_Dev/projects/soma-chat-ai
9:47pm — Start auto-checkpoint in the shell pane (it’s going to be a long one):
cc-checkpoint 15
9:48pm - 12:30am — Build. The left pane has Claude Code. The top-right shell runs builds and tests. The bottom-right shows commit history updating in real time as checkpoints land.
12:30am — Need a second agent for parallel work:
# Press C-a g for dual layout
# Or open a new session:
cc-dual ~/Local_Dev/projects/soma-chat-ai
2:15am — Sprint complete. Clean up:
cc-clean
kill-orphans
2:16am — Check the damage:
soma && gcheck
# See 14 commits, 6 checkpoints, clean working tree
Six files. Every one justified by data. No cargo-culted keybindings, no plugins-for-the-sake-of-plugins. A toolkit that fits the way you actually work.