Files
claude-nova/docs/features/ide-bridge.md
T
2026-08-11 12:38:02 -03:00

5.2 KiB

IDE bridge

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

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 — unlike the stdio protocol in session transport, which has. Treat every claim here as needing a first-run check.

What it would add

Today the extension drives Claude: it spawns claude --print and owns the session. The IDE bridge inverts that — Nova becomes a server that a claude process running in a terminal connects to, so the terminal 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 two modes are complementary, not competing. Driving is better for an in-editor chat surface; the bridge is better if you live in the terminal — and it gets the rich Claude Code TUI for free, which is the one thing Nova's UI cannot render well.

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 from tool approvals: stage the proposed file, open it, and map save → FILE_SAVED, close → DIFF_REJECTED. The tool blocks until one of those.
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 v1 deliberately 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; the user opens Nova's terminal themselves and runs claude. Discovery is automatic from there — the CLI finds the lock file without being told. A claudenova.copyTerminalCommand helper could put the right env prefix on the clipboard for shells that do not inherit it.

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. Can the bridge and the driven session coexist in one window without the CLI getting confused about which IDE it is attached to?
  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.