Skip to content

Repository files navigation

herdr-ask

Lightweight command generation and terminal chat — a native popup assistant for Herdr that also works from any terminal.

Describe the command you need, review it, and insert it into your shell with one keypress. herdr-ask never executes commands — it only generates, previews, and inserts text, and only when you explicitly confirm.

 herdr-ask  COMMAND   codex / auto   fish   ~/git/tesela

 Describe the command you need:
 > find files larger than 500 MB but ignore .git and node_modules

 Enter submit   Tab mode   Ctrl+P backend   Ctrl+M model   Esc close

Backends: Ollama and any OpenAI-compatible server (no account needed), direct OpenAI / Anthropic APIs with keys in your OS keychain, and your existing Codex CLI, Claude Code, and Pi subscriptions through ephemeral headless invocations. No telemetry, no daemon, no stored conversations.

One-minute install

cargo install herdr-ask     # or: brew install TaylorFinklea/tap/herdr-ask
herdr-ask setup             # detects Herdr, CLIs, Ollama; writes config

Or as a Herdr plugin (downloads the release binary and verifies its checksum):

herdr plugin install TaylorFinklea/herdr-ask

Try it immediately, no Herdr required:

herdr-ask command --print "compress this directory excluding node_modules"

Herdr keybinding setup

Bind the plugin action (recommended):

[[keys.command]]
key = "prefix+a"
type = "plugin_action"
command = "dev.herdr-ask.open"
description = "ask AI"

Or bind the standalone binary as a direct popup:

[[keys.command]]
key = "prefix+a"
type = "popup"
command = "herdr-ask"
description = "ask AI"
width = "80%"
height = "70%"

Either opens an 80%×70% popup over the focused pane. The popup knows which pane it came from: Ctrl+Enter inserts the generated command back into that pane — without pressing Enter for you.

Ollama setup

With Ollama running locally, herdr-ask works out of the box with zero configuration and zero API keys:

ollama pull qwen3.6:35b-a3b-nvfp4   # any model you like
herdr-ask                            # opens the popup; Ctrl+M picks a model

model = "auto" remembers your last choice, auto-selects when exactly one model exists, and opens the picker otherwise.

Model picker: scoped vs all

When a backend exposes many models (Pi discovers dozens via pi --list-models), the picker (Ctrl+M) opens on a scoped short list and Tab toggles to the full list. Define the scoped set with model_scope glob patterns per backend — "auto" and any models aliases are always scoped:

[backends.pi]
kind = "process"
adapter = "pi"
model_scope = ["openai-codex/*", "example-cloud/*"]   # picker opens on these

Navigating the picker: arrow keys or j/k, g/G to jump to the top/bottom, PageUp/PageDown to page. Press / to open a filter box and type to narrow the list (Esc clears it), then Enter to select.

Prompt history

In the editor, / recall your previous prompts, with separate history for Command and Chat mode. History is in-memory and discarded when the popup closes (the spec keeps everything ephemeral by default).

To keep history across sessions, opt in — this writes your prompts (not responses) to an owner-only file under the state dir:

[history]
persist = true       # off by default; crosses the ephemeral line deliberately
max_entries = 500

API keys and the keychain

Keys for direct OpenAI/Anthropic backends live in the native credential store (macOS Keychain, Linux Secret Service) — plaintext keys in config.toml are rejected:

herdr-ask auth set openai        # prompts with echo off, stores in keychain
herdr-ask auth get-metadata openai
herdr-ask auth test openai
herdr-ask auth delete openai

External secret managers work too — the command runs without a shell, with a minimal environment and a 5-second timeout:

credential = { source = "command", command = ["op", "read", "op://Private/OpenAI/api-key"] }

Codex / Claude Code / Pi backends

Reuse the CLIs you already pay for. Every request is a fresh, ephemeral, tool-free invocation in your source pane's working directory:

Backend Invocation
codex codex exec --ephemeral --skip-git-repo-check --sandbox read-only --output-last-message <tmp> -
claude claude --print --output-format text --no-session-persistence [--max-turns 1] --system-prompt … <prompt>
pi pi --print --no-session --no-tools --no-context-files --no-extensions --no-skills --no-prompt-templates --system-prompt … <prompt>

herdr-ask verifies these flags against the installed binary at runtime (herdr-ask doctor). If a CLI cannot run ephemerally — e.g. an old Claude without --no-session-persistence — the backend is marked unavailable rather than silently persisting a session. Optional flags like --max-turns are included only when the installed version supports them.

Any other CLI can be wired up as a custom process backend with configurable argv placeholders, prompt transport (stdin/argument/file), and output source (stdout/stderr/file).

Security model

  • Never executes. There is no execute shortcut. Insertion uses Herdr's literal pane send-text and never appends Enter.
  • Suspicious output is gated. Responses containing prose or multiple code blocks cannot be inserted without an explicit "insert anyway".
  • Ephemeral by default. Conversations live in memory and die with the popup. Process backends run in no-session modes. Nothing is logged.
  • Secrets stay secret. Keys live in the OS keychain, are resolved at request time into zeroizing containers, are redacted from error messages, and are scrubbed from process-backend environments.
  • Explicit context only. Terminal output is attached only via Ctrl+O, after you choose how many lines.
  • TLS enforced except loopback; non-loopback plain HTTP requires allow_insecure_http = true.
  • No shell interpolation anywhere — every subprocess is spawned by argv.
  • No telemetry, no analytics, no accounts, no update checker.

Configuration reference

herdr-ask config path prints the active location; precedence is $HERDR_ASK_CONFIG$HERDR_PLUGIN_CONFIG_DIR/config.toml (only when that file exists) → the platform config directory. See the fully commented examples/config.toml.

Popup vs standalone config. Herdr sets HERDR_PLUGIN_CONFIG_DIR for plugin popups. herdr-ask uses a plugin-scoped config.toml there only if one exists; otherwise it reads the same platform config as the standalone binary — so a single config serves both. Drop a config.toml into the plugin config dir (see herdr plugin config-dir dev.herdr-ask) only if you want popup-specific settings.

Prompts are customizable at four levels (built-in → [prompts.<mode>][backends.<id>.prompts]--system-prompt/--append-system-prompt), with template variables like {{shell}}, {{os}}, and {{cwd}}. Keybindings are remappable under [keys].

CLI surface:

herdr-ask [command|chat] [--print] [--json] [REQUEST...]
herdr-ask setup | doctor [BACKEND] | backends | models [BACKEND]
herdr-ask auth set|get-metadata|delete|test BACKEND
herdr-ask config path|validate [--strict]|edit

Ctrl+X in the popup exports the current conversation to a markdown file in the source directory — the only way prompts or responses ever touch disk.

Troubleshooting

Start with:

herdr-ask doctor          # pass/warn/fail across config, Herdr, backends
herdr-ask doctor --json   # machine-readable
  • "backend unavailable: does not support --no-session-persistence" — upgrade that CLI; herdr-ask refuses non-ephemeral fallbacks by design.
  • Ctrl+Enter does nothing — your terminal may not support the kitty keyboard protocol that distinguishes Ctrl+Enter from Enter. Rebind: [keys] insert = "ctrl+i".
  • "multiple models; choose one"herdr-ask models <backend> then --model <id>, or pick once with Ctrl+M (remembered).
  • Popup opens but insertion says the pane is missing — the source pane closed; the command is on your clipboard instead.
  • Ollama not detected — check curl http://127.0.0.1:11434/v1/models.

Homebrew

The repo ships a formula template in Formula/herdr-ask.rb. Tap workflow:

  1. Create/clone your tap repo (github.com/<you>/homebrew-tap).
  2. Copy Formula/herdr-ask.rb into its Formula/ directory.
  3. Replace each REPLACE_WITH_*_SHA256 with the matching value from the release's SHA256SUMS.
  4. brew install <you>/tap/herdr-ask.

Uninstall

herdr plugin uninstall dev.herdr-ask   # plugin install
cargo uninstall herdr-ask              # cargo install
brew uninstall herdr-ask               # Homebrew
# optional cleanup:
herdr-ask auth delete openai           # remove stored keys (repeat per backend)
rm -rf "$(dirname "$(herdr-ask config path)")"

Development

cargo test                # unit + integration (mock HTTP, fixture CLIs)
cargo clippy --all-targets --all-features -- -D warnings
cargo fmt --check
cargo audit

For local plugin development: HERDR_ASK_LOCAL_BINARY=target/release/herdr-ask sh scripts/install-release-binary.sh then herdr plugin link ..

License

Dual-licensed under MIT or Apache-2.0, at your option.

About

Lightweight command generation and terminal chat for Herdr and any terminal

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages