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 / tabq— 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 / tabi— jump back to the input buffer, enter insert modeq— 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.
Related plugins
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.