Skip to content

jrimmer/cc-workflow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Claude Code Worktree Workflow

Tmux + Git worktree toolkit for parallel Claude Code sessions

A toolkit that ties together tmux, Git worktrees, and Claude Code so you can run multiple Claude Code agents in parallel — each on its own branch, in its own isolated environment — and switch between them instantly.

Works locally or on a remote dev box. The remote setup is arguably the better experience — sessions persist across disconnects, your laptop stays cool, and Claude Code agents keep working while your lid is closed.


What This Solves

When you work with Claude Code on a project, you sometimes want to:

  • Work on a feature while Claude Code handles a refactor on a different branch
  • Context-switch to an urgent fix without losing your current Claude Code conversation
  • Run multiple Claude Code agents simultaneously without them stepping on each other's files
  • Kick off a long task, close your laptop, and come back to finished work

Git worktrees give you multiple checked-out branches at once. Tmux gives you persistent terminal sessions you can hop between. This toolkit wires them together so the workflow feels like switching browser tabs.


Choose Your Setup

Local (your machine)

Good for: single-machine workflows, quick setup, no network dependency.

Use install.sh — installs the shell functions and tmux config. You manage prerequisites yourself.

Remote dev box (recommended)

Good for: always-on agents, keeping your laptop light, consistent Linux environment, SSH-from-anywhere access.

Use provision.sh — fully automated setup of an Ubuntu server with everything installed and configured. Your laptop becomes a thin client.

Both setups use the same cc commands and tmux keybindings. The only difference is where the sessions run.


Local Installation

Prerequisites

Tool What it does Install
tmux Terminal multiplexer — lets you run multiple terminal sessions brew install tmux (macOS) or apt install tmux (Linux)
fzf Fuzzy finder — powers the interactive pickers brew install fzf or apt install fzf
git Version control (you already have this)
claude Claude Code CLI npm install -g @anthropic-ai/claude-code

Install

cd cc-workflow
chmod +x install.sh
./install.sh

The installer will:

  1. Copy shell functions to ~/.config/cc-workflow/
  2. Install the tmux config to ~/.tmux.conf (backs up any existing one)
  3. Install tmux plugin manager (tpm) if needed
  4. Add a source line to your .bashrc or .zshrc

After installing, reload your shell:

source ~/.bashrc   # or source ~/.zshrc

Then install the tmux plugins by opening tmux and pressing Ctrl-b I (capital I). You'll see a message when it's done.


Remote Dev Box Setup

Why remote?

Running this on a dedicated server changes the workflow in meaningful ways:

Sessions survive everything on your end. Close your laptop, switch networks, reboot — SSH back in and you're exactly where you left it. Locally, tmux protects you from accidentally closing a terminal. Remotely, it protects you from real life.

Always-on agents. Kick off ccn feature-auth, give Claude Code a complex task, then disconnect. Next morning, SSH in and the work is done (or waiting for your input). Locally, your machine has to stay awake and running.

Dedicated resources. Multiple Claude Code instances can be CPU and memory hungry. Offloading to a server keeps your laptop responsive. You're treating your laptop as a thin client.

Consistent environment. If you deploy to Linux, developing on Linux removes an entire class of "works on my machine" surprises. File paths, case sensitivity, line endings — all match production.

What you need

  • A fresh Ubuntu 22.04+ server (Hetzner, DigitalOcean, AWS, a box in your closet — anything works)
  • SSH access with a non-root sudo user
  • A terminal that supports OSC 52 for clipboard passthrough (Ghostty, WezTerm, iTerm2, kitty — most modern terminals do)

A 4-core / 8 GB RAM / SSD instance is plenty for several parallel Claude Code sessions. On Hetzner, that's a CPX31 at roughly €15/month.

Provisioning

The provisioning script sets up everything from bare Ubuntu to a fully configured dev environment:

# Option 1: pipe directly over SSH
ssh your-server 'bash -s' < provision.sh

# Option 2: copy and run
scp provision.sh your-server:
ssh your-server ./provision.sh

The script installs and configures:

  • System packages — tmux, fzf, mosh, git, ripgrep, build-essential, jq, htop
  • Node.js — via nvm, with the version pinned to LTS (currently v22)
  • Claude Code CLI — globally via npm
  • cc-workflow toolkit — shell functions and tmux config (remote-optimized with OSC 52 clipboard)
  • SSH server tuning — keepalive intervals so long sessions don't get killed
  • Mosh firewall rules — opens UDP 60000–61000 if ufw is active
  • tmux auto-attach — SSH login drops you straight into tmux
  • tmux plugin manager — for session persistence across reboots

The script is idempotent — safe to run multiple times. It checks the state of everything before making changes:

  • Packages are only installed if missing (skips apt-get update entirely on re-run)
  • Config files are only written if their content has changed
  • Shell rc lines are only appended if the marker string isn't already present
  • Git config values are only set if currently unset (won't overwrite your customizations)
  • Firewall rules are only added if not already present
  • sshd is only reloaded if its config actually changed

A clean second run produces all "no change" messages with no sudo prompts and no network calls.

Post-provision steps

After the script completes, it prints the remaining manual steps:

1. Reconnect to activate tmux auto-attach:

exit
ssh your-server    # you'll land directly in tmux

2. Install tmux plugins (one time, from inside tmux):

Press Ctrl-b then Shift-I. Wait for the confirmation message.

3. Authenticate Claude Code:

claude auth

This opens a browser-based OAuth flow. If you're on a headless server, Claude Code will give you a URL to visit on your laptop.

4. Clone a project and start working:

cd ~/projects
git clone git@github.com:your-org/your-repo.git
cd your-repo
ccn feature-something

Connecting from your laptop

Add an entry to ~/.ssh/config on your local machine:

Host dev
    HostName <your-server-ip>
    User <your-username>
    ServerAliveInterval 30
    ServerAliveCountMax 5
    ControlMaster auto
    ControlPath ~/.ssh/sockets/%r@%h-%p
    ControlPersist 600

Create the socket directory:

mkdir -p ~/.ssh/sockets

Now you have two ways to connect:

ssh dev     # standard SSH — auto-attaches to tmux
mosh dev    # resilient connection (recommended)

Use mosh for daily work. It handles network changes gracefully — switching from home WiFi to a coffee shop, or your laptop sleeping and waking. Where SSH would hang and require reconnection, mosh recovers silently. The one limitation is that mosh doesn't support scrollback, but that doesn't matter here because tmux handles all your scrolling.

The ControlMaster and ControlPersist settings in the SSH config multiplex connections. After your first SSH connection, subsequent ones (like scp or git push over SSH) reuse the existing tunnel and connect instantly.

Clipboard over SSH

The remote tmux config uses OSC 52 escape sequences to pass copied text from the server through to your local clipboard. When you yank text in tmux copy mode (prefix + v to enter, select text, y to yank), it arrives on your local clipboard ready to paste — even though the text lives on a remote machine.

For this to work, your terminal must support OSC 52:

Terminal Status Configuration needed
Ghostty Works out of the box None
WezTerm Works out of the box None
kitty Works out of the box None
iTerm2 Needs a setting Preferences → General → Selection → "Applications in terminal may access clipboard"
macOS Terminal Not supported Switch to one of the above
Windows Terminal Works out of the box None

If clipboard passthrough isn't working, make sure you're connecting via SSH (not mosh — mosh can interfere with OSC 52 on some setups). For mosh, you may need to set mosh-server to pass through the sequences, or use SSH for the occasional copy operation.


Tmux Crash Course

If you've never used tmux, here's the 2-minute version.

The mental model

Tmux runs in the background and manages sessions. Each session is like an independent desktop with its own terminal windows. When you close your terminal or disconnect from SSH, your sessions keep running. You reconnect to them later and everything is exactly where you left it.

In this workflow, each session maps to one Git branch running one Claude Code instance.

The prefix key

Almost every tmux command starts by pressing Ctrl-b, then releasing both keys, then pressing another key. This two-step pattern is called the prefix.

Throughout this doc, prefix means press Ctrl-b and release it.

For example, "press prefix + d" means:

  1. Hold Ctrl, press b, release both
  2. Press d

Survival commands

These are the only tmux commands you need to memorize on day one:

What you want to do How
Detach from tmux (leave it running in background) prefix + d
Reattach to tmux from a regular terminal Type tmux attach in your terminal
Scroll up to read Claude Code's output Mouse wheel, or prefix + v then arrow keys. Press q to exit scroll mode.
Zoom a pane to full screen (and back) prefix + z

That's it. Everything else in this workflow is handled by the cc commands and keybindings described below.


Daily Workflow

These workflows are identical whether you're running locally or on a remote box. If remote, just ssh dev or mosh dev first — you'll land in tmux automatically.

Starting a new task

Navigate to your project repo, then:

ccn feature-auth

This single command:

  1. Creates a Git worktree for a new feature-auth branch (based on main)
  2. Creates a tmux session named feature-auth
  3. Launches Claude Code inside that session
  4. Switches you into the session

You're now in Claude Code, on an isolated copy of your repo, on its own branch. Start working.

To branch off something other than main:

ccn feature-auth develop

Starting a second task in parallel

While Claude Code is working on feature-auth, you get pulled into a bug. Without closing or interrupting anything:

ccn fix-billing

You're now in a separate session with its own Claude Code instance working on fix-billing. The feature-auth session is still running in the background — Claude Code's conversation, your shell history, everything.

Switching between sessions

You have several ways to switch, depending on the situation:

Method When to use it
Alt+] / Alt+[ Step forward/backward through sessions. Fastest — no prefix key needed. Good when you have 2–3 sessions and want to flip between them.
prefix + L Toggle between the last two sessions you were in. Perfect for "I'm going back and forth between these two branches."
prefix + w Opens a fuzzy finder showing all sessions. Best when you have several sessions and want to jump to a specific one by name.
ccs Same fuzzy finder, but from a shell prompt instead of inside tmux. Shows a preview of each session's last 20 lines of output.

The most common pattern: Alt+] to step through, prefix + L to flip between two.

Checking on everything

ccstatus

Shows all worktrees and whether they have an active tmux session:

  WORKTREE SESSIONS
  ─────────────────

  SESSION                   STATUS       PATH
  -------                   ------       ----
  main                      ○ no session /home/you/projects/myapp
  feature-auth              ● active     /home/you/projects/myapp-feature-auth
  fix-billing               ● active     /home/you/projects/myapp-fix-billing

  Total active sessions: 2

When a task is done

ccrm

A fuzzy picker shows your worktrees. Select one, confirm with y, and it kills the tmux session and removes the worktree. Make sure you've merged or pushed the branch first — the worktree's local files are deleted.

Or remove a specific one directly:

ccrm feature-auth

Disconnecting (remote only)

Just close your terminal or press prefix + d to detach. Everything keeps running. Next time you SSH in, you're back where you were.

For long-running Claude Code tasks, this is the killer feature: start the work, disconnect, come back later.


Command Reference

Shell Commands

All commands start with cc for easy tab completion.

Command Description
ccn <branch> [base] New — Create worktree + tmux session + launch Claude Code. Base branch defaults to main.
ccs Switch — Fuzzy-pick a session to jump to. Shows a preview of each session's recent output.
ccl List — Show all worktrees and their session status.
ccrm [branch] Remove — Kill session + remove worktree. Prompts for confirmation. Uses fuzzy picker if no branch specified.
ccj Jumpcd into a worktree directory without creating/switching tmux sessions. Handy for quick file checks.
cca [layout] Arrange — Split the current session into a pane layout (see Pane Layouts below).
ccall <command> All — Send a shell command to every active session. Useful for ccall "git pull origin main".
ccstatus Status — Overview of all worktrees and sessions.

Tmux Keybindings

Session navigation (the important ones)

Binding Action
Alt + ] Next session (no prefix needed)
Alt + [ Previous session (no prefix needed)
prefix + L Toggle between last two sessions
prefix + w Fuzzy session picker
prefix + W Built-in tree view of all sessions/windows
prefix + N Create a new worktree session (prompts for branch name)

Pane management

Binding Action
prefix + | Split pane vertically (side by side)
prefix + - Split pane horizontally (top/bottom)
prefix + h/j/k/l Navigate between panes (vim-style: left/down/up/right)
prefix + H/J/K/L Resize panes (repeatable — hold prefix and tap repeatedly)
prefix + z Toggle zoom on current pane (full screen ↔ normal)
prefix + S Toggle synchronized panes (type in all panes at once)

Scrolling and copy

Binding Action
Mouse wheel Scroll up/down through output
prefix + v Enter copy/scroll mode (navigate with arrow keys or vim keys)
v (in copy mode) Start text selection
y (in copy mode) Copy selection to clipboard (works over SSH via OSC 52)
q (in copy mode) Exit copy mode
/ (in copy mode) Search forward through output
? (in copy mode) Search backward through output

General

Binding Action
prefix + d Detach from tmux (sessions keep running)
prefix + c Create a new window in current session

Pane Layouts

Use cca to split your Claude Code session into a multi-pane layout. Run this from a shell pane within your tmux session.

cca dev (default)

┌──────────────────────┬───────────────┐
│                      │  logs / tests │
│    Claude Code       │               │
│                      ├───────────────┤
│                      │  shell / git  │
└──────────────────────┴───────────────┘

Best for active development. Watch test output or tail logs in the right panes while Claude Code works on the left.

cca wide

┌──────────────────────────────────────┐
│            Claude Code               │
├──────────────────┬───────────────────┤
│      logs        │      shell        │
└──────────────────┴───────────────────┘

Good for wide monitors. Gives Claude Code more horizontal space.

cca minimal

┌──────────────────────────────────────┐
│            Claude Code               │
├──────────────────────────────────────┤
│              shell                   │
└──────────────────────────────────────┘

Just Claude Code and a shell. Use when you want to keep things simple.

Tip: Press prefix + z to temporarily zoom any pane to full screen. Press it again to restore the layout. This is great when Claude Code produces long output you want to read without squinting.


How Worktrees Are Organized

By default, worktrees are created as sibling directories to your main repo:

~/projects/
├── myapp/                  ← main repo (main branch)
├── myapp-feature-auth/     ← worktree (feature-auth branch)
├── myapp-fix-billing/      ← worktree (fix-billing branch)

Each directory is a fully independent checkout of your repo at a specific branch. Changes in one don't affect the others until you merge.

To use a subdirectory inside the repo instead (.worktrees/), set this in your shell rc before the source line:

export CC_WORKTREE_DIR_STYLE="subdirectory"

A note on node_modules

If your project uses Node.js, each worktree needs its own node_modules. The ccn command detects package.json and kicks off npm install in the background automatically. If you use pnpm, its content-addressable store means multiple worktrees share the actual package data on disk, so this is basically free.


Session Persistence

The tmux configuration includes tmux-resurrect and tmux-continuum plugins which automatically save your session layouts every 10 minutes and restore them if the machine restarts.

This means:

  • Your pane layouts survive reboots
  • Your session names and working directories are preserved
  • You'll need to restart Claude Code instances manually after a reboot (the process itself isn't preserved, just the session structure)

To install the plugins after initial setup, open tmux and press prefix + I (capital I).

On a remote box, reboots are rare, so this is mostly insurance. Locally, it saves you from OS updates that force a restart.


Best Practices

How many parallel sessions?

Start with 2–3. Each Claude Code instance holds a conversation in memory and may run builds or tests. On a 4-core / 8 GB machine, 3–4 active sessions is comfortable. On an 8-core / 16 GB box, you can comfortably run 5–6.

If you're just parking a session to come back to (Claude Code idle, waiting for your input), it costs almost nothing. The limit is active, working Claude Code instances.

Branch naming

Use descriptive, short branch names. They become your tmux session names and show in the status bar, so fix-billing is better than jsmith/JIRA-4521-fix-billing-edge-case-q3. You'll be reading these at a glance.

Push before you remove

ccrm deletes the worktree directory. If you haven't pushed your branch, that work is gone. Make it a habit: push, merge (or open a PR), then ccrm. If you use Claude Code's /git commands, ask it to push before you tear down.

Keep main clean

Don't create worktree sessions off a dirty main branch. Pull before branching:

cd ~/projects/myapp    # your main checkout
git pull origin main
ccn feature-new-thing

Or pull into all worktrees at once:

ccall "git pull origin main"

Use CLAUDE.md per worktree

Each worktree is its own directory, so each can have its own CLAUDE.md. Keep these focused on the specific task rather than dumping everything in. A CLAUDE.md that says "You are working on the billing refactor. The relevant files are in src/billing/. Do not modify the API contract." is more effective than a kitchen-sink file.

Remote: keep your server updated

The provisioning script doesn't set up unattended upgrades. For a dev box, running updates manually is fine:

sudo apt update && sudo apt upgrade -y

Do this periodically, especially for security patches. Your tmux sessions will survive the package upgrades (just not a kernel reboot).

Remote: SSH keys, not passwords

Make sure your server uses SSH key authentication and has password auth disabled. The provisioning script doesn't touch authentication settings — it assumes you've already set this up when you created the server. If you haven't:

# On the server
sudo sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl reload ssh

Remote: consider disk space

Each worktree is a full checkout (minus .git which is shared). A repo with a large working tree — lots of assets, generated files, or node_modules — can add up. Monitor with df -h and clean up old worktrees you're done with.

Remote: multiple repos

The workflow is per-repo. If you work across multiple repos, clone them all into ~/projects/ and cd into whichever one before using ccn. Worktrees and sessions are scoped to the repo you're in.

~/projects/
├── frontend/              ← one repo
├── frontend-feature-x/    ← its worktree
├── backend/               ← another repo
├── backend-fix-api/       ← its worktree

Sessions from different repos coexist in tmux without interference. ccstatus only shows worktrees for the repo you're currently in, but prefix + w and Alt+]/Alt+[ cycle through all sessions regardless of repo.


Recipes

Morning startup (local)

You left three sessions running overnight. Reconnect:

tmux attach

You're back in the last session you were in. Use Alt+]/Alt+[ or prefix + w to check on the others.

Morning startup (remote)

mosh dev

You're back. Everything is exactly where you left it — even if your laptop rebooted overnight.

Fire-and-forget a task (remote)

mosh dev
cd ~/projects/myapp
ccn refactor-database
# Give Claude Code a detailed task description
# Detach: prefix + d (or just close your terminal)
# Come back later:
mosh dev
# Alt+] to find the refactor-database session

Pull latest main into all worktrees

ccall "git pull origin main"

Quick file check without switching sessions

You want to glance at a file in another worktree but don't want to switch context:

ccj              # fuzzy-pick a worktree, cd into it
cat src/config.ts
cd -             # go back to where you were

Run tests in a side pane while Claude Code works

cca dev                    # set up the pane layout
# Click/navigate to the right-top pane, then:
npm run test:watch
# Click back to the Claude Code pane (or prefix + h)

Re-provision after updating the script

You've tweaked provision.sh and want to apply changes:

ssh dev 'bash -s' < provision.sh

Because it's idempotent, it only touches what's changed. No need to worry about duplicating config lines or reinstalling packages.

Emergency: kill everything and start fresh

tmux kill-server

This destroys all sessions. Use only when you want a clean slate. Your worktrees will still exist on disk — use git worktree list and git worktree remove to clean those up manually.


Troubleshooting

"not in a git repository" You need to be inside a Git repo to use ccn, ccl, ccrm, etc. cd into your project first.

"fzf: command not found" Install fzf: brew install fzf (macOS) or apt install fzf (Linux). The fuzzy pickers (ccs, ccrm, ccj, prefix + w) require it. On a provisioned remote box, fzf is already installed.

Session already exists If ccn says a session already exists, it'll switch you to the existing one instead of creating a duplicate.

Worktree already exists If the branch already has a worktree on disk, ccn will reuse it and just create the tmux session.

Alt+] / Alt+[ not working Some terminal emulators capture Alt key combinations. Check your terminal's settings for "meta key" or "option as meta" behavior. In iTerm2, go to Profiles → Keys and set "Left Option Key" to "Esc+". In macOS Terminal, it's Preferences → Profiles → Keyboard → "Use Option as Meta key." Ghostty and WezTerm handle this correctly by default.

Copy to clipboard not working (local) The local config uses pbcopy (macOS). On Linux, change pbcopy in ~/.tmux.conf to xclip -selection clipboard or xsel --clipboard.

Copy to clipboard not working (remote) The remote config uses OSC 52 escape sequences. Make sure your terminal supports it (see the Clipboard over SSH table above). In iTerm2, you need to explicitly enable clipboard access for terminal applications. If using mosh, try connecting via plain SSH for copy operations — mosh can interfere with OSC 52 depending on your version.

SSH connection drops during long sessions The provisioning script configures ClientAliveInterval 30 on the server. You should also have ServerAliveInterval 30 in your local ~/.ssh/config (included in the recommended config above). If connections still drop, your network may be aggressively killing idle TCP connections — switch to mosh, which uses UDP and handles this natively.

Pane navigation feels wrong The config uses vim-style h/j/k/l for pane movement. If you don't use vim, you can also click panes with the mouse (mouse support is enabled) or use the arrow keys with prefix + arrow.

tmux-resurrect didn't restore my sessions Resurrect saves layouts and working directories, not running processes. After a reboot, your sessions will exist with the right pane layouts, but you'll need to restart Claude Code in each one. Just Alt+] through your sessions and type claude in each.

provision.sh fails on "sshd_config.d" Older Ubuntu versions (before 22.04) may not have the /etc/ssh/sshd_config.d/ include directory. Either upgrade to 22.04+ or manually add ClientAliveInterval 30 and ClientAliveCountMax 10 to /etc/ssh/sshd_config.


File Reference

File Purpose
install.sh Local installer — copies files and configures shell. For your laptop/desktop.
provision.sh Remote provisioner — full server setup from bare Ubuntu. Idempotent.
cc-worktrees.sh Shell functions (ccn, ccs, ccrm, etc.). Sourced by your shell rc.
tmux.conf Tmux configuration. Local version uses pbcopy, remote version uses OSC 52.
README.md This file.

About

Claude Code & tmux - dessert topping & floorwax

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages