Repository navigation
This release adds a VS Code-style unified diff layout, a way to script text into the Claude pane, and a :checkhealth integration, alongside a large wave of hardening to the diff lifecycle, selection tracking, openFile, and the WebSocket server. Most of the work went into making existing MCP tool behavior correct and predictable rather than adding surface area.
Highlights
- New diff and terminal surface: a compact
diff_opts.layout = "unified"single-buffer diff,:ClaudeCodeSendTextfor scripting text into the Claude pane,:ClaudeCodeCloseAllDiffs, and diff/send lifecycle autocmds (ClaudeCodeDiffOpened/DiffClosed/SendComplete). - Diffs that respect your windows: diff cleanup is scoped to claudecode's own diffs and never touches diffview.nvim, fugitive, native
:diffsplit, or user buffers; diffs reject cleanly on:q, close when the client disconnects, and restore window sizes afterward. - Hardened server and selection handling: strict WebSocket framing (UTF-8 validation, fragment reassembly, correct close codes, port-bind retries, unique client ids), CSPRNG auth tokens with
0600lock files, reliable visual-selection pushes, andopenFilethat no longer disturbs your mode, focus, or unsaved changes.
Added
-
:ClaudeCodeSendText {text}andrequire("claudecode.terminal").send_to_terminal(text, opts)send arbitrary text to the open Claude terminal as if typed at the prompt — submitting by default;:ClaudeCodeSendText!inserts without submitting. Multi-line text is sent as one bracketed-paste block. In-editornative/snacksproviders only;external/nonewarn and returnfalse. (#197, #272, @ThomasK33) -
:checkhealth claudecodeverifies Neovim version,setup(), the Claude CLI, terminal provider, WebSocket server, lock file, and live connection state, with actionable advice on each failure. (#275, @ThomasK33) -
diff_opts.layout = "unified"renders a VS Code-style unified diff in a single buffer with interleaved deleted/added lines — a compact alternative to"vertical"/"horizontal". Requires Neovim >= 0.9.0; customizable viaClaudeCodeInlineDiffAdd/Delete/AddSign/DeleteSign. (#195, @zhao-tong; #295, #297)require("claudecode").setup({ diff_opts = { layout = "unified" }, -- "vertical" (default), "horizontal", or "unified" })
-
:ClaudeCodeCloseAllDiffscloses pending Claude diffs at once. A diff accepted with:wcloses once the file holds the accepted text; otherwise the accepted text is placed in the buffer as unsaved changes for:w/:e!so edits are never discarded. (#248, #261) -
Lifecycle autocmds:
User ClaudeCodeSendComplete(fired once per file when a send is accepted while connected, withdata = { file_path, start_line, end_line, context }, lines 0-indexed) andUser ClaudeCodeDiffOpened/ClaudeCodeDiffClosedcarrying diff metadata. Enables post-send logic such as focusing a Claude session running outside Neovim. (#228, #265, #270) -
Terminal width during diffs:
diff_opts.auto_resize_terminal(defaulttrue) to opt out of plugin-managed terminal width, andterminal.diff_split_width_percentageto set a distinct width while a diff is open. (#270) -
terminal.auto_insert(defaulttrue): set tofalseto stay in Normal mode when refocusing the Claude terminal and preserve your scroll position instead of jumping to the prompt. (#233) -
File explorer coverage:
<leader>as/:ClaudeCodeTreeAddnow work in snacks.nvim pickers (snacks_picker_list, coveringSnacks.explorer()and modalSnacks.picker.files()/grep()) and in netrw (marked files and the file under the cursor). (#192, #269, @ThomasK33; #62, @makotodejima)
Changed
- Model picker labels are now version-free evergreen aliases (
Claude Opus (Latest),Claude Sonnet (Latest),Claude Haiku (Latest)), with new 1M-context variants (opus[1m],sonnet[1m]) and aDefault (account recommended)entry, so they never go stale. (#146, #256, @ThomasK33) :checkhealth claudecodeand the MCPserverInfo.versionreport the real plugin version from theVERSIONfile instead of a hard-coded value. (#437)
Fixed
Diffs and openFile
- Diff cleanup (
closeAllDiffTabs) is now scoped to claudecode's own tracked diffs and no longer wipes out an open diffview.nvim, fugitive, or native:diffsplitreview;openFile/openDiffnever reuse a window in diff mode or the preview window. (#277, #290, #384, #391) close_tabcloses only the diff with that tab name and never deletes a user buffer. (#373)- Rejecting a diff with
:q/:close/<C-w>c(or closing the tab) resolves it as rejected,:qno longer hangs, and pending diffs auto-close when the opening client disconnects or the integration stops. (#178, #266, #248, #261) openDiffworks when the Claude terminal is the only window, creating a split instead of failing with "No suitable editor window found". (#260)- Accepted diffs are handed over in the buffer's own line-ending format; empty buffers are wiped when the terminal provider is
none; and accepting a new-file diff withrender-markdown.nviminstalled no longer segfaults. (#223, #392, #224) openFileselects exactly the requested line range or matched text and keeps the user's Visual mode, unsaved changes, focus, and preview window intact — never prompting W11/W12/W13 and never picking the preview window as the main editor window. (#353, #365, #370, #375, #382, #387, #396, #408)- Legacy
diff_opts(e.g.vertical_split,open_in_current_tab) take effect again after a config merge-order regression. (#142)
Terminal and selection
- The Snacks provider no longer fails to launch when a model alias contains shell-glob characters (e.g.
opus[1m]); the command is passed as an argv list with no shell interpretation. (#256) - Closing the native terminal (e.g.
:ClaudeCodeClose) stops the Claude job instead of leaving it running. (#336) - The Claude terminal adds loopback hosts to
no_proxy/NO_PROXYso a configured proxy no longer tunnels Claude'sws://127.0.0.1IDE connection and times out queued @ mentions. (#70, #268) focus_after_send = truewithprovider = "none"/"external"emits a one-time warning pointing toUser ClaudeCodeSendCompleteinstead of failing silently. (#228, #265)- Fixed the Snacks "climbing cursor" (cursor one row above the prompt after hide/show); on Neovim >= 0.10 floating terminals are hidden rather than closed. (#240, #183, #271)
- Worked around a Neovim core bug (< 0.12.2) that fragmented large bracketed pastes into the terminal; controlled by
terminal.fix_streamed_paste("auto"by default). (#252) - Quickly-made and single-line visual selections are pushed to Claude reliably; selections flush synchronously on visual-mode exit and persist until the cursor moves, including in the external
/ideflow and Select mode. (#246, #267, #367, #381, #388)
MCP tools and server
getDiagnosticshandles plain file paths without afile://URI scheme, and IDE diagnostics return grouped URI-based payloads with editor-native ranges and severity names. (#163, #274):ClaudeCodeAddandopenFilehandle$in file paths. (#286)- The auth token is generated from a CSPRNG and the lock file is written atomically with mode
0600in a0700directory. (#259) - System sleep/wake is detected and keepalive timestamps are reset to prevent false disconnections. (#141)
- The WebSocket server retries port binding on
EADDRINUSE(parallel instances), closes connections on malformed/oversized/binary frames with the correct status and reason, joins fragmented messages, validates UTF-8 per message, sends only valid UTF-8 regardless of buffer-name bytes, and never repeats a client id within a process. (#258, #283, #378, #383, #390, #395, #405, #422, #431)
Breaking Changes
- Native terminal adoption is now mark-based. The native terminal adopts only buffers it started (marked with
b:claudecode_native_terminal), instead of any visible terminal whose name containsclaude. A terminal started by an older plugin version (for example after:Lazy reloadfollowing an upgrade) or restored from a session file carries no mark and is no longer adopted — the next open starts a second Claude session, and the old terminal keeps running until you close its buffer or quit Claude in it. If you hit a duplicate session right after upgrading, close the old Claude terminal buffer once. (#349, #352, #364)
Compatibility
- Integration tests run on Neovim v0.8.0, and a new-tab diff no longer fails there (Neovim 0.8 has no
statuscolumn). The unified layout requires Neovim >= 0.9.0 and is skipped on 0.8. (#369, #394)
New Contributors
- @zhao-tong made their first contribution in #195
- @makotodejima made their first contribution in #62
Full Changelog: v0.3.0...v0.4.0