Userscript Development
This file is a router. The binding rules live in its references. Acting on a step without reading that reference is acting without the skill.
Activation Contract
Load when the user mentions userscripts or any userscript manager (Violentmonkey, Tampermonkey, Greasemonkey, ScriptCat, Safari "Userscripts" app), .user.js files, metadata headers (@match, @grant, @run-at), or GM APIs; when they want JS/CSS running in a page: modify pages, hide elements, auto-fill forms, scrape data, shortcuts, request interception, SPA navigation; or when they want an EXISTING userscript improved — debugged, fixed, extended, refactored, or ported between managers.
Do NOT use for external browser automation frameworks (Selenium/Puppeteer/Playwright), browser extensions (WebExtensions/MV3), or server-side scripts.
Hard Rules
- Manager-neutral by default. Label every manager-specific API/header with its owner (e.g. "Tampermonkey-only") and feature-detect it (
typeof GM.getValue === "function"). Verify support against managers.md before claiming it.
- Prefer
GM.* promise APIs — Greasemonkey 4+ ships ONLY those (sync GM_* removed; storage limited to strings/numbers/booleans).
- Never invent APIs, signatures, or version numbers. Unverifiable claims stay marked UNVERIFIED.
- Scope tightly: specific
@match (never *://*/*), declared @connect per cross-origin host (enforced by TM only), HTTPS-only URLs, no embedded secrets, sanitise data before DOM insertion.
- Violentmonkey is the UI-workflow example (FOSS, stable dashboard) for install/dashboard steps only — this implies nothing about API precedence. Other managers appear as labeled table rows; tier-2 divergences live in manager-compat.md.
- Preserve existing behaviour when editing a user's script; relabel rather than erase manager-specific knowledge.
- Live-profile testing hygiene: never live-test a new version while an older installed version still matches the same
@match — let the user install/update the new version, reload the tab, then run the pre-flight pollution probe (testing.md — Live-Profile Interference). A @grant none script shares the page world with automation evals, so a stale or foreign active script silently pollutes every snapshot/eval measurement taken before detection.
Core Digest
Start here. Open a full reference only when the Execution Steps trigger fires.
| Manager |
API style |
Divergence |
Ref |
| Violentmonkey |
GM_* sync + GM.* promise (VM 2.12.0+) |
@inject-into auto default (tries page → content on CSP fall-back) |
managers.md §1–2 |
| Tampermonkey |
GM_* sync + GM.* promise |
Largest surface; @connect strictly enforced; Chrome MV3 (TM 5.2+) breaks GM_webRequest |
managers.md §1–2 |
| Greasemonkey 4+ (Firefox-only) |
GM.* promise ONLY (sync removed 4.0; storage primitives only) |
Always sandboxed (Xray); no @sandbox/@inject-into |
managers.md §2–4 |
| Safari "Userscripts" |
GM.* promise SUBSET |
Any @grant forces content world; no unsafeWindow |
managers.md §1–4 |
@run-at: set explicitly — TM defaults document-idle, VM/GM4+/Safari default document-end; document-body invalid in GM4, context-menu TM-only — details in header-reference.md + managers.md §3.
@grant/@sandbox/@inject-into: TM uses @sandbox raw|JavaScript|DOM (default raw); VM/Safari use @inject-into page|content|auto (VM default auto); GM4+ ignores both — see sandbox-modes.md + managers.md §4.
@grant none disables sandbox in TM and VM ≥2.32 (no GM.* except GM_info); no-grant = minimal sandbox in VM ≥2.32, enabled sandbox in TM — see sandbox-modes.md.
unsafeWindow needs @grant unsafeWindow in TM; available without grant in VM (<2.32) and minimal-sandbox in VM ≥2.32; absent in Safari — see sandbox-modes.md.
@match = Chrome match-pattern base everywhere (VM ≥2.10.4 adds .tld/extra wildcards); @include globs add .tld portably — see url-matching.md.
- Header templates (no-APIs
grant none + promise GM.* starter) live at top of header-reference.md.
- If digest conflicts with observed behaviour, or task touches edge cases, open the owning reference (managers.md/url-matching.md/sandbox-modes.md/header-reference.md) — see Execution Steps.
Decision Gates
| Need |
Portable default |
Manager caveat |
| Persistent settings |
await GM.getValue/setValue |
GM4+: primitives only; batch GM.getValues/setValues: TM 5.3+/VM 2.19.1+ |
| Cross-origin request |
GM.xmlHttpRequest (+ legacy GM_xmlhttpRequest) |
@connect enforced by TM only; stream/cookie TM-only; anonymous also VM since 2.10.1 |
| Inject CSS |
GM.addStyle; fallback createElement('style') |
Sync GM_addStyle: TM/VM only (removed in GM4) |
| SPA navigation |
History-API interception (pushState/replaceState patch + popstate) |
window.onurlchange: Tampermonkey-only |
| Page context |
unsafeWindow behind typeof guard |
Absent in Safari app |
| Menu / notification / clipboard |
Feature-detect; degrade silently |
Absent in Safari app; GM4 async-only |
| Injection timing |
Set @run-at explicitly |
Defaults differ: TM document-idle; GM/VM/Safari document-end |
Any fork not listed here: check managers.md first.
Touch → Reference Routing
Before writing code in an area, READ its reference first — no exceptions:
| Touch |
Read first |
| Claim manager support |
managers.md §2 API Support Matrix |
| Target a tier-2 manager (ScriptCat/AdGuard/FireMonkey/mobile) |
manager-compat.md |
Write @match/@include or URL logic |
url-matching.md |
| Persist settings/storage |
api-storage.md |
| Make cross-origin requests |
http-requests.md + common-pitfalls.md Pitfall 2 |
| Insert data into the DOM |
api-dom-ui.md + security-checklist.md §3 |
| Watch mutations/elements (Observer) |
patterns.md (Mutation Observation) |
| Handle SPA navigation |
patterns.md (SPA Navigation) + api-tabs.md (onurlchange) |
Call ANY GM.*/GM_* API |
its api-*.md / http-requests.md page — never write the call from memory |
| Debug a failing script |
debugging.md + testing.md + full common-pitfalls.md scan |
| Live-test in a real profile (agent-driven) |
testing.md (Live-Profile Interference + Non-headless / Real-Profile Testing) |
Execution Steps
Digest-first: start from Core Digest + Touch → Reference Routing. A full reference read is CONDITIONAL — open the file only when (a) observed behaviour conflicts with the digest, (b) the task touches edge cases (tier-2 manager, Firefox MV3/USER_SCRIPT world, cross-origin/@connect, CSP/injection world), or (c) you need depth beyond the digest.
- Scope: consult digest for
@match/@exclude + @run-at defaults; open url-matching.md only on trigger; set @run-at explicitly.
- Permissions: consult digest for
@grant/@sandbox/@inject-into choice; open sandbox-modes.md only on trigger (add full read when injection world or unsafeWindow/CSP matters).
- Headers: copy starter template at top of header-reference.md (digest pointer); open rest of that file only on trigger (e.g. new directive, SRI,
@webRequest).
- Implement: use patterns.md via Touch → Reference Routing only for areas you touch; open section-scoped, not cover-to-cover; guard manager-specific calls with feature detection.
- Verify: run Pre-Delivery Gates; read security-checklist.md section-scoped against the FINAL code (full read only on trigger — e.g. DOM insertion, cross-origin, storage).
- Deliver per Output Contract. TypeScript/bundler work: typescript.md only if touched.
Output Contract
Every delivered userscript includes:
- 1–2 sentence explanation.
- Install steps naming the TARGET manager; if the user never specified one, ask which manager they use instead of assuming.
- Safe customisation points (selectors, timeouts, domains).
- Every
@grant with one-line justification.
- Explicit per-manager notes for anything not portable.
Pre-Delivery Gates
All MUST pass before returning code.
Process gates — prove the reads happened:
Artifact gates:
References
Core: managers.md · manager-compat.md · header-reference.md · url-matching.md · sandbox-modes.md · patterns.md
APIs: api-sync.md · api-async.md (preferred) · api-storage.md · http-requests.md · web-requests.md · api-cookies.md · api-dom-ui.md · api-tabs.md · api-audio.md
Quality & tooling: common-pitfalls.md · publishing.md · debugging.md · testing.md · browser-compatibility.md · security-checklist.md · version-numbering.md · typescript.md
1---2name: userscripts-development3description: Trigger: userscript, Violentmonkey, Tampermonkey, Greasemonkey, userscript manager, .user.js, @match, @grant, GM.* APIs. Write and debug userscripts for ANY manager: page modification, element hiding, auto-fill, scraping, SPA handling.4license: MIT5---67# Userscript Development89> **This file is a router.** The binding rules live in its references. Acting on a step without reading that reference is acting without the skill.1011## Activation Contract1213Load when the user mentions userscripts or any userscript manager (Violentmonkey, Tampermonkey, Greasemonkey, ScriptCat, Safari "Userscripts" app), `.user.js` files, metadata headers (`@match`, `@grant`, `@run-at`), or GM APIs; when they want JS/CSS running in a page: modify pages, hide elements, auto-fill forms, scrape data, shortcuts, request interception, SPA navigation; or when they want an EXISTING userscript improved — debugged, fixed, extended, refactored, or ported between managers.1415Do NOT use for external browser automation frameworks (Selenium/Puppeteer/Playwright), browser extensions (WebExtensions/MV3), or server-side scripts.1617## Hard Rules18191. **Manager-neutral by default.** Label every manager-specific API/header with its owner (e.g. "Tampermonkey-only") and feature-detect it (`typeof GM.getValue === "function"`). Verify support against [managers.md](references/managers.md) before claiming it.202. **Prefer `GM.*` promise APIs** — Greasemonkey 4+ ships ONLY those (sync `GM_*` removed; storage limited to strings/numbers/booleans).213. **Never invent APIs, signatures, or version numbers.** Unverifiable claims stay marked UNVERIFIED.224. **Scope tightly:** specific `@match` (never `*://*/*`), declared `@connect` per cross-origin host (enforced by TM only), HTTPS-only URLs, no embedded secrets, sanitise data before DOM insertion.235. **Violentmonkey is the UI-workflow example** (FOSS, stable dashboard) for install/dashboard steps only — this implies nothing about API precedence. Other managers appear as labeled table rows; tier-2 divergences live in [manager-compat.md](references/manager-compat.md).246. Preserve existing behaviour when editing a user's script; relabel rather than erase manager-specific knowledge.257. **Live-profile testing hygiene:** never live-test a new version while an older installed version still matches the same `@match` — let the user install/update the new version, **reload the tab**, then run the pre-flight pollution probe ([testing.md](references/testing.md) — Live-Profile Interference). A `@grant none` script shares the page world with automation evals, so a stale or foreign active script silently pollutes every snapshot/eval measurement taken before detection.2627## Core Digest2829Start here. Open a full reference only when the Execution Steps trigger fires.3031| Manager | API style | Divergence | Ref |32| --- | --- | --- | --- |33| Violentmonkey | `GM_*` sync + `GM.*` promise (VM 2.12.0+) | `@inject-into auto` default (tries page → content on CSP fall-back) | [managers.md](references/managers.md) §1–2 |34| Tampermonkey | `GM_*` sync + `GM.*` promise | Largest surface; `@connect` strictly enforced; Chrome MV3 (TM 5.2+) breaks `GM_webRequest` | [managers.md](references/managers.md) §1–2 |35| Greasemonkey 4+ (Firefox-only) | `GM.*` promise ONLY (sync removed 4.0; storage primitives only) | Always sandboxed (Xray); no `@sandbox`/`@inject-into` | [managers.md](references/managers.md) §2–4 |36| Safari "Userscripts" | `GM.*` promise SUBSET | Any `@grant` forces `content` world; no `unsafeWindow` | [managers.md](references/managers.md) §1–4 |3738- `@run-at`: set explicitly — TM defaults `document-idle`, VM/GM4+/Safari default `document-end`; `document-body` invalid in GM4, `context-menu` TM-only — details in [header-reference.md](references/header-reference.md) + [managers.md](references/managers.md) §3.39- `@grant`/`@sandbox`/`@inject-into`: TM uses `@sandbox raw|JavaScript|DOM` (default `raw`); VM/Safari use `@inject-into page|content|auto` (VM default `auto`); GM4+ ignores both — see [sandbox-modes.md](references/sandbox-modes.md) + [managers.md](references/managers.md) §4.40- `@grant none` disables sandbox in TM and VM ≥2.32 (no `GM.*` except `GM_info`); no-grant = minimal sandbox in VM ≥2.32, enabled sandbox in TM — see [sandbox-modes.md](references/sandbox-modes.md).41- `unsafeWindow` needs `@grant unsafeWindow` in TM; available without grant in VM (<2.32) and minimal-sandbox in VM ≥2.32; absent in Safari — see [sandbox-modes.md](references/sandbox-modes.md).42- `@match` = Chrome match-pattern base everywhere (VM ≥2.10.4 adds `.tld`/extra wildcards); `@include` globs add `.tld` portably — see [url-matching.md](references/url-matching.md).43- Header templates (no-APIs `grant none` + promise `GM.*` starter) live at top of [header-reference.md](references/header-reference.md).44- If digest conflicts with observed behaviour, or task touches edge cases, open the owning reference ([managers.md](references/managers.md)/[url-matching.md](references/url-matching.md)/[sandbox-modes.md](references/sandbox-modes.md)/[header-reference.md](references/header-reference.md)) — see Execution Steps.4546## Decision Gates4748| Need | Portable default | Manager caveat |49| --- | --- | --- |50| Persistent settings | `await GM.getValue/setValue` | GM4+: primitives only; batch `GM.getValues/setValues`: TM 5.3+/VM 2.19.1+ |51| Cross-origin request | `GM.xmlHttpRequest` (+ legacy `GM_xmlhttpRequest`) | `@connect` enforced by TM only; `stream`/`cookie` TM-only; `anonymous` also VM since 2.10.1 |52| Inject CSS | `GM.addStyle`; fallback `createElement('style')` | Sync `GM_addStyle`: TM/VM only (removed in GM4) |53| SPA navigation | History-API interception (`pushState`/`replaceState` patch + `popstate`) | `window.onurlchange`: Tampermonkey-only |54| Page context | `unsafeWindow` behind `typeof` guard | Absent in Safari app |55| Menu / notification / clipboard | Feature-detect; degrade silently | Absent in Safari app; GM4 async-only |56| Injection timing | Set `@run-at` explicitly | Defaults differ: TM `document-idle`; GM/VM/Safari `document-end` |5758Any fork not listed here: check [managers.md](references/managers.md) first.5960### Touch → Reference Routing6162Before writing code in an area, READ its reference first — no exceptions:6364| Touch | Read first |65| --- | --- |66| Claim manager support | [managers.md](references/managers.md) §2 API Support Matrix |67| Target a tier-2 manager (ScriptCat/AdGuard/FireMonkey/mobile) | [manager-compat.md](references/manager-compat.md) |68| Write `@match`/`@include` or URL logic | [url-matching.md](references/url-matching.md) |69| Persist settings/storage | [api-storage.md](references/api-storage.md) |70| Make cross-origin requests | [http-requests.md](references/http-requests.md) + common-pitfalls.md Pitfall 2 |71| Insert data into the DOM | [api-dom-ui.md](references/api-dom-ui.md) + security-checklist.md §3 |72| Watch mutations/elements (Observer) | [patterns.md](references/patterns.md) (Mutation Observation) |73| Handle SPA navigation | [patterns.md](references/patterns.md) (SPA Navigation) + [api-tabs.md](references/api-tabs.md) (`onurlchange`) |74| Call ANY `GM.*`/`GM_*` API | its api-*.md / http-requests.md page — never write the call from memory |75| Debug a failing script | debugging.md + testing.md + full common-pitfalls.md scan |76| Live-test in a real profile (agent-driven) | [testing.md](references/testing.md) (Live-Profile Interference + Non-headless / Real-Profile Testing) |7778## Execution Steps7980Digest-first: start from Core Digest + Touch → Reference Routing. A full reference read is CONDITIONAL — open the file only when (a) observed behaviour conflicts with the digest, (b) the task touches edge cases (tier-2 manager, Firefox MV3/USER_SCRIPT world, cross-origin/`@connect`, CSP/injection world), or (c) you need depth beyond the digest.81821. **Scope**: consult digest for `@match`/`@exclude` + `@run-at` defaults; open [url-matching.md](references/url-matching.md) only on trigger; set `@run-at` explicitly.832. **Permissions**: consult digest for `@grant`/`@sandbox`/`@inject-into` choice; open [sandbox-modes.md](references/sandbox-modes.md) only on trigger (add full read when injection world or `unsafeWindow`/CSP matters).843. **Headers**: copy starter template at top of [header-reference.md](references/header-reference.md) (digest pointer); open rest of that file only on trigger (e.g. new directive, SRI, `@webRequest`).854. **Implement**: use [patterns.md](references/patterns.md) via Touch → Reference Routing only for areas you touch; open section-scoped, not cover-to-cover; guard manager-specific calls with feature detection.865. **Verify**: run Pre-Delivery Gates; read [security-checklist.md](references/security-checklist.md) section-scoped against the FINAL code (full read only on trigger — e.g. DOM insertion, cross-origin, storage).876. **Deliver** per Output Contract. TypeScript/bundler work: [typescript.md](references/typescript.md) only if touched.8889## Output Contract9091Every delivered userscript includes:92931. 1–2 sentence explanation.942. Install steps naming the TARGET manager; if the user never specified one, ask which manager they use instead of assuming.953. Safe customisation points (selectors, timeouts, domains).964. Every `@grant` with one-line justification.975. Explicit per-manager notes for anything not portable.9899## Pre-Delivery Gates100101All MUST pass before returning code.102103**Process gates** — prove the reads happened:104105- [ ] [common-pitfalls.md](references/common-pitfalls.md) sections covering every API/pattern touched were read THIS task.106- [ ] security-checklist.md was checked against the FINAL code, not from memory.107108**Artifact gates:**109110- [ ] Scoped `@match`; HTTPS resources; no secrets; DOM input sanitised.111- [ ] Every manager-specific API labeled AND `typeof`-guarded.112- [ ] Async `GM.*` preferred; sync fallback documented.113- [ ] Only declared-grant APIs called; version string present (`X.Y.Z`).114115## References116117Core: [managers.md](references/managers.md) · [manager-compat.md](references/manager-compat.md) · [header-reference.md](references/header-reference.md) · [url-matching.md](references/url-matching.md) · [sandbox-modes.md](references/sandbox-modes.md) · [patterns.md](references/patterns.md)118119APIs: [api-sync.md](references/api-sync.md) · [api-async.md](references/api-async.md) (preferred) · [api-storage.md](references/api-storage.md) · [http-requests.md](references/http-requests.md) · [web-requests.md](references/web-requests.md) · [api-cookies.md](references/api-cookies.md) · [api-dom-ui.md](references/api-dom-ui.md) · [api-tabs.md](references/api-tabs.md) · [api-audio.md](references/api-audio.md)120121Quality & tooling: [common-pitfalls.md](references/common-pitfalls.md) · [publishing.md](references/publishing.md) · [debugging.md](references/debugging.md) · [testing.md](references/testing.md) · [browser-compatibility.md](references/browser-compatibility.md) · [security-checklist.md](references/security-checklist.md) · [version-numbering.md](references/version-numbering.md) · [typescript.md](references/typescript.md)