launch
Skill44 starsChanged 16 days agoMITView on GitHub
Launch Code OSS (VS Code from sources) into an isolated throwaway profile with unique debug ports so you can drive it with @playwright/cli AND attach a Node debugger via dap-cli in the same session. Use when working on VS Code itself and you want to interact with the running workbench, automate chat or UI flows, test UI features, take screenshots, set breakpoints in the renderer / extension host / main process, or combine UI driving with debugging.
- Reads credentials
- Deletes or force-pushes
66 other projects use a version of this, 3 of them edited.
Adopted by
Most recently taken up by
- BermanRoman/promt-seo-geotheir own version, first seen Nov 8, 2026
- ADWilkinson/astacktheir own version, first seen Nov 8, 2026
- Yeachan-Heo/oh-my-claudecodetheir own version, first seen Nov 8, 2026
- lalulali/vespyrtheir own version, first seen Nov 6, 2026
- microsoft/vscodeedited, first seen Nov 2, 2026
- pixelswithbliss/claude-skillstheir own version, first seen Nov 1, 2026
Used with
Projects with it also use add-policy, tool-rename-deprecation and 10 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
OpenCortexIDESkilladd-policy
Use when adding, modifying, or reviewing VS Code configuration policies. Covers the full policy lifecycle from registration to export to platform-specific artifacts. Run on ANY change that adds a `policy:` field to a configuration property.
Also in 4 projects that use this
gitpod-ioSkilltool-rename-deprecation
Ensure renamed built-in tool references preserve backward compatibility. Use when renaming a toolReferenceName, tool set referenceName, or any tool identifier. Run on ANY change to tool registration code. Covers legacyToolReferenceFullNames for tools and legacyFullNames for tool sets.
Also in 4 projects that use this
CesarjoquinSkillcompetitors
When the user wants to create competitor comparison or alternative pages for SEO and sales enablement. Also use when the user mentions 'alternative page,' 'vs page,' 'competitor comparison,' 'comparison page,' '[Product] vs [Product],' '[Product] alternative,' 'competitive landing pages,' 'how do we compare to X,' 'battle card,' or 'competitor teardown.' Use this for any content that positions your product against competitors. Covers four formats: singular alternative, plural alternatives, you vs competitor, and competitor vs competitor. For sales-specific competitor docs, see sales-enablement.
Also in 12 projects that use this
CesarjoquinSkillpaywalls
When the user wants to create or optimize in-app paywalls, upgrade screens, upsell modals, or feature gates. Also use when the user mentions "paywall," "upgrade screen," "upgrade modal," "upsell," "feature gate," "convert free to paid," "freemium conversion," "trial expiration screen," "limit reached screen," "plan upgrade prompt," "in-app pricing," "free users won't upgrade," "trial to paid conversion," or "how do I get users to pay." Use this for any in-product moment where you're asking users to upgrade. Distinct from public pricing pages (see cro) — this focuses on in-product upgrade moments where the user has already experienced value. For pricing decisions, see pricing.
Also in 9 projects that use this
riggyzSkillattribution
Attribution rules for issues, pull requests, work items, reviews, and comments created or edited by the agent in GitHub, Linear, Azure DevOps, or similar collaboration systems. Use when creating, commenting on, reviewing, or modifying external tracker content.
Also in 6 projects that use this
coreyhaines31Skillconversion-tracking
Set up, audit, verify, or fix website conversion tracking. Use when the conversion is a form submission, booked meeting, chat, phone call, signup, or purchase; when conversions must reach Google Ads, Meta, GA4, LinkedIn, TikTok, or Microsoft Ads; when the work involves GTM tags and dataLayer events, pixels, the Conversions API (CAPI), Enhanced Conversions, server-side tagging, or gclid and fbclid capture; or when the user reports conversion problems like "conversions stopped", "double counting", "spend but no conversions", "GA4 and Google Ads don't match", or an Unverified or Inactive status. Covers lead gen and ecommerce. Always fire on confirmed success, never the submit click; verify a test conversion arrives; diagnose before changing anything; offer free and native paths alongside paid ones and disclose partner tools. For tracking plans and UTMs, see analytics. For attribution models, see attribution. For campaigns and bidding, see ads. For CRM lead-stage design, see revops.
Also in 3 projects that use this
- +4 more from Cesarjoquin
Other versions66 projects use a version of this.
Word for word · 0
None found.
Edited · 3
- microsoft/vscode★ 195k96% same
- posit-dev/positron★ 4.3k80% same
- NeuralInverse/neuralinverse★ 22144% same
Wrote their own version · 63
- coreyhaines31/marketingskills★ 59ksame name
- Yeachan-Heo/oh-my-claude-sisyphus★ 41ksame name
- Yeachan-Heo/oh-my-claudecode★ 41ksame name
- Nexus-JPF/note-companion★ 874same name
- whitecircle/halo★ 851same name
- menkesu/awesome-pm-skills★ 441same name
- Cesarjoquin/Marketing-Skills★ 190same name
- OpenCortexIDE/cortexide★ 175same name
- simota/agent-skills★ 96same name
- open-fox/agents★ 55same name
53 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
The file
Show its rules and text
We split this skill into the rules it gives an agent
22 rules. 0 are common, 10 are distinctive, and 12 are very specific to this project.
0
Common
Most of the 60 edited versions we read keep these.
10
Distinctive
This author's own choices. Worth a look.
12
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 13
Run multiple instances at once without port conflicts.
- Very specific: line 23
Windows: use scripts\launch.ps1 instead.
- Very specific: line 79
If the local execution policy blocks scripts, invoke it with powershell -ExecutionPolicy Bypass -File <path-to-launch.ps1>.
- Distinctive: line 83
If the source profile is missing or has no stored GitHub session, do not stop and do not ask before launching.
- Distinctive: line 90
If the user has signed in, continue with the current isolated launch.
- Very specific: line 102
If the failed launch used a custom --source-user-data-dir, pass the same path as --user-data-dir <path> on macOS/Linux or -UserDataDir …
- Distinctive: line 106
After the user acknowledges, wait for the bootstrap process tree to release the persistent profile before launching again:
- Distinctive: line 116
Use the same profile path that bootstrap reported.
- Distinctive: line 118
Do not bootstrap without the user's explicit choice.
- Very specific: line 201
Use the dynamic cdpPort from the launch JSON.
- Very specific: line 203
If you are unsure about Playwright CLI syntax, run npx @playwright/cli --help or npx @playwright/cli <command> --help instead of guessing …
- Very specific: line 217
After attach, later @playwright/cli commands keep using the connected app until you close or reattach — as long as you keep passing the …
- Very specific: line 229
If a target looks stale after relaunching, run npx @playwright/cli -s=$PW_SESSION close, attach again with $CDP, and re-check tab-list.
- Distinctive: line 235
If the screenshot shows missing icons, blank content, or visibly broken layout, stop the scenario and investigate the observed problem …
- Very specific: line 239
Use the playwrightScripts/focus-chat-input.ts script in both the regular workbench and the Agents window.
- Distinctive: line 241
If a visible chat input is already focused, it does nothing.
- Distinctive: line 242
If a visible chat input exists but is not focused, it focuses that input.
- Distinctive: line 393
If the focus script cannot reach Chat because the surface is not available yet, take a snapshot and navigate the UI into a state where chat …
- Very specific: line 429
Every launch picks fresh ports and a fresh temp runDir, so you can run as many concurrent Code OSS windows as your machine can handle.
- Very specific: line 447
If you are iterating frequently, keep the repo build/watch task running separately so relaunches pick up already-generated output.
- Very specific: line 483
Snapshot shows the wrong page or no expected controls: - use tab-list, switch with tab-select <index> if needed, then re-snapshot before …
- Very specific: line 484
CLI typing commands complete but the input stays empty: - run playwrightScripts/focus-chat-input.ts, use press or clipboard paste rather …
“Common” is measured: kept by at least 6 in 10 of the 60 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Code OSS Dev - Launch + Debug
You're working on VS Code itself and you want to:
- Launch a Code OSS build from sources that is already signed in (Copilot, GitHub, etc.) so chat / agent flows work end-to-end.
- Drive it with
@playwright/cliover CDP (UI automation). - Optionally attach a debugger via dap-cli to set breakpoints in the renderer, extension host, or main process.
- Run multiple instances at once without port conflicts.
This skill provides a launcher that clones an authenticated user-data-dir to a throwaway temp folder, picks free ports for every debug surface, and prints them as JSON so you can pick them up programmatically.
The clone is slim: workspace storage, browser caches, file history, cached VSIX backups, and old logs are excluded by default. On macOS, auth tokens live in the OS keychain plus small files inside User/globalStorage - both of which are preserved. On Windows the GitHub session lives in the shared-data-dir instead, which the launcher seeds separately (see Windows authentication).
Prerequisites
- macOS, Linux, or Windows.
- macOS / Linux: the launcher is a bash script (
scripts/launch.sh) and depends onrsync,nohup, and Node onPATH. - Windows: use
scripts\launch.ps1instead. It needs no extra tooling beyond Node onPATH, and works on both Windows PowerShell 5.1 and PowerShell 7+. Parse launcher output withConvertFrom-Json. If Node is managed with fnm, put it onPATHfirst:fnm env --use-on-cd --shell powershell | Out-String | Invoke-Expression fnm use # picks up the repo's .nvmrc
- macOS / Linux: the launcher is a bash script (
- A VS Code checkout with
node_modules/installed (npm installif missing — do not symlink from a sibling worktree; that breaks builds in subtle ways). - A VS Code checkout with sources built. Run
npm run compileonce (one-shot) ornpm run watchfor incremental rebuilds. Both build the full client and all built-in extensions underextensions/. You must build the full product to run successfully, building just the client is not enough.- The launcher compiles only when outputs are missing; it does not detect stale dependencies or
out/files. After pulling, switching branches, or changing dependency manifests, runnpm installwhen needed and rebuild before launching.
- The launcher compiles only when outputs are missing; it does not detect stale dependencies or
- An authenticated Code OSS profile is recommended but not required. By default the launcher tries to seed from
~/.vscode-oss-devon macOS/Linux or$env:USERPROFILE\.vscode-oss-devon Windows, which is the user-data-dir the repo'slaunch.jsonconfigs use. Only pass--source-user-data-dir <path>(or set$CODE_OSS_DEV_AUTHED_USER_DATA_DIR) when you specifically want to seed from a different profile (e.g. your regular~/Library/Application Support/Codeinstall). If the source profile is missing, the launcher continues with an empty isolated profile. @playwright/cliavailable (it's a devDependency in the vscode repo -npm installthen usenpx @playwright/cli).- For debugger work:
dap-clionPATH. If debugger support would be useful but thedap-cliskill is not present, prompt the user to install it from https://github.com/roblourens/dap-cli. - CSS selectors are internal implementation details. If a selector-based
evalstops working, take a freshsnapshot, inspect the current DOM, and update the selector rather than assuming an old one still applies.
The launcher copies the source profile to a temp dir and never mutates the original. Each launch gets its own isolated
--user-data-dirand--extensions-dir.
The launcher always sets
files.simpleDialog.enable: truein the launched profile'sUser/settings.json. This is required for automation: VS Code's native OS file dialogs cannot be driven via@playwright/cliover CDP and are completely unreachable over SSH on headless macOS. The simple (quick-input) dialog can be navigated withpressand clipboard paste. The override is per-launch and only affects throwaway profiles.
Before launching from an agent session, call
get_current_sessionand pass itstitleas--session-title. For a regular editor window, the launcher writes that title into the throwaway profile'swindow.titlesetting. For an Agents window, it passes the title to the Command Center. This never modifies the source profile.
For unattended automation, pass
--disable-workspace-trustso a trust dialog cannot block the flow or extension-host startup. The override is process-scoped and does not modify the source profile. Only use it with content you trust.
Launch
The launcher script lives next to this SKILL.md at scripts/launch.sh (macOS/Linux) or scripts\launch.ps1 (Windows). Resolve it relative to wherever this skill file is installed - do not hardcode an absolute path.
# LAUNCH=<dir-of-this-SKILL.md>/scripts/launch.sh
SESSION_TITLE=<title-from-get_current_session>
"$LAUNCH" --session-title "$SESSION_TITLE" # default: workbench
"$LAUNCH" --agents --session-title "$SESSION_TITLE"
"$LAUNCH" -- <workspace-path> # forward extra args to code.sh
"$LAUNCH" --source-user-data-dir <path> # pick a specific authed profile
"$LAUNCH" --repo <vscode-repo-root> # if not run from the repo
"$LAUNCH" --clone-extensions # start with a copy of the source extensions/ (~few seconds)
"$LAUNCH" --full # skip slim excludes; copy everything
"$LAUNCH" --skip-prelaunch # reuse already-current build outputs
"$LAUNCH" --disable-workspace-trust # avoid trust prompts for trusted automation inputsOn Windows, invoke the PowerShell launcher with the same flags:
$skillDir = '<dir-of-this-SKILL.md>'
$launch = Join-Path $skillDir 'scripts\launch.ps1'
$sessionTitle = '<title-from-get_current_session>'
& $launch --session-title $sessionTitle # default: workbench
& $launch --agents --session-title $sessionTitle
& $launch -- --use-mock-keychain # forward extra args to code.bat
& $launch --source-user-data-dir C:\path\to\profile
& $launch --repo C:\path\to\vscode
& $launch --clone-extensions
& $launch --full
& $launch --skip-prelaunch
& $launch --disable-workspace-trustIf the local execution policy blocks scripts, invoke it with powershell -ExecutionPolicy Bypass -File <path-to-launch.ps1>. The Windows implementation has the same profile isolation, slim-copy excludes, settings merge, port allocation, foreground pre-launch, and CDP-ready contract as the bash launcher; only the shell commands and path syntax differ.
Handle a missing sign-in
If the source profile is missing or has no stored GitHub session, do not stop and do not ask before launching. Continue until the isolated Code OSS window reaches the normal GitHub/Copilot sign-in UI. Don't give up when Code OSS needs a sign-in.
At that point, use the questions tool with these choices:
- I have signed in
- Bootstrap profile to pre-authenticate future launches
If the user has signed in, continue with the current isolated launch. If they choose to bootstrap, clean up the current isolated launch and run the platform bootstrap script:
BOOTSTRAP=<dir-of-this-SKILL.md>/scripts/bootstrap/bootstrap-profile.sh
"$BOOTSTRAP" --repo <vscode-repo-root>$bootstrap = Join-Path $skillDir 'scripts\bootstrap\bootstrap-profile.ps1'
& $bootstrap -Repo <vscode-repo-root>If the failed launch used a custom --source-user-data-dir, pass the same path as --user-data-dir <path> on macOS/Linux or -UserDataDir <path> on Windows.
The bootstrap script returns only after the persistent Code OSS window's CDP endpoint is ready. It reports a log path and fails with the log tail if the window cannot become usable. Ask the user to sign in to GitHub/Copilot in that window and close it, using I have signed in as the acknowledgement choice. On macOS, the user must quit Code OSS completely with Cmd+Q; closing its last window does not stop the application.
After the user acknowledges, wait for the bootstrap process tree to release the persistent profile before launching again:
"$BOOTSTRAP" --wait-for-exit <exitMarker-from-bootstrap-json>& $bootstrap -WaitForExit -ExitMarker <exitMarker-from-bootstrap-json>Use the same profile path that bootstrap reported; omit the explicit path only when using the default profile. Once the wait succeeds, run the normal launcher again. Its isolated copy will inherit the stored authentication without racing files still held by the bootstrap window.
Do not bootstrap without the user's explicit choice. The bootstrap window uses persistent profile and shared-authentication storage, unlike normal isolated launches.
What gets copied (slim mode, the default)
The exclude list mirrors the one used by VS Code's own perf-test skill (.github/skills/auto-perf-optimize), which is known to keep Copilot auth and language-model availability working. Specifically WebStorage/, Service Worker/, Local Storage/, Cookies, Network Persistent State, TransportSecurity, Trust Tokens, Preferences, machineid, and the entire User/globalStorage/ (which holds state.vscdb) are all preserved.
Windows authentication
Windows has no shared per-app keychain for these secrets, so they live in files on disk - but not all in the user-data-dir. The GitHub session is stored at StorageScope.APPLICATION_SHARED only on Windows (see useSharedStorage and CROSS_APP_SHARED_SECRET_KEYS in src/vs/platform/secrets/common/secrets.ts), which puts the two halves of the credential in different directories:
| Piece | Location |
| Encrypted GitHub session blob | <shared-data-dir>/sharedStorage/state.vscdb |
DPAPI-wrapped decryption key (os_crypt.encrypted_key) | <user-data-dir>/Local State |
The launcher therefore seeds both: it copies the source profile and copies the source shared-data-dir into the run's throwaway shared-data dir. The source resolves the same way IEnvironmentService.appSharedDataHome does - $env:CODE_OSS_DEV_AUTHED_SHARED_DATA_DIR if set, else $env:VSCODE_PORTABLE\shared-data when running portable, else ~/<product.sharedDataFolderName> (i.e. %USERPROFILE%\.vscode-oss-shared). It also verifies Local State, machineid, and Network survived the profile copy, and warns on stderr if neither database holds a GitHub session.
This asymmetry is invisible on macOS/Linux, where the same token lands inside the profile. A Windows-only "always signed out" symptom is a shared-data-dir problem, not a profile problem: signing in against the source profile writes a perfectly good session, but before this seeding existed every launch handed Code OSS an empty shared dir and threw it away.
The bootstrap script writes the session blob to %USERPROFILE%\.vscode-oss-shared and the key to the profile's Local State; later launches copy both and inherit the session.
Profiles that predate the
APPLICATION_SHAREDmigration can still hold the secret inUser/globalStorage/state.vscdb.ApplicationSharedStorageMainregisters application storage as a read fallback, so those profiles authenticate even with no shared-data-dir present - which is why a missing shared dir is reported as a fact rather than assumed fatal.
Excluded (transient, regenerable, or known-not-needed):
User/workspaceStorage/- per-workspace state, including stored chat sessions (often multi-GB)User/History/- local file edit historyCachedExtensionVSIXs- backup VSIXs (hundreds of MB)logs- Chromium caches at the profile root:
Cache,Code Cache,CachedData,GPUCache,ShaderCache,Dawn*Cache,component_crx_cache; and under the persistent integrated-browser partition:Cache,Code Cache,GPUCache,Dawn*Cache Backups,blob_storage,BrowserMetrics,Crashpad,Session StorageSingleton*,*.lock,*.sock(would conflict with the source instance)
The persistent integrated-browser partition keeps website state such as cookies, local and session storage, IndexedDB, WebStorage, service workers, and preferences; only its regenerable caches are excluded.
extensions/ defaults to a fresh empty directory - fastest and conflict-free, but the launched instance starts with no third-party extensions installed. Pass --clone-extensions to copy the source extensions dir into the temp profile so the new instance is independent of the source. Pass --full to skip all excludes if you suspect the slim copy is missing something you need.
Why never share the source
extensions/dir directly? The extension management service writes a shared.obsoletefile; two concurrent writers crash each other's shared background process. The launcher always uses an isolated extensions dir for the same reason it uses--shared-data-dir(see below).
The script runs pre-launch (electron download, compile-if-missing, built-in extensions) in the foreground, then starts Code OSS detached and blocks until the renderer's CDP endpoint is responding (up to ~90s) before printing the JSON line on stdout. If anything fails — preLaunch errors, code.sh exits early, CDP never opens — the script exits non-zero and dumps the relevant log tail to stderr.
For repeated launches of the same prepared build, pass --skip-prelaunch after one successful normal launch. Only use it while a watch task keeps all output current or neither sources nor build outputs have changed; otherwise the new instance may run stale or incomplete code.
{"pid":12345,"cdpPort":53111,"extHostPort":53112,"mainPort":53113,"agentHostPort":53114,"userDataDir":".../user-data","extensionsDir":".../extensions","sharedDataDir":".../shared-data","runDir":"...","logFile":".../code.log","repo":"...","agents":false,"timings":{"profileMs":231,"preLaunchMs":251,"cdpReadyMs":459,"totalMs":941}}The additive timings object uses monotonic elapsed time to identify time spent preparing the isolated profile, running pre-launch, and starting Code OSS through CDP readiness. totalMs covers the complete launcher operation through readiness.
Capture it with Node, which is already a launcher prerequisite. No retry loop is needed because CDP is already up when the JSON is printed:
INFO=$("$LAUNCH" | tail -n1)
CDP=$(node -p 'JSON.parse(process.argv[1]).cdpPort' "$INFO")
EXT=$(node -p 'JSON.parse(process.argv[1]).extHostPort' "$INFO")
MAIN=$(node -p 'JSON.parse(process.argv[1]).mainPort' "$INFO")
AGENT=$(node -p 'JSON.parse(process.argv[1]).agentHostPort' "$INFO")
LOG=$(node -p 'JSON.parse(process.argv[1]).logFile' "$INFO")
PID=$(node -p 'JSON.parse(process.argv[1]).pid' "$INFO")On Windows, capture and parse the JSON with PowerShell:
$info = & $launch | Select-Object -Last 1 | ConvertFrom-Json
$cdp = $info.cdpPort
$ext = $info.extHostPort
$main = $info.mainPort
$agent = $info.agentHostPort
$log = $info.logFile
$pid = $info.pidWhat each port is for
| Port | Process | Use with |
cdpPort (--remote-debugging-port) | Renderer (the workbench window) | @playwright/cli over CDP, also Chrome DevTools |
extHostPort (--inspect-extensions) | Extension host (Node) | dap-cli (Node inspector protocol) |
mainPort (--inspect) | Electron main process (Node) | dap-cli (Node inspector protocol) |
agentHostPort (--inspect-agenthost) | Agent host process (Node) | dap-cli (Node inspector protocol) |
Drive the UI with @playwright/cli
Use the dynamic cdpPort from the launch JSON. The normal loop is: attach, confirm the target, inspect a screenshot, snapshot, interact, then re-snapshot after meaningful UI changes.
If you are unsure about Playwright CLI syntax, run npx @playwright/cli --help or npx @playwright/cli <command> --help instead of guessing option names.
Always pick a unique
PW_SESSIONname and pass it as-s=$PW_SESSIONon everynpx @playwright/cli ...call. The CLI is backed by a persistent daemon (cliDaemon.js) keyed by session name; if two shells both omit-s=, they share the implicit"default"session and the most-recently-attached CDP "wins" for every subsequent command from either shell. The launch skill is built around isolation (per-instance UDD, ports, shared-data-dir), and this pattern keeps that isolation intact at the Playwright-driving layer too. A note on the alternativePLAYWRIGHT_CLI_SESSIONenv var: it's documented in the package README and works correctly foropen-style workflows, but it interacts poorly withattach --cdp=...(the daemon ends up with both--cdp=...and--endpoint=<env-value>, and the latter wins, causing aconnect ENOENTfailure). Confirmed against@playwright/cli@0.1.13. Explicit-s=NAMEworks in all modes.
# At the top of your script / subagent prompt:
PW_SESSION="my-uniq-$$" # any unique string; $$ is fine for one shell per agent
# launch.sh blocks until CDP is ready, so a single attach is enough.
npx @playwright/cli -s=$PW_SESSION attach --cdp=http://127.0.0.1:$CDP
npx @playwright/cli -s=$PW_SESSION tab-list
npx @playwright/cli -s=$PW_SESSION snapshotAfter attach, later @playwright/cli commands keep using the connected app until you close or reattach — as long as you keep passing the same -s=$PW_SESSION.
Selecting the right Electron target
Electron apps can expose multiple windows or webviews. If tab-list shows about:blank, a webview, or otherwise the wrong target, switch targets before interacting:
npx @playwright/cli -s=$PW_SESSION tab-list
npx @playwright/cli -s=$PW_SESSION tab-select 2
npx @playwright/cli -s=$PW_SESSION snapshotIf a target looks stale after relaunching, run npx @playwright/cli -s=$PW_SESSION close, attach again with $CDP, and re-check tab-list.
Inspect the launched window before proceeding
After every launch or relaunch, once the intended window has rendered, take a screenshot and open it with an image-viewing tool. Visually check that the expected surface, text, icons, and layout are intact before interacting with the app or asking the user to sign in. Saving a screenshot without inspecting it, reading a DOM/accessibility snapshot, or reaching the CDP endpoint does not satisfy this check.
If the screenshot shows missing icons, blank content, or visibly broken layout, stop the scenario and investigate the observed problem before proceeding. After repair, relaunch and inspect again; do not wait for the user to point out the broken UI.
Focusing the chat input
Use the playwrightScripts/focus-chat-input.ts script in both the regular workbench and the Agents window. It performs the complete focus flow in one Playwright call:
- If a visible chat input is already focused, it does nothing.
- If a visible chat input exists but is not focused, it focuses that input.
- Otherwise, it invokes the platform chat-focus chord, waits for the input, and focuses it only if the chord did not already do so.
The script detects the platform from the browser page, prefers the active Agents session, and excludes inline chat inputs. If the cloned profile has customized the default chord, it falls back to the surface-specific command through the Command Palette.
LAUNCH_DIR=<dir-of-this-SKILL.md>
FOCUS_CHAT="$LAUNCH_DIR/playwrightScripts/focus-chat-input.ts"
npx @playwright/cli -s=$PW_SESSION run-code --filename="$FOCUS_CHAT"$skillDir = '<dir-of-this-SKILL.md>'
$focusChat = Join-Path $skillDir 'playwrightScripts\focus-chat-input.ts'
npx @playwright/cli "-s=$pwSession" run-code "--filename=$focusChat"The script returns { focused, focusChanged, focusInvoked, shortcutInvoked, commandPaletteFallbackInvoked, selector }. focusChanged reports whether this script invocation moved focus into Chat, while focusInvoked reports whether the script had to call focus() directly. Both are false when the chat input was already focused. If the script fails, take a fresh snapshot and resolve any blocking dialog or unavailable chat state before retrying.
Typing into Monaco (chat input, editors)
fill and type silently fail on Code OSS — Monaco's native-edit-context element doesn't react to Playwright's default input pipeline. Use one of these alternatives:
scripts/monaco-paste.tshelper (recommended — cross-platform, fast, no system clipboard, parallel-safe). Reads text from a positional arg or stdin and dispatches aClipboardEvent('paste')with aDataTransferpayload into the focused chat-input Monaco editor. Honors--session NAMEorPW_SESSIONso it stays inside the same-s=session as everything else. The Bash wrapperscripts/monaco-paste.shremains available for compatibility.LAUNCH_DIR=<dir-of-this-SKILL.md> # the same dir that holds scripts/launch.sh FOCUS_CHAT="$LAUNCH_DIR/playwrightScripts/focus-chat-input.ts" PASTE="$LAUNCH_DIR/scripts/monaco-paste.ts" export PW_SESSION # helper reads this env var # Send a prompt: npx @playwright/cli -s=$PW_SESSION run-code --filename="$FOCUS_CHAT" node "$PASTE" 'Please run `pwd && ls` using your terminal tool.' npx @playwright/cli -s=$PW_SESSION press Enter # Long / arbitrary text via stdin (avoids any shell-quoting headaches): printf 'multi-line prompt\nwith backticks `x`\nand emoji 🎉' | node "$PASTE" # Append without clearing: node "$PASTE" --append " continued text" # Skip the read-back check (useful when intentionally pasting more than the # chat input's ~600-character soft cap): node "$PASTE" --no-verify "...long text..." # Or pass the session explicitly per call (if you don't want to export PW_SESSION): node "$PASTE" --session "$PW_SESSION" "..."$paste = Join-Path $skillDir 'scripts\monaco-paste.ts' node $paste --session $pwSession 'Please respond with OK.' npx @playwright/cli "-s=$pwSession" press EnterThe helper prints a single JSON line on stdout:
{ok, actualLength, expectedLength, viewLineCount, firstViewLine, error?}. Exit 0 on success, 1 on verify failure, 2 on argument errors. Tested reliable across 20+ sequential pastes including unicode (中文), emoji (🎉), backticks, ampersands, embedded quotes, and newlines.Why a helper script and not just docs: the inline recipe involves a multi-line
node -eheredoc with embedded JS template literals, which is exactly the kind of code that gets miscopied. There are also three non-obvious correctness traps the helper handles internally:- Monaco's
native-edit-contextdoesn't react tofillortype, only to actual paste events (or per-keypress). - Monaco renders ASCII spaces as U+00A0 (NBSP) in the view-line DOM, so verification has to normalize before comparing.
- Monaco updates its DOM asynchronously after a paste event — a synchronous read-back inside the same
evalreturns stale state. The helper polls rendered view lines across paint cycles until the pasted prefix appears or verification times out.
- Monaco's
- Per-key
press(universal but slow — each press is a separate CLI invocation with Node startup cost):npx @playwright/cli -s=$PW_SESSION press H npx @playwright/cli -s=$PW_SESSION press i npx @playwright/cli -s=$PW_SESSION press Enter - Clipboard paste via
pbcopy(fast on macOS, butNSPasteboardis system-wide so any concurrent shell that touches the pasteboard will collide). Only use when nothing else on the machine is using the clipboard for the duration of the paste.LAUNCH_DIR=<dir-of-this-SKILL.md> FOCUS_CHAT="$LAUNCH_DIR/playwrightScripts/focus-chat-input.ts" printf '%s' "Your prompt here" | pbcopy npx @playwright/cli -s=$PW_SESSION run-code --filename="$FOCUS_CHAT" npx @playwright/cli -s=$PW_SESSION press Meta+v npx @playwright/cli -s=$PW_SESSION press Enter
Parallel multi-instance pattern
Because the launch skill is built around isolation, the natural workload is many agents on one machine, each driving their own Code OSS. The pattern boils down to giving each agent a unique PW_SESSION and passing it everywhere:
LAUNCH_DIR=<dir-of-this-SKILL.md>
FOCUS_CHAT="$LAUNCH_DIR/playwrightScripts/focus-chat-input.ts"
PASTE="$LAUNCH_DIR/scripts/monaco-paste.ts"
export PW_SESSION
# In agent A's shell:
PW_SESSION="agent-A-$$"
INFO=$("$LAUNCH" --agents -- --use-mock-keychain | tail -n1)
CDP=$(node -p 'JSON.parse(process.argv[1]).cdpPort' "$INFO")
npx @playwright/cli -s=$PW_SESSION attach --cdp=http://127.0.0.1:$CDP
npx @playwright/cli -s=$PW_SESSION run-code --filename="$FOCUS_CHAT"
node "$PASTE" "prompt for A" # helper picks up $PW_SESSION
# In agent B's shell (running concurrently):
PW_SESSION="agent-B-$$"
INFO=$("$LAUNCH" --agents -- --use-mock-keychain | tail -n1)
CDP=$(node -p 'JSON.parse(process.argv[1]).cdpPort' "$INFO")
npx @playwright/cli -s=$PW_SESSION attach --cdp=http://127.0.0.1:$CDP
npx @playwright/cli -s=$PW_SESSION run-code --filename="$FOCUS_CHAT"
node "$PASTE" "prompt for B"Each agent gets its own cliDaemon bound to its own CDP, so the pastes / clicks / snapshots don't cross-contaminate. Verified live with two concurrent instances. macOS Mach-ports caveat: on macOS, beyond ~2–3 concurrent Code OSS instances Crashpad's exception handler tends to die with mach_port_request_notification: invalid capability. That's a separate, OS-level limit; it's not affected by the session name.
Cleanup for
cliDaemonprocesses: stop your session's daemon withnpx @playwright/cli -s=$PW_SESSION close, or nuke all stale daemons (after killing all the Code OSS windows) withnpx @playwright/cli kill-all. Session daemons live under~/Library/Caches/ms-playwright/daemon/<hash>/.
Agents window selector differences
The Agents window does not use the regular workbench .interactive-input-editor wrapper. Selector checks that are scoped to that wrapper may return nothing even when the Agents chat input is focused.
// Regular-workbench-specific selector; do not assume this exists in Agents.
document.querySelectorAll('.interactive-input-editor .view-line')
// More useful checks in Agents.
document.querySelectorAll('.view-line')
document.activeElement?.matches('.native-edit-context, textarea.inputarea')The focus script accounts for these DOM differences and prioritizes the active Agents session.
Verifying and clearing chat text
For the regular workbench sidebar, this confirms that text landed in the Monaco input:
npx @playwright/cli -s=$PW_SESSION eval '
(() => {
const sidebar = document.querySelector(".part.auxiliarybar");
const viewLines = sidebar?.querySelectorAll(".interactive-input-editor .view-line") ?? [];
return Array.from(viewLines).map(viewLine => viewLine.textContent).join("|");
})()'For the Agents window, use a fresh snapshot plus the broader selector/focus checks above instead of assuming the regular sidebar wrapper is present.
To clear the focused Monaco input:
# macOS
npx @playwright/cli -s=$PW_SESSION press Meta+a
# Linux / Windows
npx @playwright/cli -s=$PW_SESSION press Control+a
npx @playwright/cli -s=$PW_SESSION press BackspaceIf the focus script cannot reach Chat because the surface is not available yet, take a snapshot and navigate the UI into a state where chat exists before retrying. Avoid treating completed CLI commands as proof that text was entered.
Screenshots (paper trail)
SHOTS="$PWD/screenshots/$(date +%Y-%m-%dT%H-%M-%S)"
mkdir -p "$SHOTS"
npx @playwright/cli -s=$PW_SESSION screenshot --filename="$SHOTS/after-launch.png"Keep screenshots inside the workspace, not
/tmp, so they survive for review.
For wide windows, --full-page can make layout easier to inspect, and element screenshots are useful when a snapshot gives a stable ref for the panel you care about:
npx @playwright/cli -s=$PW_SESSION screenshot --full-page --filename="$SHOTS/full-window.png"
npx @playwright/cli -s=$PW_SESSION screenshot e42 --filename="$SHOTS/panel.png"On macOS, a screenshot "Permission denied" failure usually means the terminal lacks Screen Recording permission. Use text/state verification while resolving that permission issue.
Debug with dap-cli
To set breakpoints in VS Code source while the window is running, attach dap-cli to one of the ports. If dap-cli would help but the corresponding skill is unavailable, prompt the user to install it from https://github.com/roblourens/dap-cli before continuing with debugger-specific steps.
Read the dap-cli skill for the full attach/breakpoint/inspect workflow when it is available - this skill only tells you which port to point it at:
- Extension host (most common - Copilot Chat extension, built-in extensions, your own extension under development) ->
extHostPort - Main process (Electron lifecycle, window/menu wiring, IPC) ->
mainPort - Local agent host (
src/vs/platform/agentHost/node/..., agent session lifecycle, AHP wiring, Claude/Copilot agent providers) ->agentHostPort - Renderer (the workbench itself,
src/vs/workbench/...) ->cdpPort
You can run @playwright/cli and dap-cli against the same window simultaneously - drive the UI with one terminal, hit a breakpoint and inspect state in another.
Multiple instances
Every launch picks fresh ports and a fresh temp runDir, so you can run as many concurrent Code OSS windows as your machine can handle. Each one's ports come back in its own JSON blob - keep them separate.
The launcher also passes --shared-data-dir=<runDir>/shared-data. This is required for multi-instance isolation: Code OSS keeps a fixed-path SQLite DB at ~/.<dataFolderName>-shared/sharedStorage/state.vscdb that is not covered by --user-data-dir. Without overriding it, two concurrent instances would fight over the same file and one would die with "shared background process terminated unexpectedly". Each launch gets its own shared-data dir, seeded from the source shared-data-dir so the Windows GitHub session survives - see Windows authentication for why that copy matters.
Restart after source changes
Workbench code is loaded when the Code OSS window starts; source changes are not hot-reloaded into an already-running instance. After the build output is current, kill the launched process, launch again, and reattach to the new cdpPort from the new JSON blob.
kill "$PID" 2>/dev/null || true
INFO=$("$LAUNCH" | tail -n1)
CDP=$(node -p 'JSON.parse(process.argv[1]).cdpPort' "$INFO")
PID=$(node -p 'JSON.parse(process.argv[1]).pid' "$INFO")
npx @playwright/cli -s=$PW_SESSION attach --cdp=http://127.0.0.1:$CDP
npx @playwright/cli -s=$PW_SESSION tab-list
npx @playwright/cli -s=$PW_SESSION snapshotIf you are iterating frequently, keep the repo build/watch task running separately so relaunches pick up already-generated output. After one successful normal launch, --skip-prelaunch avoids repeating the preparation while those outputs remain current.
Cleanup
The launcher writes everything under a temp runDir (printed in the JSON). When you're done:
# Disconnect this session's playwright daemon (leaves other sessions' daemons alone)
npx @playwright/cli -s=$PW_SESSION close
# Or nuke any stale daemons left behind by crashed callers across all sessions:
# npx @playwright/cli kill-all
# Kill the Code OSS instance
kill "$PID" 2>/dev/null || true
# Remove the throwaway profile
rm -rf "$(dirname "$LOG")"On Windows, use the cleanup helper. The PID returned by code.bat can be a short-lived wrapper, so the helper finds and terminates only Code - OSS.exe processes whose --user-data-dir references this launch's exact runDir, then removes the throwaway profile:
$cleanup = Join-Path $skillDir 'scripts\cleanup\cleanup.ps1'
& $cleanup -RunDir $info.runDir -PlaywrightSession $pwSessionCode OSS is a full Electron app and easily eats 1-4 GB. Always clean up.
Troubleshooting
Daemon pid=...: listen EINVALfrom@playwright/cli- the daemon's socket path (TMPDIR+ a fixed ~33-char prefix + the-s=session name) exceeded the ~103-byte unix socket limit. macOS's defaultTMPDIRleaves only ~16 characters for the session name, so shorten-s=first. If you need a longer name, scope the override to the single command (TMPDIR=/tmp npx @playwright/cli ...) rather thanexporting it, so the launcher keeps using your private per-user temp dir.- "Sent env to running instance. Terminating..." - The dynamic
--user-data-dirshould prevent this. If you see it, another Code OSS is using the same profile path; pass--source-user-data-dirto a different source or check that the temp copy actually happened (ls "$(node -p 'JSON.parse(process.argv[1]).userDataDir' "$INFO")"). - Renderer ESM errors /
import { Menu } from 'electron'-ELECTRON_RUN_AS_NODEis set in your env. The launcher unsets it for the child, but if you spawncode.shyourself, do the same. missing config value GIT_CONFIG_VALUE_*from Git - indexed Git configuration inherited from an agent runtime lost an empty value while the app processes were launched. The launcher unsetsGIT_CONFIG_COUNTandGIT_CONFIG_PARAMETERSso host-only Git overrides do not affect Code OSS; do the same when invokingcode.shdirectly from such a runtime.- Built-in extension fails to load (
Cannot find module .../extensions/.../out/extension.js) - extensions weren't compiled. Runnpm run compile(one-shot, also rebuilds all built-in extensions) ornpm run watch(incremental). A common cause: you rannpm run transpile-clientto satisfy unit tests, which populatedout/but notextensions/*/out/, so preLaunch's "isout/missing?" check skipped the compile. launch.shexits non-zero with a log tail - either pre-launch failed,code.shdied before CDP came up, or CDP never opened within 90s. The tail printed to stderr is fromrunDir/code.log- read it to diagnose.- Snapshot shows the wrong page or no expected controls - use
tab-list, switch withtab-select <index>if needed, then re-snapshot before interacting. - CLI typing commands complete but the input stays empty - run
playwrightScripts/focus-chat-input.ts, usepressor clipboard paste rather thanfill/type, and verify the input state before sending. - Auth missing in the launched window - use the missing sign-in flow so the user can either finish signing into the current isolated window or choose to bootstrap the persistent source profile. On Windows, check the shared-data-dir first: the GitHub session blob lives in
%USERPROFILE%\.vscode-oss-shared\sharedStorage\state.vscdb, not in the profile. The launcher logscopying shared data: <src> -> <dst>on stderr when it finds it, and warnsno shared-data-dir at <path>when it doesn't.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

