Claude, Codex, Copilot, Grok and Pi as a Neovim buffer β with the editor handed back to them
English | ζ₯ζ¬θͺ
Demo β’ Features β’ Requirements β’ Installation β’ Configuration β’ Usage
01-parallel-chats.mp4
Three chats, opened in seconds, working on three different questions at once β in one Neovim.
Unlike chat plugins that send static context to an LLM, vibing.nvim gives the AI direct access to your running Neovim through CLI backends and MCP.
- π€ Neovim as an agent tool β the AI reads and writes buffers, runs Ex commands and queries your live LSP (diagnostics, definitions, references, symbols)
- π¬ The chat is a Markdown buffer β your keymaps, your motions, your search; saved under
.vibing/chat/, resumable, greppable, version-controllable - π Multi-backend β Claude, Codex, GitHub Copilot, Grok or Pi, switched globally or per chat
- π§΅ Concurrent chats β open as many as you like; start one while another is still streaming
- πͺ Multi-agent orchestration β one chat creates and drives worker chats, then aggregates what they report back
- π± Git worktree workflow β isolate a task in
.vibing/worktrees/<branch>/by asking for it - π Per-turn diffs β every turn ends in
### Modified Files;gdopens a patch viewer. The baseline is a git tree snapshot, so ased -ithrough Bash is tracked like anEdit - π‘οΈ Granular permissions β allow/deny/ask per tool, path and command patterns, answered in the buffer without killing the turn
- β³ Usage-limit scheduling β
<CR>parks your message instead of burning a doomed request, and sends it verbatim once the limit resets - π Per-chat response language
- Need local/offline models (Ollama, etc.)
- Prefer minimal dependencies β vibing.nvim needs Node.js for the MCP server
- Want a battle-tested plugin with a large community (we're still growing!)
vibing.nvim doesn't conflict with completion plugins (Copilot, Codeium) or other chat plugins β they compose well.
Real, unedited sessions against the CLI β same plugin, same defaults, no mock-ups.
A turn closes with ### Modified Files; press gd on a filename and a three-pane patch viewer
opens β the tree as it was when the turn started, next to what it is now.
02-per-request-diff.mp4
When a tool needs your say-so, the options are written into the chat. Delete the lines you don't
want and press <CR>. The hook blocks while you decide, so the turn resumes instead of being
killed and retried β the model keeps everything it had worked out so far.
03-approval-prompt.mp4
A choice list is rendered into your unsent section. Write your answer under it, press <CR>, and
it goes straight back as the tool's return value; the same turn carries on.
04-ask-user-question.mp4
Ask for parallel work and the chat becomes an orchestrator: a worker chat per task, each with its
own transcript you can open mid-task. ## Request and ## Report record who said what to whom.
05-orchestration.mp4
Played at 3Γ speed.
"Start this in its own worktree" is all it takes: a branch under .vibing/worktrees/<branch>/,
and every edit, test run and commit happens in there. The checkout you are looking at never moves β
note that the file in the left window stays untouched throughout.
06-worktree.mp4
Ask to be walked through a code path and you get a real tour: the quickfix list holds the whole
route (walk it again with :cnext), each stop opens the actual file at the actual line, and the
explanation stays inline as a virtual-text annotation.
07-code-tour.mp4
Played at 3Γ speed.
- Neovim 0.10+ (uses
vim.system()) - Node.js 18+ β for the bundled MCP server
- A C compiler β optional; without one the chat Tree-sitter parser is skipped and the previous whole-buffer Markdown parser is used
- At least one AI CLI backend:
| Backend | Install |
|---|---|
| Claude CLI | npm install -g @anthropic-ai/claude-code |
| Codex CLI | npm install -g @openai/codex (0.140+) |
| GitHub Copilot CLI | npm install -g @github/copilot (needs Node.js 22+) |
| Grok Build CLI | xAI's install docs |
| Pi coding agent | Pi installer (Node.js 22.19+) |
Codex version note
vibing.nvim runs the Codex backend's lightweight calls (chat title generation, /summarize, daily
summary) with --ignore-user-config --strict-config, which keeps those calls out of your MCP
servers and makes them fail loudly rather than silently unfenced if Codex renames a config key.
Both flags were verified present in 0.140.0 and 0.147.0, on codex exec and
codex exec resume alike. Older releases were not tested; if they lack the flags, ordinary chat is
unaffected but lightweight calls fail with an unknown-argument error. Upgrade Codex if you see
that.
--ignore-user-config also drops model_provider, so if your config.toml points Codex at a
custom or local provider, those lightweight calls go to the default OpenAI endpoint instead.
Ordinary chat still uses your provider. vibing.nvim warns once per Neovim session when this applies
to you, asking Codex itself (codex doctor --json) which provider is configured; it says nothing
if Codex cannot answer. Set ai.agents.codex.extensions.provider_notice = false to turn the warning and its
probe off.
With lazy.nvim:
Pi requires Node.js 22.19+; the bundled MCP server itself requires Node.js 18+.
{
"shabaraba/vibing.nvim",
build = "./build.sh", -- builds the bundled chat parser and MCP server
dependencies = {
"stevearc/oil.nvim", -- optional: add context straight from the file browser
},
opts = {},
},opts is passed to require("vibing").setup(). See Configuration.
packer.nvim
use({
"shabaraba/vibing.nvim",
run = "./build.sh",
config = function()
require("vibing").setup()
end,
}),The bundled Claude Code plugin (MCP + skills + subagent)
vibing.nvim ships a Claude Code plugin that bundles the
vibing-nvim MCP server together with Neovim-aware skills and a read-only navigation subagent.
Nothing to install. The plugin is not registered into Claude Code's global state at all β
vibing.nvim hands the CLI its own agent-plugin/ directory per session with --plugin-dir, so
whichever checkout is running is the one that serves you. build.sh builds the MCP server and the
small Tree-sitter parser used for chat boundaries (and, once, cleans up an install from an older
version of vibing.nvim).
Updating from claude-plugin/. The bundled directory is now agent-plugin/.
Update any manually configured MCP args, integrations.plugins.extra,
VIBING_MCP_SERVER_DIR, and lazy.nvim watch commands that point at the old directory.
Rebuild from the updated checkout with ./build.sh, then restart Neovim and any resident CLI
processes. The plugin name, MCP tool names, and .claude-plugin/ manifest directory are unchanged.
That gives you mcp__plugin_vibing-nvim_vibing-nvim__* tools (buffer/window/cursor access, Ex
commands, and LSP queries against the running Neovim instance), the bundled skills
(nvim-context, nvim-lsp-navigation, vibing-chat-recall, vibing-chat-search, and the
vibing-worktree-{list,create,attach,run,finish} worktree workflow), and the nvim-navigator
subagent (read-only code navigation via @vibing-nvim:nvim-navigator).
You still need Neovim running with mcp = { enabled = true } (the default) for the MCP tools to
have anything to connect to. The one trade-off is that a plain claude session started outside
Neovim no longer sees these tools; that was never a supported way to use them.
The Codex backend gets the same plugins without a --plugin-dir of its own: the MCP server is
registered per run as -c mcp_servers.vibing-nvim.* (tools named mcp__vibing-nvim__*) and the
skills are listed for the model in developer_instructions. Subagents do not carry over.
Your own project plugins. Anything you drop into .vibing/plugins/<name>/ (with a
.claude-plugin/plugin.json) is loaded the same way, for chats in that project only. Run
:VibingReloadCommands after adding one, or :VibingCreatePlugin <name> to write a working
skeleton in the first place. Note that a plugin may declare mcpServers, so a plugin in a
repository you cloned can start a process on your machine β set integrations.plugins.project_dir = false
for repositories you do not trust. See
handbook/configuration.md for integrations.plugins.
Upgrading from an older vibing.nvim: build.sh removes the user-scope install and its
marketplace entry for you. To do it by hand:
/plugin uninstall vibing-nvim@vibing
/plugin marketplace remove vibing
:VibingChatType your message under the ## User header and press <CR> in normal mode to send. The AI
responds in the same buffer; <C-c> cancels a running request. Chats stay ordinary Markdown files
you can save, search and edit normally β a small vibing Tree-sitter parser isolates chat headers
and tool output and injects the standard Markdown parser into each message body, so fenced code
keeps its highlighting.
require("vibing").setup() works out of the box. Commonly tweaked options:
require("vibing").setup({
ai = {
chat = { agent = "claude", model = "sonnet", effort = "default" },
helpers = { model = "haiku", effort = "low" },
},
chat = {
language = "ja",
storage = { location = "project" },
},
ui = { window = { position = "current", width = 0.4 } },
permissions = { mode = "acceptEdits", deny = { "Bash" } },
})The permissions above are a template for new chat files; each chat's frontmatter carries its own permissions, and those are what is enforced at runtime.
Old setup keys are rejected with migration hints. Objects merge recursively and lists replace (including empty lists). See the migration guide.
Full reference: every option (window details, UI/gradient/tool markers, diff backends, granular permission rules, MCP, Node.js executable, daily summary, β¦) is documented in handbook/configuration.md.
Project-local Codex permission profiles
For the Codex backend, vibing.nvim creates .vibing/codex-permissions.toml when it initializes a
project. Its default profile permits workspace and Git metadata writes without using full bypass
mode; edit the file to narrow or extend the OS sandbox. Existing files are never overwritten.
Git-tracked profiles are rejected unless ai.agents.codex.extensions.allow_tracked_profile = true explicitly
trusts the reviewed file; locally generated, untracked profiles continue to load automatically. See
Project-local Codex permission profiles.
Also available offline as :help vibing-commands, which carries per-command help tags.
| Command | Description |
|---|---|
:VibingChat [position|file] |
Create new chat with optional position (current|right|left|top|bottom|back) or open saved file |
:VibingToggleChat |
Toggle existing chat window (preserve current conversation) |
:VibingChatFork [position] |
Fork current chat (create branch from current conversation) |
:VibingChatHandoff [position] |
Summarize this chat and start a new one whose first message carries the summary (cheap continuation of a long chat) |
:VibingChatJumpNextUser [count] |
Move the cursor to the next User section in the chat buffer |
:VibingChatJumpPrevUser [count] |
Move the cursor to the previous User section in the chat buffer |
:VibingSlashCommands |
Show slash command picker in chat |
:VibingSetFileTitle [--linked] |
Generate AI title and rename chat file (uses an existing ## summary if present; --linked also renames linked chats) |
:VibingSummarize [--with-title] [--linked] |
Generate AI summary of chat history and insert into buffer (--with-title then renames the file from that summary; --linked does the same to every chat linked through frontmatter) |
:VibingDeleteChats [--unrenamed] |
Delete chat files (use --unrenamed to delete all unrenamed files) |
:VibingContext [path] |
Add context: oil.nvim entry, visual selection (range), path argument, or current buffer when no args |
:VibingClearContext |
Clear all context |
:VibingCancel |
Cancel current request |
:VibingSchedule [when] |
Schedule this chat's unsent message (default: the recorded limit reset; or 30m, 18:30, β¦) |
:VibingPendingResumes |
List chats waiting on a usage limit reset or a scheduled send |
:VibingCancelResume [all] |
Cancel the pending auto-resume/scheduled send for this chat (or every one with all); also clears the project's recorded usage limit |
:VibingReloadCommands |
Reload custom slash commands and completion candidates |
:VibingCreatePlugin [name] |
Create a project-local Claude Code plugin under .vibing/plugins/ |
:VibingCopyUnsentUserHeader |
Copy ## User <!-- unsent --> to clipboard |
:VibingDailySummary [YYYY-MM-DD] |
Generate daily summary from project chat files (default: today) |
:VibingDailySummaryAll [YYYY-MM-DD] |
Generate daily summary from all chat files (default: today) |
:VibingChatalways creates a fresh chat. Pass a position (current/right/left/top/bottom/back) or a saved chat file path to reopen it.:VibingChatForkbranches the current conversation in a different direction.:VibingChatHandoffcontinues a long conversation in a fresh chat: the current one is summarized and the new chat's first message starts with that summary. Unlike a fork it does not carry the history, so every later request reads a few thousand tokens instead of the whole transcript. An existing## summarysection is reused as-is; run/summarizefirst if it is out of date.- Worktree lifecycle is handled by the bundled
vibing-worktree-{list,create,attach,run,finish}skills entirely through natural language ("split this off into a worktree"), not by editor commands.
| Command | Description |
|---|---|
/context <file> |
Add file to context |
/clear |
Clear context |
/save |
Save current chat |
/summarize |
Summarize conversation |
/model <model> |
Set AI model for the current backend |
/effort <level> |
Set reasoning effort (low/medium/high/xhigh/max) |
/help |
Show available slash commands |
/permissions or /perm |
Interactive permission builder - configure tool allow/deny rules |
/allow [tool] |
Add tool to allow list (-tool removes), or show current list if no args |
/deny [tool] |
Add tool to deny list (-tool removes), or show current list if no args |
/ask [tool] |
Ask before using tool (-tool removes), or show current list if no args |
/permission [mode] |
Set permission mode (default/acceptEdits/bypassPermissions/plan/dontAsk/auto) |
/new-session |
Reset session and start fresh |
/allow, /deny and /ask also accept granular patterns like Bash(git:*),
Read(src/**/*.ts) and WebFetch(github.com).
All except q are configurable through keymaps.
| Key | Description |
|---|---|
<CR> |
Send message (normal mode) |
<C-c> |
Cancel current request |
<C-a> |
Add file to context |
gd |
Show diff for file under cursor (in Modified Files section) |
gf |
Open file under cursor (Modified Files section, or any path in the chat) |
gx |
Open URL on current line in browser |
q |
Close chat window |
Frontmatter and layout
Chats are saved as Markdown files (.vibing/chat/chat-<timestamp>-....md by default) with YAML
frontmatter for session resumption and configuration:
---
vibing.nvim: true
session_id: <cli-session-id>
created_at: 2024-01-01T12:00:00
working_dir: .vibing/worktrees/feature-x # Optional: working directory (relative to git root)
agent: claude # claude | codex | copilot | grok | pi (overrides global adapter setting for this chat)
mode: code # code | plan | explore
model: sonnet # Backend model id, e.g. sonnet or gpt-5.6-terra
effort: default # CLI/model default | low | medium | high | xhigh | max
permission_mode: acceptEdits # default | acceptEdits | bypassPermissions | plan | dontAsk | auto
permissions_allow:
- Read
- Edit
- Write
- Glob
- Grep
permissions_deny:
- Bash
permissions_ask: []
language: ja # Optional: default language for AI responses
---
# Vibing Chat
## User
Hello, Claude!
## Assistant
Hello! How can I help you today?- Session resumption β reopening a saved chat resumes the conversation via
session_id - Fork tracking β a forked chat carries a
forked_fromfield until its first response - Auditability β model, mode and permissions are all visible in frontmatter
- Language β the optional
languagefield fixes the AI's response language
How the pieces fit together
For detailed architecture documentation, see CLAUDE.md.
graph TB
subgraph Neovim["Neovim Process"]
Plugin["vibing.nvim<br/>(Lua Plugin)"]
Buffer["Chat Buffer<br/>(.vibing/chat/*.md)<br/>- Markdown + YAML<br/>- Session metadata<br/>- Permission settings"]
RPC["RPC Server<br/>(Async TCP)"]
Plugin -->|manages| Buffer
Plugin -->|uses| RPC
end
subgraph MCP["Node.js MCP Server"]
MCPServer["MCP Server<br/>- Buffer operations<br/>- LSP queries<br/>- Command execution"]
end
subgraph AI["AI CLI Backends"]
Claude["Claude CLI<br/>(claude -p --output-format stream-json)"]
Codex["Codex CLI<br/>(codex exec --json)"]
Copilot["Copilot CLI<br/>(copilot -p --output-format json)"]
end
RPC <-->|JSON-RPC| MCPServer
Plugin -->|spawns & communicates<br/>JSON Lines| Claude
Plugin -->|spawns & communicates<br/>JSON Lines| Codex
Plugin -->|spawns & communicates<br/>JSON Lines| Copilot
| Aspect | Traditional REST API | vibing.nvim (CLI Adapters) |
|---|---|---|
| Context | Manually assembled | MCP: agent requests on-demand |
| Editor Access | None (fire & forget) | Full bidirectional MCP |
| Session State | Plugin manages | CLI session with resume |
| Tool Execution | Plugin implements | CLI native tools |
Which AI backends are supported?
- Claude CLI (
claude -p --output-format stream-json) β full Claude Code capabilities - Codex CLI (
codex exec --json) β OpenAI Codex backend - GitHub Copilot CLI (
copilot -p --output-format json) β GitHub Copilot backend - Grok Build CLI (
grok --single --output-format streaming-json) β xAI Grok backend - Pi coding agent (
pi --mode rpc) β a harness rather than a vendor CLI; it uses the provider selected in Pi's own configuration, including local models served through OpenAI-compatible endpoints
Switch globally with ai.chat.agent = "claude"|"codex"|"copilot"|"grok"|"pi" in setup, or per-chat by adding
agent: claude, agent: codex, agent: copilot, agent: grok or agent: pi to a chat file's YAML
frontmatter. effort: low|medium|high|xhigh|max controls reasoning for Claude, Codex and Grok when
the selected model supports that level. New chats use effort: default, which passes no override
and therefore preserves the same CLI/model default used before effort was configurable. Omitting
the field has the same runtime behaviour for existing chats.
Note: on the Copilot backend,
permissions.mode,permissions.askand the in-chat Tool Approval UI are enforced through a generated Copilot plugin (.vibing/copilot-plugin/) that vibing.nvim loads per run withcopilot --plugin-dir. Your own~/.copilot/configuration and login are never touched. Copilot's static--deny-toolflags are still passed as a backstop; they coverBash(includingBash(cmd:*)patterns),Write,Edit,WebFetchandWebSearch, and vibing.nvim warns once when it drops a tool name Copilot cannot express.Note: the Pi backend is the one backend with no tool approval of its own β Pi does not ask before running
bash, and its JSON/RPC modes cannot prompt at all. vibing.nvim's permission layer is therefore the only one, and it is delivered as a Pi extension (pi-extension/) that./build.shcompiles andpi --extensionloads. If that bundle is missing, a Pi turn is restricted to read-only tools and says so once. Pi 1.2.0 includes an MCP client; configure servers in Pi's ownmcp.json. Claude plugin servers are not injected by this backend. Real Pi/local-model verification is pending; see the manual procedure.Model selection is Pi's, not vibing.nvim's:
model:has to name something Pi can resolve through its own~/.pi/agent/models.jsonor a provider you are logged into. For a local endpoint:{ "providers": { "mlx-local": { "baseUrl": "http://127.0.0.1:8081/v1", "api": "openai-completions", "apiKey": "not-needed", "models": [{ "id": "mlx-community/Qwen3.6-27B-4bit" }] } } }then
ai.agents.pi.extensions.provider = "mlx-local"andmodel: mlx-community/Qwen3.6-27B-4bit. Naming the provider is not optional for a local endpoint:--modelis a fuzzy pattern Pi matches over its built-in cloud catalogue as well, so an unqualifiedmodel: qwenpicks a cloud model and the turn fails on a missing API key. With it named, any id your endpoint can serve works frommodel:alone β no models.json edit β which is what lets onemlx_lm.serverserve every model.The same extension also adds the two tools Pi does not have. Pi's built-in set is
bash,read,write,edit,ls,grep,findand nothing else, soweb_fetchandweb_searchare registered beside the gate β shaped like claude'sWebFetch/WebSearch, so your existing rules for those apply unchanged.web_fetchworks with no configuration;web_searchhas to call a real search API (claude's and codex's happen server-side inside the inference API, which a local endpoint has no equivalent of), so setBRAVE_SEARCH_API_KEY,TAVILY_API_KEYorSEARXNG_URL. With none of them set the tool is simply not offered. See handbook/configuration.md β "Pi: web tools".
Why does it require Node.js?
Node.js is required for the MCP server, which provides AI with direct access to your running Neovim
instance (buffer reads/writes, LSP queries, command execution). The AI CLI binaries themselves
(claude, codex, copilot) are separate installs.
How does it compare to the Claude Code CLI?
vibing.nvim provides similar capabilities to Claude Code CLI but integrated into Neovim:
- Same
claudeCLI underneath - MCP for editor control (the CLI controls a terminal, vibing controls Neovim)
- Additional Codex, Copilot and Grok backends for non-Anthropic workflows
Think of it as "Claude Code (or Codex, or Copilot) for Neovim users."
Can I use vibing.nvim alongside other AI plugins?
Yes. vibing.nvim doesn't conflict with completion plugins (Copilot, Codeium) or other chat plugins. Use vibing.nvim for deep interactions and other tools for quick completions or different providers.
Contributions are welcome! See CONTRIBUTING.md, and feel free to submit issues or pull requests.
MIT β see LICENSE.
Made with β€οΈ using Claude Code