npx skills add ...
npx skills add microsoft/vscode --skill launch
npx skills add microsoft/vscode --skill launch
Launch and automate VS Code Insiders with the Copilot Chat extension using @playwright/cli via Chrome DevTools Protocol. Use when you need to interact with the VS Code UI, automate the chat panel, test the extension UI, or take screenshots. Triggers include 'automate VS Code', 'interact with chat', 'test the UI', 'take a screenshot', 'launch with debugging'.
Automate VS Code Insiders with the Copilot Chat extension using @playwright/cli. VS Code is built on Electron/Chromium and exposes a Chrome DevTools Protocol (CDP) port that @playwright/cli can attach to, enabling the same snapshot-interact workflow used for web pages.
@playwright/cli is available via devDependencies. Run npm install at the repo root, then use npx @playwright/cli to invoke commands. Alternatively, install globally with npm install -g @playwright/cli.code-insiders is required. This extension uses 58 proposed VS Code APIs and targets vscode ^1.110.0-20260223. VS Code Stable will not activate it — you must use VS Code Insiders.npm run compile for a one-shot build, or npm run watch for iterative development..interactive-input-part, .monaco-editor, and .view-line are VS Code internals that may change across versions. If automation breaks after a VS Code update, re-snapshot and check for selector changes.📸 Take screenshots for a paper trail. Use
npx @playwright/cli screenshot --filename=<path>at key moments — after launch, before/after interactions, and when something goes wrong. Screenshots provide visual proof of what the UI looked like and are invaluable for debugging failures or documenting what was accomplished.Save screenshots inside
.vscode-ext-debug/screenshots/(gitignored) using a timestamped subfolder so each run is isolated and nothing gets overwritten:
After attach, all subsequent commands target the connected app without needing to reattach.
VS Code uses multiple webviews internally. Use tab commands to list and switch between them:
To debug a VS Code extension via npx @playwright/cli, launch VS Code Insiders with --extensionDevelopmentPath pointing to your extension source and --remote-debugging-port for CDP. Use --user-data-dir to avoid conflicting with an already-running VS Code instance.
Key flags:
--extensionDevelopmentPath=<path> — loads your extension from source (must be compiled first). Use $PWD when running from the repo root.--remote-debugging-port=9223 — enables CDP (use 9223 to avoid conflicts with other apps on 9222)--user-data-dir=<path> — uses a separate profile so it starts a new process instead of sending to an existing VS Code instance. Always use a persistent path (e.g., $PWD/.vscode-ext-debug) rather than /tmp/... so authentication, settings, and extension state survive across sessions.Without --user-data-dir, VS Code detects the running instance, forwards the args to it, and exits immediately — you'll see "Sent env to running instance. Terminating..." and CDP never starts.
⚠️ Authentication is required. The Copilot Chat extension needs an authenticated GitHub session to function. Using a temp directory (e.g.,
/tmp/...) creates a fresh profile with no auth — the agent will hit a "Sign in to use Copilot" wall and model resolution will fail with "Language model unavailable."Always use a persistent
--user-data-dirlike$PWD/.vscode-ext-debug. On first use, launch once and sign in manually. Subsequent launches will reuse the auth session.
After making changes to the extension source code, you must restart VS Code to pick up the new build. The extension host loads the compiled bundle at startup — changes are not hot-reloaded.
Tip: If you're iterating frequently, run
npm run watchin a separate terminal so compilation happens automatically. You still need to kill and relaunch VS Code to load the new bundle.
VS Code uses Monaco Editor for all text inputs including the Copilot Chat input. Monaco editors appear as textboxes in the accessibility snapshot but require specific npx @playwright/cli commands to interact with.
fill <ref> — The Best ApproachThe fill command with a snapshot ref handles focus and input in one step:
This is the simplest and most reliable method. It works for both the main editor chat input and the sidebar chat panel.
Tip: If
fillsilently drops text (the editor stays empty), the ref may be stale or the editor not yet ready. Re-snapshot to get a fresh ref and try again. You can verify text was entered using the snippet in "Verifying Text in Monaco" below.
type — After FocusIf focus is already on a Monaco editor, type works:
press — Individual KeystrokesAlways works when focus is on a Monaco editor. Useful for special keys, keyboard shortcuts, and as a universal fallback for typing text character by character:
| Method | Result | Reason |
|---|---|---|
click <ref> on editor | "Element blocked by another element" | Monaco overlays a transparent div over the textarea |
Setting textarea.value + dispatching input event via eval | No effect | Monaco doesn't read from the textarea's value property |
If fill doesn't work (e.g., ref is stale), you can focus the editor via JavaScript:
After JS mouse events, document.activeElement becomes a DIV with class native-edit-context — this is VS Code's native text editing surface.
Monaco renders text in .view-line elements, not the textarea:
--remote-debugging-port=9223lsof -i :9223netstat -ano | findstr 9223npx @playwright/cli tab-list to list targets and switch to the right one with npx @playwright/cli tab-select <index>click doesn't work on Monaco editors — see the "Interacting with Monaco Editor" section above for the full compatibility matrixfill <ref> is the best approach; individual press commands work everywhere; type works after focus is establishedIf npx @playwright/cli screenshot returns "Permission denied", your terminal needs Screen Recording permission. Grant it in System Settings → Privacy & Security → Screen Recording. As a fallback, use the eval verification snippet to confirm text was entered — this doesn't require screen permissions.
Always kill the debug VS Code instance when you're done. Leaving it running wastes resources and holds the CDP port.