Files
workflow-miner/docs/research-zsh-integration.md
vikingowl 48923450f8 docs: add architecture plan and research notes
Initial project documentation for workflow-miner — a Rust CLI + zsh
plugin that mines recurring command workflows from Atuin shell history.
2026-02-22 08:41:50 +01:00

4.0 KiB

Research: zsh Plugin Integration

Plugin Structure (Universal Minimum)

my-plugin/
  my-plugin.plugin.zsh    # Main entry point (required, name must match dir)
  functions/              # Optional: autoloaded functions
    _my-plugin            # Optional: completion function
  bin/                    # Optional: executables added to $PATH

Plugin Manager Compatibility

Manager Loading mechanism Install method
oh-my-zsh Sources $ZSH_CUSTOM/plugins/<name>/<name>.plugin.zsh git clone into plugins dir, add to plugins=()
zinit zinit light user/repo Configured in .zshrc
antigen antigen bundle user/repo Configured in .zshrc
antidote Listed in .zsh_plugins.txt Listed in text file
sheldon TOML config Configured in sheldon.toml
Manual source /path/to/plugin.zsh User adds source line

All managers expect a Git repo with a .plugin.zsh file. Host on GitHub and it works everywhere.

Zsh Plugin Standard Best Practices

From the Zsh Plugin Standard:

  1. Standardized $0 handling for reliable plugin directory detection:

    0="${ZERO:-${${0:#$ZSH_ARGZERO}:-${(%):-%N}}}"
    0="${${(M)0:#/*}:-$PWD/$0}"
    
  2. Use functions/ subdirectory for autoloaded functions and completions (managers add to $fpath)

  3. Use bin/ subdirectory for executables (managers add to $PATH)

  4. Provide an unload function named {pluginname}_plugin_unload

  5. Use add-zsh-hook for hooks rather than defining precmd/preexec directly

  6. Completion files named _<command> in functions/

ZLE (Zsh Line Editor) Mechanisms

Key variables

  • $BUFFER — current command line being edited
  • $LBUFFER / $RBUFFER — left/right of cursor
  • $CURSOR — cursor position
  • zle -I — invalidate display (allows printing mid-edit)
  • zle reset-prompt — redraw prompt after external output
  • zle -R "message" — display message in status area

Widget pattern for LLM/suggestion integration

_my_widget() {
    zle -I
    local result
    result=$(my-binary --some-args 2>/dev/null)
    if [[ -n "$result" ]]; then
        BUFFER="$result"
        CURSOR=${#BUFFER}
    fi
    zle reset-prompt
}
zle -N _my_widget
bindkey '^X^S' _my_widget

Hook functions

  • preexec — fires after Enter but before execution, receives command as $1
  • precmd — fires after command finishes, before prompt draws
  • zshaddhistory — fires when a command is added to history
  • chpwd — fires on directory change

Registration:

autoload -Uz add-zsh-hook
add-zsh-hook preexec my_preexec_fn
add-zsh-hook precmd my_precmd_fn

Distribution Strategy for a Rust Binary + zsh Plugin

The plugin is a thin shell wrapper around the Rust binary:

wfm-zsh/
  wfm.plugin.zsh       # Check binary in PATH, set up hooks, keybindings
  functions/
    _wfm                # Completion function

The .plugin.zsh file:

  1. Checks if the binary is in $PATH
  2. Sets up shell hooks (precmd/preexec if ambient mode)
  3. Adds completions to $fpath
  4. Defines ZLE widgets and keybindings
  5. Defines convenience aliases/functions

Distribution:

  • Host on GitHub as a standalone repo (works with all managers)
  • Provide manual install instructions (source line)
  • Binary distributed separately via cargo install or Homebrew
  • Optionally submit to oh-my-zsh as a community plugin

References