Skip to content

URL Scheme ​

Otty registers the otty:// URL scheme, so any link — a bookmark, a menu-bar launcher, a script, or text printed into the terminal — can bring Otty forward, focus a specific window, tab, or pane, or hand it a command to type.

bash
open otty://pane/$OTTY_PANE_ID     # focus the pane this shell is running in
open "otty://pane/p_19f6a…?line=1204"  # …and scroll to one line of it
open otty://window/current         # bring Otty's active window forward
open otty://tab/t_19f6a…           # jump to a tab by id
open "otty://run/brew%20update"    # open a tab with `brew update` typed at the prompt

The links resolve from anywhere the system can open a URL — open otty://…, a browser, a note, a Raycast/Alfred action — and they are also clickable when printed inside a pane. On Linux the handler is registered by the desktop entry Otty installs, so xdg-open otty://… and a browser click both work; on Windows Otty claims the scheme on first launch.

Targets ​

A URL is otty://<kind>/<selector>. The selector after the kind is exactly the one otty --window / --tab / --pane accept.

URLSelector forms
otty://window/<sel>w_<id>, or current · last · a 1-based index · title:<pattern>
otty://tab/<sel>t_<id>, or a 1-based index
otty://pane/<sel>p_<id>, or a session id such as $OTTY_PANE_ID · add ?line=<n> for a line deeplink
otty://run/<command>one shell command, percent-encoded — see Run links

Get the ids from otty pane list (or otty tab list / otty window list). The $OTTY_PANE_ID environment variable is exported into every pane's shell, so a running command always knows its own pane.

A pane link can carry the row as well as the pane:

otty://pane/p_19e630f19fe_21?line=1204

Open one and Otty raises that window, switches to that tab, focuses that pane, and scrolls that line into view with a brief highlight. Without ?line= the link just focuses the pane and leaves the viewport where it was.

Where one comes from:

  • About This Line ▸ Copy Deeplink, from a right-click in the pane, the Shell menu, or the command palette — see Shell Menu.

  • otty pane link prints one, so a script or a coding agent can hand you a way back to what it is talking about:

    bash
    otty pane link                              # the focused pane
    otty pane link "$OTTY_PANE_ID" --line 1204  # one line of a specific pane
    otty pane link --last-command               # the pane's most recent command
  • otty pane focus 'otty://pane/…?line=…' follows one from anywhere, which is how a deeplink works on Windows and Linux without going through the system's URL handler. otty pane focus <pane> --line <n> does the same without a URL.

line is the live absolute scrollback line — rows above the viewport plus the row itself, counted from the top of the buffer as it stands now. That is the same coordinate a shell-integration command mark reports, which is what makes a link and a command comparable.

So a deeplink is a handle on a live session, not an archive reference:

  • It drifts once that pane's scrollback overflows (the default 10,000-line buffer renumbers everything) or a resize re-wraps the rows above it. Following a stale one lands near the content rather than on it.
  • It means nothing after the pane closes — an unknown pane is a no-op, and Otty just comes to the front.
  • A non-numeric or negative line is dropped rather than clamped, so the link behaves like a plain pane link instead of jumping to line 0.
  • On the alternate screen (vim, htop, less) there is no scrollback row to pin, so Copy Deeplink hands out a pane-only link instead of naming the wrong line.

Paste one into a chat message, a commit note, or a bug report when the thing you're talking about is on screen but 300 lines up.

otty://run/<command> opens a new tab and types the command at the prompt. It does not press Enter — you read the line and run it yourself, or clear it with ⌫. Nothing about the link can change that: there is no parameter that makes Otty submit a command for you.

bash
# Build one in a shell:
python3 -c "import sys,urllib.parse; print('otty://run/'+urllib.parse.quote(sys.argv[1],safe=''))" \
  'curl -fsSL https://example.com/install.sh | sh'
  • The command is the whole path, percent-encoded — in JavaScript that is 'otty://run/' + encodeURIComponent(command), and in most other languages the "encode every reserved character" variant of URL quoting. Encoding # matters most: unencoded, everything after it is dropped.
  • Send one line. Wrap a long command for display if you like, but join it back into a single line for the link — Otty accepts an embedded newline only when the shell supports bracketed paste, and refuses the link otherwise rather than risk running the first lines of a script.
  • A command carrying a carriage return, an escape character, or a text-direction override is refused outright: each of those can make the staged line behave or read differently than it looks.
  • ?where=window opens a new window instead of a tab.
  • Clicking a run link printed inside a pane always asks first, showing the decoded command. That confirmation is never remembered — terminal output is not a trusted source of links.
  • Buttons like "Run in Otty" on dotfiles.otty.sh are ordinary run links.

Notes ​

  • The navigation links (window / tab / pane, ?line= included) never prompt: they only move focus, they never run a command or change your files. run is the one that does something, and it stops at your own prompt first.
  • If Otty is not running, the link launches it first, then focuses the target once a window is available.
  • An unknown or stale selector is a no-op; Otty just comes to the front.

See also ​

Otty