Files

97 lines
4.0 KiB
Markdown

# nvim-agentic
A Claude Code plugin that turns the user's running Neovim into a first-class
collaborator.
It assumes two things are already wired up:
1. **`mcp-neovim-server`** reaching the running nvim's RPC socket (default
`/run/user/1000/nvim.sock`, created by NixVim's `extraConfigLuaPre`
`serverstart` at editor startup). The plugin ships a `.mcp.json` that points
the `neovim` MCP server at the `mcp-session-pool` over Streamable-HTTP
(`pool.localhost:12010/p/neovim/mcp`); the pool fronts a supervised
stdio→HTTP bridge. Tools surface as `vim_*`.
2. **`coder/claudecode.nvim`** loaded inside nvim, with `<leader>a*` keymaps
wired (toggle, focus, send, accept/deny diff).
This plugin doesn't ship those — it depends on them and ties them together with
one agent and three skills.
## Connection path
```text
Claude Code session
└─ .mcp.json → http://pool.localhost:12010/p/neovim/mcp (Streamable-HTTP)
└─ mcp-session-pool (:12010, M=1 session pooler, /repin endpoint)
└─ nvim-mcp-bridge — supergateway (:12016, --stateful)
└─ mcp-neovim-server (stdio child)
└─ /run/user/1000/nvim.sock (nvim msgpack-RPC)
└─ the running Neovim
```
No MetaMCP in this path: nvim is ephemeral and MetaMCP never re-dials a
restarted upstream. The pool *does* re-establish (a fresh `initialize`), so
pool→bridge self-heals. Recovery is event-driven — a `systemd.path` watching
the nvim socket fires a oneshot that POSTs `/repin` to the pool when the socket
(re)appears; no polling. The pool and bridge run on emmett as systemd services
under user `oleks`, so the per-user socket is directly reachable. If
`vim_*` tools are missing: socket exists? `systemctl status
nvim-mcp-bridge`? `http://127.0.0.1:12010/pools/neovim` pinned? session
restarted since the wiring landed?
## Layout
```text
nvim-agentic/
├── .claude-plugin/plugin.json # manifest + userConfig (socket, config path)
├── agents/
│ └── companion.md # the nvim-companion agent identity
└── skills/
├── editor-introspect/ # read live state via the vim_* MCP tools
├── editor-act/ # drive nvim safely (open, jump, toggle, run)
└── claude-code-handoff/ # delegate buffer edits to the in-editor Claude
```
## Agent
`companion` — answers questions about the *running* nvim ("what's bound to
`<leader>ff`?", "is lualine actually using catppuccin-mocha?") and acts on it
("open neovim.nix at the lualine block"). It reads the declarative NixVim config
as the source-of-truth for *why* things are set up the way they are, and the
live editor for *what is actually loaded right now*.
## Skills
- **`editor-introspect`** — read-only queries against the live nvim (keymaps,
buffers, options, diagnostics, plugins, messages, cursor, selection).
- **`editor-act`** — safe driving: open files, jump to definitions, trigger user
keymaps, toggle UI. Does **not** edit buffer contents.
- **`claude-code-handoff`** — when the work is "change code in this buffer,"
hand it to the in-editor Claude Code session so the user gets a diff to accept
or reject inline.
## Why split it three ways
The companion's three jobs have different blast radii:
- *Reads* are free; do them eagerly.
- *Edits to navigation/UI* are cheap to undo; do them when asked.
- *Edits to code* deserve a diff and human review; route them through the inner
Claude that already has the right UX for that.
The skills enforce that separation so the agent doesn't drift into running
`nvim_buf_set_text` directly when it should be sending to the Claude Code split.
## Configuration
- **`nvim_socket`** — default `/run/user/1000/nvim.sock`. Where the
running nvim listens; must match `NVIM_SOCKET_PATH` in the
`nvim-mcp-bridge` service that `mcp-neovim-server` runs under.
- **`config_path`** — default
`/home/oleks/projects/servers/emmett/nixos/neovim.nix`. The
declarative NixVim source the companion cites from.
## License
MIT.