# 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](https://github.com/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](session-transport.md), 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/.lock` (or `$CLAUDE_CONFIG_DIR/ide/`): ```json { "pid": 1234, "workspaceFolders": ["/path/to/project"], "ideName": "Nova", "transport": "ws", "authToken": "<32-char lowercase hex, 128 bits from a CSPRNG>" } ``` 3. Export `CLAUDE_CODE_SSE_PORT=` and `ENABLE_IDE_INTEGRATION=true` to the terminal where `claude` runs. 4. Claude connects and must present `x-claude-code-ide-authorization: ` 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](tool-approvals.md): 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/.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.