Sno Station
Sno Station은 AI 에이전트를 하나의 팀으로 묶는 오픈소스 워크스테이션으로, 로컬 머신에서 에이전트 협업을 가능하게 함
Claude CLI·Codex CLI 백엔드와 MCP 기반 편집기 제어를 지원하는 Neovim AI 코딩 플러그인
Neovim plugin for AI-assisted coding: Claude CLI & Codex CLI backends with MCP-powered editor control
플러그인Lua · TypeScriptGitHub @shabaraba직접 설치운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Claude, Codex, Copilot and Grok as a Neovim buffer — with the editor handed back to them
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.README 전문 보기 ↗Claude, Codex, Copilot, Grok and Pi as a Neovim buffer — with the editor handed back to them
English | 日本語
Demo • Features • Requirements • Installation • Configuration • Usage
https://github.com/user-attachments/assets/6deaaf7a-e94c-4b11-9144-011ce3a785a6
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.
.vibing/chat/, resumable, greppable, version-controllable.vibing/worktrees/<branch>/ by asking for it### Modified Files; gd opens a patch viewer. The
baseline is a git tree snapshot, so a sed -i through Bash is tracked like an Edit<CR> parks your message instead of burning a doomed request,
and sends it verbatim once the limit resetsvibing.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.
https://github.com/user-attachments/assets/5ab1745a-e12c-4a75-b1c6-48bdd7c14053
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.
https://github.com/user-attachments/assets/9119e216-2970-41f6-bfba-615f4585bf28
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.
https://github.com/user-attachments/assets/7015c274-9d6b-4329-9a67-e9379f679b50
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.
https://github.com/user-attachments/assets/0d6dfa6e-8b82-49a0-b996-1c22332276cd
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.
https://github.com/user-attachments/assets/9810e4fa-4765-4d39-bbac-2a81cd3e0a8e
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.
https://github.com/user-attachments/assets/8182307e-83f6-428a-af11-1122b69f4483
Played at 3× speed.
vim.system())| 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+) |
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.
use({
"shabaraba/vibing.nvim",
run = "./build.sh",
config = function()
require("vibing").setup()
end,
}),
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
:VibingChat
Type 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.
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 |
:VibingChat always creates a fresh chat. Pass a position
(current / right / left / top / bottom / back) or a saved chat file path to reopen it.:VibingChatFork branches the current conversation in a different direction.:VibingChatHandoff continues 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 ## summary section is reused as-is; run /summarize first if it is out
of date.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 |
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_idforked_from field until its first responselanguage field fixes the AI's response languageFor 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 |
claude -p --output-format stream-json) — full Claude Code capabilitiescodex exec --json) — OpenAI Codex backendcopilot -p --output-format json) — GitHub Copilot backendgrok --single --output-format streaming-json) — xAI Grok backendpi --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
endpointsSwitch 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, , , , , , and nothing else, so and are registered beside the gate — shaped like claude's / , so your existing rules for those apply unchanged. works with no configuration; has 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 set , or . With none of them set the tool is simply not offered. See → "Pi: web tools".
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.
vibing.nvim provides similar capabilities to Claude Code CLI but integrated into Neovim:
claude CLI underneathThink of it as "Claude Code (or Codex, or Copilot) for Neovim users."
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
: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) |
readwriteeditlsgrepfindweb_fetchweb_searchWebFetchWebSearchweb_fetchweb_searchBRAVE_SEARCH_API_KEYTAVILY_API_KEYSEARXNG_URL