Ghostty tabs all say 'Claude Code': no-title, env var, and zsh hook fix
Contents
Open multiple projects in Ghostty, split each tab into 2-3 panes running Claude Code, and every tab in the bar reads Claude Code.
Liran Baba described the exact scenario: four Ghostty tabs open one morning, hunting for the one running claudoscope tests, cycling through tabs and panes repeatedly.
When you run multiple AI CLIs at the same time — like the tmux loop with Claude Code and Codex or Kana Chat v3 — not being able to tell which pane belongs to which repo from the tab bar means you get lost fast.
Two things are fighting over the title
Liran Baba’s post identified two causes.
Ghostty’s shell integration puts the foreground process name into the terminal title.
At a prompt that’s zsh, during a Claude Code session it’s claude, during a Node run it’s node.
The other one is Claude Code’s own title updates.
Claude Code auto-updates the terminal title with context-based text.
The official env var list includes CLAUDE_CODE_DISABLE_TERMINAL_TITLE — set it to 1 to stop this.
Disabling just Ghostty’s side doesn’t help; Claude Code overwrites via OSC 2. Disabling just Claude Code’s side doesn’t help either; Ghostty’s shell integration reverts to the foreground process name. The correct order: silence both, then take over with your own zsh hooks.
Tell Ghostty to leave the title alone
On the Ghostty side, add shell-integration-features = no-title.
This disables only the title updates, not the entire shell integration.
shell-integration-features = no-title
Ghostty’s shell integration also handles prompt boundaries, working directory tracking, jump_to_prompt, SSH features, and more.
Using no-title instead of shell-integration = none avoids losing all of that.
On macOS, check which config file Ghostty is actually reading — ~/.config/ghostty/config or ~/Library/Application Support/com.mitchellh.ghostty/config.
If you want dotfiles management, keep the canonical copy on the XDG side and symlink from Application Support.
Silence Claude Code with an env var
On the Claude Code side, add the env var in ~/.claude/settings.json.
{
"env": {
"CLAUDE_CODE_DISABLE_TERMINAL_TITLE": "1"
}
}
You could also export it in your shell before launching claude, but settings.json is easier to keep consistent.
The official docs describe this as “disables the automatic terminal title updates based on conversation context.”
As noted in Adding live memory to Claude Code with CTX, Claude Code hooks and settings are useful but easy to conflict with existing terminal config.
If you already share settings.json across machines, think about whether this env should go in user settings, project settings, or local settings rather than dumping it in one place.
Tag running sessions with zsh preexec
From here, zsh takes over the title.
The original 30-line hook used the git repo name as the default title and prepended a marker when claude was running.
Emoji widths vary across terminals, so this version uses CC instead.
autoload -U add-zsh-hook
function _ghostty_context() {
local ctx
ctx=$(git rev-parse --show-toplevel 2>/dev/null) && echo "${ctx:t}" || echo "${PWD:t}"
}
function _ghostty_set_title() {
printf '\033]2;%s\007' "$(_ghostty_context)"
}
function _ghostty_set_title_for_cmd() {
local cmd="$1"
local ctx="$(_ghostty_context)"
case "$cmd" in
claude|claude\ *|clauded|clauded\ *)
printf '\033]2;CC %s\007' "$ctx"
;;
esac
}
add-zsh-hook chpwd _ghostty_set_title
add-zsh-hook precmd _ghostty_set_title
add-zsh-hook preexec _ghostty_set_title_for_cmd
chpwd fires right after cd.
The tab name updates as soon as you move to a different repo.
preexec fires just before a command runs.
It catches claude or clauded and prepends CC to the repo name.
precmd fires before the next prompt is drawn.
After Claude Code exits, it resets the tab name back to just the repo name.
zsh’s preexec sees the command line after alias expansion.
If clauded='claude --dangerously-skip-permissions' is an alias, claude * catches it.
If clauded is a function or script (not an alias), alias expansion doesn’t apply — that’s why the clauded|clauded\ * branch is there.
The split-level limitation remains
The most important caveat from the original post: Ghostty’s OSC 2 sets the title on the surface (split), not as a persistent tab-level title. With 3 splits in a tab and Claude Code running in just one, the tab bar shows whichever split has focus.
No zsh hook can fix this. Until Ghostty supports persistent tab-level titles or OSC-based tab coloring, keeping it to one project per tab and one role per split is the practical limit. Ghostty discussion #7581 and issue #12235 track this open area.
To cover more AI CLIs, add gemini, cursor-agent, or codex to the case statement — same pattern.
But cramming too much into tab titles defeats the purpose.
A reasonable set: repo name normally, CC repo when Claude Code is active, CC! repo for dangerous-permissions mode.
Visibility before session management
In Claude Code context window degradation, I wrote about signs of long sessions going stale — re-reading the same files, circling back to discarded approaches. The all-tabs-same-title problem is similar, except the context degradation is on the human side. You end up sending instructions to the wrong session because you can’t tell which tab is which.
When running multiple Claude Code or Codex instances, align tab names, tmux session names, log file names, and git worktree names at the same granularity. Fixing tab names alone while leaving log and directory naming inconsistent just moves the confusion somewhere else.