# ev - progressive zsh command suggestions [![GitHub](https://img.shields.io/badge/GitHub-emhat098%2Fev-181717?logo=github)](https://github.com/emhat098/ev) [![Release](https://img.shields.io/github/v/release/emhat098/ev?label=release)](https://github.com/emhat098/ev/releases) [![Tests](https://github.com/emhat098/ev/actions/workflows/test.yml/badge.svg)](https://github.com/emhat098/ev/actions/workflows/test.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) **ev** is a [Zsh](https://www.zsh.org/) / [Oh My Zsh](https://ohmyz.sh/) plugin for **progressive command autocomplete**: a selectable suggestion list under your prompt that deepens as you type - commands → subcommands → flags. Use it as a [zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions) alternative when you want a **navigable list** (not only ghost text), including git, gh, docker, and other CLI subcommands. **Repository:** https://github.com/emhat098/ev Shows a **ranked list** below the line and continues into subcommands and flags: ```text ❯ git st ▸ status stash switch ``` ```text ❯ git status - ▸ -v --verbose -sb ``` Works in Linux terminals and iTerm2 (macOS). Install with Oh My Zsh, Zinit, Antigen, Zplug, or a plain `source`. ### What it's for | If you want… | ev does this | |--------------|--------------| | Faster CLI typing in zsh | Suggests matching tools from your PATH cache | | Git / GitHub CLI help while typing | `g` → `git`, `git s` → `status`, `gh repo l` → `list` | | Flag discovery without memorizing `--help` | Suggests next tokens / flags from curated chains + help cache | | A list you can navigate | ↑/↓ select · Tab accept · Enter runs (or accepts after ↑/↓) | | No lag while typing | Prefix lookup only; rebuild with `ev-rebuild-cache` when tools change | Suggestions come from a **local cache** (PATH tools + curated chains + optional `--help` discovery). Typing only does prefix lookups - the cache is **never** rebuilt on shell start or while you type. Refresh it yourself with `ev-rebuild-cache` when tools or chains change. ## Requirements * [Zsh](https://www.zsh.org/) 5.9+ (`add-zle-hook-widget`) * Optional: [Oh My Zsh](https://ohmyz.sh/) (recommended) * Optional: `timeout` (coreutils) to cap slow `--help` calls during cache build ## Installation Latest release: [v1.0.0](https://github.com/emhat098/ev/releases/tag/v1.0.0) ### Oh My Zsh 1. Clone this repository into oh-my-zsh's custom plugins directory: ```sh # HTTPS git clone https://github.com/emhat098/ev.git \ ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/ev # or SSH git clone git@github.com:emhat098/ev.git \ ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/ev # or GitHub CLI gh repo clone emhat098/ev ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/ev ``` Or from a local checkout of this repo: ```sh ./install.sh ``` `./install.sh` symlinks the plugin, updates `plugins=(...)`, adds late keybinds, and builds the cache once. 2. If you cloned manually (skipped `./install.sh`), add the plugin in `~/.zshrc`: ```zsh plugins=( # ... other plugins ev zsh-syntax-highlighting # keep near the end if you use it zsh-history-substring-search ) ``` 3. Restart the shell, then build the cache if you have not already (required after a plain clone): ```sh exec zsh ev-rebuild-cache ``` > **Note:** If you use `zsh-history-substring-search`, run `./install.sh` (or append the keybind block from that script) so ↑/↓ still navigate ev's list when it is visible. The installer adds bindings *after* other plugins load. ### Antigen ```zsh antigen bundle emhat098/ev antigen apply ``` Then restart and build the cache: ```sh exec zsh ev-rebuild-cache ``` ### Zinit ```zsh zinit light emhat098/ev ``` ```sh exec zsh ev-rebuild-cache ``` ### Zplug ```zsh zplug "emhat098/ev", as:plugin ``` ```sh exec zsh ev-rebuild-cache ``` ### Manual (no framework) ```sh git clone https://github.com/emhat098/ev.git ~/.zsh/ev # or: gh repo clone emhat098/ev ~/.zsh/ev echo 'source ~/.zsh/ev/ev.plugin.zsh' >> ~/.zshrc exec zsh ev-rebuild-cache ``` ## Usage Type normally at your prompt. While you are completing a known command chain, suggestions appear under the line. 1. `g` → `git` 2. `git s` → `status` 3. `git status` → `-v`, `--short`, … 4. `gh repo l` → `list` Press **Tab** to accept the highlighted suggestion (e.g. `g` → `git `). Use **↑/↓** to move in the list, then **Enter** to accept that selection. Plain **Enter** (without ↑/↓) runs the line as typed. After you install or remove CLIs, or edit `chains.zsh`, run `ev-rebuild-cache` so the list picks up the changes. ## Key bindings | Key | Widget / action | |-----|-----------------| | ↑ / ↓ | Previous / next suggestion (history when the list is hidden) | | Tab | Accept highlighted suggestion | | Enter | Run the line; after ↑/↓, accept the selected suggestion instead | | Shift-Tab / Ctrl+P | Previous suggestion | | Ctrl+N | Next suggestion | | Alt+Space | Accept highlighted suggestion | | Ctrl+G | Hide suggestion list | Widgets you can rebind with `bindkey` after the plugin loads: * `ev-tab` / `ev-enter` * `ev-up` / `ev-down` * `ev-prev` / `ev-next` * `ev-hide` * `ev-accept-widget` Example: ```zsh bindkey '^ ' ev-accept-widget ``` ## Configuration Override these variables in `~/.zshrc` **before** the plugin is loaded (or export them in the environment). | Variable | Default | Description | |----------|---------|-------------| | `EV_ENABLED` | `1` | Set to `0` to disable the suggestion UI | | `EV_MAX` | `10` | Maximum rows shown in the list | | `EV_TMP` | `${XDG_CACHE_HOME:-~/.cache}/ev` | Cache directory (persists across reboots; overrides XDG default) | | `EV_HELP_TIMEOUT` | `1` | Seconds per `--help` / `-h` call during `ev-rebuild-cache` | | `EV_HELP_MAX_DEPTH` | `2` | Nested help discovery depth (root = 0) | | `EV_HELP_MAX_CALLS` | `60` | Max help invocations per cache build | ```zsh EV_MAX=12 EV_HELP_MAX_CALLS=80 ``` ## Cache The index under `$EV_TMP` is load-only at runtime: * **Shell start** - loads `$EV_TMP/index.zsh` if it exists; does not scan `PATH` or run `--help` * **While typing** - prefix map lookups only * **Rebuild** - only when you run `ev-rebuild-cache` (or when `./install.sh` builds once during setup) ```sh ev-rebuild-cache ``` If no cache exists yet, startup prints: ```text ev: no suggestion cache yet - run: ev-rebuild-cache ``` Rebuild after: * Installing, upgrading, or removing CLI tools * Editing [`chains.zsh`](https://github.com/emhat098/ev/blob/main/chains.zsh) * Changing help-discovery limits and wanting a fuller index Cache files: `index.zsh`, `stamp`, `commands.txt`, `chains/` under `$EV_TMP` (default `${XDG_CACHE_HOME:-~/.cache}/ev/`). A one-time migrate copies an older `/tmp/ev-$UID` cache if present. ## Customizing chains Edit [`chains.zsh`](https://github.com/emhat098/ev/blob/main/chains.zsh). Keys are colon-separated command paths; values are next tokens (earlier = higher rank for short prefixes): ```zsh _EV_CHAINS[mycli]="init build deploy status" _EV_CHAINS[mycli:deploy]="--env --dry-run --force" ``` Then run `ev-rebuild-cache`. At build time, [`discover.zsh`](https://github.com/emhat098/ev/blob/main/discover.zsh) also runs `CMD --help` / `-h` / `help` and merges discovered subcommands and flags (cobra, gh, click, argparse styles; with depth and call limits). Nested help is skipped for tools that tend to block (for example `docker --help`). ## File layout | File | Purpose | |------|---------| | [`ev.plugin.zsh`](https://github.com/emhat098/ev/blob/main/ev.plugin.zsh) | Main plugin entry (Oh My Zsh / Antigen / Zinit load this) | | [`chains.zsh`](https://github.com/emhat098/ev/blob/main/chains.zsh) | Curated next-token catalogs | | [`discover.zsh`](https://github.com/emhat098/ev/blob/main/discover.zsh) | `--help` / `-h` discovery (used only during rebuild) | | [`build-cache.zsh`](https://github.com/emhat098/ev/blob/main/build-cache.zsh) | Builds `$EV_TMP/index.zsh` (invoked by `ev-rebuild-cache`) | | [`stamp.zsh`](https://github.com/emhat098/ev/blob/main/stamp.zsh) | Cache fingerprint helpers for `build-cache.zsh` | | [`install.sh`](https://github.com/emhat098/ev/blob/main/install.sh) | Optional Oh My Zsh helper (symlink, keys, one-time cache build) | | [`tests/`](https://github.com/emhat098/ev/blob/main/tests) | Unit tests (`zsh tests/run.zsh`) | ## Testing ```sh zsh tests/run.zsh ``` Covers stamp fingerprints, help parsing, curated chains, cache build, progressive context parsing, command/next-token filtering, accept/nav widgets, and manual-only cache loading (no auto-rebuild on init). ## Troubleshooting * **Message: `no suggestion cache yet`** - Run `ev-rebuild-cache` once, then keep using the shell as usual. * **No list appears** - Confirm `ev` is in `plugins=(...)` (or sourced), then `exec zsh`. Check `EV_ENABLED` is not `0`. Confirm `$EV_TMP/index.zsh` exists. * **Missing / stale commands** - Installs do not refresh the index automatically. Run `ev-rebuild-cache`. * **Missing nested tokens** (e.g. `gh repo list`) - Rebuild cache; ensure the CLI is on `PATH`; add a node in `chains.zsh` if needed. * **↑/↓ don't move the list** - Re-run `./install.sh` so late keybindings are applied after history-substring-search. * **Slow `ev-rebuild-cache`** - Lower `EV_HELP_MAX_CALLS` or `EV_HELP_MAX_DEPTH` in `~/.zshrc` before rebuilding. ```sh ls -la "${EV_TMP:-${XDG_CACHE_HOME:-$HOME/.cache}/ev}/" ``` ## Uninstallation 1. Remove `ev` from the `plugins=(...)` list in `~/.zshrc` (or remove the `source …/ev.plugin.zsh` line). 2. Remove the keybind block between `# >>> ev plugin keys >>>` and `# <<< ev plugin keys <<<` if present. 3. Delete the plugin and cache: ```sh rm -rf ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/ev rm -rf "${EV_TMP:-${XDG_CACHE_HOME:-$HOME/.cache}/ev}" # or: rm -rf ~/.zsh/ev # manual install ``` 4. Restart the shell: `exec zsh`. ## Links * Source: https://github.com/emhat098/ev * Releases: https://github.com/emhat098/ev/releases * Issues: https://github.com/emhat098/ev/issues * Owner: [@emhat098](https://github.com/emhat098) ## License MIT - see [LICENSE](LICENSE).