惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

美团技术团队
W
WeLiveSecurity
Stack Overflow Blog
Stack Overflow Blog
L
LangChain Blog
S
SegmentFault 最新的问题
Apple Machine Learning Research
Apple Machine Learning Research
Google DeepMind News
Google DeepMind News
F
Full Disclosure
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The Register - Security
The Register - Security
G
Google Developers Blog
C
Check Point Blog
GbyAI
GbyAI
A
About on SuperTechFans
V
Vulnerabilities – Threatpost
T
The Blog of Author Tim Ferriss
T
Tor Project blog
AWS News Blog
AWS News Blog
Cyberwarzone
Cyberwarzone
C
CERT Recently Published Vulnerability Notes
MongoDB | Blog
MongoDB | Blog
Latest news
Latest news
aimingoo的专栏
aimingoo的专栏
U
Unit 42
Y
Y Combinator Blog
P
Privacy International News Feed
Cisco Talos Blog
Cisco Talos Blog
S
Securelist
S
Schneier on Security
雷峰网
雷峰网
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Attack and Defense Labs
Attack and Defense Labs
P
Proofpoint News Feed
C
Cisco Blogs
Webroot Blog
Webroot Blog
T
Troy Hunt's Blog
Google Online Security Blog
Google Online Security Blog
月光博客
月光博客
P
Privacy & Cybersecurity Law Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
罗磊的独立博客
Cloudbric
Cloudbric
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Hacker News: Ask HN
Hacker News: Ask HN
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园 - 司徒正美
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Microsoft Security Blog
Microsoft Security Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Why every Ghostty tab said 'Claude Code' (and the 30-line zsh hook that fixed it)
Liran Baba · 2026-05-12 · via DEV Community

Liran Baba

I had four Ghostty tabs open the other morning. Three projects, four tabs, each tab split into two or three panes. Every single tab in the tab bar said Claude Code. I cmd-tabbed three times trying to find the one running tests for claudoscope. By the time I scrolled the panes inside each tab to read a path or a prompt, I'd forgotten what I was looking for in the first place.

That's a regression. Tab titles used to mean something. Then everyone started running an AI coding agent in every other pane, and the tab bar quietly turned into visual noise.

Here's the Thing
Two layers fight for the tab title every time you open a Ghostty tab. Ghostty's shell integration sets it to the running process name (zsh, claude, node), and Claude Code emits its own OSC 2 escape sequence to overwrite the title with Claude Code. Turn both off, then drive the title from a 30-line zsh hook that shows the git repo name by default and adds a 👾 marker the moment you launch claude. Repo name when idle, agent when working, no manual reset.

Why every tab says 'Claude Code'

Two layers of misbehavior, both on by default.

Layer 1 is Ghostty. Its shell integration sets the title to whatever process is currently in the foreground. At a prompt, the tab says zsh. Run claude, the tab says claude. Run node, it says node. Fine for a single pane. With four panes per tab, every tab settles on whatever its focused pane is running, which is almost never what you're looking for.

Layer 2 is Claude Code. The agent emits its own OSC 2 escape sequence on startup and during the session, overwriting the title with the literal string Claude Code. Even if you'd disabled Ghostty's integration, Claude would still clobber whatever title you had. Several open Claude Code GitHub issues asked for a way to turn this off, and an env var eventually shipped to do it. It just isn't advertised anywhere prominent.

Stack the two and you get what's in your tab bar right now. Ghostty steps on your title, then Claude steps on Ghostty's. By the time anything reaches the tab bar, all signal is gone.

Layer Sets title to Wins when
Ghostty shell integration Foreground process (zsh, claude, node) Always, unless disabled
Claude Code OSC 2 emitter Claude Code While claude is running
Your zsh hooks Whatever you want When the other two are off

What I wanted

Five things, written as a checklist so I'd know when I was done:

  1. Tab shows the git repo name by default. If I'm not in a repo, the directory basename.
  2. Tab updates when I cd into a different repo. No manual command.
  3. When I launch claude in a pane, the tab gets a visible marker so I can see at a glance which panes have a session running.
  4. When claude exits, the tab reverts to the repo name automatically.
  5. Works for my clauded alias too, the one that adds --dangerously-skip-permissions because I trust myself in a worktree (don't @ me).

The fix, in three parts

Part 1: Tell Ghostty to back off

~/.config/ghostty/config:

shell-integration-features = no-title

Enter fullscreen mode Exit fullscreen mode

One macOS gotcha worth flagging. Ghostty might have generated a config at ~/Library/Application Support/com.mitchellh.ghostty/config instead of the XDG path. The XDG path wins if both exist, but cmd+, in the GUI opens whichever Ghostty thinks is canonical. Easiest fix: keep the file in ~/.config/ghostty/config so it lives in your dotfiles repo, and symlink the Application Support path to it.

Part 2: Tell Claude Code to back off

~/.claude/settings.json:

{
  "env": {
    "CLAUDE_CODE_DISABLE_TERMINAL_TITLE": "1"
  }
}

Enter fullscreen mode Exit fullscreen mode

That env var stops Claude from emitting OSC 2 sequences for the rest of the session. It shipped in response to one of those open issues. Nothing in Claude's startup banner mentions it exists, which is most of why nobody uses it.

Part 3: Drive the title from zsh

~/.zshrc:

# --- Ghostty tab title ---
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;👾 %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
# --- end ---

Enter fullscreen mode Exit fullscreen mode

_ghostty_context returns the git repo name if you're in one, falling back to the directory basename. _ghostty_set_title emits an OSC 2 escape with that name. _ghostty_set_title_for_cmd checks the command line you just hit enter on, and if it matches a claude invocation, prefixes the repo name with a 👾. Three hooks, one per zsh lifecycle event.

How it works

The hooks each fire at a different moment in zsh's prompt loop.

Hook Fires when What this gives you
chpwd Every directory change Tab updates the moment you cd into a different repo, no command needed
precmd Before every prompt redraw Safety net: when claude exits and a new prompt is about to draw, the title resets to the repo name
preexec After enter, before the command runs Intercepts the moment you launch claude and writes the marker title before zsh hands off to the child process

The reason it feels automatic is that the hooks line up with the three things you actually do. cd into a repo and chpwd paints the repo name. Launch claude and preexec swaps in 👾 reponame. When the session ends, precmd runs before the next prompt draws and the repo name comes back. You never type anything to manage any of it.

One thing worth knowing: zsh's preexec sees the command line after alias expansion. If clauded is a plain alias for claude --dangerously-skip-permissions, the existing claude * pattern in the case statement already catches it. If you've defined clauded as a function or a script, alias expansion doesn't apply and you need the explicit clauded* pattern, which is why I included both. type clauded tells you which case you're in.

Edge cases

A few honest limitations.

Surfaces, not tabs. Ghostty's OSC 2 sets the surface (split) title, and the tab label follows whichever split has focus. So if a tab has three splits and only one is running claude, the 👾 only shows when that split is focused. Worth flagging because no zsh hook can fix it. Ghostty discussion #7581 tracks the request for persistent tab-level titles.

Other AI tools. The case statement is one line away from covering everything else:

case "$cmd" in
  claude|claude\ *|clauded|clauded\ *) printf '\033]2;👾 %s\007' "$ctx" ;;
  gemini|gemini\ *)                    printf '\033]2;✨ %s\007' "$ctx" ;;
  cursor-agent|cursor-agent\ *)        printf '\033]2;🖱️ %s\007' "$ctx" ;;
esac

Enter fullscreen mode Exit fullscreen mode

Suspend and resume. Ctrl-Z triggers precmd, so the title goes back to the repo name even though the agent is still alive in the background. fg doesn't match the claude pattern, so the title stays as the repo name through the resumed session. Acceptable to me, but worth knowing.

What I'd build next

Two things that would make this better, both blocked on Ghostty.

The first is tab color via OSC sequences. iTerm2 has supported OSC 6 ; 1 ; bg ; ... for years. Ghostty doesn't yet (open issue #12235). The day it lands, the same case statement could drive tab color too: red for claude --dangerously-skip-permissions, blue for read-only mode. That's a useful safety signal you can read at a glance, faster than reading text.

The second is the persistent tab-level title mentioned above. With that, a tab could stand for a project even when its splits are running mixed-purpose commands. Right now you can pick "repo name" or "agent marker," but only one at a time per surface. That's the limitation a lot of multi-pane workflows are bumping into.

Closing

Three configs. About 30 lines of zsh. Maybe 20 minutes to write. The friction it removes shows up dozens of times a day, and it's the kind of saved-time you don't notice until you sit down at a machine that doesn't have it. Broken signals in a tool you stare at for eight hours a day add up. They just don't show up on any dashboard.

If you've got a different cut of this (better hook patterns, support for more AI agents, tab color tricks), I'd take a look. The snippet's in a gist if you want to copy it without retyping.

If you're interested in the broader Claude Code surface area I keep finding things in, the hooks post is the obvious next read. The visibility problem with Claude Code sessions is what got me building Claudoscope in the first place. Broken tab titles are a tiny version of the same theme.


Originally published at liranbaba.dev