Elyra
Elyra The coding agent eTerm The terminal that knows where each command ends e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Release notes
Changelog
Elyra
Shell integration

Shell integration

Without it, eterm is a good terminal that cannot tell one command from another. With it, it knows where each command's output begins and ends, and where the shell is working.

Turning it on

zsh needs nothing done. eterm starts every zsh session pointed at a ZDOTDIR of its own — four small files in ~/.config/eterm/zdotdir, rewritten at every launch. Each one sources your own .zshenv, .zprofile, .zshrc or .zlogin and then hands ZDOTDIR back, so your configuration runs exactly as it would have and a shell started inside the session is an ordinary shell again. Nothing is written into your home directory. cmd-,Shell says on for every session when this is what is happening.

The integration is sourced after your rc file, and that order is not incidental: zsh hooks run in the order they were added, and a prompt framework that rebuilds PROMPT in its own precmd — starship, powerlevel10k — would otherwise throw away the mark that says where typing starts. Sourcing it twice, which is what happens when the line below is also in your .zshrc, leaves one hook rather than two.

To take your shell back, set ZDOTDIR yourself:

# ~/.config/eterm/settings.toml
[shell.env]
ZDOTDIR = "~/.config/zsh"      # or "~", to go without the integration

Settings win over everything eterm sets, this included.

bash needs one line. Open cmd-,Shell. The Shell integration row says whether it is installed, and Install appends one marked line to your .bashrc:

source "/Applications/eterm.app/Contents/Resources/shell/eterm.bash"  # added by eterm

It appends and never rewrites: an rc file is yours, often long, and frequently in version control. Installing twice does nothing the second time. Then open a new session — this one keeps the shell it started with.

bash gets a line and zsh does not because there is nowhere to put it otherwise: --rcfile is read only by interactive non-login shells and BASH_ENV only by non-interactive ones, and neither is the login shell a terminal starts. Changing which kind of shell you get in order to make room for a hook would be a worse trade than asking for the line.

To do it by hand instead, add one line to your shell's rc file:

# ~/.bashrc
source /path/to/eterm/shell/eterm.bash

# ~/.zshrc
source /path/to/eterm/shell/eterm.zsh

Installed from the disk image, the scripts are inside the bundle at /Applications/eterm.app/Contents/Resources/shell/.

When a shell says nothing

Press return twice in a session that has never reported anything and a strip appears above the footer: this shell is not reporting: nothing folds, nothing jumps, and the command line stays grey, with a button that adds the line to the rc file it belongs in.

It is there because the alternative is silence. Every one of the features below is invisible when it is missing — a terminal that cannot fold looks exactly like one nobody has asked to fold — and a row in a settings pane is only read by people who already went looking. It appears once per window, only for a shell eterm ships integration for, and Not now ends it.

What it does

The scripts emit two OSC sequences your terminal would otherwise never see:

  • OSC 7 reports the working directory whenever the prompt is drawn. This is what names tabs, what a new tab or split inherits, and what a restored session comes back to.
  • OSC 133 marks the prompt (A), the end of the prompt (B), the start of a command's output (C) and its end with an exit code (D). A, C and D are what make a block a block; B is what makes the command line colourable, because it says where the prompt stops and typing starts.

Neither reaches the emulator on its own: vte's parser handles a dozen OSC codes and drops the rest, including these two. eterm reads the pty itself and scans for them on the way past — see Architecture.

What you get

  • Folding. cmd-shift-enter collapses the output of the command at the cursor to a single line: ▸ 14 lines folded, with the exit code when it was not zero. Clicking that line puts the output back.
  • Prompt jumping. cmd-up and cmd-down move the viewport between prompts.
  • Handing a block to the next pane. cmd-shift-s pastes the command and its output — trimmed the way AI mode trims it, both ends kept — into whichever pane had focus before this one. The shape of the day: tests in one pane, an agent waiting in the other, and the distance between the failure and the question used to be a selection, a copy, a click and a paste. With one pane, or the previous one gone, it goes to the clipboard instead. See Blocks.
  • The session as a runbook. cmd-shift-m puts every finished command on the clipboard as Markdown, in order, with its exit code, how long it took and what it printed — trimmed and redacted the way AI mode does it, since a runbook goes to other people by definition. A handover or a bug report, already in the shape it will be read in.
  • A strip of blocks. Down the right edge of every session, the scrollback as a map: a mark per command, red where it failed, and the part on screen as a band. Where the problems are, before you scroll to them.
  • Comparing two runs. cmd-shift-c on a block shows what changed in its output since the last time the same command ran, in a window that draws it like a pull request's diff. Same text exactly: cargo test and cargo test -- --nocapture are different commands.
  • A coloured command line. Green for a command that exists. See configuration.
  • Suggestions and completions while typing. See Suggestions.
  • Directories. Tab names, inherited directories, and session restore.
  • Tasks in AI mode. Knowing that a command has finished, and with which exit code, is what lets the model read the result and propose the next step. Without integration an approved command still runs, but nothing follows it.

Two runs of the same command

You run the tests, change something, run them again, and now there are two hundred lines on screen and the question is which of them differ from last time. cmd-shift-c on the block answers that: the two outputs are aligned line by line and what changed is shown as a diff, three unchanged lines either side of each change, in the same window that draws a pull request.

Two runs that printed the same thing say so rather than showing an empty diff, because an empty diff means two different things and you deserve to know which. Outputs past four thousand lines are not compared — nobody reads a diff that long, and the table behind the comparison grows with the product of the two lengths.

Over ssh

The marks come from the shell, so they only exist where the integration is installed. A ZDOTDIR does not travel over ssh, so a remote host means putting the line in an rc file there by hand. Everything that reads the screen — find, selection, AI mode — works over ssh regardless, because the screen is local.

Writing your own

Any integration that emits the standard sequences works. The bash script guards against one trap worth knowing: bash's DEBUG trap fires for every command, including the ones inside PROMPT_COMMAND, so without a guard every prompt starts a block that never ends.

One trap is worth naming, because it cost this project a release: in zsh, status is a read-only alias for $?, so local status=$? fails the hook on its first line — silently, at every prompt, emitting nothing at all. It is correct bash. scripts/testing/shell-integration.sh runs both shells and reads what they actually emit; run it after changing either script. cargo test goes one further and starts a real zsh against a generated ZDOTDIR, because every claim about what zsh does with a directory of startup files is a claim that cannot be checked by reading the files.

B cannot be printed from a hook: the prompt is drawn after the hook returns, so the mark has to live inside PROMPT or PS1 itself, wrapped in %{ %} for zsh or \[ \] for bash so that line editing does not count it as width.

printf '\033]133;A\007'                    # prompt starts here
printf '\033]133;C\007'                    # output starts here
printf '\033]133;D;%s\007' "$status"       # command finished with this code
printf '\033]7;file://%s%s\007' "$HOST" "$PWD"