|
| 1 | +--- |
| 2 | +title: "Taking Control of Terminal Titles" |
| 3 | +date: 2025-08-24 21:55:00 +1000 |
| 4 | +tags: iTerm2 terminal |
| 5 | +header: |
| 6 | + image: /assets/images/2025-08-24/terminal.jpg |
| 7 | + image_description: "" |
| 8 | + teaser: /assets/images/2025-08-24/terminal.jpg |
| 9 | + overlay_image: /assets/images/2025-08-24/terminal.jpg |
| 10 | + overlay_filter: 0.5 |
| 11 | + caption: > |
| 12 | + Photo by [Sean Wang](https://unsplash.com/@maooooozaizi) |
| 13 | + on [Unsplash](https://unsplash.com/photos/an-escalator-in-a-large-building-with-lots-of-windows-DZSbsJHadH0) |
| 14 | +excerpt: One simple command to rule them all |
| 15 | +--- |
| 16 | + |
| 17 | +## The Background |
| 18 | + |
| 19 | +As a developer, I use the terminal extensively and often SSH into remote machines |
| 20 | +for management tasks. When I connect via SSH, iTerm2 automatically updates both |
| 21 | +the window and tab titles to something like `user@machine-ip:directory-name`, |
| 22 | +which provides helpful context about where I'm working. The problem? These titles |
| 23 | +don't automatically revert when I disconnect, leaving me with stale information |
| 24 | +in my terminal tabs. |
| 25 | + |
| 26 | +For years, I had no idea what was happening under the hood, let alone how to fix |
| 27 | +it. My workaround was crude but effective: close the tab and open a new one. |
| 28 | +Eventually, I got fed up and decided to take some actions. After some research, |
| 29 | +I added this mysterious snippet to my `.zshrc`: |
| 30 | + |
| 31 | +```bash |
| 32 | +precmd() { |
| 33 | + # sets the tab title to current dir |
| 34 | + echo -ne "\e]1;${PWD##*/}\a" |
| 35 | +} |
| 36 | +``` |
| 37 | + |
| 38 | +I'll be honest—I didn't fully understand how it worked, but it did the job. This |
| 39 | +function "forced" my tab title to always display the current directory name, |
| 40 | +even after disconnecting from SSH sessions. |
| 41 | + |
| 42 | +Later, I discovered [Prezto][], a configuration framework for Zsh that includes |
| 43 | +a handy `set-tab-title` function. This gave me more flexible control over tab |
| 44 | +titles, so I happily retired my hacky `precmd` solution. |
| 45 | + |
| 46 | +You might have noticed that the window title remained untouched throughout this |
| 47 | +journey. It simply wasn't annoying enough to warrant investigation—until Claude |
| 48 | +Code came along. |
| 49 | + |
| 50 | +Since its public launch in February, Claude Code has become my go-to tool for |
| 51 | +AI-assisted coding. Like SSH, it helpfully sets custom tab and window titles to |
| 52 | +show what it's working on. Also like SSH, these titles stick around after Claude |
| 53 | +Code exits, cluttering my terminal with outdated information like "Building |
| 54 | +Feature X..." or "Optimising test performance..." long after those tasks |
| 55 | +completed. |
| 56 | + |
| 57 | +Since I was using Claude Code across multiple projects every day, these stale |
| 58 | +titles were starting to get on my nerves and that was the final straw. It was |
| 59 | +time to properly understand how terminal titles work and build a comprehensive |
| 60 | +solution. |
| 61 | + |
| 62 | +## Understanding Terminal Titles in iTerm2 |
| 63 | + |
| 64 | +Before diving into solutions, let's understand what we're dealing with. iTerm2 |
| 65 | +(and most terminal emulators) recognize two distinct title types: |
| 66 | + |
| 67 | +1. **Tab Title** - Displayed on the individual tab |
| 68 | +2. **Window Title** - Shown in the center of the window's title bar |
| 69 | + |
| 70 | +These titles are controlled through ANSI escape sequences - special character |
| 71 | +combinations that terminals interpret as commands rather than text to display. |
| 72 | + |
| 73 | +### The Magic Behind Terminal Titles |
| 74 | + |
| 75 | +Here's how escape sequences work for setting titles: |
| 76 | + |
| 77 | +```bash |
| 78 | +# Set window title only (shows in title bar) |
| 79 | +echo -ne "\033]2;Your Window Title\007" |
| 80 | + |
| 81 | +# Set tab title only (shows in tab) |
| 82 | +echo -ne "\033]1;Your Tab Title\007" |
| 83 | + |
| 84 | +# Set both window and tab to the same title |
| 85 | +echo -ne "\033]0;Your Title\007" |
| 86 | +``` |
| 87 | + |
| 88 | +The cryptic `\033]` starts the escape sequence, the number (0, 1, or 2) |
| 89 | +specifies which title to set, and `\007` (the bell character) ends it. |
| 90 | + |
| 91 | +Quick note on escape characters: You might see these written as `\e` (instead of |
| 92 | +`\033`) and `\a` (instead of `\007`) in some scripts—they're equivalent, just |
| 93 | +different representations of the same ASCII characters. I'm using the octal |
| 94 | +format (`\033` and `\007`) here for maximum compatibility across different |
| 95 | +shells and systems. |
| 96 | + |
| 97 | +Simple, right? Well, not exactly intuitive and quite cumbersome to use, which is |
| 98 | +why we're building a better interface. |
| 99 | + |
| 100 | +## The Solution: A Simple Title Manager |
| 101 | + |
| 102 | +Let's create a user-friendly function that makes title management a breeze. Add |
| 103 | +this to your `~/.zshrc` (or `~/.bashrc` for Bash users): |
| 104 | + |
| 105 | +```bash |
| 106 | +# Terminal title management functions |
| 107 | +terminal_titles() { |
| 108 | + case "$1" in |
| 109 | + window) |
| 110 | + echo -ne "\033]2;$2\007" |
| 111 | + ;; |
| 112 | + tab) |
| 113 | + echo -ne "\033]1;$2\007" |
| 114 | + ;; |
| 115 | + both) |
| 116 | + echo -ne "\033]0;$2\007" |
| 117 | + ;; |
| 118 | + help) |
| 119 | + echo "Usage: terminal_titles [window|tab|both|reset|help] [title]" |
| 120 | + echo " window [title] - Set window title only" |
| 121 | + echo " tab [title] - Set tab title only" |
| 122 | + echo " both [title] - Set both window and tab to same title" |
| 123 | + echo " reset - Reset to default titles" |
| 124 | + echo " help - Show this help message" |
| 125 | + echo " (no args) - Same as reset" |
| 126 | + ;; |
| 127 | + reset|"") |
| 128 | + # Window title: full path with ~ for home |
| 129 | + echo -ne "\033]2;${PWD/#$HOME/~}\007" |
| 130 | + # Tab title: last portion of the full path |
| 131 | + echo -ne "\033]1;${PWD##*/}\007" |
| 132 | + ;; |
| 133 | + *) |
| 134 | + echo "Unknown command: $1" |
| 135 | + echo "Use 'terminal_titles help' for usage" |
| 136 | + ;; |
| 137 | + esac |
| 138 | +} |
| 139 | + |
| 140 | +# Alias for convenience |
| 141 | +alias tt='terminal_titles' |
| 142 | +``` |
| 143 | + |
| 144 | +### How to Use It |
| 145 | + |
| 146 | +Now you have a simple `tt` command at your disposal: |
| 147 | + |
| 148 | +```bash |
| 149 | +# Reset titles to show current directory info |
| 150 | +tt |
| 151 | + |
| 152 | +# Set a custom window title |
| 153 | +tt window "Development Server" |
| 154 | + |
| 155 | +# Set a custom tab title |
| 156 | +tt tab "logs" |
| 157 | + |
| 158 | +# Set both to the same title |
| 159 | +tt both "Project X" |
| 160 | + |
| 161 | +# Get help |
| 162 | +tt help |
| 163 | +``` |
| 164 | + |
| 165 | +The default behavior (`tt` with no arguments) resets your titles to something |
| 166 | +useful: |
| 167 | + |
| 168 | +- **Window title**: Full path of current directory (with `~` for home) |
| 169 | +- **Tab title**: Just the current folder name |
| 170 | + |
| 171 | +For example, if you're in `/Users/you/projects/awesome-app`, the window shows |
| 172 | +`~/projects/awesome-app` and the tab shows `awesome-app`. |
| 173 | + |
| 174 | +## Conclusion |
| 175 | + |
| 176 | +After years of closing and reopening tabs to clear stale SSH titles, and more |
| 177 | +recently dealing with Claude Code's persistent status messages, I finally have a |
| 178 | +solution that just works. The `tt` command is now muscle memory—quick to type, |
| 179 | +easy to remember, and does exactly what I need. |
| 180 | + |
| 181 | +The beauty of this solution is its simplicity. No complex configurations, no |
| 182 | +heavyweight terminal managers—just a few lines of shell script that give you |
| 183 | +complete control. Whether you're jumping between SSH sessions, running Claude |
| 184 | +Code throughout the day, or just want cleaner tab organization, you now have the |
| 185 | +tools to keep your terminal titles under control. |
| 186 | + |
| 187 | +Happy terminal customizing! 🚀 |
| 188 | + |
| 189 | +--- |
| 190 | + |
| 191 | +> **Note**: After building this solution, I discovered that Prezto actually has |
| 192 | +> a `set-window-title` command hiding alongside the `set-tab-title` function I'd |
| 193 | +> been using for years. Had I found it earlier, this entire journey into the |
| 194 | +> depths of ANSI escape sequences might never have happened. But you know what? |
| 195 | +> I'm okay with missing it. Sometimes the scenic route teaches you more than the |
| 196 | +> shortcut ever could, and now I truly understand what's happening under the |
| 197 | +> hood rather than just blindly using a command. |
| 198 | +
|
| 199 | +## Appendix: Auto-Reset with Smart Persistence |
| 200 | + |
| 201 | +If you want titles to automatically reset after commands like Claude Code exit, |
| 202 | +but keep your manually-set titles, here's an enhanced version with intelligent |
| 203 | +auto-reset. This adds more complexity but offers finer control: |
| 204 | + |
| 205 | +```bash |
| 206 | +# Terminal title management functions with smart auto-reset |
| 207 | +terminal_titles() { |
| 208 | + case "$1" in |
| 209 | + window) |
| 210 | + echo -ne "\033]2;$2\007" |
| 211 | + export TERMINAL_TITLE_MANUAL=1 |
| 212 | + ;; |
| 213 | + tab) |
| 214 | + echo -ne "\033]1;$2\007" |
| 215 | + export TERMINAL_TITLE_MANUAL=1 |
| 216 | + ;; |
| 217 | + both) |
| 218 | + echo -ne "\033]0;$2\007" |
| 219 | + export TERMINAL_TITLE_MANUAL=1 |
| 220 | + ;; |
| 221 | + help) |
| 222 | + echo "Usage: terminal_titles [window|tab|both|reset|auto|help] [title]" |
| 223 | + echo " window [title] - Set window title only" |
| 224 | + echo " tab [title] - Set tab title only" |
| 225 | + echo " both [title] - Set both window and tab to same title" |
| 226 | + echo " reset - Reset to default titles" |
| 227 | + echo " auto - Enable auto-reset after commands" |
| 228 | + echo " help - Show this help message" |
| 229 | + echo " (no args) - Same as reset" |
| 230 | + ;; |
| 231 | + auto) |
| 232 | + unset TERMINAL_TITLE_MANUAL |
| 233 | + terminal_titles reset |
| 234 | + echo "Auto-reset enabled" |
| 235 | + ;; |
| 236 | + reset|"") |
| 237 | + # Window title: full path with ~ for home |
| 238 | + echo -ne "\033]2;${PWD/#$HOME/~}\007" |
| 239 | + # Tab title: last portion of the full path |
| 240 | + echo -ne "\033]1;${PWD##*/}\007" |
| 241 | + unset TERMINAL_TITLE_MANUAL |
| 242 | + ;; |
| 243 | + *) |
| 244 | + echo "Unknown command: $1" |
| 245 | + echo "Use 'terminal_titles help' for usage" |
| 246 | + ;; |
| 247 | + esac |
| 248 | +} |
| 249 | + |
| 250 | +# Alias for convenience |
| 251 | +alias tt='terminal_titles' |
| 252 | + |
| 253 | +# Auto-reset after commands ONLY if not manually set (for zsh) |
| 254 | +if [[ -n "$ZSH_VERSION" ]]; then |
| 255 | + precmd() { |
| 256 | + # Only reset if titles weren't manually set |
| 257 | + if [[ -z "$TERMINAL_TITLE_MANUAL" ]]; then |
| 258 | + # Window title: full path with ~ for home |
| 259 | + echo -ne "\033]2;${PWD/#$HOME/~}\007" |
| 260 | + # Tab title: last portion of the full path |
| 261 | + echo -ne "\033]1;${PWD##*/}\007" |
| 262 | + fi |
| 263 | + } |
| 264 | +fi |
| 265 | +``` |
| 266 | + |
| 267 | +This enhanced version adds: |
| 268 | + |
| 269 | +- **Smart persistence**: Manually-set titles survive command execution |
| 270 | +- **Auto-reset mode**: Titles automatically update after commands (when not |
| 271 | + manually set) |
| 272 | +- **Flexible control**: Switch between manual and automatic modes with `tt auto` |
| 273 | + |
| 274 | +### The Smart Workflow |
| 275 | + |
| 276 | +1. **Normal operation**: Titles auto-update to show your current directory |
| 277 | +2. **Set custom title**: `tt tab "server logs"` - This persists across commands |
| 278 | +3. **Return to auto-mode**: `tt auto` or `tt reset` |
| 279 | + |
| 280 | +[Prezto]: https://github.com/sorin-ionescu/prezto |
0 commit comments