My Claude Code Sessions Label Themselves
August 29, 2026
Eight Panes That Look Identical
I keep six or eight Claude Code sessions open in one iTerm2 window. They all look the same. A dark rectangle with text scrolling in it.
Finding the pane I want means reading scrollback until something looks familiar, and I was doing that a dozen times a day. Claude Code has /rename, and I never remembered to use it.
Each Session Names Itself
The agent in a pane is the only thing that reliably knows what it’s working on. So it labels the pane itself, with a color, a tinted tab, and an emoji in the session name.
🔨 orange is code being written. 🐞 pink is a bug hunt. 📝 blue is prose. 🔴 red is a live meeting being captured. Now I glance at the window and know what my whole fleet is doing.
One Command and a Hook
The agent runs one command: cc-tag write claude code color rename blog post.
Nine classes live in a table in a note in my Obsidian vault. The script reads that table, finds the class, and does three things to the pane it’s running in. It types /color blue, tints the iTerm2 tab, and types /rename 📝 claude code color rename blog post.
A hook reminds the agent to run it when a session starts, and again if fifteen minutes go by and the work has changed shape. I never type it myself.
Appendix: How It’s Built
Notes for anyone building the same thing, human or agent.
The taxonomy is a markdown table
| Key | Emoji | Color | Use when |
|---|---|---|---|
| live | 🔴 | red | A meeting or live event is being captured right now |
| bug | 🐞 | pink | Chasing a defect, production or local |
| build | 🔨 | orange | Writing a feature; code is being produced |
| dig | 🔍 | cyan | Research or investigation with no artifact yet |
| write | 📝 | blue | Drafting prose: docs, posts, emails, notes |
| people | 👥 | purple | People work: 1:1s, feedback, relationships |
| plan | 📅 | yellow | Planning, triage, scheduling, deciding what to do |
| sweep | 🧹 | default | A routine automated sweep is grinding: inbox, backlog, loops |
| harness | 🤖 | green | Changing the agent, its tooling, or this taxonomy itself |
The script parses that table out of the vault note at runtime. Editing the note is editing the config, so there’s no copy inside the script to drift out of sync with the doc that describes it.
The emoji matters more than the color. Color is the fastest thing to read across a whole window, and it’s the first thing to disappear everywhere else. The /resume picker lists sessions by name. A transcript pasted into an issue is text. A host that isn’t running iTerm2 has no tab to tint. The emoji rides along in the name through all of it.

That’s this post being written, seen from my phone. No color, no tab, just the name and the emoji.
There’s no API, so it types
/color and /rename are slash commands you type into Claude Code. iTerm2’s AppleScript interface has a write text command that sends a string to a session as if it came from the keyboard, and every pane exports its own UUID in $ITERM_SESSION_ID, so the script addresses the exact pane it’s running in and nothing else.
Two traps are worth knowing about:
- Get the tty from iTerm2, not from process ancestry. The tab tint is an OSC 6 escape sequence, which means writing bytes to the pane’s terminal device. Walking up the process tree finds the wrong one: a shell wrapper puts the login shell on a second pty underneath the pane, so ancestry lands a layer too deep and the tab never changes. The tty has to come from iTerm2’s own session object, the same place the UUID matched.
- Refuse to type over a draft. If I’m halfway through composing a message and a script appends
/color blueto it and hits return, it just sent my half-written prompt. So the script reads the pane’s visible text first, looks at whatever follows the last❯, and injects nothing if it isn’t blank. Earlier❯lines are echoes of commands that already ran, so only the last one counts.
Report only what you can prove
The three surfaces aren’t equally checkable, and a cheerful success line for all three would be a lie about two of them.
The rename is provable. Claude Code writes session state to JSON under ~/.claude/sessions/, so the script polls that file until the name it asked for shows up. The /color is confirmable only as far as receipt: the command lands in ~/.claude/history.jsonl with the session id and a timestamp, which proves it arrived, not that the chrome changed. The tab tint can’t be checked at all, because iTerm2 won’t read a tab color back.
So the output says which is which:
cc-tag: /color blue accepted
cc-tag: tab tinted 70,120,220 (📝 write) — only a human can confirm iTerm2 honored the tab
cc-tag: renamed to "📝 claude code color rename blog post" (verified in session state)
Two hooks, because there’s no timer
SessionStart fires on startup, resume, clear, and compact, and injects a short instruction telling the session to classify itself once the work is clear. The /clear case is the one that earns its keep, since clearing context is usually the exact moment a pane changes jobs.
UserPromptSubmit is the stand-in for a timer, which Claude Code doesn’t have. It fires on every prompt, which is far too often to nudge on, so the hook keeps a stamp file per session id and stays silent unless the last hint was fifteen or more minutes old. A pane that started as research and turned into a build gets re-labeled without me saying anything.
Both hooks emit nothing when $ITERM_SESSION_ID is unset, so headless and scheduled runs never see any of it.
Let counts change the taxonomy, not vibes
A taxonomy whose colors drift is worse than no taxonomy. Blue has to mean prose every single time or I stop reading the colors.
So the classes are fixed, and the agent is told to always pick the closest one even when the fit is bad. When the fit is bad, it says so: cc-tag dig scansnap driver hunt --misfit "hardware config, not research". Every labeling appends to a TSV outside git, and cc-tag --review prints the distribution, recent subjects per class, and every logged misfit.
The table changes only when three or more logged entries argue for the same change. A day in, that’s eight labelings across five classes and no misfits, which isn’t enough to conclude anything yet.
– John