Skip to content

✨ feat: 🏷️ Session Status rows and tabs are named by repo - #103

Open
hadees wants to merge 9 commits into
mainfrom
feat/session-status-repo-name
Open

hadees wants to merge 9 commits into
mainfrom
feat/session-status-repo-name

Conversation

@hadees

@hadees hadees commented Oct 16, 2026

Copy link
Copy Markdown
Owner

iTerm2 Session Status rows, and the tab names beside them, now show the repo instead of Claude Code's conversation titles.

Why the rows said "Chat (claude)"

On iTerm2 3.7.5 and later, the Claude Code workgroup renames a session to the workgroup root's display name ("Chat") when claude starts. iTerm2 then adds the job, so the result is "Chat (claude)". OSC 0 titles no longer name that session.

What changes

  • bin/tabname prints the label for a directory: the git toplevel's name, or the repo then an @ then the worktree name for a linked worktree, with the format set by tabname.worktree-format. Outside a work tree it exits 1 and prints nothing. The Alfred session switcher planned next will use it too.
  • The cc-status shim renames the session with it2 session set-name on the first UserPromptSubmit and on every Stop, so a lost rename heals at the next turn end. The name is [<WORKTABS_GROUP>] <WORKSET or tabname>, it lives in memory only, and no iTerm2 preference is involved. The detail line no longer repeats the repo.
  • claude() exports CLAUDE_CODE_DISABLE_TERMINAL_TITLE=1 only where machine-local git config says claude.terminal-title = false, and the overlay sets that on the Mac whose hooks run the shim. With no config, Claude's own titles are untouched, so a public-only clone or a Linux box loses nothing. claude-doctor gains a titles: line.
  • Docs: CLAUDE.md, the shared memory's wrapper defaults, and docs/private-overlays.md.

Landing order

Merge the personal overlay PR (the claude.terminal-title line) first, then this one, then run dotfiles.

Tests

The full suite passes: 713 of 713 ok. On this Mac the shim tests need /usr/bin ahead of the asdf python3 shim under a sandboxed HOME, which is not caused by this branch.

Not verified yet

  • Live: a new claude tab should read <repo> (claude) after the first prompt.
  • Whether Claude's generated title displaces the repo name between Stops with the opt-out unset. If it doesn't, the export can go.
  • /resume titles after a week with the opt-out on.
  • That the workgroup's exit restore gives a plain shell its title back. Two stale Chat (-zsh) tabs need a one-time it2 session set-name "$(tabname)".

🤖 Generated with Claude Code

hadees and others added 9 commits October 15, 2026 17:10
Prints the git toplevel's name, or repo@worktree for a linked worktree
(format overridable via tabname.worktree-format). Nothing and exit 1
outside a work tree. A script so the claude() wrapper and an Alfred
switcher can share one answer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Code's own title

iTerm2's Session Status row is named by the session name, which an OSC 0
title sets. Claude Code writes its rename/AI/haiku title there, so rows
read as conversation topics. The wrapper now exports
CLAUDE_CODE_DISABLE_TERMINAL_TITLE=1 (off-spellings normalised to unset,
so one launch can get Claude's titles back) and writes one title itself
before exec: the tabname label, prefixed by WORKTABS_GROUP, or the
WORKSET name. Only an attended launch gets the bytes; the attended test is
hoisted out of the second-session question and shared.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The claude() wrapper now titles the tab with the repo, and the row name
is that title, so prefixing each Session Status detail with the cwd's
basename only spent room. The detail carries the status alone. A session
launched without the wrapper keeps Claude's own title as its row name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ude's own off

iTerm2 3.7.5+'s Claude Code workgroup renames the session to "Chat" on job
start, so a title written before exec is overwritten within a second. The
cc-status shim will name the session after that; the wrapper keeps the
CLAUDE_CODE_DISABLE_TERMINAL_TITLE default so Claude Code's own OSC 0 titles
do not compete, and loses the OSC 0 write and its pty tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On iTerm2 3.7.5+ the Claude Code workgroup renames the session to "Chat"
when the job starts, so every Session Status row read "Chat (claude)".
The shim now runs it2 session set-name with [WORKTABS_GROUP] and
WORKSET or tabname of the cwd, on a session's first UserPromptSubmit
(marked by <session_id>.named) and on every Stop and StopFailure, never
on SessionStart. It is an in-memory override; no iTerm2 pref is written.
The rename runs after cc-status and before the paint, so the paint stays
the last it2 call of a turn end. explain prints name= and rename=.

The sh gate now sends every Stop and StopFailure to python3.
tests/cc-status-shim.bats gains a tabname stub and 11 rename tests.
Counts changed for Stops carrying a cwd: the seat Stop over an active
seat and the question Stop with a quiet task go from 1 it2 call to 2;
the seat-not-among-tasks, stale-state-file, statement-ending, active-task
question and no-question-mark Stops go from "no it2 call" to exactly the
rename (rename_only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… title

Record why rows read "Chat (claude)" on iTerm2 3.7.5+ (the Claude Code
workgroup renames the session on job start), that the cc-status shim
renames it to the repo through it2 session set-name, and that no pref in
the tracked iTerm2 plist is part of the mechanism. Also tabname's
contract, the wrapper's CLAUDE_CODE_DISABLE_TERMINAL_TITLE default and
its costs, and the shim's new per-turn python3 cost. The shared memory
counts five wrapper defaults.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ET needs no tabname

The .named marker is now written on every first UserPromptSubmit, not only
after a rename, so a session outside a repo or without tabname costs one
python start rather than one per prompt. session_title() checks $WORKSET
before looking for tabname: a working set names the row even with no
tabname installed and outside a repo. explain gains marker=present|none.

Tests: the "outside a repo, without tabname, or without it2" case stays; a new
test asserts the marker for not-a-repo and no-tabname and that the second
prompt skips python (no it2 call, no tabname call, payload fed unchanged);
without it2 the sh gate passes before python so there is no marker. Two
WORKSET tests and one explain marker test added; the explain test asserts
marker=. No existing count assertion changed. CLAUDE.md: the wording that
said no tabname always means no rename now excepts WORKSET, and notes the
marker. The "uncommitted" plist sentence is step 8's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…his clone holds

The tab-name paragraph described "the plist's uncommitted Workgroups blob",
which is a fact about one Mac's working tree. Say instead that the Claude Code
onboarding writes the workgroup and its Enter/Exit triggers into the plist
itself. The step-7 wording fix ($WORKSET needs no tabname) was already in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nal-title is false

CLAUDE_CODE_DISABLE_TERMINAL_TITLE=1 was exported on every machine, but it
only pays where the cc-status shim, iTerm2 3.7.5+ and the integration are
all present. Gate it on machine-local git config, default off, so a
public-only clone or a Linux box keeps Claude's own titles. claude-doctor
gains a titles: line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant