1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
---
title: "My Setup: Claude Code + Ghostty + Worktrees on a Mac"
date: 2026-03-11T23:55:00+01:00
draft: false
slug: "claude-code-ghostty-worktrees-mac-setup"
slug_en: "claude-code-ghostty-worktrees-mac-setup"
description: "How I combine Ghostty, git worktrees, and Claude Code in my daily macOS development workflow. No flickers, massive scrollback, and true parallelism with worktrees."
tags: ["claude-code", "ghostty", "git", "worktrees", "productivity", "mac", "workflow", "setup"]
categories: ["tools"]

translation:
  hash: ""
  last_translated: ""
  notes: |
    - "irse de cañas": "going out for beers". Spanish social ritual. Use local equivalent.
    - "dicho en cristiano": "in plain language". No religious connotation.
    - "de cojones": vulgar but affectionate intensifier meaning "really good / badass". Equivalent: "damn good" or "kickass".
    - "ñapa": "hack/bodge". Affectionate, not derogatory.
    - "currar": colloquial for "to work". Very common in Spain.
    - "mola": "it's cool / it's great". Casual.
    - "chapucilla": diminutive of "chapuza" (hack/kludge). Even more affectionate than "ñapa".
    - "Quick Terminal estilo Quake": reference to the dropdown console in Quake (1996 videogame). Widely known among devs.
    - "buffer del carajo": "huge-ass buffer". Vulgar but common intensifier.
    - "la guinda / el pastel": "the icing / the cake". Spanish version of "icing on the cake".

social:
  publish: true
  scheduled_date: 2026-03-14
  platforms: ["twitter", "linkedin"]
  excerpt: "Ghostty doesn’t flicker, has a massive scrollback buffer, and supports worktrees for running three Claude Code agents in parallel. My Mac development setup, step by step."

wordpress:
  publish: true
  categories: [1]
  tags: ["claude-code", "ghostty", "git", "worktrees", "productivity", "mac"]

video:
  generate: false
  style: "educational"
---

I currently have three Claude Code sessions open. One is translating blog posts. Another is writing tests for a CLI. The third is helping me debug a data pipeline. Each one runs in its own *worktree*, each one in its own Ghostty *split*, while I jump between them using `Cmd+Alt+Arrow`.

I haven’t opened iTerm2 in months. I haven’t touched tmux in weeks. Everything lives in a single Ghostty window.

Is it the perfect setup? No. Is it the most productive setup I’ve ever had? Absolutely.

## Why Ghostty and not another terminal?

I’ll be direct: Ghostty consumes battery like it’s mining Bitcoin. I already detailed this in [the post about GPU-powered terminals and battery drain](/posts/terminal-gpu-battery-macbook-ghostty-iterm2/). That hasn’t changed and it’s still its biggest downside. If I’m on battery, I close Ghostty and switch to Terminal.app without hesitation.

But when I’m plugged in—about 80% of the time at my desk with the Studio Display—Ghostty wins for two reasons that have nothing to do with aesthetic nerdiness:

**1. No flickering.** This sounds trivial until you spend eight hours a day staring at a terminal. iTerm2 has micro-flickers during fast *scrolling*, artifacts when resizing, and subtle *flickers* when switching tabs. Terminal.app is even worse. Ghostty, thanks to GPU rendering, offers a visual smoothness that doesn’t strain your eyes. When Claude Code spits out 200 lines of output and you *scroll* through it, the difference between a terminal that flickers and one that doesn’t is like the difference between reading a book in steady light versus someone flipping the light switch on and off.

**2. You can give it a huge-ass scrollback buffer.** With `scrollback-limit = 50000`, I get 50,000 lines of history per panel. Claude Code is verbose: it generates code, explains it, runs it, displays the output, and sometimes rambles on for hundreds of lines. In iTerm2 or Terminal.app, the default *scrollback* isn’t enough, and you lose context. With Ghostty, I can *scroll* back to the start of a session from two hours ago and find exactly what I need.

There are additional nice features too—native *splits* with `Cmd+D`, Quake-style *Quick Terminal* with `Ctrl+\``—but those are just the icing. The cake is no flicker and no lost scrollback history.

## The anatomy of my window

When I’m in “parallel mode”—multiple independent tasks at once—my Ghostty window looks like this:

┌──────────────────────────────────┬──────────────────────────────────┐ │ │ │ │ Claude Code (worktree A) │ Claude Code (worktree B) │ │ feature/new-validation │ chore/translations │ │ │ │ │ │ │ ├──────────────────────────────────┴──────────────────────────────────┤ │ │ │ Main repo — tests, builds, git log │ │ │ └─────────────────────────────────────────────────────────────────────┘


Two *splits* on top, each one running an agent in its own *worktree*. One *split* on the bottom running `main`, where I run tests, view diffs, and handle merges once the agents are done.

The *Quick Terminal* (`Ctrl+``) is for tasks that take three seconds: checking the backlog in Linear, viewing deploy status, or running a quick `make validate`.

## How to set this up step by step

### 1. Configure Ghostty for development

Here’s the essentials from my `~/.config/ghostty/config`:

Shell

command = /opt/homebrew/bin/fish shell-integration = fish

Readable font for long sessions

font-family = JetBrainsMono Nerd Font font-size = 15 font-thicken = true

Quick Terminal (dropdown Quake-style)

keybind = global:ctrl+grave_accent=toggle_quick_terminal quick-terminal-position = bottom quick-terminal-animation-duration = 0.15

Splits

keybind = cmd+d=new_split:right keybind = cmd+shift+d=new_split:down keybind = cmd+alt+left=goto_split:left keybind = cmd+alt+right=goto_split:right keybind = cmd+alt+up=goto_split:top keybind = cmd+alt+down=goto_split:down

Zoom split (toggle fullscreen for a panel)

keybind = cmd+shift+enter=toggle_split_zoom

Jump between Claude Code prompts (game changer)

keybind = cmd+up=jump_to_prompt:-1 keybind = cmd+down=jump_to_prompt:1

Functional transparency

background-opacity = 0.92 background-blur = 20

Generous scrollback (Claude outputs a lot)

scrollback-limit = 50000

Eliminate unnecessary blinking

cursor-style-blink = false


Most of the settings are sensible defaults, but two are especially noteworthy.

`jump_to_prompt` lets you navigate between Claude Code responses using `Cmd+Up` and `Cmd+Down`. When a session is 40 minutes long and you want to revisit something, instead of *scrolling* endlessly, you can jump right to the previous prompt. It’s really cool.

`toggle_split_zoom` lets you expand a particular *split* to fullscreen without closing the others. If you’re reviewing a long diff in the left split, press `Cmd+Shift+Enter` to expand it to fill the whole window. Review the diff, press it again, and everything snaps back into place.

### 2. Create worktrees

I’ve already written about [what worktrees are](/en/git-worktrees-coding-agents-parallel/) and [how Claude Code uses them](/en/claude-code-worktrees-parallelism-agents/). Here’s the TL;DR of what I do every morning:

```bash
# From the main repo
cd ~/code/my-project

# Create worktrees for the day’s tasks
git worktree add ../my-project-feature-a -b feature/validation
git worktree add ../my-project-translations -b chore/translations

Naming convention: the original repo is main. Each worktree is the repo’s name plus a descriptive suffix. A quick ls ~/code/my-project* gives me an overview.

3. Open splits and launch agents

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Split 1 (already here): main repo
cd ~/code/my-project

# Cmd+D → vertical split
cd ~/code/my-project-feature-a
claude

# Cmd+Alt+Left → go back to the original split
# Cmd+Shift+D → horizontal split below
cd ~/code/my-project-translations
claude

In under a minute, my window is set up with three splits and two agents working.

4. Monitor and merge

The main split is the command center. From there:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# See which worktrees are active
git worktree list

# View the changes from an agent (without changing splits)
git diff feature/validation

# When an agent is done: merge
git merge feature/validation

# Cleanup
git worktree remove ../my-project-feature-a
git branch -d feature/validation

No need to switch splits to view diffs. All worktrees share the same git database, so I can see all branches and their changes from main.

What I’ve learned after two months with this setup

Split zoom is a lifesaver

When an agent generates a 200-line diff and I want to review it carefully, the split gets too small. Cmd+Shift+Enter expands it into a fullscreen view. After reviewing, I either approve or request changes, then return to the three-split layout. No sessions are lost.

Quick Terminal is for tasks that don’t deserve a split

I used to cram everything into splits. Mistake. I’d end up with five tiny panels where nothing was readable. Now the rule is simple: if a task takes more than a minute, it deserves a split. If not, use the Quick Terminal.

Ctrl+` → git log --oneline -5 → Ctrl+`

It pops up, executes, and disappears. No clutter.

No more than three splits at a time

I’ve tried four and five splits. It doesn’t work. On a 27-inch screen, three is the max before each panel gets too narrow and Claude’s output becomes illegible. On a 14-inch laptop, two is the practical limit.

If I need more than three parallel tasks, I open a new tab with Cmd+T and recreate the layout there. Ghostty retains the state of all tabs, so everything is exactly as I left it when the lid is reopened (window-save-state = always).

Worktrees need their own .env

Each worktree is an independent working directory. This means your .env.local, .claude/settings.json, or any file not in git doesn’t exist in a new worktree.

Here’s my little hack: a Fish script that copies what’s needed when creating a worktree:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function wt --description "Create worktree with local files"
    set -l branch $argv[1]
    set -l dir (basename (pwd))-$branch
    git worktree add ../$dir -b $branch
    # Copy local files not tracked by git
    for f in .env.local .claude/settings.json
        if test -f $f
            cp $f ../$dir/$f
        end
    end
    echo "Worktree ready at ../$dir"
end

It’s not elegant, but it works. I could use symlinks, but I prefer independent copies: if an agent changes something in .env.local (which they shouldn’t, but you never know), I don’t want it affecting the others.

Ghostty + Claude Code has a hidden trick

Ghostty’s shell integration (shell-integration = fish) lets the terminal know where each prompt begins and ends. This is what powers jump_to_prompt. But the side effect is that you can select the output of an entire command with a single click in the left margin.

When Claude Code spits out a block of code and you want to copy it, instead of selecting manually (and accidentally including the prompt or missing a line), just click the margin to select all the output. Cmd+C and done.

It’s not documented anywhere, but once you discover it, you can’t live without it.

When I DON’T use this setup

On battery. This is the cost of Ghostty, and there’s no way around it: GPU rendering uses what it uses. I covered this in-depth in Your terminal is burning through battery like it’s mining Bitcoin, and the numbers are obscene. If I’m at a coffee shop, I close Ghostty and open Terminal.app. The workflow is clunkier, but the battery lasts three times as long.

For sequential tasks. If task B depends on task A, there’s no benefit to running in worktrees. A single split with a single agent is enough. Worktrees shine for true parallelism, not for pretending to be productive.

When I don’t fully understand the problem. If I don’t know exactly what I want each agent to do, launching three in parallel is asking for chaos. First, I explore in a single split, understand the problem, and only once I have clear, independent tasks, I set up the three-split layout.

The big picture

flowchart LR
    subgraph Ghostty["   Ghostty   "]
        direction TB
        subgraph Splits["   Splits Cmd+D / Cmd+Shift+D   "]
            direction LR
            S1[" Split 1 \nClaude Code\nworktree A"]
            S2[" Split 2 \nClaude Code\nworktree B"]
            S3[" Split 3 \nmain\ntests + merges"]
        end
        QT[" Quick Terminal Ctrl+` \ngit status, linear, make validate"]
    end

    subgraph Git["   Git   "]
        direction TB
        G1[".git/\n(shared)"]
        W1["worktree A\nfeature/x"]
        W2["worktree B\nchore/y"]
        W3["main"]
    end

    S1 --> W1
    S2 --> W2
    S3 --> W3
    W1 --> G1
    W2 --> G1
    W3 --> G1

    style Ghostty fill:#1a1a2e,stroke:#4a9eed,color:#fff
    style Git fill:#1a2e1a,stroke:#4aed5c,color:#fff
    style Splits fill:#2d3748,stroke:#4a9eed,color:#fff

Ghostty as a visual multiplexer. Worktrees as a git multiplexer. Claude Code as the workhorse. Each part does one thing and does it well.

I don’t need tmux because Ghostty handles multiplexing. I don’t need cloned repos because worktrees share the same state. I don’t need to switch branches because each split is a separate directory.

It’s the first time in years that I feel like my terminal isn’t getting in the way. That my train of thought—“I want to advance three things at once”—translates directly into actions without ceremony, boilerplate, or friction.

Is it perfect? No. The battery issue is still a problem. Five splits are too much. Occasionally, an orphaned worktree will need cleaning up with git worktree prune.

But life doesn’t run on perfection. It runs on what works. And this works damn well.


TL;DR: Ghostty for splits and Quick Terminal (no tmux). Git worktrees for multiple parallel checkouts (no cloning). Claude Code in each split, each in its own worktree. Cmd+D to split, Cmd+Alt+Arrows to navigate, Cmd+Shift+Enter for zoom, `Ctrl+`` for quick terminal. Three splits is the practical max on a 27-inch screen. On battery, use Terminal.app and forget the GPU.