{
  "id": "chrome-extension-mv3",
  "type": "loadout",
  "title": "Chrome extension (Manifest V3)",
  "summary": "Manifest V3 rules for service workers, messaging and minimal permissions, plus a skill that gets your extension through Chrome Web Store review.",
  "description": "Agents trained on years of Manifest V2 tutorials will happily write background pages, remote code and\n\"<all_urls>\" permissions — all of which either break in Manifest V3 or get you rejected from the Chrome\nWeb Store. This loadout keeps your agent on MV3, keeps permissions tight, and walks through store\nsubmission so the review goes smoothly first time.\n",
  "highlights": [
    "Service worker lifecycle rules (no global state, use chrome.storage and alarms)",
    "Least-privilege permissions with activeTab and optional host permissions",
    "Clear message-passing pattern between content scripts, popup and service worker",
    "Store-prep skill: permission justifications, privacy disclosures, zip packaging"
  ],
  "version": "1.0.0",
  "license": "MIT",
  "author": {
    "name": "Agent Loadout",
    "url": "https://loadout.jnw-it.com"
  },
  "categories": [
    "browser-extension"
  ],
  "stacks": [
    "chrome",
    "javascript",
    "typescript"
  ],
  "files": [
    {
      "path": "AGENTS.md",
      "role": "instructions",
      "description": "Manifest V3 extension project rules.",
      "skill": null,
      "agent": null,
      "install_to": {
        "claude-code": "AGENTS.md",
        "codex": "AGENTS.md",
        "cursor": "AGENTS.md",
        "gemini-cli": "AGENTS.md",
        "copilot": "AGENTS.md"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/AGENTS.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/latest/AGENTS.md",
      "bytes": 3246,
      "lang": "markdown",
      "content": "# AGENTS.md: Chrome extension (Manifest V3)\n\n## Ground rules\n- This is a **Manifest V3** extension (`\"manifest_version\": 3`). Do not use MV2 APIs: no `background.page`,\n  no `background.persistent`, no `chrome.browserAction`/`pageAction` (use `chrome.action`), no blocking `webRequest` (use `declarativeNetRequest`).\n- **No remotely hosted code.** Every script the extension runs ships in the package. No `eval`, no `new Function`,\n  no `<script src=\"https://...\">`, no loading JS from a CDN. Fetching *data* (JSON) is fine.\n\n## Layout\n```\nmanifest.json\nsrc/background.ts    service worker\nsrc/content/*.ts     content scripts (run in web pages, isolated world)\nsrc/popup/           popup UI (popup.html + script)\nsrc/options/         options page\nicons/               16, 32, 48, 128 px PNGs\n```\n\n## Service worker lifecycle\n- The service worker stops when idle (after about 30 seconds) and restarts on events. **Global variables are lost.**\n  Persist state in `chrome.storage.local` (or `session` for per-browser-session data).\n- Register all event listeners synchronously at the top level of the worker, not inside `await`s or callbacks,\n  or they will be missed when the worker wakes.\n- Use `chrome.alarms` instead of `setInterval`/`setTimeout` for anything longer than a few seconds.\n- No DOM in the service worker (`document`, `window`, `localStorage` do not exist). Use an offscreen document if you truly need DOM APIs.\n\n## Permissions (least privilege — reviewers check this)\n- Prefer `activeTab` + `scripting` over broad host permissions: the extension gets access to the current tab only when the user clicks it.\n- If host access is needed, list specific origins, and prefer `optional_host_permissions` requested at the moment of use\n  with `chrome.permissions.request`.\n- Never add `<all_urls>`, `tabs`, `history`, `cookies` or `webRequest` \"just in case\". Every permission needs a one-line reason in `PERMISSIONS.md`.\n\n## Messaging\n- Content script ↔ service worker: `chrome.runtime.sendMessage` / `chrome.runtime.onMessage`. Popup → tab: `chrome.tabs.sendMessage(tabId, ...)`.\n- Messages are typed objects with a `type` field. Validate the shape on receipt; treat messages from content scripts as untrusted\n  (web pages can influence them).\n- To respond asynchronously from `onMessage`, `return true` from the listener and call `sendResponse` later.\n\n## Content scripts\n- Do not trust page data. Never insert page-controlled strings with `innerHTML`; use `textContent` or build elements.\n- Scope `matches` as narrowly as possible. Avoid running on every page if the feature is for one site.\n- Style injected UI in a Shadow DOM so page CSS cannot break it (and vice versa).\n\n## Dev loop\n- `npm run build` outputs to `dist/`. The human loads it at `chrome://extensions` → Developer mode → Load unpacked → `dist/`.\n- After changing `manifest.json` or the service worker, the extension must be reloaded on that page. Remind the human.\n- Debug the worker from `chrome://extensions` → \"service worker\" link; debug content scripts in the page's DevTools.\n\n## Before you say \"done\"\n`npm run build` passes, `manifest.json` is valid JSON with no new permissions you have not justified, and you have said what to reload and test.\n"
    },
    {
      "path": "CLAUDE.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Claude Code.",
      "skill": null,
      "agent": "claude-code",
      "install_to": {
        "claude-code": "CLAUDE.md",
        "codex": null,
        "cursor": null,
        "gemini-cli": null,
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/CLAUDE.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/latest/CLAUDE.md",
      "bytes": 391,
      "lang": "markdown",
      "content": "# CLAUDE.md\n\n@AGENTS.md\n\n## Claude Code notes\n- Skills from this loadout live in `.claude/skills/`: `extension-store-prep`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From the Agent Loadout \"Chrome extension (Manifest V3)\" v1.0.0 — https://loadout.jnw-it.com/loadouts/chrome-extension-mv3/ -->\n"
    },
    {
      "path": "GEMINI.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Gemini CLI.",
      "skill": null,
      "agent": "gemini-cli",
      "install_to": {
        "claude-code": null,
        "codex": null,
        "cursor": null,
        "gemini-cli": "GEMINI.md",
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/GEMINI.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/latest/GEMINI.md",
      "bytes": 392,
      "lang": "markdown",
      "content": "# GEMINI.md\n\n@./AGENTS.md\n\n## Gemini CLI notes\n- Skills from this loadout live in `.agents/skills/`: `extension-store-prep`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From the Agent Loadout \"Chrome extension (Manifest V3)\" v1.0.0 — https://loadout.jnw-it.com/loadouts/chrome-extension-mv3/ -->\n"
    },
    {
      "path": ".cursor/rules/manifest.mdc",
      "role": "rules",
      "description": "Rule applied when editing manifest.json.",
      "skill": null,
      "agent": null,
      "install_to": {
        "claude-code": null,
        "codex": null,
        "cursor": ".cursor/rules/manifest.mdc",
        "gemini-cli": null,
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/.cursor/rules/manifest.mdc",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/latest/.cursor/rules/manifest.mdc",
      "bytes": 831,
      "lang": "markdown",
      "content": "---\ndescription: Manifest V3 manifest.json rules\nglobs: \"**/manifest.json\"\nalwaysApply: false\n---\n\n- `\"manifest_version\": 3`. Background is `{ \"service_worker\": \"background.js\", \"type\": \"module\" }` — never `scripts` or `page`.\n- Use `\"action\"` for the toolbar button, not `browser_action`/`page_action`.\n- Adding any entry to `permissions` or `host_permissions` requires a justification line in `PERMISSIONS.md`. Prefer `activeTab`.\n- Prefer `optional_host_permissions` for sites the user opts into.\n- No remote URLs in `content_security_policy`. Do not loosen the default extension CSP.\n- `web_accessible_resources` lists only files a page genuinely needs, with narrow `matches`.\n- Bump `version` (dot-separated integers, e.g. `1.2.0`) on every store upload.\n- Keep `description` under 132 characters; it is shown in the store.\n"
    },
    {
      "path": "skills/extension-store-prep/SKILL.md",
      "role": "skill",
      "description": "Prepares a Chrome Web Store submission.",
      "skill": "extension-store-prep",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/extension-store-prep/SKILL.md",
        "codex": ".agents/skills/extension-store-prep/SKILL.md",
        "cursor": ".agents/skills/extension-store-prep/SKILL.md",
        "gemini-cli": ".agents/skills/extension-store-prep/SKILL.md",
        "copilot": ".agents/skills/extension-store-prep/SKILL.md"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/skills/extension-store-prep/SKILL.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/latest/skills/extension-store-prep/SKILL.md",
      "bytes": 2357,
      "lang": "markdown",
      "content": "---\nname: extension-store-prep\ndescription: Prepare a Manifest V3 Chrome extension for Chrome Web Store submission. Use when the user wants to publish, submit, package or release their extension.\n---\n\n# Chrome Web Store prep\n\n## 1. Audit the manifest\n- `manifest_version` is 3, `version` is bumped since the last upload, `name` and `description` (≤132 chars) are final.\n- Icons: 16, 32, 48 and 128 px PNGs exist and are referenced.\n- For each permission and host permission, write a one-sentence justification describing the user-facing feature that needs it.\n  If you cannot, recommend removing it. `activeTab` is preferred over host permissions; broad host permissions slow review.\n\n## 2. Check for common rejection reasons\n- **Remote code:** search the build for `eval(`, `new Function(`, remote `<script src=`, and imports from `http`. Any hit is a blocker.\n- **Single purpose:** the extension does one clearly described thing. Flag unrelated features.\n- **Obfuscation:** minified is fine, obfuscated is not. Do not enable obfuscating build plugins.\n- **Data use:** list every piece of user data the extension reads, stores or sends anywhere.\n\n## 3. Write the listing material into `store/`\n- `store/description.md`: what it does in the first sentence, then features, then a privacy line.\n- `store/permissions.md`: each permission with its justification (pasted into the dashboard's Privacy tab).\n- `store/privacy.md`: data collected, why, where it goes, retention. If nothing leaves the device, say so plainly.\n  A privacy policy URL is required if the extension handles personal or sensitive data — the human must host it somewhere.\n- Screenshot list: tell the human to capture at least one 1280×800 (or 640×400) screenshot, plus a 440×280 small promo tile.\n\n## 4. Package\n```sh\nnpm run build\ncd dist && zip -r ../extension-$(node -p \"require('./manifest.json').version\").zip . -x \"*.map\" && cd ..\n```\n`manifest.json` must be at the root of the zip. Exclude source maps and dev files.\n\n## 5. Hand over\nTell the human the zip path, then the dashboard steps they must do themselves: upload at the Chrome Web Store Developer\nDashboard, fill in Store listing and Privacy tabs from the `store/` files, choose visibility, submit for review.\nA one-off developer registration fee applies to new accounts. Do not claim the extension has been submitted.\n"
    }
  ],
  "requires": {
    "tools": [
      "node",
      "chrome"
    ],
    "env": [],
    "mcp": []
  },
  "tested": {
    "status": "untested",
    "last_run": null,
    "fixture": null,
    "per_agent": {
      "claude-code": "untested",
      "codex": "untested",
      "cursor": "untested",
      "gemini-cli": "untested",
      "copilot": "untested"
    }
  },
  "security": {
    "scanned": true,
    "scanner": "intake-precheck/1",
    "verdict": "clean",
    "network": false,
    "scripts": [],
    "notes": "Instructions only."
  },
  "featured": false,
  "deprecated": false,
  "last_checked": "2026-10-15",
  "published": "2026-10-15",
  "updated": "2026-10-15",
  "compatibility": {
    "claude-code": "native",
    "codex": "native",
    "cursor": "native",
    "gemini-cli": "native",
    "copilot": "native"
  },
  "counts": {
    "rules": 2,
    "skills": 1,
    "mcp": 0
  },
  "url": "https://loadout.jnw-it.com/loadouts/chrome-extension-mv3/",
  "api_url": "https://loadout.jnw-it.com/api/loadouts/chrome-extension-mv3.json",
  "verification": {
    "level": "verified-install",
    "status": "verified",
    "label": "Verified install",
    "verified": true,
    "checked_at": "2026-10-16T22:54:03Z",
    "commit": "ad36e852567aa3c87821d2a9e2fe4052fd983374",
    "content_current": true,
    "agent_tests": "not-run",
    "counts": {
      "pass": 9,
      "fail": 0,
      "skip": 0,
      "not_applicable": 1
    },
    "meaning": "Level 1: automated checks on the files (parse, install paths, MCP config, safety scan, references, size, licence, starter build). No AI agent was run.",
    "checks": [
      {
        "id": "listing",
        "name": "Listing",
        "status": "pass",
        "summary": "Schema valid; 5 files declared, none missing or extra",
        "details": [
          "tested.status: untested (agent tests are separate from Level 1)"
        ]
      },
      {
        "id": "parse",
        "name": "Files parse",
        "status": "pass",
        "summary": "All 5 files parse; front matter valid",
        "details": []
      },
      {
        "id": "adapters",
        "name": "Adapters",
        "status": "pass",
        "summary": "CLAUDE.md and GEMINI.md import AGENTS.md and are up to date",
        "details": [
          "CLAUDE.md: matches the generator",
          "GEMINI.md: matches the generator"
        ]
      },
      {
        "id": "install-paths",
        "name": "Install paths",
        "status": "pass",
        "summary": "Correct paths for all 5 agents; layouts re-discovered",
        "details": [
          "Claude Code: 3 files, 1 skill in .claude/skills/",
          "Codex CLI: 2 files, 1 skill in .agents/skills/",
          "Cursor: 3 files, 1 skill in .agents/skills/, 1 rule",
          "Gemini CLI: 3 files, 1 skill in .agents/skills/",
          "GitHub Copilot: 2 files, 1 skill in .agents/skills/"
        ]
      },
      {
        "id": "mcp",
        "name": "MCP config",
        "status": "n/a",
        "summary": "No MCP servers in this loadout",
        "details": []
      },
      {
        "id": "safety",
        "name": "Safety scan",
        "status": "pass",
        "summary": "Clean: no credentials, risky shell or hidden text",
        "details": [
          "Scanner intake-precheck/1: verdict clean; network no (listing says no)"
        ]
      },
      {
        "id": "references",
        "name": "References",
        "status": "pass",
        "summary": "No cross-file references to check",
        "details": []
      },
      {
        "id": "size",
        "name": "Size limits",
        "status": "pass",
        "summary": "5 files, 7.0 KB total; largest AGENTS.md 3.2 KB",
        "details": [
          "Limits: 30 files, 32 KB per file, 128 KB total, 500 lines per skill or rule"
        ]
      },
      {
        "id": "licence",
        "name": "Licence",
        "status": "pass",
        "summary": "MIT, by Agent Loadout; text in the repository LICENSE",
        "details": []
      },
      {
        "id": "starter-build",
        "name": "Starter build",
        "status": "pass",
        "summary": "Manifest V3 extension in the loadout's layout type-checks against @types/chrome, builds, and passes the loadout's manifest and remote-code rules",
        "details": [
          "Applied 6 loadout files at every agent's paths",
          "✓ install: npm ci --no-audit --no-fund",
          "✓ type-check and build: npm run build",
          "✓ manifest and package rules: node check-manifest.mjs",
          "Covers: Service worker with top-level listeners, state in chrome.storage, typed and validated messages, async sendResponse (AGENTS.md)",
          "Covers: Manifest rules from the Cursor rule: MV3, action, module service worker, permissions justified in PERMISSIONS.md, description ≤132 chars",
          "Covers: No eval, new Function or remote scripts in the built package",
          "Covers: Not covered: loading the extension in Chrome (branded Chrome no longer accepts --load-extension in CI)"
        ]
      }
    ]
  },
  "install": {
    "claude-code": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "CLAUDE.md",
          "target": "CLAUDE.md",
          "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/CLAUDE.md",
          "role": "adapter"
        },
        {
          "path": "skills/extension-store-prep/SKILL.md",
          "target": ".claude/skills/extension-store-prep/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/skills/extension-store-prep/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/chrome-extension-mv3/claude-code.zip"
    },
    "codex": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/extension-store-prep/SKILL.md",
          "target": ".agents/skills/extension-store-prep/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/skills/extension-store-prep/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/chrome-extension-mv3/codex.zip"
    },
    "cursor": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": ".cursor/rules/manifest.mdc",
          "target": ".cursor/rules/manifest.mdc",
          "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/.cursor/rules/manifest.mdc",
          "role": "rules"
        },
        {
          "path": "skills/extension-store-prep/SKILL.md",
          "target": ".agents/skills/extension-store-prep/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/skills/extension-store-prep/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/chrome-extension-mv3/cursor.zip"
    },
    "gemini-cli": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "GEMINI.md",
          "target": "GEMINI.md",
          "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/GEMINI.md",
          "role": "adapter"
        },
        {
          "path": "skills/extension-store-prep/SKILL.md",
          "target": ".agents/skills/extension-store-prep/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/skills/extension-store-prep/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/chrome-extension-mv3/gemini-cli.zip"
    },
    "copilot": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/extension-store-prep/SKILL.md",
          "target": ".agents/skills/extension-store-prep/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/chrome-extension-mv3/1.0.0/skills/extension-store-prep/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/chrome-extension-mv3/copilot.zip"
    }
  },
  "source_zip_url": "https://loadout.jnw-it.com/download/chrome-extension-mv3/source.zip"
}