Skip to main content

Local-first, provider-agnostic error/fix memory with a semantic search TUI.

Project description

errnest

Local-first, provider-agnostic error/fix memory for the command line.

Capture the errors you hit and how you fixed them. errnest surfaces the past fix the next time a failure looks the same — matched semantically via embeddings, with an offline text-match fallback when no AI backend is reachable. Browse everything in an interactive terminal dashboard.

tests PyPI Python License: MIT


Contents

Why errnest

Every developer re-solves the same handful of errors over and over — port already in use, a ModuleNotFoundError after switching branches, a Docker permission error. errnest is a small, local database of those errors and their fixes, plus a fast way to ask "have I seen this before?" — from a shell one-liner, a TUI, or your own tooling.

  • Local-first. SQLite on disk, no server, no account, no telemetry.
  • Can capture automatically. A shell hook surfaces past fixes the moment a command fails, with no extra step — see Automatic error surfacing.
  • Provider-agnostic AI. Ollama by default (fully offline); swap in OpenAI, an OpenAI-compatible endpoint, Anthropic, or Gemini via one config key. The rest of the codebase only ever calls embed() / generate().
  • Degrades gracefully. No AI backend reachable? Search falls back to text matching instead of failing.
  • Redacts before it stores. Secrets and volatile tokens (paths, hex, emails) are stripped/normalized before anything hits disk.
  • A real dashboard, not just a CLI — see below.

Install

pip install errnest                 # core (local Ollama backend)
pip install errnest[openai]         # + OpenAI / OpenAI-compatible
pip install errnest[anthropic]      # + Anthropic (generation)
pip install errnest[gemini]         # + Google Gemini
pip install errnest[all-providers]  # everything

Requires Python 3.9+. No provider is required to install or run errnest — with nothing configured it talks to a local Ollama daemon, and even without Ollama running, capture/search/dashboard all still work (offline text matching instead of semantic search).

Quickstart

some-failing-command 2>&1 | errnest capture -c "some-failing-command"
errnest resolve 1 "bumped the timeout in config.yaml"
errnest search "connection refused on startup"
errnest doctor       # check provider + models + credentials
errnest dashboard    # browse everything interactively

Or skip manual capture entirely — see below.

Automatic error surfacing

Two tiers, from fully hands-off to full detail:

Tier 1 — the shell hook. Add to your ~/.zshrc or ~/.bashrc:

eval "$(errnest shell-init zsh)"   # or: bash

Every failed command is now captured automatically — no errnest capture needed. It's deliberately cheap (no AI call, <200ms) so it never adds noticeable latency to your prompt: it matches on the command string alone and shows past resolved fixes for that exact command. Tune what gets captured via [capture] in ~/.errnest/config.toml (see Configuration) — skip low-severity exit codes, ignore specific commands (cd, git status, …) or regex patterns, or turn it off entirely.

Tier 2 — errnest run. For when you want the real thing:

errnest run docker compose up

Wraps the command, tees stderr (your terminal sees everything, unchanged), and on failure stores the full redacted error with an embedding — real semantic search against your history, not just an exact command-string match. An AI explanation is generated in the background by default (--no-explain to skip it).

The dashboard

errnest dashboard opens an interactive TUI (built with Textual) over your error store — search, filter by status or tag, resolve/dismiss/copy fixes, see semantically-similar past errors, and spot recurring patterns via clustering.

errnest dashboard

Panes: a filterable, searchable error list on the left; a detail view on the right with metadata, error output, applied fix, an AI explanation (once resolved), and the top 3 semantically similar past errors. A Patterns tab clusters your error history (k-means over embeddings) once you've captured 20+ errors, so you can spot what's actually costing you the most time.

Keybindings:

Key Action
j / k or / Navigate the error list
r Mark selected error resolved
d Dismiss selected error (won't-fix)
u Reopen a resolved/won't-fix error back to open
e Ask the AI backend to explain the selected error
c Copy the fix text to your clipboard
s Focus search
/ Focus filter chips
p or 14 Switch tabs (All / Unresolved / Resolved / Patterns)
q or Ctrl-C Quit

Search is semantic (embeddings + cosine similarity) when an embed backend is reachable, and degrades to a live substring filter when it isn't — with no error shown, just a quieter dashboard. Copy-fix uses a native clipboard tool (pbcopy / xclip / wl-copy / clip) so it works even in terminals that don't support OSC 52 clipboard sync.

AI explanations

errnest explain 7             # generate (or show a cached) explanation
errnest explain 7 --regenerate

Generation always runs detached — the command returns immediately, and the result lands in the error's record whenever it finishes (check back with errnest show 7 or the dashboard). Local models can genuinely take tens of seconds; nothing here blocks your terminal waiting for it. errnest run and the dashboard's e key generate the same way.

AI backends

The rest of errnest only calls embed() / generate() — it never references a provider directly. The active provider comes from [ai] provider in ~/.errnest/config.toml:

[ai]
provider = "ollama"        # ollama | openai | openai-compatible | anthropic | gemini
embed_fallback = "ollama"  # used when the provider can't embed (e.g. anthropic)

[ai.ollama]
host = "http://localhost:11434"
embed_model = "nomic-embed-text"
gen_model = "llama3"

[ai.anthropic]
api_key_env = "ANTHROPIC_API_KEY"
gen_model = "claude-haiku-4-5"

Provider SDKs are imported lazily; a provider that's unreachable or missing its SDK degrades to a clear AIUnavailableError, and every caller in errnest (search, the dashboard, doctor) handles that by falling back rather than crashing.

Switch providers without hand-editing TOML:

errnest config set ai.provider anthropic
errnest config show     # effective config, secrets masked

See USAGE.md for the full config reference, every environment variable, and per-provider setup steps.

CLI reference

Command Description
errnest capture -c "<cmd>" Capture a failing command + error output (stdin) as unresolved
errnest run <cmd> [args...] Run + tee a command; capture full stderr + embedding on failure
errnest resolve <id> "<fix>" Record the fix and mark an error resolved
errnest edit <id> Edit an error's fix text in $EDITOR
errnest dismiss <id> Mark an error won't-fix
errnest reopen <id> Revert a resolved/won't-fix error back to open
errnest explain <id> Generate (in the background) or show a cached AI explanation
errnest search "<query>" Show the most similar past errors + fixes
errnest show <id> Full detail for one error
errnest shell-init zsh|bash Print the Tier 1 auto-capture shell hook (see below)
errnest dashboard Open the interactive TUI
errnest doctor Check provider/model reachability and credentials
errnest config show Print effective config (secrets masked)
errnest config set <key> <value> Set a config value, e.g. ai.provider anthropic

Embedding in other tools

from errnest import ErnestClient

client = ErnestClient()

# Never raises — returns None if there's no confident, resolved match.
fix = client.surface(stderr_text)

# Browse recent errors, optionally scoped to a project directory.
recent = client.search("build failed", cwd="/path/to/project", top_k=3)

Wrap it behind a try/except ImportError in your own tool to make errnest an optional dependency — never import it at module top level, and treat every call as safe-to-fail (it already is, on errnest's side).

Configuration

Environment variables:

Variable Purpose Default
ERRNEST_CONFIG Path to the config file ~/.errnest/config.toml
ERRNEST_DB Path to the SQLite error store ~/.errnest/errors.db
ERRNEST_SIM_THRESHOLD Cosine-similarity cutoff for auto-surfacing 0.7
OLLAMA_HOST Ollama daemon URL http://localhost:11434

Tier 1 auto-capture is tuned via [capture] in ~/.errnest/config.toml (auto_capture, min_exit_code, ignore_commands, ignore_patterns, surface_threshold, quiet). Full config file reference, per-provider setup, and troubleshooting: USAGE.md.

How it works

  • Storage: SQLite (~/.errnest/errors.db), one row per captured error, with an optional embedding vector attached at capture time.
  • Redaction: API keys, tokens, emails, and provider-specific credential patterns are stripped before storage; paths/hex/numbers are normalized so near-identical errors from different runs still match.
  • Search: cosine similarity over embeddings when an embed backend is reachable; Jaccard token overlap over normalized text otherwise.
  • Patterns: a small pure-Python k-means (no numpy) over stored embeddings, computed once per dashboard session.

See CHANGELOG.md for release history.

Contributing

Issues and PRs welcome. To run the test suite locally:

pip install -e ".[dev,all-providers]"
pytest
ruff check .

Provider tests mock the SDK/HTTP layer — no real API calls or a running Ollama instance are needed to run the suite.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

errnest-0.2.0.tar.gz (250.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

errnest-0.2.0-py3-none-any.whl (48.0 kB view details)

Uploaded Python 3

File details

Details for the file errnest-0.2.0.tar.gz.

File metadata

  • Download URL: errnest-0.2.0.tar.gz
  • Upload date:
  • Size: 250.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for errnest-0.2.0.tar.gz
Algorithm Hash digest
SHA256 d354129a57cc103120b4bea00b43e57e76833f9493724cc68f25895d01cf119c
MD5 266c715a7284887d89a1d87661563fcb
BLAKE2b-256 63dcc44fb91833c6c89468502a85f1e8b33e00ebb5fbb72df62c2dfa91eec99a

See more details on using hashes here.

File details

Details for the file errnest-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: errnest-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 48.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for errnest-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e7f77db22fe55f47bc10cec685e9a4d7ed5180923b56170fabb5c0c1dd05234f
MD5 27ec8b8ff10686dc2680da95a65ce6f9
BLAKE2b-256 4ee4ddd3f4b1e4d6fd0b2884aad1f56e8ae96ac6fed68eb7b76799be30f18c26

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page