---
title: augur
tagline: Inline command suggestions for bash, one word at a time.
status: active
highlight: 3
tags: [rust, bash, ollama]
links:
repo: https://github.com/hvpaiva/augur
github: hvpaiva/augur
---
## The problem
ble.sh suggests the rest of the most recent command that starts with what
you typed. A whole line is only ever right when you are repeating
yourself, and most of the time you are typing a line you have never typed
before. But a single word can be right in a line that is new: in
`kubectl -n staging get po`, `pods` comes from what followed `get` in
every other kubectl command you ran.
## Constraints
The answer has to arrive inside a pause in typing, and a keystroke has to
cancel it. It has to work with no language model at all, because most
machines will not have one. And a suggestion that is merely valid is
worse than no suggestion, because the grey text teaches you to trust it.
## What I built
Two halves, installed separately. A bash file sourced into the
interactive shell, and a Rust engine running as one background process
per shell, talking over a pair of FIFOs that ble.sh manages.
The shell half records and suggests. ble.sh's `PREEXEC` and `POSTEXEC`
hooks append the command, its directory and its timing to one
append-only file shared by every shell, which is how something run in one
terminal is suggested in another a moment later. The suggestion side
registers as the first auto-complete source, runs ble.sh's own completion
for the word under the cursor, tags each candidate as a file name or a
word, and polls for the engine's answer in four-millisecond slices so
that typing cancels the wait.
The engine decides. Completion says what is valid here: installed
commands, subcommands, flags, branches, pods, whatever the completion
scripts list. History says what you use, narrowed by this directory, this
git repository, the previous command and this session. When neither has
an opinion, a local base model served by Ollama picks among the valid
words, using log-probabilities rather than generated text. The model is
optional and off by default.
The rules are where the work is, because a word the completion offers is
valid, not likely. With nothing typed the valid words are every word, so
one is shown only when the history or the model points at it, and a file
name only when the history does. A guess no completion backs must clear a
likelihood bar, and is never shown when it names a file that is not
there: the `notes.txt` you read in another directory is not offered in
this one. Paths arrive one component at a time, because the path you
typed before is as often a sibling of the one you are typing as the path
itself.
augur prefers showing nothing to showing a guess it has no reason for.
git clone github.com/hvpaiva/augur