← Back to Notes

TermX Title Sync Redesign

How pane titles flow from Claude Code to TermX — and how to make it better

Key Concepts

📡

TTY (Pseudo-Terminal)

The pipe connecting your shell to TermX. Each pane gets one (e.g. /dev/ttys003). Everything you see flows through it — text, colors, and control sequences.

✉️

OSC Escape Sequence

Special bytes mixed into the terminal stream that say "this is a command, not text." Example: \e]0;Title\a sets the window title. iTerm2 added custom ones under \e]1337;….

🔗

IPC (Inter-Process Communication)

Any way for two programs to talk. File-based IPC: one writes a file, the other reads it. Other options: sockets, pipes, or sending data through the TTY itself.

👁️

FSEvents / kqueue

macOS APIs to watch for file changes. Instead of reading a file every 700ms to check ("polling"), the OS tells you when it changes. Efficient, but still file-based.

Current Architecture (the glitchy one)

Claude Code hook script runs writes file ~/.cache/termx/titles/ ttys003 line 1: title | line 2: #color polls every 700ms TermX reads + renders title ⚠ Polling waste Reads file every 700ms even when idle ⚠ Stale files Files persist after sessions end ⚠ Race conditions TTY not ready during early init ⚠ Fragile icon stripping Hardcoded unicode ranges break easily ⚠ No hook wired up Write side doesn't exist yet ⚠ Color collisions Naive hash, 10 colors, many clashes

Design Options

Option A: Keep files, replace polling with FSEvents

Minimal change — keep the file IPC but use macOS file system notifications instead of polling.

Claude Code hook writes file writes file ~/.cache/termx/titles/ same file format FSEvents notify TermX reads only on change
Pros
  • Small code change — just swap poll loop for FSEvents watcher
  • No polling waste
  • File format stays the same
Cons
  • Still file-based — stale files, cleanup needed
  • Still need TTY name to know which file to write
  • Can't easily pass rich metadata
  • Two systems to debug (files + watcher)

Option B: OSC Escape Sequences (through the TTY)

Data flows through the same channel as everything else — the terminal stream. No files, no polling.

TTY stream (/dev/ttys003) — text, colors, and control sequences all flow here Claude Code Hook printf '\e]1337;…\a' TermX VT Parser already parses OSC 1337 The data rides the same stream as normal terminal output — no separate channel needed

How it would work

# Claude Code hook emits an escape sequence: printf '\e]1337;SetTermXTitle=name=%s;color=%s\a' "my-project" "#7eb8ff" # TermX's VT100 parser already handles \e]1337;… # We just add a new handler for "SetTermXTitle" # → instant title update, no files, no polling
Pros
  • Zero polling — instant delivery through the PTY
  • No files to manage, no stale state
  • Session lifecycle handled automatically (TTY dies = done)
  • Extensible — can add any metadata to the sequence
  • TermX already parses OSC 1337 — small code addition
Cons
  • Sequence lost if emitted before TermX is ready (rare)
  • Need persistence for arrangement restore (save to session state)

Option C: iTerm2 Python API (WebSocket)

Use iTerm2's built-in Python API — a long-running script communicates over WebSocket.

Claude Code triggers Python script IPC Python Script long-running daemon WebSocket TermX Python API server
Pros
  • Most powerful — full session control
  • Can set titles, colors, profiles, badges, etc.
  • Bidirectional communication
Cons
  • Requires a running Python daemon
  • Heavy dependency for just title/color sync
  • Python API may not survive TermX's divergence from iTerm2
  • Extra process to manage and debug

Side-by-Side Comparison

A: FSEvents B: OSC Sequences C: Python API
Transport File + OS notification TTY stream (already exists) WebSocket
Latency ~10-100ms <1ms (next read cycle) ~5-20ms
Stale state? Yes — files persist No — dies with TTY No
Code change size Small Medium Large
Extensibility Limited (file format) Key-value pairs in sequence Full API
External deps None None Python runtime
Arrangement restore Read cached file Save to session state on receive API can re-query

Recommendation: Option B — OSC Sequences

The most natural fit for a terminal emulator. Data flows through the same channel as everything else. No files to manage, no polling to tune, no daemons to run. TermX already parses OSC 1337 — we just add a new parameter.

Implementation Plan

1. TermX side — Add handler in VT100 parser for SetTermXTitle key in OSC 1337. Parse name=, color=, project=, status= params. Store in PTYSession properties. Save to session arrangement for restore.

2. Hook script — Claude Code hook that emits the OSC sequence with session name + color. Runs on session start and status changes.

3. Remove old system — Delete file polling code, cache directory references, and icon stripping logic.

4. Color upgrade — Better hash (djb2/fnv) over larger palette, or let the hook pick the color directly.