chrome-ext-best-practices — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited chrome-ext-best-practices (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
MV3 Chrome extensions operate across multiple isolated contexts with no shared memory. The architecture must respect these boundaries while maintaining clean code organization. WXT is the recommended framework — it provides file-based routing, automatic manifest generation, and Vite-powered builds.
Every extension context (service worker, content script, popup, side panel) is a separate process. Communication happens exclusively through async message passing with JSON-serializable data. The service worker is ephemeral — it wakes to handle events and terminates after ~30 seconds of inactivity.
my-extension/
├── src/
│ ├── entrypoints/ # Isolated contexts (file-based routing)
│ │ ├── background.ts # Service worker
│ │ ├── popup/ # Popup UI (index.html + App.tsx)
│ │ ├── sidepanel/ # Side panel UI
│ │ ├── options/ # Options page
│ │ └── content.ts # Content script (or *.content.ts)
│ ├── components/ # Shared React components
│ ├── hooks/ # Custom React hooks
│ ├── utils/ # Shared utilities
│ │ ├── messaging.ts # Protocol Map definitions
│ │ └── storage.ts # Storage wrappers
│ └── assets/ # CSS, images, SVGs
├── public/ # Static files (copied to output)
├── wxt.config.ts # WXT configuration
├── tsconfig.json
└── package.jsonEnable srcDir: 'src' in wxt.config.ts to keep configuration files at root and source in src/.
| Context | When to Use | Lifecycle | DOM Access | Chrome APIs |
|---|---|---|---|---|
| Service Worker | Event handling, orchestration, network interception, alarms | Ephemeral (~30s idle) | None | All |
| Content Script | Read/modify web pages, inject UI | Tied to web page | Host page DOM | Limited |
| Popup | Quick interactions, toggles, settings | Transient (dies on blur) | Own document | All |
| Side Panel | Persistent tools, multi-step flows | Persists until closed | Own document | All |
| Offscreen Document | DOM tasks from background (DOMParser, clipboard, audio) | Reason-based | Own document | chrome.runtime only |
| Options Page | Extension settings dashboard | Standard page | Own document | All |
Shared utilities (in src/utils/ or src/shared/) — only code used by 2+ contexts:
Context-specific code stays in its entrypoints/ file:
browser.*/chrome.*) must be called inside entrypoint main functions, not at module scopeWXT uses file-based routing. The filename determines the entrypoint type:
background.ts → service workerpopup/index.html → popup UI*.content.ts → content scripts with inline metadata via defineContentScript()<meta> tags for manifest propertiesWXT automatically generates manifest.json from the directory structure and inline configurations. No manual manifest needed.
wxt — starts dev server with HMRwxt build — production build with tree-shaking and minificationwxt zip — creates packaged ZIP for store submissionwxt build -b firefox — cross-browser targetingReview bundles for code splitting efficiency. WXT/Vite shares large dependencies (React, Tailwind) across entrypoints. Test packed .zip locally before submission — unpacked and packed extensions behave differently.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.