Queue questions about lines/selections in neovim and flush them as one payload to a CLI coding agent (claude, codex, aider, …) running in an adjacent tmux pane.
<leader>aa opens a capture screen showing the current line or selection as a
read-only fenced block; type a question below it and :wq to queue the entry
(:q cancels). <leader>af opens a flush screen holding every queued entry as
editable text plus a separate trailing-prompt field; :wq sends, :q cancels.
The plugin resolves a target pane (from $TMUX_ORIGIN_PANE_ID if set, or a
fallback split it manages itself), starts the agent if the pane is at a shell
prompt, assembles the payload (file location plus, for selections, the selected
code), and sends it.
Both screens are floating windows edited like a file: :wq (or ZZ) confirms,
:q (or q) cancels.
- neovim 0.10+
- tmux (the surrounding terminal must be inside a tmux session)
- a CLI coding agent in
$PATH(default:claude)
With lazy.nvim:
{
'lucatume/agent-ask.nvim',
cmd = { 'AgentAsk', 'AgentSend', 'AgentClear' },
keys = {
{ '<leader>aa', mode = { 'n', 'v' }, desc = 'agent: ask' }, -- capture current line/selection
{ '<leader>af', mode = 'n', desc = 'agent: send' }, -- flush queue
},
config = function()
require('agent-ask').setup({})
end,
}setup() accepts a table merged over the defaults:
require('agent-ask').setup({
agent = { cmd = 'claude', args = {} },
agent_processes = { 'claude', 'codex', 'aider', 'pi' },
shell_processes = { 'zsh', 'bash', 'fish', 'sh', 'dash', 'ksh', 'tcsh' },
fallback_split = { direction = 'right', size = '50%' },
format = 'auto', -- 'auto' | 'file'
agent_startup_timeout_ms = 8000,
agent_startup_poll_ms = 100,
agent_postinit_delay_ms = 500,
paste_settle_ms = 150,
keymaps = { ask = '<leader>aa', send = '<leader>af' },
})format:
auto(default) — file reference for current-line context on disk; file reference plus fenced code block for visual selections; fenced code block with a comment header for unsaved buffers.file— always sendIn path:range: question, no code body.
:AgentAsk— open the capture screen for the current line: a read-only fenced block on top, an editable prompt area below.:wqqueues the entry,:qcancels. An empty prompt on:wqqueues nothing.:'<,'>AgentAsk(or:N,MAgentAsk) — capture a range; the selection is shown as the fenced block and included in the per-entry payload.:AgentSend— open the flush screen: the assembled queue as editable text, a separator, then a trailing-prompt field. Edit freely, then:wqto send (trailing text, if any, is appended after the queue) or:qto cancel without sending. A no-op on an empty queue.:AgentClear— empty the queue.
:AgentSend auto-resolves the target pane and starts the agent if the pane is
at a shell prompt — there is no separate start command.
$TMUX_ORIGIN_PANE_IDif set and the pane still exists. Set this in the shell that launches neovim to anchor the agent next to a specific pane.- The fallback pane this plugin previously created, if it still exists.
- A new split (direction/size from
fallback_split).
If the resolved pane is at a shell prompt, the agent is started before the prompt is sent.
I drive this with a Cmd+K shortcut in tmux: from any pane, it splits right,
opens nvim there, and forwards the originating pane id so the agent lands
back in the pane I came from. Tmux sees Cmd+K as M-k once the terminal
forwards it as ESC+k:
# in ~/.tmux.conf
bind -n M-k run-shell "tmux split-window -h \
-t '#{pane_id}' \
-c '#{pane_current_path}' \
-e 'TMUX_ORIGIN_PANE_ID=#{pane_id}' \
'nvim .'"-e does not format-expand its argument, so the binding wraps the whole
command in run-shell and lets tmux expand #{pane_id} first.
Note for Ghostty: by default super+k is bound to clear_screen and never
reaches tmux. Override it to send ESC+k instead:
# in ~/.config/ghostty/config
keybind = super+k=text:\x1bk
macos-option-as-alt defaults to false, so Option+K still types ˚ and
does not collide with this binding.
Tests use plenary.nvim's busted
runner. Plenary is expected at ~/.local/share/nvim/lazy/plenary.nvim (the
default lazy install location); adjust tests/minimal_init.lua if it lives
elsewhere.
make testSpec layout:
tests/
minimal_init.lua # rtp bootstrap
config_spec.lua
context_spec.lua
payload_spec.lua
tmux_spec.lua
queue_spec.lua
assemble_spec.lua
When fixing a bug or adding behaviour, write the failing spec first, then make it pass.
