aster-completion 0.1.6

Natural shell completion
Documentation

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

cargo build --release
cargo install --path .

Or install the published package while keeping the executable name aster:

cargo install aster-completion

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:

eval "$(aster init zsh)"

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. Long rows preserve the completion suffix, rendering as … suggestion when the typed command prefix would hide it.
  • 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, GNU gls, and eza suggestions asynchronously, including ls aliases backed by eza. Aster passes a validated argv directly without a shell, translates eza colors 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. History remains first at every position; explicit filesystem matches follow it and remain stable while native results arrive. 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:

[completion]
max_candidates = 8
key = "ctrl-space"
accept = "full"

[history]
ignore_leading_space = true
successful_first = true

[ui]
menu_width = 64
max_visible = 6
prompt_offset = 2
border = "4"
accent = "10"
text = "7"
muted = "8"
ghost = "8"
selected_background = "8"
selected_text = "15"
selected_source = "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 PATH command inventory, shell builtins, semantic command icons, and authored descriptions for common tools.
  • Lazy command-description enrichment from exact man pages and sandboxed --help output, 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.