Feature

Jump to the exact terminal tab your AI agent is running in

Twelve tabs across three windows and two Spaces, and one of them stopped ten minutes ago to ask whether it may run a migration. Finding it is not a search problem, it is a bookkeeping problem, and the terminal is not keeping the books. This is how CrewTower does: which window, which tab, which split pane, per terminal, and what it says when it cannot.

macOS 15 or later · 7 terminals and tmux panes · $9.99 once

Twelve tabs, one of them is asking a question

An agent CLI is quiet until it is not. It reads, edits, runs tests, then hits a command it will not run without you and stops. Nothing about that pause is visible from outside the tab it happened in.

With one session that is a non-problem. It starts costing you around the third, because the question changes from "what did it ask" to "where did it ask". A notification does not fix that half: a banner tells you a session is waiting and still leaves you to find it. What fixes it is something having written down where each session lives at the moment it started.

Why terminal titles are not enough

The obvious handle is the tab title, and agents do write one: Claude Code names the tab after the work and appends its session id. It is a useful key, and the one that fails most often, for four separate reasons.

  • It can be switched off by something that is not you. CLAUDE_CODE_DISABLE_TERMINAL_TITLE=1 stops Claude Code writing the title at all, and tools that stamp their own titles set it at install time without always putting it back. Every jump in a native-tab terminal then quietly degrades, with nothing to tell you why.
  • Titles truncate. A tab is narrow and the terminal cuts from the right, which is exactly where the session id is.
  • Titles move. Claude Code prefixes the title with a status glyph that changes as the run progresses, so matching on equality matches a string that was true a second ago.
  • Titles collide. Two sessions in one repository produce two tabs describing the same work in almost the same words.

So a title is a good first key and a bad only key. CrewTower treats it as the first of four.

CrewTower detects CLAUDE_CODE_DISABLE_TERMINAL_TITLE in your Claude Code settings and offers a button to clear it. It never removes it behind your back: something else may be relying on it, and the rule in this app is that existing configuration is not taken away silently.

How CrewTower resolves window, tab and pane

The work starts before you press anything. A hook process is a descendant of the agent, which is a descendant of the terminal, so it inherits that terminal's per-session environment. CrewTower's hook ships a small allow-list of those variables with every event: the iTerm2 session id, the WezTerm pane, kitty's window id and control socket, Warp's own focus URL, the tmux socket and pane. They are recorded at session start rather than hunted for later.

Pressing Jump then runs a fixed order, and the order is load-bearing:

  1. Find the application by walking the process ancestry from the process owning the session's TTY up to the first one macOS knows as an application - the terminal, or the editor for an integrated terminal. Needs no permission.
  2. Bring it to the front, and wait for it. Tab selection and window raising only affect the key window while the app is active, so activation goes first; CrewTower polls up to a second and a half, and reports a failure rather than pressing on.
  3. Select the multiplexer pane. A tmux pane lives inside some terminal window, so both steps are needed. CrewTower runs switch-client, select-window and select-pane against the recorded pane on the server that session belongs to - the socket is parsed out of $TMUX, because addressing the wrong one on a machine running several does nothing at all. This goes before the terminal step, since selecting a pane can move the client to another window.
  4. Run the terminal's own strategy, one per terminal, in the table below.
  5. Fall back through the general keys - tab title, then working directory, then window title.

Every step is verified rather than assumed. A tab button on macOS reports a press as successful even when the tab bar ignores it, so CrewTower re-reads the tab list and confirms the session's own tab is now selected; Terminal.app's selection is read back and compared against the session's TTY; a raised window is compared against the focused window. That is what lets a failed jump say something specific instead of leaving you on the wrong tab wondering whether the feature works.

The terminals, and what each one gives us

Precision is not uniform, because what a terminal exposes is not uniform.

TerminalHow the exact surface is foundAccessibility
TerminalAppleScript selects the tab whose TTY matches, then the selection is read back and comparedNot needed
iTerm2Every split is a scriptable session with a TTY; the matching one is selected and verifiedNot needed
WezTermwezterm cli list gives one record per pane; matched on TTY, then on directory, and activated by idNot needed
kittykitten @ focus-window over kitty's remote-control socket, matched on the shell's pid then its directoryNot needed, but remote control must be on
WarpThe per-session focus URL Warp puts in the environment; no second route if it was not capturedNot needed
GhosttyNative tabs expose no TTY map, so the tab is found by title, then by directory, and pressedNeeded
AlacrittyNo tabs and no scripting: the OS window is raised, exact with one window, title-matched with severalNeeded
tmux panesThe pane recorded at session start, selected on its own server inside whichever terminal hosts the clientNot needed

Two consequences worth stating plainly. Jump works without Accessibility in five of those seven terminals, because AppleScript, a CLI and a URL scheme are not that permission. And Zellij panes are not selected: the terminal comes forward and whichever pane you had is still showing.

A terminal that is not on the list can be taught in one line. A jump rule is a URL template with placeholders - myterm://focus?tty={tty} - filled from what the hook recorded, and your rule always beats the built-in strategy for that terminal. Sessions with no terminal at all are handled separately: Codex inside the ChatGPT app and Claude Code inside the Claude desktop app have a conversation rather than a tab, so Jump uses each app's own deep link to that thread.

What happens without Accessibility permission

Focusing another application's window is what that permission governs, so the paths that read and press another app's tabs need it. The five terminals marked "not needed" above do not, and neither does anything else in the app: approving, denying and answering from the notch need no permission at all, because they travel over the socket the agent's own hook is already blocking on.

The failure mode is the part that matters. Every Accessibility call declines quietly when you have not granted it, which would make a jump into Ghostty report "no tab matched" when the truth is it was never allowed to look. CrewTower checks the permission where it starts to matter and says which of five things happened: no terminal window is showing this session at all; the terminal is not running any more; it would not come to the front; it is in front but picking its tab needs Accessibility; or it is in front, we were allowed to look, and this session's tab was not found. The note lands on the card you pressed, keyed to that session so two failing sessions cannot explain each other.

One imprecision is deliberate. When two sessions report the same working directory and neither has a usable title, CrewTower keeps you on the tab you were already looking at rather than sliding you to an equally plausible other one. Landing somewhere confidently wrong is worse than landing nowhere.

Why there is no keystroke fallback

The obvious shortcut for a permission prompt is to type the answer into the terminal for you. CrewTower does not do this anywhere, and it is not an omission. Approving is not a jump at all: an agent's permission hook blocks on a local Unix socket while it waits, and CrewTower answers over that same socket, so the prompt resolves where it stands with no window switch and nothing typed. Jump is for the other case - when you want to be in the terminal, or when an agent offers no decision channel and there is nothing to answer over. Keystrokes were investigated and rejected on the evidence: TIOCSTI, the clean way to push a character into a terminal, is blocked by macOS and fails even against a pty this app owns; Ghostty exposes no input API; and synthesised key events go to whatever holds keyboard focus, so the fallback for "we could not find the right window" would be typing a y into whichever window you happened to be in. That is worse than making you switch.

Compared with Vibe Island's precise jump

Vibe Island ships the closest equivalent, and on breadth it is ahead. The rows we lose stay in.

 CrewTowerVibe Island precise jump
Terminals handled precisely7, plus tmux panes20+ claimed, window activation for the rest
tmux panesYes, on its own tmux serverYes
Zellij panesNoYes, per its site
IDE terminalsEditor raised, no extensionInstalls an extension for VS Code, Cursor, Windsurf and Antigravity
SSH remote agentsNo dedicated handlingJumps to the local tab holding the connection
Add your own terminalYes, a one-line URL ruleBy request to the vendor
Says why a jump failedYes, five distinct reasonsNot stated
macOS15 or later14 or later
Price, one Mac$9.99 early bird, was $14.99$15

If the number of terminals decides it, that row is not close and you should read it as written. Figures are from Vibe Island's own public site; there is a fuller side-by-side comparison of the two products.

Questions

How do I find which terminal Claude Code is running in?

By elimination: ps for the claude process, read its controlling TTY, then work out which tab owns that TTY - which most terminals will not tell you. CrewTower records the answer at session start instead, from the environment the terminal already handed the hook.

Which tmux pane is my agent in?

The pane id is in $TMUX_PANE inside that pane, which is little use from outside it. CrewTower's hook captures it at session start with the tmux socket from $TMUX, then selects that pane on that server after bringing the window hosting the client forward.

Does jumping need Accessibility permission?

Only where the tab is reachable no other way - Ghostty and Alacritty among the supported terminals, plus the general window-title fallback. Terminal, iTerm2, WezTerm, kitty, Warp and tmux panes go through AppleScript, a CLI or a URL scheme and work without it. Approving from the notch never needs it.

Why does my jump land on the wrong tab?

Almost always because the title key is missing and two sessions share a working directory. Check whether CLAUDE_CODE_DISABLE_TERMINAL_TITLE is set in your Claude Code settings; with titles on, the two are told apart again. If the key really is ambiguous, CrewTower leaves you where you were rather than guessing.

Does it work with the integrated terminal in VS Code or Cursor?

The editor is found the same way a terminal is, by walking up from the agent process, and brought to the front. Picking the exact window falls to the general title and directory matching rather than a dedicated extension, so it is less precise than a real terminal.

Can I add a terminal that is not supported?

Yes, if it can focus a session from a URL. Add a jump rule in Settings with a template such as myterm://focus?tty={tty}; placeholders are filled from what the hook recorded, and your rule takes priority over any built-in strategy.

Does anything get typed into my terminal?

No. There is no synthetic-keystroke path anywhere in the app. Approvals travel over the hook's own socket; Jump only changes which window is in front.

Land on the tab that is waiting

Every agent session in the MacBook notch, the blocked ones answerable from there, and the exact tab one press away when you would rather go. One price, one Mac, no subscription.

Get CrewTower for Mac

$9.99 once · macOS 15 or later · 30 days, money back