Repository navigation
📝 新增 scriptcat-userscript Skill:指导 AI 编码代理编写页面/后台/定时脚本,并通过 sctl 安装与验证 - #1824
Merged
Merged
Conversation
- 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 不变)
- @grant none 下只有 GM_info/GM.info,没有 unsafeWindow(exec_script.ts 只注入 { GM, GM_info },unsafeWindow 仅在沙盒上下文 create_context.ts 设置) - cron 的 once 以「开始运行」计周期,失败的运行也占用本周期(lastruntime 在运行开始与每次状态更新时写入),失败重跑须用 CATRetryError - GM_foo 与 GM.foo 互通仅在名字完全一致时成立:@grant GM_xmlhttpRequest 不会暴露 GM.xmlHttpRequest(getGrantCandidates 只换前缀、区分大小写) - 每一行 @crontab 都会在运行时注册,安装校验和脚本列表只看第一行,任一行非法会停掉全部任务,因此改为「只写一行」 - 脚本体被包在 async 函数里,返回值总是 Promise;不 return Promise 时同步部分结束即记为完成,不会出现 "return not promise" 警告 - 解析 cron 的是 npm 包 cron(非 node-cron);UserConfig 的 min 不作用于 text;sctl grep 在多实例时同样报 SCRIPTCAT_AMBIGUOUS;page reload 已等待 load
- dev-loop.md:写之前用 sctl 侦察页面(snapshot/eval 试选择器与逻辑,debug network/request 找站点接口,鉴权信息不外泄) - dev-loop.md:用 @require file:/// 壳脚本迭代本地文件,审批一次即可(每次页面加载重读文件,需「允许访问文件网址」) - dev-loop.md:后台/定时脚本的可观测办法(重装即重跑 @background、测试时临时每分钟 cron、共享 @storageName 的探针页面脚本) - 新增 references/patterns.md:等待元素、SPA 路由切换、带记忆的菜单开关、跨域请求、样式、多语言、带重试的后台运行 - SKILL.md 加入口链接,README 结构表与已知限制同步
- scripts/check.mjs:零依赖静态检查,按运行时规则报告未声明 grant 的 API 调用(含 depend 一并注入的 API)、@grant none 下用 GM API/unsafeWindow、none 与其他 grant 混写、页面脚本缺 @match/@include、后台脚本用 GM_getTab 系列、cron 字段/once/空格写法错误;警告未知 grant、后台脚本无顶层 return、多行 @crontab、@run-at/@early-start 不生效、请求域名不在 @connect 也不在 @match 下;有 error 时退出码 1 - scripts/stub.mjs:从本地脚本生成 @require file:/// 壳脚本(保留头部与 UserConfig),配合本地热重载 - check.test.mjs 对照 SUPPORTED_GRANTS 守卫内置 grant 清单;vitest 的 scripts 项目纳入 skills/**/*.test.mjs - SKILL.md、dev-loop.md、README 补充用法
3 tasks
…间接办法 - dev-loop.md:新增 sctl run / sctl logs 用法与注意事项(只限后台/定时脚本、exit 0 看状态、不经审批但有真实副作用、 手动运行不排重试、debug 级 GM_log 不入库、与编辑器运行或 cron 交叠可能误认),旧 sctl / 旧脚本猫的识别与回退 - background.md:删去无代码依据的「编辑器运行不同步值、菜单不触发」,改为与 sctl run 同一通路 - SKILL.md、api.md:GM_log 的 debug 级别在正式版不入库 - README 已知限制:依赖 scriptscat/sctl#17 与 #1825 的版本
- 对象字面量的键(`{ GM_getValue: 1 }`)不再算 API 调用;三元表达式里的仍算
- return/typeof/case/throw/in/of/void/delete/new 等关键字后的 `/` 按正则开头处理,正则里的引号不再吞掉同一行后面的调用
- @match 带通配子域或有多个域名时,请求其中一个域名不再视为同域(运行时只放行与页面完全相同的域名,见 getConnectMatched)
- connect-missing 文案区分:脚本已有 @connect 时 GM_xmlhttpRequest 会被拒绝,没有任何 @connect 时询问用户;仍为 warn,因为检查只看 URL 字面量,无法确定它是否真被请求
- 某个文件读不了时继续检查其余文件,最后以退出码 2 结束
- check/stub 命令行用例真实 spawn node,按 scripts/check-i18n.test.mjs 的做法单独设超时,避免满载时超出 340ms 单测预算
- 脚本列表只显示运行状态(运行中/错误/已停止)与定时脚本下次运行时间,GM_log 与错误信息改指向日志页(按 name/uuid 过滤)或 sctl logs - @connect:脚本已有 @connect 时未列入的域名被 GM_xmlhttpRequest 直接拒绝(之前允许过的除外),只有完全没有 @connect 才询问;native GM_download 总是询问;仅与页面完全相同的域名免声明 - CATRetryError 在 @grant none 下不存在;没有任何 @grant 行的脚本同样运行在沙盒里 - 同 @name/@namespace 不能在页面脚本与后台/定时脚本之间切换(类型不符被拒),临时页面脚本需另起名字 - GM_addElement 注入 <script> 在 Firefox 仍受页面 CSP 约束;urlchange 依赖 Navigation API,onurlchange === null 只说明授权生效 - dev-loop:补 get/run/logs 的 --scriptcat 行为、sctl run 的实际状态行与 JSON 字段(含 truncated)、@run-in 不符返回 INVALID_REQUEST;壳脚本示例的说明移到注释行;后台壳脚本可用 sctl run / 编辑器运行免审批重读本地 @require
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Checklist / 检查清单
N/A — 无关联 issue。扩展运行时代码没有改动;附带的两个 Node 工具脚本有单测,验证方式见下方「验证」。
背景
AI 编码代理(Claude Code、Codex 等)写脚本猫脚本时,常按 Tampermonkey 的习惯来写,在脚本猫特有的地方出错:后台/定时脚本没有页面、Promise 生命周期、
CATRetryError、oncecron、==UserConfig==、@grant none的语义等。sctl 已经能把脚本装进用户的真实浏览器,但代理还缺一套「侦察 → 编写 → 安装 → 验证 → 迭代」的固定流程,也缺一个能在安装前发现常见错误的工具。本次改动
新增
skills/scriptcat-userscript/。这是一个 Agent Skill,不进入扩展构建产物。SKILL.md:入口,包括选脚本类型、脚本骨架(与「新建脚本」模板一致)、按权限/时机/存储/后台/用户配置分组的易错规则,以及有 sctl 和没有 sctl 两种情况下的验证分流。references/api.md:按src/pkg/utils/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 安装与验证流程,另有三节:@require file:///…的壳脚本,之后改本地文件、reload 页面即生效,不必每次审批。page snapshot/page eval找选择器、试跑逻辑;用debug network/debug request --body找站点接口,改用GM_xmlhttpRequest。@storageName的临时探针页面脚本把值打到 console;用 disable/enable 或临时 crontab 触发运行。references/patterns.md:常用写法(等待元素、SPA 路由、菜单开关持久化、跨域请求、样式注入、本地化、后台脚本 Promise + 重试)。scripts/check.mjs:零依赖静态检查。能发现未声明 grant 的 API 调用、@grant none下使用 GM API 或unsafeWindow、cron/once写法错误、多行@crontab、后台脚本使用标签页 API、请求的域名缺@connect等问题;有 error 时以 exit 1 退出。scripts/stub.mjs:从本地脚本生成热重载用的壳脚本。vitest.config.ts:把skills/**/*.test.mjs加进scripts(node 环境)测试项目。已知限制
sctl run/sctl logs,依赖 ✨ 新增 sctl run / sctl logs:触发后台/定时脚本运行并读取脚本运行日志 sctl#17 与 ✨ 外部接入:支持 sctl 触发后台/定时脚本运行并读取脚本运行日志 #1825。旧版 sctl 不认识run,旧版脚本猫返回METHOD_NOT_FOUND;遇到这两种情况,Skill 退回探针脚本等间接办法。建议在 ✨ 外部接入:支持 sctl 触发后台/定时脚本运行并读取脚本运行日志 #1825 和 sctl#17 之后合并本 PR。debug级别的GM_log写入日志,Skill 已提示把需要读取的内容记到info及以上。check.mjs内置的 grant 清单有单测对照SUPPORTED_GRANTS守卫;伴随注入表(DEPENDS,抄自gm_api的depend声明)没有自动守卫,修改时需要手工同步(README 已注明)。check.mjs靠文本匹配找 API 调用(会先去掉注释和字符串),不做完整的 JS 解析。建议审查重点
SKILL.md第 3 节和background.md中关于@grant none、once、多行@crontab、返回值语义的描述(这几处是对照代码修正过的)。check.mjs的规则:哪些按 error 报、哪些按 warn 报。references/dev-loop.md中 sctl 命令和退出码是否与 sctl v0.3.0 一致。验证
基于 base
6c0459b8…headbef426d7,git diff --stat只涉及skills/scriptcat-userscript/下 11 个文件和vitest.config.ts(1 行)。npx vitest run skills/scriptcat-userscript:30/30 通过。npx vitest run --project scripts:87/87 通过。npx eslint skills/scriptcat-userscript/scripts vitest.config.ts、npx prettier --check skills/scriptcat-userscript vitest.config.ts:通过。check.mjs,上述各类错误都报出来了,exit 1;stub.mjs输出的头部保留原有元数据并追加@require file:///…。origin/main源码,部分用一次性 vitest 探针实测,探针没有提交):@grant none下unsafeWindow为 undefined(exec_script.ts:56只注入GM/GM_info)。once按lastruntime门控,运行开始时就写入,失败的运行也算一次(sandbox/runtime.ts:148,200)。@crontab都会被注册(sandbox/runtime.ts:270)。async function里,所以「return not promise」分支不会触发。@grant GM_xmlhttpRequest不会带出GM.xmlHttpRequest:grant.ts只互换前缀,而且区分大小写。docs/cli.md与skills/sctl(多实例--scriptcat/SCRIPTCAT_AMBIGUOUS退出码 3,tabs open --background返回{"tabId"},edit --replace/--with)。skills/下全部链接和锚点都能解析。ad709853只改了 Markdown:prettier 通过,skills/下相对链接都能解析。sctl run/sctl logs的命令、参数、输出、退出码,以及 debug 级别不入库这一点,都来自 ✨ 外部接入:支持 sctl 触发后台/定时脚本运行并读取脚本运行日志 #1825 / sctl#17 的真实验证(隔离的 daemon 加无头 Chromium)。tsc --noEmit没跑:本地 worktree 的依赖缺fake-indexeddb(migrate.test.ts),与本改动无关;vitest.config.ts不在 tsconfig 范围内。审查修复(2026-11-05)
又做了一轮独立审查,并对照代码订正:
bef426d7:sctl logs。@connect:脚本只要声明了任何@connect,未列出的域名就会被拒绝,只有完全没写@connect时才会询问用户。@grant none下没有CATRetryError。@grant也运行在沙盒里。onurlchange依赖 Navigation API。GM_addElement在 Firefox 下的 CSP 例外,与api.md保持一致。sctl run的输出文案和 run-in 报错;壳脚本示例;后台壳脚本用sctl run迭代(重新读取本地文件这一点来自代码,未在浏览器里验证)。4ead49a5check.mjs:@match通配时,不再把请求当成同域。scripts/check-i18n.test.mjs的 15 秒超时。验证(head
bef426d7):Skill 测试 35/35 通过,eslint 和 prettier 通过,相对链接和锚点检查通过;两处误报/漏报都用探针复现过,修复后结果正确。