Repository navigation
Releases
v1.2.2 - locale-safe marker stripping
Fixes a marker bug that only appears on someone else's machine.
The bug
strip_mark used a ${#pre} -le 8 length guard. ${#var} counts characters under a UTF-8 locale but bytes under C/POSIX, and hooks routinely inherit an unset locale. Any TABTINT_MARK over 8 bytes (every ZWJ emoji, and ●●●) then failed to strip, so the watcher's re-assert prepended a fresh marker on every poll.
Measured: a tab title growing from 32 bytes to 264 over eight polls, unbounded until you focused the tab.
The default ● is 3 bytes and was never affected. This only bit people who set TABTINT_MARK.
The fix
Test the shape of the prefix instead of its length. A marker glyph never contains an ASCII letter, digit or space; a real title fragment essentially always does. That is locale-independent, stays glyph-agnostic, and also fixes the original truncation bug in v1.2.1, which the length guard never did.
| Version | Behaviour |
|---|---|
| v1.2.1 | truncated any title containing a WORD JOINER followed by a space |
| v1.2.1 + main | unbounded title growth under a C or unset locale |
| v1.2.2 | green across every locale and mark tested |
Also
test/strip-mark.sh, 10 cases across 3 locales and 2 marks. Fails on both prior implementations.- README now documents that the zero-turn
,projectcommand only fires at an idle input box. Typed mid-turn, the message reaches the model directly and never hitsUserPromptSubmit.
Full changelog: v1.2.1...v1.2.2
v1.2.1 — marker hardening
Three defects in the v1.2.0 marker, found by adversarial review of the shipped code.
- A tab whose title read back empty had its name replaced by a lone marker, and it could never be removed.
mark_clearcorrectly refused to write an empty title but dropped its flag anyway, so no cleanup path could see the glyph, includingtabtint sync. A shared-window tab with no usable title now falls back to the (invisible) wash instead: better than a destroyed tab name. rest_windowdeleted the unread marker even when the strip had failed, killing the retry that failure path exists to enable. Now conditional, so the watcher stays alive and tries again.tab_ctxwas measured at 730ms per call against 19 windows because it counted tabs for every window before testing for a match. The lazy form does the same work in ~381ms.
Correction to the v1.2.0 notes: those said one-tab windows "pay no extra round trip". That was wrong. tab_ctx replaces focused_tty, which could bail on "not frontmost" without touching a window at all, so the Stop hook went from two Apple Events to one but the remaining one is more expensive. One event instead of two, not free.
v1.2.0 — works with multiple tabs in one window
Fixes a silent no-op reported on r/SideProject.
The bug. Terminal draws only the selected tab's body and gives the tab bar no colour of its own. So for anyone running several tabs in one window, painting a background tab was invisible exactly when it mattered, and selecting that tab cleared it before they saw it. The feature did nothing for them and never said so.
The fix. A tab that shares a window now gets a dot in front of its tab title instead of the wash:
● api <- finished, you have not looked
web
docs <- selected
The dot is stripped the moment you select the tab. It is located with a zero-width U+2060 sentinel rather than a stored copy of your title, so it is stripped rather than restored and can never overwrite something you set with /rename. Glyph is configurable with TABTINT_MARK.
One-tab windows are unchanged and still get the colour wash, and the Stop hook folds focus detection into the same Apple Event so they pay no extra round trip.
If you do not see the dot, enable Terminal > Settings > Profiles > Window > Title > "Custom title". AppleScript cannot read that checkbox, so the tool cannot warn you automatically.
v1.1.0 — installable as a Claude Code plugin
Install is now two lines inside Claude Code, with no install script and no settings.json edit:
/plugin marketplace add dotcomjack/claude-session-tint
/plugin install claude-session-tint@dotcomjack
- Hooks (SessionStart, UserPromptSubmit, Stop) and the
/tabtintcommand register themselves - A starter palette ships with the plugin, so a fresh install works with no setup
- Your palette stays at
~/.claude/tabtint-palette.conf, outside the plugin directory, so it survives updates - Scripts now resolve siblings relative to their own location instead of a fixed path, so the same files work as a plugin (where the root is versioned and moves on every update) and as a manual install
- The manual
install.shpath still works and is documented under a details block in the README
v1.0.0
Know which Claude Code session needs you.
Tag a terminal window with a project. It wears that color quietly while it works, brightens when a response lands while you're looking elsewhere, and drops back the moment you focus it.
Includes the zero-turn command pattern: typing ,project in the Claude Code input runs a command with no model turn, no tokens, and no assistant message. That part works on every terminal and every OS, and prompt-hook.sh is 100 lines if you just want to steal the technique.
Window coloring is macOS Terminal.app only, because Terminal.app is the one terminal with no native tab colors.
./install.shmerges into settings.json rather than replacing it, backs it up, and validates the JSON before writing./install.sh --uninstallrestores every window it touched- MIT