[FEATURE] Terminal Title Configuration (Script-Based, like statusLine)

Open 💬 25 comments Opened Jan 13, 2026 by chadfurman

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

Claude Code sets terminal titles based on the current task or just "Claude Code" - but there's no way to include:

  • Directory - which folder you're working in
  • Repository - which repo this session belongs to
  • Branch - often contains ticket numbers (e.g., ACME-1234-fix-auth)
  • PR number - when reviewing or working on a pull request
  • Original terminal name - what you had before Claude Code took over
  • Custom information - client codes, project phases, billing identifiers

This breaks automatic time tracking. Tools like RescueTime, Timing, Toggl, and ActivityWatch categorize work by window title. When every Claude Code tab just shows "Claude Code" or a task snippet, there's no way to distinguish which client or project you're working in.

For freelancers and consultants billing multiple clients, this means:

  • Manual time entry instead of automatic tracking
  • Inaccurate billing when you forget to log project switches
  • No audit trail for disputed invoices

Community tools don't solve this. Existing solutions (bluzername/claude-code-terminal-title, arorajatin/claude-code-terminal-title) auto-generate titles with fixed formats - no user control over what context appears.

Proposed Solution

Add terminalTitle to settings.json, working exactly like the existing statusLine:

{
  "terminalTitle": {
    "type": "command",
    "command": "~/.claude/terminal-title.sh"
  }
}

Claude Code passes JSON context to the script (same pattern as statusLine), script outputs the desired title:

#!/bin/bash
input=$(cat)
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
PARENT=$(basename "$(dirname "$DIR")")
PROJECT=$(basename "$DIR")
BRANCH=$(git -C "$DIR" branch --show-current 2>/dev/null)

echo "${PARENT}/${PROJECT} [${BRANCH}]"
# → "acme/api [ACME-1234-auth-fix]"

Context Object - The JSON passed to the script could include:

{
  "workspace": {
    "current_dir": "/Users/dev/clients/acme/api",
    "git_branch": "ACME-1234-auth-fix",
    "git_root": "/Users/dev/clients/acme"
  },
  "model": {
    "name": "claude-opus-4-5-20251101",
    "display_name": "Opus 4.5"
  },
  "session": {
    "id": "a1b2c3",
    "task": "Fixing authentication bug",
    "original_terminal_title": "dev-server"
  }
}

Why Script-Based (Not Templates):

  1. Consistency - mirrors statusLine exactly, no new syntax to learn
  2. Flexibility - parse branch names, read project config files, call APIs, include PR numbers
  3. Power users - the people who care about this are comfortable with scripts
  4. Simpler to implement - reuse the existing statusLine infrastructure

Bonus: A /title slash command for ad-hoc overrides would be nice:

/title "🔍 [ACME] Debugging auth - 2hr estimate"

Alternative Solutions

Attempted workarounds:

  1. CLAUDE_CODE_DISABLE_TERMINAL_TITLE=1 - Documented but non-functional (#4765, open since July 2025)
  1. Community tools - bluzername/claude-code-terminal-title and arorajatin/claude-code-terminal-title auto-generate titles but offer no template/script-based customization
  1. Terminal escape sequences - Don't work due to subprocess architecture (no TTY passthrough)

Related issues this would consolidate:

  • #4765 - CLAUDE_CODE_DISABLE_TERMINAL_TITLE broken (open since July 2025)
  • #7229 - Terminal window title settings (open)
  • #15802 - Set terminal title from session (closed as duplicate)
  • #16572, #3396, #15082, #15084 - Various related issues

Priority

High - Significant impact on productivity

Feature Category

Configuration and settings

Use Case Example

Freelancer with multiple clients:

  1. I'm a freelancer working on projects for 3 clients: Acme Corp, Globex, and Initech
  2. I use Timing.app for automatic time tracking - it categorizes work by window title
  3. Currently, all my Claude Code sessions show as "Claude Code" or task snippets
  4. At end of week, I can't tell how many hours went to each client
  5. I manually reconstruct time from memory, often inaccurately

With this feature:

  1. I create ~/.claude/terminal-title.sh:
#!/bin/bash
input=$(cat)
DIR=$(echo "$input" | jq -r '.workspace.current_dir')
CLIENT=$(basename "$(dirname "$DIR")")
PROJECT=$(basename "$DIR")
echo "[$CLIENT] $PROJECT"
  1. My terminal titles now show: [acme] api, [globex] frontend, [initech] backend
  2. Timing.app automatically categorizes hours per client
  3. Invoicing is accurate, auditable, and takes 5 minutes instead of an hour

Additional Context

Technical note: This mirrors the existing statusLine configuration pattern exactly - same JSON context object, same script-based approach. Implementation could likely reuse much of that infrastructure.

Why this matters for Anthropic: Professional/freelancer adoption depends on workflow integration. Time tracking is table stakes for anyone billing hourly. This is low-effort (pattern exists), high-impact for market expansion.

View original on GitHub ↗

25 Comments

batmat · 6 months ago

+1 for this feature - hitting this pain point daily with Warp terminal.

The problem in action: Here's my Window menu showing two different Quarkus-based project folders, both running Claude Code. Completely indistinguishable 😅 :

<img width="308" height="361" alt="Image" src="https://github.com/user-attachments/assets/6f51ea56-3a32-4922-ad33-2dcb491a5952" />

I have to click through each window to find the right one.

Suggestion to enhance your proposal: Pass Claude Code's generated title to the script as a variable (e.g., "claude_title" in the JSON context). This enables a hybrid approach instead of forcing users to choose between 100% custom or 100% CC-generated:

echo "[MyProject] ${CLAUDE_TITLE} [Client-ABC]"

Users could then:

  • Prefix: "PROJ-123 | $CLAUDE_TITLE"
  • Suffix: "$CLAUDE_TITLE | Client: Acme"
  • Wrap & shorten: "[MyPrefix] (${CLAUDE_TITLE:0:10}) [Suffix]"

Interim solutions that would help:

  • Simple prefix/suffix settings (e.g., "terminalTitlePrefix": "[MyProject]")

Workaround I'm using: I manually set the tab's title every single time (despite if I could, I would use just the current folder name etc.).

flxmrk · 6 months ago

+1 I would be happy if just the new configurable "session name" was the title of the terminal.
Related: https://github.com/anthropics/claude-code/issues/3161

rpedroni · 5 months ago

+1 for Warp terminal tab title support.

Specifically, it would be helpful if Claude Code could set the Warp tab title - even just propagating the session name from /rename would be a significant improvement. Currently when working across multiple projects, all tabs look identical which makes context-switching painful.

The script-based approach proposed here would be ideal for customization, but even a simple "emit the session name to terminal title" would solve the immediate pain point.

homoky · 5 months ago

This is one of the things that slows me down a lot.

chadfurman · 5 months ago

We could also add "placeholders" to the terminal title where Claude can substitute things in, as well.

MarkZuber · 4 months ago

+1 to at least add the current working directory that claude was launched from. i have multiple running in different panes on different projects and you can't tell them apart

KyleCartmell · 4 months ago

+1 for me I would like to set a static name for the session e.g. "mow the lawn" and retain the animated status indicator.

biplav-crl · 4 months ago

+1 when working across multiple terminals, I need to cycle through each terminals to reach the one of internest.

flxmrk · 4 months ago

We built this to solve the tab/title/organization challenge: https://github.com/flexmark-intl/aiterm/ - personally, I feel WAY MORE productive using it than I ever was running so many different osx terminal windows each with tabs in them. Its a new project, but we use it internally every day!

chad-fossa · 4 months ago

The new /rename feature fixed this for me

homoky · 4 months ago

I can confirm that.

paul-matthews · 4 months ago

+1 — I use a tab color system in my dotfiles where each project gets a color palette (set via TABCOLOR_PRESET in .envrc + direnv). Work tabs get a bright color + emoji title (🐳 Docker), and Claude tabs inherit the same palette darkened 75% via a SessionStart hook.

The color pairing works great — I can instantly see which Claude tab belongs to which project. But Claude overwrites the tab title with its own status (* Claude Code (gopls)), so the 🤖 Docker title I set in the hook gets lost immediately.

A terminalTitle config (script-based, like statusLine) would let me keep the project context visible. Something like:

{
"terminalTitle": {
"type": "command",
"command": "bash ~/.claude/session-tab-title.sh"
}
}

rasmusloje · 4 months ago
The new /rename feature fixed this for me

This is helpful for windows users with the limitations that Linux/Mac doesn't have.

If I could just call /rename automatically through a hook... It would be a great addition removing manual work.

rasmusloje · 4 months ago

It seems like Claude knows how to automatically rename terminal titles based on context when starting executing plans from plan mode. That's at least my experience, consistently.

So the functionality seems to be there.

flxmrk · 4 months ago

We just decided to build our own terminal - gives you best of both worlds, your own title + term title - not to mention a swarm of other productivity features when you are working with lots of tabs and agents across projects.

TIP: Name your tab like this:
YourFeature %title

And you get claude's title whenever he decides to set it AND a consistent reminder of what you are doing in that tab that never changes.

https://flexmark-intl.github.io/aiterm/

brianruggieri · 4 months ago

I ran into this same issue, running several Claude sessions at once in iTerm. I found myself losing track of which pane was doing what. Now that Claude Code added more hook events recently, I was able to put together claude-code-pulse (ccp). It wraps the CLI and keeps titles updated in realtime.

For myself, I wanted: branch, PR number, and a live status (like when an agent is idle, or when user input was required). It's 'ccp' instead of 'claude' at launch, so not the native solution this issue is asking for, but it has worked for me.

I even added an ai title option to generate a couple word summary of the most recent prompt, to make it even easier to remember what's going on at a glance.

danielsht86 · 4 months ago

Yes please. /rename doesn't work because it's manual and resets whenever you clear the conversation to start a new session. then you have to manually rename. We all have different workflows so a programatic way (like /statusline) where we can choose how the title gets set/updates via hooks would be incredible. Thanks :)

BenNewman100 · 4 months ago

for me /rename doesn't set the tab title in PowerShell 7 running in Windows Terminal.

it's also not programmatically call able from hook scripts as far as I know

chadfurman · 4 months ago

After using /rename a bit and encountering the challenges others have laid
out on this issue like it clearing between sessions and it not being a
template or scriptable, I too.am finding that while /rename improved the
situation around terminal titles and Claude code, there's still a need for
a "status line" style of support

---

Chad Furman, Lead SWE
518-227-0716 (c)
@.***

On Wed, Mar 11, 2026, 23:13 Ben Newman @.***> wrote:

BenNewman100 left a comment (anthropics/claude-code#17951) <https://github.com/anthropics/claude-code/issues/17951#issuecomment-4043612714> for me /rename doesn't set the tab title in PowerShell 7 running in Windows Terminal. it's also not programmatically call able from hook scripts as far as I know — Reply to this email directly, view it on GitHub <https://github.com/anthropics/claude-code/issues/17951#issuecomment-4043612714>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AAE3CISFAAPWTGAAPNIQTIT4QITONAVCNFSM6AAAAACRRVCH76VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DANBTGYYTENZRGQ> . You are receiving this because you authored the thread.Message ID: @.***>
BenNewman100 · 4 months ago

I absolutly agree.

shashankkr9 · 4 months ago

Workaround: LLM-summarized terminal titles via statusLine hook

Sharing a working implementation and gotchas for anyone trying to set terminal tab titles using the statusLine command hook + claude -p for LLM summarization.

The approach

The statusLine config runs a shell script on every render. The script:

  1. Extracts the first user message from the session transcript (JSONL)
  2. Pipes it to claude -p --model haiku to generate a 3-5 word title
  3. Caches the result per session so the LLM call only happens once
  4. Embeds OSC escape sequences in the statusline output to set the terminal title

Bugs encountered (and fixes)

1. Error: Reached max turns (1) — file paths and @ mentions trigger tool use

Even with --tools "", the CLI still loads MCP servers and plugins from global/project settings. If the user message contains @file_ref or /path/to/file, the model tries to use file-reading tools, exhausts --max-turns 1, and returns the error string.

Fix: Add --strict-mcp-config --mcp-config '{"mcpServers":{}}' to completely isolate the summarization call from all MCP/plugin tools. Also strip @ and file paths from the input with sed.

CLEAN_MSG=$(echo "$USER_MSG" | sed 's/[#*_>@]//g; s|[^ ]*/[^ ]*||g')
SUMMARY=$(echo "$CLEAN_MSG" | env -u CLAUDECODE claude -p --model haiku \
  --tools "" --max-turns 1 \
  --strict-mcp-config --mcp-config '{"mcpServers":{}}' \
  --system-prompt "..." 2>/dev/null | head -1)

2. Chatty/invalid LLM responses get cached permanently

Haiku sometimes returns full sentences like "I'm ready to generate terminal tab titles..." instead of a title. This happens when the first "user" message in the transcript is a <local-command-caveat> system tag (from /resume) rather than a real user prompt.

Fix: Skip transcript lines starting with < when extracting the user message, and validate the LLM output before caching:

# Skip system tags
USER_MSG=$(jq -r '...' "$TRANSCRIPT" | grep -v '^\s*<' | head -1)

# Reject bad responses
if echo "$SUMMARY" | grep -qiE '^(Error:|I'\''m ready|Ready for|I need the)'; then
  SUMMARY=""  # fall back to truncation
fi

Feature request

The core issue is that --tools "" doesn't fully disable tools when MCP servers and plugins are configured globally. It would help if:

  • --tools "" also suppressed MCP/plugin tools in --print mode, or
  • There was a simpler flag like --no-tools that means "truly zero tools"

This would make claude -p much more reliable for simple text-in/text-out scripting use cases like title generation, commit message drafting, etc.

guplem · 4 months ago

Adding my use case: I don't need AI-generated summaries in the title at all. I just want to identify which tab is which by git branch name (or repo name when on main).

It would be great if the terminal title had a simple mode selector, e.g.:

  • default - current behavior ("Claude Code status")
  • branch - show the git branch name, or repo name when on the default branch
  • summary - AI-generated task summary (what it currently does mid-session)

Related issues: #34223, #26063, #32600, #34889

rasmusloje · 3 months ago

The day has come

https://code.claude.com/docs/en/changelog#2-1-81

  • v2.1.81 — Fixed terminal tab title not updating with an auto-generated session description

It's doing exactly what I was looking for and needed. Recommend everyone upgrading ASAP 👍

jfgreco · 4 days ago

Adding a concrete data point for this — I went down this exact road building a plugin (jfgreco/tabtag) that sets a per-project tab name plus live status (● / ✓ / ⚠) via the terminalSequence hook output, re-asserting on every lifecycle event (SessionStart / UserPromptSubmit / PostToolUse / Stop) to try to "stay the last writer."

It doesn't hold on 2.1.195. The title paints correctly, but the moment the session settles (goes idle after a turn), Claude Code re-writes its own auto-generated summary and wins. terminalSequence only occupies the title in the brief window before Claude's own write, so it "blips then reverts." There's no hook that fires after that write, and — per the terminal-config, settings.json, and env-var docs — no way to disable it.

The only thing that reliably works is launching with --name, because that changes what Claude writes instead of fighting it — but it's launch-only (can't track branch / PR / cwd mid-session) and precludes any live status.

A statusLine-style title template rendered by Claude Code itself (so it owns the write) would solve this cleanly — same reason statusLine works where manual prompt hacks don't. The plugin's hook/state machine is all there (source) if it's a useful reference for what people are reaching for. +1

AgentMonocle · 1 day ago

+1, with a use case beyond directory/branch/PR templating: organizing multiple concurrent Claude Code sessions.

I run several sessions at once in a planner/executor pattern (one planning session dispatching work plans to 2-3 execution sessions in the same repo). Since all sessions share one working directory, the auto-generated titles are near-identical and I can't tell the windows apart. I tried setting the title from inside a session with a plain OSC 0 escape (printf '\033]0;Session 3 - Execution Session\007'), but Claude Code overwrites it with the auto-generated summary on the next turn.

A script-based terminalTitle setting like statusLine would solve this — even a simpler static terminalTitle: "string" per session (or honoring /rename in the tab title, per #74094) would be enough to label windows by role.