Community编程与开发github.com

lenneTech/running-check-script

运行 package.json 的 check 脚本(支持 monorepo),自动修复直至绿色,并上报审计结果,以确保工作可运行;由 "pnpm run check" 触发。

running-check-script 是什么?

此技能是执行 lt-dev 工作流中 `package.json` 的 `check` 脚本的唯一权威来源。它确保项目的所有构建、类型检查、lint、测试(除非明确跳过)和依赖审计步骤均通过,并返回退出码 0。该技能是 monorepo 感知的:它首先查找所有定义 `check` 脚本的子项目,然后逐个运行并修复。它提供两种模式:**Advisory**(报告失败,但工作流继续)和 **Blocking**(任何失败都会停止工作流,防止推送、合并或 PR 创建)。其核心是自动修复循环:如果 `check` 失败,它会解析所有错误,逐个修复根本原因,然后重新运行,只要每次迭代都能减少错误计数就继续循环,无硬性上限。它通过详细的升级阶梯处理 `pnpm audit`/`npm audit`/`yarn audit` 发现的漏洞,批准项仅限走完所有可能修复路径后的无法解决的漏洞。该技能支持所有流行的包管理器(pnpm、npm、yarn),并包含针对 `oxlint --fix-suggestions` 等可能修改行为的 lint 工具的专门防护。它主要与 Claude Code 配合使用,但也适应 Cursor 和 Codex Agent 等存在 Monitor 或 Auto-fix 模式的平台,涉及 /lat-dev 系列的工具,包括 review、rebase、ship 和 ticket-cycle 等任务。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/lenneTech/claude-code/tree/HEAD/plugins/lt-dev/skills/running-check-script

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

运行 check 脚本

本技能是在 lt-dev 工作流中执行 package.json 中 check 脚本的唯一权威来源。每个需要保证项目可运行性的审查者、变基者或编排者都必须完全遵循此流程——在代理间重复规则会导致差异。

目标: 一次完全绿色的 check 运行(退出码 0)是任何审查或变基被视为完成的不容协商的先决条件。唯一可接受的遗留项是上游依赖漏洞,且该漏洞的完整修复升级阶梯已用尽。

何时使用此技能

调用者阶段触发条件模式
/lt-dev:review阶段 1.5在生成任何专业审查者之前Advisory
lt-dev:code-reviewer阶段 1.5在单次审查之前(如果编排者已运行则跳过)Advisory
lt-dev:test-reviewer(输入简报)遵守此处定义的跳过语义Advisory
/lt-dev:take-ticket步骤 8所有测试支柱均为绿色后Blocking
/lt-dev:git:ship步骤 1, 步骤 4b预检,以及变基后重新验证Blocking
/lt-dev:ticket-cycle通过 take-ticket + git:ship整个周期Blocking
lt-dev:branch-rebaser阶段 6.5在 lint/format 之后,测试之前Blocking

调用者模式

两种模式下的流程完全相同。唯一区别在于未解决错误时的处理方式,而正是这种区别体现了划分的意义:

  • Advisory — 调用者的任务是报告。出现红色 check 会被作为严重问题上报,但工作流继续,因为用户仍然希望获得其余审查结果。
  • Blocking — 调用者的任务是发布。出现红色 check 会停止工作流。不会推送任何内容,不会创建 MR/PR,也不会合并任何内容。

每个通向 MR/PR 或合并的路径都以 Blocking 模式运行。 红色 check 到达基分支会让整个团队付出代价:所有人的 CI 都变红,下一个 take-ticket 从损坏的代码上分出分支,而最终修复它的人不知道是哪个更改导致了问题。这就是为什么步骤 3 中的阶梯没有迭代上限,以及为什么步骤 5 的遗留项列表如此狭窄。

流程

步骤 1 — 发现 check 脚本

使用专用辅助脚本(位于 lt-dev 插件中):

bash "${CLAUDE_PLUGIN_ROOT}/scripts/discover-check-scripts.sh"

输出为 TSV 格式,每行一个项目:

<package.json 路径>\t<check 脚本>\t<包含测试:no|unit+api|yes>\t<包管理器>

includes_tests 值 — 调用者决定每个值的处理方式,发现过程本身不规定任何行为:

值含义Playwright 覆盖范围
no在 check 链中未检测到测试运行器。未覆盖。
unit+apicheck 链委托给 lt check.mjs 包装器,它运行单元(应用)+ API(api) 测试步骤,并故意跳过 Playwright(Playwright 通过 lt dev test / CI 运行 — 请参阅 scripts/check.mjs)。未覆盖 — 如果需要,Playwright 仍必须单独运行。
yes在 check 链中直接匹配 test、vitest、jest 或 playwright 模式(传递性地,一级复合解析)。范围由脚本自身定义。未知 — 如果需要 Playwright 覆盖,调用者应在本地验证。

该辅助脚本:

  • 使用 git ls-files "package.json" "**/package.json",因此它尊重 .gitignore 并跳过 node_modules
  • 通过 jq → node -e → grep+sed 回退链解析 JSON(即使未安装 jq 也能工作)
  • 解析单级复合脚本("check": "pnpm run ci" → 查找 ci)
  • 通过锁文件检测每个项目的包管理器(pnpm-lock.yaml → pnpm,依此类推;默认 pnpm)

如果辅助脚本未返回任何内容 → 没有项目定义 check → 跳过此技能其余部分。

步骤 1a — 无 oxlint 建议修复

oxlint --fix-suggestions 应用 oxlint 自己标记为可能改变行为的修复:其 no-console 建议会删除调用。--fix-dangerously 更进一步。当前模板两者均未传递,但来自旧模板的项目仍然在 check 包装器、lint:fix、lint-staged 或项目自己的 scripts/check.mjs 中传递它们。lt fullstack update 在根目录和应用中删除它们(仅限已跟踪的干净文件);API 项目保留自己的文件,因此要在那里命中,需手动处理或采用当前的 nest-server-starter 文件。在 nest-server(2026-09, claude-code PR #10)中观察到:一个提交钩子将一个 console.log 变成了空函数体,但仍然报告“通过”。在步骤 2 之前,查找使用了任一标志的地方;注释中的提及不算:

git grep -n -E "(^|[[:space:]\"'])--fix-(suggestions|dangerously)" -- '*package.json' '*.lintstagedrc*' '*lint-staged.config.*' '*scripts/check.mjs' \
  | grep -v -E '^[^:]+:[0-9]+:[[:space:]]*(//|/?\*|#)'

第二个过滤器删除注释行:当前的 nest-server-starter 包装器在注释中解释了为什么它只传递普通 --fix。

命中时,check 在无自动修复的情况下运行:

  • 将 lt 包装器作为其只读门运行:<pm> run check --no-fix(npm: npm run check -- --no-fix)。每个仍然传递这些标志之一的包装器已经知道 --no-fix。如果项目自己的链调用了 lint:fix,或者其自己的 scripts/check.mjs 是命中之一,则用 lint 代替 lint:fix 运行该项目的步骤。
  • 手动或使用普通 oxlint --fix 修复 lint 发现;照常使用项目的格式化工具格式化。
  • 在步骤 8 报告中命名每个命中(file:line)及其补救措施:根目录和应用使用 lt fullstack update,API 项目手动删除标志(或当前的 nest-server-starter 文件)。

步骤 2 — 为每个项目运行 check

对于每个发现的项目,cd 到其目录并使用检测到的包管理器运行 check 脚本:

cd "$(dirname <package.json 路径>)"
<包管理器> run check

捕获 stdout、stderr 和退出码。跟踪迭代计数器,从 1 开始。

步骤 3 — 自动修复循环(迭代直到真正绿色)

如果项目的 check 退出码非零:

  1. 解析当前运行的所有错误(类型检查、lint、构建、缺少导入、类型不匹配、未使用符号、审计发现等)
  2. 通过 Read + Edit 修复每个错误的根本原因。预先存在的错误也会被修复 — 无论错误来自此 diff 还是早已存在,对项目是否可运行没有区别,红色 check 同样会阻碍下一个人。范围边界不适用于可运行性。这里决定一切的唯一问题是“这个可以修复吗?”,而只要答案是肯定的,它就会被修复。
  3. 从头重新运行 check。永远不要信任部分状态。
  4. 继续迭代,直到满足以下终端条件之一:
    • (a) 绿色 — check 退出码 0 → 此项目完成
    • (b) 卡住 — 一次完整迭代未导致错误计数的净减少
  5. 无硬性迭代上限。只要每次迭代严格减少错误计数,就继续。目标是真正的绿色。

一个狭窄的例外,是关于冲突而非范围。 第 2 点成立:预先存在的错误会被修复,因为无论谁写的,红色 check 都会阻碍下一个人。第 2 点未涵盖的是另一个活跃会话正在进行中的未提交工作内部的错误 — 重塑这并非范围问题,而是两个会话同时编辑一个文件,当同级别的会话返回时,面对重写的重构,损失的时间比修复节省的还要多。当树是脏的,且 bash "${CLAUDE_PLUGIN_ROOT}/scripts/change-provenance.sh" 将失败路径归因于一个活跃的同级会话时:如果错误是琐碎且自包含的(缺少导入、格式违规、散落的 console.log),则修复它;否则将其报告为阻塞项,并注明作者,外加一个 SOLVED 携带诊断,以便他们能一步修复自己的工作。无人活跃,或无法归因:第 2 点不变。

卡住是改变方法的信号,而不是停止的许可。 当相同的修复被重试时,计数停止下降,因此下一次迭代必须以不同的方式处理错误:完整读取失败文件而不是围绕报告行打补丁,检查两个错误是否共享一个原因,查看工作正常的兄弟模块如何解决相同问题,单独运行失败步骤以查看被总吞的输出,检查是否属于步骤 6.5 到 6.8 中已知的危险之一。只有在真正不同的方法被尝试过后计数仍然维持时,步骤 5 中的遗留分类才适用 — 而在 Blocking 模式下,除了已用尽梯子的依赖漏洞外,一切仍会停止工作流。

步骤 4 — 审计发现:强制修复升级阶梯

当 pnpm audit / npm audit / yarn audit(由 check 调用)报告漏洞时,在将发现分类为“已接受”之前,请耗尽此阶梯。每一步后重新运行 check。

#步骤命令(pnpm / npm / yarn)
1更新到最新兼容版本pnpm update <pkg> / npm update <pkg> / yarn upgrade <pkg>
2自动修复pnpm audit --fix / npm audit fix / yarn audit --fix
3强制 semver-major 升级pnpm audit --fix --force / npm audit fix --force
4如果公告列表中有修复,则提升直接依赖到下一个主要版本pnpm add <pkg>@<major> / npm install <pkg>@<major> / yarn add <pkg>@<major>
5强制传递依赖版本pnpm.overrides / resolutions (yarn) / overrides (npm) 块在 package.json 中,然后重新安装
6提升消费者,而非存在漏洞的包引入漏洞的直接依赖,以及如果框架固定了它则提升 lt 框架(@lenne.tech/nest-server / @lenne.tech/nuxt-extensions)— 完整的 /lt-dev:maintenance:maintain 是彻底形式
7修补消费者 — 仅满足以下条件pnpm patch <pkg> → patchedDependencies
8替换包如果已废弃,切换到受维护的替代品

步骤 6 是经常被跳过的梯级,但它通常是最有效的。 步骤 1 到 5 都向前推动存在漏洞的包。当这些失败时,原因通常不是存在漏洞的包本身 — 而是消费者通过固定一个有漏洞的范围或使用已修补的主要版本删除的 API 来阻碍它。所以要问的问题是“谁把这个版本留在这里?”,答案是直接依赖,或一个覆盖无法击败其固定的框架。覆盖是一种固定,而固定严格不如真正的版本提升:它使树与上游测试内容产生分歧,并悄无声息地腐烂。在有真正版本提升的地方进行版本提升。

步骤 7(修补)是有意狭窄的,狭窄本身就是重点。 当修补的主要版本仅加载失败时 — 一个被删除的默认导出,一个重命名的入口点 — 一行补丁看起来像是正确关闭了公告而不是压制它。有时确实如此。但首先必须满足三个条件,而第三个通常是失败的:

  1. 补丁针对存在漏洞的包中的稳定路径,并固定到一个确切的版本。破坏补丁的不是 dist/ 本身 — 发布的包只包含 dist/ 目录 — 而是版本之间移动的路径。dist/chunks/<hash>.mjs 在下一次构建中被重命名,补丁随后对团队中的每个人都失败;dist/index.mjs 是包自己的入口点并保持不变。命名了压缩符号的补丁结合版本固定仍然是可接受的,因为固定将风险转化为响亮的 pnpm install 失败,而不是静默的误修补。修补存在漏洞的依赖,而不是恰好导入它的消费者 — 参见下面的度案例,其中第一次评估检查了消费者的块并因此拒绝了该梯级。
  2. 权衡生产闭包与维护成本。补丁是对每个未来版本提升的持续义务,因此对于无法到达生产的开发工具路径,它可能花费的比它带来的更多 — 在一个单一项目中。一个模板则翻转了这种算法:每个生成的项目继承一个补丁,而遗留项则必须在每个项目中重新论证。这是一个判断,而非否决;条件 1 和 3 是硬性条件。
  3. 加载不等于工作。一个删除了导出的主要版本通常也改变了行为,因此修补后的导入使模块加载,同时让消费者针对一个从未测试过的 API 运行。如果你不能说在那个主要版本中还有什么其他变化,以及为什么这里不重要,那么补丁就是一个穿着修复外衣的猜测。

修补存在漏洞的包,而不是消费者。 这是决定梯级是否有效的区别,并且很容易出错,因为错误指向消费者。度案例,lt-crm 2026-10-06:@nuxt/[email protected] 执行 import Git from 'simple-git',将 simple-git 提升到仅有的已修复行(4.x,它删除了那个默认导出)使 nuxt prepare 以 does not provide an export named 'default' 中止。第一次评估检查了 devtools 自己的 dist/chunks/module-main.mjs,在那里找到了导入,并基于条件 1 — 一个编译后的块 — 拒绝了修补,这是关于错误文件的正确判决。补丁属于下一级,在 simple-git 自己的 dist/index.mjs 中,在那里添加一行将模块已经作为 simpleGit 导出的同一工厂重新导出为 default。加固的代码没有任何变化;只有模块的表面恢复。

有了这些,所有三个条件都成立:它是存在漏洞的包而不是消费者工件,它固定到一个确切的版本(patchedDependencies: { '[email protected]': … } 加上覆盖目标),以及 devtools 实际调用的 API — branch、revparse 等 — 都是 4.x 默认导出的,相同的 simple-git 实例,只是以不同的方式公开。

步骤 4a — 如果需要,使用 pnpm 覆盖

步骤 5 — 分类剩余问题

(项目仍为红色...)

其他过程步骤

步骤 6 — 报告格式

步骤 7 — 同步注意事项

步骤 8 — 为 Blocking 模式通过/失败设置状态

已知的交互(特定文件错误)

6.5 — 路径相关错误

6.6 — 嵌套 node_modules

6.7 — Stats 不匹配

6.8 — ENOENT for 配置文件

常见陷阱

  • 不要以非零代码过早退出,除非以 Blocking 模式运行。
  • 在补丁步骤之后重新运行以确认。
  • 修复行范围用 npx prettier --write 重新格式化。
  • 与 defineConfig 助手保持一致。

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

相关技能