Infrastructure
VS Code Session Namer
The problem this solves
Section titled “The problem this solves”I run Claude Code in parallel. A normal day has ten to twenty sessions open at once, each in its own VS Code window — one fixing a CRM bug, one drafting a blog post, one debugging a Chrome extension, one renaming NAS photos. VS Code truncates its tab titles aggressively, and the first twenty characters of an aiTitle rarely distinguish two sessions doing adjacent work. When a voice nudge fires “Hey James, over on the overlay session,” I need to find the right window in under a second — not hover-and-read across the dock.
The cost of that friction, measured over a day, is focus. Switching between sessions only pays off when the switching tax is lower than the parallelism gain. Until this repo existed, it was not.
What it is
Section titled “What it is”A floating-label overlay system for VS Code, pinned to each window that’s running a Claude Code session. Each label:
- Shows a short, human-readable session name (auto-proposed, editable)
- Sits just past the tab’s close-X at the top of the window, above the native tab strip
- Re-positions live as the window moves, resizes, or the Explorer sidebar toggles
- Hides when another app covers the window, reappears when the window comes forward
- Stays visible during screenshot capture so you can actually show someone what you see
- Carries a stable per-session color and emoji so the same session reads the same hue every time
The auto-namer watches the aiTitle VS Code writes to ~/.claude/nudges/{sid}.name, proposes a crisp 2-5 word name via Haiku, detects when a session has drifted off-topic mid-conversation, and stores the current name in session-names.json.
How it works
Section titled “How it works”Four moving parts:
1. The label — overlay/session-label.py
Section titled “1. The label — overlay/session-label.py”A Python script that opens a borderless, floating NSPanel anchored to a specific VS Code window found by title prefix match. It polls CGWindowListCopyWindowInfo at 30 Hz to follow the window across moves and resizes, runs an occlusion check against every other real app window to know when to hide, and uses the macOS Accessibility API plus pixel sampling to detect whether the primary sidebar (Explorer) is currently open so the label lands after the last tab rather than on top of it.
One process per live VS Code window. The process lives for 24 hours or until killed.
2. The launcher — overlay/launch-all-labels.sh
Section titled “2. The launcher — overlay/launch-all-labels.sh”Idempotent: kills every existing session-label.py process, reads session-names.json, spawns one label per entry. I run this after any code change to re-flow all labels. The SessionStart hook runs it on every new session.
3. The namer — session-namer/propose.py, teach.py, drift-check.py
Section titled “3. The namer — session-namer/propose.py, teach.py, drift-check.py”Proposes a name for a session by reading the last few minutes of transcript and asking claude -p for a 2-4 word summary (no paid API calls — the Max plan covers this). Drift-check runs on UserPromptSubmit and compares the current name against what the session is actually doing; if they have diverged past a threshold, it updates the name, announces the drift to the user, and respawns the label. The teach.py helper lets me correct a name manually and append a rule to naming-rules.md so the namer learns from the correction.
4. The auto-binding — overlay/auto-label.py
Section titled “4. The auto-binding — overlay/auto-label.py”One-shot per-session driver. On SessionStart, this polls for the aiTitle file to appear, calls propose.py, writes to session-names.json, and spawns the label. By the time the first prompt comes back, the label is already pinned.
Why this is not trivial
Section titled “Why this is not trivial”Five non-obvious failure modes had to be solved before this felt right:
- Window tracking under move/resize.
CGWindowBoundsare point-accurate but the window list changes order; you have to re-find the target every tick by title match, not cache a window ID. - Occlusion without flicker. If you naively check “is my rect covered by another window” you get false positives from system pseudo-windows (Dock, Window Server, Wispr Flow, Control Center). The allowlist of system owners to ignore took incident-driven growth.
- Screenshot safety. The macOS Screenshot utility registers a full-screen overlay while active. Without explicit handling, every label disappears the moment you try to show someone what the system looks like — defeating its own purpose. The fix is extending the same system-owner allowlist.
- Sidebar detection. VS Code is Electron. Its AX tree is flat — the entire content area reports as one 1440-wide
AXScrollArea. So the label can’t ask the accessibility layer “where does the Explorer end?” I fell back to pixel sampling a horizontal strip of the window and looking for the color transition between sidebar and editor. That works in light themes; in dark themes where both regions share#191a1b, it still needs work (current status: env-var override, auto-detect returns 0 in dark mode, on the followup list). - Name drift. A session that starts as “Fix overlay disappearing” but ends up scaffolding a whole new repo needs to be called “VS Code session namer” by the end — without me typing the rename. The drift check catches this on every user prompt and respawns silently.
Each of those problems is a line in the commit log. The clean-looking system is the accretion.
The “three hours on a Tuesday afternoon” case
Section titled “The “three hours on a Tuesday afternoon” case”Worth writing down because the value is not obvious if you are reading this from outside.
The naive view: I spent three hours on overlay labels while real work waited. The actual view:
- I run parallel Claude Code sessions because serial work does not scale to my task volume. Fifteen sessions in flight is common.
- With the labels, I can spot the right window from across the screen in under a second. Without them, it took five to ten seconds of hover-read-hover per switch.
- At even 30 switches a day — conservative — that is 4 minutes of straight switching time, plus the much larger cost of the focus tax: every time I have to think about finding the window, I have dropped the context of whatever I was about to do there.
- Over a month, this pays for the afternoon many times over. Over a year, it is the difference between “parallel Claude Code is something I do” and “parallel Claude Code is a core productivity multiplier.”
The principle: the frictions that look tiny from outside are the ones that compound. Fixing them once is worth more than the isolated minutes suggest.
Where the code lives
Section titled “Where the code lives”- Repo:
~/apps/vscode-session-namer - GitHub:
https://github.com/ojhurst/vscode-session-namer(private) - Path references from
cc/hooks/(session-start, session-label-refresh, drift-check) call into this repo by absolute path
How to update this page
Section titled “How to update this page”If the detection strategy changes (pixel sampling, AX API, extension-reported state, anything else), update the “How it works” section. If a new non-obvious failure mode gets solved, add it to “Why this is not trivial” — the whole point of that section is to preserve the lessons.
Related
Section titled “Related”- Naming a New Repo
- Browser Overlays — the sibling overlay system for Chrome
- Voice Nudges — the other half of the “find the right session” story