[FEATURE] Agent-maintained session state in the terminal tab title (name + closed state vocabulary, e.g. "proj-42 - blocked")

Status Open
Maintainer reply None cached
Activity 1 comment · opened Jul 16, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

Running several Claude Code sessions in VS Code terminal tabs (or tmux windows), the thing I need at a glance is which session is blocked waiting on me — a decision, a permission, a review — versus which is happily working. Today no title signal can express that:

  • The auto-generated topic title is set early and goes stale (mine still described the first investigation hours after that work had shipped).
  • claude -n "<name>" labels the tab but is static for the whole session.
  • /rename updates the title live, but only from literal user keystrokes.
  • #71369 asks for activity state (generating / unread / idle) — valuable, but it's a different axis. "Actively generating" doesn't tell me the session is semantically blocked on my input; only the agent knows it's in that state.
  • #67858 is the closest prior request: same motivation (parallel sessions, stale names), asking for mechanisms to keep the session name current (model-settable name, live auto-naming, a hook output field, a CLI rename subcommand). It was labeled duplicate and went stale with no engagement. This proposal is complementary rather than the same ask: keep the name stable, and add a validated state dimension on top — plus a working recipe on today's documented primitives.

Proposed Feature

First-class support for an agent-maintained state suffix on the session name in the terminal title:

proj-42 - investigating
proj-42 - verifying
proj-42 - blocked        ← the one that matters at a glance

with the state drawn from a closed, user-configurable vocabulary (e.g. investigating | fixing | verifying | blocked | waiting-review | done in a settings key). Closed matters: free-text states drift ("verifying" / "checking" / "testing…") and titles stop being scannable across tabs.

Possible shapes (any would work):

  1. A supported way for the agent/hooks to update the session's title state — related to #76414 (programmatic /rename) and #67386 (hooks setting the session name), but with a validated state vocabulary rather than free text.
  2. A ${state} variable in a script-based title (#17951's model), fed by an agent-writable, schema-validated field.

It already works today on documented primitives — proof of demand and feasibility

We built this with existing documented pieces; sharing the recipe both as a workaround for others and as evidence the feature wants first-class support:

1. VS Code workspace settings — read titles from escape sequences, and stop Claude Code re-asserting its own title (the documented env var):

"terminal.integrated.tabs.title": "${sequence}",
"terminal.integrated.env.osx": { "CLAUDE_CODE_DISABLE_TERMINAL_TITLE": "1" }

2. A closed state vocabulary in ~/.claude/tab-states.conf:

investigating fixing verifying blocked waiting-review done

3. A cc-title helper the agent calls at state transitions — validates the state against the vocabulary (rejects drift), persists the title per-TTY, writes OSC 0. It walks up the process tree to the first ancestor owning a TTY, since tool-call shells have none:

#!/usr/bin/env sh
# cc-title "<name> - <state>" — validated against ~/.claude/tab-states.conf
conf="$HOME/.claude/tab-states.conf"
title="$*"
[ -f "$conf" ] || { echo "missing $conf" >&2; exit 2; }
allowed=$(grep -v '^[[:space:]]*#' "$conf" | tr -s '[:space:]' ' ')
case "$title" in
    *' - '*) state="${title##* - }" ;;
    *) echo "need '<name> - <state>'; allowed:$allowed" >&2; exit 2 ;;
esac
ok=0; for s in $allowed; do [ "$state" = "$s" ] && ok=1 && break; done
[ "$ok" -eq 1 ] || { echo "'$state' not allowed:$allowed" >&2; exit 2; }
pid=$$
while [ "$pid" -gt 1 ] 2>/dev/null; do
    t=$(ps -o tty= -p "$pid" 2>/dev/null | tr -d ' ')
    case "$t" in
        ''|'??'|'-') pid=$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d ' ') ;;
        *)
            printf '%s' "$title" > "$HOME/.claude/tab-state/$(printf '%s' "$t" | tr '/' '_')"
            printf '\033]0;%s\007' "$title" > "/dev/$t" 2>/dev/null && exit 0 || exit 1
            ;;
    esac
done
exit 1

4. A Stop hook that self-corrects the title at every turn boundary via the documented terminalSequence hook-output field — a sibling script reads the per-TTY state file and emits {"terminalSequence": "<ESC>]0;<title><BEL>", "suppressOutput": true} (bytes built at runtime with printf '\033' passed to jq --arg).

5. A CLAUDE.md/memory instruction telling the agent to call cc-title at session start and on genuine state transitions. The tool enforces the vocabulary, so a drifting agent is corrected rather than trusted.

Launch with claude -n "proj-42" and the agent keeps the suffix current. Verified end-to-end: mid-turn updates render immediately, and the Stop hook re-asserts the saved title when the turn ends.

Observed behaviors that shaped the workaround (useful data points)

  • Claude Code re-asserts its own terminal title on render, so any external OSC title write is reverted almost immediately (a single write, a write-as-last-action, and repeated writes all lose the race). This is why the env var is required today.
  • CLAUDE_CODE_DISABLE_TERMINAL_TITLE=1 silences Claude Code's own title writes but does not suppress hook-emitted terminalSequence output — the two compose exactly as needed.
  • The terminalSequence allowlist (OSC 0/1/2/9/99/777 + BEL) already permits both titles and desktop notifications, which makes hooks a natural home for this.

Why native support would still be better

  • The workaround needs three separate pieces of user setup (env var, VS Code setting, hook) and only covers terminals honoring OSC via ${sequence} — the VS Code extension tab reads the internal session name instead (see #67386), which none of this reaches.
  • The state vocabulary should be a first-class, schema-validated setting rather than a homegrown conf file.
  • With the env var set, Claude Code's own (useful) auto-titling is lost entirely; a native state suffix could compose with it instead of replacing it.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗