Specification: signalbox jumplist

Open with J (change this in settings).
Note: the mockup below is interactive and operates as the living specification for the signalbox jumplist.
Hub health (spec control, not part of the jumplist):
signalbox Jump

SESSIONS

type to search · ⌃j/⌃k move · ⌃1-9 direct · tab next unread · ⌃p pin · ⌃r rename · ⌃x hide · ⌃⌫ remove · ↩ jump / F feedback · esc clear/close
Rename:

What this is

The jumplist shows every agent session in one place and jumps you to the machine session you pick: the right terminal window and tmux pane, or the captured editor window. URL-only rows such as CI runs stay on the board as information, with no jump action. It is a floating panel that takes keys while your app keeps focus, and dismisses on esc, the shortcut, or a click elsewhere. Drag any background area to reposition it, like Alfred or Raycast. It remembers its position across opens and recenters if that spot is offscreen, for example after a display is unplugged.

Hub status light

The hub status light sits at the left of the footer, opposite the cog, where you already look while using the jumplist. Everyday hub status belongs here, not hidden inside Settings. Its exact states are Hub healthy with a green dot, Hub warning with an amber dot, and Hub starting with a grey dot. It never names a mode because "Remote" reads as a place, not as health.

Click the light for a popover with Mode, Address, Hub version, and, only in Remote mode, Uplink. Address reads not answering in amber when the hub is down. Uplink reads connected, down, N events spooled in amber, or starting. The values are monospace and selectable. Open Settings closes the jumplist and opens the Hub tab; Open log closes it and opens the Logs tab. A click elsewhere, or esc, closes the popover. There are no menu bar items for the light, these facts, or its links.

Status marks

One mark, on the trailing edge beside the age: whether this row wants you.

What the agent is doing is not a mark. A working session shows a cycling spinner glyph, the word Working..., and then the prompt it is working on, all on its subtext line - which is where messaging apps put activity, and none of them keeps a glyph column for it. Idle rows therefore say nothing at all on either side: the default posture stays silent, and only a row that wants you draws the eye. Elapsed time is the age column's job, so the subtext does not repeat it.

The prompt is italic, and that is load-bearing rather than decorative. The subtext line changes speaker: while working it quotes you, and once the turn is done it quotes the agent. Rendered identically the two are indistinguishable without reading them, so your own words quoted back are italicised, as they would be in prose. Amber was rejected for the working state because amber already means "needs your input", and one colour cannot mean two things on the same board.

A bold title still means unread, and the amber dot alone still means the session is asking for you.

Subtext

The line under each name follows the one-line preview rule from agent-markdown.md: take the first paragraph, keep inline styles, and collapse whitespace to one line. While a session works you see your prompt (what it is working on); once it finishes, asks, or fails you see its reply (what came back). Identical rule to iOS.

Tags

Tags are discreet lowercase pills after the title: a filing aid, never a status. They come from signalbox session tag or fire --tag; #tag matches them and !tag excludes them in Additional filters. components/scripts/demo.sh tags every seeded session demo, so !demo hides a demo board.

Icons

The icon on each row shows the originator, taken from the event's agent field (see the data model). Each surface maps the agent name to its glyph. Known agents today, with the fallback for everything else:

IconAgent
claude - Claude Code
opencode - OpenCode
codex - Codex
πpi - pi
cursor - Cursor's own agent (the isometric-cube mark alone)
cursor/<agent> - an agent hosted in Cursor's terminal (e.g. cursor/claude): the Cursor mark with the sub-agent's glyph badged bottom-right in a clear halo
vscode/<agent> - an agent hosted in VS Code's terminal (e.g. vscode/claude): the VS Code ribbon mark with the sub-agent's glyph badged bottom-right in a clear halo
github - GitHub Actions
anything else (script, custom agents) - a plain ring, the fallback

Search and selection

The top of the panel is a search bar and it is always focused: any printable key filters the list live, matching the name, the prompt breadcrumb, the reply, and the agent. A query starting with # switches to tag mode - #demo shows only sessions carrying that tag (case-insensitive, exact match); clearing it restores all. Every open starts with an empty search. Any additional filters apply underneath the search, to this list and the menu bar dropdown alike.

Rows keep the order you work in (most recently engaged first, like the app switcher), with pinned sessions first; status does not reorder them. The panel opens with the topmost unread row selected, and selection stays topmost-unread within the filtered rows.

Hidden sessions

Hiding a session with ⌃X drops it from the list until it speaks again. To bring one back before then, the jumplist mirrors the mobile app: a collapsed Hidden (N) divider sits at the bottom of the list, below every visible row. It is always shown once the board has a session, Hidden (0) included, so it is always clear whether a session is set aside - a missing row is otherwise a mystery. Hidden (0) is inert (no chevron, nothing to expand). With a count, select it and press (or click it) to expand: the hidden rows appear beneath it, dimmed and marked with an eye-slash. ⌃X on a hidden row unhides it - the same key that hid it, reversed (it fires the show event) - and it rejoins the list above. Press on the divider again to collapse. A live search reaches into the hidden rows too, revealing the section when a match is tucked inside it, so a hidden session is never lost - only set aside.

Pinning and the row menu

Pin a session to keep it at the top: pinned sessions sort before the rest (engagement order within each group) and carry a pin mark on the row and in the menu bar dropdown. Order is a hub decision: the app applies the same rule locally - a pin toggle moves the row into or out of the pinned group immediately, and an engagement bump never lifts an unpinned row above a pinned one - so the list always matches what the hub's order would show. Pin or unpin the selected row with ⌃P; pins survive agent activity and hub restarts.

Right-click any row for the same actions as a menu: Pin/Unpin, Rename, Hide (or Unhide on a revealed hidden row), and Remove.

Asks on the board

When an agent blocks on you, the row shows the actual ask, not a generic "needs your permission": a pending tool approval reads as the command it wants to run (Bash: git push origin main - the codex-mcp-server and signalbox-hub rows below), and an agent question reads as the question with its options inline (the dwmkerr-toolkit row). Both are ordinary attention events whose reply carries the ask - the amber mark and preview pane render them with no special casing. The wire vocabulary (reason: permission_request / question, and the no-clobber rule that keeps the rich ask when a bare duplicate notification arrives) is specified in events.md; how each agent's adapter captures the ask (Claude Code's PermissionRequest and matcher-scoped PreToolUse[AskUserQuestion] hooks, Codex's native permission requests) is specified in adapters.md. The board never answers for you: asks are read-only signal, and the answer happens where the agent runs - jump.

Preview

The right pane shows the last three exchanges on black like the terminal it quotes, oldest first, with older turns dimmed. The pane scrolls and opens tailed to the newest exchange. The agent's markdown is rendered in terminal style - bold, italic, inline code, lists, headings, fenced blocks and tables all stay mono. The pane's text is selectable for copying, and a rendered link opens in the browser when clicked. A cropped exchange ends in . The newest exchange alone keeps the live working line and bullet temperature: amber for asks, blue for finished work, red for failure and dim for read.

The action row above the footer is shown on every machine row. For a tmux or cursor origin it says exactly where takes you, for example "Jump to iTerm (tmux dwmkerr:1) on localhost". An originless machine row initially says "Jump to session". Pressing replaces that text with "Jump not supported for this session yet (plain terminal). Press F to send feedback." While that notice is shown, F opens https://github.com/dwmkerr/signalbox/issues/67. URL-only rows remain information-only: their action row is hidden and does nothing.

Shortcuts

Typing is searching, so list commands use the control key:

KeysAction
Jump to the selected machine session; a successful jump marks it seen. On an originless machine row, show "Jump not supported for this session yet (plain terminal). Press F to send feedback." URL-only rows do nothing.
FWhile the unsupported-jump notice is shown, open issue 67
⌃J ⌃K or arrowsDown / up
⌃1 ⌃2Jump directly to a session
Next unread
⌃PPin / unpin the selected session (pinned sessions sort first)
⌃RRename session (your own name for it; empty clears)
⌃XHide until the session speaks again; on a row in the expanded Hidden section, unhide it
⌃⌫Remove now
⌘,Open Settings (same as the footer cog)
Hub status lightOpen the hub facts popover; Open Settings targets Hub and Open log targets Logs
⌘+ ⌘− ⌘0Zoom the jumplist in / out / reset (0.8×-1.6×, persists)
escClear the search, then close

Zoom scales the whole panel, geometry and text together, and text stays crisp for dense screens or recordings. The footer keys strip pairs F feedback with jump; ⌘+/−/0 remain omitted because they are universal conventions and the strip is already full.