Skip to content

ctx.terminals

Open and manage terminal tabs through TerminalService. The terminal is a core feature — a built-in DockKind like the editor — so this mirrors ctx.editors: create opens a terminal in a workspace, and closeWorkspace reaps a workspace's terminals. The tab renders from the workspace's terminal records; the PTY session lives on ctx.process.

ts
ctx.terminals: TerminalService

Example

tsx
// open a shell terminal in the active workspace, rooted at a folder
ctx.terminals.create({ cwd: "/path/to/project" });

// open a specific kind in a specific workspace
ctx.terminals.create({ kind: "claude", workspaceId });

Methods

On ctx.terminals. Method names link to the full signature.

MethodWhat it does
create(input?)Open a new terminal in a workspace (defaults to the active one). Returns its record.
closeWorkspace(id)Close and kill every terminal in a workspace (e.g. on workspace delete).
focus(terminalId)Switch to the workspace containing this terminal and activate its tab in the center dock. No-ops for unknown ids.

Tab adornments

Leading icons and trailing Phosphor indicators on terminal tabs. Prefer the adorn verbs (setIndicator / bindIndicator / …) — full guide: Tab adornments.

registerTabDecoration remains as a deprecated shim over bindIndicator.

ts
ctx.subscriptions.push(
  ctx.terminals.bindActivity({
    id: "my-ext.tab",
    provide(terminalId) {
      if (!isBusy(terminalId)) return null;
      return { activity: "working", tooltip: "Working" };
    },
  }),
);

ctx.terminals.invalidateTabAdornments();
MethodWhat it does
setActivity / clearActivity / flashActivity / bindActivityHost-owned Activity (ADR 0030)
setIndicator / clearIndicator / flashIndicator / bindIndicatorTrailing static Phosphor indicators
setIcon / clearIcon / bindIconLeading ReactNode icons
invalidateTabAdornmentsRe-query binders
registerTabDecoration (deprecated)Shim → bindIndicator

OSC events

Subscribe to raw OSC (Operating System Command) escape sequences emitted by a terminal's PTY. Unlike the title that appears on the tab, this fires from the raw output stream regardless of whether the terminal's panel is mounted — making it reliable for background workspace monitoring.

Common OSC codes:

CodeMeaning
0Set window/tab title
7Working directory (file://…)
9iTerm2 notification (attention, progress)
133Shell prompt marker (semantic shell)
ts
const BRAILLE_START = 0x2800;
const BRAILLE_END = 0x28ff;
const IDLE_CHAR = "\u2733"; // ✳ — Claude Code idle/waiting signal

ctx.subscriptions.push(
  ctx.terminals.subscribeOsc(terminalId, ({ code, payload }) => {
    if (code !== 0) return;
    const first = payload.charCodeAt(0);
    if (first >= BRAILLE_START && first <= BRAILLE_END) {
      setStatus(terminalId, "busy"); // agent is running
    } else if (payload.startsWith(IDLE_CHAR)) {
      setStatus(terminalId, "idle"); // agent is waiting for input
    }
  }),
);
MethodWhat it does
subscribeOsc(terminalId, handler)Subscribe to parsed OSC sequences from a terminal's PTY stream. Returns a Disposable.

Each event is an OscEvent.

Raw output

Subscribe to the raw PTY output stream of a terminal. The handler receives every chunk of bytes the PTY produces — ANSI escape sequences, OSC sequences, plain text — exactly as they arrive, before any parsing. This fires regardless of whether the terminal's panel is visible, making it suitable for background activity monitoring (for example, confirming an agent is still producing output between OSC signals).

Keep handlers lightweight: they execute synchronously on every PTY chunk, which can be multiple times per second while a program is running.

ts
// Track the last time any output arrived to confirm agent activity.
let lastOutputAt = 0;
ctx.subscriptions.push(
  ctx.terminals.subscribeOutput(terminalId, () => {
    lastOutputAt = Date.now();
  }),
);
MethodWhat it does
subscribeOutput(terminalId, handler)Subscribe to raw PTY output chunks from a terminal. Returns a Disposable.

Active terminal

Track which terminal tab the user is looking at. "Active" is the center dock's single active panel of the active workspace — null when an editor tab (or nothing) is active, and transiently during workspace switches before the incoming workspace's active tab is published. A terminal that is merely visible in a non-active split does not count.

ts
// clear a "needs attention" marker once the user views the terminal
ctx.subscriptions.push(
  ctx.terminals.subscribeActive((terminalId) => {
    if (terminalId) attention.delete(terminalId);
  }),
);
MethodWhat it does
getActive()The record id of the active center-dock terminal tab, or null.
subscribeActive(listener)Subscribe to active-terminal changes (tab activation, group activation, workspace switch). Returns a Disposable.

Types

Pass TerminalService.

Related: CreateTerminalInput · TerminalRecord · TerminalKind · TerminalTabDecoration · TerminalTabDecorationProvider · OscEvent.

See also

Persistent sessions live on ctx.process. Other State members on ctx.