claude-preview.nvim

skill
Security Audit
Fail
Health Pass
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Community trust — 88 GitHub stars
Code Fail
  • execSync — Synchronous shell command execution in opencode-plugin/nvim.ts
  • process.env — Environment variable access in opencode-plugin/nvim.ts
Permissions Pass
  • Permissions — No dangerous permissions requested
Purpose
This is a Neovim plugin that provides a live diff preview of file changes made by AI coding agents (like Claude Code or OpenCode), allowing users to review and approve edits before they are written to disk.

Security Assessment
The overall risk is Medium. The plugin does not request explicitly dangerous permissions and does not appear to make unauthorized external network requests. However, the automated audit raised a flag for synchronous shell command execution (`execSync`) and environment variable access (`process.env`) within its OpenCode integration script. Because its core function involves intercepting AI edits and executing shell commands to display diffs, this behavior is expected. Still, users should verify exactly which local commands and environment variables are being accessed. No hardcoded secrets were detected.

Quality Assessment
The project exhibits strong health and maintenance signals. It is licensed under the permissive and standard MIT license. It is actively maintained, with its most recent code push occurring today. Additionally, it has garnered 88 GitHub stars, indicating a healthy level of community trust and usage for a niche developer tool.

Verdict
Use with caution—while the tool is actively maintained and safe in concept, developers should quickly review its shell execution scripts before integrating it into their local development environment.
SUMMARY

A Neovim plugin that shows a live diff preview of AI coding agent's file edits before you accept or reject them.

README.md

claude-preview.nvim

A Neovim plugin that shows a diff preview before your AI coding agent applies any file change — letting you review exactly what's changing before accepting.

Supports Claude Code and OpenCode as backends.


Demo

Claude Code

Claude Code demo

OpenCode

OpenCode demo


Table of Contents


Features

  • Diff preview — side-by-side or inline diff opens in Neovim before any file is written
  • Multiple layouts — tab, vsplit, or GitHub-style inline diff with syntax highlighting
  • Neo-tree integration — file tree indicators show which files are being modified, created, or deleted
  • Multi-backend — works with Claude Code CLI and OpenCode
  • No Python dependency — file transformations use nvim --headless -l

Requirements

  • Neovim >= 0.9

For Claude Code backend:

For OpenCode backend:


Installation

lazy.nvim

{
  "Cannon07/claude-preview.nvim",
  config = function()
    require("claude-preview").setup()
  end,
}

Manual (path-based)

vim.opt.rtp:prepend("/path/to/claude-preview.nvim")
require("claude-preview").setup()

Quick Start

Claude Code

  1. Install the plugin and call setup()
  2. Open a project in Neovim
  3. Run :ClaudePreviewInstallHooks — writes hooks to .claude/settings.local.json
  4. Restart Claude Code CLI in the project directory
  5. Ask Claude to edit a file — a diff opens automatically in Neovim
  6. Accept/reject in the CLI; the diff closes automatically on accept
  7. If rejected, press <leader>dq to close the diff manually

OpenCode

  1. Install the plugin and call setup()
  2. Open a project in Neovim
  3. Run :CodePreviewInstallOpenCodeHooks — copies the plugin to .opencode/plugins/
  4. Ensure your OpenCode config (~/.config/opencode/opencode.json) has permission prompts enabled:
    {
      "permission": {
        "edit": "ask",
        "bash": "ask"
      }
    }
    
  5. Start OpenCode in the project directory
  6. Ask OpenCode to edit a file — a diff opens automatically in Neovim
  7. Accept/reject in OpenCode; the diff closes automatically on accept
  8. If rejected, press <leader>dq to close the diff manually

How it works

AI Agent (terminal)                              Neovim
        |                                          |
   Proposes an Edit                                |
        |                                          |
   Hook/plugin fires ──→ compute diff ──→ RPC → show_diff()
        |                                          | (side-by-side or inline)
   CLI: "Accept? (y/n)"                            |
        |                                     User reviews diff
   User accepts/rejects                            |
        |                                          |
   Post hook fires ────→ cleanup ─────→ RPC → close_diff()

Claude Code uses shell-based hooks (PreToolUse/PostToolUse) configured in .claude/settings.local.json.

OpenCode uses a TypeScript plugin (tool.execute.before/tool.execute.after) loaded from .opencode/plugins/.

Both backends communicate with Neovim via RPC (nvim --server <socket> --remote-send).


Configuration

All options with defaults:

require("claude-preview").setup({
  diff = {
    layout   = "tab",    -- "tab" (new tab) | "vsplit" (current tab) | "inline" (GitHub-style)
    labels   = { current = "CURRENT", proposed = "PROPOSED" },
    auto_close = true,   -- close diff after accept
    equalize   = true,   -- 50/50 split widths (tab/vsplit only)
    full_file  = true,   -- show full file, not just diff hunks (tab/vsplit only)
  },
  highlights = {
    current = {          -- CURRENT (original) side — tab/vsplit layouts
      DiffAdd    = { bg = "#4c2e2e" },
      DiffDelete = { bg = "#4c2e2e" },
      DiffChange = { bg = "#4c3a2e" },
      DiffText   = { bg = "#5c3030" },
    },
    proposed = {         -- PROPOSED side — tab/vsplit layouts
      DiffAdd    = { bg = "#2e4c2e" },
      DiffDelete = { bg = "#4c2e2e" },
      DiffChange = { bg = "#2e3c4c" },
      DiffText   = { bg = "#3e5c3e" },
    },
    inline = {           -- inline layout
      added        = { bg = "#2e4c2e" },          -- added line background
      removed      = { bg = "#4c2e2e" },          -- removed line background
      added_text   = { bg = "#3a6e3a" },          -- changed characters (added)
      removed_text = { bg = "#6e3a3a" },          -- changed characters (removed)
    },
  },
})

Commands

Command Description
:ClaudePreviewInstallHooks Install Claude Code hooks to .claude/settings.local.json
:ClaudePreviewUninstallHooks Remove Claude Code hooks (leaves other hooks intact)
:CodePreviewInstallOpenCodeHooks Install OpenCode plugin to .opencode/plugins/
:CodePreviewUninstallOpenCodeHooks Remove OpenCode plugin
:ClaudePreviewCloseDiff Manually close the diff (use after rejecting a change)
:ClaudePreviewStatus Show socket path, hook status, and dependency check
:checkhealth claude-preview Full health check (both backends)

Keymaps

Key Description
<leader>dq Close the diff (same as :ClaudePreviewCloseDiff)

Diff Layouts

claude-preview supports three diff layouts, configured via diff.layout:

Layout Description
"tab" (default) Side-by-side diff in a new tab — CURRENT on the left, PROPOSED on the right
"vsplit" Side-by-side diff as a vertical split in the current tab
"inline" GitHub-style unified diff in a single buffer with syntax highlighting preserved

Inline diff features

  • Syntax highlighting — the file's language highlighting is preserved
  • Character-level diffs — changed portions within a line are highlighted with a brighter background
  • Sign column+/- signs indicate added/removed lines
  • Navigation]c / [c to jump between changes

To use inline diff:

require("claude-preview").setup({
  diff = { layout = "inline" },
})

Neo-tree Integration (Optional)

If you use neo-tree.nvim, claude-preview will automatically decorate your file tree with visual indicators when changes are proposed. No extra configuration is required — it works out of the box.

neo-tree integration demo

What you get

Status Icon Name Color Description
Modified 󰏫 Orange An existing file is being edited
Created 󰎔 Cyan + italic A new file is being created (shown as a virtual node)
Deleted 󰆴 Red + strikethrough A file is being deleted via rm

Additional behaviors:

  • Auto-reveal — the tree expands to highlight the changed file
  • Virtual nodes — new files/directories appear in the tree before they exist on disk
  • Clean focus — git status, diagnostics, and modified indicators are temporarily hidden while changes are pending
  • Auto-cleanup — all indicators clear when you accept, reject, or press <leader>dq

Neo-tree configuration options

All neo-tree options with defaults:

require("claude-preview").setup({
  neo_tree = {
    enabled = true,             -- set false to disable neo-tree integration
    position = "right",         -- neo-tree window position: "left", "right", "float"
    symbols = {
      modified = "󰏫",
      created  = "󰎔",
      deleted  = "󰆴",
    },
    highlights = {
      modified = { fg = "#e8a838", bold = true },
      created  = { fg = "#56c8d8", bold = true },
      deleted  = { fg = "#e06c75", bold = true, strikethrough = true },
    },
  },
})

Note: Neo-tree is a soft dependency. If neo-tree is not installed, the plugin works exactly as before — only the diff preview.


Architecture

claude-preview.nvim/
├── lua/claude-preview/
│   ├── init.lua        setup(), config, commands
│   ├── diff.lua        show_diff(), close_diff()
│   ├── hooks.lua       install/uninstall for both backends
│   ├── changes.lua     change status registry (modified/created/deleted)
│   ├── neo_tree.lua    neo-tree integration (icons, virtual nodes, reveal)
│   └── health.lua      :checkhealth (both backends)
├── bin/                          Claude Code hook scripts
│   ├── claude-preview-diff.sh    PreToolUse hook entry point
│   ├── claude-close-diff.sh      PostToolUse hook entry point
│   ├── nvim-socket.sh            Neovim socket discovery
│   ├── nvim-send.sh              RPC send helper
│   ├── apply-edit.lua            Single Edit transformer
│   └── apply-multi-edit.lua      MultiEdit transformer
└── opencode-plugin/              OpenCode plugin
    ├── index.ts                  tool.execute.before/after hooks
    ├── nvim.ts                   Neovim socket discovery + RPC
    └── edits.ts                  Edit computation helpers

Testing

The test suite uses plenary.nvim for core plugin tests and shell scripts for backend integration tests. CI runs on both Ubuntu and macOS.

./tests/run.sh                      # all tests (plugin + backends)
./tests/run.sh plugin               # core plugin tests only (plenary busted)
./tests/run.sh backends             # all backend integration tests
./tests/run.sh backends/claude      # Claude Code backend only
./tests/run.sh backends/opencode    # OpenCode backend only

Dependencies: Neovim >= 0.10, jq, bun (for OpenCode tests). Plenary is auto-installed to deps/ on first run.


Recommended companion settings

For buffers to auto-reload after a file is written, add this to your Neovim config:

vim.o.autoread = true
vim.api.nvim_create_autocmd({ "FocusGained", "BufEnter", "CursorHold" }, {
  command = "checktime",
})

Troubleshooting

Diff doesn't open

  • Run :ClaudePreviewStatus — check that Neovim socket is found
  • Run :checkhealth claude-preview — check for missing dependencies
  • Restart the CLI agent after installing hooks (hooks are read at startup)

Claude Code hooks not firing

  • Run :ClaudePreviewInstallHooks in the project root
  • Verify .claude/settings.local.json contains the hook entries
  • Ensure jq is in PATH
  • Restart Claude Code CLI

OpenCode plugin not loading

  • Run :CodePreviewInstallOpenCodeHooks in the project root
  • Verify .opencode/plugins/index.ts exists
  • Ensure "permission": { "edit": "ask" } is set in ~/.config/opencode/opencode.json
  • Restart OpenCode

Diff doesn't close after rejecting

  • Press <leader>dq or run :ClaudePreviewCloseDiff — the post hook only fires on accept

License

MIT — see LICENSE

Reviews (0)

No results found