Suvadu 0.5.1: Forgiving Search, Smarter Recall, and What Your AI Agents Actually Changed
Suvadu 0.5.1 finds hive-agent when you type hiveagent, ranks recall by your habits, says why a command failed and what fixed it, and records which files each AI agent session changed.
Suvadu 0.5.1 is about the two moments a history tool is supposed to help with and often doesn't: when you half-remember a command and type it slightly wrong, and when an AI agent has been working in your repository and you want to know what actually happened. Search now forgives punctuation and ranks by your habits. A failed command can say why it failed and what fixed it last time. And every agent session in a git repository now records which files changed, so “what did the agent do?” gets an observed answer instead of a guess.
This post walks through what changed, with the costs measured for it and the limits it still has. The full list is in the changelog. If you are upgrading, the short version is: rerun suv init cursor, suv init opencode and suv init antigravity if you use them, and open new terminals. The rest is at the end.
A note on numbers: every figure below comes from BENCHMARKS.md, measured on one Apple M4 Max (128 GB RAM, macOS 27.2), one run each. Treat them as an order of magnitude on that machine, not a promise about yours.
Search that forgives punctuation
Suvadu's default search mode, terms, requires every word you type to appear in the command. That is predictable, and it is also why hiveagent used to find nothing when what you ran was docker logs hive-agent.
In 0.5.1, when a terms query matches nothing, recall asks again with separators ignored: whitespace, -, _, ., / and : are dropped from both your words and your history. So hiveagent finds hive-agent, hive_agent, hive.agent, hive/agent and hive:agent, and hive-agent finds hiveagent.
- Exact matches always win. The fallback runs only when nothing matched as typed, and it never mixes with exact results, so the count and every page come from one rule.
- It says so. The results title reads
ignoring punctuation (-_./:)and the status rowMatch terms (loose), and the matched text is underlined with the separators inside it. - Agents get it too. The MCP
search_commandstool falls back the same way and notes it in its response.
It is answered by a second search index, with separators removed. Building it once on upgrade held the write lock for 0.68 s at 100,000 entries, it adds 12–25% to the database depending on how long your commands are, and recording a command takes about 25 µs longer because every insert updates it.
Two other search fixes ride along. Substring search now actually uses the trigram index it had since 0.4.0: SQLite never handed a LIKE … ESCAPE to it, so every terms and literal query scanned the whole history. On the 100,000-entry benchmark corpus most judged queries fell from 10–40 ms to well under a millisecond, with the same results. And terms and fuzzy now fold Unicode case rather than only lowercasing, so σ finds ΟΔΟΣ and straße finds STRASSE.
Recall that learns your habits
Until now, Suvadu's Smart rank boosted commands run in your current directory and otherwise ordered matches by how well the text matched. It did not care that you run cargo test twenty times a day, or that one spelling of a command always fails. 0.5.1 adds three signals, all within a match tier:
- Frecency. How often and how recently you ran that exact command. Each run counts 16 in its first hour, 8 that day, 4 for three days, 1 for a week, 0.5 for a month and 0.25 after that. Twenty runs a day or two ago now beat one run five minutes ago, without burying it. It is the same definition the MCP
suggest_nexttool already used. - Outcome. A command that fails more than half of at least three runs is scaled down, to a quarter when it has never succeeded. One failure among many successes changes nothing, and Ctrl+C (exit 130) is not a failure.
- Sequence. What you usually run next. Recall looks at the newest 500 runs of this shell's last command and at what followed each within 10 minutes; anything seen there at least twice is boosted, up to ×3. After
git add -A,git commitnow comes before thegit statusyou run far more often. Turn this one off alone with Sequence Boost insuv settingsorsearch.sequence_boost = false.
These are fixed, documented rules rather than a learned model, and two guarantees hold: no boost lifts a command over a closer textual match, and Ctrl+S switches to Recent rank, which orders exactly as before. The statistics are read on the first keystroke of a recall session and reused; that first keystroke cost 11 ms more for a one-letter query in the Commands view of a 100,000-command history, and later keystrokes cost what they did. The search docs have every rule.
What is not measured yet is whether the ranking is better. Suvadu's benchmark measures that every match is found and how fast, not whether the one you wanted is first. That is the honest gap in this section.
Why it failed, and what fixed it last time
Suvadu has always recorded that a command failed. It has never recorded what it printed, and the MCP failure tools said so in every response. 0.5.1 changes that in a deliberately narrow way.
Keep Failure Output is an opt-in setting, off by default ([capture] failure_output = true). With it on, a command that exits non-zero keeps the last 4 KB it printed, but only where Suvadu already has that output:
| Path | Output kept |
|---|---|
| Claude Code | the PostToolUseFailure error text |
| Cursor | afterShellExecution's output |
| Codex | a PostToolUse response's output, when it also carries an exit code |
| OpenCode | the bash tool's output, sent by the plugin |
suv wrap -- cmd | the command's stderr, passed through to your terminal unchanged |
| Commands you type in Zsh or Bash | nothing |
The last row is a design decision, not a gap to be filled later: capturing a terminal's output means standing between the program and the terminal, which breaks anything interactive. Escape codes and progress redraws are stripped, the text is redacted with the same rules as the command before it is cut, and an excluded command keeps nothing. Recall's detail pane shows it under an Output row, and what_failed, command_status and learn_from_failures show its last three lines, marked as the command's own text rather than instructions — because what a program printed can itself be written to look like instructions.
The second half needs no setting. For any failed command, recall now says what fixed it before, most common first:
Last time this failed, you fixed it with: npm install, then reran it (3×) Nothing new is stored for this. When you select a failed command, Suvadu takes the newest 50 failures of exactly that command and, after each, looks at the next 20 commands of the same session, in the same directory, within 15 minutes: a successful similar command (gti status → git status, or the same program with the same first argument) is the fix, or, if you simply reran it, the setup step that passed before the rerun. Looking around in between (cd, ls, git status, an editor) is skipped. Each lookup took 0.6–0.7 ms on a 100,000-command history. It is an inference from what ran next, never an observed cause, and the MCP tools label it fixed before (inferred).
What your AI agents actually changed
The question people ask after an agent session is rarely “which commands ran?” It is “what did it change?” Before 0.5.1, the MCP what_changed tool answered by reading a directory's recent commands and inferring their likely effect — a mv moves something, an npm install touches package.json — and it marked the answer as inferred.
Now each Claude Code, Codex, OpenCode or Cursor session working in a git repository gets a git snapshot at its first prompt and at each turn end: the commit and branch checked out, and which paths git status reports as staged, unstaged, untracked, renamed, deleted or conflicted. Paths only, never contents or diffs. Given a session id, what_changed compares the two snapshots and answers with provenance: observed: the HEAD movement, the commits made in between with their subjects, and every path whose status changed. In suv sessions, the timeline header says Changed: 3 files, 1 commit, and the handoff scaffold lists each path under Changed-file evidence.
Three caveats are part of the answer, and Suvadu states them in it:
- “Changed in the repository” is not “changed by the agent”. If you edited a file in your editor while the agent ran, it is in the list too.
- A path already changed at the start, with the same status at the end, is not listed, even if it changed again in between.
- Files outside the repository are invisible to
git status, and so to this.
The cost lands on the agent's hook, not your prompt: git runs only at those two moments, never per command, without taking the index lock, with a 2-second limit. In a 10,000-file repository a snapshot added about 30–35 ms to the hook that took it, nearly all of it git status; a later prompt added under 1 ms. At most 500 paths are kept, with your exclusions applied and the rest redacted. Turn it off with Record Agent Git Snapshots in suv settings. For Claude Code, Codex and OpenCode there is nothing to reinstall; the existing hooks take the snapshots.
Cursor conversations are sessions now
Cursor used to be the odd one out: Suvadu recorded its commands and prompts, but not its conversations. suv init cursor now also registers Cursor's afterAgentResponse and stop hooks, so each conversation is a session (cursor-<conversation_id>) with its prompts, each turn's final response, how the turn ended and the model, and the conversation's commands under the turn they ran in. It shows up in suv sessions and the MCP session tools like the others.
The limits are Cursor's, and Suvadu shows them rather than papering over them. Cursor's hooks report no token usage, so a Cursor session's tokens read unavailable, never zero. Cursor sends only a turn's final response, so intermediate messages, thinking and non-shell tool calls are not captured. And events carry no timestamps, so they are timed when the hook receives them. Rerun suv init cursor and restart Cursor to start capturing them.
Antigravity's agent commands are recorded
Antigravity's agent runs each command as zsh -c '<command>' from its language server, a shell that never reads ~/.zshrc, so Suvadu's shell hook never saw those commands. suv init antigravity used to only check for that hook. Now it adds a marked block to ~/.zshenv, backing up the previous file first. The block loads a small capture script only in Antigravity's agent shells, and the script records each command with its exit code, duration and directory when the shell exits. Every other shell skips the block.
The limits: zsh only, no prompts (Antigravity has no hooks for them), and a command started with exec or killed by a signal is not recorded. Run suv init antigravity and restart Antigravity to start capturing.
Smaller things worth knowing
- Bookmarks with placeholders. Save
ssh {{user:root}}@{{host}}; picking it insuv bookmarksopens a small popup for each value, andsuv bookmarks get <label> --set host=db1prints it filled. Values are inserted verbatim and never stored. The new MCPlist_snippetstool lists them, so an agent can fill a template instead of retyping it. Bookmarks. - Team skills in the repository. Commit Markdown skills to
.suvadu/skills/. Because they arrive with whatever you clone, they are untrusted untilsuv skills trustrecords a hash of each file; only then doessuv skills syncwrite them into your agents' files, and a file changed since (after a pull, say) stops syncing until you trust it again. Over MCP, an untrusted skill is listed by name and path only. Shared Skills. - Every command keeps its git branch. Read from the repository's own files with no
gitprocess, in about 10–20 µs, and shown as a Branch row in recall's detail pane. Git branch capture. - A zsh-autosuggestions strategy.
ZSH_AUTOSUGGEST_STRATEGY=(suvadu history)suggests what Up would recall. That lookup is now an index seek:suv get --prefixfell from 12–25 ms to 5–8 ms p95 at 100,000 commands, most of it starting the process. - Risk rules read every command of a line.
suv guardandassess_risksplit&&,;, pipes,sudo,bash -c,ssh hostand$(…)and rate each part, catch every spelling of recursivermand force push, add rules forterraform destroy,kubectl deleteand SQL deletes withoutWHERE, and stop blockingchmod 755orpip install -r requirements.txt. - Bash records the whole line. The Bash hook used to store only the last simple command, so
cd /tmp && lsbecamels. - A project
.suvadu.tomlcan only tighten privacy now: it can no longer turn redaction off or recording back on, and its exclusions add to yours. - Plenty of fixes to capture that used to fail quietly: commands over 64 KB, long OpenCode sessions, megabyte tool outputs, torn transcript lines, forked Codex sessions, a config file that doesn't parse. The changelog lists them.
Upgrading
- Rerun
suv init cursorandsuv init opencodeif you use them, then fully quit and reopen each. Claude Code and Codex need nothing new. - Run
suv init antigravityif you use Antigravity, then restart it. It now edits~/.zshenv, keeping a backup. - Open new terminals. The shell hook is generated when each shell starts, so open shells keep the old one until restarted.
- The database upgrades itself on the first run, one process at a time, so several shells opening at once wait rather than fail.
- A bookmark with a literal
{{word}}now asks for a value when you pick it. Write it as\{{word}}to keep it literal. cargo install suvaduneeds Rust 1.95 or newer.
See Update & Uninstall for the commands for each install method.
What 0.5.1 still doesn't do
- No output from your own shell. Failure output comes only from agent hooks and
suv wrap, and only for failed commands. - No Cursor token counts. Cursor's hooks don't report them.
- No Fish or Nushell. Suvadu records Zsh and Bash only.
- No sync. Moving history between machines is still an explicit
suv exportandsuv import; agent sessions and their snapshots are not exported. - Unmeasured: ranking quality, cold start, peak memory, and any comparison with another tool.
Try Suvadu with your own history
For macOS or Linux with Zsh 5.1+ or Bash 4+. Install the CLI, enable your shell, then try Ctrl+R.
1. Install the CLI
With Homebrew:
brew tap AppachiTech/suvadu && brew install suvadu Other install methods
Install script. Works on macOS and Linux, Intel and ARM. Downloads a binary for your operating system and architecture, verifies its checksum, and installs to /usr/local/bin, using sudo only if that directory is not writable. End the command with bash -s -- --user instead to install into ~/.local/bin without sudo.
curl -fsSL https://downloads.appachi.tech/suvadu/install.sh | bash Cargo. Requires Rust 1.95 or newer. Compiles from source, which can take a few minutes; cargo binstall suvadu installs the pre-built binaries instead. Make sure ~/.cargo/bin is on your PATH.
cargo install suvadu 2. Enable recording in your shell
Homebrew and Cargo do not activate shell recording, and the install script adds the hook line only if you answer y when it offers. Open the instructions for the shell you use. If another history tool owns Ctrl+R, disable its history binding before enabling Suvadu; keep your existing history data.
Zsh setup
Add this line once to ~/.zshrc. Keep the existing line if it is already present:
eval "$(suv init zsh)" Save the file, then activate it in your Zsh terminal:
source ~/.zshrc Bash setup
Add this line once to ~/.bashrc. Keep the existing line if it is already present:
eval "$(suv init bash)" Save the file, then activate it in your Bash terminal:
source ~/.bashrc Bash 4+ is required. For a login shell, make sure ~/.bash_profile sources ~/.bashrc, or use the startup file your shell reads. Shell setup details.
3. Check setup and try a search
suv doctor Run echo hello, press Ctrl + R, and search for hello. Select a result to put it in your prompt; press Enter to run it. Agent and MCP setup are optional.
Import existing Zsh or Bash history, an Atuin database (read-only), or a Suvadu JSONL export.
Already on Suvadu? Run suv update, brew upgrade suvadu or cargo install suvadu, depending on how you installed it, then follow the upgrade steps above.
Builder of Suvadu. Writes Rust, thinks about shell history more than most people, and believes developer tools should be local-first.