shuck

August 2, 2026 · View on GitHub

An interactive grepper for neovim - type a command, run it. Then look at the results, adjust the command, re-run it. So somewhere between vim-grepper and grep-as-you-type.

how is it different from other tools?

  • opens in an inline split, rather than a floating window.
  • uses the triggering buffer as context.
  • input is just a normal buffer, so you can leave insert mode to edit it.
  • run or re-run the input command via in insert mode, or in normal mode
  • display is just a normal buffer.
  • preserves last run command and command history.
  • command results are streamed in, so it feels snappy even for long-running commands.

Installation

With Neovim's native plugin manager (vim.pack, Neovim 0.12+):

vim.pack.add({ "https://github.com/dlants/shuck" })
require("shuck").setup({})

vim.keymap.set("n", "<leader>g", function()
  require("shuck").open({ cmd = "rg -H --no-heading --vimgrep " })
end)
vim.keymap.set("n", "<leader>/", function()
  require("shuck").open({ cmd = "rg -H --no-heading --vimgrep " })
end)

open({ cmd = ... }) creates a shuck split at the top of the window. cmd is the command shuck starts from: the input buffer is seeded with it, and <C-u> resets to it. It is optional and defaults to rg -H --no-heading --vimgrep .

Each base command remembers its own last accepted search, so opening again with the same base restores that search — command, results and selection — without re-running it. Opening while shuck is already up never stacks a second picker: the same base command just focuses it, a different one re-points it at the new command with a cleared display.

The search root is picked from the current buffer (cwd if the buffer is under it, else the nearest git root, else the buffer's directory), and per-directory command history is persisted under stdpath("data")/shuck/.

Close with <Esc>, <C-c>, :q or require("shuck").close(). :Shuck is available as a convenience wrapper for open({}).

Keymaps (inside the picker)

  • insert mode, input buffer
    • <CR> — open the selected result
    • <C-CR> — run the command in the prompt
    • <C-j> / <C-k> — next / previous result
    • <C-u> — reset the input buffer to the command shuck was opened with (start a fresh search)
    • <Up> / <Down> — cycle prefix-matched command history
    • <C-r> — open the history picker
    • <C-x> / <C-v> / <C-t> — open in split / vsplit / tab
    • <C-c> — close
  • normal mode, input buffer
    • <CR> / <C-CR> — re-run the command in the prompt
    • <Up> / <Down> — cycle prefix-matched command history
    • <C-r> — open the history picker
    • <C-x> / <C-v> / <C-t> — open in split / vsplit / tab
    • q — send results to the quickfix list
    • <Esc> / <C-c> — close
  • history picker (<C-r>, takes over the results buffer; keys go to the input buffer as usual)
    • <C-j> / <C-k> or <Down> / <Up> — next / previous history entry
    • <CR> — accept the entry into the input buffer without running it
    • <C-CR> — accept and run it
    • editing the input buffer leaves the picker and shows results again
    • <C-c> — close
  • normal mode, results buffer
    • <CR> — open the selected result
    • <C-x> / <C-v> / <C-t> — open in split / vsplit / tab
    • i — jump back to the input buffer, enter insert mode
    • q — send results to the quickfix list
    • <C-c> — close

Opening a result also seeds the quickfix list with the whole result set (the opened entry is the current one), so :cnext/:cprev walk the rest of the search.

Setup

require("shuck").setup({
  max_render      = 200,
  max_results     = 10000,
  stream_flush_ms = 30,
  spinner_ms      = 80,
  history_dir     = nil, -- defaults to stdpath("data")/shuck
})

Development

Headless tests run with nvim -l lua/shuck/run_tests.lua from the repo root.

Other neovim plugins by dlants:

  • magenta.nvim — transparent tools for agentic AI workflows.
  • needle — a fast, signal-aware fuzzy picker.
  • glean — a git diff reviewer.