[BUG] VS Code extension never draws mod UI (Pane, AbovePrompt, status, toast) although the engine accepts the vscode surface
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
In the VS Code extension (2.1.287), a mod's hooks, tools and commands run, but nothing a mod draws ever appears: no Pane, no AbovePrompt band, no $.ui.status, no $.ui.toast. $.ui.open from a person-initiated command.run resolves { isPlaced: false }.
The bundled engine is ready for VS Code. Its ui_render handler accepts surface: "vscode" ("ui_render: surface must be "desktop", "mobile" or "vscode""), and the API typings declare a vscode element table and say Pane is "Raised on every surface". But the extension never sends these requests: extension.js and webview/index.js contain zero occurrences of ui_attach, ui_render or ui_press. So the webview never attaches as a render surface.
What Should Happen?
The VS Code chat panel attaches as the vscode surface and draws mod UI the way Desktop does: panes, the band above the prompt, status entries and toasts. If that isn't planned yet, the docs and the plugin-authoring skill should say VS Code doesn't draw mod UI, and $.ui.open should return a reason that says so.
Error Messages/Logs
No errors. $.ui.open({ id, title, focus: true }) resolves { isPlaced: false, ... }, and /questions-style commands fall back to their text output.
Steps to Reproduce
- In VS Code with the Claude Code extension 2.1.287, open a chat and load any mod with a pane. I used a hot-reloaded mod from ~/.claude/dev-mods/<session>/ and also .claude/skills/<name>. Minimal hooks module:
import type { Register } from 'claude-code'
export const register: Register = on => {
on('session.start', async ($, e, next) => {
await $.command.register({ name: 'hello-pane', description: 'Open a test pane' })
return next(e)
})
on('command.run', { command: 'hello-pane' }, async $ => {
const r = await $.ui.open({ id: 'hello', title: 'Hello', focus: true })
$.ui.status('hello status')
$.ui.toast('hello toast')
return { text: isPlaced=${r.isPlaced} }
})
on('ui.render', { component: 'Pane', requestId: 'hello' }, ($, e) => {
const { Text } = $.ui.resolve(e)
return <Text>hello from a pane</Text>
})
}
- Run /hello-pane. It prints isPlaced=false; no pane, status or toast appears.
- Run the same mod with claude in a terminal: the pane, status and toast all draw.
- grep -c ui_render ~/.vscode/extensions/anthropic.claude-code-2.1.287-darwin-arm64/{extension.js,webview/index.js} gives 0 for both files; the bundled resources/native-binary/claude contains the handler.
Claude Model
Not sure / Multiple models
Is this a regression?
No, this never worked
Last Working Version
_No response_
Claude Code Version
Claude Code Version: 2.1.287 (VS Code extension; marketplace latest as of 2026-10-02)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Other
Additional Information
- claude plugin test with $.ui.mount({ surface: 'vscode', ... }) draws and validates these trees, so the gap is only the extension's side of the connection.
- Mods work in the VS Code chat panel as far as hooks go: tool.call, $.tool.register, $.command.register, $.prompt.submit and $.ui.ask all work.
- Related: #91870.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗