Skip to content

✨ feat(status-bar): add Claude Code session status aggregator component#648

Draft
kud wants to merge 9 commits into
gnachman:masterfrom
kud:feat/agent-status-bar
Draft

✨ feat(status-bar): add Claude Code session status aggregator component#648
kud wants to merge 9 commits into
gnachman:masterfrom
kud:feat/agent-status-bar

Conversation

@kud
Copy link
Copy Markdown
Contributor

@kud kud commented Apr 9, 2026

Ticket

Description

Adds a new status bar component, iTermStatusBarClaudeCodeComponent, that aggregates Claude Code agent statuses across all open iTerm2 windows and surfaces them as a compact label in the status bar (e.g. "2 waiting, 1 working", "1 idle", "No sessions").

The component reads session statuses that were written by Claude Code triggers — specifically the three sentinel values "Waiting", "Working…", and "Idle" — via the existing SessionStatusController observable dictionary. It subscribes to changes and updates the label reactively.

Interaction model:

  • Single click with exactly one active session → navigates directly to that session (same behaviour as the Jobs component).
  • Single click with multiple sessions → opens a popover (ClaudeCodeStatusPopoverViewController) listing each session name, a coloured status dot derived from the trigger's configured colour, and the status text. Selecting a row navigates to that session and closes the popover.
  • Popover rows are sorted: Waiting → Working → Idle.

Implementation notes:

  • ClaudeCodeSummaryBuilder is a pure-logic enum holding the three sentinel strings and the buildSummary(from:) / isClaudeCodeStatus(_:) helpers, deliberately separated from the heavyweight status-bar component hierarchy so the logic can be unit-tested without a running app.
  • 21 unit tests in ModernTests/ClaudeCodeSummaryBuilderTests.swift cover empty sessions, single/multiple counts, all three statuses, mixed combinations, and the sentinel filter.
  • The component is registered in iTermStatusBarSetupViewController.m so it appears in the "Add Status Bar Component" sheet.
  • The popover uses auto layout (acceptable here — this is a standalone popover window, not the terminal window or toolbelt).
  • SF Symbol brain is used as the component icon (available on macOS 12+, matching the deployment target).
  • A TODO comment in the popover notes a planned Allow/Deny button row, deferred until a safe mechanism exists for targeting the correct Claude TUI session.

Screencast

No visual changes to existing UI — this is an additive component. To see it in action, add "Claude Code" from the status bar component picker, then start a session whose status bar is being driven by Claude Code triggers.

How to Validate

  1. Build a debug build: make run.
  2. Open Preferences → Profiles → Session → Status Bar and add the "Claude Code" component (brain icon).
  3. Set up a Claude Code trigger that writes "Waiting", "Working…", or "Idle" to the session status, or manually fire SessionStatusController updates from the debugger.
  4. Confirm the label updates reactively and shows the correct counts.
  5. With one active session, click the label — verify it navigates to that session.
  6. With two or more active sessions, click the label — verify the popover opens, rows are sorted Waiting → Working → Idle, and clicking a row navigates and closes the popover.
  7. Run unit tests: tools/run_tests.expect ModernTests/ClaudeCodeSummaryBuilderTests.

Developer Checklist

  • Code is readable and maintainable
  • Unit tests included and passing (21 tests in ModernTests/ClaudeCodeSummaryBuilderTests.swift)
  • PR is atomic and focused on a single feature
  • Commits follow Conventional Commits
  • No auto layout used in the terminal window or toolbelt
  • Uses it_fatalError instead of fatalError
  • No compiler warnings introduced
  • New files added to Xcode project via tools/add_file_to_xcodeproj.rb
  • Deployment target is macOS 12 — no availability guards needed for the APIs used

kud added 9 commits April 9, 2026 22:37
Add iTermStatusBarClaudeCodeComponent that displays aggregated session statuses
(Waiting/Working/Idle) across all windows with a clickable popover showing
session details. Includes ClaudeCodeStatusPopoverViewController for navigation,
ClaudeCodeSummaryBuilder for testable logic, and 21 unit tests.
- Widen status column to 96pt and increase pill right margin to 16pt to prevent clipping
- Disable selection highlight (selectionHighlightStyle = .none) to avoid blue row flash
- Replace resetCursorRects with NSTrackingArea + cursorUpdate for consistent pointing hand cursor across entire table view
Register new Swift files iTermStatusBarClaudeCodeComponent.swift and ClaudeCodeStatusPopoverViewController.swift in Xcode project and status bar setup, integrating them into build phases and UI registration. This change updates iTerm2.xcodeproj/project.pbxproj and sources/iTermStatusBarSetupViewController.m to support new components.
@kud
Copy link
Copy Markdown
Contributor Author

kud commented Apr 9, 2026

Progress update

✅ Done

  • Status bar component (iTermStatusBarClaudeCodeComponent) — registered, shows summary label ("1 waiting", "2 waiting, 1 working", "No sessions") with brain SF symbol icon
  • Session popover — single-click opens a popover listing each Claude session with its name and a coloured status pill (teal = Waiting, orange = Working, green = Idle)
  • Hover effect — subtle rounded highlight on row hover via custom SessionRowView
  • Pointing hand cursor — only active over actual session rows (not empty space), via NSTrackingArea + cursorUpdate
  • Navigation — clicking a row in the popover navigates to that session via revealSession(withGUID:) and closes the popover; double-click navigates directly when one session is active
  • No popup on empty state — clicking "No sessions" does nothing
  • Icon spacing — SF symbol constrained to 11pt to leave breathing room before the text
  • Unit tests — 21 tests covering ClaudeCodeSummaryBuilder (status classification, pluralisation, edge cases)
  • Xcode project — both files added via add_file_to_xcodeproj.rb

🔲 Not yet done / known gaps

  • Allow / Deny buttons in popover — responding to Claude's permission prompts still requires switching to the tab manually. Implementing this requires detecting which prompt type is currently shown and injecting the correct keystrokes into Claude's TUI without ambiguity. Deferred to a follow-up.
  • Stale status after Claude exits — when claude quits within a tab, its last status ("Idle", "Waiting") lingers in SessionStatusController until the tab itself closes. The trigger's job: "claude" gate prevents the shell's prompt from writing new statuses, but the old one persists. Needs a mechanism to detect process exit and clear the status (e.g. subscribing to GlobalJobMonitor.didChangeNotification).
  • Onboarding not updated — the "Install Claude Code Integration" wizard still only mentions the Toolbelt Session Status tool; it doesn't mention the new status bar component.
  • Session filtering by provenance — currently filters by status text values ("Waiting", "Working…", "Idle"). A more robust approach would propagate the trigger's provenance: "claude-v1" field through to iTermSessionTabStatus so non-Claude sessions that happen to use the same status strings are excluded.

@gnachman
Copy link
Copy Markdown
Owner

Thanks for taking this on — a status bar version of the Session Status tool is a good idea and I'd like to see it happen. However, this PR needs a different approach. Let me explain why and how to get there.

The core issue

The Session Status system in iTerm2 is generic. Any program can set status text, indicator dots, and colors on any session via OSC 21337 or triggers. The toolbelt's "Session Status" tool (ToolStatus.swift) shows all of these, regardless of what set them. Your PR hardcodes three specific strings ("Waiting", "Working…", "Idle") and builds everything around Claude Code specifically. That's too narrow — this component should work for any status, not just Claude Code's.

What to study before starting over

Read these files carefully and understand how they fit together:

  1. ToolStatus.swift — This is the toolbelt version of what you're building. Your status bar component should show the same data in a compact form. Pay attention to:

    • How it uses SessionStatusController to get all active statuses (no filtering by specific strings)
    • How it scopes to the current window via toolbeltWindowContainsSession
    • How it sorts by StatusPrioritySettings (user-configurable), then age, then session ID
    • How it does incremental table updates (insert/remove/move) instead of full reloads
    • How it shows dot indicators, status text with custom colors, session names, and keyboard shortcuts
  2. ToolStatusCellView.swift — The cell layout: dot, name, shortcut, status text, detail. Your popover rows should show similar information.

  3. iTermSessionTabStatus.swift and SessionStatusController — The data model. Note that hasActiveStatus is the right filter, not checking against hardcoded strings.

  4. StatusPrioritySettings.swift — The user-configurable priority system. Use this for sorting instead of a hardcoded sort order.

  5. iTermStatusBarTriggersComponent.swift — A good example of a status bar text component to model yours after.

Suggested approach

  1. Start by understanding the full data flow: OSC 21337 → VT100TabStatusUpdateiTermSessionTabStatusSessionStatusControllerToolStatus. Your component taps into the same pipeline at SessionStatusController.

  2. The status bar label should summarize all active statuses in the current window — maybe a count, or the highest-priority status text. Don't hardcode specific strings.

  3. The popover (or menu) should list sessions the same way the toolbelt tool does: session name, dot, status text, shortcut. Clicking navigates to the session.

  4. Use StatusPrioritySettings for sort order so it matches the toolbelt tool's behavior and respects user configuration.

  5. Scope to the current window, not all windows.

  6. Name it "Session Status" to match the toolbelt tool.

General advice

When contributing to an unfamiliar codebase, spend time reading the existing code before writing new code. The patterns are already there — your job is to extend them, not reinvent them. The toolbelt tool is your reference implementation; your status bar component should be a compact version of it, not a parallel system.

Give it another shot with this framing and I think it'll come together well.

@kud
Copy link
Copy Markdown
Contributor Author

kud commented Apr 12, 2026

@gnachman Okay, I see! I based this PR on the new Claude Integration actually. Maybe it was too Claude-centric, and you're right—we can make it more generic. I will have another round then. Thanks for the explanation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants