A Neovim plugin that integrates with Claude Code CLI to provide a seamless AI coding experience in Neovim.
demo.mp4
- 🔄 Pure Neovim WebSocket Server (implemented with Neovim built-ins)
- 🌐 RFC 6455 Compliant (WebSocket with JSON-RPC 2.0)
- 🔍 Selection tracking to provide context to Claude
- 🛠️ Integration with Neovim's buffer and window management
- 📝 Support for file operations and diagnostics
- 🖥️ Interactive vertical split terminal for Claude sessions (supports
folke/snacks.nvim
or native Neovim terminal) - 🔒 Automatic cleanup on exit - server shutdown and lockfile removal
- 💬 At-Mentions: Send visual selections as
at_mentioned
context to Claude using:'<,'>ClaudeCodeSend
- 🔀 Diff View Integration: Claude can open native Neovim diff views to compare file changes with configurable options
- Neovim >= 0.8.0
- Claude Code CLI installed and in your PATH
- Optional for terminal integration: folke/snacks.nvim - Terminal management plugin (can use native Neovim terminal as an alternative).
The WebSocket server uses only Neovim built-ins (vim.loop
, vim.json
, vim.schedule
) for its implementation.
Note: The terminal feature can use Snacks.nvim
or the native Neovim terminal. If Snacks.nvim
is configured as the provider but is not available, it will fall back to the native terminal.
Using lazy.nvim
Add the following to your plugins configuration:
{
"coder/claudecode.nvim",
dependencies = {
"folke/snacks.nvim", -- Optional dependency for enhanced terminal
},
opts = {
-- Configuration for claudecode main
-- Optional: terminal_cmd = "claude --magic-flag",
-- Configuration for the interactive terminal:
terminal = {
split_side = "right", -- "left" or "right"
split_width_percentage = 0.3, -- 0.0 to 1.0
provider = "snacks", -- "snacks" or "native"
show_native_term_exit_tip = true, -- Show tip for Ctrl-\\ Ctrl-N
},
},
-- The plugin will call require("claudecode").setup(opts)
config = true,
-- Optional: Add convenient keymaps
keys = {
{ "<leader>a", nil, mode = { "n", "v" }, desc = "AI/Claude Code" },
{ "<leader>ac", "<cmd>ClaudeCode<cr>", mode = { "n", "v" }, desc = "Toggle Claude Terminal" },
{ "<leader>ak", "<cmd>ClaudeCodeSend<cr>", mode = { "v" }, desc = "Send to Claude Code" },
{ "<leader>ao", "<cmd>ClaudeCodeOpen<cr>", mode = { "n", "v" }, desc = "Open/Focus Claude Terminal" },
{ "<leader>ax", "<cmd>ClaudeCodeClose<cr>", mode = { "n", "v" }, desc = "Close Claude Terminal" },
},
}
For those who prefer a function-style config:
{
"coder/claudecode.nvim",
dependencies = {
"folke/snacks.nvim", -- Optional dependency
},
config = function()
-- If using snacks, ensure it's loaded
-- require("snacks")
require("claudecode").setup({
-- Optional configuration
})
end,
}
Using packer.nvim
use {
"coder/claudecode.nvim",
requires = {
"folke/snacks.nvim", -- Optional dependency
},
config = function()
require("claudecode").setup({
-- Optional configuration
})
end
}
For local development with LazyVim, create a lua/plugins/claudecode.lua
file with the following content:
return {
{
dir = "~/GitHub/claudecode.nvim", -- Path to your local repository
name = "claudecode.nvim",
dependencies = {
"folke/snacks.nvim", -- Added dependency
},
dev = true,
opts = {
-- Development configuration for claudecode main
log_level = "debug",
auto_start = true,
-- Example terminal configuration for dev:
terminal = {
split_side = "right",
split_width_percentage = 0.25,
provider = "native",
show_native_term_exit_tip = false,
},
},
config = true,
keys = {
{ "<leader>a", nil, mode = { "n", "v" }, desc = "AI/Claude Code" },
{ "<leader>ac", "<cmd>ClaudeCode<cr>", mode = { "n", "v" }, desc = "Toggle Claude Terminal" },
{ "<leader>ak", "<cmd>ClaudeCodeSend<cr>", mode = { "v" }, desc = "Send to Claude Code" },
{ "<leader>ao", "<cmd>ClaudeCodeOpen<cr>", mode = { "n", "v" }, desc = "Open/Focus Claude Terminal" },
{ "<leader>ax", "<cmd>ClaudeCodeClose<cr>", mode = { "n", "v" }, desc = "Close Claude Terminal" },
},
},
}
This configuration:
- Specifies the local repository path using the
dir
parameter. - Enables development mode via
dev = true
. - Sets a more verbose
log_level
for debugging. - Includes convenient keymaps for easier testing.
require("claudecode").setup({
-- Port range for WebSocket server (default: 10000-65535)
port_range = { min = 10000, max = 65535 },
-- Auto-start WebSocket server when the plugin is loaded.
-- Note: With lazy-loading (e.g., LazyVim), this means the server starts when a plugin command is first used.
auto_start = true,
-- Custom terminal command to use when launching Claude
-- This command is used by the new interactive terminal feature.
-- If nil or empty, it defaults to "claude".
terminal_cmd = nil, -- e.g., "my_claude_wrapper_script" or "claude --project-foo"
-- Log level (trace, debug, info, warn, error)
log_level = "info",
-- Enable sending selection updates to Claude
track_selection = true,
-- Milliseconds to wait before "demoting" a visual selection to a cursor/file selection
-- when exiting visual mode. This helps preserve visual context if quickly switching
-- to the Claude terminal. (Default: 50)
visual_demotion_delay_ms = 50,
-- Diff provider configuration for openDiff MCP tool
diff_provider = "auto", -- "auto", "native", or "diffview" (when implemented)
diff_opts = {
auto_close_on_accept = true, -- Auto-close diff when accepting changes
show_diff_stats = true, -- Show diff statistics
vertical_split = true, -- Use vertical split for diff view
open_in_current_tab = true, -- Open diff in current tab instead of new tab (reduces clutter)
},
-- Configuration for the interactive terminal (passed to claudecode.terminal.setup by the main setup function)
terminal = {
-- Side for the vertical split ('left' or 'right')
split_side = "right", -- Default
-- Width of the terminal as a percentage of total editor width (0.0 to 1.0)
split_width_percentage = 0.30, -- Default
-- Terminal provider ('snacks' or 'native')
-- If 'snacks' is chosen but not available, it will fall back to 'native'.
provider = "snacks", -- Default
-- Whether to show a one-time tip about exiting native terminal mode (Ctrl-\\ Ctrl-N)
show_native_term_exit_tip = true -- Default
}
})
-
Start the Claude Code integration with the interactive terminal:
:ClaudeCode
This will:
- Start the WebSocket server
- Open a terminal split with Claude Code CLI already connected
- Configure the necessary environment variables automatically
-
You can now interact with Claude in the terminal window. To provide code context, you can:
-
Send Visual Selection as At-Mention: Make a selection in visual mode (
v
,V
, orCtrl-V
), then run::'<,'>ClaudeCodeSend
This sends the selected file path and line range as an
at_mentioned
notification to Claude, allowing Claude to focus on that specific part of your code.
-
-
Switch between your code and the Claude terminal:
- Use normal Vim window navigation (
Ctrl+w
commands) - Or toggle the terminal with
:ClaudeCode
- Open/focus with
:ClaudeCodeOpen
(can also be used from Visual mode to switch focus after selection) - Close with
:ClaudeCodeClose
- Use normal Vim window navigation (
-
Use Claude as normal - it will have access to your Neovim editor context! Claude can:
- Open files in your editor
- View your current selection and open editors
- Open diff views to show proposed changes with handy keymaps:
]c
/[c
to navigate between changes<leader>dq
to exit diff mode (current tab mode only)<leader>da
to accept all changes (current tab mode only)
:ClaudeCodeSend
- Send current selection to Claude:ClaudeCode
- Toggle the Claude Code interactive terminal window:ClaudeCodeOpen
- Open (or focus) the Claude Code terminal window:ClaudeCodeClose
- Close the Claude Code terminal window
Note: The server starts automatically when the first command is used. To manually control the server, use the Lua API:
require("claudecode").start() -- Start server
require("claudecode").stop() -- Stop server
require("claudecode").status() -- Check status
No default keymaps are provided. Add your own in your configuration:
vim.keymap.set({"n", "v"}, "<leader>ac", "<cmd>ClaudeCode<cr>", { desc = "Toggle Claude Terminal" })
vim.keymap.set({"v"}, "<leader>ak", "<cmd>ClaudeCodeSend<cr>", { desc = "Send to Claude Code" })
-- Or more specific maps:
vim.keymap.set({"n", "v"}, "<leader>ao", "<cmd>ClaudeCodeOpen<cr>", { desc = "Open/Focus Claude Terminal" })
vim.keymap.set({"n", "v"}, "<leader>ax", "<cmd>ClaudeCodeClose<cr>", { desc = "Close Claude Terminal" })
The plugin follows a modular architecture with these main components:
- WebSocket Server - Handles communication with Claude Code CLI using JSON-RPC 2.0 protocol
- Lock File System - Creates and manages lock files that Claude CLI uses to detect the editor integration
- Selection Tracking - Monitors text selections and cursor position in Neovim
- MCP Tool System - Implements Model Context Protocol tools that Claude can execute:
openFile
- Opens files with optional line/text selectiongetCurrentSelection
- Gets current text selectiongetOpenEditors
- Lists currently open filesopenDiff
- Opens native Neovim diff views for file comparisons
- Diff Integration - Native Neovim diff support with configurable behavior
For more details, see ARCHITECTURE.md.
This project uses Nix for development environment management. For the best experience:
-
Install Nix with flakes support
-
Clone the repository:
git clone https://github.com/coder/claudecode.nvim cd claudecode.nvim
-
Enter the development shell:
nix develop # Or use direnv if available direnv allow
-
Run development commands:
# Format code nix fmt # Check code for errors make check # Run tests make test
-
Link to your Neovim plugins directory:
# For traditional package managers ln -s $(pwd) ~/.local/share/nvim/site/pack/plugins/start/claudecode.nvim
Without Nix, ensure you have:
- Lua 5.1+
- LuaCheck for linting
- StyLua for formatting
- Busted for testing
If Claude isn't connecting to Neovim:
- Check if the WebSocket server is running:
:ClaudeCodeStatus
- Verify lock file exists in
~/.claude/ide/
- Check that Claude CLI has the right environment variables set
Enable more detailed logging:
require("claudecode").setup({
log_level = "debug",
})
Contributions are welcome! Please see DEVELOPMENT.md for development guidelines.
MIT
- Claude Code CLI by Anthropic
- Based on research from analyzing the VS Code extension