CLI tool
Giammarco-Ferranti/deja avatar
Giammarco-Ferranti/deja

deja: A Go daemon that turns zsh autosuggestions into a local prediction engine

Predictive inline shell autosuggestions for zsh - Go daemon, no TUI, no sync.

750 stars15 forksGoMIT

At a glance

What is it?
deja replaces zsh-autosuggestions with a background Go daemon that scores commands by fuzzy match, directory, and sequence, then shows ghost text inline. It is fast and local, but its setup and upgrade path need attention.
Who is it for?
Adopt deja if you want a faster, more context-aware replacement for zsh-autosuggestions and you accept a daemon process plus a local SQLite database. Do not use it if you need multi-user sync, a TUI, or if you rely on zsh-autosuggestions' exact prefix matching.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 8 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 14, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What deja solves and who it is for

deja targets zsh users who find zsh-autosuggestions too literal. That plugin only suggests commands that start with what you have typed, so a typo or a skipped letter kills the suggestion. deja instead uses fuzzy matching, directory awareness, and command sequence prediction to guess what you actually want. The intended user is a developer who lives in a few project directories, runs repeating command sequences like make build then make test, and wants inline ghost text without a separate pane or a cloud account. The README positions it as a smarter replacement, not a complement. It is local-only, with all data in a SQLite database, so it suits users who avoid sync servers and want their shell history to stay on the machine. The daemon architecture means one background process serves all terminal windows, which is a design choice for people who open many shells and want consistent suggestions across them.

How the prediction engine works

The core mechanism is a scoring blend. The README lists four inputs: fuzzy matching, directory awareness, sequence prediction, and frecency scoring. Frecency combines frequency and recency with a 1-week exponential decay, so a command you ran yesterday outranks one from two weeks ago even if both appear often. Directory awareness boosts commands that you run in the current directory, which is a concrete improvement over global history matching. Sequence prediction learns that make test often follows make build, so after you type make, it can suggest the next step. The fuzzy matching lets you skip letters or mix up order, which addresses a real failure mode of prefix-based suggestions. The daemon responds in under 1 ms per keystroke, according to the README, and it auto-spawns on first use. The ghost text uses zsh's POSTDISPLAY widget, not a separate pane, so it renders inline without stealing focus. The README does not detail the scoring weights or how the sequence model is trained, so the exact ranking behavior is not fully specified.

Installation and activation paths

The README gives four installation routes: Homebrew, curl, Oh My Zsh, and zinit. The Homebrew command is a one-liner that installs the binary, imports history, appends an activation line to ~/.zshrc, and reloads the shell. The curl installer is similar and can be audited on GitHub before running. For Oh My Zsh, you install the binary separately, clone the plugin into the custom plugins directory, add deja to the plugins array, and run deja import. With zinit, you add a single ice line and the plugin handles integration. A manual route fetches only deja.plugin.zsh. A key warning is to pick one activation method, not both, because double-sourcing the integration causes issues. The README also says deja stands down if it detects zsh-autosuggestions is loaded, so you must not list both. The activation script is written to ~/.local/share/deja/init.zsh by deja init zsh, which prints a source line rather than the script itself. The README explicitly notes that eval "$(deja init zsh)" costs 25-36 ms per shell startup because it launches the binary, while sourcing the file directly skips that cost. This is a real trade-off: the one-liner is convenient but slow on every new shell.

Key bindings and the alternatives picker

The key bindings are more extensive than zsh-autosuggestions. The right arrow accepts the full suggestion, Ctrl+Right accepts the next word, and Tab opens an inline alternatives picker that cycles through ranked suggestions without leaving the line. Shift+Right and Shift+Left cycle fuzzy presets (tight, smart, loose), which gives you control over how aggressively the matcher guesses. Shift+Up toggles ghost text on an empty prompt, and Ctrl+X suppresses the current suggestion session-wide. There is also an unbound dismiss key for the current line. All bindings are rebindable via environment variables like DEJA_ACCEPT_KEY and DEJA_CYCLE_KEY. The README warns that Enter executes whatever is literally in the buffer, while the right arrow accepts the ghost. This distinction is important: if you are used to pressing Enter to accept a suggestion, deja will not behave that way. The alternatives picker is a differentiator, but the README does not describe its UI beyond saying it is inline, so the exact interaction is unverified.

History import and privacy constraints

deja import reads your zsh history from $HISTFILE if it is exported, otherwise it falls back to ~/.zsh_history. If your history lives elsewhere, you can point to it with deja import --file /path/to/history. The README notes a common gotcha: if $HISTFILE is set in ~/.zshrc without export, child processes cannot see it, so you must use the explicit file flag. The import respects your history settings: a leading space (HIST_IGNORE_SPACE) or a HISTORY_IGNORE match keeps a command out of deja too. This is a thoughtful privacy feature, but it also means deja's suggestions are only as good as your history file. If you have never exported HISTFILE or if your history is truncated, the import will be incomplete. The daemon stores everything in a local SQLite database, and nothing leaves the machine, which is a strong privacy guarantee. However, the README does not specify the database location or how to purge it, so you cannot easily delete learned data without manual intervention.

Upgrade mechanism and startup cost

The README describes a self-updating integration script. Each shell compares the installed binary's stat identity against the one baked into the script, which takes 0.083 ms with no subprocess. If they differ, the script regenerates in the background, and the shell that noticed carries on with the old script while the next one picks up the new. This means an upgrade never blocks shell startup, but it also means the current shell may use a stale integration for its lifetime. The README claims this avoids both slow startups and stale integrations, though the trade-off is that the first shell after an upgrade runs with the old script. The daemon auto-spawns on first use and persists across sessions, so there is no per-shell daemon startup cost. The main startup cost is the eval bootstrap on first run before the init.zsh file exists. After that, sourcing the file directly is fast. This is a sensible design, but it adds a moving part: if the binary is updated outside the package manager, the stat check handles it, but if the init.zsh file is deleted, you are back to the slow eval path.

Limitations and when to avoid it

The most obvious limitation is that deja is zsh-only. There is no mention of bash, fish, or other shells, so it is not a general autosuggestion tool. The README explicitly says it replaces zsh-autosuggestions, so if you use that plugin, you must remove it. The daemon architecture is a double-edged sword: it provides low latency and shared state, but it is a background process that runs indefinitely. If you are security-conscious about what runs on your machine, a daemon that reads your entire command history and stores it in SQLite may be a concern, even if it is local. The README does not mention any sandboxing or permission model for the daemon. The fuzzy presets (tight, smart, loose) are a useful feature, but the README does not explain what each preset changes in scoring terms. The Tab alternatives picker is a differentiator, but its behavior is not documented beyond being inline. The biggest practical issue is the activation warning: if you already use zsh-autosuggestions, you must disable it, and if you use a plugin manager, you must avoid double-sourcing. That is a real setup friction point.

Alternatives and the difference in approach

The direct alternative is zsh-autosuggestions, which deja explicitly replaces. zsh-autosuggestions uses prefix matching on your history and runs entirely within zsh, with no daemon and no external database. It is a single zsh script that hooks into the line editor, so it is lightweight and trivial to install. The difference is fundamental: zsh-autosuggestions only suggests commands that start with what you typed, while deja uses fuzzy matching and sequence prediction. If you value simplicity and zero background processes, zsh-autosuggestions is the safer choice. Another alternative is fish shell's built-in autosuggestions, which also use history but are native to fish. deja's advantage is its directory awareness and sequence prediction, but those come at the cost of a Go binary and a SQLite file. For users who want maximum control over their shell, a manual history search with Ctrl+R in zsh is a no-dependency fallback, though it is not inline. The choice comes down to whether you need the predictive features enough to accept the daemon.

Editorial conclusion

Adopt deja if you want a faster, more context-aware replacement for zsh-autosuggestions and you accept a daemon process plus a local SQLite database. Do not use it if you need multi-user sync, a TUI, or if you rely on zsh-autosuggestions' exact prefix matching. Before adopting, verify the daemon's memory footprint on your system, confirm the ~25-36 ms startup cost of the eval bootstrap is acceptable, and test the Tab alternatives picker with your terminal emulator. The project is MIT licensed and actively maintained, but its value depends on your tolerance for a background process and the one-time import step.

Official sources

  1. Official README
  2. Project repository
  3. Release notes
Community notes

Community notes