Wxt
mengxi-ream/read-frog
Build browser extensions with WXT - a next-gen framework with file-based entrypoints, Vite builds, auto-imports, typed storage with migrations, content script UIs, and multi-browser MV2/MV3 support.
A skill your agent uses when building or shipping a Manifest V3 browser extension and hitting its quirks — service worker dying and losing state, permission warnings, a Chrome Web Store rejection…
$ npx skills add ericrisco/rsc-harness --skill chrome-extension -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install ericrisco/rsc-harness chrome-extension --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/ericrisco/rsc-harness.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/chrome-extension .claude/skills/chrome-extension && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "chrome-extension" agent skill from https://github.com/ericrisco/rsc-harness/tree/main/skills/chrome-extension into .claude/skills/chrome-extension/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "chrome-extension", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/ericrisco/rsc-harness/tree/main/skills/chrome-extensionType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add ericrisco/rsc-harness --skill chrome-extension -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install ericrisco/rsc-harness chrome-extension --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ericrisco/rsc-harness.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/chrome-extension .agents/skills/chrome-extension && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "chrome-extension" agent skill from https://github.com/ericrisco/rsc-harness/tree/main/skills/chrome-extension into .agents/skills/chrome-extension/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "chrome-extension", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add ericrisco/rsc-harness --skill chrome-extension -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install ericrisco/rsc-harness chrome-extension --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ericrisco/rsc-harness.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/chrome-extension .cursor/skills/chrome-extension && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "chrome-extension" agent skill from https://github.com/ericrisco/rsc-harness/tree/main/skills/chrome-extension into .cursor/skills/chrome-extension/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "chrome-extension", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/ericrisco/rsc-harness.git --path skills/chrome-extension--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add ericrisco/rsc-harness --skill chrome-extension -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install ericrisco/rsc-harness chrome-extension --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ericrisco/rsc-harness.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/chrome-extension .gemini/skills/chrome-extension && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "chrome-extension" agent skill from https://github.com/ericrisco/rsc-harness/tree/main/skills/chrome-extension into .gemini/skills/chrome-extension/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "chrome-extension", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install ericrisco/rsc-harness chrome-extensionInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add ericrisco/rsc-harness --skill chrome-extension -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/ericrisco/rsc-harness.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/chrome-extension .github/skills/chrome-extension && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "chrome-extension" agent skill from https://github.com/ericrisco/rsc-harness/tree/main/skills/chrome-extension into .github/skills/chrome-extension/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "chrome-extension", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add ericrisco/rsc-harness --skill chrome-extension -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install ericrisco/rsc-harness chrome-extension --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ericrisco/rsc-harness.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/chrome-extension .opencode/skills/chrome-extension && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "chrome-extension" agent skill from https://github.com/ericrisco/rsc-harness/tree/main/skills/chrome-extension into .opencode/skills/chrome-extension/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "chrome-extension", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
chrome-extensionA skill your agent uses when building or shipping a Manifest V3 browser extension and hitting its quirks — service worker dying and losing state, permission warnings, a Chrome Web Store rejection…
Chrome Extension is an agent skill from ericrisco/rsc-harness. Use when building or shipping a Manifest V3 browser extension and hitting its quirks — service worker dying and losing state, permission warnings, a Chrome Web Store rejection, content-script/worker/popup messaging, or an MV2-to-V3 migration. NOT a generic web app (that is nextjs), NOT a desktop shell (that is electron).
Its SKILL.md is about 2.7k tokens, which your agent loads only when the skill is triggered. The skill folder holds 7 other files, including scripts and reference files (for example `evals/README.md`, `evals/cases.yaml` and `references/store-and-migration.md`).
It sits in Development, covering Browser extensions. It works with Chrome Extensions, Next.js and Vite. The repository describes itself as: Your agent invents things because it has no memory, and can't touch your database because it has no arms. rsc is the meta-harness that gives it both, plus the trade to know the… The licence is MIT.
3 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit e3d5b33. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Ships 1 file in scripts/ (Shell), which the agent can run.
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Chrome Extension loads about 2.7k tokens when it runs, and up to ~4k if it reads all its reference files. Until then it costs about 86 tokens; SKILL.md has 1,157 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); the scripts in this folder are not scanned.
The full file from ericrisco/rsc-harness at commit e3d5b33, republished under its MIT licence (© ericrisco). 1,157 words, ~2,747 tokens.
.claude/skills/chrome-extension/SKILL.md (or your agent's skills folder). This skill also uses 4 other files; get the full folder from GitHub.An MV3 extension is three isolated JavaScript contexts that never share memory and only talk via messages:
background.service_worker) — the logic and lifecycle brain. Ephemeral: Chrome kills it when idle and restarts it on the next event. It has no DOM and no window.chrome.* APIs except messaging and storage.action.default_popup), options page, side panel. Normal web pages that load and unload as the user opens/closes them.Internalize that picture first. Most extension bugs are someone treating one of these as if it shared state with another. They do not. The wire between them is chrome.runtime messaging and chrome.storage.
Manifest V3 is the only version the Chrome Web Store accepts; MV2 phase-out began June 2024 and is still rolling out. Build MV3 from the start.
| Setup | Pick when | Cost |
|---|---|---|
Vanilla (raw files, load unpacked) | tiny extension, no npm imports, you want the fastest possible reload loop | no TS, no HMR, manual reloads |
Vite + CRXJS (@crxjs/vite-plugin) | TS, npm imports, React/Vue popup, you want HMR | a build step; you ship dist/, not the repo |
Default to Vite + CRXJS the moment you want TypeScript or a framework popup — it does the manifest wiring and HMR for you. Reach for vanilla only for a one-file experiment.
Minimal tree (Vite + CRXJS):
my-ext/
manifest.json # source of truth; CRXJS reads it
src/
background.ts # service worker
content.ts # content script
popup/
index.html
popup.tsx
public/icons/ # 16, 48, 128 px PNGs
vite.config.ts
# build output -> dist/ (this is what you zip){
"manifest_version": 3,
"name": "Highlighter",
"version": "1.0.0",
"description": "Highlights selected text on the current page.",
"icons": { "16": "icons/16.png", "48": "icons/48.png", "128": "icons/128.png" },
"action": { "default_popup": "popup/index.html" },
"background": { "service_worker": "background.js", "type": "module" },
"permissions": ["activeTab", "storage", "scripting"],
"host_permissions": [],
"minimum_chrome_version": "120"
}Rules that fail review or break the worker if you get them wrong:
manifest_version must be 3. There is no 2 path forward.background.service_worker is a string, not an array. There is no background.scripts and no background.page in MV3. The persistent key does not exist — delete it. (Source: developer.chrome.com "Migrate to a service worker", accessed 2026-06-02.)"type": "module" lets the worker use import. Use it if you bundle.host_permissions empty until you can name exactly which sites and why (see permission table).The service worker is ephemeral. It terminates when idle and wakes on an event. Two consequences govern almost all your code:
chrome.runtime.onMessage.addListener inside an async callback or after an await, Chrome may not have registered it when it wakes the worker, and your event is lost. (Source: developer.chrome.com "Migrate to a service worker", accessed 2026-06-02.)chrome.storage.// Bad — global resets to 0 every time the worker is killed and restarts
let clickCount = 0;
chrome.action.onClicked.addListener(() => {
clickCount++; // silently back to 1 after idle
});
// Good — durable across worker restarts, listener registered at top level
chrome.action.onClicked.addListener(async () => {
const { clickCount = 0 } = await chrome.storage.local.get("clickCount");
await chrome.storage.local.set({ clickCount: clickCount + 1 });
});Messaging choices:
chrome.runtime.sendMessage(msg) / chrome.tabs.sendMessage(tabId, msg) paired with chrome.runtime.onMessage. Return true from the listener to keep the channel open for an async sendResponse.chrome.runtime.connect() → Port, with port.onMessage / port.postMessage.Content script → service worker → popup: there is no direct content-script-to-popup channel when the popup is closed. Route through the worker or through chrome.storage and let the popup read on open.
Reviewers reject broad permissions with no justification, and broad host_permissions trigger a scary install-time warning that tanks conversion. Declare the narrowest thing that works.
| Permission | Grants | Warning? | Use when |
|---|---|---|---|
activeTab | temporary access to the current tab, only after a user gesture (toolbar click) | none | the user clicks your icon and you act on that one page |
scripting | chrome.scripting.executeScript to inject programmatically | none alone (needs a host or activeTab to target) | inject on demand instead of on every page |
host_permissions: ["https://example.com/*"] | persistent access to those origins | yes, lists the sites | you must run in the background on specific sites |
host_permissions: ["<all_urls>"] | every site | loud, broad warning | almost never — avoid; prefer activeTab |
optional_permissions + chrome.permissions.request() | runtime opt-in inside a user gesture | shown only when requested | a feature only some users need; ask when they enable it |
declarativeNetRequest | static/dynamic rules block or modify requests, no request bodies seen | install-time warning | ad/tracker blocking — replaces blocking webRequest |
declarativeNetRequestWithHostAccess | same, but access granted per host instead of install-time | per-host | DNR scoped to granted hosts only |
Rule: write a one-sentence justification for every permission before you add it. If you cannot, drop it. activeTab covers more cases than people expect — try it first. (Source: developer.chrome.com "Declare permissions" + "declarativeNetRequest", accessed 2026-06-02.)
// Declarative — in manifest. Runs automatically on matching pages.
"content_scripts": [{
"matches": ["https://example.com/*"],
"js": ["content.js"],
"run_at": "document_idle"
}]// Programmatic — inject on a gesture. Needs "scripting" + activeTab or a host match.
chrome.action.onClicked.addListener(async (tab) => {
await chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ["content.js"],
});
});run_at: document_start (before DOM), document_end, or document_idle (default, after load). Pick document_start only if you must beat the page's own scripts."world": "MAIN") only when you must touch the page's own JS objects, and know it loses the isolation guarantee.chrome.userScripts.execute() exists (Chrome 135, Mar 2025) — see references/store-and-migration.md.chrome.storage.local (~10 MB, larger with unlimitedStorage) for most data; chrome.storage.sync (~100 KB, ~8 KB/item) only for small settings you want to follow the user across devices.setTimeout/setInterval are unreliable in the worker — it may be asleep when they fire. Use chrome.alarms for anything beyond a few seconds. (Source: developer.chrome.com "Migrate to a service worker", accessed 2026-06-02.)<script src="https://cdn...">, no eval-of-fetched-string. All executable JS must ship inside the package — bundle every dependency. This is enforced by MV3's CSP and by review. (Source: developer.chrome.com "What is Manifest V3", accessed 2026-06-02.)dist/ output — never the repo. No node_modules, no .git, no source maps you do not want public.(Source: developer.chrome.com "Register your developer account" + fee guide, accessed 2026-06-02.) For the full dashboard walkthrough, data-disclosure form, staged rollout, appeals, and the MV2→MV3 migration map, see references/store-and-migration.md.
| Anti-pattern | Why it breaks | Do instead |
|---|---|---|
background.scripts / persistent: true | MV2 shape; rejected, worker never registers | background.service_worker: "bg.js" (string) |
Listener added after an await | Chrome wakes the worker without your listener; event lost | register all listeners synchronously at top level |
| State in a global var | worker dies, var resets silently | persist to chrome.storage |
<all_urls> when a click suffices | scary install warning, review pushback | activeTab triggered by the toolbar click |
<script src="https://cdn…"> | remote code is banned in MV3 | bundle the dependency into the package |
setInterval for periodic work | fires only while worker is alive | chrome.alarms |
Zipping the repo / node_modules | bloated, leaks source, may fail review | zip only the built dist/ |
Auth token in a global / sync | lost on restart, or synced off-device | chrome.storage.local (or session for in-memory) |
| MAIN world by default | loses isolation, page can tamper | isolated world unless you must reach page JS |
Run scripts/verify.sh <dir> to lint a produced manifest.json against the MV3 invariants above.
© ericrisco, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 4 other files (scripts, references) in skills/chrome-extension of ericrisco/rsc-harness.
Open the folder on GitHubat commit e3d5b33
Chrome Extension next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Chrome Extension this skillericrisco/rsc-harness | 167 | — | ~2.7k | Automated safety check: Pass | MIT | |
| Wxtmengxi-ream/read-frog | 10k | — | ~500 | Automated safety check: Pass | GPL-3.0 | |
| Crxjssamber/cc-skills | 228 | — | ~2.6k | Automated safety check: Pass | MIT | |
| Browser Extension Devmobazha/mobazha-unified | 165 | — | ~2k | Automated safety check: Pass | MPL-2.0 | |
| Project Scaffoldingaiskillstore/marketplace | 430 | — | ~2k | Automated safety check: Pass | None | |
| Wxt Browser Extensionsvantezzen/skip-silence | 468 | 2 repos | ~1.9k | Automated safety check: Pass | MIT |
mengxi-ream/read-frog
Build browser extensions with WXT - a next-gen framework with file-based entrypoints, Vite builds, auto-imports, typed storage with migrations, content script UIs, and multi-browser MV2/MV3 support.
samber/cc-skills
CRXJS Chrome extension development — true HMR for popup, options, content scripts, side panels, manifest-driven builds, dynamic content script imports (?script, ?script&module), and defineManifest…
mobazha/mobazha-unified
Chrome/Brave 浏览器扩展开发执行器。覆盖 Ext-0 原型验证到 Ext-4 生态增强的全生命周期. An agent skill from mobazha/mobazha-unified.
aiskillstore/marketplace
IDE-grade project scaffolding wizard for creating new projects with comprehensive configuration.
vantezzen/skip-silence
WXT browser extension performance optimization guidelines. An agent skill from vantezzen/skip-silence.
MoonshotAI/kimi-code
Kimi Browser Extension(Kimi 浏览器扩展,原 Kimi WebBridge)lets AI control the user's real browser — navigate, click, type, read, screenshot, and interact with any website using the user's actual login…
ericrisco/rsc-harness
A skill your agent uses when designing or analyzing a controlled experiment — falsifiable hypothesis, sample size from an MDE, reading significance/CI/power, CUPED, or rescuing tests that won't go…
ericrisco/rsc-harness
A skill your agent uses when making a web UI conform to WCAG 2.2 Level AA — axe-core or Lighthouse a11y violations, keyboard operability, focus management, ARIA roles/names/live regions, contrast…
ericrisco/rsc-harness
A skill your agent uses when running or fixing paid acquisition on Google or Meta — campaign structure (Performance Max, Demand Gen, Search, Advantage+), platform-fit creative, budget/scaling rules…
ericrisco/rsc-harness
A skill your agent uses when measuring whether an LLM or agent system actually got better and gating merges on it: golden sets, fixing an inflated LLM-as-judge, scoring RAG (faithfulness, contextual…
ericrisco/rsc-harness
A skill your agent uses when a creative goal must become a finished media file: pick and order generative-media models per modality — AI voiceover, image-to-video clips, score — then glue them with…
ericrisco/rsc-harness
A skill your agent uses when instrumenting product or web analytics — GA4/PostHog SDK wiring, event taxonomy, funnels, double-counted events, consent gating, PII scrubbing.
Works with
Categories
A skill your agent uses when building or shipping a Manifest V3 browser extension and hitting its quirks — service worker dying and losing state, permission warnings, a Chrome Web Store rejection…. Chrome Extension is an agent skill from ericrisco/rsc-harness. Use when building or shipping a Manifest V3 browser extension and hitting its quirks — service worker dying and losing state, permission warnings, a Chrome Web Store rejection, content-script/worker/popup messaging, or an MV2-to-V3 migration.
Chrome Extension fits situations like: shipping a Manifest V3 browser extension and hitting its quirks — service worker dying and losing state; permission warnings; A Chrome Web Store rejection; content-script/worker/popup messaging.
Run `npx skills add ericrisco/rsc-harness --skill chrome-extension -a claude-code`. Or copy the skill folder (skills/chrome-extension in ericrisco/rsc-harness) into .claude/skills/chrome-extension in your project. Claude Code loads it when a task matches its description.
Run `npx skills add ericrisco/rsc-harness --skill chrome-extension -a codex`. Or copy the skill folder (skills/chrome-extension in ericrisco/rsc-harness) into .agents/skills/chrome-extension in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add ericrisco/rsc-harness --skill chrome-extension -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/chrome-extension, .gemini/skills/chrome-extension, .github/skills/chrome-extension and .opencode/skills/chrome-extension in your project.
Going by SKILL.md and its folder, Chrome Extension needs a shell for the scripts in its folder. Our summary lists: A Bash shell.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. The check reads SKILL.md only: the scripts in the folder are not scanned, so read them before running anything.
Chrome Extension is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 2.7k tokens (SKILL.md is roughly 11k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 1.3k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Chrome Extension: Wxt (mengxi-ream/read-frog, 10k stars), Crxjs (samber/cc-skills, 228 stars), Browser Extension Dev (mobazha/mobazha-unified, 165 stars) and Project Scaffolding (aiskillstore/marketplace, 430 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
ericrisco (a GitHub user) maintains it in ericrisco/rsc-harness, which has 167 GitHub stars. The repository holds 227 skills in this directory. The repository was last updated on October 7, 2026.
Source: ericrisco/rsc-harness on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.