Shell integration

What the terminal's optional shell integration adds to a host, how to see that it is working, and how to take it out again.

Last updated 10 September 2026.

This page belongs to the Terminal section of the manual.

What it is

Out of the box, the terminal remembers a command by watching what the shell echoes back as you type. That is a guess: a line edited with the arrow keys, a word completed with Tab, or a command recalled from the shell's own history can come out wrong, and a row that came from watching carries no mark.

The shell integration removes the guessing. Once installed, the host itself announces each command exactly as the shell is about to run it, and the row in History shows a ✓ to say so. It is optional, it works only over SSH, and command history is a Pulse Pro feature.

What it adds

For bash, these lines are appended to ~/.bashrc:

# pulse-shell-integration
__pulse_announce() { local l n; l=$(HISTTIMEFORMAT= builtin history 1); l=${l#"${l%%[! ]*}"}; n=${l%%[[:space:]]*}; [ -n "$n" ] && [ "$n" != "${__pulse_last-}" ] || return 0; __pulse_last=$n; l=${l#"$n"}; l=${l#"${l%%[! ]*}"}; printf '\033]7770;%s\007' "$(printf %s "$l" | base64 | tr -d '\n')"; }
eval "__pulse_prev=($(trap -p DEBUG))"; __pulse_prev=${__pulse_prev[2]-}
trap "__pulse_announce${__pulse_prev:+; $__pulse_prev}" DEBUG
# pulse-shell-integration end

For zsh, these lines are appended to ~/.zshrc:

# pulse-shell-integration
__pulse_announce() { printf '\033]7770;%s\007' "$(printf %s "$1" | base64 | tr -d '\n')"; }
autoload -Uz add-zsh-hook && add-zsh-hook preexec __pulse_announce
# pulse-shell-integration end

Each block is appended only if that file already exists, and only if it does not already carry the # pulse-shell-integration marker, so installing twice changes nothing. In bash, if a DEBUG trap is already set (by iTerm2, VS Code, or bash-preexec, say), the block chains it rather than replacing it. The announcement itself is a private terminal escape, OSC 7770, carrying the command base64-encoded. It travels inside your SSH session and nowhere else: nothing is sent anywhere but to your own Pulse terminal.

Install

While connected to the host over SSH, open the ⋯ menu and choose Shell integration › Install…. Pulse pastes the lines into the session as two cat >> … <<'PULSE' commands, one per shell, and you watch them land in the terminal as they are typed. Nothing is hidden.

It takes effect from your next login. The trap is set when the shell reads its profile, so the session you installed from carries on as before. Disconnect, connect again, and the host starts announcing.

On bash 5.1 and later the paste lands in the line editor rather than running straight away; press Return to run it.

Check

To see that the lines are in place:

grep -n pulse-shell-integration ~/.bashrc ~/.zshrc

After logging in again, to see that the hook is live:

trap -p DEBUG            # bash
echo $preexec_functions  # zsh

Then run any command and look in History: the row should carry a ✓.

Remove

While connected, open the ⋯ menu and choose Shell integration › Remove…. Pulse deletes the block from marker to end marker in each profile (with sed -i.pulse-bak, which works on macOS and Linux, and removes the backup afterwards). You can also delete the lines by hand. Removing the app from your device does not touch the host; the lines stay until you remove them.

Known limits

History records the command lines you run, on your device and in your own iCloud if you sync. A line the host did not echo back, which is what a password prompt looks like, is dropped before it is stored, with or without the integration. What stays on your device and what leaves it is set out in the privacy page.