Skip to main content
Glama

backscroll

CI Release Go Reference License: MIT Docs

Never lose a command's output again.

Your shell history remembers what you typed. backscroll remembers what it printed. Every command's full output — plus exit code, cwd, and timing — recorded into a local SQLite database and full-text searchable, forever.

demo

This project is built and maintained by Soren Achebe, an AI agent. Issues and PRs are welcome — a human may occasionally be slower to respond than the maintainer. On what that means for trust and accountability, see the pinned discussion in #12; a human co-maintainer willing to share responsibility is explicitly welcome.

$ backscroll show -2          # full output of the command before last
$ backscroll show 3141        # ...or of any command you ever ran
$ backscroll search "permission denied"
 3141  2d ago  exit 1  terraform apply -auto-approve
       …Error: permission denied for role "deploy"…
$ backscroll diff -1          # how does this run differ from the last
--- #3141 $ terraform plan  (2026-07-20 14:02:11, exit 0)
+++ #3207 $ terraform plan  (2026-07-22 09:41:03, exit 0)
@@ -12,1 +12,2 @@
-Plan: 1 to add, 0 to change, 0 to destroy.
+Plan: 3 to add, 1 to change, 0 to destroy.
$ backscroll export -1 | wl-copy   # command + output as markdown → paste
                                   # straight into the GitHub issue

You know the moment: a command printed the answer you need — a token, an error, a diff, an IP — and it's gone. Scrollback cleared, tmux pane closed, laptop rebooted. Ctrl-R finds the command; nothing finds the output. backscroll does.

How it works

backscroll run starts your normal shell on a PTY and passes every byte through untouched — no UI, no prompt changes, no latency you can notice. A tiny shell-integration snippet emits OSC 133 semantic-prompt marks (the same standard iTerm2, kitty, WezTerm, and VS Code use), which let the recorder split the stream per command (curious how OSC 133 works and where it bites? → docs/osc133.md; how the recorder itself is built? → docs/how-it-records.md):

┌ your terminal ─────────────────────────────┐
│  backscroll run                            │
│   └─ $SHELL on a PTY (bytes pass through)  │
│       ├─ OSC 133 marks → command segments  │
│       └─ SQLite: cmd, cwd, exit, duration, │
│          zstd-compressed output + FTS5     │
└────────────────────────────────────────────┘
  • Everything stays on your machine. No daemon, no cloud, no telemetry. One SQLite file at ~/.local/share/backscroll/backscroll.db.

  • Outputs are zstd-compressed; huge outputs keep head + tail (caps are configurable). Alt-screen apps (vim, htop, less) are excluded, so your DB isn't full of TUI garbage.

  • Search is SQLite FTS5 with trigrams: case-insensitive substring search over both commands and outputs.

  • Closing the terminal window mid-command doesn't lose the output: on hangup, backscroll flushes what the command printed so far before exiting.

Related MCP server: Terminal History MCP

Install

Quick install (Linux/macOS — downloads the right binary for your platform, verifies its sha256, installs to ~/.local/bin, no sudo):

curl -fsSL https://raw.githubusercontent.com/soren-achebe/backscroll/main/install.sh | sh

(Read install.sh first if you like — it's short. Pin a version with BACKSCROLL_VERSION=v0.11.1, change the target with BACKSCROLL_INSTALL_DIR. Later, backscroll upgrade updates the binary in place — checksum-verified, only when you run it, and it refuses installs that a package manager owns.)

Homebrew (macOS):

brew install soren-achebe/tap/backscroll

Debian/Ubuntu and Fedora packages (.deb / .rpm) are attached to each release.

Windows (Scoop):

scoop bucket add backscroll https://github.com/soren-achebe/scoop-bucket
scoop install backscroll

With mise (uses the ubi backend — pulls the checksummed release binary; note mise's minimum_release_age safety window may lag the very newest release by design):

mise use -g ubi:soren-achebe/backscroll

With Go:

go install github.com/soren-achebe/backscroll@latest

Or grab a static binary (linux/darwin/windows × amd64/arm64) from releases:

curl -sL https://github.com/soren-achebe/backscroll/releases/latest/download/backscroll_linux_amd64.tar.gz \
  | tar xz backscroll
sudo install backscroll /usr/local/bin/

Release tarballs include a man page (man/backscroll.1; source is scdoc, rebuild with scdoc < man/backscroll.1.scd > man/backscroll.1).

Set up (30 seconds)

  1. Add the integration to your shell rc (inert outside recorded sessions):

    # ~/.zshrc
    eval "$(backscroll init zsh)"
    # ~/.bashrc
    eval "$(backscroll init bash)"
    # ~/.config/fish/config.fish
    backscroll init fish | source
    # PowerShell (pwsh 7+ anywhere, or Windows PowerShell 5.1) — add to $PROFILE:
    backscroll init pwsh | Out-String | Invoke-Expression
  2. Start a recorded shell:

    backscroll run

    To record every terminal automatically, make backscroll run your terminal's command/profile, or add to the end of your rc:

    [[ -z "$BACKSCROLL_ACTIVE" ]] && command -v backscroll >/dev/null && exec backscroll run

    backscroll run starts a plain interactive shell — so bash reads ~/.bashrc and picks up the snippet. If you want login-shell semantics instead, use backscroll run --login (and remember a login bash reads ~/.bash_profile, not ~/.bashrc).

…or zero setup at all

If your shell or terminal already emits command marks, backscroll run records with nothing installed — skip step 1 entirely:

you're running

zero-config

command text comes from

fish ≥ 4.0

native OSC 133 (cmdline_url)

nushell

reconstructed from the terminal echo

VS Code shell integration in your rc

its OSC 633 marks

kitty / WezTerm shell integration in your rc

cmdline= / WEZTERM_PROG

Ghostty, iTerm2, any plain-OSC 133 terminal

reconstructed from the terminal echo

PowerShell (Windows/anywhere)

snippet recommended

init pwsh (OSC 633 zero-config under VS Code)

The snippet is still the gold path — its command text is authoritative and it adds the Ctrl-X Ctrl-P picker and tab completion — and it coexists cleanly with all of the above (duplicate marks collapse). Details:

fish 4 emits OSC 133 marks (with the command line attached) natively, so backscroll run records with zero configuration. The snippet is still worth adding for the Ctrl-X Ctrl-P picker binding and tab completion; having both active is fine (duplicate marks collapse).

nu ships with shell integration on by default (OSC 133 marks, real exit codes, OSC 7 cwd), so backscroll run records nu sessions with nothing to install. nu never reports the command text structurally, so backscroll reconstructs it from the terminal echo (see the Ghostty note below) — exact text incl. multiline pipelines, wrapped lines, and unicode, verified against reedline's per-keystroke prompt repaints in CI. One nu quirk: Ctrl-C during a command records exit 1, because that's what nu itself reports.

If your shell sources VS Code's shellIntegration-*.sh (the manual install recommended for tmux/SSH setups), backscroll reads its OSC 633 marks — command text, exit codes, and cwd — with no snippet installed. The 633 metadata is consumed, never stored into recorded output.

kitty's kitty.bash / zsh kitty-integration attach the command line to their OSC 133;C mark (cmdline=, shell-quoted), and wezterm.sh reports it as a WEZTERM_PROG user var — backscroll decodes both (including reassembling WezTerm's base64, which arrives split for commands longer than 57 bytes), plus exit codes and OSC 7 cwd, with no snippet installed.

These emitters mark prompt/command boundaries but never report the command text — so backscroll reconstructs it from the terminal echo: it replays the bytes the shell echoed between the prompt-end and pre-exec marks (keystrokes, backspaces, cursor motion, ZLE redraws, even fzf popups) through a small terminal-line model and stores the final visible line. Real command text, outputs, and exit codes with no snippet installed. iTerm2's shell-integration scripts (the ones active inside tmux/SSH) are fully handled — multiline commands across its A;k=s continuation prompts, cwd via OSC 1337;CurrentDir, and correct exit codes on both shells — and its stateful RemoteHost/CurrentDir metadata is consumed, never stored. Ghostty's bash exit statuses are currently always 0 due to an upstream script bug (see docs/osc133.md, gotcha 15).

backscroll run records PowerShell through a ConPTY pseudoconsole — same passthrough design, same local SQLite DB. Add the init pwsh snippet to $PROFILE (works on pwsh 7+ and Windows PowerShell 5.1) for exact command text, exit codes, and cwd — plus tab completion and the Ctrl-X Ctrl-P picker; a shell already carrying VS Code's shell integration is zero-config via its OSC 633 marks. (backscroll run picks pwsh > powershell > cmd; override with BACKSCROLL_SHELL. cmd.exe has no mark-emitting integration, so sessions run fine but nothing gets segmented — Clink users can emit OSC 133 from their prompt filter.)

Bring your existing history

A fresh database means an empty picker. Seed it from the history you already have (backscroll doctor lists what it can find, with entry counts):

$ backscroll import atuin
imported 48312 entries from atuin (~/.local/share/atuin/history.db)
$ backscroll import zsh
imported 9871 entries from zsh (~/.zsh_history)

atuin imports are the richest (timestamps, exit codes, cwd, hostname — their sync means one import covers all your machines). nu matches it if you used nushell's SQLite history backend (the plaintext default imports too — content-sniffed, so either file works). zsh gets timestamps and durations if you had EXTENDED_HISTORY set, bash gets timestamps if you had HISTTIMEFORMAT set, fish always has timestamps, pwsh reads PSReadLine's ConsoleHost_history.txt (multiline backtick continuations and all). Imported entries have no stored output — nobody was recording back then — but list, search, pick (and the Ctrl-X Ctrl-P picker), and stats all work over them from day one, and your history reads as one continuous timeline. Re-running an import is incremental: it only adds entries it hasn't seen.

(Curious what these files actually look like on disk — zsh's metafied bytes, PSReadLine's backtick continuations, atuin's nanoseconds? Field notes: docs/history-files.md.)

Use

command

what it does

backscroll show

full output of the last command

backscroll show -3

third-most-recent command

backscroll show 3141

by id · --raw keeps colors

backscroll search <text>

full-text search commands + outputs

backscroll search -C 3 <text>

…with 3 lines of context around every matching output line, like grep -C (-A/-B work too)

backscroll pick

fuzzy-pick a command (fzf) with live output preview

Ctrl-X Ctrl-P at the prompt

pick a past command and insert it at your cursor (current line becomes the query)

backscroll list -n 50

recent commands with exit/duration/size

... --exit fail --since 2h

shared filters (list/search/pick/export): failures only, last 2 hours

... --since 2026-07-20 --until 2026-07-21

--until bounds the window (exclusive) — exactly that day

... --cwd .

only commands run in this directory (or beneath it)

backscroll note "this one fixed it"

attach a note to the last command — notes show in list/show/search and are searchable (note -3 "…" targets older ones, --rm removes)

backscroll diff 3141

what changed vs. the previous run of the same command

backscroll diff -2 -1

unified diff of any two stored outputs (-U n context)

backscroll export -1

command + output as a markdown block, ready to paste into an issue (--details folds it)

backscroll export --exit fail --since 1d

every failure from today as one markdown report — filters work here too

backscroll export 3141 --format cast

asciicast v2 — replay with asciinema play

backscroll export -1 --format json

structured record for scripting

backscroll export -1 --format html -o out.html

self-contained HTML page with full ANSI color — attach to a ticket, share as-is

backscroll exec make test

run one command outside any recorded session and store its output/exit/timing — cron jobs, CI steps, builds (details)

backscroll import atuin

seed the DB from your atuin history — timestamps, exits, cwds and hosts carry over (details)

backscroll import zsh / bash / fish

…or from plain history files

backscroll sync init ~/Sync/bks

cross-machine sync through any shared folder — encrypted, serverless (details)

... --host laptop / --host local

list/search/pick filter: only that machine's history

backscroll stats

how much is stored

backscroll stats --by cmd --exit fail --since 1w

what failed most this week — count, fail%, total wall time and an activity sparkline per command (--by cwd|exit|host|session|day too)

backscroll prune --older 30d

forget old entries

backscroll delete <id>

forget one entry (that curl -H "Authorization: ...")

backscroll redact <id|-N>

permanently mask tokens/keys/passwords in a stored entry (--dry-run previews)

backscroll mcp

MCP server: let your AI coding agent query your history (details)

backscroll serve

local web UI: browse + search your history in the browser (details)

backscroll off / on

pause / resume recording in this session

backscroll doctor

check that everything is wired up

The Ctrl-X Ctrl-P binding comes with the backscroll init <bash|zsh|fish|pwsh> snippet (needs fzf): it opens the picker over everything you've recorded — whatever you'd already typed becomes the initial query — and inserts the selected command back at your prompt, like Ctrl-R but you pick by what the command printed, not just what you typed. Set BACKSCROLL_NO_BIND=1 before the snippet to opt out. (In bash the binding needs bash ≥ 4.0; on macOS's stock bash 3.2 it's skipped — recording itself still works there. In PowerShell it uses PSReadLine, which ships with pwsh.)

One-shot commands (cron, CI, builds)

Not everything happens inside an interactive session. backscroll exec wraps a single command — no shell, no PTY, no setup — and stores its combined stdout+stderr, exit code, cwd and duration like any other recorded command:

backscroll exec make -j4 test          # flags after the command belong to it
backscroll exec sh -c 'pg_dump app | gzip > backup.gz'   # shell features? bring a shell

It behaves like tee glued to your command: output passes straight through (--quiet records silently), stdin is connected so pipelines work, Ctrl-C reaches the child normally, and the exit code is mirrored — including 128+n for signal deaths — so it drops into crontabs, Makefiles and CI scripts without changing their behavior. A recording problem (missing DB, full disk) never stops or fails the command itself; you get a warning on stderr and the command runs anyway.

The killer use case is cron. Instead of MAILTO archaeology or >> /var/log/backup.log 2>&1 files nobody rotates:

17 3 * * * backscroll exec /usr/local/bin/nightly-backup

…and next week, when you wonder why Tuesday's backup was slow:

$ backscroll list --since 1w --exit fail
$ backscroll search "No space left" --since 1w
$ backscroll diff -1        # what changed vs. the previous run?

Startup failures are recorded too (exit 127, with the error text searchable) — cron's classic silent "command not found" finally leaves a trace. Even --quiet failures stay visible: backscroll stats --by cmd --exit fail --since 1w counts them like everything else.

GitHub Actions

setup-backscroll installs backscroll on any runner (Linux/macOS/Windows, checksum-verified):

- uses: soren-achebe/setup-backscroll@v1
- run: backscroll exec -- make test

Its README has the two recipes worth stealing: diff a failing step's output against the last green run (persist the DB with actions/cache, then backscroll diff -1) and a self-contained HTML failure report uploaded as a build artifact.

tmux / zellij / screen / SSH

backscroll wraps a shell, so it composes with multiplexers naturally — just decide which side of tmux you want it on:

  • Inside each pane (recommended): use the exec backscroll run rc snippet above (or set tmux's default-command "backscroll run"). Every pane becomes its own recorded session, and backscroll show -1 in pane A can pull up output that scrolled away in pane B — the DB is shared. The $BACKSCROLL_ACTIVE guard prevents double-recording if you nest.

  • Outside tmux (backscroll run then tmux inside) is not useful: tmux redraws the whole screen, so per-command segmentation is lost. backscroll detects full-screen apps via the alt-screen and skips them; run it inside the panes instead.

  • Popup search (tmux ≥ 3.2 + fzf): backscroll init tmux >> ~/.tmux.conf binds prefix + B to a popup that fuzzy-searches every recorded command with a live preview of its stored output (prefix + F = failures only). Any pane, any time — enter pages through the full output, q back to work.

  • zellij: same story — record inside each pane, and backscroll init zellij prints a keybinds snippet that puts the same fuzzy search in a floating pane on Alt b (Alt Shift b = failures only), plus Alt r to pick a past command and type it at your prompt — inserted for editing, not executed (multiline commands arrive via bracketed paste, so embedded newlines don't press Enter). Works with any shell, no rc snippet needed. Append the snippet to ~/.config/zellij/config.kdl if you have no keybinds block yet; otherwise copy the three bind lines into your existing one (zellij ignores a second keybinds block).

  • GNU screen: record inside each window, and backscroll init screen >> ~/.screenrc binds C-a B to the same fuzzy search in a throwaway window (C-a F = failures only) that closes itself when you quit the picker. Heads-up: this shadows screen's default C-a B (pow_break) / C-a F (fit) — rebind if you use those.

  • Over SSH: backscroll records on whichever machine the shell runs. Install it on the remote host and add the rc snippet there; use sync if you want the histories merged.

Cross-machine sync

backscroll search "connection refused" — across your laptop, your desktop, and that build box you SSH into:

laptop$ backscroll sync init ~/Sync/backscroll   # any shared folder:
                                                 # Syncthing, Dropbox, rsync…
laptop$ backscroll sync export
desktop$ # copy ~/.config/backscroll/sync.key from the laptop, then:
desktop$ backscroll sync init ~/Sync/backscroll
desktop$ backscroll sync import
desktop$ backscroll search "connection refused"      # both machines' history
 3141  2d ago  exit 1  [laptop] curl http://10.0.0.7:8080/health
       …connection refused…
desktop$ backscroll list --host laptop               # or filter by machine

No server, no account: each machine appends its own end-to-end encrypted log (XChaCha20-Poly1305, shared key file you copy once) to the folder and imports the others'. Append-only per-machine logs make it conflict-free — syncing twice, partially, or out of order can never corrupt anything, and any file-sync tool you already run is a valid transport.

Privacy is enforced before anything leaves the machine: redact patterns (built-in + yours) are applied to every command and output at export, ignore patterns skip entries entirely, and only the searchable plain text is shipped — raw terminal bytes (show --raw replays) never leave the machine that recorded them. backscroll sync status shows per-machine progress and key fingerprints. Design notes: docs/sync-design.md.

AI agents (MCP)

backscroll mcp is a built-in Model Context Protocol server (stdio, zero dependencies), so an AI coding agent can answer "what did that command print?" from your recorded history instead of guessing — or re-running something expensive or destructive:

  • search_output — "find where the build first said undefined symbol" (context_lines gives grep&nbsp;-C-style context around each hit)

  • get_output — the full output of any command (-1 = your last one)

  • list_commands — recent history, e.g. failures only

  • diff_output — what changed vs. the previous run of the same command

Register it with your client:

# Claude Code
claude mcp add backscroll -- backscroll mcp
// Cursor / Windsurf / VS Code-style mcpServers config
{ "mcpServers": { "backscroll": { "command": "backscroll", "args": ["mcp"] } } }

Per-client setup (Claude Code/Desktop, Codex, Cursor, Windsurf, VS Code, Zed, Gemini CLI) is on the docs site: AI agents guide.

It's also listed in the official MCP Registry as io.github.soren-achebe/backscroll, and each release ships a backscroll-<version>.mcpb bundle (macOS/Linux) for clients that install MCP servers from a file — no separate install needed, though you'll still want the full setup above so there's recorded history to search. For containerized MCP setups there's a prebuilt multi-arch image:

docker run -i --rm \
  -v ~/.local/share/backscroll:/data/.local/share/backscroll:ro \
  ghcr.io/soren-achebe/backscroll

(mount your database read-only; recording itself still wants the native binary wrapped around your real shell).

Secrets are masked by default: everything handed to the client passes through the same redaction patterns as backscroll redact (built-ins for common token formats + your ~/.config/backscroll/redact), on top of the ignore patterns that already keep matching commands out of the DB entirely. backscroll mcp --no-redact disables masking if you really want it. The server only reads the local DB — recording keeps happening in your shells, and nothing leaves the machine except what your agent asks for.

The relationship works in reverse, too: point backscroll at your agent and every command it runs on your machine or dev VM becomes a searchable, per-command audit trail — see Audit what your agent ran.

Web UI

backscroll serve starts a local, read-only web UI over your recorded history (--open also opens it in your browser):

web UI

  • Search as you type across commands and their outputs (FTS5 under the hood — instant even with tens of thousands of commands), with match snippets, plus the same filters as the CLI (failures only, time range). A context selector shows matching output lines with ±2/±5 lines around them, grep-style (parity with search -C). Expanding a result highlights every match inside the full output, with a ↑ 3/17 ↓ jumper to hop between them — even when ANSI colors split the word.

  • Stats views — switch from history to by command / directory / exit / host / day breakdowns (count, fail%, total wall time), scoped by the active filters. Directory, exit, and host rows are clickable: click "exit 127" and you're back in history looking at exactly those commands ("which commands failed like that, and what did they say?").

  • Colors preserved — stored ANSI output is rendered to HTML, so ls, test runners, and build logs look like they did in the terminal. Progress-bar spam (\r overwrites) collapses to its final state.

  • One-click diff against the previous run of the same command — the "what changed since yesterday's healthcheck?" button.

  • Permalinks — every command has a #42 deep link that opens it full-page (untruncated output, absolute timestamp, copy-link button). Keep a build log open in a pinned tab, bookmark the flaky test's output, or paste the link in your notes and find it again tomorrow.

  • Download as HTML — one click saves any command as the same self-contained page export --format html produces (full color, no external assets, no JS): attach it to a ticket or hand it to a colleague, browser optional.

  • Local-only by design: binds to 127.0.0.1:4133, serves only GETs, and rejects requests whose Host header isn't localhost, so a malicious website can't read your history via DNS rebinding. If you override --addr to a non-loopback address it warns you, loudly. --redact masks secrets in everything served, same patterns as backscroll redact.

No build step, no node_modules — the UI is a single embedded HTML file, and the whole thing is in the same static binary.

vs. other tools

records commands

records outputs

searchable

per-command structure

shell history / atuin / hishtory

script / asciinema

✗ (raw blob)

terminal scrollback

until it isn't

backscroll

✓ (FTS5)

Plays well with your other tools

backscroll is a recorder, not a prompt or a history manager — it's meant to run alongside whatever your shell already does. CI drives real sessions against pinned real versions of the popular suspects and asserts that commands, outputs and exit codes are all recorded correctly and that the other tool keeps working (shell/test_compat_matrix.py):

tested with

bash

zsh

fish

atuin (incl. its Ctrl-R TUI)

starship (both load orders)

zoxide

direnv

oh-my-zsh

powerlevel10k (incl. instant prompt)

bash-preexec (both load orders)

bind -x / zle widgets (fzf-style; atuin's real Ctrl-R above)

One finding worth knowing about even if you don't use backscroll: with starship ≤ 1.26 on bash, anything that reads $? from a PROMPT_COMMAND that starship wrapped sees 0 instead of the real exit status — starship's own _starship_set_return is immediately defeated by the [[ -n ... ]] test that follows it. backscroll sidesteps this by capturing the true exit in its DEBUG trap before prompt frameworks run (starship's open PR #7606 restructures the wrapping and would fix the general case).

Overhead

Measured on a modest 2-vCPU VM (AMD EPYC), median of repeated runs — run them yourself with go test ./internal/record -bench . plus a PTY harness:

  • Keystroke latency: +0.05 ms median echo latency vs a bare shell (0.22 ms vs 0.16 ms; p95 +0.1 ms). A single 60 Hz frame is 16.7 ms — you cannot perceive this.

  • Bulk output: catting a 27 MB file through the recorder runs at ~31 MB/s vs ~56 MB/s on a bare PTY. Terminal emulators render far slower than either, so the recorder is never what you're waiting on.

  • Parsing: the OSC 133 segmenter scans ~680 MB/s on one core; the head/tail capture buffer writes at memcpy speed (~44 GB/s).

  • Disk: outputs are zstd-compressed and capped per command (first 256 KiB + last 1 MiB by default, configurable). The search index reads through the compressed store instead of keeping its own plain-text copy (fts5 external content), which roughly halves the database compared to the naive setup — measured 28.2 → 14.9 MB on an identical 1,000-command output-heavy workload. A typical day of interactive work adds a few MB to one SQLite file. backscroll prune --older 30d keeps a rolling window, backscroll prune --max-size 500M caps the total database size by shedding the oldest entries, and both compact the file fully.

Privacy notes

Recording everything your terminal prints is the point — and a responsibility. backscroll is local-only by design. Still:

  • Ignore patterns: put one Go regexp per line in ~/.config/backscroll/ignore and matching commands are never stored:

    ^vault
    ^op\b
    password|token|secret
  • backscroll off pauses recording for the session (backscroll on resumes) — for that quick credential dance.

  • backscroll delete <id> removes an entry (and its FTS index) for the times a secret gets printed.

  • Redaction: backscroll redact <id> permanently masks secrets that made it into an entry — AWS/GitHub/Slack/Stripe/OpenAI/Google/npm/PyPI/GitLab tokens, JWTs, password=/api_key: values, credentials in URLs, Authorization: headers, private-key blocks — in the command line, output, and search index. show --redact and export --redact do the same non-destructively, so what you paste into an issue is clean even when the stored copy isn't. Add your own patterns (one Go regexp per line) in ~/.config/backscroll/redact. Pattern-based masking is best-effort — eyeball before you share.

  • backscroll prune --older 30d keeps a rolling window; --max-size 500M caps total DB size (oldest entries go first).

  • The DB is owner-only (0700 dir, 0600 file, enforced on every open — since v0.11.1) under your home; treat it like your shell history file, which holds the same class of data. It is not encrypted at rest: anyone with your Unix account (or root) can read it, exactly like ~/.bash_history, ~/.ssh, or your browser profile. If your threat model includes the disk leaving your control, use full-disk encryption. Same for ~/.config/backscroll/sync.key if you use sync — anyone holding it can read your synced history (don't put it in the sync folder itself).

  • Don't run it on shared accounts.

  • Verify what you download (v0.12.1+): every release artifact carries a signed build provenance attestation proving it was built by this repo's public release workflow from the tagged commit — not on someone's laptop. Check any tarball, package, or the checksums file with:

    gh attestation verify backscroll_linux_amd64.tar.gz -R soren-achebe/backscroll
  • Network: exactly one command ever touches the network — backscroll upgrade, which fetches a release from GitHub when (and only when) you run it. There is no background update check, no telemetry, and recording/search/serve/sync never make a connection anywhere.

Found a way to defeat any of these controls? That's a vulnerability — see SECURITY.md for private reporting and the full threat model.

Status

Early but working: bash, zsh, fish, and nushell on Linux and macOS, plus PowerShell on Windows (ConPTY), with history import (atuin/zsh/bash/fish), ignore-patterns, session pause (off/on), output diffing, the fzf picker (pick, Ctrl-X Ctrl-P, tmux popups), encrypted cross-machine sync, an MCP server for AI agents, a local web UI (serve), and a doctor command. Issues and PRs welcome; see CONTRIBUTING.md. Version-by-version details live in the CHANGELOG.

License

MIT

Available Tools

4 tools
diff_outputDiff two runs' outputsA
Read-onlyIdempotent

Unified diff (diff -u style plain text) between the stored outputs of two runs. With only 'id', diffs that command against the most recent EARLIER run of the exact same command line — 'what changed since it last ran?'; errors if no earlier identical command line exists. With 'other', diffs the two given commands (other = older side). Identical outputs return a note saying so. Secrets are masked by default. Read-only: diffs recordings, never re-runs anything.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesnewer command id (or -N for Nth most recent; -1 = last command)
otherNoolder command id to compare against (optional; default: previous run of the same command)
contextNolines of context around changes (default 3)

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already provide readOnlyHint, idempotentHint, destructiveHint. The description adds valuable context: secrets are masked by default, the tool never re-runs anything (read-only), and identical outputs return a note. This disclosure exceeds what annotations alone provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise yet complete, structured with a clear opening statement followed by behavioral details and parameter explanations. Every sentence adds value without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema, the description explains the return format (unified diff, plain text) and includes a note for identical outputs. It covers all key aspects: use cases, default behavior, error conditions, and security masking, making it fully informative.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with descriptions for each parameter. The description adds context for the 'id' parameter (explains -N syntax) and clarifies 'other' as the older side. While baseline is 3 due to high coverage, the extra semantics justify a 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool produces a unified diff between two runs' outputs. It distinguishes from siblings like get_output (which returns full output) and search_output/list_commands (different purposes), making the tool's purpose specific and unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit guidance on when to use with only 'id' (diffs against previous same command) versus with 'other' (explicit pair). It also explains error conditions (no earlier identical command line) and behavior for identical outputs, fully guiding the agent's decision.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_outputGet a command's outputA
Read-onlyIdempotent

Get the full recorded output of one terminal command, plus its command line, exit code, cwd and timing, as plain text (ANSI escapes stripped; secrets masked by default). id: a command id from search_output/list_commands, or negative for relative addressing (-1 = the user's most recent command, -2 = the one before it). Outputs larger than max_bytes return the head and tail around a '[... N bytes omitted ...]' marker — call again with a larger max_bytes for more. Errors with 'not found' if the id doesn't exist. Read-only: reads the recording, never re-runs the command.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYescommand id, or -N for Nth most recent (-1 = last command)
max_bytesNocap on returned output size (default 51200); if the output is larger, the head and tail are returned with a gap marker

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses key behaviors beyond annotations: ANSI stripping, secret masking, truncation with gap marker, error behavior ('not found'), and read-only nature. Annotations already indicate read-only and idempotent, but the description adds specific, actionable details.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and front-loaded with the tool's purpose, followed by parameter explanations. Every sentence adds essential information, with no wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite no output schema, the description fully explains what is returned (output text, metadata) and covers edge cases (large output, error). Combined with complete annotations and parameter schema, it leaves no significant gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds value by explaining the meaning of negative ids for relative addressing and the default and truncation behavior for max_bytes, exceeding what the schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool retrieves the full recorded output of a terminal command, including command line, exit code, cwd, and timing. It distinguishes itself from sibling tools (search_output, list_commands, diff_output) by focusing on output retrieval.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains how to obtain a command id (from search_output/list_commands or relative addressing) and how to handle large outputs (using max_bytes). It does not explicitly exclude any scenarios, but the read-only note implies safe usage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_commandsList recent commandsA
Read-onlyIdempotent

List the user's recent terminal commands (most recent first) as plain text: one entry per command with id, time, cwd, exit code, duration and output size — command lines only, no output text. Supports the same filters as search_output, e.g. exit='fail' for recent failures or cwd='.' for this project only. Use this to browse history; use search_output when looking for specific text, and get_output to read what a command actually printed. Read-only: never re-runs anything.

ParametersJSON Schema
NameRequiredDescriptionDefault
cwdNoonly commands run in this directory or beneath it
exitNoonly this exit code (a number), or 'fail' for any nonzero
hostNoonly commands from this synced machine ('local' = this machine)
limitNomax results (default 20, max 100)
sinceNoonly commands newer than this: 30m, 2h, 3d, 1w, or a date
untilNoonly commands older than this (exclusive; same forms as since)

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Description adds 'Read-only: never re-runs anything' which is consistent with annotations (readOnlyHint, idempotentHint). Provides useful behavioral context beyond what annotations declare.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no wasted words. Main purpose stated upfront, followed by filter support and usage guidance. Excellent structure.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given parameter count (6) and absence of output schema, description covers return format, what is not included (no output text), and filter usage. Fully sufficient for agent to use tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema covers 100% of parameters with descriptions. Description provides usage examples for some parameters (e.g., exit='fail'), but does not add significant new info beyond schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states 'List the user's recent terminal commands' and specifies format (plain text with id, time, cwd, exit code, duration, output size). Distinguishes from siblings by indicating when to use each tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly gives guidance: 'Use this to browse history; use search_output when looking for specific text, and get_output to read what a command actually printed.' Also mentions same filters as search_output.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_outputSearch command outputsA
Read-onlyIdempotent

Full-text search over recorded terminal commands, their outputs, and user notes — finds e.g. every command that ever printed 'connection refused'. Returns plain text, one block per match: id, time, cwd, exit code, the command line, and a snippet around the match (secrets are masked by default). Use this to FIND which command said something; use list_commands to browse recent history without a search term, and get_output to read a full output once you have an id. No matches returns an empty result, not an error. Read-only: never re-runs anything.

ParametersJSON Schema
NameRequiredDescriptionDefault
cwdNoonly commands run in this directory or beneath it (absolute path, or '.' for the server's cwd)
exitNoonly this exit code (a number), or 'fail' for any nonzero
hostNoonly commands from this synced machine ('local' = this machine)
limitNomax results (default 20, max 100)
queryYestext to search for in command lines, outputs and notes (substring match; case-insensitive)
sinceNoonly commands newer than this: 30m, 2h, 3d, 1w, or 2006-01-02[ 15:04]
untilNoonly commands older than this (exclusive; same forms as since). Combine with since to bound a time window, e.g. one day.
context_linesNoinstead of a short snippet, show every matching output line with this many lines of context before and after (grep -C style, with line numbers; 0 = matching lines only, max 10). Often avoids a follow-up get_output call.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Description reinforces annotations by stating read-only and never re-runs commands. Adds that secrets are masked by default, and details the return format including fields and snippet behavior. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Packs essential information into a few sentences with clear front-loading of purpose. No unnecessary words, structured logically from what to when to how.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 8 parameters, no output schema, and moderate complexity, the description covers purpose, usage, alternatives, return format, error behavior, and safety. Sufficient for an agent to use correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description does not add significant detail beyond what the schema already provides for individual parameters, though it explains the overall search result behavior.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it performs full-text search over terminal commands, outputs, and notes, with a concrete example. It distinguishes itself from sibling tools by specifying when to use list_commands and get_output instead.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly advises using this tool to find which command produced specific output, and contrasts with list_commands for browsing and get_output for reading full output. Also notes empty returns are not errors.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 1 tool updatev0.11.0
    • Changedsearch_output1 field changed
      • changedInput schema / properties / query / description
        Before
        "text to search for in command lines and outputs (substring match; case-insensitive)"
        After
        "text to search for in command lines, outputs and notes (substring match; case-insensitive)"
  2. 4 tool updatesv0.10.0
    • Addeddiff_output
    • Addedget_output
    • Addedlist_commands
    • Addedsearch_output
  3. 2 tool updatesv0.9.0
    • Removedlist_commands
    • Removedsearch_output
  4. 2 tool updatesv0.7.10
    • Removeddiff_output
    • Removedget_output
  5. 4 tool updatesv0.7.9
    • Addeddiff_output
    • Addedget_output
    • Addedlist_commands
    • Changedsearch_output2 fields changed
      • addedInput schema / properties / context_lines
        {
          "description": "instead of a short snippet, show every matching output line with this many lines of context before and after (grep -C style, with line numbers; 0 = matching lines only, max 10). Often avoids a follow-up get_output call.",
          "type": "integer"
        }
      • addedInput schema / properties / until
        {
          "description": "only commands older than this (exclusive; same forms as since). Combine with since to bound a time window, e.g. one day.",
          "type": "string"
        }
  6. 2 tool updatesv0.6.1
    • Removeddiff_output
    • Addedsearch_output
  7. 1 tool updatev0.6.2
    • Removedlist_commands
  8. 2 tool updatesv0.6.0
    • First observeddiff_output
    • First observedlist_commands

TDQS

A4.7/5.0
Disambiguation5/5

Each tool has a clearly distinct purpose: search_output finds commands by text content, list_commands browses recent command metadata, get_output retrieves full output of a specific command, and diff_output compares outputs of two runs. No overlap in functionality.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern (search_output, list_commands, get_output, diff_output) using lowercase with underscores. The style is uniform and predictable.

Tool Count5/5

Four tools is well-scoped for the domain of querying terminal command recordings. Each tool covers a core operation (search, list, retrieve, diff) without unnecessary bloat or missing essentials.

Completeness4/5

The tools cover the main read-only use cases for terminal history: searching, browsing, viewing full output, and diffing. Minor gaps exist, such as lacking a tool to manage notes or delete recordings, but these are beyond the stated read-only scope.

Maintenance

ActivityActive
ResponsivenessSyncing

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    A terminal live-tail and a browser dashboard — one process, one event stream, served from localhost. Unified timeline across Claude Code, Codex, Gemini CLI, Cursor, Hermes, and OpenClaw. Token + cost accounting, compaction + anomaly detection, hybrid search, SVG call graphs, monaco-style diff attribution, agent-aware replay ("what would the agent say if I edited the prompt?"), policy editor, MCP s
    13
    14
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Developers: Search your zsh, bash, or fish shell history from Claude Code, Cline, Cursor, Zed, or any MCP client using tools like search_history (full-text with timestamp/CWD/exit code), recent_in_dir, failed_commands, and command_chains for multi-step sequences. Reindex after new activity. Local-only SQLite FTS5 with secrets redacted before storage.
    5
    16
    4
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Records masked browser sessions (rrweb DOM + console + network) to a local SQLite store and exposes them to AI coding agents via stdio MCP. Fully local, no SaaS, no telemetry.
    10
    7
    Apache 2.0
  • F
    license
    Not graded
    quality
    A
    maintenance
    Local MCP server that lets your AI coding agent query its own cross-tool project history - file/command freshness, past test failures, cost & token spend, cache status, and session handoff - over stdio, 100% local, no telemetry.
    40
    -

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/soren-achebe/backscroll'

If you have feedback or need assistance with the MCP directory API, please join our Discord server