ADR-0004: A VS Code extension as a thin client of the portcheck CLI¶
- Status: Accepted
- Date: 2026-10-20
Context¶
Port problems show up while developers are in their editor: a dev server fails with
EADDRINUSE: address already in use :::3000, and fixing it means leaving the editor
for a terminal, finding the process, and deciding whether it's safe to stop.
Existing options:
- VS Code's built-in Ports view manages port forwarding (SSH, Codespaces, dev containers). It doesn't say which local process holds a port or help free it.
- Marketplace extensions such as Port Monitor show port status and kill processes, but don't explain who holds a port, have no safety guards, and don't understand containers or WSL.
- A browser extension could only see the symptom ("localhost refused to connect") and can reach a local program only through native messaging, which needs a separate per-browser manifest install (and registry entries on Windows).
portcheck already has the hard parts: process attribution, parent chains, containers,
WSL, safe kill, free-port finding, .portcheck.yaml, and a versioned JSON output.
Decision¶
- Build a VS Code extension first. A browser extension is an optional later phase.
- Keep the extension a thin client. It runs the
portcheckCLI with--json(schema v1) and never reimplements port logic. All behaviour, including safety guards, stays in Go and is tested once. - Bundle the binary. Publish one VSIX per platform (
darwin-arm64,darwin-x64,linux-x64,linux-arm64,win32-x64,win32-arm64) containing that platform'sportcheckfrom the same release. The settingportcheck.pathand thePATHare fallbacks. - Run where the code runs.
extensionKind: ["workspace"], so in Remote-SSH, WSL and Dev Containers the extension and itsportcheckrun on the remote host and see its ports. - The USP is "error to fix in one click". "Port in use" errors printed in the
integrated terminal become links that open a fix: stop the holder (with the CLI's
guards and confirmation), run on the next free port, or the right
docker stop/wslcommand. - Same repository and CI. The extension lives in
extensions/vscode/, uses strict TypeScript, bundles with esbuild, and is tested with Vitest (pure logic) and@vscode/test-electron(the real extension inside VS Code). It ships with every portcheck release, to the VS Code Marketplace and Open VSX (for VSCodium, Cursor and Windsurf).
Consequences¶
- ✅ One implementation of port logic, so the extension can't drift from the CLI.
- ✅ Works the same locally and in remote setups.
- ✅ Every new CLI capability (e.g. a new container runtime) reaches the extension for free.
- ⚠️ Each refresh starts a
portcheckprocess. On macOS that's now cheap (ADR-0003), and the view only refreshes while it's visible. - ⚠️ The extension depends on the JSON schema. That's why the schema is versioned:
schemaVersionis checked, and a mismatch is reported instead of misread. - ⚠️ Marketplace and Open VSX publishing need publisher accounts that only the maintainer can create.