Files

5.3 KiB

IDE bridge

Status: Next — not built. This is a design note, not a description of shipped behaviour.

This is now the main roadmap item. With chat in a terminal, the bridge is what gives that session access to Nova: the selection, the open files, and diffs rendered for review in the editor instead of as terminal text.

Provenance: the protocol details below come from coder/claudecode.nvim's PROTOCOL.md, which documents a working reimplementation of the same integration the VS Code extension uses. None of it has been verified against the CLI by this project. Treat every claim here as needing a first-run check. (The stdio protocol the extension used previously was verified; that work is in commit 89d1a06.)

What it would add

Nova becomes a server that the claude process running in your terminal connects to, so that session can see the editor:

  • the current selection and open files as context
  • diffs pushed into Nova for review rather than rendered as terminal text
  • "open this file at this line" from Claude

The extension already launches the terminal session (see launching); the bridge is the return path.

How discovery works

  1. Serve WebSocket on a random port, bound to loopback.
  2. Write ~/.claude/ide/<port>.lock (or $CLAUDE_CONFIG_DIR/ide/):
{
  "pid": 1234,
  "workspaceFolders": ["/path/to/project"],
  "ideName": "Nova",
  "transport": "ws",
  "authToken": "<32-char lowercase hex, 128 bits from a CSPRNG>"
}
  1. Export CLAUDE_CODE_SSE_PORT=<port> and ENABLE_IDE_INTEGRATION=true to the terminal where claude runs.
  2. Claude connects and must present x-claude-code-ide-authorization: <authToken> during the handshake; the server validates it against the lock file.

Messages are JSON-RPC 2.0 over RFC 6455 WebSocket.

MCP tools to implement

Tool Nova feasibility
openFile nova.workspace.openFile(uri, {line, column})
getCurrentSelection activeTextEditor.selectedRange
getLatestSelection track via onDidChangeSelection
getOpenEditors nova.workspace.textEditors
getWorkspaceFolders nova.workspace.path
checkDocumentDirty document.isDirty
saveDocument editor.save()
openDiff ⚠️ No diff viewer API. Reuse the editable-proposal pattern: stage the proposed file, open it, and map save → FILE_SAVED, close → DIFF_REJECTED. The tool blocks until one of those. Working code for this — including change reconstruction and a unified diff renderer — is in commit 89d1a06 (Scripts/diff.js, Scripts/permissions.js).
close_tab Nova cannot close tabs programmatically
closeAllDiffTabs same
getDiagnostics An extension can only read its own IssueCollection. Claude running the linter itself via Bash is the workaround.
executeCode No notebook/Jupyter API

Returning a clear "unsupported" for the last four is fine — the VS Code extension's surface is a superset of what any given editor can do.

The Nova-shaped problem

Nova's JS runtime has no WebSocket (fetch, Streams, TextEncoder yes; WebSocket no). So the server cannot live in the extension. It needs a Node sidecar spawned via Process, with the extension and sidecar talking over stdio — Process supports stdio: "jsonrpc", which is a natural fit here since the payloads are already JSON-RPC.

That means Node 18+ becomes a runtime requirement, which the extension has so far avoided. Gate it behind a setting that is off by default.

Nova extension ──jsonrpc/stdio──> node sidecar ──ws──> claude (terminal)
      │                                │
      └─ Nova API (editors, selection) ┘   ~/.claude/ide/<port>.lock

The terminal-tab angle

Nova has a built-in terminal, but no extension API to create one or send text to it — commands can only be placed in the editor, extensions, text and command-palette menus, and there is no terminal object in the API surface.

So the realistic shape is: the extension runs the sidecar and publishes the lock file; you paste the launch command into Nova's terminal as you already do. Discovery is automatic from there — the CLI finds the lock file without being told. If the environment variables do not reach the terminal, launch.js can prefix them onto the copied command.

If Panic ever exposes a terminal API, the only change is automating that last step.

Open questions

  1. Does the CLI require the ideName to be on an allowlist, or is any string accepted?
  2. What does it do with tools that answer "unsupported" — degrade, or error the turn?
  3. With several terminal sessions open in one project, do they all attach to the same lock file — and does the editor need to distinguish them?
  4. Lock-file lifecycle: who cleans up after a crash? (pid is in the file precisely so stale entries can be detected.)

Effort

The WebSocket server, JSON-RPC plumbing, lock file and handshake are a well-defined chunk; the tool implementations are mostly one-liners against the Nova API. The risk is not the code — it is that the protocol is reverse engineered and unversioned, so it can change under us. claudecode.nvim tracking it successfully is good evidence it is stable enough in practice.