← buildbench

My terminal thinks Claude Code is named '2.1.121'

I open-sourced my WezTerm config today. Most of it is unremarkable — Hebrew BiDi, a Ghostty-stolen palette, powerline tabs, a custom status bar with battery, load, RAM and a clock face that rotates with the time of day. The repo is at github.com/levz0r/dotfiles.

What was remarkable is what it took to make the tab titles legible.

The bug

My tab title function looked like this:

local proc = basename(pane.foreground_process_name)
local label = (cwd_name and (cwd_name .. ' · ' .. proc)) or proc

Reasonable. Each tab shows <dir> · <process>, e.g. dira · nvim, apeiros · psql. Worked for nvim, node, psql, ssh, every shell.

Then I opened a few Claude Code tabs and the tab strip filled with this:

1 apeiros · 2.1.121    2 data · 2.1.121    6 buildbench · 2.1.121

My first guess: pane title leak. Claude must be setting OSC 0;<title> to its version, and somewhere I’m falling back to pane.title. I’d just removed that fallback, so I went hunting.

No leak. The 2.1.121 was actually coming from pane.foreground_process_name. Claude Code renames its own process to its version number.

Why

I haven’t dug into the source, but a process can change its argv[0] (or use prctl(PR_SET_NAME) on Linux / setproctitle on macOS) to anything it wants. ps, top, and any tool that introspects the process table will see the new name. WezTerm’s foreground_process_name reads from there, so it sees 2.1.121 and dutifully reports it.

My guess at intent: it makes ps aux | grep claude useless, but ps aux | grep 2.1.121 immediately tells you which version you’re running. Convenient for the Claude team. Hostile to my tab bar.

The fix

Detect Claude two ways: by title (Claude Code substring) and by a process name that looks like a semver triple.

local function tab_proc(pane)
  local detected = detect_from_title(pane.title)
  if detected then return detected end
  local p = basename(pane.foreground_process_name)
  if p and p:match '^v?[%d]+%.[%d]+%.[%d]+' then return 'claude' end
  return (p and #p > 0) and p or nil
end

Any process whose name starts with 1.2.3 is now treated as Claude. Then the Claude branch strips both the Claude Code prefix and the trailing · 2.1.121 from the title, leaving just the session name (or the cwd if there isn’t one).

Result:

1 🤖 dira    2 🤖 buildbench    3 🤖 Explore exotic themes…

The lesson

When you’re parsing data the OS gave you about another process, the other process gets to lie. foreground_process_name is not a name — it’s a string the program chose to expose. The same trick is why htop can show node servers as their npm script name and PostgreSQL as its current query.

I knew this in theory. I had to be reminded by a tab bar that read apeiros · 2.1.121.

The fix is six lines. The hour I spent staring at it was worth more than the lines themselves — that’s the kind of bug that earns a place in muscle memory.