Bidirectional communication between Neovim and Pi.

Neovim plugin with a bundled Pi extension.
This package works in two modes:
pi install git:github.com/aliou/nvim-pi and use the extension directly in terminal Pi sessionsWhen Neovim is not running, the extension still loads cleanly and degrades gracefully.
| Tool / Command | Description |
|---|---|
nvim_context | Query the connected Neovim instance for focused buffer, splits, diagnostics, or current function |
/neovim:settings | Configure Neovim integration settings for the Pi extension |
/neovim:undotree [file] | Inspect a Neovim persistent undo tree for a file |
@vim: autocomplete | Type @vim: in Pi’s input to autocomplete file paths from visible Neovim splits |
Additional disabled-by-default behavior:
extensions/undo can update matching Neovim persistent undo files after successful Pi edit and write tool calls.Behavior provided by hooks:
write and edit tool callschecktime timer while Pi is open to detect external file changes<C-Space> by default) to send editor context to Pi:PiNvimStatusInstall this repo as a Neovim plugin. The lua/ directory is runtimepath-compatible.
Example with vim.pack:
vim.pack.add({ { name = "nvim-pi", src = "https://github.com/aliou/nvim-pi" } })
require("pi-nvim").setup()
Use an existing Pi git install as a Neovim plugin:
pi install git:github.com/aliou/nvim-pi
vim.opt.runtimepath:append(vim.fn.expand("~/.pi/agent/git/github.com/aliou/nvim-pi"))
require("pi-nvim").setup()
If you installed it into project settings with pi install -l, use that project’s .pi/git/github.com/aliou/nvim-pi path instead.
Local checkout setup for development:
vim.opt.runtimepath:append(vim.fn.expand("/absolute/path/to/nvim-pi"))
require("pi-nvim").setup()
Requirements:
pi must be on PATHnvim must support serverstart() and --remote-exprWhen you open Pi through pi-nvim, the plugin launches:
pi --extension /absolute/path/to/nvim-pi
The extension then injects runtime editor state through hooks. By default (load_extension = "auto"), the plugin checks whether nvim-pi is installed globally in Pi and skips --extension if so — this avoids loading the extension twice when you already installed it with pi install.
You can also install it directly in Pi:
pi install git:github.com/aliou/nvim-pi
This is useful if you want the extension available in terminal Pi sessions too. If no Neovim instance is running, the extension still loads and simply reports that no instance was found when needed.
/neovim:settings currently exposes:
| Setting | Default | Description |
|---|---|---|
Connection status messages | on | Show nvim: connected / no instance found style messages in the Pi session |
Editor state injection | off | Inject current Neovim editor state (open splits, cursor position) into each prompt automatically |
@vim: autocomplete | enabled | Enable autocomplete for open Neovim splits |
Persistent undo tools | disabled | Update Neovim persistent undo files after successful Pi edit and write tool calls |
Feature toggles are shown as unavailable if Pi did not load that extension entry point.
require("pi-nvim").setup({
auto_start = true,
data_dir = nil,
-- Pi CLI flags
models = nil,
provider = nil,
model = nil,
thinking = nil,
load_extension = "auto", -- "auto": skip --extension if installed globally; true: always pass; false: never pass
extra_args = nil,
-- Window configuration
win = {
layout = "auto",
width_threshold = 150,
width = 80,
height = 20,
focus_source_on_stopinsert = true, -- switch to source window on exiting terminal mode
keys = {
close = { "<C-q>", mode = "n", desc = "Close Pi" },
stopinsert = { "<C-q>", mode = "t", desc = "Exit terminal mode" },
suspend = { "<C-z>", mode = "t", desc = "Suspend Neovim" },
picker = { "<C-Space>", mode = "t", desc = "Open context picker" },
},
},
})
The plugin does not create global leader mappings by default.
Example mappings:
vim.keymap.set("n", "<leader>po", require("pi-nvim").open, { desc = "Open Pi" })
vim.keymap.set("n", "<leader>pc", require("pi-nvim").close, { desc = "Close Pi" })
vim.keymap.set("n", "<leader>pp", require("pi-nvim").toggle, { desc = "Toggle Pi" })
Terminal/window-local keys are configured under setup({ win = { keys = ... } }).
The nvim_context tool supports these actions:
focused_buffer - focused file, cursor position, selection, filetypesplits - all visible splits with metadatadiagnostics - diagnostics for the current buffercurrent_function - treesitter info for the symbol at the cursorIf multiple matching Neovim instances are found, Pi prompts you to choose one when UI is available.
Commands and API:
:PiNvimStatus - show RPC and terminal staterequire("pi-nvim").open() - open the Pi terminalrequire("pi-nvim").close() - close the Pi terminalrequire("pi-nvim").toggle() - toggle the Pi terminalrequire("pi-nvim").start() - start the RPC server manuallyrequire("pi-nvim").stop() - stop the RPC server manuallyrequire("pi-nvim").status() - get the current RPC staterequire("pi-nvim").is_open() - check if the Pi terminal is openPi extension commands:
/neovim:settings - configure connection messages, @vim: autocomplete, and persistent undo integration/neovim:undotree [file] - open a persistent undo tree overlay for a file; when no file is provided, Pi shows a picker if UI is availableCheck:
:PiNvimStatus shows the RPC server as runningpi and nvim are both on PATH~/.local/share/nvim/pi-nvim/Discovery prefers:
Pi will prompt for selection in interactive mode. In non-interactive mode, the tool returns an error instead of guessing.
Check:
<stdpath('log')>/pi-nvim/rpc.log (typically ~/.local/state/nvim/pi-nvim/rpc.log):PiNvimStatus:checkhealth pi-nvim
Neovim plugin (Lua) Pi extension (TypeScript)
------------------- ------------------------
require("pi-nvim").setup() pi --extension /path/to/nvim-pi
| |
v v
rpc.start() extensions/nvim/index.ts registers:
lockfile.create() - hooks (editor state, lifecycle)
| - tool (nvim_context)
v - command (/neovim:settings)
~/.local/share/nvim/pi-nvim/ - renderers (connection, diagnostics)
<cwd-hash>-<pid>.json |
| session_start discovers lockfile
| before_agent_start queries splits
| tool_result reloads files
| turn_end requests diagnostics
v |
nvim --server <socket> --remote-expr <luaeval(...)> <------+
Core (src/) has zero Pi dependencies:
nvim.ts lockfile discovery + RPC
types.ts domain types + type guards
format.ts shared formatting helpers
Additional extensions:
splits-autocomplete/ @vim: autocomplete for open splits
undo/ /neovim:undotree plus disabled-by-default persistent undo update hooks
Additional Lua features:
cli/terminal open/close/toggle Pi in a split or float
cli/picker <C-Space> context picker to send info to Pi
cli/watch periodic checktime timer while Pi is open
The TypeScript extension discovers Neovim instances through lockfiles, then queries the running editor through nvim --remote-expr, which evaluates require("pi-nvim").query(...) inside Neovim.