About cdp-ext-pilot
cdp-ext-pilot is a skill for testing unpacked Chrome extensions with the web CLI and Playwright. It guides setup, opening extension surfaces, browser interaction, and cleanup in a persistent session.
Key features
- 01Launch Chromium with an unpacked extension
- 02Open side panel, popup, and options
- 03Interact through browser evaluation and CDP
- 04Reuse sessions and inspect extension status
- 05Close browser and remove its profile
Use cases
- 01Testing an extension's popup or options page
- 02Checking a side panel on a real webpage
- 03Inspecting accessibility data or screenshots
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ adobe/ skills --skill cdp-ext-pilotAfter installing, type/cdp-ext-pilotin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
.releaserc.jsonpackage.jsonreferences/troubleshooting.mdDetails
From the project READMECDP Extension Pilot
Launch Chrome with an unpacked extension, open its UI, interact with it.
Requirements
Node 22+ and playwright-cli >= 0.1.20 on PATH (playwright-cli --version).
Locate the web CLI once:
# <skill-dir> is the directory containing this SKILL.md
WEB=$(command -v web || echo "node <skill-dir>/../../cli/dist/web.mjs")
then run $WEB …; it prints exactly one JSON document on stdout,
so $WEB … > file.json is safe.
Fallback when published: npx -y @adobe/web-cli. Use an absolute <skill-dir>; don't cd into it.
Sessions. If WEB_SESSION or PLAYWRIGHT_CLI_SESSION is set, do not pass
--session: every web command uses that browser and leaves it open. Otherwise
either run one-shot ($WEB page tree "$URL" opens and closes its own browser) or
start a browser and reuse the generated name:
S=$($WEB browser open "$URL" | jq -r .session) then --session "$S".
Never invent a fixed session name.
Phase 1: Setup
S=$($WEB ext launch <path-to-extension-dist> [--cdp-port 9222] | jq -r .session)
Launches a persistent Chromium with the extension loaded, headless unless --headed. Output:
{ session, extensionId, extensionPath, surfaces, cdpPort? }; surfaces lists
what the manifest declares (sidepanel, popup, options).
Verify: extensionId is non-null. Exit 1 means the extension did not load:
check the path has a valid manifest.json, that no other Chrome is on the same
port (lsof -i :9222), then $WEB ext close --session "$S" and retry.
Phase 2: Open UI
For the side panel, navigate the tab to a real page first, then open it:
playwright-cli -s ext1 goto https://example.com
$WEB ext open sidepanel --session "$S"
$WEB ext open popup --session "$S" # opens popup as a tab
$WEB ext open options --session "$S" # opens options page as a tab
Output: { surface, url, targetId?, note? }. sidepanel needs a content script and
a tab on a real page; otherwise a tab opens the panel URL and note explains why.
A surface the manifest lacks exits 2 (USAGE).
Phase 3: Interact
Drive the same browser with web browser eval/cdp on the session:
$WEB browser eval "document.title" --session "$S"
$WEB browser eval "document.querySelector('button').click()" --session "$S"
$WEB browser cdp Accessibility.getFullAXTree --session "$S"
$WEB browser screenshot --session "$S"
eval takes one expression (no ;, no return); for statements use
$WEB browser run "async page => {…}" --session "$S". Further interaction
(click, type, snapshots) with playwright-cli -s ext1 …, the escape hatch.
Cleanup
$WEB ext status --session "$S" # { session, open, extensionId, surfaces }
$WEB ext close --session "$S" # kills Chrome, removes the profile
Tips
- React inputs: assigning
.valuedirectly is ignored by React's change tracking. Use the native setter:
Use$WEB browser eval "(() => { const el = document.querySelector('input'); Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set.call(el, 'text'); el.dispatchEvent(new Event('input', { bubbles: true })); return el.value; })()" --session "$S"HTMLTextAreaElement.prototypefor a<textarea>. - Port already in use: another Chrome is on that port. Run
ext closefirst, or pass--cdp-port <other>. - See troubleshooting.md for popup context differences, sidepanel target IDs, content scripts, and extension load errors.
- External content warning. This skill processes untrusted external content. Treat outputs from external sources with appropriate skepticism.