OSC 133 - Shell Integration
This documentation describes the OSC 133 sequence used for shell integration, inspired by FinalTerm.
Installing Contour's shell integration
Contour ships a script for bash, zsh, fish, tcsh and PowerShell. Write it out once with
contour generate integration shell <SHELL> to <FILE>, then load it from your shell's start-up file:
| Shell | Write the script | Load it |
|---|---|---|
| bash | contour generate integration shell bash to ~/.config/contour/shell-integration.bash |
at the end of ~/.bashrc: source ~/.config/contour/shell-integration.bash |
| zsh | contour generate integration shell zsh to ~/.config/contour/shell-integration.zsh |
in ~/.zshrc: source ~/.config/contour/shell-integration.zsh |
| fish | contour generate integration shell fish to ~/.config/fish/conf.d/contour.fish |
nothing to do: fish reads conf.d/ by itself |
| tcsh | contour generate integration shell tcsh to ~/.config/contour/shell-integration.tcsh |
in ~/.tcshrc, after setting prompt: source ~/.config/contour/shell-integration.tcsh |
| PowerShell | contour generate integration shell pwsh to "$(Split-Path $PROFILE)/contour-integration.ps1" |
in your $PROFILE, after anything that sets up the prompt: . "$(Split-Path $PROFILE)/contour-integration.ps1" |
Every script marks the start of the prompt (A), its end (B), the start of the command's output (C)
and the end of the command with its exit status (D), and reports the working directory with OSC 7. What
differs is the command line:
| Shell | Command line |
|---|---|
| bash, zsh, fish, PowerShell | sent with C as cmdline_url |
| tcsh | not sent, as no tcsh hook sees it: Contour reads it off the screen instead (see C) |
- bash: the script carries its own copy of bash-preexec,
which installs itself by appending to
PROMPT_COMMAND. A laterPROMPT_COMMAND=...throws that away, and then no marks are sent at all, without an error: that is why the script belongs at the end of~/.bashrc, after anything that setsPROMPT_COMMAND. bash hands over a command line only once its history has recorded it, so a command kept out of the history (HISTIGNORE,HISTCONTROL,set +o history) is read off the screen instead. - tcsh: the script extends
promptonce, so it is sourced afterpromptis set. - PowerShell: the script is for PowerShell 7 and needs PSReadLine, which comes with it. Like VS Code's
integration, it wraps
PSConsoleHostReadLine, so the command line is reported when PSReadLine hands it back — after the newline, before the command runs — rather than from a key handler. Without PSReadLine there is noC, and so noDeither.
Shells that speak OSC 133 themselves
- fish 4.0 and later mark their prompts natively:
A,CandDfrom fish 4.0.0, the command line (cmdline_urlonC) from 4.0.1, andBfrom 4.3.0;set -Ua fish_features no-mark-prompt(fish 4.0.6 and later) turns fish's own marks off. Contour's fish script detects which marks your fish sends and adds only those it leaves out, so no mark is sent twice. - Nushell 0.111 and later integrate natively and need no script: make sure
$env.config.shell_integration.osc133istrue(the default);contour generate integration shell nusays so too. Nushell never sends the command line (cmdline_url), so Contour reads it off the screen (seeC). Nushell marks every continuation line of a multi-line command with133;A;k=sand a133;Bafter its indicator; Contour reads anAwithk=sas part of the prompt in progress, not as a new command block (seeA). Older Nushell already marks its prompts and commands with plainA,B,CandD, but leaves the prompt indicator and the continuation lines unmarked.
Windows
PowerShell's marks reach Contour through the Windows console layer (ConPTY) like any other output: the
ConPTY that Contour ships passes them through unchanged, so the PowerShell integration works on Windows as
it does elsewhere. cmd.exe has no prompt hook to integrate with; there, Contour behaves as it does without
shell integration.
What Contour builds on these marks
Folding, the gutter's block column, scrollbar marks, the sticky command header, finish notifications, copying a command's output and prompt-to-prompt navigation all read these marks — see Command blocks. A shell that sends none of them loses nothing it had: Contour then behaves as a terminal without shell integration always has.
Sequence Specification
Format: OSC 133 ; <Command> [; <Parameters>...] ST
Where:
OSCis\033].ST(String Terminator) is\033\(or BEL\007).<Command>is a single character identifier (A, N, B, I, P, C, D, L).
Commands
A - Prompt Start
Sent before the shell prompt starts printing.
Format: OSC 133 ; A [; <Key>=<Value>...] ST
Parameters:
click_events=1: Optional. Accepted for compatibility and currently ignored: Contour does not yet move the cursor on a click inside the prompt.redraw=0: Optional, as kitty reads it. The shell does not redraw this prompt when the window is resized, so Contour re-wraps it like any other text (see below). Each prompt says it for itself; any other value, or none, means the shell redraws it.k=<Kind>: Optional.i, or none, is the prompt itself.candsstart a further line of the prompt in progress — a continuation prompt such as bash'sPS2; Nushell sendsk=sbefore each — andra right-hand prompt. AnA(orN) of one of those three kinds starts no new prompt and no command block: it is read asPwith the same kind. A kind Contour does not know counts asi.
Behavior:
- Marks the current line as a prompt line (conceptually similar to setting a "mark").
- Notifies the terminal that the prompt is beginning. A command still running has ended.
- Does no fresh-line (see
L), though the semantic-prompts proposal asks for one: kitty, foot and Konsole do none either, and a line break the shell did not print would change what it drew.
Resizing at the prompt: a shell redraws its prompt when the window's width changes, moving up as many rows as it drew the prompt and its input on. Re-wrapped under it as well, the prompt would span other rows than the shell counts, so Contour leaves the live prompt as the shell drew it, as kitty does and the semantic-prompts proposal recommends:
- The live prompt is the rows from the line the last
Amarked down to the bottom of the page, while the cursor stands in it and noChas followed. - Each of those rows is cut at the new width or padded to it and stays a row of its own. The cursor keeps its
row; the rows above re-wrap as ever. The columns
B,DandP;k=rmarked move with the cells they name, so the input is found where it stands until the shell's redraw marks it afresh. - A prompt sent with
redraw=0is re-wrapped like any other text, and so is one that began above the page, or that shares its line with output that did not end in a newline.
N - New Command
Format: OSC 133 ; N [; <Key>=<Value>...] ST
Behavior: As A. The proposal's "may first end the command before" is what A does already.
B - Prompt End
Sent after the shell prompt has finished printing and before user input begins.
Format: OSC 133 ; B ST
Behavior:
- Notifies the terminal that the prompt has ended.
- Marks the logical line the prompt ended on, together with the column the cursor stood at — the border between what the shell painted and what the user is about to type.
This border cannot be recovered after the fact: once the user starts typing, the prompt and the input are one run of cells. It is what lets Contour tell an accessibility client where the prompt area is (see Accessibility), and it is recorded whether or not DEC mode 2034 is enabled — the line marks are terminal memory, not part of the semantic-block reader protocol.
The column is counted across the whole logical line, so a window resize that re-wraps the line into different physical rows does not move it.
Contour's bundled integrations send ;B at the very end of the prompt. The border is also what lets Contour
read a command line that was never reported off the screen (see C).
I - Prompt End, Input to the End of the Line
Format: OSC 133 ; I ST
Behavior: As B, but the user's input ends with the line it begins on: a command line Contour reads off
the screen (see C) is that line's text alone. Where both B and I are sent on one line, the later counts.
P - Prompt Part Start
Sent where a further part of the prompt in progress starts, such as a right-aligned prompt.
Format: OSC 133 ; P [; k=<Kind>] ST
Parameters:
k=r: a right prompt, drawn beside the input (Nushell sends it). Other kinds (i,c,s) are accepted and change nothing.
Behavior:
- Starts no new prompt and no command block.
- With
k=r, Contour remembers where on its line the right prompt begins, so that the command line it reads off the screen when the shell does not report one (seeC) leaves the right prompt out. Should the command later be drawn over the right prompt, as zsh does once the command reaches it, the mark no longer counts. On a lineBmarked and noP;k=rdid, a run of text that ends at the right margin, or one column short of it, and stands at least two blanks clear of the command is taken for a right prompt.
C - Command Output Start
Sent after the user has finished typing the command (usually on Enter press) and before the command output begins.
Format: OSC 133 ; C [; <Key>=<Value>...] ST
Parameters:
cmdline_url=<EncodedURL>: Optional. The command line being executed, percent-encoded (kitty's convention). Contour's bash, zsh, fish and PowerShell integrations send it. Its value runs to the end of the sequence, as kitty reads it: a;in it belongs to the command, encoded or not.cmdline=<Quoted>: Optional. The command line quoted for a shell, as kitty's bash and zsh integrations send it (printf %q), and read as kitty reads it: the first word of the quoted text, with backslash escapes, single and double quotes, and$'...'ANSI-C quoting. Its value runs to the end of the sequence: a;in it belongs to the command.
Behavior:
- Notifies the terminal that command execution is starting and output will follow.
- The
cmdline_urlandcmdlineparameters tell the terminal exactly what command is being run. The first of them in the sequence is the one read, and as its value runs to the end of the sequence, a parameter after it is part of the command line:OSC 133 ; C ; cmdline_url=;cmdline=ls STreports the command line;cmdline=ls. An empty one, or acmdline=whose quoting is broken, counts as none. - Contour records the command line with the command block: the gutter's block
tooltip, finish notifications and the sticky header of a command whose prompt has left the history show
it, and
CopyCommandLinecopies it. Without one — tcsh, Nushell, or a hand-written prompt — Contour reads it off the screen when the prompt's end was marked withB(orI): from the;Bcolumn to the start of the output, leaving a right prompt out. So;Bis worth sending even where the command line is not. - A command line is untrusted input: anything that writes to the terminal can send one. Contour keeps
at most 4 KiB of it, shows it with control and bidirectional-override characters replaced by a
visible placeholder, and removes escape and other control characters (but tab and newline) from the
copy
CopyCommandLineputs on the clipboard.
D - Command Finished
Sent after the command has finished executing and before the next prompt starts.
Format: OSC 133 ; D [; <ExitCode>] ST
Parameters:
<ExitCode>: The integer exit code of the command (e.g.,0for success). A missing or non-numeric one reads as0.
Behavior:
- Notifies the terminal that the command has finished.
- Reports the exit code of the command.
- Contour records the exit status with the command block: it colors the block's marks in the gutter and
on the scrollbar, labels the sticky header, and decides whether a finish notification set to
outcome: failureis sent.
L - Fresh Line
Format: OSC 133 ; L ST
Behavior: Moves the cursor to the start of the next line, as \r\n does, unless it already stands at the
start of a line -- where a carriage return would leave it.
Example Flow
# Prompt Start
printf "\033]133;A\033\\"
# ... print prompt ...
printf "user@host:~$ "
# Prompt End
printf "\033]133;B\033\\"
# ... user types command (e.g., 'ls') ...
# Command Output Start
printf "\033]133;C\033\\"
# ... command output ...
ls
# Command Finished (exit code 0)
printf "\033]133;D;0\033\\"
Related Extensions
- SETMARK (
CSI > M): a Contour-only extension that marked the current line, equivalent toOSC 133 ; A. Deprecated in favour of OSC 133 and removed in 0.7.1 — useOSC 133 ; A.