by coder
π§© Claude Code Neovim IDE Extension
# Add to your Claude Code skills
git clone https://github.com/coder/claudecode.nvimThe first Neovim IDE integration for Claude Code β bringing Anthropic's AI coding assistant to your favorite editor with a pure Lua implementation.
π― TL;DR: When Anthropic released Claude Code with VS Code and JetBrains support, I reverse-engineered their extension and built this Neovim plugin. This plugin implements the same WebSocket-based MCP protocol, giving Neovim users the same AI-powered coding experience.
https://github.com/user-attachments/assets/9c310fb5-5a23-482b-bedc-e21ae457a82d
When Anthropic released Claude Code, they only supported VS Code and JetBrains. As a Neovim user, I wanted the same experience β so I reverse-engineered their extension and built this.
vim.loop and Neovim built-insNo comments yet. Be the first to share your thoughts!
{
"coder/claudecode.nvim",
dependencies = { "folke/snacks.nvim" },
config = true,
keys = {
{ "<leader>a", nil, desc = "AI/Claude Code" },
{ "<leader>ac", "<cmd>ClaudeCode<cr>", desc = "Toggle Claude" },
{ "<leader>af", "<cmd>ClaudeCodeFocus<cr>", desc = "Focus Claude" },
{ "<leader>ar", "<cmd>ClaudeCode --resume<cr>", desc = "Resume Claude" },
{ "<leader>aC", "<cmd>ClaudeCode --continue<cr>", desc = "Continue Claude" },
{ "<leader>am", "<cmd>ClaudeCodeSelectModel<cr>", desc = "Select Claude model" },
{ "<leader>ab", "<cmd>ClaudeCodeAdd %<cr>", desc = "Add current buffer" },
{ "<leader>as", "<cmd>ClaudeCodeSend<cr>", mode = "v", desc = "Send to Claude" },
{
"<leader>as",
"<cmd>ClaudeCodeTreeAdd<cr>",
desc = "Add file",
ft = { "NvimTree", "neo-tree", "oil", "minifiles", "netrw" },
},
-- Diff management
{ "<leader>aa", "<cmd>ClaudeCodeDiffAccept<cr>", desc = "Accept diff" },
{ "<leader>ad", "<cmd>ClaudeCodeDiffDeny<cr>", desc = "Deny diff" },
},
}
That's it! The plugin will auto-configure everything else.
If you've used Claude Code's migrate-installer command to move to a local installation, you'll need to configure the plugin to use the local path.
Claude Code offers a claude migrate-installer command that:
~/.claude/local/Check your installation type:
# Check where claude command points
which claude
# Global installation shows: /usr/local/bin/claude (or similar)
# Local installation shows: alias to ~/.claude/local/claude
# Verify installation health
claude doctor
If you have a local installation, configure the plugin with the direct path:
{
"coder/claudecode.nvim",
dependencies = { "folke/snacks.nvim" },
opts = {
terminal_cmd = "~/.claude/local/claude", -- Point to local installation
},
config = true,
keys = {
-- Your keymaps here
},
}
Claude Code also offers an experimental native binary installation method currently in alpha testing. This provides a single executable with no Node.js dependencies.
Install the native binary using one of these methods:
# Fresh install (recommended)
curl -fsSL claude.ai/install.sh | bash
# From existing Claude Code installation
claude install
The exact binary path depends on your shell integration. To find your installation:
# Check where claude command points
which claude
# Verify installation type and health
claude doctor
Configure the plugin with the detected path:
{
"coder/claudecode.nvim",
dependencies = { "folke/snacks.nvim" },
opts = {
terminal_cmd = "/path/to/your/claude", -- Use output from 'which claude'
},
config = true,
keys = {
-- Your keymaps here
},
}
Note: If Claude Code was installed globally via npm, you can use the default configuration without specifying
terminal_cmd.
" Launch Claude Code in a split
:ClaudeCode
" Claude now sees your current file and selections in real-time!
" Send visual selection as context
:'<,'>ClaudeCodeSend
" Claude can open files, show diffs, and more
:ClaudeCode to open Claude in a split terminal<leader>as to send it to Claudenvim-tree/neo-tree/oil.nvim/mini.nvim, press <leader>as on a file to add it to Claude's context:ClaudeCode - Toggle the Claude Code terminal window:ClaudeCodeFocus - Smart focus/toggle Claude terminal:ClaudeCodeSelectModel - Select Claude model and open terminal with optional arguments:ClaudeCodeSend - Send current visual selection to Claude:ClaudeCodeAdd <file-path> [start-line] [end-line] - Add specific file to Claude context with optional line range:ClaudeCodeDiffAccept - Accept diff changes:ClaudeCodeDiffDeny - Reject diff changesWhen Claude proposes changes, the plugin opens a native Neovim diff view:
:w (save) or <leader>aa:q or <leader>adYou can edit Claude's suggestions before accepting them.
This plugin creates a WebSocket server that Claude Code CLI connects to, implementing the same protocol as the official VS Code extension. When you launch Claude, it automatically detects Neovim and gains full access to your editor.
The protocol uses a WebSocket-based variant of MCP (Model Context Protocol) that:
~/.claude/ide/[port].lock (or $CLAUDE_CONFIG_DIR/ide/[port].lock if CLAUDE_CONFIG_DIR is set) with connection infoBuilt with pure Lua and zero external dependencies:
vim.loopFor deep technical details, see ARCHITECTURE.md.
{
"coder/claudecode.nvim",
dependencies = { "folke/snacks.nvim" },
opts = {
-- Server Configuration
port_range = { min = 10000, max = 65535 },
auto_start = true,
log_level = "info", -- "trace", "debug", "info", "warn", "error"
terminal_cmd = nil, -- Custom terminal command (default: "claude")
-- For local installations: "~/.claude/local/claude"
-- For native binary: use output from 'which claude'
-- Send/Focus Behavior
-- When true, successful sends will focus the Claude terminal if already connected
focus_after_send = false,
-- Selection Tracking
track_selection = true,
visual_demotion_delay_ms = 50,
-- Terminal Configuration
terminal = {
split_side = "right", -- "left" or "right"
split_width_percentage = 0.30,
provider = "auto", -- "auto", "snacks", "native", "external", "none", or custom provider table
auto_close = true,
snacks_win_opts = {}, -- Opts to pass to `Snacks.terminal.open()` - see Floating Window section below
-- Provider-specific options
provider_opts = {
-- Command for external terminal provider. Can be:
-- 1. String with %s placeholder: "alacritty -e %s" (backward compatible)
-- 2. String with two %s placeholders: "alacritty --working-directory %s -e %s" (cwd, command)
-- 3. Function returning command: function(cmd, env) return "alacritty -e " .. cmd end
external_terminal_cmd = nil,
},
},
-- Diff Integration
diff_opts = {
layout = "vertical", -- "vertical" or "horizontal"
open_in_new_tab = false,
keep_terminal_focus = false, -- If true, moves focus back to terminal after diff opens
hide_terminal_in_new_tab = false,
-- on_new_file_reject =