Skip to content

coder/claudecode.nvim

Repository files navigation

Claude Code Neovim Integration

Tests Neovim version Status

A Neovim plugin that integrates with Claude Code CLI to provide a seamless AI coding experience in Neovim.

demo.mp4

Features

  • 🔄 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

Requirements

  • 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.

Installation

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,
}
use {
  "coder/claudecode.nvim",
  requires = {
    "folke/snacks.nvim", -- Optional dependency
  },
  config = function()
    require("claudecode").setup({
      -- Optional configuration
    })
  end
}

Local Development with LazyVim

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:

  1. Specifies the local repository path using the dir parameter.
  2. Enables development mode via dev = true.
  3. Sets a more verbose log_level for debugging.
  4. Includes convenient keymaps for easier testing.

Configuration

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
  }
})

Usage

  1. 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
  2. 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, or Ctrl-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.

  3. 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
  4. 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)

Commands

  • :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

Keymaps

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" })

Architecture

The plugin follows a modular architecture with these main components:

  1. WebSocket Server - Handles communication with Claude Code CLI using JSON-RPC 2.0 protocol
  2. Lock File System - Creates and manages lock files that Claude CLI uses to detect the editor integration
  3. Selection Tracking - Monitors text selections and cursor position in Neovim
  4. MCP Tool System - Implements Model Context Protocol tools that Claude can execute:
    • openFile - Opens files with optional line/text selection
    • getCurrentSelection - Gets current text selection
    • getOpenEditors - Lists currently open files
    • openDiff - Opens native Neovim diff views for file comparisons
  5. Diff Integration - Native Neovim diff support with configurable behavior

For more details, see ARCHITECTURE.md.

Developing Locally

This project uses Nix for development environment management. For the best experience:

  1. Install Nix with flakes support

  2. Clone the repository:

    git clone https://github.com/coder/claudecode.nvim
    cd claudecode.nvim
  3. Enter the development shell:

    nix develop
    # Or use direnv if available
    direnv allow
  4. Run development commands:

    # Format code
    nix fmt
    
    # Check code for errors
    make check
    
    # Run tests
    make test
  5. 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

Troubleshooting

Connection Issues

If Claude isn't connecting to Neovim:

  1. Check if the WebSocket server is running: :ClaudeCodeStatus
  2. Verify lock file exists in ~/.claude/ide/
  3. Check that Claude CLI has the right environment variables set

Debug Mode

Enable more detailed logging:

require("claudecode").setup({
  log_level = "debug",
})

Contributing

Contributions are welcome! Please see DEVELOPMENT.md for development guidelines.

License

MIT

Acknowledgements

About

🧩 Claude Code Neovim IDE Extension

Topics

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Releases

No releases published

Languages