Aster
Aster is a natural, history-first shell completion system. It would rather show nothing than offer a completion it cannot justify.
The project is in early development. The current vertical slice provides a shared local daemon, SQLite-backed command history, Zsh history import, and an inline completion menu, ghost text, command discovery, lazy descriptions, native Zsh candidates, full acceptance, and segment-by-segment Tab completion.
Principles
- Completed command history is the highest-priority source.
- Candidate providers are ordered tiers; lower tiers fill unused menu capacity but never outrank higher-confidence results.
- The menu appears while typing and highlights the highest-ranked candidate.
- Ctrl-Space accepts the entire candidate; Tab accepts one useful segment.
- No per-command shell completion scripts are required.
- Every host has one shared daemon and one history database.
- tmux panes and concurrent SSH shells remain lightweight clients.
- Silence is preferable to an uncertain completion.
Build
Or install the published package while keeping the executable name aster:
Aster currently targets Unix systems because its shared transport is an
owner-private Unix socket. Inline fuzzy mode requires fzf on the host.
Zsh Setup
Add one integration line to .zshrc:
This is Aster's only shell integration. You do not need to source completion scripts for every installed program.
The integration:
- Imports the existing Zsh history at shell startup when the file changed since the previous import.
- Records submitted foreground commands and the shell status reported afterward.
- Starts the per-host daemon automatically.
- Uses Tab to accept the shortest next word or path segment from an open Aster suggestion; each accepted segment resets the refreshed menu to row 1. With no open suggestion, Tab delegates to the previous Zsh widget.
- Uses Shift-Tab to move upward through suggestions without entering a modal editing state; letters and Backspace continue editing normally.
- Consumes a second consecutive Space to enter inline fuzzy mode over shared history and installed commands. Escape restores only the current command's base buffer; Ctrl-C and each new prompt discard all fuzzy state.
- Captures append-safe candidates from the configured Zsh completion system in a forked completion context after about 30-60 ms idle and blends them into Aster's menu without blocking input.
- Shows ranked candidates automatically as the command buffer changes.
- Renders a bordered, color-highlighted menu and selected-candidate ghost text as part of ZLE's multiline display.
- Adds a lazy preview box at 100 columns or wider only when useful content is available. History-only and generic rows stay compact; command details reuse the existing asynchronous description workers, and selected native text files are read through a bounded, sanitized background helper. Changing rows erases the previous preview immediately and stale async results are target-checked.
- Previews simple
ls, GNUgls, andezasuggestions asynchronously, includinglsaliases backed byeza. Aster passes a validated argv directly without a shell, translatesezacolors into ZLE-safe highlight spans, disables icons and hyperlinks, caps output, and kills previews that exceed the short deadline. - Uses Ctrl-Space to accept the entire highlighted candidate and preserves its previous binding as the fallback when Aster has no candidate.
- Uses Ctrl-N and Ctrl-K to move through an open menu and updates inline ghost text to preview the selected row; outside the menu their prior widgets remain active.
- Leaves Up and Down dedicated to their existing Zsh history widgets, cancelling stale completion work before history changes the command buffer.
- Accepts the full suggestion with Ctrl-Space, or the shortest next segment with Tab; Enter remains the shell's untouched command-submission binding.
- Leaves tmux pane titles and automatic window naming entirely under tmux and the foreground application's control.
Aster owns ZLE's suggestion display. Do not load a second autosuggestion plugin
alongside it; competing POSTDISPLAY highlights can recolor or stale the menu.
Native Zsh capture is asynchronous and best-effort. At argument positions,
explicit filesystem matches precede history and remain stable while native
results arrive; elsewhere history remains first. Duplicate displays are removed.
At every argument position Aster offers bounded
local filesystem matches, so path completion does not depend on a
command-specific completion function. Root-command flags parsed from man pages
or sandboxed --help output appear lazily with their descriptions ahead of
duplicate native matches. Quoted
replacements, mid-word edits, and other non-append-safe completion behavior
remain under ordinary Zsh Tab. For ambiguous filesystem matches, Tab leaves the
buffer unchanged and never selects the first entry arbitrarily.
The same setup works inside tmux and on SSH hosts. Each remote host runs its own
daemon and keeps its own local history; tmux panes on that host share it.
Aster uses its Unicode UI when locale charmap reports UTF-8 and automatically
falls back to ASCII borders and markers otherwise.
Image and PDF candidates currently show bounded metadata rather than graphics.
Kitty image transmission needs terminal-owned image IDs and cleanup that ZLE's
POSTDISPLAY model cannot provide safely; it is tracked for the future PTY UI.
Conservative Acceptance
Given a history entry:
cd ~/dev/gitrepos/aster
and the current buffer:
cd ~/d
pressing Tab on the automatically highlighted candidate inserts only:
ev/
Aster then queries again from the new buffer. Pressing Ctrl-Space instead inserts
the complete ev/gitrepos/aster remainder. Tab also recognizes structured shell
values. For example, ssh ali completes to ssh alice@ before accepting the
host, while scp zzu can advance through zzuser@, example.com:/, and each
remote path component separately. Assignments, comma-separated values, URLs,
rsync's host::module syntax, quoting, escaping, and bracketed IPv6 hosts use the
same conservative boundary scanner.
Commands
aster daemon
aster stop
aster doctor
aster init zsh
aster import-history --file ~/.zsh_history
aster record --command "git status" --cwd "$PWD" --exit-code 0
aster complete --buffer "git st" --cursor 6 --cwd "$PWD"
Client commands automatically start the daemon if it is unavailable.
Configuration
The default configuration is written by aster init zsh:
[]
= 8
= "ctrl-space"
= "full"
[]
= true
= true
[]
= 64
= 6
= 2
= "4"
= "10"
= "7"
= "8"
= "8"
= "8"
= "15"
= "0"
completion.key also accepts Ctrl-letter names such as ctrl-x. Ctrl-I (Tab),
Ctrl-J, Ctrl-K, Ctrl-M, and Ctrl-N are reserved by the menu integration. The
generated binding is refreshed when a new shell starts.
UI colors accept ANSI palette indexes from 0 through 255 or exact #RRGGBB
values. ANSI indexes follow the active terminal theme. prompt_offset is the
visual width of the final prompt line before editable text. The menu follows the
live letter cursor and clamps before the terminal's final column.
Paths can be overridden for testing or custom deployments:
| Variable | Purpose |
|---|---|
ASTER_CONFIG |
Configuration file |
ASTER_STATE_DIR |
SQLite database and daemon log directory |
ASTER_SOCKET |
Unix socket path |
XDG_CONFIG_HOME |
Default config root |
XDG_STATE_HOME |
Default state root |
Run aster doctor to print the resolved paths and verify daemon connectivity.
Custom state and socket paths must live beneath directories owned by the current
user and inaccessible to group and other users.
Status
Implemented:
- Shared auto-started daemon.
- Versioned JSON protocol over an owner-private Unix socket.
- SQLite WAL history shared by all shells on a host.
- Change-detected Zsh history import.
- Current-directory, success, and recency-aware history ranking.
- Cached
PATHcommand inventory, shell builtins, semantic command icons, and authored descriptions for common tools. - Lazy command-description enrichment from exact man pages and sandboxed
--helpoutput, with executable-fingerprinted persistent caching. - Asynchronous native Zsh candidate capture for append-safe options, subcommands, paths, aliases, and other configured completion sources.
- Prefix-only completion with conservative partial acceptance.
- Cursor-anchored, scrolling ZLE menu with ghost text, descriptions, semantic kinds, highlighted matches, position counter, and configurable key hints.
- Debounced asynchronous completion requests that never block character input.
- Zsh native-completion fallback.
Description discovery never runs on the completion request path. An unresolved
visible command keeps its origin fallback, is queued on a bounded worker pool,
and is refreshed in the menu when metadata becomes ready. Aster checks the exact
man page first. On macOS, it may then run --help through sandbox-exec with
network and writes denied, an empty environment, bounded output, and a hard
timeout. Direct executable probing is disabled on platforms without that
sandbox. Cached misses expire after one day; descriptions expire after 30 days
or immediately when the executable path, identity, size, mode, or timestamps
change.
Planned:
- Structured option, subcommand, and enum candidates.
- Filesystem segments.
- Optional project-aware providers.
See docs/architecture.md and
docs/roadmap.md.