Install
$ agentstack add skill-organvm-a-i-skills-browser-extension-patterns ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Browser Extension Patterns
Build cross-browser extensions with Manifest V3 architecture.
Manifest V3 Structure
my-extension/
├── manifest.json # Extension manifest
├── background/
│ └── service-worker.js # Background service worker
├── content/
│ └── content-script.js # Injected into web pages
├── popup/
│ ├── popup.html # Popup UI
│ ├── popup.js # Popup logic
│ └── popup.css # Popup styles
├── options/
│ ├── options.html # Settings page
│ └── options.js
├── icons/
│ ├── icon-16.png
│ ├── icon-48.png
│ └── icon-128.png
└── _locales/ # Internationalization
└── en/messages.json
Manifest Configuration
{
"manifest_version": 3,
"name": "My Extension",
"version": "1.0.0",
"description": "Brief description of what it does",
"permissions": ["storage", "activeTab"],
"host_permissions": ["https://*.example.com/*"],
"background": {
"service_worker": "background/service-worker.js"
},
"content_scripts": [{
"matches": ["https://*.example.com/*"],
"js": ["content/content-script.js"],
"css": ["content/content-style.css"],
"run_at": "document_idle"
}],
"action": {
"default_popup": "popup/popup.html",
"default_icon": {
"16": "icons/icon-16.png",
"48": "icons/icon-48.png",
"128": "icons/icon-128.png"
}
},
"options_page": "options/options.html",
"icons": {
"16": "icons/icon-16.png",
"48": "icons/icon-48.png",
"128": "icons/icon-128.png"
}
}
Background Service Worker
// background/service-worker.js
// Installation
chrome.runtime.onInstalled.addListener((details) => {
if (details.reason === 'install') {
chrome.storage.local.set({ settings: { enabled: true, theme: 'light' } });
}
});
// Message handling from content scripts and popup
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
switch (message.type) {
case 'getData':
fetchData(message.url).then(sendResponse);
return true; // Async response
case 'updateBadge':
chrome.action.setBadgeText({ text: String(message.count) });
break;
}
});
// Alarm-based periodic tasks (replaces MV2 persistent background)
chrome.alarms.create('sync', { periodInMinutes: 30 });
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'sync') syncData();
});
Content Scripts
// content/content-script.js
// DOM manipulation on target pages
function enhancePage() {
const elements = document.querySelectorAll('.target-class');
elements.forEach(el => {
const badge = document.createElement('span');
badge.className = 'my-extension-badge';
badge.textContent = 'Enhanced';
el.appendChild(badge);
});
}
// Run when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', enhancePage);
} else {
enhancePage();
}
// Communicate with background
async function requestData(url) {
return chrome.runtime.sendMessage({ type: 'getData', url });
}
// Listen for messages from background/popup
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'getPageData') {
sendResponse({ title: document.title, url: location.href });
}
});
Storage Patterns
// Chrome storage API (synced across devices)
const storage = {
async get(key) {
const result = await chrome.storage.sync.get(key);
return result[key];
},
async set(key, value) {
await chrome.storage.sync.set({ [key]: value });
},
async getLocal(key) {
const result = await chrome.storage.local.get(key);
return result[key];
},
onChange(callback) {
chrome.storage.onChanged.addListener((changes, area) => {
callback(changes, area);
});
}
};
// Usage
await storage.set('settings', { theme: 'dark', enabled: true });
const settings = await storage.get('settings');
Popup UI
My Extension
Enabled
// popup/popup.js
document.addEventListener('DOMContentLoaded', async () => {
const settings = await chrome.storage.sync.get('settings');
document.getElementById('enabled').checked = settings.settings?.enabled;
document.getElementById('enabled').addEventListener('change', async (e) => {
await chrome.storage.sync.set({
settings: { ...settings.settings, enabled: e.target.checked }
});
// Notify content scripts
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.tabs.sendMessage(tab.id, { type: 'settingsChanged', enabled: e.target.checked });
});
});
Cross-Browser Compatibility
// Polyfill for Firefox WebExtensions API
const browser = globalThis.browser || globalThis.chrome;
// Feature detection
const isFirefox = typeof browser !== 'undefined' && browser.runtime?.getBrowserInfo;
const isChrome = typeof chrome !== 'undefined' && chrome.runtime?.id;
Firefox Manifest Differences
{
"background": {
"scripts": ["background/service-worker.js"]
},
"browser_specific_settings": {
"gecko": {
"id": "my-extension@example.com",
"strict_min_version": "109.0"
}
}
}
Permission Strategy
| Permission | When | Impact | |-----------|------|--------| | activeTab | Need current tab only | Low (user-triggered) | | storage | Need to save settings | Low | | tabs | Need tab URLs/titles | Medium | | host_permissions | Need page access | High (shows warning) | | `` | Need all page access | Very High (avoid if possible) |
Principle: Request minimum permissions. Use activeTab over broad host permissions when possible.
Anti-Patterns
- MV2 patterns in MV3 — No persistent background pages; use service workers and alarms
- `` permission — Request only the hosts you need
- Synchronous storage — Always use async
chrome.storageAPI - No error handling in messaging — Messages fail silently if receiver doesn't exist
- Heavy content scripts — Minimize injected code; communicate with background for heavy work
- No uninstall cleanup — Use
runtime.onInstalledto handle updates and cleanup
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: organvm
- Source: organvm/a-i--skills
- License: Apache-2.0
- Homepage: https://organvm.github.io/a-i--skills/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.