关于 sanity-studio-upgrade
为 Sanity Studio v3 及更高版本的升级生成计划,适用于维护 Sanity 项目的开发者。它根据仓库实际情况筛选变更,并要求只读检查、核实版本与区分已确认事项和待答问题。
主要功能
- 01按仓库情况筛选升级变更
- 02从锁文件和注册表核实版本
- 03只读检查,不修改项目文件
- 04区分已确认事项与人工问题
- 05检测 v2 项目并停止规划
适用场景
- 01准备 Sanity Studio v3 及后续版本升级
- 02检查升级建议是否适用于当前代码库
- 03识别需要人工确认的迁移问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ sanity-io/ agent-toolkit --skill sanity-studio-upgrade安装后在 Claude Code 中输入/sanity-studio-upgrade确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/boundaries.mdboundary.v4.mdboundary.v5.mdboundary.v6.mddeprecations.mddetect.mdpackage-coupling.mdplugins.mdreport-template.mdversion-lookup.md详细介绍
Sanity Studio 升级规划器
为当前仓库中的 Sanity Studio 生成升级计划。
这份计划的价值在于删减。两个版本之间的所有破坏性变更已经列在变更日志中,而这对正在升级的用户并无帮助。此技能会生成一份精简得多的清单,只包含适用于此仓库的变更,以及只有人类才能回答的问题。
决定输出是否可信的三条规则
1. 绝不要凭记忆写出版本号。先查证,并确认该版本确实存在。
软件包版本每周都会变化。报告中的每个版本都必须来自仓库的锁定文件(当前状态)或实时注册表查询(目标版本)。如果无法查询,请在报告中说明,不要猜测。报告中写出过时的“最新”版本,比不提供计划更糟,因为读者会据此采取行动。
还有一种更糟糕的情况:假设某个软件包的版本号与另一个软件包同步,结果写出了从未发布过的版本号。这种版本无法安装。凡是要描述为“同步版本”或“与核心包版本一致”的版本,都必须先确认其确实存在。
API 名称也一样。凭记忆写出的导出项、导入子路径或函数签名都是猜测,而读者会把这些内容直接粘贴到编辑器中。请从软件包的 README 或 exports 映射中查证,或者在代码片段本身加上行内 // VERIFY: 注释。请参阅 plugins.md 的第 6 节和 report-template.md 的第 8 节。
2. 只读和报告,绝不修改。
检查文件,运行只读 Shell 命令,查询注册表。不要编辑 package.json、配置文件或源代码。由读者决定要改什么;如果规划器修改代码,就会被停用。如果用户之后明确要求你执行升级,那是另一项他们已同意的任务。
实际界限如下,因为对这条规则含糊其辞,会让 Agent 对本可安全运行的命令犹豫不决:
- 绝不允许:写入仓库中的任何文件、向仓库安装内容,或修改锁定文件。
pnpm install、npm install、npm ci和不带--no-install的npx都不允许。 - 可以:读取文件,以及进行网络只读操作,例如运行
npm view、获取变更日志,或在仓库外的临时目录中运行npm pack,之后再删除该目录。 - 建议读者执行,但不要由你执行:
pnpm install --lockfile-only可以用来确认依赖预测。应将它作为供读者运行的命令写入报告,并说明预期结果。不要自行运行,因为它会改写读者的锁定文件。
此技能唯一会写入内容的操作,是解包 tarball 以检查类型声明;plugins.md 的第 6 节说明了如何在项目外完成此操作。
3. 区分已查证的内容和推断。
每项发现都归入以下三类之一:已确认适用(你在仓库中找到了相应条件)、需要人工回答(无法从代码中判断),或不适用(完全省略)。绝不要用无法查证的内容凑数。诚实而简短的报告,胜过看似面面俱到的报告。
有一点很容易忽略,值得明确说明:**只读规划器没有依赖解析器,因此无法知道变更后依赖树会包含什么。**关于去重的预测,应作为预期写入报告,并附上验证命令,而不能作为已确认的发现。请参阅 package-coupling.md。
验证规则,一处说明
参考文件中会按编号引用这些规则,因此在此统一说明,不再在各处重复。以上内容对应 R1 至 R4;其余规则则针对这些规则可能出错的具体情况。
| 规则 | 适用位置 | |
|---|---|---|
| R1 | 绝不凭记忆陈述版本。当前状态以锁文件为准,目标版本通过实时查询注册表获取。 | version-lookup.md §1 |
| R2 | 读取并报告。不得更改仓库中的任何内容,不得安装任何内容,也不得修改锁文件。 | 以上,以及 plugins.md §6 |
| R3 | 区分已验证内容与推断内容。绝不声称变更后依赖树会包含什么;规划器没有解析器。 | package-coupling.md §6、plugins.md §5 |
| R4 | 每个推荐版本在写入报告前,都必须确认它已实际发布。 | version-lookup.md §3 |
| R5 | 每个“X 要求 Y”都必须注明所读取的清单,以及该清单对应的安装版本。要求不会跨主版本沿用。 | version-lookup.md §6 |
| R6 | 评估版本范围,绝不直接读取。凡包含 ||、< 或单个子句中有两个比较运算符的内容,都必须先交给 semver.satisfies 处理。 | version-lookup.md §6 |
| R7 | 导出列表中的名称不代表导出可用。检查其声明类型和任何 @deprecated 标记;never 表示已移除。 | plugins.md §6 |
R6 和 R7 之所以存在,是因为它们都曾导致自信却错误的具体建议:把实际兼容的 Node 版本报告为不受支持,以及将必须进行的代码修改归入“已满足”。执行这些检查成本很低;跳过时,错误却不会显现。
范围:v3 及更高版本
此技能用于规划从 Studio v3 开始的升级。Studio v2 不在适用范围内,必须立即停止处理。
原因并不是 v2 很难,而是从 v2 升级到 v3 属于 Studio 配置和插件层的重写,而非依赖升级:配置格式发生了变化,v2 插件和覆盖项所依赖的“parts”系统已不复存在,软件包经过了整合,而且 v2 时代的许多插件生态从未迁移。针对这个升级边界生成计划,只会列出听起来很有把握、但此技能无法验证的具体内容,然后交给即将为此投入数周时间的人。这是最糟糕的结果。
如果检测到 v2,请停止并报告,而不是继续规划。第 1 步会说明如何检测以及该怎么说。不要为 v2 项目编写计划文件。
流程
步骤 1:确认事实依据,并检查版本下限
阅读 references/detect.md 并按其中说明操作。该文件列出了要读取的文件、要运行的命令,以及要记录的事实。
最常见的错误是从 package.json 读取版本。插入符范围不等于已安装版本。请从锁文件中获取解析后的版本。
第二常见的错误是从错误的锁文件读取版本。一个代码库可能包含多个安装树,且它们的版本可能不一致。因此,请先确定计划针对哪个依赖树,从该依赖树中获取所有版本信息,并在报告标题中注明它。
如果此代码库中没有 Sanity Studio,请说明这一点并停止。
然后,在做任何其他事情之前检查版本下限。 如果解析后的 sanity 主版本低于 3,或者没有 sanity 包且项目依赖 @sanity/base 或 @sanity/desk-tool 等 v2 时代的包,或者存在 sanity.json 但不存在 sanity.config.*,则这是一个 v2 项目。请在此处停止,并告知读者:
- 你检测到了什么,以及相应证据
- 此技能适用于 v3 及更高版本,并说明为什么从 v2 迁移到 v3 属于不同类型的项目:需要重写配置和插件层,而不是升级版本
- 合适的起点是官方 v2 到 v3 迁移指南;读者应前往 Sanity 文档网站查找,而不是听你转述
- 每个 v2 时代的插件都需要单独检查兼容性,因为许多插件从未移植
- 升级到 v3 后,再次运行此技能即可规划剩余的升级跨度,这项工作会小得多
请提供有用的信息。说明原因并指明下一步的明确终止是好的回答;拒绝回答则不是。但不要把它软化成部分计划,也不要估算从 v2 到 v3 的工作量。
如果同时存在 sanity.json 和 sanity.config.*,则以解析后的 sanity 版本为准。如果一个除此之外为 v3 及更高版本的项目中遗留了 sanity.json,值得将其作为清理事项提及,但不应因此停止。
步骤 2:确定升级跨度
你需要两个版本:起始版本(解析后、已安装)和目标版本。
阅读 references/version-lookup.md,了解如何权威地查询当前版本和变更日志。
目标版本为 latest。 请推荐该版本并说明理由。只升级一小段意味着做完全部工作后仍然落后;更重要的是,当前插件版本会跟随当前核心版本,因此,使用较旧的核心版本搭配当前插件,通常会导致比最新版本更差的依赖树。报告目标版本的发布日期,并给出一条备用方案,让读者在应用你无法获知的约束时参考;这是信息披露,不是保留意见,也不会改变推荐。只有在读者明确提出约束时才偏离此建议,并为固定的版本指定复查日期。
在报告中明确写出升级跨度。v3 项目和从 v5.31 升级的项目完全不同,读者需要知道自己属于哪种情况。
步骤 3:只加载你会跨越的边界
先阅读 references/boundaries.md。这是一个简短索引:其中包含覆盖范围、两端边界的读取规则,以及标明跨度需要哪些边界文件的表格。
然后只读取这些文件。 边界内容按主版本拆分,每个版本对应一个文件,因此,跨越一个边界的跨度只需加载一个文件,而不是全部文件。从 5.31 升级到 v6 的项目需要读取索引、boundary.v6.md 和 deprecations.md,除此之外不需要读取其他内容:v4 和 v5 文件会占用上下文,并引入不适用的检查结果。
每个 boundary.v*.md 都包含进入该主版本时的变更,以及该主版本系列内部未声明的变更,因为跨越边界的跨度会落在上一系列版本中,需要同时了解这两类变更。
然后阅读 references/package-coupling.md。它适用于所有跨度。
第 4 步:获取参考资料未涵盖的内容
references/boundaries.md 声明了其范围内覆盖上下限的两项限制,必须同时遵守。
对于跨度中高于上限或低于下限的部分,获取相应的变更日志。项目起始版本低于下限时,需要获取两段内容:从当前版本到该下限,以及从上限到目标版本。经过筛选的中间部分直接使用。
下限和上限同样重要,而且下限更容易被忽略。没有下限,你就会把文件当作依据,用于它从未检查过的范围,并报告其中没有适用内容;这看起来像是经过核实的结论,实际上却是未经检查的空白。除非某个范围位于声明的上下限之内,或你亲自获取了该范围并得到实际内容,否则绝不要声称该范围没有问题。
这最后一条绝非赘言。低于下限的部分根本没有上游变更日志,因此获取结果几乎为空,获取结果为空不代表该范围没有问题。 boundaries.md 会说明这会影响哪些范围,以及应如何报告。在这种情况下,把沉默报告成“没有适用内容”,是这项技能最容易产生自信谎言的方式。
尤其要留意在 minor 版本中发布、却未标记为重大变更的破坏性变更。它们最容易让人措手不及,因为没人会逐一阅读跨越 40 个版本的 minor 版本说明。参考资料列出了已知示例;请假定还有更多。
第 5 步:测试适用性
对于每项候选变更,在仓库中查找其适用条件。如果无法从代码中确定,就将它放入人工确认问题部分,而不是调查结果中。
以下示例说明了两者的区别:
- “自定义身份验证提供程序现在会替换内置提供程序,而不是追加” → 用 grep 检查配置中是否有
auth块。存在或不存在。可以确定。 - “默认搜索策略已更改,结果会有所变化” → 这是否重要取决于编辑器如何搜索。无法确定。请提出问题。
优先选择你确实能够评估的条件。grep -r "data-slate" 是一项真实测试。“你们有复杂的自定义组件吗?”则不是。
第 6 步:解析依赖关系图
应用 references/package-coupling.md 中的说明。大多数升级问题都发生在这里,而变更日志无法告知你这些问题,因为它们取决于该项目直接依赖哪些包。
最重要的规则:对于项目直接依赖的任何 @sanity/* 包,检查目标 sanity 版本依赖的版本,并与之匹配,而不是使用该包自己的 latest。对于 @sanity/ui 这类共享包使用 latest,会让同一依赖树中出现两个主版本,从而导致重复上下文错误和样式缺失的组件,看起来像是彼此无关的 bug。
第 6b 步:评估插件
阅读 references/plugins.md,并在项目含有任何插件时遵循其中的说明;这种情况几乎总是存在。
相比 Studio 本身,插件更容易阻碍升级,因此需要单独检查,并在报告中单独列出。请牢记以下三点:逐一核实每个插件的兼容性,不要仅凭发布者是谁就假定其兼容;当插件不兼容时,让维护归属决定路径,而不是决定结论;并且,对你准备建议的任何代码片段,除了核实版本之外,还要单独核实其 API 结构,因为兼容版本仍可能已经重命名了你准备推荐的导出项。
第 7 步:撰写报告
严格遵循 references/report-template.md。除非用户指定其他路径,否则将报告写入仓库根目录下的 SANITY-UPGRADE-PLAN.md。
请写入文件,不要只打印到终端。升级计划通常需要传达给不在这台设备前的人:负责人、审查者、支持工程师。文件可以加入拉取请求,也可以粘贴到讨论串中;终端输出则不行。
值得明确说明的判断
建议按顺序升级,而不是一步到位。 在一次提交中跨越多个主版本,会让人无法确定故障的来源。建议在每个主版本的最后一个发行版处设置中间停靠点,并说明原因:这样可以隔离每一组变更。
但只有能够到达的停靠点才算数。每个停靠点都需要解析好对应的插件版本,因为如果某个插件只兼容较新的主版本,就可能导致无法安装某个中间版本。如果无法解析某个停靠点,应将其标记为暂定,而不要把整个序列说成已经验证。经过验证的停靠点宁少勿假。
跳过停靠点时,要说明代价。 跳过某个停靠点通常是正确的:如果某个边界唯一相关的变更只是提高 Node 版本下限,就不值得单独为它提交一次;如果某个停靠点只有强制引入重复主版本才能到达,那还不如不设停靠点。但这样一来,接下来的升级就会一次跨过两个边界,而这正是设置升级序列要避免的情况。只展示跳过的依据,却不说明后果的计划,会让人觉得这笔取舍没有代价。
所以要把两方面都说清楚:为什么跳过这个停靠点,以及合并后的升级若失败,问题可能来自任一边界,因此调试时需要二分定位,而不能只看计划。这一句就够了。读者看清这笔取舍后,才能决定是否接受。
如实评估工作量。 如果项目只有两个文件导入了 @sanity/ui,且没有自定义身份验证,就说明这次升级工作量很小。为了显得周全而夸大范围,只会浪费读者一周时间。同样,如果项目从 sanity/_internal 导入内容,或有大量自定义组件,就应说明这次升级工作量很大,而不是写出一份把难事说得轻而易举的计划。
让计划经得起快速浏览。 读者一定会快速浏览。一份跨越多个边界的计划可能长达几千字,而读者即将用一周时间完成这项工作,不会花一个下午仔细研读文档。因此,计划开头应让读者在一屏内掌握全貌,后续内容则作为参考,等工作推进到相应阶段再查阅;发现的问题应按读者需要采取的行动分组,而不是按变更来自哪个发行版分组。要特别避免这种情况:一项会悄无声息地导致故障的变更,夹在两个外观方面的变更之间,表格中视觉权重完全相同。长度不是问题,权重一视同仁才是。
为实际执行工作的工程师撰写,描述代码库,而不是团队。 这类计划通常由客户方的人员阅读,而他们往往不是当初做出当前决策的人。证据、细节和严重程度都应与事实所支持的程度完全一致:淡化真实的阻碍或真实的成本无济于事,工程师也看得出来。应删除的是暗含的评判。“部署流水线根据 caret 版本范围构建,而不是依据锁文件构建”和“当初配置这一切的人搞错了”表达的是同一项发现,但只有前者会促成行动。只在说明下一步该做什么时使用“你”。内容也应始终围绕升级:未使用的依赖不属于升级发现,而偏离主题、变成泛泛代码审查的计划,会把读者的注意力耗费在不会阻碍升级的事情上。
说明你无法查明的内容。 单体仓库中常有隐藏信息:例如,tsconfig.json 继承了仓库外的基础文件、工作区根目录提升了 React,或 CI 配置位于其他目录。如果某项必要事实无法查明,就在“需要人工确认的问题”一节中点名说明。“我无法读取 moduleResolution,因为你的 tsconfig 继承了仓库外的文件”很有用。默默略过则不可取。
不要对路线图作出承诺。 如果有人问某个已弃用的 API 会否恢复或移除,只需描述它当前有文档说明的状态,然后就此打住。切勿推测未来发行版的情况。
参考文件
| 文件 | 何时阅读 |
|---|---|
references/detect.md | 始终在第 1 步阅读 |
references/version-lookup.md | 始终在第 2 步和第 4 步阅读 |
references/boundaries.md | 始终在第 3 步阅读。索引:覆盖范围边界以及需要阅读哪些边界文件 |
references/boundary.v4.md | 第 3 步中,如果范围跨入 v4 或位于 v4 中,则阅读 |
references/boundary.v5.md | 第 3 步中,如果范围跨入 v5 或位于 v5 中,则阅读 |
references/boundary.v6.md | 第 3 步中,如果范围跨入 v6 或位于 v6 中,则阅读 |
references/deprecations.md | 第 3 步中始终阅读。内容简短,且不与任何边界关联 |
references/package-coupling.md | 始终在第 6 步阅读 |
references/plugins.md | 第 6b 步中,项目含有插件时阅读 |
references/report-template.md | 阅读第 7 步 |