Skip to content
| Marketplace
Sign in
Visual Studio Code>Other>Editor Context — share what you're looking atNew to Visual Studio Code? Get it now.
Editor Context — share what you're looking at

Editor Context — share what you're looking at

pandarayc

|
2 installs
| (0) | Free
Publishes the file, line range and selected text you are looking at in Cursor/VS Code — over a WebSocket, in process to sibling extensions, and on disk to subscribed agents.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

editor-context-bridge

English · 中文

Publish the file and line range you are looking at in Cursor / VS Code, so an agent running on the same machine can read exactly that range.

The extension is a producer only. It writes one small JSON file per editor window and never talks to an agent:

~/.editor-context/windows/<extension-host pid>.json
{
  "version": 1,
  "pid": 9931,
  "appName": "Cursor",
  "updatedAt": "2026-10-16T10:25:58.977Z",
  "sig": "src/auth.ts|sel|11-14",
  "context": "[编辑器位置] /w/src/auth.ts 第 11-14 行",
  "workspaceFolders": ["/w"],
  "activeFile": "/w/src/auth.ts",
  "relativePath": "src/auth.ts",
  "languageId": "typescript",
  "selection": { "startLine": 11, "endLine": 14, "startColumn": 1, "endColumn": 5, "isEmpty": false }
}

One file per window, so there is no lock to take and no window overwrites another; a reader takes the newest by mtime. selection and activeFile are the structured reading, context is the same thing pre-rendered as one line for consumers that only want text, and sig changes when the position does — which is what lets a reader avoid saying the same thing twice.

The ambient file never contains your code. windows/<pid>.json is rewritten on every caret move, so it stays an address and nothing else. The selected text travels only when you ask for it — an explicit Add to Bot — or to a subscriber that opted into a continuous feed, and never for a path excludeGlobs rejects.

Subscribers

The extension is a publisher. It describes where you are and hands that to whoever asked; it does not know what any consumer does with it. There are two kinds of subscriber, and the difference is what lands on disk.

In process. A sibling extension gets every event with no filesystem at all:

const api = vscode.extensions.getExtension('pandarayc.editor-context-bridge')?.exports;
const sub = api.subscribe((delivery) => { /* ... */ });

Over a socket. The extension runs a WebSocket publisher on ws://127.0.0.1:7788/editor-context by default. A subscriber connects, gets a hello, sends one subscribe frame, and then receives a delivery per event — no filesystem, no polling, and the delivery arrives while you are still holding the selection.

// ← { "type": "hello", "protocol": "editor-context/1", "requiresToken": false, … }
→ { "type": "subscribe", "name": "dsh", "label": "DSH", "includeText": true, "autoSelection": false }
// ← { "type": "subscribed", "name": "dsh", "includeText": true, "autoSelection": false }
// ← { "type": "delivery", "delivery": { … } }

The same two knobs as a manifest, so routing is one function whatever transport a subscriber arrived on. Editor Context: Show WebSocket Address prints the URL and the number of connected subscribers.

Every window also announces where it is listening in ~/.editor-context/endpoints/<pid>.json — the port it actually got, the app name, the workspace folders, and a heartbeat every minute. The configured port is a preference, not a rule: a second window that finds 7788 taken binds an ephemeral port instead and announces that, so two windows of the same project both stay reachable. A consumer reads the directory, ignores the entries whose process is gone, and dials what is left — no fixed port to configure, and nothing to re-coordinate when a window restarts.

A non-loopback ws.host is refused unless ws.token is set; the token is checked before the policy is applied, so a client that guessed wrong learns nothing about what it would have received.

On disk. An agent that cannot dial in registers by writing one manifest — that file is the whole protocol, so a new consumer appears without an extension update:

~/.editor-context/
├── windows/<pid>.json                    where the caret is, always (address only)
├── requests/<ts>-<pid>-<seq>.json        the public mailbox: explicit sends
└── subscribers/
    ├── dsh.json                          the manifest, written by the subscriber
    └── dsh/inbox/<ts>-<pid>-<seq>.json   targeted deliveries
{ "label": "DSH", "includeText": true, "autoSelection": false }
Key Default Meaning
label the file name How the recipient picker names it
includeText true Whether deliveries carry the selected text. false sends the address only
autoSelection false Whether every changed non-empty selection is delivered, not only explicit sends

Editor Context: Add to Bot — on the editor title bar, in the right-click menu and in the palette — sends the current position to every registered subscriber and to the public mailbox. Add to Bot (choose recipient)… sends to one, and that delivery is private: it is not copied to the public mailbox.

The public mailbox exists for consumers that cannot register. A hook script runs only when a prompt is sent, so it has nowhere to put a manifest; it just reads requests/.

What a subscriber receives

The position fields carry the same names as the ambient file, so a reader that already parses windows/<pid>.json finds them where it expects:

{
  "version": 1,
  "kind": "editor-context/delivery",
  "id": "0f0b2c1e-…",
  "at": "2026-10-17T06:12:00.000Z",
  "reason": "explicit",
  "target": "dsh",
  "activeFile": "/w/src/auth.ts",
  "relativePath": "src/auth.ts",
  "languageId": "typescript",
  "selection": { "startLine": 11, "endLine": 14, "startColumn": 1, "endColumn": 5, "isEmpty": false },
  "context": "[编辑器位置] /w/src/auth.ts 第 11-14 行",
  "text": "export function login(user: User) {\n  …",
  "textTruncated": false
}

text is cut to editorContext.maxTextBytes and textTruncated says so. A selection longer than 5000 lines is not read at all beyond that point.

excludeGlobs produces no delivery, not an empty one. The rule is about the path appearing in a message, so a payload with the address blanked out would defeat it.

Pick your agent

Agent Install Where the position lands
DSH dsh-editor-context a chip in the composer — from @, or straight from the button — use this one
Claude Code the hook injected on the next prompt, on a changed position
anything else nothing read the JSON

The hook (not recommended)

The repository ships a UserPromptSubmit hook for editors that have no extension API, or for agents that only ever read a prompt. On DSH, skip it. Measured, it appends one durable user message per turn whenever the cursor is moving — a position is a high-cardinality dedup key, so the "only when it changed" cache never fires. The chip exists because of that measurement.

node scripts/install-dsh-hook.mjs              # copy the script, merge hooks.json
node scripts/install-dsh-hook.mjs --print      # show the entry without writing

It preserves every existing hook and is idempotent. To remove it again, delete the UserPromptSubmit entry from ~/.dsh/hooks/hooks.json and the copied editor_context.py; DSH reads that file once at process start, so either direction needs a restart.

A request is served before the ambient reading by every consumer that reads the state directory, and exactly once: windows/ is where the caret is, requests/ is where you pointed.

Install

From the Marketplace

pandarayc.editor-context-bridge

code --install-extension pandarayc.editor-context-bridge (or cursor in place of code), or search for Editor Context in the Extensions panel.

Remote-SSH — Cursor runs on another machine

A remote window has no cursor CLI: that lives on the client machine. So the extension has to be placed in the server-side extension directory, and the script that does it ships in this repository rather than in the VSIX — the machine that needs it is precisely the one that cannot run cursor.

node scripts/install-cursor-remote.mjs --vsix ./editor-context-bridge-<version>.vsix

It unpacks the VSIX into ~/.cursor-server/extensions/<publisher>.editor-context-bridge-<version>/ and adds the entry to extensions/extensions.json, backing that file up first and replacing an existing entry rather than appending a duplicate. It probes ~/.cursor-server and falls back to ~/.vscode-server; --server <dir> is explicit. No restart is needed — the extension host picks it up on its own.

A window on this machine

cursor --install-extension editor-context-bridge-<version>.vsix
# or: Command Palette → Extensions: Install from VSIX…

VS Code is the same API: code --install-extension.

Development

npm install --cache ../.npm-cache
npm run compile
# open this folder in Cursor and press F5 for an Extension Development Host

Then open a file, select a few lines, and:

cat ~/.editor-context/windows/*.json

Settings

Key Default Meaning
editorContext.enabled true Turning it off deletes this window's state file
editorContext.excludeGlobs .env, .env.*, *.pem, *.key, id_rsa*, id_ed25519*, .ssh/**, node_modules/** A match publishes nothing at all, not even the path. A pattern containing / matches the workspace-relative path; a bare one matches the file name
editorContext.language zh Language of the context line: zh / en
editorContext.sendText true Include the selected text in deliveries. Never affects the ambient file
editorContext.maxTextBytes 20000 Byte budget for that text; 0 disables the cap
editorContext.ws.enabled true Run the WebSocket publisher
editorContext.ws.host 127.0.0.1 Bind address. Non-loopback is refused without a token
editorContext.ws.port 7788 Preferred port. If it is taken, the window binds an ephemeral one and announces it in endpoints/<pid>.json; 0 always asks for an ephemeral port
editorContext.ws.path /editor-context Endpoint path
editorContext.ws.token empty Shared secret required in the subscribe frame
editorContext.stateDir empty → ~/.editor-context Move the directory
editorContext.debounceMs 150 Quiet period before a caret move is written

Five commands are registered: Add to Bot and Add to Bot (choose recipient)… (above), Show WebSocket Address (the URL and the subscriber count), Show State File (opens the file this window is writing), and Copy Current Position as Context (puts the context line on the clipboard).

editorContext.sendToAgent is still registered as an alias of Add to Bot, so a keybinding written against 0.2.0 keeps working.

Limitations

  • Whole ranges are not highlighted, only addressed. Jumping the editor to a range would need an inbound channel, and the extension has none.
  • With several windows open the most recently updated one wins. There is no cross-process notion of the focused window.

The hook has its own caveats — the change cache may have to live inside your project, and an unchanged position is never re-injected — which is part of why it is not the recommended path; see The hook.

Under the hood

src/core.ts is pure logic — position model, glob exclusion, rendering, the delivery model — with no vscode import, so it unit-tests under plain Node. src/state.ts is the on-disk layout: window files, the public mailbox, the subscriber registry and its inboxes. src/server.ts is the WebSocket publisher; src/extension.ts is the event wiring and the fan-out. hooks/editor_context.py is a zero-dependency Python 3.8+ UserPromptSubmit hook.

npm run compile && npm test    # 63 passed

For why the rendering is one terse line, why context: '' means two different things, and the four contracts the hook must not break: docs/internals.md. The original survey and design record is docs/design.md (Chinese).

License

MIT

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft