Skip to content

Discussion: shell buffer insertion for editable suggestions #5

@subinium

Description

@subinium

Background

hey confirms the suggested command in a TUI prompt and then Command::new($SHELL).arg("-c").arg(command) runs it as a child process (src/main.rs:336-345). This means:

  • cd, export, source cannot affect the parent shell — currently surfaced as Warn with "affects only this subshell" (correct, but a workaround).
  • The user can't trivially edit the suggestion before running it (the confirm flow is yes/no/edit-in-place, not "hand it to my shell").

A common pattern in shell_gpt, aichat, and friends is a shell function that captures the suggested command and pushes it to the readline buffer (print -z in zsh, commandline in fish, READLINE_LINE in bash). The user sees the command on their prompt, can edit it, and runs it themselves.

Constraint

hey init <shell> is taken — it generates clap_complete scripts (src/completions.rs:11-20, dispatched at src/main.rs:62-65). Whatever this feature is called must not collide.

Shell-buffer mechanics (worth getting right up front)

  • zsh: print -z -- "$cmd" pushes onto the editor buffer stack; the line appears at the next prompt with the cursor at end. Works from a regular function. Fine.
  • fish: commandline -- "$cmd" replaces the current command line inline. Works from a regular function. Fine.
  • bash: READLINE_LINE is only writable from a function invoked via bind -x '"\C-g": _hey_edit' (i.e. a readline keybinding callback). A function called from the prompt cannot prefill the next buffer. Any bash story here requires the user to bind a key, or we accept "bash gets a less nice UX" (e.g. just copy to clipboard / print, no buffer push).

This is the load-bearing constraint — the bash limitation shapes the whole design.

Proposed shape

Two pieces:

  1. A clean-stdout mode in hey. Today --dry-run prints a decorated TUI block via print_command_block (src/main.rs:238-246) — not capturable. Make --dry-run emit just the bare command on stdout when stdout is not a TTY (we already detect this at src/main.rs:121). No new flag needed.
  2. A hey init <shell> extension OR a sibling subcommand that prints a shell function the user sources. The function calls hey --dry-run "$@" | <buffer-push>.

Open question: extend hey init to also emit the helper, or add a sibling like hey shell-init <shell>? The former is one install command but mixes completions + helper; the latter is symmetric but adds a subcommand. Leaning toward extending hey init since both are "shell glue" and the snippet is small.

Sketch

zsh:

hey-edit() {
  local out
  out="$(command hey --dry-run "$@")" || return
  print -z -- "$out"
}

fish:

function hey-edit
    set -l out (command hey --dry-run $argv); or return
    commandline -- $out
end

bash (requires keybinding, not a plain function):

_hey_edit() {
  local out
  out="$(command hey --dry-run "${READLINE_LINE}")" || return
  READLINE_LINE="$out"
  READLINE_POINT=${#READLINE_LINE}
}
bind -x '"\C-g": _hey_edit'   # type prompt, hit Ctrl-G

The bash flow is genuinely different (you type the prompt onto the command line first, then hit a binding). If that's too much UX divergence, bash can fall back to clipboard.

Why not always use this?

  1. Today hey ls docker containers is one keystroke. Buffer insertion adds Enter.
  2. Users who want pure execution shouldn't be forced to install a shell function.

Buffer insertion stays opt-in alongside the existing flow.

Out of scope

  • PowerShell / Windows.
  • Streaming the suggestion as it generates.
  • Replacing the existing confirm() prompt.

Decision points

  1. Reuse --dry-run (+ make it TTY-aware) vs. add a dedicated --print flag?
  2. Extend hey init <shell> to emit completions + helper, or add a separate hey shell-init <shell>?
  3. Bash: bind -x snippet, clipboard fallback, or skip bash for v1?

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestquestionFurther information is requested

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions