@danibram/crap4ts

中文文档
0.6.0发布于 2026/05/28MITTypeScriptES Module构建来源已验证近 30 天下载 1.6万

C.R.A.P. (Change Risk Analysis & pretroductions) TypeScript / JavaScript 代码库的索引. 将环形复杂度与测试覆盖度结合起来,形成地表风险到变化代码.

中文使用文档
文档目录29 节
  1. 现场实例
  2. Diff 假设
  3. 莫诺雷波布局
  4. v0.6 特性
  5. 30秒设置
  6. 手动快速启动
  7. 产出
  8. 记者
  9. 热点(v.6
  10. 认知复杂性(v0.6)
  11. 压制职能
  12. 配置
  13. Per-path 覆盖
  14. CLI 旗帜
  15. 覆盖范围格式
  16. 解释分数
  17. 基线工作流程(v0.3)
  18. PR-bot 注释
  19. 单曲
  20. 工作空间发现
  21. 合并每包的覆盖率
  22. GitHub 代码扫描( SARIF)
  23. CI 整合
  24. GitHub 动作
  25. 一般 CI( 仅退出代码)
  26. 如何运作
  27. 限制
  28. 鸣谢
  29. 许可证

废话4t

一个代码质量的门,你可以实际运送. 并不要求你修复遗产——只是阻止你把事情弄得更糟.

npm version license

任何代码质量工具最难卖的是 "我们已经有14,000个发现 从我们尝试的最后一个灯, 你真的要求我 修复所有之前,我可以合并?"废话4ts说: 没有。 。 。 .

它计算了C.R.A.P的分数——麦凯比复杂度按测试覆盖度加权计算——对于您TypeScript / JavaScript 代码库中的每一个函数,然后根据基线从 main. . . . . 遗产热点在报道中保持可见,但绝不阻止合并.

CRAP(m) = comp(m)² × (1 − cov(m)/100)³ + comp(m)

最初的度量衡由Alberto Savoia & Bob Evans于2007年提出(请原谅我的法语,但这个代码是C. R.. crap4ts将其带入了TypeScript生态系统,并拥有一等支持的vitest, jest, bun test, 以及任何LCOV排放工具.

现场实例

打开公关,反对这回波, 每一个都行使一种情形 让机器人评论。 点击其中任何一个来查看实际粘贴的注释外加代码扫描注释的bot帖.

Diff 假设

公关显示什么
# 1 Demo - 清除现有函数的回归processOrder 倒退, --fail-regression 1号出口
#2 Demo - 添加了新的高CRAP功能新设 validateInvoice 标记为 新建遗产不要挡道
#3 Demo - 改进CRAP的元素processOrder 分裂成帮助者,大负 QQ,QQ 判决
#4 Demo - 纯移动/重命名检测git mv 报告为: 移动,而非新+删除
# 7 Demo - SARIF 上传到代码扫描粘贴注释 财务报告和审定财务报表 pR diff 中的内含错误注释

莫诺雷波布局

每一个公关都增加了一个自成一体的固定装置 examples/monorepos/ 在固定图中显示运行命令+预期每个包输出.

公关工作空间配置固定线
#9 Demo - Monorepo (pnpm-workspace.yaml) (英语)pnpm-workspace.yamlexamples/monorepos/pnpm/
#10 Demo - Monorepo (package.json#workspaces) (英语)package.json#workspaces (bun/纱线/npm)examples/monorepos/npm/
#11 Demo - 莫诺雷波 (图尔博)turbo.json + 键 pnpm-workspace.yamlexamples/monorepos/turbo/

v0.6 特性

公关显示什么固定线
#14 Demo - v. 6. 特性认知复杂性、内源禁用评论、 超度、热点examples/v06/

30秒设置

cd my-project
bunx @danibram/crap4ts init --workflow
git add crap.config.json .github/workflows/crap.yml
git commit -m "ci: gate on CRAP regressions" && git push

crap4ts init 检测您的测试跑道( test / jest / bun test), 写 crap.config.json 带有合理默认值,并降低基线感知的 GitHub 工作流程:

  • 从 main 每个公关
  • 在每个 PR 下与 diff 一起 贴出粘贴的 bot 注释
  • 上传 SARIF 到代码扫描( 内置说明 + 安全标签)
  • 当任何函数退后时退出非零

就是这样。 没有犹太考古学 没有"我该扫描哪条路径" 没有从这个README复制.

手动快速启动

# Without coverage data — pure complexity ranking
crap4ts src/

# With vitest v8 coverage
crap4ts src/ --coverage coverage/coverage-final.json

# Fail CI when any function exceeds CRAP 50
crap4ts src/ --coverage coverage/coverage-final.json --fail-on 50

# Markdown for PR comments
crap4ts src/ --reporter markdown > crap-report.md

# GitHub Actions inline annotations
crap4ts src/ --reporter github

# Compare a PR against a baseline from main and fail on regressions
crap4ts src/ --baseline crap-main.json --fail-regression

# Opinionated PR-bot comment (sticky marker for in-place updates)
crap4ts src/ --baseline crap-main.json --reporter pr-comment > comment.md

# Monorepo overview, grouped by package
crap4ts --workspace --report-by package

# Hotspots: rank by CRAP × git churn (risky AND frequently-changed)
crap4ts --hotspots --since 90d

# Cognitive complexity instead of cyclomatic (nesting-weighted)
crap4ts --complexity cognitive

# Merge per-package coverage before scanning a monorepo
crap4ts merge-coverage 'packages/*/coverage/coverage-final.json' -o merged.json

产出

Scanned 261 files. 910 functions analysed.
Coverage: v8 (coverage/coverage-final.json)
Above threshold (CRAP > 30): 133 (14.6%)

    CRAP  COMP  COVERAGE          LOCATION                            FUNCTION
─  ─────  ────  ────────────────  ──────────────────────────────────  ───────────────────────
▲ 2070.0    45  ░░░░░░░░░░    0%  src/billing/excel-validator.ts:108  validateBillingCostItem
▲ 1406.0    37  ░░░░░░░░░░    0%  src/billing/periods-feature.ts:84   getUnifiedPeriodsRows
✓   18.0    18  ██████████  100%  src/config.ts:56                    mergeConfig
 ...

状态图标 : ✗ 超过 --fail-on, (中文(简体) ), ▲ 超过 --threshold, (中文(简体) ), ✓ 干净.

记者

记者用于
table默认 — 终端中的对齐列
json机器可读输出(CI脚本,仪表板)
markdown公关机构 / GitHub 问题(GFM表)
github::warning:: 编号 : ::error:: pR的说明
pr-comment有意见的PR-bot评论有粘性标记
sarifSARIF 2.1.0 → GitHub 代码扫描 / VS代码
eslintESLint JSON – 评论狗和ESLint 认知工具

热点(v.6

一个无人触及的功能上的高CRAP分数是低优先级的——你不会因为没有编辑而打破. 实际被咬的功能是 既危险又不断改变那是热点四角星.

crap4ts --hotspots --since 90d
Scanned 25 files. 175 functions analysed.
Hotspots: CRAP × commits since 90 days ago (HOT column, sorted).

     CRAP   HOT  CHURN  COMP  COVERAGE       LOCATION              FUNCTION
─  ──────  ────  ─────  ────  ─────────────  ────────────────────  ───────────────
▲   756.0  6804      9    27  ░░░░░░░░░░ n/a  src/cli.ts:387        buildCliConfig
▲  1560.0  6240      4    39  ░░░░░░░░░░ n/a  src/config.ts:137     mergeConfig
▲   650.0  5850      9    25  ░░░░░░░░░░ n/a  src/cli.ts:141        run

HOT = CRAP × CHURN,其中churn是接触窗口中文件的承诺数量。 说明如何 buildCliConfig (9项承诺) mergeConfig 尽管生的CRAP较低——编辑次数超过两倍,因此是比较有风险的地方. --since 接受 90d, (中文(简体) ), 6w, (中文(简体) ), 3m, (中文(简体) ), 1y,或任何 git 日期表达式(默认) 90d。在git repo之外,churn会降解为0(没有崩溃).

认知复杂性(v0.6)

经典的 CRAP 度量法使用 cyclomatic 复杂度,它计算独立路径,但处理平面 switch 和一堆深沉的 if编号. 认知复杂性 (公制Biome和SonarJS移动到) 重量并读取更接近“这对人类来说有多难”:

crap4ts --complexity cognitive
  • 巢穴受到处罚:a if 在两个循环中,成本超过顶级.
  • else 编号 : else if 平地(+1,无筑巢处罚).
  • 布尔运行计数一次( O)a && b && c = +1; 交替运算符增加更多(a && b || c = +2) (中文(简体) ).
  • switch 是+1 总计,不是一次一次.

这是一个"crap4ts"扩展——Canonic CRAP在"环相上下"的定义——但认知轨迹对深层嵌入式代码的感知风险更好. Recursion的+1还没有模型化. 所选的衡量标准记录为: complexityMetric 在JSON的信封.

压制职能

三级,外科手术越来越多:

机制范围何处
--ignore <glob>文件完全没有解析CLI / 配置
--allow <glob>文件解析后, 函数隐藏在报表中CLI / 配置
/* crap4ts-disable-next-function */一个函数来源
/* crap4ts-disable-file */全文件来源( 靠近上方)
/* crap4ts-disable-next-function */
export function generatedMonster(/* ... */) { /* gnarly but generated */ }

内源评论是外科选项——他们住在他们豁免的代码旁边,因此豁免在审查中可见并随文件出行.

配置

crap4ts 读取 crap.config.json 从当前工作目录上行到最近的 git root(或文件系统 root) 。 跌回一个 crap 节 package.json (仅cwd). CLI 旗帜总是覆盖配置文件 .

{
  "include": ["src/**/*.ts"],
  "ignore": ["src/legacy/**", "**/*.gen.ts"],
  "allow": ["generated/**", "render*"],
  "threshold": 30,
  "failOn": 100,
  "missing": "pessimistic",
  "min": 10,
  "reporter": "table",
  "top": 50,
  "summary": false,
  "coverage": "./coverage/coverage-final.json",
  "coverageFormat": "auto",
  "tsconfig": "./tsconfig.json",
  "baseline": "./crap-main.json",
  "failRegression": true,
  "epsilon": 0.01,
  "complexity": "cyclomatic",
  "overrides": [
    { "paths": "legacy/**", "threshold": 200, "failOn": 500 },
    { "paths": ["src/generated/**", "**/*.gen.ts"], "failOn": null }
  ]
}

Per-path 覆盖

overrides 让您放松(或收紧)特定路径的大门,而不提高全球条,这是在已经拥有高CRAP角的代码库上采用crap4ts的关键. 每个条目都通过光标和覆盖来匹配 threshold 和(或) failOn 用于匹配的文件。 决议是 最后一场比赛 (压力) overrides 语义学:后入句比起更早入句. "failOn": null 明确清除这些路径的大门(区别于省略它,它继承了全球)。 因此,在上述例子中: legacy/** 可能会在失败前到达 CMRAP 500 , 生成的文件永远不会出故障 门, 其它所有的东西都会在环球上保持线条 failOn: 100。 。 。 .

CLI 旗帜

过滤

旗帜默认说明
-i, --ignore <glob>无在行走时跳过文件( 可重复 ,), 未解析) (中文(简体) ).
--allow <glob>无解析文件但隐藏匹配函数 。 如果包含路径模糊 / 或者说 **; 否则匹配函数名( e)* 没有交叉 :: 或者说 .) (中文(简体) ).
--min <score>无从报表中隐藏此分数下的行 。 对 没有 影响 --fail-on。 。 。 .
--top <n>50只显示N最坏的罪犯.

阈值

旗帜默认说明
-t, --threshold <n>30标出一个函数的分数 .
--fail-on <n>无如果任何函数超过此值, 则退出代码 1 .

覆盖范围

旗帜默认说明
-c, --coverage <file>无报道路径.
--coverage-format <fmt>autoauto | v8 | istanbul | lcov
--missing <policy>pessimistic如何在无覆盖数据的情况下对函数进行分分 : pessimistic (0%), optimistic (100% = CRAP = comp), skip (放下行来.

产出

旗帜默认说明
-r, --reporter <name>tabletable | json | markdown | github | pr-comment | sarif
--summary关只打印汇总数据+最坏的罪犯(无表格).
-o, --output <file>无写入文件而不是 stdout .

基线

旗帜默认说明
--baseline <file>无与先前发布的JSON报告相比. 添加 QQ 列, 并将每个函数分类为新 / 移动 / 退后 / 改进 / 不变 .
--fail-regression关退出 1 如果任何函数出现退入 --epsilon。要求 --baseline。 。 。 .
--epsilon <n>0.01容忍"不变" CRAP diff. 职能

杂项

旗帜默认说明
--tsconfig <path>无Tsconfig.json的路径(非常需要).
--config <path>无胡说八道.
-h, --help
-v, --version

覆盖范围格式

crap4ts 自动检测三种格式:

来源格式
@vitest/coverage-v8 → coverage-final.jsonv8
伊斯坦布尔/ @vitest/coverage-istanbul 编号 : nyc → coverage-final.jsonistanbul
bun test --coverage --coverage-reporter=lcov → lcov.infolcov
jest --coverage → coverage/lcov.infolcov

用vitest生成覆盖:

# vitest.config.ts:
# test: { coverage: { provider: 'v8', reporter: ['json'] } }
vitest run --coverage

解释分数

原始论文提供了一个软门槛: 30个 用于"此函数需要注意". 在~50以上,你有有意义的危险代码。 在 ~100 以上,您有代码, 其中一个改变 有很大的可能性 引入回归.

该公式有两个纯成分:

  • 覆盖率=100% → CRAP = comp(m). 即使经过充分测试的具有环形复杂度的代码 30+ 也很难解释;考虑拆分.
  • 覆盖率=0% → CRAP = comp(m)² + comp(m). 复杂度5跳至30,复杂度10跳至110. 测试覆盖迅速降低分数——添加 任意 测试大约在八分之一的CRAP.

确定高CRAP值的最快方法几乎总是添加测试,然后自信地重构.

基线工作流程(v0.3)

密码质量门最难卖的是 "我们已经有 CMRAP 200 的功能 在这个repo, 你要去让我 修复这一切之前,我可以合并?"基线工作流程如下: 不,只是不要让它更糟.

  1. 打开 main,在每座建筑中发出一个基线JSON:

    crap4ts src/ --coverage coverage/coverage-final.json \
      --reporter json --output crap-main.json
    

    史达什 crap-main.json 您的 PRC 可以获取它( 艺术、 S3、 分支保护缓存等) .

  2. 每一份公关记录上

    crap4ts src/ --coverage coverage/coverage-final.json \
      --baseline crap-main.json --fail-regression
    

crap4ts 然后打印出 QQ 列并分类每个函数:

状态含义
regressed同样的功能, CRAP 上升超过 --epsilon
new基线中不存在函数
moved函数正体散列匹配不同文件中或不同名称下的基线条目
improvedCRAP 下降超过 --epsilon
unchanged
removed基线中存在函数,但现已消失

移动检测基于散列,所以重构的 PRs,在周围打乱代码不被报告为墙壁 new + 键 removed 双人 默认 --epsilon 0.01 吸收覆盖工具在运行之间引入的CRAP的子百分比紧张.

PR-bot 注释

crap4ts src/ --baseline crap-main.json \
  --reporter pr-comment --output comment.md

pr-comment 发出粘相 <!-- crap4ts-report --> 在第一行标出。 连接您的 PR- comment 工作流程以找到该标记并更新现有的注释, 而不是发布每个运行的新注释 。 评论体崩溃 改进和现有的热点 <details> 因此,"什么变得更糟"的桌子 保持前和中.

单曲

覆盖工具根据运行地点以四个形状发送文件路径 :

  1. 绝对 : /home/alice/proj/src/foo.ts
  2. 相对于工作空间根 : src/foo.ts
  3. 相对于子包(当覆盖工具运行时) packages/foo/: src/bar.ts
  4. 与 ./ 或者说 ../ 前缀

早先的版本 crap4ts 运行 path.resolve() 静悄悄地将相对路径与当前 process.cwd()。这在回收根上是好的,但在单体上却无声无息地失败了——子包中的每个功能都显示覆盖率为0%.

从v0.4开始,匹配者使用双等指数:

  1. 绝对报告路径进入由绝对路径键入的散列图 。 准确点击解析 O(1)。 。 。 .
  2. 相对报告路径匹配 组件后缀 相对于查询的组件. src/foo.ts 匹配 /repo/packages/foo/src/foo.ts· ; foo/bar.ts 已经 没有 匹配 /proj/oofoo/bar.ts 因为匹配边界是路径分隔符,而不是字节边界.

如果多个相对条目相匹配,则最具体的(最长的匹配后缀)获胜. 跨机绝对路径( CI 报告运行于 /home/runner/work/... 本地询问 /Users/dani/...)故意不匹配——默默地将不同的绝对根相融合会掩盖出真正的bug. 一个未来 --path-prefix 旗帜会在您需要的时候重新显示.

crap4ts 还读取最近的 .gitignore 并将其图案添加到行走者中,从而生成目录(dist-temp/, (中文(简体) ), build-out/等)自动跳过而无需重复 crap.config.jsonB. 忽略模式!foo)并筑巢于地 .gitignore 树上更深层的文件还没有被尊重——如果击中它们就打开一个问题.

工作空间发现

crap4ts --workspace 自动检测您的工作空间配置( 按此顺序) :

  1. pnpm-workspace.yaml – 报告 packages: 列表
  2. package.json#workspaces - npm / 纱线 / 包子形状, 既有平方阵形, 也有被线抹去的形式

当它找到包时,扫描会扩展到所有它们,报告中的每一个功能都带有它的包标签,以及 --report-by package 按软件包组合表格,排序最糟糕的软件包:

▸ @scope/billing  (12 fns · Σcrap 870)
   CRAP  COMP  COVERAGE         LOCATION                  FUNCTION
─  ────  ────  ───────────────  ────────────────────────  ───────────
▲ 462.0    21  ░░░░░░░░░░  n/a  src/recurring.ts:14       computeBill
▲ 156.0    12  ████░░░░░░  40%  src/discounts.ts:7        applyCode
…

▸ @scope/auth     (3 fns · Σcrap 102)
   …

这是单列波维护者在分解时实际使用的视图:"哪个团队需要重构什么". JSON记者总是带着 package 字段,当工作区活动时,所以仪表板也可以组合.

覆盖自动检测配置 --workspace-config path/to/pnpm-workspace.yaml 当从 repo 根外跑出.

合并每包的覆盖率

每个软件包(图尔博、Nx、平行CI)进行测试的单相机输出一个 coverage-final.json 每个包,但crap4ts会消耗一个覆盖文件. merge-coverage 组合它们——文件的结合,在不止一个报告中出现文件时,对每份报表的点击数进行总结:

crap4ts merge-coverage 'packages/*/coverage/coverage-final.json' -o coverage/merged.json
crap4ts --workspace --coverage coverage/merged.json

JSON(istanbul / v8)目前只有——每包输出模式几乎总是会出JSON. LCOV合并被推迟.

GitHub 代码扫描( SARIF)

crap4ts --reporter sarif 输出 a SARIF 2.1.0 (法语) gitHub 代码通过官方扫描摄入 upload-sarif 行动。 高级区域行动方案的职能随后出现:

  • 在每个 PR 的 diff 中插入提示/ 错误说明到违法行范围旁边
  • 在那个 安全 —— 代码扫描 选项卡作为持续结果 partialFingerprints 所以重跑时不要重复
  • 在读取SARIF(通过SARIF查看器扩展,IntelliJ等的VS代码)的编辑器中

发布了两项规则:

规则ID职等触发当
crap4ts/threshold警告crap > --threshold (30个)
crap4ts/fail-on错误crap > --fail-on

最小工作流程 :

- name: Run crap4ts (SARIF)
  run: bunx @danibram/crap4ts src/ \
    --coverage coverage/coverage-final.json \
    --threshold 30 \
    --fail-on 100 \
    --reporter sarif \
    --output crap.sarif

- name: Upload to Code Scanning
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: crap.sarif
    category: crap4ts

category: crap4ts 将这些结果与其他代码扫描源(CodeQL,ESLint SARIF等)区分开来,因此每个工具都会在仪表板上得到自己的一栏.

CI 整合

GitHub 动作

- name: Run tests with coverage
  run: vitest run --coverage

- name: Check CRAP
  run: bunx @danibram/crap4ts src/ \
    --coverage coverage/coverage-final.json \
    --threshold 30 \
    --fail-on 100 \
    --reporter github

--reporter github 通过工作流程命令在 PR diff 上发布内置说明 .

一般 CI( 仅退出代码)

bunx @danibram/crap4ts src/ \
  --coverage coverage/coverage-final.json \
  --fail-on 100

回归退出 1 如果任何函数超过 --fail-on与 --reporter json > crap.json 保存机器可读文物.

如何运作

crap4ts 通过解析类型脚本 ts-morph (真正的AST——不是regex). 对每个类似功能的节点来说,它都是决定点 crap4j 原为:

  • if, (中文(简体) ), for, (中文(简体) ), for-of, (中文(简体) ), for-in, (中文(简体) ), while, (中文(简体) ), do-while
  • case (c) 条款(每例一次; default 是倒转)
  • catch 条款
  • 有条件的表达式a ? b : c(中文(简体) )
  • 逻辑运算符 : &&, (中文(简体) ), ||, (中文(简体) ), ??
  • 可选的链条(?.)为 没有 决定点

嵌入功能内部的决定属于内在功能——它们不会出血.

覆盖范围计算为 covered_statements / total_statements 从覆盖报告中取出。 这是文件所指明的基础路径覆盖的实际近似值,足以推动确定优先顺序.

限制

  • 说明范围,而不是基于路径范围. 对大多数代码来说,在实际操作上是等同的;对于密度条件来说,略微不足.
  • 类型脚本 .ts 编号 : .tsx 编号 : .js 编号 : .jsx 仅限 ——还没有Vue / Svelte / MDX.
  • 内部分类方法 class { ... } 仅限 ——取出TS命名空间方法和计算出的名称方法,但可能失去有用的命名.

鸣谢

  • 阿尔贝托·萨沃亚和鲍勃·埃文斯 原始C.R.A.P.度量衡 财务报告和审定财务报表 crap4j (2007年).
  • 这个 ts-morph 没有它,这个工具会是一堆雷克克斯.

许可证

麻省理工学院 丹尼尔·比德马

版本与发布记录

@danibram/crap4ts 共发布 6 个版本,首次发布于 2026/05/26;过去 12 个月发布了 6 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.6.0latest2026/05/285 个月前
0.5.12026/05/265 个月前
0.5.02026/05/265 个月前
0.4.02026/05/265 个月前
0.3.02026/05/265 个月前
0.2.02026/05/265 个月前

关于 @danibram/crap4ts 的常见问题

@danibram/crap4ts 是什么?

C.R.A.P. (Change Risk Analysis & pretroductions) TypeScript / JavaScript 代码库的索引. 将环形复杂度与测试覆盖度结合起来,形成地表风险到变化代码.

如何安装 @danibram/crap4ts?

在项目目录运行 npm install @danibram/crap4ts,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 crap4ts,可以用 npx crap4ts 直接运行。

@danibram/crap4ts 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@danibram/crap4ts 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@danibram/crap4ts 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@danibram/crap4ts 可以商用吗?

@danibram/crap4ts 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@danibram/crap4ts 的下载量有多少?

根据 npm 官方下载统计,@danibram/crap4ts 在 2026/10/11 – 2026/11/09 这 30 天内被下载 16,295 次,日均约 543 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@danibram/crap4ts 还在维护吗?

最新版本 0.6.0 发布于 2026/05/28(5 个月前);至今共发布 6 个版本,其中过去 12 个月发布了 6 个;首次发布于 2026/05/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。