verify-claudecode-nvim
Skill3.1k starsChanged todayMITView on GitHub
Drive the real claudecode.nvim plugin in a real Neovim (an agent-tty terminal) while a scripted WebSocket client (scripts/ws-client.mjs) plays the Claude Code CLI over the plugin's WebSocket MCP server, and capture screen text, PNG screenshots, WebM video, JSON-RPC traffic, and file state as evidence. Use to prove a claudecode.nvim feature works end to end (openDiff accept/reject, openFile, selection tracking, :ClaudeCodeSend at-mentions, closing diffs) without the claude CLI or network.
- Reads credentials
- Installs packages
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 1 is distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 19
Run every command from the repo root.
- Very specific: line 83
After Escape, wait for $V until 'mode()' n before typing a : command.
- Very specific: line 84
After FILE_SAVED, Claude writes the file itself: write result.content[1].text to disk, then call close_tab.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Verify claudecode.nvim
The user surface is Neovim plus the Claude Code CLI. This skill keeps Neovim real: an agent-tty terminal session, keystrokes through agent-tty type and send-keys, state read over Neovim RPC. Only the CLI is replaced, by scripts/ws-client.mjs, at the boundary the CLI uses: it reads the lock file the plugin writes (the token stays out of argv), sends the x-claude-code-ide-authorization header, and speaks MCP JSON-RPC.
Each run gets its own HOME, XDG dirs, CLAUDE_CONFIG_DIR, agent-tty home, and nvim socket. Runs never touch the user's Neovim or ~/.claude/ide, and two runs can go side by side.
Needs:
nvim0.12.3,jq, and Node 26, all frommise install.agent-tty0.5.0 (npm install -g agent-tty). It needs Node 24 to 26, so the harness runs it on the repo's mise Node.- Playwright Chromium for PNG and WebM:
npx playwright install chromium. setsidfrom util-linux.
Run every command from the repo root. Read features/README.md before driving: it is the map of what to verify and how.
V=.agents/skills/verify-claudecode-nvim/scripts/vcn.shLaunch
$V launch # opens hello.txt; `$V launch other.txt` opens the other seed fileLaunch prints two lines, for example:
VCN_STATE=/tmp/vcn.AbC123
OUT=/home/coder/.xum/sessions/<ws>/scratch/verify-claudecode-nvim/20261031T120000Z-AbC123Every bash call is a fresh shell. Start each later call with the VCN_STATE value launch printed. Use a new request id for every rpc or tool call, because the harness refuses an id the run already used: export VCN_STATE=/tmp/vcn.AbC123; source "$VCN_STATE/run.env". That also sets OUT, WORK, PORT, SID (the agent-tty session), and NVIM_PID.
Launch first runs agent-tty doctor and saves it to $OUT/agent-tty-doctor.json. A failed check, such as the Node version or the Chromium launch, stops the launch and names the check. Launch then returns only when the run is usable: nvim answers RPC, the plugin wrote <port>.lock naming the nvim pid, the client finished the handshake, and the MCP initialize call answered with a result. If a step misses its 10 s deadline, or initialize answers without a string result.protocolVersion, launch saves $OUT/launch-failure.screen.txt, tears the run down, and exits 1.
- Seed files in
$WORK(nvim's cwd and the workspace folder):hello.txtisalpha,beta,gamma.other.txtisone,two,three,four. - The terminal is 160 columns by 40 rows.
VERIFY_OUT=<dir>sets the evidence dir. It must be missing or an empty directory, and its path must not have a.or..part. Launch refuses it if it is a symlink, is not a directory, is owned by another user, or is group- or world-writable, or if a dir above it is unsafe (see Evidence). It also refuses a path thatmkdircannot create. When it refuses a dir aftermkdir, it runsrmdiron each dir that was missing beforemkdir. That never deletes a file or a non-empty dir, but it does remove an empty dir that another process made at one of those paths in the meantime. Launch creates a missing evidence dir with mode0700, so a default ACL on its parent never gives the group access to it. It removes any ACL from the evidence dir withsetfacl -b -kand makes it0700, and it refuses the dir when a new file there still does not get mode0600, for example under a default ACL with no workingsetfacl. A relative path is taken from the current directory and recorded as an absoluteOUT.VCN_INIT_JSONRPC=<version>sets thejsonrpcfield of theinitializerequest (default2.0). Onlyscripts/proof-launch-refusals.shsets it, to get an error answer from the plugin.VCN_SETUP='<json>'is deep-merged intorequire("claudecode").setup()over the base{ terminal = { provider = "none" } }. Example:VCN_SETUP='{"diff_opts":{"layout":"unified"}}' $V launch.
Doctor
$V doctorRead-only. Prints one ok or FAIL line per check and exits 1 on any FAIL: agent-tty session running, nvim server running under the session's TUI process, the ws-client.mjs client alive, RPC answers as the recorded pid, nvim not stuck in a prompt, lock file names that pid, plugin server running on the lock port, lock authToken matches the server token (compared by sha256), a client finished the handshake. Run it first whenever a step misbehaves.
- A
FAILon the client or handshake line means the client died. Clean up and relaunch, because a new client cannot receive answers to calls the old one made. - A
FAILon the RPC line usually means nvim waits atPress ENTER, where it answers no RPC.$V exprthen printsvcn: nvim RPC gave no answer in 5s.$V capture blockedshows the message. Keep that capture as evidence, then$V keys Enterdismisses it.
Drive
The user acts through keys. Claude acts through tool calls. Read state only to observe.
User.
$V keys <arg>...sends input to nvim, in order, as oneagent-tty batch:Argument agent-tty step Examples A key name: Enter,Escape,Tab,Backspace,Space, arrows,Home,End,PageUp,F1-F12, orCtrl+/Alt+a keysend-keysEnter,Escape,Ctrl+W,UpAny other argument type, verbatim':w',gg,cc,VEvery argument after -ltype, verbatim-l 'Enter the name'So
$V keys ':3' Enter cctypes:3, presses Enter, and typescc. Key names match case-sensitively:Enteris a key,enteris text. tmux names such asC-worBSpaceare typed as text, so writeCtrl+WandBackspace.- Claude.
$V tool <id> <toolName> '<arguments json>'sendstools/call.$V rpc <id> <method> '<params json>'sends any request, for example$V rpc t1 tools/list.$V await <id> [secs]prints that response line or exits 1 after the timeout (default 10 s). Build arguments withjq -nc --arg ...so paths and newlines stay valid JSON. - A second Claude.
VCN_CLIENT=b $V connectstarts one more client, namedb, and finishes its handshake. PutVCN_CLIENT=bbeforerpc,tool, orawaitto act asb. Its messages land in$OUT/ws-out-b.jsonland$OUT/ws-in-b.jsonl, and request ids are per client.$V disconnectkills the launch client by its recorded PID, the way an exiting Claude CLI drops its socket.VCN_CLIENT=b $V disconnectkillsb. - Notifications. The plugin pushes
selection_changedandat_mentionedto the client. They land in$OUT/ws-in.jsonl:jq -c 'select(.method == "at_mentioned")' "$OUT/ws-in.jsonl". - Observe.
$V expr '<vimscript>'evaluates over RPC.$V until '<expr>' '<want>' [secs]waits for a state instead of sleeping. Examples:$V until "bufname() =~# ' (proposed)$'" 1,$V expr "join(getline(1, '\$'), '|')",$V expr 'luaeval("vim.json.encode(require(\"claudecode.server.init\").get_status())")'. - Capture.
$V capture <name>writes the screen text to$OUT/<name>.screen.txtand prints it.$V screenshot <name>writes$OUT/<name>.png.$V record <name>writes$OUT/<name>.webm, a video of the whole session from launch to now, sped up. All three first wait up to 5 s for the screen to stay unchanged for 300 ms.
Rules:
- Change state only through keys or tool calls. Calling plugin Lua over RPC to change state skips the path you are proving.
- openDiff answers only after the user acts.
$V await <id> 2exiting 1 before the action is the proof that it blocks. - After
Escape, wait for$V until 'mode()' nbefore typing a:command. - After
FILE_SAVED, Claude writes the file itself: writeresult.content[1].textto disk, then callclose_tab. The plugin never writes it. $V untilconfirms nvim state, not the screen. Use$V captureor$V screenshot, which wait for the screen, when the evidence is what the user sees.- To watch live, a human runs
source "$VCN_STATE/run.env"; PATH="$NODE_DIR:$PATH" agent-tty --home "$ATT_HOME" dashboard --session "$SID"in a terminal. The dashboard is read-only and leaves the session size alone.
Evidence
Everything lands in $OUT, which cleanup never deletes. The default is ${XUM_SCRATCH_DIR:-/tmp}/verify-claudecode-nvim/<UTC time>-<id>. The harness runs with umask 077, so $OUT is 0700 and its files are 0600: they hold buffer text and accepted diffs. A default ACL on $OUT would override the umask, so launch removes it first (see VERIFY_OUT). Launch refuses a default parent dir (${XUM_SCRATCH_DIR:-/tmp}/verify-claudecode-nvim) that is a symlink, is owned by another user, or is group- or world-writable. It also refuses either dir when a dir above it, as given or as resolved, is owned by a user other than you or root, or is writable by others without the sticky bit. On a shared host, set VERIFY_OUT to a dir you own.
ws-out.jsonlholds every JSON-RPC message the client sent.ws-in.jsonlholds every message it received, responses and notifications. A client fromconnectwritesws-out-<name>.jsonl,ws-in-<name>.jsonl, andws-client-<name>.log.<name>.screen.txt,<name>.png, and<name>.webmcome from$V capture,$V screenshot, and$V record.run.envrecords paths, PIDs, port, session id, and the nvim and agent-tty versions. It holds no auth token.agent-tty-doctor.jsonandws-client.logsit next to it.- Cleanup adds
final.screen.txt,final.png,messages.txt(:messages), andsession.webm. - The
⧉in diff tab names draws as an empty box in PNG and WebM, because neither agent-tty's fonts nor this host's fonts have that glyph. The.screen.txtfile holds the real character.
A proof:
- Captures the screen text and a PNG before and after each user action, plus the response JSON, and ends with a WebM.
- Checks side effects directly:
sha256sum "$WORK/<file>"before and after, buffer text through$V expr. - Replaces nothing but the Claude CLI, and only at its WebSocket boundary. No stubbed plugin modules, no internal setters.
- Names the feature ID from
features/and the entry point (key, command, or tool) next to each artifact it cites. A path that was not driven is reported as not verified, never covered by a different entry point.
Cleanup
$V cleanupCleanup saves the final screen text, a PNG, :messages, and a WebM of the whole session into $OUT. Then it destroys the agent-tty session, which ends nvim, and stops the client by its recorded PID. It waits for every process tagged VCN_RUN_TAG=$VCN_STATE to exit, and after 3 s kills only those. Then it drops the run's agent-tty home from the per-user home registry, removes its empty socket directory under /tmp/agent-tty, deletes $VCN_STATE, and leaves $OUT alone. It takes 10 to 15 s. Rerunning it prints nothing to clean. Run it after every failed iteration too.
To confirm, grep -lzxF "VCN_RUN_TAG=$VCN_STATE" /proc/[0-9]*/environ 2>/dev/null prints nothing (the 2>/dev/null hides other users' unreadable processes). Never pkill by name or by -f pattern: other agents' nvim and agent-tty hosts run on the same host, and a -f pattern can match your own shell.
Helpers
scripts/vcn.shis the harness.$V helplists its subcommands.scripts/init.luais the nvim config. It puts this checkout first onruntimepath, setsterminal.provider = "none"so noclaudeCLI starts, and mergesVCN_SETUP.scripts/proof-launch-refusals.shproves that launch exits 1 and leaves no run behind wheninitializefails (it sendsjsonrpc1.0and the plugin answers -32600), and whenVERIFY_OUTor the default parent dir is unsafe or cannot be created. It also proves that a refused launch leaves the proof's temp dir as it found it, and that a relativeVERIFY_OUTthat starts with-works. It printsPASS <case>per case orFAIL: <reason>. It takes about 60 s.- The
out-dash,out-acl,out-madeandinitcases start a real run, so they need everything under Needs, including Playwright Chromium for the user who runs the proof. Without it,out-dashfails atagent-tty doctor. - The
parent-made-unsafe,out-acl-unremovable,out-aclandout-madecases needsetfacland a filesystem with default ACLs. Without them, the proof printsSKIPfor all four.out-aclchecks that every evidence file of a run in a0500dir with a default ACL is0600.out-madechecks that launch creates a newVERIFY_OUTin a dir with a default ACL as a0700dir whose evidence files are all0600. - The
initcase writes its evidence, withinit-refusal-launch.txt, to a freshmktemp -ddir. The proof deletes that dir when every case passes. When theinitcase fails, the proof keeps it and printsinit evidence kept in <dir>. Delete it when you are done.
- The
scripts/journey-opendiff-accept.shproves openDiff accept through:won a fresh launch. It printsPASS opendiff-acceptorFAIL: <reason>and writesopendiff-accept-*files to$OUT: screen text at each step, PNGs at open, after the edit, and after:w, a WebM, and the file's sha256 at each step.
A full run, with the VCN_STATE value from launch:
V=.agents/skills/verify-claudecode-nvim/scripts/vcn.sh
$V launch
export VCN_STATE=/tmp/vcn.AbC123
$V doctor
.agents/skills/verify-claudecode-nvim/scripts/journey-opendiff-accept.sh
$V cleanupTrending
Less talk lately.about the whole repository
Who's talking about coder/claudecode.nvim
- anomalyco/opencode“Editor integrations”GitHub · Jun 19, 2025 · 68 reactions
- microsoft/vscode“Support CLI agents”GitHub · Aug 21, 2025 · 10 reactions
- LazyVim/LazyVim“feat(extras): ai: add `claudecode.nvim`”GitHub · Jul 5, 2025 · 8 reactions
- folke/sidekick.nvim“feature: Native IDE Integration via ACP/MCP Protocol”GitHub · Dec 18, 2025 · 6 reactions
- A Hacker News story“Show HN: Claudecode.nvim – Bringing Claude to Neovim (+how their protocol works)”Hacker News · Jun 2, 2025 · 4 points
The biggest 5 of 42 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 42
Talked about on GitHub, Hacker News and Bluesky (42 times).
On GitHub · 38
- Jun 19, 2025Editor integrations — anomalyco/opencode68 reactions · 33 comments
- Aug 21, 2025Support CLI agents — microsoft/vscode10 reactions · 2 comments
- Jul 5, 2025feat(extras): ai: add `claudecode.nvim` — LazyVim/LazyVim8 reactions · 2 comments
- Dec 18, 2025feature: Native IDE Integration via ACP/MCP Protocol — folke/sidekick.nvim6 reactions · 3 comments
- Mar 20, 2026feature: Integrate with /ide in various AI tools — folke/sidekick.nvim3 reactions · 6 comments
33 more on GitHub
- Jun 23, 2025[PLUGIN REQUEST] coder/claudecode.nvim — nix-community/nixvim2 reactions · 2 comments
- Sep 27, 2025Propuesta de cambio de plugin de Claude Code — Gentleman-Programming/Gentleman.Dots1 reaction · 2 comments
- Jul 9, 2025fix(vectorcode): wrap logging in vim.schedule to prevent fast event context errors — ravitemer/codecompanion-history.nvim1 reaction · 1 comment
- Oct 28, 2026chore(deps): bump home/.local/share/nvim/site/pack/default/opt/claudecode.nvim from `2390c6e` to `c0deb41` — perrin4869/dotfiles0 reactions
- Oct 27, 2026build(deps): Bump the submodules group with 2 updates — yindia/dotfiles0 reactions
- Oct 25, 2026Enables Neovim to open coding agents(Claude) — yu-hasebe/dotfiles0 reactions
- Sep 7, 2026[Resource]: claudecode.nvim — hesreallyhim/awesome-claude-code0 reactions · 1 comment
- Sep 7, 2026Add `coder/claudecode.nvim` — rockerBOO/awesome-neovim0 reactions
- Sep 4, 2026Claude-Code Plugins to add — rockerBOO/awesome-neovim0 reactions · 2 comments
- Aug 28, 2026claude_code_ide: Make Zed a recognised Claude Code IDE — zed-industries/zed0 reactions · 1 comment
- Jun 30, 2026[plugins] Add claudecode.nvim plugin configuration — tntzxzttt/nvim0 reactions
- Jun 26, 2026Install claude-code integration plugin — nox456/neovim-config0 reactions
- Jun 26, 2026Bump claudecode-nvim from `288c9bf` to `2390c6e` in /plugins — josh/lazy-nvim-nix0 reactions
- Jun 25, 2026feat: 遷移 CCC nvim 整合到 coder/claudecode.nvim + claude-tmux.nvim — Shiou-Ju/nvim0 reactions · 2 comments
- Jun 23, 2026Bump claudecode-nvim from `bcb3fe2` to `288c9bf` in /plugins — josh/lazy-nvim-nix0 reactions
- Jun 18, 2026Bump claudecode-nvim from `e6c9dd4` to `bcb3fe2` in /plugins — josh/lazy-nvim-nix0 reactions
- Jun 16, 2026Bump claudecode-nvim from `7c7d47e` to `e6c9dd4` in /plugins — josh/lazy-nvim-nix0 reactions
- Jun 12, 2026Bump claudecode-nvim from `7b8b709` to `7c7d47e` in /plugins — josh/lazy-nvim-nix0 reactions
- Jun 9, 2026Bump claudecode-nvim from `b9b99f9` to `7b8b709` in /plugins — josh/lazy-nvim-nix0 reactions
- Jun 8, 2026Bump claudecode-nvim from `8ec3212` to `b9b99f9` in /plugins — josh/lazy-nvim-nix0 reactions
- Jun 4, 2026Bump claudecode-nvim from `781f3ab` to `8ec3212` in /plugins — josh/lazy-nvim-nix0 reactions
- Jun 2, 2026Bump claudecode-nvim from `102d835` to `781f3ab` in /plugins — josh/lazy-nvim-nix0 reactions
- Apr 28, 2026feat(nvim): adopt official Claude Code extra, retire avante/gp/claude-code.nvim, fix obsidian fork + sops event — olafkfreund/nixos_config0 reactions
- Apr 28, 2026Bump claudecode-nvim from `432121f` to `102d835` in /plugins — josh/lazy-nvim-nix0 reactions
- Apr 27, 2026Weekly radar update — 2026-04-27 — breckenedge/claude-code-radar0 reactions
- Feb 26, 2026[Sprint 16] WebSocket server + lock file (ide_server.rs) — magiash/magia-legacy0 reactions
- Feb 26, 2026[Sprint 16] #579 Claude Code IDE Integration Protocol — magiash/magia-legacy0 reactions
- Feb 8, 2026feat(neovim): claude-code window 위치를 하단으로 변경 — iai6203/dotfiles0 reactions
- Jan 20, 2026feat: 🎸 refactor: migrate Neovim config from packer.nvim to lazy.nvim — kohbis/dotfiles0 reactions
- Oct 1, 2025at_mentioned support for sending context (region) to Claude Code — stevemolitor/monet0 reactions
- Sep 16, 2025vimPlugins.claude-fzf{-history}-nvim: init at 2025-09-06 — NixOS/nixpkgs0 reactions · 1 comment
- Jul 17, 2025coder/claudecode.nvim 用の ddt.vim ターミナルプロバイダを作る — gamoutatsumi/nix-config0 reactions · 12 comments
- Jun 30, 2025Support LSP Features — stevemolitor/claude-code.el0 reactions · 3 comments
On Hacker News · 3
- Jun 2, 2025Show HN: Claudecode.nvim – Bringing Claude to Neovim (+how their protocol works)4 points
- Jun 26, 2025Looking for AI tools in neovim3 points · 1 comment
- Aug 7, 2025Claudecode.nvim2 points · 1 comment
On Bluesky · 1
- Apr 4, 2026思いつきでNeovimの環境構築を半ばまで済ませ、 github.com/coder/claude... でClaude Codeを使えるようにしてみた — @ymatz.net3 likes and reposts · 1 reply
In this repository3 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- verify-diff-lifecycleSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

