From 082557eff0268a9ffbbaea51b1cf525b6d5e4ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=B8=80=E4=B9=8B?= Date: Fri, 30 Oct 2026 14:37:58 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=F0=9F=93=9D=20=E6=96=B0=E5=A2=9E=20scriptc?= =?UTF-8?q?at-userscript=20Skill=EF=BC=9A=E6=8C=87=E5=AF=BC=20AI=20?= =?UTF-8?q?=E7=BC=96=E7=A0=81=E4=BB=A3=E7=90=86=E7=BC=96=E5=86=99=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2/=E5=90=8E=E5=8F=B0/=E5=AE=9A=E6=97=B6=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=EF=BC=8C=E5=B9=B6=E9=80=9A=E8=BF=87=20sctl=20?= =?UTF-8?q?=E5=AE=89=E8=A3=85=E4=B8=8E=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SKILL.md:选脚本类型、骨架、按权限/时机/存储/后台分组的易错规则,有无 sctl 两条验证路径 - references/api.md:按 script_compat.ts 的 grant 白名单列出已实现的 GM/CAT API 与和 TM 的差异 - references/background.md:Promise 生命周期、CATRetryError 重试、cron 与 once 扩展、日志 - references/userconfig.md:==UserConfig== 格式(类型推断按 UserConfigPanel 实际行为) - references/dev-loop.md:sctl install → 后台标签页 reload → debug console / page eval 验证 → 重装或 sctl edit 迭代; 已在真实浏览器实测:用户脚本 console 可见、整文件 edit 生效、同 name+namespace 重装原地更新(uuid 不变) --- skills/scriptcat-userscript/README.md | 30 ++++ skills/scriptcat-userscript/SKILL.md | 156 ++++++++++++++++++ skills/scriptcat-userscript/references/api.md | 70 ++++++++ .../references/background.md | 87 ++++++++++ .../references/dev-loop.md | 95 +++++++++++ .../references/userconfig.md | 61 +++++++ 6 files changed, 499 insertions(+) create mode 100644 skills/scriptcat-userscript/README.md create mode 100644 skills/scriptcat-userscript/SKILL.md create mode 100644 skills/scriptcat-userscript/references/api.md create mode 100644 skills/scriptcat-userscript/references/background.md create mode 100644 skills/scriptcat-userscript/references/dev-loop.md create mode 100644 skills/scriptcat-userscript/references/userconfig.md diff --git a/skills/scriptcat-userscript/README.md b/skills/scriptcat-userscript/README.md new file mode 100644 index 000000000..6846df2dd --- /dev/null +++ b/skills/scriptcat-userscript/README.md @@ -0,0 +1,30 @@ +# scriptcat-userscript + +给 AI 编码代理(Claude Code、Codex 等)用的 Skill:指导编写、修改、调试脚本猫用户脚本——页面脚本(`@match`)、 +后台脚本(`@background`)、定时脚本(`@crontab`),并在接入 [sctl](https://github.com/scriptscat/sctl) 时 +把脚本装进用户的脚本猫、在真实浏览器里验证。 + +## 结构 + +| 文件 | 内容 | +|---|---| +| `SKILL.md` | 入口:选脚本类型、骨架、容易写错的规则、安装与验证的分流 | +| `references/api.md` | 脚本猫实现的全部 GM / CAT API(按 grant 分组),以及与 Tampermonkey 的差异 | +| `references/background.md` | 后台/定时脚本:Promise 生命周期、`CATRetryError` 重试、cron 与 `once` 扩展、日志 | +| `references/userconfig.md` | `==UserConfig==` 用户配置格式 | +| `references/dev-loop.md` | 用 sctl 安装 → 打开页面 → 看控制台 → 重装或 `sctl edit` 迭代的流程 | + +API 以 `src/types/scriptcat.d.ts` 和 `src/pkg/utils/script_compat.ts`(支持的指令与 grant 白名单)为准; +修改这两处时同步检查本 Skill。sctl 本身的操作规范在 sctl 仓库的 `skills/sctl`,这里只引用、不重复。 + +## 安装 + +```sh +ln -s "$(pwd)/skills/scriptcat-userscript" ~/.claude/skills/scriptcat-userscript +ln -s "$(pwd)/skills/scriptcat-userscript" ~/.codex/skills/scriptcat-userscript +``` + +## 已知限制 + +- sctl 无法触发后台/定时脚本运行,也读不到脚本猫的运行日志,这两类脚本只能由用户在脚本列表查看运行状态与 + `GM_log` 输出。 diff --git a/skills/scriptcat-userscript/SKILL.md b/skills/scriptcat-userscript/SKILL.md new file mode 100644 index 000000000..7cb323f61 --- /dev/null +++ b/skills/scriptcat-userscript/SKILL.md @@ -0,0 +1,156 @@ +--- +name: scriptcat-userscript +description: "Write, fix and debug userscripts for ScriptCat (Tampermonkey-compatible): page scripts that run on matching sites (@match), background scripts that keep running without a page (@background) and scheduled scripts (@crontab); metadata, @grant and the GM_* / GM.* / CAT_* APIs, user config, retries. When the sctl CLI is connected, install the script into the user's ScriptCat and verify it in their real browser. Use whenever the user wants a userscript, 油猴脚本, 后台脚本, 定时脚本, a .user.js file or a ScriptCat script written, changed or debugged. Not for developing the ScriptCat extension itself, and not for managing installed scripts without changing their code (that is the sctl skill)." +--- + +# Writing ScriptCat userscripts + +ScriptCat runs Tampermonkey-style userscripts and adds two kinds of its own: background scripts and scheduled +scripts, which run in the extension's background sandbox instead of a web page. Most Tampermonkey knowledge +carries over; the differences below are the ones that break scripts. + +**Authority.** The API surface is `scriptcat.d.ts` — in this repository `src/types/scriptcat.d.ts`, otherwise +. Working examples live in +. The developer docs at +can lag behind the types; when they disagree, trust the types. + +## 1. Pick the script type + +| The script must… | Type | Key metadata | +|---|---|---| +| Change, read or automate a web page | Page script | `@match` / `@include`, `@run-at` | +| Keep running while the browser is open, with no page (listeners, polling, a menu in the popup) | Background script | `@background` | +| Run on a schedule (check-in, sync, report, periodic fetch) | Scheduled script | `@crontab ` | + +Background and scheduled scripts have **no web page**: no `document` of any site, no `unsafeWindow` page, no +`GM_getTab`/`GM_saveTab`. To touch a page from the background, open it with `GM_openInTab` and let a page +script do the DOM work, or fetch data with `GM_xmlhttpRequest` instead of scraping a rendered page. + +## 2. Start from the right skeleton + +These match the templates ScriptCat's own "new script" uses. + +```js +// ==UserScript== +// @name Example page script +// @namespace https://example.com/your-namespace +// @version 0.1.0 +// @description What it does, in one line +// @author You +// @match https://example.com/* +// @grant none +// @noframes +// ==/UserScript== + +(function () { + "use strict"; + // ... +})(); +``` + +```js +// ==UserScript== +// @name Example background script +// @namespace https://example.com/your-namespace +// @version 0.1.0 +// @description What it does, in one line +// @author You +// @background +// @grant GM_xmlhttpRequest +// @grant GM_notification +// ==/UserScript== + +return new Promise((resolve, reject) => { + // ... resolve() when the work is really finished, reject(err) on failure +}); +``` + +A scheduled script is the background skeleton with `@crontab * * once * *` (or another expression) in place of +`@background`. Background and scheduled bodies are wrapped in a function by ScriptCat, so a top-level `return` +is legal there. + +## 3. Rules that break scripts when ignored + +**Identity and updates** + +- `@name` + `@namespace` identify a script. Keep both stable once the user has installed it; changing either + makes a different script. Bump `@version` on every change you hand back. + +**Permissions** + +- Every API needs a `@grant` line (`@grant GM_xmlhttpRequest`, …). An ungranted API is simply undefined at + runtime. Granting `GM_foo` also exposes `GM.foo` and vice versa. +- `GM_info` and `unsafeWindow` are always available without a grant; `@grant unsafeWindow` is harmless. +- `@grant none` runs the script without the sandbox and without any GM API. Use it only for scripts that need + nothing but the page. +- With any other grant the script is sandboxed: its `window` is not the page's. Reach page globals (a site's + own JS objects, variables it defines) through `unsafeWindow`. +- `GM_xmlhttpRequest`, `GM_cookie` and `GM_download` (native mode) ask the user before touching a host that is + not declared with `@connect `. Declare every host you request; `GM_cookie` requires it. + +**Page timing and SPAs** + +- `@run-at`: `document-start` (as early as possible), `document-end` (DOM parsed), `document-idle` (the + default; everything loaded), `document-body` (wait until `` exists), `context-menu` (run when the user + picks the script from the page's right-click menu). Only the first value counts, and an unknown value silently + falls back to the default. +- Add `@early-start` together with `document-start` when the script must beat the page's own scripts, and wait + for `CAT_scriptLoaded()` before relying on the full API. +- Single-page apps change the URL without reloading, so a page script runs once and then misses later views. + Listen with `@grant window.onurlchange` and `window.addEventListener("urlchange", e => …)`, and use a + `MutationObserver` for content that renders late. Never assume an element exists at injection time — wait + for it. +- `@noframes` keeps the script out of iframes; without it, a `*://*/*` match also runs inside every iframe. +- `@inject-into content` runs in the content-script world: unaffected by the page's CSP, but `unsafeWindow` + is then not the page's `window`. The default is `page`. +- `GM_addStyle` and `GM_addElement` bypass CSP; prefer them over creating `