AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP verified MIT Self-run

Harnt Nvim

mcp-pieterpel-harnt-nvim · by PieterPel

Drive any native-TUI coding agent in Neovim at full fidelity via reverse-MCP.

No reviews yet
0 installs
13 views
0.0% view→install

Install

$ agentstack add mcp-pieterpel-harnt-nvim

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/mcp-pieterpel-harnt-nvim)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
17d ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Harnt Nvim? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

harnt.nvim

> Run any coding agent in Neovim, at full native fidelity, behind one shared > editor layer. The agent keeps its own TUI — harnt never renders a chat box, so > no feature is ever lost.

One diff flow, one approval popup, one keymap set — three different agents, each in its own native TUI, each reaching back into the same editor layer:

| Claude Code | Codex | Antigravity | |---|---|---| | | | |

Each is recorded against the real CLI (just demo; see [DEMO.md](./docs/DEMO.md)).

Status: beta. Working end-to-end for Claude Code, Codex, Antigravity (agy), and OpenCode — diffs, approvals, editor-context, a change-log, and one keymap set across all three, each verified against the real CLI. Pure Lua, no daemon. Not yet on luarocks (install from git — see below). See [PLAN.md](./docs/PLAN.md) for the roadmap and [BET.md](./docs/BET.md) for why this exists.


The idea in one paragraph

Every modern coding agent ships its own terminal UI and a way to reach into your editor for editor-shaped work — open this diff, give me the selection, what are the diagnostics, apply these edits, ask the user to approve. Claude Code calls it its IDE integration, Codex has /ide and its app-server, Antigravity has lifecycle hooks, OpenCode hosts an HTTP server its own TUI is a client of. Different wires, one shape: the editor is a tool-server the agent reaches back into. harnt hosts that channel for many agents and unifies the genuinely shared part — context, diff, approvals, apply, and one set of keymaps — while each agent's own TUI stays untouched in a terminal split. That is the opposite of ACP-style tools, which unify the agent protocol and strip the deep features in the process.

What it is (and isn't)

  • ✅ One keymap / diff / approval / config surface across every agent.
  • ✅ Full native fidelity — plan mode, slash commands, /compact, streaming,

resume all live in the agent's own TUI, which we never replace.

  • ✅ Interactive diffs and approvals shown in Neovim, even when the

agent would otherwise auto-apply; plus a per-session change-log (:Harnt changes) of everything the agent touched.

  • ✅ Pure Lua, in-process, on vim.uv. No mandatory Node/Bun runtime, no daemon.
  • Not a chat UI, and not a driver for headless-only agents (ACP /

app-server as sole surface). Use an ACP client for those — see the non-goal in [PLAN.md](./docs/PLAN.md).

Providers

| Provider | How harnt connects | Diffs / approvals | Editor context | Status | |---|---|---|---|---| | Claude Code | WebSocket IDE integration (lockfile) | openDiff | IDE tools (live) | ✅ verified | | Codex | app-server proxy (stdio) behind codex --remote; /ide unix socket for context | app-server tap | /ide socket (pull) | ✅ verified | | Antigravity (agy) | lifecycle hooks (.agents/hooks.json) | PreToolUse gate | PreInvocation inject | ✅ verified | | OpenCode | client of the agent's own HTTP server (opencode serve) behind opencode attach; /event SSE tap | edit → diff review, cmd → approval (HTTP reply) | @-mention push (/tui/append-prompt) | ✅ wire verified | | Fake | in-process | — | — | test seam | | Qwen | companion spec | — | — | planned |

Each agent is met in its own native protocol — there is deliberately no universal wire format (that's the whole bet). Adding an agent is a Lua table on the shared editor services; third parties can register one without a core PR. See [PROVIDERS.md](./docs/PROVIDERS.md).

Requirements

  • Neovim 0.10+.
  • The CLI for each agent you want to use, installed and authenticated:
  • Claude Code (claude), Codex (codex), Antigravity (agy), OpenCode (opencode).
  • nc (netcat) — only for Antigravity's hook bridge.
  • Run :checkhealth harnt — it reports, per provider, what's present and what's missing.

Installation

Pure Lua, no dependencies beyond Neovim. Not published to luarocks yet, so install from git.

lazy.nvim

{
  "harnt.nvim",
  url = "https://github.com/PieterPel/harnt.nvim",
  opts = {},
  -- optional: your own launch keymaps
  keys = {
    { "ac", "Harnt open claude", desc = "harnt: Claude" },
    { "ax", "Harnt open codex",  desc = "harnt: Codex" },
    { "ag", "Harnt open antigravity", desc = "harnt: Antigravity" },
    { "ao", "Harnt open opencode", desc = "harnt: OpenCode" },
  },
}

rocks.nvim

:Rocks install harnt.nvim   " once published to luarocks

Quickstart

require("harnt").setup({})   -- registers claude, codex, antigravity, opencode

Then:

:Harnt open claude        " launch the agent's TUI in a split; harnt hosts its channel

Work with the agent as you normally would. When it proposes an edit, harnt shows it as a diff in Neovim; when it wants to run a command, an approval popup appears. The keys are the same for every agent.

Commands

| Command | Does | |---|---| | :Harnt open [provider] | Launch a provider's TUI (default claude) | | :Harnt toggle [provider] | Show/hide (or launch) a provider's terminal | | :Harnt stop [provider] | Stop one provider, or all | | :Harnt send | Send the current file/selection to running agents (@-mention) | | :Harnt accept / reject | Accept / reject the current diff | | :Harnt review | Reject the diff and send your inline comments back as feedback | | :Harnt changes | Browse the session change-log (read-only) | | :Harnt health | :checkhealth harnt |

Keymaps (buffer-local, inside a diff/review)

| Key | Action | |---|---| | a | accept the diff | | r | reject the diff | | c | comment on the current line | | R | submit review (reject + send comments as feedback) |

Configuration

Everything lives under require("harnt").setup{}. The full set of options and their defaults:

require("harnt").setup({
  keymaps = {
    diff = {                     -- buffer-local, active inside a diff/review window
      accept  = "a",
      reject  = "r",
      comment = "c",     -- comment on the current line
      review  = "R",     -- reject + send the comments back as feedback
    },
  },
  diff = { presenter = nil },    -- how a proposed change is *shown* (see below)
  approvals = { chooser = nil }, -- how an approval prompt is *shown* (see below)
})

Rendering diffs your own way

harnt's diff service owns the change's lifecycle (baseline vs. proposal buffers, accept/reject, writing the result) and stays UI-agnostic; how it's laid out is a presenter you can replace. The default lays the two buffers out as a side-by-side vimdiff in a new tab, with a winbar showing the keys.

A presenter is given the buffers the service already created and returns a teardown to dismiss whatever windows it opened:

require("harnt").setup({
  diff = {
    -- view = { path, original_buf, proposed_buf }
    presenter = function(view)
      -- e.g. a floating window instead of a tab:
      local win = vim.api.nvim_open_win(view.proposed_buf, true, {
        relative = "editor", width = 100, height = 30, row = 2, col = 4,
        border = "rounded", title = " " .. vim.fn.fnamemodify(view.path, ":t") .. " ",
      })
      return { teardown = function() pcall(vim.api.nvim_win_close, win, true) end }
    end,
  },
})

The keys (accept/reject/comment/review) are bound on those buffers by the service regardless of presenter, so a custom presenter never has to re-implement them. view.original_buf is absent for review-only diffs (an agent that applies its own edit and only wants a verdict), where proposed_buf is a rendered patch.

Rendering approvals your own way

Command approvals go through a chooser (default: vim.ui.select, so it already follows whatever UI you've wired vim.ui.select to — dressing.nvim, snacks, telescope, …). Replace it for a fully custom prompt:

require("harnt").setup({
  approvals = {
    -- req = { key, prompt, detail? }
    -- decision ∈ "allow_once" | "allow_always" | "deny_once" | "deny_always"
    chooser = function(req, on_choice)
      vim.ui.select({ "allow once", "allow always", "deny once", "deny always" }, {
        prompt = req.prompt,
      }, function(_, idx)
        on_choice(({ "allow_once", "allow_always", "deny_once", "deny_always" })[idx or 1])
      end)
    end,
  },
})

Per-provider notes

  • Claude Code — harnt writes ~/.claude/ide/.lock and hosts the

WebSocket the CLI connects into; edits are also recorded via a PostToolUse hook so auto-applied changes still show in :Harnt changes.

  • Codex — harnt proxies codex app-server (stdio) and launches the native

TUI via codex --remote; diffs/approvals are tapped from that stream, while a separate /ide unix socket feeds the agent your live selection + open files.

  • Antigravity (agy) — harnt merges a harnt hook into .agents/hooks.json

(non-destructively, restored on stop): PreToolUse gates edits/commands (blocking = interactive diffs), PreInvocation injects editor context. Needs nc. Runs agy interactively in the split.

  • OpenCode — harnt spawns opencode serve (its own HTTP+SSE server) and

launches the native TUI via opencode attach; it taps the /event stream. Edit permissions show as an interactive diff (accept/reject/comment, reconstructed from the tool call and answered over HTTP with free-text feedback on reject); command permissions use the approval popup; auto-applied edits land in :Harnt changes; and :Harnt send pushes the current file/selection as a native @-mention. The TUI drives and renders; harnt observes. OpenCode also ships a headless opencode acp surface — the exact thing harnt refuses to render; we use the native TUI + server instead. See [OPENCODE.md](./docs/OPENCODE.md).

No feature loss (the guarantee)

Because harnt never renders the agent's chat, the agent's own features are untouched. Verified end-to-end per provider (just e2e-*):

| Agent | Verified intact through harnt | |---|---| | Claude | plan mode, slash commands, /compact; openDiff + diagnostics + selection | | Codex | /ide context ("IDE context is on"), app-server diffs + approvals, native TUI | | Antigravity | PreToolUse diff gate (reject blocks / accept writes), PreInvocation context | | OpenCode | native TUI (plan/build agents, slash commands, sessions) untouched; edit permission → diff review, command → approval, session.diff → change-log, :Harnt send@-mention |

Extending

Add an agent with a table — see [PROVIDERS.md](./docs/PROVIDERS.md). The shared services (context / diff / approvals / apply / changes) are the reusable heart; a provider is mostly discovery + env + a tool/decision map on top.

Development

Everything runs through Nix + just, identically on your machine and in CI:

nix develop            # enter the toolchain (stylua, selene, emmylua, busted, …)
just                   # list tasks
just ci                # the full gate: fmt-check · lint · typecheck · test · smoke
just test              # busted, with Neovim as the Lua interpreter (nlua)
just smoke             # clean-room load check (fresh install, nothing else on rtp)
just e2e-codex-ide     # real-CLI smoke (needs the agent authed); see the justfile

Tooling decisions live in [TOOLS.md](./docs/TOOLS.md).

License

[MIT](./LICENSE) © 2026 Pieter Pel

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.