Terminal Integration
Repl integrates with the host terminal at several levels: it detects window size, decodes VT keyboard sequences, emits progress notifications and shell-integration marks to capable terminals, and classifies terminal identity to adapt rendering. This page documents those interactions.
VT sequence notation used throughout this page:
| Abbreviation | Expansion | Hex |
|---|---|---|
ESC | Escape | 0x1B |
CSI | Control Sequence Introducer (ESC [) | 0x1B 0x5B |
OSC | Operating System Command (ESC ]) | 0x1B 0x5D |
SS3 | Single Shift Three (ESC O) | 0x1B 0x4F |
BEL | Bell | 0x07 |
ANSI and color
Section titled “ANSI and color”ANSI escape sequences are enabled or disabled based on a precedence chain:
| Precedence (highest → lowest) | Source |
|---|---|
| Session override | TerminalSessionOverrides.AnsiSupported |
| Explicit config | OutputOptions.AnsiMode = Always / Never |
NO_COLOR | Any set value disables ANSI (per no-color.org) |
CLICOLOR_FORCE=1 | Forces ANSI on |
| Redirected output | Console.IsOutputRedirected disables ANSI |
TERM=dumb | Disables ANSI |
When ANSI is enabled, the default renderer uses 256-color SGR styling (38;5;N). Add Repl.Spectre for 24-bit color (38;2;r;g;b) and richer rendering — Spectre honors the same detection chain, requesting true-color when the host allows ANSI and degrading to plain text when it doesn’t.
Terminal size detection
Section titled “Terminal size detection”Repl uses the terminal window size to format output (table column widths, word wrap, indentation). The width is resolved from the first available source:
| Source | Context |
|---|---|
TerminalSessionOverrides.WindowSize | Explicit override — always wins |
| Console direct query | Local interactive sessions — Console.WindowWidth |
DTTERM probe (CSI 18 t) | Streamed/WebSocket sessions — active probe at startup |
| Telnet NAWS (RFC 1073) | Telnet sessions — negotiated at connection |
@@repl: control protocol | WebSocket/SignalR clients — client-pushed JSON |
| Default fallback | 120 columns |
VT probe (DA1 + DTTERM window size)
Section titled “VT probe (DA1 + DTTERM window size)”For sessions over a stream pair (WebSocket, named pipe, custom transport), Repl sends CSI c (DA1 — Device Attributes) followed by CSI 18 t (DTTERM window size request) immediately after connection. The terminal’s response (CSI 8 ; rows ; cols t) is parsed as the initial size.
Resize events — when the user resizes the terminal window during a session — are delivered as the same CSI 8 ; rows ; cols t sequence and processed continuously.
Telnet NAWS
Section titled “Telnet NAWS”Repl.Telnet negotiates the Telnet NAWS option (RFC 1073) during the connection handshake. Window dimensions are received via subnegotiation and updated on resize. Repl.Telnet also negotiates TERMINAL-TYPE (RFC 1091) to obtain the terminal identity string.
@@repl: control protocol
Section titled “@@repl: control protocol”Custom transport clients (WebSocket, SignalR) can push metadata by writing a @@repl: JSON control message to the input stream. The framework parses hello and resize verbs:
@@repl:{"verb":"hello","terminal":"xterm-256color","cols":220,"rows":50,"ansi":true}@@repl:{"verb":"resize","cols":160,"rows":40}This is Repl’s own convention — not a terminal standard. Client libraries that wrap the StreamedReplHost can use it to pass terminal state.
Keyboard input
Section titled “Keyboard input”For sessions over a stream pair (StreamedReplHost, Telnet, WebSocket), Repl decodes VT escape sequences from the input stream. The following key events are recognized:
| Keys | VT sequence shape |
|---|---|
| Arrow keys (Up/Down/Left/Right) | CSI A / B / C / D |
| Home / End | CSI H / F or CSI 1~ / 4~ |
| Insert / Delete | CSI 2~ / 3~ |
| Page Up / Page Down | CSI 5~ / 6~ |
| F1–F4 | SS3 P / Q / R / S |
For local interactive mode, keyboard input goes through the standard Console.ReadKey path, which handles modifiers natively on the host OS.
Progress reporting (OSC 9;4)
Section titled “Progress reporting (OSC 9;4)”Repl emits OSC 9;4 progress notifications — supported by Windows Terminal, ConEmu, WezTerm, iTerm2, and Ghostty — so the host shows a progress indicator in the taskbar or title bar.
The escape sequence format:
ESC ] 9;4;<state>;<percent> BELState codes:
| State | Code | Meaning |
|---|---|---|
| Normal | 1 | In progress, known percent |
| Error | 2 | Failed |
| Indeterminate | 3 | In progress, percent unknown |
| Warning | 4 | Completing with warnings |
| Clear | 0 | Done — remove progress indicator |
Configuration
Section titled “Configuration”Control emission with InteractionOptions.AdvancedProgressMode:
app.Options(o =>{ o.Interaction.AdvancedProgressMode = AdvancedProgressMode.Auto; // default // AdvancedProgressMode.Always — always emit // AdvancedProgressMode.Never — never emit});In Auto mode, OSC 9;4 is emitted when any of the following is true:
- The local environment sets
WT_SESSION(Windows Terminal),ConEmuANSI=ON(ConEmu), orTERM_PROGRAM=WezTerm. - The session’s terminal capabilities include
ProgressReporting(see Terminal capabilities below).
Emission is suppressed when TMUX is set or TERM starts with screen or tmux, since multiplexers pass the raw escape sequence to the outer terminal unparsed.
OSC 9;4 is never emitted when ANSI output is disabled or when the session is in protocol-passthrough mode (e.g., MCP serve).
Shell integration marks (OSC 133 / OSC 633)
Section titled “Shell integration marks (OSC 133 / OSC 633)”Modern terminals understand semantic marks that delimit the prompt, the user input, and the command output. When the marks are present, the terminal can offer command navigation (jump between commands), command-aware selection and copy, success/failure decorations in the gutter, and sticky command headers. Repl owns the prompt and the command lifecycle in interactive mode, so it emits those marks itself — no shell script hooks required.
The feature is opt-in:
var app = ReplApp.Create() .UseTerminalIntegration(); // ShellIntegration = Auto by default
// or explicitly:app.UseTerminalIntegration(options =>{ options.ShellIntegration = ShellIntegrationMode.Always;});Each interactive prompt cycle is delimited with the FinalTerm semantic sequence (OSC 133), or the VS Code shell-integration dialect (OSC 633) when the VS Code integrated terminal is detected:
| Moment | Mark |
|---|---|
| Before the prompt text | A (prompt start) |
| After the prompt text, before input | B (input start) |
| After a committed line (VS Code only) | E;<command line> (command-line report) |
| Right before command execution | C (output start) |
| After the command completes | D;<exit code> (command end) |
Exit codes follow shell conventions: 0 for success, 1 for errors (failed results, unknown commands, validation failures), and 130 (128+SIGINT) when a command is cancelled with Ctrl+C. An abandoned cycle — Escape at the prompt, an empty line, or end of input — reports D without an exit code, the FinalTerm “command aborted” form.
ShellIntegrationMode mirrors the AdvancedProgressMode semantics:
Auto(default) — emit when the terminal is known to render marks. For a hosted session, only what the remote client advertised counts:TerminalCapabilities.ShellIntegrationMarks, usually inferred from its reported terminal identity. For the local console, the environment identifies Windows Terminal (WT_SESSION), VS Code (TERM_PROGRAM=vscode), or WezTerm (TERM_PROGRAM=WezTerm); multiplexers (tmux, GNU screen) stay off because mark positioning is unreliable through panes.Always— emit whenever the structural gates allow it. Useful for terminals that render marks but are not auto-detected, such as iTerm2 reached over SSH.Never— never emit.
Regardless of mode, marks are never written when output is redirected (and no hosted session is active), when ANSI output is disabled (NO_COLOR, TERM=dumb, AnsiMode.Never), or around a protocol-passthrough command (MCP stdio, completion payloads) — no mark may sit inside a raw protocol stream.
CLI one-shot mode emits no marks: Repl does not own the surrounding shell prompt there, and fake prompt markers would corrupt the host shell’s own command navigation.
Troubleshooting
Section titled “Troubleshooting”Ask the running app first: IReplSessionInfo exposes ShellIntegrationStatus, which reports the detection outcome for the current prompt cycle — the active dialect ("OSC 133", "OSC 633 (VS Code)") or "off (<gate>)" naming the gate that disabled emission:
app.Map("terminal", (IReplSessionInfo session) => session.ShellIntegrationStatus ?? "no prompt cycle yet");If a terminal is misdetected and shows raw ]133;… text, set ShellIntegrationMode.Never app-side, or use NO_COLOR=1 as a local end-user escape hatch (it disables all ANSI styling, marks included). For hosted sessions, fix the identity or capabilities the client advertises — environment variables on the server affect every connected session.
Terminal capabilities
Section titled “Terminal capabilities”When a session connects, Repl classifies its terminal identity to determine which capabilities to enable. The identity string comes from:
- Telnet TERMINAL-TYPE negotiation (
Repl.Telnet) @@repl:helloJSON control message (terminalfield)TerminalSessionOverrides.TerminalIdentity— explicit override
The classifier maps substrings of the identity string to capability flags:
| Terminal identity contains | Ansi | ProgressReporting | ShellIntegrationMarks | VtInput | ResizeReporting |
|---|---|---|---|---|---|
windows terminal | ✓ | ✓ | ✓ | ✓ | ✓ |
wezterm | ✓ | ✓ | ✓ | ✓ | ✓ |
iterm | ✓ | ✓ | ✓ | ✓ | ✓ |
ghostty | ✓ | ✓ | ✓ | ✓ | ✓ |
conemu | ✓ | ✓ | ✓ | ✓ | |
vscode | ✓ | ✓ | ✓ | ✓ | |
xterm, vt, ansi | ✓ | ✓ | ✓ | ||
alacritty, rxvt, konsole, gnome, linux | ✓ | ✓ | ✓ | ||
screen, tmux | ✓ | ✓ | ✓ | ||
dumb |
Matching is case-insensitive and substring-based — xterm-256color matches xterm.
ShellIntegrationMarks is what Auto mode checks for hosted sessions. A vscode identity additionally selects the OSC 633 dialect. ConEmu deliberately lacks the flag: it renders OSC 9;4 progress but not FinalTerm/VS Code prompt marks.
VtInput enables VT keyboard sequence decoding. ResizeReporting enables live terminal resize events. Omitting either flag in a TerminalSessionOverrides override will silently suppress that behavior.
For local sessions (no identity string), terminal detection falls back to environment variables: WT_SESSION, ConEmuANSI (any case — on, On, ON all match), and TERM_PROGRAM are checked for OSC 9;4 gating.
Override capabilities when the auto-detection doesn’t fit:
// In a hosted session:new TerminalSessionOverrides{ TerminalIdentity = "xterm-256color", TerminalCapabilities = TerminalCapabilities.Ansi | TerminalCapabilities.VtInput | TerminalCapabilities.ResizeReporting,}Adapting output to terminal capabilities
Section titled “Adapting output to terminal capabilities”Handlers can query terminal properties to adapt their output:
using Repl.Interaction;
app.Map("report", (ITerminalInfo terminal, IDataStore store) =>{ if (terminal.WindowSize?.Width >= 120) return BuildWideReport(store); else return BuildCompactReport(store);});With Repl.Spectre, adaptation is automatic — Spectre.Console degrades gracefully when the terminal doesn’t support colors or Unicode.
Suppressing framework output
Section titled “Suppressing framework output”In CLI mode, Repl suppresses banners and interactive prompts automatically. For protocol commands (the completion bridge, mcp serve), all framework diagnostics go to stderr and structured output to stdout, making output safe to pipe.
Toward a TUI mode
Section titled “Toward a TUI mode”Currently in place:
- OSC 9;4 progress in supported terminals
- Shell-integration marks (OSC 133 / OSC 633) for command navigation and gutter decorations
- ANSI 256-color rendering (default) and 24-bit color via
Repl.Spectre - Screen clear and cursor positioning for interactive menus
- VT key decoding (arrows, Home/End, PgUp/PgDn, F1–F4)
- Terminal size probing and live resize events
- Terminal identity classification (15+ terminal families)
Not yet implemented:
- Kitty Keyboard Protocol — extended modifier-aware key input (
CSI <code>;<mods> u) - Mouse reporting (
?1000/?1006and variants) - Alternate screen buffer (
?1049) - Bracketed paste mode (
?2004) - Sixel and Kitty graphics
- OSC 7 (working directory), OSC 8 (hyperlinks), OSC 52 (clipboard)
- True-color palette in the default renderer (256-color only; 24-bit available via
Repl.Spectre)
These primitives form the foundation for a future TUI mode — full-screen, keystroke-driven surfaces (panels, menus, live updates) that will compose with the same command graph. The current Repl.Spectre integration covers static rich rendering; a dedicated Repl.Tui package is on the roadmap.