Skip to content

Instantly share code, notes, and snippets.

@lawrencecchen
Created September 22, 2026 23:16
Show Gist options
  • Select an option

  • Save lawrencecchen/c35906e356ed852a6f0b30f73f8ea423 to your computer and use it in GitHub Desktop.

Select an option

Save lawrencecchen/c35906e356ed852a6f0b30f73f8ea423 to your computer and use it in GitHub Desktop.
Per-terminal persistent command history for cmux, plus a coding-agent prompt

Per-terminal command history in cmux

This gives each cmux terminal its own persistent zsh or Bash history file. The key is CMUX_SURFACE_ID, which identifies the terminal surface. Do not key this by CMUX_WORKSPACE_ID: a workspace can contain multiple terminals, and workspace identity is not the right granularity for command history.

Prompt for a coding agent

Copy this prompt into your coding agent:

I use cmux on macOS and want the Up/Down arrows in each terminal to show only commands from that terminal. The history must survive cmux quit/reopen and a normal computer restart.

First inspect the current cmux environment contract. Use CMUX_SURFACE_ID as the per-terminal key. Do not use CMUX_WORKSPACE_ID because it identifies a workspace, not an individual terminal. Do not use CMUX_TERMINAL_LIFECYCLE_ID because it changes whenever the shell process is recreated.

Build a user-level shell configuration for both zsh and Bash. When CMUX_SURFACE_ID is present, select a history file under ~/.local/state/cmux/history/<shell>/<surface-id>. Sanitize the ID before using it as a filename, create the directory with mode 700, and leave normal non-cmux shell history unchanged.

For zsh, enable INC_APPEND_HISTORY and APPEND_HISTORY, and disable SHARE_HISTORY so commands from other terminals are not imported. For Bash, set histappend, clear any history loaded from the global file, reload the per-surface file, and append through PROMPT_COMMAND without destroying an existing prompt command. Put the block after shell plugins so they cannot overwrite it.

Test the implementation with isolated temporary HOME directories and at least two fake surface IDs. Prove that the files are separate, that a second shell with the same ID reads the first shell's commands, and that a shell without CMUX_SURFACE_ID keeps its normal history behavior. Explain the remaining limitation that a hard crash can prevent cmux from restoring a terminal snapshot even if the shell history file was written.

Install

Download the shell file for the shell you use, then source it at the end of your startup file:

# ~/.zshrc
source "$HOME/.config/cmux/cmux-history.zsh"
# ~/.bashrc, or ~/.bash_profile on a login-only Bash setup
source "$HOME/.config/cmux/cmux-history.bash"

The snippets are guarded by CMUX_SURFACE_ID, so shells outside cmux keep their existing history file.

What persists

cmux stores the terminal surface UUID in its session snapshot and injects the same CMUX_SURFACE_ID when it recreates that terminal after a normal quit/reopen or computer restart. The history files are ordinary files on disk. Duplicating a live terminal gets a different identity and therefore a separate history file, which avoids merging two active shells.

# Per-terminal command history for cmux.
# Source this at the end of ~/.bashrc or ~/.bash_profile.
if [[ -n ${CMUX_SURFACE_ID:-} ]]; then
cmux_history_id="${CMUX_SURFACE_ID//[^[:alnum:]_.-]/_}"
cmux_history_dir="${XDG_STATE_HOME:-$HOME/.local/state}/cmux/history/bash"
mkdir -p -m 700 -- "$cmux_history_dir"
HISTFILE="$cmux_history_dir/$cmux_history_id"
HISTSIZE=10000
HISTFILESIZE=20000
# Remove commands Bash may have loaded from ~/.bash_history, then read
# only this terminal's file.
shopt -s histappend
history -c
[[ -r "$HISTFILE" ]] && history -r "$HISTFILE"
cmux_history_append() { builtin history -a; }
# Bash 3.2 uses a string; newer Bash versions can use an array.
if [[ $(declare -p PROMPT_COMMAND 2>/dev/null) == "declare -a"* ]]; then
case " ${PROMPT_COMMAND[*]} " in
*" cmux_history_append "*) ;;
*) PROMPT_COMMAND+=(cmux_history_append) ;;
esac
else
case ";${PROMPT_COMMAND:-};" in
*";cmux_history_append;"*) ;;
*) PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND;}cmux_history_append" ;;
esac
fi
fi
# Per-terminal command history for cmux.
# Source this at the end of ~/.zshrc.
if [[ -n ${CMUX_SURFACE_ID:-} ]]; then
typeset -g _cmux_history_id="${CMUX_SURFACE_ID//[^[:alnum:]_.-]/_}"
typeset -g _cmux_history_dir="${XDG_STATE_HOME:-$HOME/.local/state}/cmux/history/zsh"
mkdir -p -m 700 -- "$_cmux_history_dir"
HISTFILE="$_cmux_history_dir/$_cmux_history_id"
HISTSIZE=10000
SAVEHIST=10000
setopt APPEND_HISTORY
setopt INC_APPEND_HISTORY
unsetopt SHARE_HISTORY
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment