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 commit89d1a06.)
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
- Serve WebSocket on a random port, bound to loopback.
- 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>"
}
- Export
CLAUDE_CODE_SSE_PORT=<port>andENABLE_IDE_INTEGRATION=trueto the terminal whereclauderuns. - 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
- Does the CLI require the
ideNameto be on an allowlist, or is any string accepted? - What does it do with tools that answer "unsupported" — degrade, or error the turn?
- 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?
- Lock-file lifecycle: who cleans up after a crash? (
pidis 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.