Story 7 min read

Why I Built Suvadu

The story behind building a database-backed shell history replacement in Rust — and why standard Zsh history was not enough.

Madhubalan Appachi Madhubalan Appachi · Published · Updated

It started with a production incident at 2 AM. I remembered running a specific kubectl command three days earlier that fixed the exact same problem. I knew the command existed somewhere in my history. I pressed Ctrl+R, typed fragments, scrolled through hundreds of irrelevant matches, and eventually gave up. I retraced my steps manually, wasting 40 minutes on something I had already solved.

That night, I decided standard shell history had to go.

The Problem with Shell History

Your shell's built-in history is fundamentally text-file based. Zsh's extended history can include timestamps and durations, and its options can control sharing and duplicate handling, but it does not natively attach fields such as working directory, exit code, session, or executor to every entry.

Think about what that means:

  • No context. You can see that you ran docker compose up -d, but not which directory you were in, whether it succeeded, or how long it took.
  • No filtering. You can't search for "all failed commands from last week" or "commands I ran in the payments repo."
  • No indexing. Every search is a linear scan through a flat file. With 50,000 commands, that starts to lag.
  • Limited duplicate handling. Shell options can suppress some duplicates, but they do not turn repeated executions into structured, queryable records.
  • No source tracking. Was that command typed by you, executed by your IDE, or run by an AI assistant? History doesn't know, and it doesn't care.

I had been working around these limitations for years with various hacks: shell aliases to grep history, custom scripts to deduplicate, even a messy Python wrapper. None of it felt right.

What I Wanted

I sketched out what an ideal shell history system would look like:

  1. Database-backed. Every command stored in a proper database with metadata: working directory, exit code, duration, timestamps with millisecond precision.
  2. Fast search. Indexed queries that feel instant, even with a large history.
  3. Minimal overhead. Recording a command should not make the shell feel slower. Shell responsiveness is non-negotiable.
  4. Fully local. No cloud sync, no accounts, no telemetry. My command history is deeply personal and often contains sensitive information.
  5. Executor tracking. In an age of AI coding assistants and IDE terminals, I wanted to know who ran each command, not just what was run.

Why Rust

The performance requirement alone ruled out scripting languages. Every millisecond matters in a shell hook that fires on every single command. Rust gave me native speed, memory safety, and the ability to statically link SQLite directly into the binary. One binary, no dependencies, no runtime.

SQLite with WAL (Write-Ahead Logging) mode turned out to be the perfect storage engine. It gives you concurrent reads while writes happen, and synchronous=NORMAL avoids a disk flush on every commit. The database is a single file that lives at ~/Library/Application Support/tech.appachi.suvadu/history.db on macOS.

Review note, 19 September 2026 (Suvadu 0.4.1): these were design goals, not measured results. We have not yet published reproducible benchmarks for recording or search latency; see how recording works for what has and hasn't been measured.

Building the Hooks

The trickiest part was making the Zsh integration invisible. Suvadu hooks into two Zsh lifecycle events:

  • preexec fires just before a command executes. We capture the command string and record the start time using Zsh's native $EPOCHREALTIME — pure shell arithmetic, no subprocesses.
  • precmd fires just before the next prompt appears. We grab the exit code from $?, compute duration, detect the executor, and fire off suv add with all the metadata.

The executor detection was the feature I was most excited about. By inspecting environment variables at capture time, Suvadu can tell whether a command was run by a human in a terminal, by Claude Code, by Cursor, by a CI pipeline, or by a dozen other tools. This turns your history from a flat log into a rich audit trail of everything happening in your development environment.

Getting executor detection right, though, was harder than I expected. Environment variables are surprisingly inconsistent across tools — some set them, some don't, some set them only in certain contexts. CI detection had its own edge cases: different runners expose different variables, and some tools nest inside others in ways that make the "true" executor ambiguous. It took several iterations and a lot of real-world testing before the heuristics became reliable.

What Suvadu Does Today

What began as a frustrated evening of hacking has grown over years into something I use every day and can no longer imagine working without. The interactive TUI search became the centerpiece — you can filter by date range, tags, exit codes, executors, and working directory. Search requires each typed word to appear as a literal substring, then ranks closer matches. Arrow-key navigation returns matching commands with recency as the primary order.

Beyond search, Suvadu now handles:

  • Session tagging to organize commands by project or workspace.
  • Bookmarks and notes for commands you want to remember with context.
  • A stats dashboard with heatmaps, hourly distribution charts, and top commands analysis.
  • Alias suggestions that analyze your history and recommend shell aliases.
  • Import/export in JSONL and CSV formats, plus direct import from ~/.zsh_history.
  • Native Claude Code integration via PostToolUse hooks.

Most recently, Suvadu gained a built-in MCP server, so connected AI clients can query selected shell-history context. The server reads the local database, while any result it returns is available to the client and may be handled by that client's AI provider.

The Name

Suvadu (pronounced "soo-va-doo") means "trace" or "footprint" in Tamil. Every command you run leaves a trace. Suvadu makes sure you never lose it.

Your history is the story of your work. It deserves better than a text file.

If you're tired of losing commands, try Suvadu. It stores your history in a local database and makes it easier to search and review.

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.