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.
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.
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.
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.
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 ✓.
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.
~/.bashrc or ~/.zshrc. The install
skips a shell whose file does not exist. Create it first
(touch ~/.bashrc) and install again..bashrc. Some
hosts read only ~/.profile or ~/.bash_profile over SSH.
Add [ -f ~/.bashrc ] && . ~/.bashrc to ~/.profile.sh, for instance, has no hook to attach to. Change the login shell
with chsh, or start one with bash -l..bash_profile before .bashrc is read leaves the hook
inert on that version.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.