skills/chrome-extension-architect/SKILL.md
Privacy-first Chrome Manifest Version 3 extension architect - sidePanel design, MV3 service worker lifecycle, least-privilege permission audits, storage strategy, cross-browser sidebar patterns, and headless Playwright testing.
npx skillsauth add alcyone-labs/agent-skills chrome-extension-architectInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Elite-level Chrome extension architecture and debugging workflow with privacy-first defaults and least-privilege permissions.
Use this skill when the user asks about browser extensions (especially Chrome MV3) including:
chrome.sidePanel, Firefox sidebar_action, Safari constraints)Default target: Chrome MV3. If the user doesn’t specify browser(s), assume Chrome stable.
Start every major answer with target + scope.
Target: <Chrome MV3 | Firefox MV3 | Safari> | Scope: <side panel | permissions | lifecycle | storage | compat | debugging>Privacy-first default.
Least privilege, always.
permissions + minimal host_permissions.activeTab, scripting (targeted injection), declarativeNetRequest (when network rules are needed).<all_urls>, *://*/*, broad tabs, unbounded host permissions.Every permission/API must be justified + privacy-risk tagged.
MV3 service worker reality check (single biggest bug source).
Side panel architecture must be modern.
chrome.sidePanel + setPanelBehavior({ openPanelOnActionClick: true }).setOptions() to vary panel path per-tab / conditionally.Cross-browser: feature-detect, don’t UA-sniff.
chrome.sidePanel vs Firefox browser.sidebarAction).Ask (or infer) these quickly:
Need a persistent/reusable UI?
├─ Chrome/Edge -> sidepanel (chrome.sidePanel)
├─ Firefox -> sidebar_action / browser.sidebarAction
└─ Safari -> expect limitations; consider alternative UI (popup/options) or separate Safari strategy
Need to interact with the current tab?
├─ One-off user action -> activeTab + scripting
└─ Always-on per-site -> narrow host_permissions only for required domains
Need DOM / rendering in background?
└─ Use offscreen document (Chrome) or move work into panel/page context
Then read the matching references:
references/sidepanel/README.mdreferences/permissions/README.mdreferences/service-worker-lifecycle/README.mdreferences/storage-state/README.mdreferences/cross-browser/README.mdreferences/debugging/README.mdreferences/templates/README.mdUse this response skeleton for most user questions:
Input: “Build a MV3 extension with a sidebar that saves notes per tab. Minimal permissions.”
Expected output (high level):
chrome.sidePanel with panel path + per-tab contextsidePanel, storage, optional activeTab if reading title/url on demandchrome.storage.local keyed by tabId (ephemeral) + url (stable) with explicit privacy warning about storing URLssetPanelBehavior + message passing between panel and SWInput: “My service worker forgets auth after a minute. I store it in a global variable.”
Expected output (high level):
chrome.storage.local (or session for ephemeral) with encryption guidanceInput: “I use sidePanel in Chrome. How do I support Firefox?”
Expected output (high level):
sidebar_action differences (no programmatic open; UX expectations)webextension-polyfill for promise-based APIs where appropriatetabs unless you truly need cross-tab enumeration. It’s a high-privacy-impact permission.alarms, message triggers, offscreen documents).This skill includes comprehensive headless testing support via Playwright (Chrome 128+).
| Component | Test Approach |
|-----------|--------------|
| Popup | Load chrome-extension://ID/popup.html, interact with elements |
| Side Panel | Navigate to panel URL, test UI state |
| Content Script | Inject into test page, verify DOM changes |
| Service Worker | Send messages, check storage, test alarms |
| Full Flows | Multi-step user journeys across all contexts |
const context = await chromium.launchPersistentContext('', {
headless: true, // Now works with extensions!
args: [
`--load-extension=${EXTENSION_PATH}`,
'--headless=new', // Required flag
],
});
import { test, expect } from '@playwright/test';
import path from 'path';
const EXTENSION_PATH = path.join(__dirname, '../dist');
test('popup displays correctly', async ({ browser }) => {
const context = await browser.newContext({
args: [
`--load-extension=${EXTENSION_PATH}`,
`--disable-extensions-except=${EXTENSION_PATH}`,
],
});
// Get extension ID from service worker
let [serviceWorker] = context.serviceWorkers();
if (!serviceWorker) {
serviceWorker = await context.waitForEvent('serviceworker');
}
const extensionId = serviceWorker.url().split('/')[2];
// Test popup
const popup = await context.newPage();
await popup.goto(`chrome-extension://${extensionId}/popup.html`);
await expect(popup.locator('h1')).toHaveText('My Extension');
});
references/playwright-testing/README.md - Overview and decision treereferences/playwright-testing/api.md - Complete API referencereferences/playwright-testing/configuration.md - Setup and fixturesreferences/playwright-testing/patterns.md - Testing scenariosreferences/playwright-testing/gotchas.md - Pitfalls and workaroundsInstall helpers are in resources/install.sh.
development
Builds precise production-ready custom Agent Skills following AgentSkills.io guidelines. Use when user requests to create, refine or package Skills
development
Best practices for using simple-logger in TypeScript applications
tools
Implement Sauve-specific Jazz extension behavior on top of worker-authority architecture and typed popup-worker protocol
tools
Test Chrome extensions (Manifest V3) headlessly with Playwright — load unpacked extensions, extract the extension ID, test popups/content scripts/background, run reliably in CI, and use the correct headless mode that actually supports extensions.