Guide 9 min read

The Best Zsh History Replacement in 2026 (and How to Upgrade)

Fix native Zsh history settings and search first, then compare replacements and install, import, search, and roll back a database-backed history tool.

Madhubalan Appachi Madhubalan Appachi · Published · Updated

When Zsh history lets you down, it is usually one of two different problems. Either the command was never saved (or the file was damaged), or it is saved but you cannot find it quickly. A replacement tool can help with the second problem, but the first is often a few lines of Zsh configuration. This guide shows how to check and fix native Zsh history first, how to search it well, where a flat history file really runs out, and how to install, import into, and roll back a database-backed replacement.

Review note: I build Suvadu, one of the tools discussed here. I reviewed this guide on 19 September 2026 against the Zsh 5.9 manual (zshoptions, zshbuiltins, zshzle) and Suvadu 0.4.1.

What built-in Zsh history already does

Zsh keeps an in-memory history list and, if HISTFILE is set, saves it to a text file such as ~/.zsh_history. A handful of parameters and options control it:

  • HISTFILE is where history is saved. If it is unset, history lives only in memory and disappears when the shell exits. macOS sets it in /etc/zshrc; on other systems it depends on your distribution or framework.
  • HISTSIZE is how many entries each shell keeps in memory; SAVEHIST is how many are written to the file. macOS defaults are 2,000 and 1,000. Older commands are dropped once these limits are reached, so raise them if you want long history, keeping HISTSIZE at least as large as SAVEHIST.
  • INC_APPEND_HISTORY writes each command to the file as soon as it is entered instead of when the shell exits, so a crashed or killed terminal does not lose its session. INC_APPEND_HISTORY_TIME writes after the command finishes, which records durations correctly.
  • SHARE_HISTORY appends commands as they are entered and also imports new commands from other running shells. The manual treats it and the two incremental-append options as alternatives: set one, not all three.
  • EXTENDED_HISTORY saves each command's start time and duration in the file (: 1758268800:3;make test).
  • HIST_IGNORE_DUPS skips a command identical to the previous one; HIST_IGNORE_ALL_DUPS removes older duplicates; HIST_IGNORE_SPACE drops commands typed with a leading space, which is useful for commands you do not want saved.

A reasonable starting point in ~/.zshrc:

HISTFILE=~/.zsh_history
HISTSIZE=100000
SAVEHIST=100000
setopt EXTENDED_HISTORY
setopt SHARE_HISTORY        # or INC_APPEND_HISTORY / INC_APPEND_HISTORY_TIME, not together
setopt HIST_IGNORE_DUPS
setopt HIST_IGNORE_SPACE

Missing or damaged history vs. hard-to-find history

Work out which problem you have before changing tools:

  • Commands never reach the file. Check echo $HISTFILE $HISTSIZE $SAVEHIST in the affected shell. An unset HISTFILE, a tiny SAVEHIST, or a framework overriding your values after your settings will all lose history. Without an incremental-append or sharing option, a terminal that is killed rather than exited never writes its commands.
  • Other terminals do not see new commands. That is expected without SHARE_HISTORY: each shell loads the file at startup. Run fc -RI to read new entries from the file on demand, or enable sharing.
  • "zsh: corrupt history file". The file contains a damaged or truncated entry, often after a crash or a full disk. Back up the file first, close other shells so they do not rewrite it, and remove the damaged lines in an editor. A history replacement does not repair this file for you.
  • The command is saved but you cannot find it. That is a recall problem. Try the native search tools below before deciding it is a tooling problem.

Search native Zsh history before you replace it

Native Zsh search is more capable than its reputation:

  • Ctrl+R runs history-incremental-search-backward in the default emacs keymap. Keep pressing it to step to older matches. It is not bound in the vi keymaps, so bind it yourself if you use vi mode. history-incremental-pattern-search-backward accepts glob patterns.
  • fc -l lists history; fc -l 1 lists all of it, and piping to grep gives you every match at once: fc -l 1 | grep docker.
  • history -E (or -i for ISO format) shows when each command ran, which needs EXTENDED_HISTORY for commands from earlier sessions.
  • zsh-history-substring-search makes Up/Down cycle through commands containing what you have typed, similar to the Fish shell.

Where a history file runs out

With good settings, native history is reliable for many people. A flat file still has limits that configuration cannot fix:

  • Little context. Even with EXTENDED_HISTORY, the file stores a start time, duration, and command. It does not record the directory, the exit status, or which terminal session ran a command, so you cannot ask "what failed in this project yesterday?"
  • One match at a time. Ctrl+R shows a single match; to see and filter many results you drop to fc -l | grep or add a tool such as fzf.
  • Size limits are a trade-off. You can raise SAVEHIST a lot, but the file is rewritten to trim it and every shell reads it at startup.
  • No view of AI agents. Coding agents usually run commands in non-interactive shells, which do not write to your history file, so their commands rarely appear there.

Your options in 2026

Tool What it is Best for
Native Zsh + pluginsThe settings above, plus zsh-history-substring-searchKeeping your setup small once recording is fixed
fzfA fuzzy finder bound to Ctrl+R over your existing historyA quick, zero-lock-in search upgrade
McFlyNeural-ranked Ctrl+R with a SQLite store alongside the history fileContext-ranked recall
AtuinSQLite history with fuzzy scoped search, many importers, optional encrypted sync, agent hooks, and MCPThe strongest all-round shell-history replacement, Fish or multi-machine users
SuvaduLocal SQLite history for Bash and Zsh plus AI-agent session captureReviewing and continuing coding-agent work alongside your own history

fzf and McFly still read or keep your normal history file, so fix recording first. Atuin and Suvadu record new commands through their own shell hooks. For a fuller comparison, see Atuin, fzf, McFly, hstr, and Suvadu compared.

Switching to Suvadu: install, import, search, and roll back

Suvadu records each new command in a local SQLite database with its directory, exit code, duration, session, and whether a person or an agent ran it. It does not change HISTFILE or your history options: Zsh keeps writing ~/.zsh_history as before. Like HIST_IGNORE_SPACE, it skips commands typed with a leading space. It requires Zsh 5.1+ on macOS or Linux.

Know two 0.4.1 limits before you start: import accepts only Zsh history and Suvadu's own JSONL, and typed Ctrl+R search only matches within the newest ~5,000 eligible entries (a date or directory filter reaches older ones; a fix is planned but not released).

1. Install

brew tap AppachiTech/suvadu && brew install suvadu

Alternatives: curl -fsSL https://downloads.appachi.tech/suvadu/install.sh | bash (verifies a checksum and installs to /usr/local/bin with sudo) or cargo install suvadu. None of them edit your shell configuration.

2. Enable the Zsh hook

Add this line once to ~/.zshrc, then reload. If another tool such as Atuin, McFly, or fzf binds Ctrl+R or the arrow keys, disable its binding first; the one loaded last wins.

echo 'eval "$(suv init zsh)"' >> ~/.zshrc
source ~/.zshrc
suv doctor

The hook binds Ctrl+R to Suvadu search and Up/Down to Suvadu's recency-first recall. To keep native (or zsh-history-substring-search) arrow behavior, set enable_arrow_navigation = false under [shell] in Suvadu's config or in suv settings, then open a new shell.

3. Import your existing Zsh history

Preview first, then import. The import reads the file and never modifies it, and reports how many commands it imported and skipped.

suv import --from zsh-history ~/.zsh_history --dry-run
suv import --from zsh-history ~/.zsh_history

If the file used EXTENDED_HISTORY, imported commands keep their timestamps and durations, and importing the same file again skips entries already present. Plain-format lines have no timestamps, so they are stamped with the import time and are not deduplicated on a repeat import; import a plain file once. The file never held directories or exit codes, so imported entries do not have them. Multi-line commands are kept together. If the file contains invalid UTF-8 bytes, Suvadu warns and replaces them.

4. Run a first search

echo hello

Press Ctrl+R and type hello. The detail panel shows the directory, exit code, and time of the command you just ran. Select it to put it on your prompt; press Enter to run it. Search is literal: every typed word must appear in the command.

Next step: if nothing was recorded or Ctrl+R still opens another tool, work through the troubleshooting guide. The installation guide and shell integration reference cover Zsh setup in more detail.

5. Roll back

  • Stop recording without removing anything: suv pause pauses the current shell; suv disable stops recording everywhere until suv enable. The key bindings stay active.
  • Return to native Zsh history: delete the eval "$(suv init zsh)" line from ~/.zshrc and open a new terminal (or run exec zsh). Re-sourcing the file in the same shell does not remove bindings that are already loaded.
  • Uninstall: for Homebrew and Cargo installs, suv uninstall asks for confirmation, removes the binary, removes that exact eval line from ~/.zshrc and ~/.bashrc, and removes Suvadu's Claude Code and Codex hooks. For an install-script install it only prints the binary's path for you to delete (for example sudo rm /usr/local/bin/suv), so remove the ~/.zshrc line yourself. See update and uninstall.

Uninstalling keeps Suvadu's database and config so you can change your mind; delete them separately if you want them gone. Your ~/.zsh_history is untouched throughout, and because Zsh kept writing it while Suvadu was active, it still contains the commands from that period, as far as your Zsh settings saved them.

The bottom line

Start with native Zsh: set HISTFILE, generous HISTSIZE/SAVEHIST, EXTENDED_HISTORY, and one of SHARE_HISTORY or an incremental-append option. That fixes most missing history. If you then want richer recall and context, fzf and McFly are small steps and Atuin is the most complete shell-history replacement. Suvadu is worth trying when you also want to see and continue what your coding agents did.

Install Suvadu →  ·  Compare the top shell history tools →

Madhubalan Appachi
Madhubalan Appachi

Builder of Suvadu. Writes Rust, thinks about shell history more than most people, and believes developer tools should be local-first.