Files
claude-nova/docs/features/ide-bridge.md
T

123 lines
5.3 KiB
Markdown

# 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](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.** 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](launching.md)); 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/`):
```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=<port>` and `ENABLE_IDE_INTEGRATION=true` to the
terminal where `claude` runs.
4. 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.