Appearance
$TERM and Identification
📸 TL;DR
DO NOT change it unless you understand what it is.
How Otty tells the programs running inside it what kind of terminal they're talking to — the $TERM value, the identification environment variables, and the escape-sequence replies that TUIs probe to unlock features.
$TERM
Every shell Otty launches gets a TERM environment variable. It controls which terminfo entry programs load, and therefore which capabilities they believe are available.
term = auto # auto | xterm-256color | <any installed terminfo name>auto (the default) resolves to xterm-256color — a deliberately conservative choice that's present on every Unix terminfo install and covers everything line editors need (cursor motion, line erase, 256-color). Truecolor is advertised separately through COLORTERM, so you don't lose 24-bit color by staying on xterm-256color.
Set term to any other value and Otty checks that a matching terminfo entry actually exists first. If it doesn't, Otty logs a warning and falls back to xterm-256color so the shell still gets a working terminfo — rather than leaving you with a broken TERM that mangles line editing over SSH or in less.
WARNING
Don't set term = xterm-kitty, term = xterm-ghostty, or another terminal's name hoping to inherit its features. TERM selects a capability database, not a behaviour — claiming to be a terminal you can't fully emulate makes programs emit sequences Otty doesn't handle. Otty advertises its real capabilities through device attributes and COLORTERM instead.
Environment variables
Alongside TERM, Otty exports a fixed set of identification variables into every child process:
| Variable | Value | Purpose |
|---|---|---|
TERM | xterm-256color (default) | terminfo capability database |
COLORTERM | truecolor | Advertises 24-bit color. nvim, modern shells, and ls light up on this. |
TERM_PROGRAM | otty | The canonical "which terminal am I in?" probe. |
TERM_PROGRAM_VERSION | build version | Paired with TERM_PROGRAM for version gating. |
CW_TERM | otty | Stops Amazon Q / Fig / CodeWhisperer from exec-ing cwterm mid-.zshrc (which would suppress shell-integration marks). |
To check from inside a pane:
bash
echo "$TERM_PROGRAM ($TERM_PROGRAM_VERSION), TERM=$TERM, COLORTERM=$COLORTERM"
# otty (1.0.2), TERM=xterm-256color, COLORTERM=truecolorTERM_PROGRAM=otty is the recommended way for scripts and prompts to detect Otty — it's stable across the TERM value you choose, survives tmux, and doesn't require parsing escape replies.
Device attributes
When a program sends a Device Attributes query, Otty answers synchronously over the PTY. These are the legacy DEC identification handshakes; most modern TUIs prefer XTVERSION below, but plenty still probe DA1/DA2.
| Query | Otty's reply | Meaning |
|---|---|---|
DA1 — CSI c / CSI 0 c | CSI ? 6 c | "I'm a VT102-class terminal." Compact and conservative. |
DA2 — CSI > c | CSI > 0 ; <version> ; 1 c | Model ID 0 (generic), build version, ROM slot 1. |
The <version> in the DA2 reply is the build version encoded as a single integer: major × 10000 + minor × 100 + patch (any -rc/-beta suffix is dropped first). So a 1.0.2 build reports 10002. This matches the encoding xterm and Alacritty use, so version-gating logic written for them works unchanged.
DA3 (tertiary attributes) is not implemented — Otty sends no reply, which is the correct behaviour for an unsupported query.
XTVERSION
The modern terminal-identification probe. nvim, kakoune, and others send CSI > q and read back a free-form name+version string to gate features like undercurl, truecolor, and the Kitty keyboard protocol.
Otty replies with a DCS string:
DCS > | otty(<version>) ST— transmitted as ESC P > | otty(<version>) ESC \. The reply is terminated with ESC \ (7-bit ST) so it survives 8-bit-clean PTYs, and <version> is the plain build version string (e.g. otty(1.0.2)). Programs that recognize the otty( prefix can enable Otty-supported features without guessing from TERM.
Cursor and status reports
Device Status Report queries are answered immediately:
| Query | Reply | Meaning |
|---|---|---|
CSI 5 n | CSI 0 n | Terminal is ready / OK. |
CSI 6 n (cursor position) | CSI <row> ; <col> R | 1-based cursor position. |
These power things like a shell's ability to detect where the cursor landed after printing a prompt.
terminfo
Otty ships a few precompiled terminfo entries inside the app bundle (Otty.app/Contents/Resources/terminfo/) and splices that directory to the front of TERMINFO_DIRS for every shell. This lets xterm-ghostty, alacritty, and friends resolve even if you've never installed those terminals — useful when a config or remote dotfile references one. The bundle path is searched first, so a shipped entry wins over an outdated system copy, and /usr/share/terminfo is appended so nothing else is hidden.
Over SSH, the ssh wrapper goes a step further: it extracts your current terminfo with infocmp, pipes it to the remote host, and compiles it there with tic into ~/.terminfo. The result is cached per user@host so it only happens on the first connection. That's why your TERM keeps working on a fresh remote box that's never heard of it — no manual terminfo install required.
See also
- Shell Integration — the
sshwrapper that pushes terminfo and forwardsCOLORTERM/TERM_PROGRAM. - Unicode and Text Styles — what
COLORTERM=truecoloractually unlocks. - Configuration Reference — the
termkey.