Building + publishing a VS Code extension
A VS Code extension is a TypeScript module on the Extension API (package.json manifest with contributes, activationEvents, main). Source in integrations/<name>/. Publish to two marketplaces from one .vsix: the Visual Studio Marketplace (vsce) and Open VSX (ovsx — what Cursor / Windsurf / VSCodium / Gitpod install from). Command playbook: pooriaarab/scripts scripts/vscode-extension/README.md.
The trap that segfaults packaging: Node version
vsce package / vsce publish / ovsx publish run an esbuild-based bundler that segfaults on Node 22 and 25 (exit 139, no useful error). Use Node 20 LTS (/opt/homebrew/opt/node@20/bin on PATH, or nvm use 20). Same class of failure as several other CLIs — if packaging dies silently, check Node first.
The other traps
- vsce rejects SVG in README.md.
vsce packageerrors "SVGs are restricted in README.md" if the README embeds any.svgimage. Use PNG, or remove the image. It also warns on a missingrepository/license— add both topackage.jsonto keep the listing clean. package.jsonneedspublisher. Thepublisherfield = the Marketplace publisher id / the Open VSX namespace. Set it before packaging.- The VS Code Marketplace publisher form fights automation. Creating a publisher at
marketplace.visualstudio.com/manage/createpublisheris a React form whose Create button doesn't reliably submit via browser automation (coordinate scaling + trusted-event issues), and it needs a Microsoft-account login + an Azure DevOps PAT (scope: Marketplace → Manage) forvsce publish -p <PAT>. Create the publisher + PAT by hand — it's a 5-minute human step, not worth automating. - Open VSX is the easy path. GitHub login at
open-vsx.org, sign the one-time Eclipse Publisher Agreement, mint an access token — then it's fully headless.
Build + package
export PATH="/opt/homebrew/opt/node@20/bin:$PATH" # Node 20
npm install && npm run compile
npx @vscode/vsce package # -> <name>-<version>.vsix
Publish
Open VSX (headless, do this one first):
npx ovsx create-namespace <publisher> -p <OVSX_TOKEN> # once
npx ovsx publish <name>-<version>.vsix -p <OVSX_TOKEN> # -> live for Cursor/Windsurf/VSCodium/Gitpod
VS Code Marketplace (after the human publisher + PAT exist):
npx @vscode/vsce publish -p <AZURE_DEVOPS_PAT>
Store the tokens in the product .env.local (OVSX_TOKEN, VSCE_PAT), never commit.
Related skills
raycast-extension— the other dev-tool command surface (needs store screenshots, unlike this).marketplace-app-hosting— extensions don't need hosting; they're published bundles.