adom-vscode
Public Made by Adomby adom
Let Claude, Codex and Kimi drive VS Code inside your Adom container
Install?
CLI + VS Code extension that lets AI assistants and the Hydrogen Desktop frontend drive VS Code from inside an Adom container: open files, live get/set of any setting/theme/font (no reload), native streamed exec, a frontend event queue, container identity (hd/hw), api-key status/inject, extension query/update, modes, paste screenshots, and any VS Code command via a local HTTP API on port 8821 (with port-file fallback discovery).
adom-wiki pkg install adom/adom-vscode
Latest: v1.1.25, published
Dependencies 1 skill
- skill adom/adom-theme ^2.0.0
Contents
README
markdownadom-vscode
A CLI + VS Code extension that lets AI assistants and the Hydrogen Desktop / Hydrogen Web frontend drive VS Code from inside an Adom Docker container: open files, change settings live, run container commands, pass events to the frontend, switch modes, paste screenshots, and run any VS Code command via a local HTTP API on port 8821.
The Adom panel, your AI's cockpit inside VS Code
Click the Adom icon in the activity bar to open a side panel with the Overview, Modes, Commands, AI Prompts, Settings & Theme, Exec & Queue, Container & Keys, Extensions, and the live Claude Context viewer, every active Claude Code session in the container with token cost and last activity at a glance.

The Extension: About page
Installed in every Adom container. Open the Extensions sidebar and search for adom-vscode: the details pane renders the full quick-start guide, mode reference, and version metadata.

What it does
- Open files in VS Code tabs (images, text, anything)
- Reveal files/folders in the Explorer sidebar
- Preview Markdown files in rendered view
- Settings, live: get/set any VS Code setting, theme, fonts, font sizes. Changes apply immediately via the native VS Code API (no reload, active AI sessions survive)
- Exec (HD-local only): run shell commands in the container as the workspace user, streamed (SSE) or buffered, no host-side
wsl.exeneeded. Disabled on cloud (hw) containers by security policy - Event queue: any container process pushes events; the HD/HW frontend pulls them when it wants (login-refresh needed, long-job progress, anything)
- Container identity: query whether this is an
hd(local Hydrogen Desktop) orhw(Adom cloud) container, OS, kernel, key env vars - API key: check the health of
/var/run/adom/api-keyand inject a fresh key after a frontend login refresh - Extensions: list installed extensions (with Claude/Codex/Kimi summary), query version + running state + pending update, update one or all, search, install
- Start new Claude Code conversations
- Modes: switch VS Code into different full-screen experiences:
claudecode: Claude Code panel UI (graphical AI chat)claudetty: full-screen terminal running the claude CLItty: generic full-screen terminal with optional auto-command
- Control layout: sidebar, panel, terminal, activity bar (show/hide/toggle)
- Run any VS Code command (generic escape hatch)
- Paste image into terminal: Ctrl+V an image while focused in a code-server terminal uploads to
/tmp/shots/and pastes the file path (so the CLI Claude Code can ingest it)
Already installed
Adom VS Code ships with every Adom container, you have it on day one. Try it now: ask Claude in any terminal:
"open
ne555-timer.kicad_sch" · "set my editor font size to 14" · "switch to Claude Code mode" · "is my api key still good?"
To upgrade after a new release lands on the wiki, ask Claude:
"update adom-vscode"
Or run it yourself: adom-wiki pkg update adom-vscode.
Port discovery
The extension serves its HTTP API on 127.0.0.1:8821. If 8821 is already taken it walks 8822..8831, then takes an ephemeral port, and writes the truth to:
~/.local/share/adom-vscode/port.json # { "port": N, "preferredPort": 8821, "pid": ..., "version": ..., "startedAt": ... }
External services that expect the well-known port should read that file when 8821 does not answer. The CLI already does (resolution order: ADOM_VSCODE_PORT env var, then port.json, then 8821). GET /health reports both port (actual) and preferredPort.
HTTP API (the verb surface)
Everything the CLI does is a plain HTTP call, so HD, HW, and any script can hit the API directly. GET /health returns {version, port, preferredPort, portFile, verbs: [...]} so callers can feature-detect instead of gating on versions.
| Verb | Method + route | What it does |
|---|---|---|
| health | GET /health |
Liveness, version, actual port, full verb roster |
| open | POST /open {path} |
Open a file in a tab |
| reveal | POST /reveal {path} |
Reveal in Explorer sidebar |
| preview-markdown | POST /preview-markdown {path} |
Rendered Markdown preview |
| config.get | GET /config?key=<dotted.key> |
Effective value + default/global/workspace layers |
| config.set | POST /config {key, value} |
Set in User (Global) settings, applies live, no reload |
| theme.get | GET /theme |
Active theme, preferred dark/light, all installed themes |
| theme.set | POST /theme {theme} |
Switch color theme live |
| fonts.get | GET /fonts |
All font settings (editor, terminal, chat prose chat.fontFamily, chat code chat.editor.fontFamily) plus the RESOLVED families the renderer actually shows (via document.fonts.check) |
| fonts.set | POST /fonts {editorFamily?, proseFamily?, proseCodeFamily?, editorSize?, terminalSize?, proseSize?, proseCodeSize?} |
Set any subset live |
| exec | POST /exec {command, cwd?, timeoutSec?} |
Buffered: run bash -lc as the workspace user, return {exitCode, stdout, stderr, timedOut}. HD-local only; cloud (hw) refuses with exec_disabled_on_cloud |
| exec.stream | POST /exec/stream {command, cwd?, timeoutSec?} |
Streaming: SSE events start, stdout, stderr, exit {exitCode, timedOut}. HD-local only, same policy |
| queue.push | POST /queue/push {topic?, type?, payload?, ttlSec?} |
Push an event for the frontend; returns {id, depth} |
| queue.pull | GET/POST /queue/pull {topic?, max?, peek?} |
Pull (consume) events; peek reads without consuming |
| queue.status | GET /queue/status |
Depth + per-topic counts |
| queue.clear | POST /queue/clear {topic?} |
Drop all events, or one topic |
| container.info | GET /container |
kind: hd|hw|unknown, cloud flag, OS, kernel, hostname, carbon URL, VS Code proxy URI, detection markers |
| apikey.status | GET /apikey |
/var/run/adom/api-key: exists, readable, size, age, sha256 fingerprint |
| apikey.set | POST /apikey {value} |
Write a fresh key (root-owned path, goes through passwordless sudo) |
| extensions.list | GET /extensions (?all=1 incl. builtins, ?id=x&latest=1 for one + gallery check) |
Installed extensions + ai summary for claude/codex/kimi |
| extensions.update | POST /extensions/update {id?} |
Update one extension, or all when id omitted |
| extensions.search | POST /extensions/search {query} |
Open Extensions sidebar with a query |
| extensions.install | POST /extensions/install {id} |
Install by marketplace id |
| terminal.tui | POST /terminal/tui {name, command?, shellPath?, shellArgs?, env?, cwd?, viewColumn?, icon?, color?} |
Open a NAMED terminal as an EDITOR TAB running a command (AI CLIs for the Hydrogen AI Threads panel); returns a handle {id}. icon is a codicon name, color a theme color id (terminal.ansi*), so per-AI tabs are distinguishable. Never touches terminal.integrated.defaultLocation. Capability marker terminal.tui.icon in the /health roster |
| terminal.tui.list | GET /terminal/tui/list |
Handles: id, name, command, running state |
| terminal.tui.dispose | POST /terminal/tui/dispose {id?, name?} |
Close a named TUI terminal by handle |
| claude.new | POST /claude/new |
New Claude Code conversation |
| tabs.list | GET /tabs |
Event-driven tab census from the extension host (window.tabGroups): every tab with kind (claude | codex | kimi | terminal | other), label, group/tab index, active flags; Codex rows carry sessionId from the editor URI. No DOM, no CDP |
| tabs.events | GET /tabs/events (SSE) |
The same census pushed on every tab open/close/move/focus change (event: census), plus event: claude-session when the SessionStart hook reports a new Claude session. Hydrogen holds one stream per workspace instead of polling the iframes |
| claude.session-start | POST /claude/session-start {session_id, transcript_path?, cwd?, source?} |
Ingest for a Claude SessionStart hook: records the session (last 100) and broadcasts it on /tabs/events |
| claude.sessions | GET /claude/sessions |
The recorded Claude sessions (id, transcript path, cwd, source, time) |
| claude.open-session | POST /claude/open-session {sessionId, prompt?, retagEntrypoint?} |
Open an EXISTING Claude conversation as an editor tab and CONFIRM it bound (bound:true once a CLI resumes that id), instead of the fire-and-forget claude-vscode.editor.open. Rewrites a programmatic transcript entrypoint (sdk-cli/sdk-ts/sdk-py -> claude-vscode) first, because the Claude extension silently declines to restore those and opens a fresh tab |
| claude.context | GET /claude/context |
Live AI session stats (context %, cache, tools, compactions) |
| command | POST /command {command, args?} |
Any VS Code command (escape hatch) |
| notify | POST /notify {message, level?} |
Toast notification |
| mode.* | POST /mode/claudecode|claudetty|tty|reset |
Full-screen mode switching |
The event queue survives window reloads (persisted to ~/.local/share/adom-vscode/queue.json, capped at 1000 items, optional per-item TTL).
Why the settings verbs matter
POST /config uses vscode.workspace.getConfiguration().update(key, value, ConfigurationTarget.Global), which applies immediately with no window reload. That fixes two long-standing frontend bugs at once: font-size changes that silently never landed when a host-side settings.json merge hung, and theme changes that needed a full workbench reload (which dropped the user's Claude sessions).
For fonts, GET /fonts reports the resolved family the renderer actually shows (checked with document.fonts.check in the workbench), not just the configured stack. The configured chain often names a font that is not installed on the rendering side; the frontend needs the true value for its readout.
CLI quick start
adom-vscode health # Liveness + version + verb roster
adom-vscode open /path/to/file.png # Open file in VS Code tab
adom-vscode reveal /path/to/folder/ # Reveal in Explorer sidebar
adom-vscode preview /path/to/README.md # Markdown preview
# Settings (all apply live, no reload)
adom-vscode config get workbench.colorTheme
adom-vscode config set editor.fontSize 14
adom-vscode theme get
adom-vscode theme set "Adom Dark"
adom-vscode font get
adom-vscode font set --editor-size 14 --prose-family Inter
adom-vscode font-size get
adom-vscode font-size set --editor 14 --terminal 13 --prose 15
# Exec (streams live by default; --json for buffered)
adom-vscode exec "ls -la" --cwd /home/adom/project
adom-vscode exec "npm test" --timeout 600
adom-vscode exec "uname -a" --json
# Event queue (container process -> HD/HW frontend)
adom-vscode queue push login --type refresh-needed --payload '{"expiresInHours":24}'
adom-vscode queue pull --topic login
adom-vscode queue pull --peek
adom-vscode queue status
adom-vscode queue clear --topic progress
# Container identity + API key
adom-vscode container
adom-vscode apikey status
adom-vscode apikey set --stdin < new-key.txt
# Extensions
adom-vscode extensions list
adom-vscode extensions status anthropic.claude-code --latest
adom-vscode extensions update anthropic.claude-code
adom-vscode extensions update
adom-vscode extensions search "python"
adom-vscode extensions install ms-python.python
# Layout, modes, misc
adom-vscode claude new
adom-vscode sidebar toggle
adom-vscode panel toggle
adom-vscode terminal new
adom-vscode activity-bar toggle
adom-vscode notify "Build complete"
adom-vscode reload # Reload VS Code window (drops AI sessions!)
adom-vscode command <any-vscode-command>
adom-vscode --help
Modes
Modes take over VS Code's full screen for a focused experience. Use mode reset to return to normal.
adom-vscode mode claudecode # Claude Code panel UI
adom-vscode mode claudetty # Full-screen terminal + claude CLI
adom-vscode mode tty # Blank full-screen terminal
adom-vscode mode tty -c "echo hello" -n "Demo" # TTY with auto-command
adom-vscode mode reset # Exit any mode
URL auto-trigger
Add ?mode=X to any VS Code URL to auto-enter a mode on page load. Perfect for sharing onboarding links with new users.
| URL param | Mode |
|---|---|
?mode=claudecode |
Claude Code panel UI |
?mode=claudetty |
Full-screen terminal + claude CLI |
?mode=tty |
Blank full-screen terminal |
?mode=tty&cmd=echo%20hello |
TTY with auto-command |
Example: https://<slug>.adom.cloud/?mode=claudecode&folder=/home/adom/project
Paste-screenshot interceptor
When you press Ctrl+V with an image on the clipboard while focused in a code-server terminal (e.g. running CLI Claude Code), the interceptor uploads the image to the extension at POST /paste-screenshot, which saves it to /tmp/shots/ (deduplicated via SHA-256) and writes the file path back to the terminal so the CLI can ingest it. Without this, code-server returns an empty string for clipboard.readText() when the clipboard holds only an image, so the paste does nothing.
The skill pack
The page ships a parent skill plus six sub-skills so any AI in the Adom
ecosystem can discover and drive every verb. adom-wiki pkg install (and
adom-vscode install) deploys all of them to ~/.claude/skills/:
| Skill | Covers |
|---|---|
adom-vscode (parent) |
Mental model, file ops, workspace scoping, port discovery, management |
adom-vscode-settings |
Live config/theme/font get+set, resolved font families |
adom-vscode-exec |
Streamed + buffered container exec, timeouts, the no-wsl.exe rationale |
adom-vscode-queue |
The container-to-frontend event queue, topics/TTL/peek, patterns |
adom-vscode-container |
hd/hw identity, api-key status/inject, port discovery, /health feature detection |
adom-vscode-extensions |
Extension list/status/update, the Claude/Codex/Kimi AI summary |
adom-vscode-modes |
Full-screen modes, layout verbs, URL auto-triggers, paste-screenshot |
Architecture
Claude Code / scripts / HD frontend
↓ runs CLI or hits HTTP directly
adom-vscode open /foo/bar.png
↓ HTTP POST to localhost:8821 (see Port discovery)
Adom VS Code Extension (inside code-server)
↓ vscode.* API / child_process
VS Code UI + container shell
See ARCHITECTURE.md for the full design document.
See COMMANDS.md for all VS Code commands usable with adom-vscode command <id>.
Version
One version number everywhere: the VERSION file, the extension, the CLI --version, and the wiki package all carry the same value (1.1.10 as of this release).
v1.1.10 added: live config/theme/font verbs, streamed exec, the frontend event queue, container identity (hd/hw), api-key status/inject, extension query/update (incl. Claude/Codex/Kimi), port fallback + port.json discovery, and verb-roster feature detection on /health.
v1.1.11 added: the skill pack (parent + 6 sub-skills) installed by both the wiki package and adom-vscode install.
v1.1.12: exec verbs are HD-local only (disabled on cloud hw containers with errorCode exec_disabled_on_cloud); /health reports execAllowed and omits exec verbs where disabled.
v1.1.13: extension-dir GC on every install (prunes stale version dirs VS Code's startup GC never reaps on long-lived windows; keeps current, live-process, and running-server versions), and install now pushes a system/update-pending queue event so the frontend can reload at a safe moment.
v1.1.15: version realignment (1.1.14 shipped a dependency-only bump with a 1.1.13 binary; now tarball, binary, VERSION and page all match) and the issue #642 fix: extensions --latest is platform-scoped via the VS Code-compat gallery, returns the allPlatforms matrix with an explanatory _hint, accepts --platform, and extensions update says 'already current' instead of firing a no-op install. \nv1.1.16: extensions install --vsix <path|url> installs a VSIX directly via code-server's CLI with the extension-host env stripped (absorbs the CODE_SERVER_PARENT_PID / not-spawned-with-IPC trap from issue #642).
v1.1.17: named TUI terminals as editor tabs with real handles (POST /terminal/tui, /terminal/tui/dispose, GET /terminal/tui/list; CLI terminal tui / tui-list / tui-dispose), built for the Hydrogen AI Threads panel; /command now passes a non-array args value as a single argument instead of silently dropping it, and reports argsShape.
v1.1.18: terminal.tui accepts optional icon (codicon name -> ThemeIcon) and color (theme color id -> ThemeColor) so AI TUI tabs match the AI Threads panel identities; tui-list echoes them; /health roster gains the terminal.tui.icon capability marker.
v1.1.19: terminal.tui accepts shellPath/shellArgs/env (createTerminal semantics): the CLI runs AS the terminal process, no shell underneath, no echoed launch line pinned atop the TUI, no quoting constraints, and the tab closes when the process exits. shellPath wins over command; a missing or non-executable shellPath fails explicitly (errorCode shell_path_not_executable) instead of falling back to a bare prompt. Capability marker terminal.tui.shell in the /health roster.
v1.1.20: /command returns errorCode command_not_found (HTTP 400) with did-you-mean suggestions when a command id is unknown (args-taking commands often use the bare id, e.g. moveActiveEditor); terminal.tui injects ADOM_VSCODE_TUI_ID into the terminal env and tui-list reports processId, giving callers a DOM-free identity chain (pid -> /proc environ -> registry id).
v1.1.21: event-driven tab census + Claude session identity (John 2026-09-03: never poll the iframes again). GET /tabs and the SSE stream GET /tabs/events publish the extension host's own tabGroups census on every change; POST /claude/session-start ingests the Claude SessionStart hook (id + transcript path, before the jsonl is flushed) and GET /claude/sessions lists it; POST /claude/open-session opens an existing conversation by id and confirms the bind (retagging sdk-cli transcripts, which the Claude extension otherwise declines). Capability markers in /health: tabs.list, tabs.events, claude.open-session, claude.session-start, claude.sessions.
v1.1.22: /command maps a JSON null argument to undefined when spreading an args array (JSON has no undefined; the Claude extension's editor.open crashes on a literal null session id, which broke every send-to-AI new tab).
v1.1.23: the paste-screenshot interceptor is retired (the Claude CLI handles image paste itself). It was the only thing this tool wrote into code-server's workbench.html, and that write was the only reason it ever needed sudo, so installs are now entirely user-level and a code-server upgrade can no longer wipe a patch of ours. An already-injected block is stripped on the next install. The /paste-screenshot endpoint goes with it.