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.
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=1stops 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:
- 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.
- 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.
- Select the multiplexer pane. A tmux pane lives inside some terminal window, so both steps are needed. CrewTower runs
switch-client,select-windowandselect-paneagainst 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. - Run the terminal's own strategy, one per terminal, in the table below.
- 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.
| Terminal | How the exact surface is found | Accessibility |
|---|---|---|
| Terminal | AppleScript selects the tab whose TTY matches, then the selection is read back and compared | Not needed |
| iTerm2 | Every split is a scriptable session with a TTY; the matching one is selected and verified | Not needed |
| WezTerm | wezterm cli list gives one record per pane; matched on TTY, then on directory, and activated by id | Not needed |
| kitty | kitten @ focus-window over kitty's remote-control socket, matched on the shell's pid then its directory | Not needed, but remote control must be on |
| Warp | The per-session focus URL Warp puts in the environment; no second route if it was not captured | Not needed |
| Ghostty | Native tabs expose no TTY map, so the tab is found by title, then by directory, and pressed | Needed |
| Alacritty | No tabs and no scripting: the OS window is raised, exact with one window, title-matched with several | Needed |
| tmux panes | The pane recorded at session start, selected on its own server inside whichever terminal hosts the client | Not 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.
| CrewTower | Vibe Island precise jump | |
|---|---|---|
| Terminals handled precisely | 7, plus tmux panes | 20+ claimed, window activation for the rest |
| tmux panes | Yes, on its own tmux server | Yes |
| Zellij panes | No | Yes, per its site |
| IDE terminals | Editor raised, no extension | Installs an extension for VS Code, Cursor, Windsurf and Antigravity |
| SSH remote agents | No dedicated handling | Jumps to the local tab holding the connection |
| Add your own terminal | Yes, a one-line URL rule | By request to the vendor |
| Says why a jump failed | Yes, five distinct reasons | Not stated |
| macOS | 15 or later | 14 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