跳到正文
Hacker News· ericholiveira·· 8 小时前AI 评分58

Farol 开源发布:为 PR 审批人生成合并风险地图

Show HN: Farol – A risk map of a pull request for the person who approves it

AI 导读

Farol 发布开源 CLI 和 GitHub Action,在 AI 审查之后为 PR 审批人给出合并风险评分(0–100)、fast track/quick review/careful review 三档结论,以及风险最高的文件清单。

正文 · AI 翻译

farol

了解合并拉取请求的风险,以及风险所在。

一个免费、开源的 CLI 和 GitHub Action。在你的 AI 审查之后,farol 会告诉批准合并的人这次合并有多大的风险,以及哪些文件承载了风险,从而让风险最高的改动获得最多的关注。

网站 · 在线演示 · 快速开始 · 参考文档

License: MIT Node.js 22+ TypeScript · Go · Python Shadow mode


智能体编写代码,智能体审查代码。最终决定仍然由人做出:批准合并或不批准。这个批准环节是稀缺的,而如果它被均匀地分摊到每一个拉取请求上,就会变成走马观花,于是一行对共享钩子的改动就能让四十个页面崩溃。

farol 出现在 AI 审查之后,而不是取代它。你的审查智能体会对 diff 中的行发表评论。farol 为批准者提供智能体无法可靠给出的信息:接受这次改动的风险,以及风险所在。目标不仅是更快的审查:低风险的拉取请求可以快速通过,而高风险的则要逐行阅读,从承载风险的文件开始。它基于静态分析(编译器对你代码的视角,而非模型的猜测)工作,因此同一个拉取请求总是得到相同的答案。

Claude Code 团队在 How the Claude Code team does code review 中描述了同样的原则:审查的投入程度跟随改动的风险。正如他们所说,“是改动决定层级,而不是作者。”

farol(葡萄牙语意为灯塔)在审查者打开 diff 之前完成分诊:

  • 这次改动会影响什么?运行被改动代码的每一个现有页面、API 操作、RPC 方法、任务和脚本,以及到每一个的调用链。
  • 这次合并有多大的风险?一个结论(快速通道、快速审查、仔细审查)和一个 0–100 的风险评分,评分依据包括影响范围、diff 覆盖率、安全问题、契约破坏和依赖变更。
  • 我从哪里开始?风险最高的文件优先,附带内联 diff、标记未测试的行,以及一份手动尝试的检查清单。

免费且开源(MIT)。用 npx @winninjs/farol 作为 CLI 运行,或通过 GitHub Action winnin/farol/action 在每个拉取请求上运行。无需账户,无需托管服务:它运行在你的机器上或 CI runner 中,对每个 PR 发表评论,且从不阻止合并。它从不执行你的代码,也从不将代码发送到任何地方。

The farol report of a pull request: a careful review at 74/100, the two files to start with, and three breaking contract changes

在线演示

在真实的拉取请求上查看 farol →

演示中保存了 farol 在 CI 中为一个拉取请求生成的报告,该拉取请求位于 FastAPI 全栈模板上:一个 FastAPI 后端和一个使用 TanStack Router 的 React 前端。该拉取请求为条目添加了状态,并新增了一个 Insights 页面,其中包含审查者应当发现的那类问题:破坏性的 API 变更、用 f-string 构建的 SQL、没有测试的共享组件。

  • 后端报告:⚠ 仔细审查,一个破坏性的契约变更和一个高严重性的安全问题。
  • 前端报告:⚠ 仔细审查,一个新页面以及一个共享组件所影响到的现有页面。

这些报告是交互式的:将文件标记为已审查、将页面标记为位于功能开关之后、打开内联 diff、按照测试计划操作,并从 Result 标签页复制审查记录。它们与拉取请求评论所链接的是同一批文件(该拉取请求);副本位于 docs/demo/。

AI 审查智能体 farol
读取 diff,以及提示中能容纳的任何上下文 基线和头部的整个程序,连同编译器(TypeScript 类型检查器、go/types、Python ast)
告诉你 这些行里可能有什么问题 这些行触及了什么、这次合并有多危险,以及应该先看哪里
答案是 概率性的:每次运行措辞都不同 确定性的:同一个 PR 给出同一份报告,而且每条结论都附带其调用链
不确定时 可能听起来仍然很自信 会说明 unknown 并请求仔细审查
运行方式 一个模型,通常是托管服务 在本地或你的 CI 中:它从不执行你的代码,也从不把代码发送到任何地方

按这个顺序两者都用。agent 先审查这些行;然后 farol 告诉批准者这次合并需要多少关注、哪些文件值得关注、哪些页面和端点需要手动测试,以及哪些契约变更需要检查其客户端。当一个小改动影响范围很大时,agent 的审查恰恰不够,而这正是 farol 所展示的。

为什么选择 farol

  • 审查注意力流向风险所在之处。 一个 40 个文件的 PR 通常只有两三个文件真正可能造成伤害。farol 对它们进行排序,因此审查者会仔细阅读这些文件,并心安理得地略读其余部分。
  • 它跟随代码,而不是文件名。 farol 使用真正的编译器(TypeScript 类型检查器、go/types、Python 的 ast)构建声明图。对 formatPrice 的更改会列出渲染它的十二个页面,而不是“1 个文件已更改”。
  • 契约破坏在客户端受影响之前就被捕获。 被移除的响应字段、新增的必需参数、GraphQL、FastAPI、Go 处理器和 gRPC 中更改的类型,以及调用每个端点的服务。
  • 审查整个 PR,或只审查自上次查看以来更改的内容。 每次推送都会得到两份报告:完整的拉取请求,以及仅自上次推送以来的提交。经过一轮修复后,审查者检查新的提交,而不是从头开始。
  • 它利用你的 CI 已有的内容。 覆盖率、lint、安全和审计报告变成更改行上的 diff 覆盖率和发现项。没有任何东西运行两次。
  • 失败关闭。 当 farol 无法读取更改的某部分时,它会说明这一点(unknown)并请求仔细审查。它绝不会因为不理解某事物就称其安全。
  • 为循环中的人而构建。 将文件标记为已审查,将页面标记为位于功能标志之后,勾选发现项,并将结果粘贴回 PR 中,作为审查内容的记录。

目录

开始使用

你需要 Node.js 22+ 和 git(Go 项目还需要 go,Python 项目需要 python3 3.11+)。无需安装:用 npx @winninjs/farol 运行 farol。

1. 描述你的项目,在根目录的 farol.config.json 中。一行就足以开始;选择你的技术栈:

你的项目 farol.config.json 完整配方
使用 React Router 的 React 应用 { "routes": { "file": "src/routes.tsx" } }(构建路由表的文件) React Router
Next.js 应用(Pages Router 或 App Router) { "routes": { "adapter": "next" } } Next.js
使用 TanStack Router 的 React 应用 { "routes": { "adapter": "tanstack-router", "dir": "src/routes" } } TanStack Router
TypeScript API(GraphQL、REST) { "surface": "backend" } TypeScript API
Go 服务 { "language": "go", "surface": "backend" } Go
Python 服务(FastAPI、Flask、Django、Celery……) { "language": "python", "surface": "backend" } Python

2. 查看 farol 发现了什么:它找到的页面、端点、任务和脚本。

npx @winninjs/farol routes

3. 分析一个分支与其目标分支的差异,并打开报告。

npx @winninjs/farol analyze --base origin/main --head HEAD --out farol.json
npx @winninjs/farol html --in farol.json --out farol.html && open farol.html     # xdg-open on Linux

示例:使用 React Router 的 React 应用

cd path/to/your/react-app
echo '{ "routes": { "file": "src/routes.tsx" } }' > farol.config.json

npx @winninjs/farol routes                                        # lists your pages: "/", "/products", "/settings"…
git checkout my-feature-branch
npx @winninjs/farol analyze --base origin/main --head HEAD --out farol.json
npx @winninjs/farol html --in farol.json --out farol.html && open farol.html

报告已经显示了结论、评分、应从哪些文件入手、变更影响到的页面,以及需要手动测试的内容。后续步骤:添加你的覆盖率、lint 和审计报告,并在每个 PR 上运行 farol,使用适合你技术栈的 recipe。所有选项都在参考文档中,各技术栈的选项则在examples/README.md中。

经常运行?npm install --global @winninjs/farol 为你提供一个简单的 farol 命令。

Recipes

每个 recipe 包含三部分:要提交的 farol.config.json、要生成的报告,以及 GitHub Actions 工作流。examples/ 中的工作流是完整的,并且共享同一种结构:

  1. tests 运行你的测试、linter 和审计,并保留报告。它运行 PR 的代码,因此没有密钥,且使用只读令牌。如果你已经有测试任务,保留它并添加其上传步骤。
  2. farol 在其之后运行(无论通过还是失败),读取 git 和报告,并在每次推送时发布一条新评论,其中包含一个在浏览器中打开 HTML 报告的链接,以及一份仅包含自上次推送以来提交的第二份报告。它从不运行 PR 的代码。
  3. 草稿会被跳过,fork PR 不会收到评论(其令牌为只读),并且每个 farol 步骤都是 continue-on-error。

在你确认可用后,将 action 固定到某个提交:winnin/farol/action@<sha> # main as of <date>。

examples/README.md 针对每个技术栈深入介绍:每个配置键、CLI 参数和 action 输入,以及如何检查配置。

React 前端(React Router)

React Router 路由表(对象和 JSX 形式、懒加载路由、拆分路由模块),按声明计算可达性。

{
  "routes": { "file": "src/routes.tsx" },
  "roles": { "global": ["src/main.tsx", "src/App.tsx", "src/styles/global.css", "package.json", "vite.config.ts"] }
}
npx vitest run --coverage.enabled --coverage.reporter=json --coverage.reportsDirectory=reports/coverage
npx eslint -f json -o reports/eslint.json <changed files>
npm audit --json > reports/audit.json                     # or pnpm audit --json

npx @winninjs/farol analyze --base origin/main --head HEAD \
  --coverage reports/coverage/coverage-final.json --eslint reports/eslint.json --audit reports/audit.json --out farol.json

CI:将 examples/workflow.yml 复制到 .github/workflows/farol.yml。

在 Vitest 配置中设置 coverage.include: ['src/**/*.{ts,tsx}'],以便没有测试加载的文件被报告为未测试。拆分的路由树放在 routes.extraFiles 中;更完整的配置在 examples/farol.config.json 中。

Next.js

Pages Router 和 App Router:每个页面、API 路由、布局、_app/_document 和中间件,均从文件名读取。动态段([id])、全捕获、路由组和并行槽都能被理解。

{
  "routes": { "adapter": "next" },
  "roles": { "global": ["next.config.*", "package.json"] }
}

pages/ 和 app/ 会在项目中或其 src/ 中找到;当它们位于别处时设置 "dir"。报告和 CI 与 React Router 相同(Jest 或 Vitest 覆盖率、ESLint、审计)。在 tsconfig 继承工作区包("extends": "tsconfig/nextjs.json")的 monorepo 中,请在分析前安装依赖,以便该包位于 node_modules 中。

TanStack Router

基于文件的路由:routes 目录下每个调用 createFileRoute('<id>') 的模块,根路由、无路径布局(_app)和父路由作为其子路由的布局。

{ "routes": { "adapter": "tanstack-router", "dir": "src/routes" } }

报告和 CI 与 React Router 相同。

TypeScript API(GraphQL 或 REST)

GraphQL 根操作(schema 文件和 gql 模板,按 resolver)以及 Fastify/Express 风格的 REST 端点。

{
  "surface": "backend",
  "backend": { "graphql": true, "rest": true },
  "roles": { "global": ["package.json", "src/app.ts"] }
}

报告和 CI 与 React Router 相同:examples/workflow.yml。如果 resolver 通过注入的上下文(ctx.loaders.Brand.byId())访问数据,请告诉 farol 这些 provider 所在的位置:"backend": { "context": { "loaders": ["src/modules/*/loaders/index.ts"] } }。

Go 服务

chi、gin、echo 和 net/http 路由,gRPC 服务方法和 main 二进制文件,按声明计算可达性,并将接口调用解析到其实现。

{ "language": "go", "surface": "backend" }
go test -coverpkg=./... -coverprofile=reports/cover.out ./...
golangci-lint run --output.json.path=reports/golangci.json --issues-exit-code=0 ./...   # enable gosec
govulncheck -json ./... > reports/govulncheck.json

npx @winninjs/farol analyze --base origin/main --head HEAD \
  --coverage reports/cover.out --eslint reports/golangci.json --audit reports/govulncheck.json --out farol.json

CI:复制 examples/workflow-go.yml。farol 离线读取模块,因此工作流会先为 base 和 head 运行 go mod download。

Python 服务

FastAPI 和 Flask 路由(routers、blueprints、prefixes)、Django URL、MCP tools/resources/prompts、Celery 任务、 Temporal workflows 和 activities、Prefect flows 以及 __main__ 脚本。farol 解析你的代码;它从不导入代码。

{ "language": "python", "surface": "backend", "python": { "roots": ["src", "."] } }
pytest --cov=src --cov-report=json:reports/coverage.json
ruff check --output-format json --extend-select S --exit-zero . > reports/ruff.json    # S = flake8-bandit
pip-audit --format json -o reports/pip-audit.json

npx @winninjs/farol analyze --base origin/main --head HEAD \
  --coverage reports/coverage.json --eslint reports/ruff.json --audit reports/pip-audit.json --out farol.json

CI:复制 examples/workflow-python.yml(使用 uv;替换为你的安装器)。

添加 Datadog 流量和错误

可选,且仅供参考:它从不改变评分。启用后,报告会显示每个受影响的 页面或操作获得多少流量、其不同的生产错误(链接到 Datadog)、已经抛出 错误的已更改文件,以及调用每个已更改端点的服务。

{
  "datadog": {
    "env": "prod",
    "rum": { "applicationId": "<RUM application id>" },
    "apm": { "service": "orders-api", "operations": ["fastapi.request"] }
  }
}
DD_API_KEY=… DD_APP_KEY=… npx @winninjs/farol usage --out usage.json      # last 14 days
npx @winninjs/farol analyze … --usage usage.json

CI:示例工作流有一个 farol-usage job:设置 DD_API_KEY 和 DD_APP_KEY secrets 以及 仓库变量 FAROL_DATADOG=true。它每天读取一次 Datadog,且从不查看 PR 的代码。密钥、作用域和 匹配规则见参考。

支持的内容

TypeScript / React 前端 TypeScript / JavaScript 后端 Go Python
入口点 React Router 路由表(对象和 JSX 形式、懒加载路由、拆分路由模块);Next.js Pages 和 App Router(pages、API routes、layouts、middleware);TanStack Router 基于文件的路由 GraphQL 根操作(schema 文件和 gql 模板)以及 REST 端点(Fastify/Express 风格) chi、gin、echo 和 net/http 路由;gRPC 服务方法;main 二进制文件 FastAPI 和 Flask 路由(routers、blueprints、prefixes);Django URL;MCP tools、resources 和 prompts;Celery 任务;Temporal workflows 和 activities;Prefect flows;__main__ 脚本
可达精度 按声明(TypeScript 类型检查器) 按模块,GraphQL 按 resolver 按声明(go/types,接口调用解析到实现) 按声明(Python ast 带本地类型推断)
契约变更 — GraphQL schema(破坏性 / 危险 / 安全) Handler 请求和响应类型(net/http、chi、gin、echo);gRPC 消息 FastAPI 参数、body、response 和 Pydantic 模型
依赖 package.json + npm/pnpm/yarn/bun lockfiles 同上 go.mod + go.sum pyproject.toml(PEP 621、extras、groups、uv、Poetry)、requirements.txt + uv.lock/poetry.lock/Pipfile.lock
Diff 覆盖率 Istanbul coverage-final.json(Vitest、Jest) 同上 go test -coverprofile coverage.py JSON(pytest --cov)
Lint eslint -f json 同上 golangci-lint JSON ruff JSON
安全 内置规则(XSS sinks、eval、硬编码密钥、javascript: URL……) 同上 gosec(通过 golangci-lint) flake8-bandit(ruff S 规则)
漏洞 npm audit --json、pnpm audit --json 同上 govulncheck -json pip-audit --format json
抑制 eslint-disable、@ts-ignore、@ts-expect-error、as any…… 同上 //nolint、//lint:ignore、#nosec # noqa、# type: ignore、# nosec、# pragma: no cover……
流量和错误(可选) Datadog RUM 页面浏览量和错误 Datadog APM 请求、错误 span 和调用方 同上 同上

在所有语言中:环境、基础设施和 CI 文件(.env*、Dockerfiles、compose、CI pipelines、helm、k8s、 terraform……)单独放在一个部分;自任意提交起的增量分析(--since);根据 git blame 和 CODEOWNERS 建议审查者;报告以 GitHub 评论、Markdown 或单文件 HTML 页面形式呈现。

阅读审查结果

farol 生成两样东西:一份包含结论、主要文件和测试清单的PR 评论,以及 一份单文件 HTML 报告(从评论链接过去),审查就在那里进行。下面说明每个部分的用途以及 如何使用它。

结论和合并风险评分

它告诉你的是这个 PR 值得你花多少时间。

结论 含义 该做什么
✓ 快速通道 仅限新代码、测试或低风险改动。 浏览 diff 并批准。
◐ 快速审查 现有代码以受控方式改动,或信号较弱(覆盖率低、lint 错误、配置文件)。 检查 Start here 中的文件,然后批准。
⚠ 仔细审查 可能出现大范围或静默破坏:评分 60+、破坏性契约变更、高严重度发现、未测试的核心文件,或代码 farol 无法读取。 逐行阅读 Start here 中的文件,并测试它们触及的内容。

评分(0–100)是至少一个风险项造成破坏的概率。展开 What adds to it 可查看 每一项及其分值:文件按影响范围 × 未测试比例、发现、契约变更、依赖。结论 总是附带原因;请先阅读原因再看 diff。

从这里开始

改动过的代码文件,风险最高的在前。每一行显示有多少现有页面或操作会运行该文件、其 diff 覆盖率、发现及其自身风险分值;接入 Datadog 时,还会显示其流量和生产错误。打开内联 diff:没有测试运行的添加行标记为 ○,有发现的行标记为 ⚠。

使用方法:自上而下审查,并在每个文件上勾选 Mark as reviewed。评分会随之下降,因此你 始终知道还剩多少风险。其下是需快速浏览的低风险文件、环境/基础设施/CI 文件 (按环境检查其取值),以及无需审查的文件(文档、锁文件、测试)。

位于功能开关之后

在每个页面或操作行上,以及顶部面板中:标记完全位于开关之后的入口点。仅被 仅位于开关后的入口点使用的文件会退出审查,标记为 behind a flag 而非已审查。用于 以暗发布方式上线的进行中功能,使剩余评分反映用户实际会触及的内容。

页面 / 操作

每个新增和受影响的入口点,以列表或按模块的树形展示。打开一个即可查看改动如何逐文件 到达它。接入 Datadog 时,每个入口点会显示其流量占比和不同错误,最繁忙的在前。用它来 判断影响范围:一个改动到达 3 个很少使用的管理页面,与到达结账页面并不相同。

契约(后端)

每个请求或响应结构发生变化的端点,标记为 破坏性(为基线构建的客户端可能失败)、 危险(它们可能行为异常)或 安全(兼容性新增),并附上确切字段和来自 Datadog 的调用服务。 用它确认每个破坏性变更都是有意为之且其客户端已更新,然后勾选 checked with clients。

A breaking contract change: GET /brands no longer returns response[].name

安全、依赖、覆盖率

评分背后的证据:改动行上的安全发现(每项带一个 checked 复选框)、新增或升级的 存在已知漏洞的依赖,以及每个文件的 diff 覆盖率。未提供的报告显示为 not verified,绝不会显示为通过。

测试计划

需要手动尝试的新增或受影响的页面或操作,新增的排在前面,然后按到达它们的文件风险排序。PR 评论将其列为任务清单。当你在本地或预览环境运行该分支时,使用它。它会跟随你的审查进度:一旦某个条目背后的所有变更文件都被标记为已审查,该条目也会被标记(✓ 文件已审查),或者标记为 🚩 处于标志之后。它自己的复选框,用于手动测试,仍由你勾选。

审查者

谁应该审查:编写此 PR 所更改代码的人(git blame,按每个文件的风险加权),加上 CODEOWNERS。PR 自身的作者和机器人被排除在外,不活跃的人会被标注。使用它来请求了解最高风险文件的人,而不仅仅是任何有空的人。

结果

审查的 Markdown 记录:初始判定和分数、当前分数、标记的入口点、已审查的文件,以及已检查的发现和契约变更。复制为 Markdown 并粘贴到 PR 中作为你的审查评论,以便下一个人知道查看了什么。

自上次推送以来

在推送到一个打开的 PR 后,评论会携带第二份报告,仅包含自上次推送以来的提交(analyze --since <sha>;强制推送后,从其合并基点开始)。其判定、分数和从这里开始仅涵盖这些更改。使用它进行第二轮审查:检查修复是否完成了要求且没有其他改动,然后仅当新提交触及新地方时才回到完整报告。

类别和原因

保留在详情和评论的折叠块中,以便审查者先形成自己的意见:isolated(仅新增)、touches-existing(现有代码更改)、global(全局文件、清单或关键依赖)和 unknown(部分更改无法读取:绝不安全)。每个原因代码都在 docs/reasons.md 中。

工作原理

  1. 对比合并基点和头部,检测重命名并忽略仅空白和尾随逗号的行。
  2. 读取两棵树而不运行任何东西:TypeScript 编译器 API、go list + go/types(离线)、Python ast。从中获取入口点以及哪个声明使用哪个声明的图。
  3. 比较声明通过其代码的哈希,因此注释和格式从不计入。新声明和入口点是附加的。
  4. 可达性:从每个更改的声明到运行它的每个入口点,保留最短链。
  5. 分类、加权和计划:类别、分数、判定、起始文件、测试清单。

在四个仓库的 662 个历史提交上,快速通道在 56 个提交中有 0 个问题,分数的 AUC 为 0.72–0.83;在你自己的历史上运行 farol calibrate。每种语言的详细信息在 docs/how-it-works.md 中,每个选择背后的原因在 docs/DECISIONS.md 中。

安全和隐私

  • analyze 从不执行被分析仓库的代码,且从不使用网络。Go 和 Python 助手仅使用其标准库并且仅进行解析。
  • 只有 comment(GitHub API)和 usage(Datadog API)与网络通信。错误消息不从 Datadog 复制,仅错误类型。
  • 在 CI 中,运行你的代码的作业没有机密和写令牌;发表评论的作业从不运行你的代码。
  • 影子模式:farol 提供信息,它从不批准、请求更改或使检查失败。

限制

  • 静态读取:运行时构建的值不可见(计算出的路由路径、getattr、importlib、DI 容器、循环中填充的注册表)。当无法读取入口点时,该 PR 为 unknown;当无法解析某个调用时,farol 假定它触达的范围更多,而非更少。
  • TypeScript 前端:仅支持 React Router、Next.js 和 TanStack Router(基于文件):不支持基于代码的 TanStack 路由树、Remix/React Router 文件约定、Vue 或 Svelte。JavaScript 文件、重命名后的文件和 TypeScript 后端使用模块级可达性。
  • Go:来自 chi/gin/echo 分组之外中间件栈的路由前缀不会被应用,且用自身注册 API 包装路由器的项目类型(Gitea 的 web.Router)不会被读取。Python:Django REST Framework 路由器不会被读取。
  • 评分和判定阈值是在少数仓库上检验过的启发式规则。请在你的仓库上校准它们。

常见问题

我们已经有 AI 审查器了。为什么还要加 farol? 它们回答的是不同的问题。该 agent 评论 diff 中的代码行;在它之后,farol 通过对整个程序的静态分析,告诉批准合并的人这次合并有多大风险、哪些文件承载了风险。两者都运行:farol 的评论和报告与 agent 的并列显示,且都不会阻止合并。

它是免费的吗? 是的。farol 是 MIT 许可证下的开源项目,没有账户、托管服务或付费层级:用 npx @winninjs/farol 或 GitHub Action winnin/farol/action 运行它。

它会拖慢 CI 吗? 不会。farol 在你的测试之后运行,并复用它们的报告。对于一个 300 文件的 Python 服务,分析约需 3 秒;对于大型 TypeScript monorepo 或冷缓存的 Go 服务,需 8–10 秒;当辅助输出被缓存时,base 不到一秒。

我可以在 GitHub 之外使用它吗? 可以。analyze、html 和 report 在任何地方都能用;只有 comment 是 GitHub 专用的。用你平台的 API 发布 npx @winninjs/farol report 的输出。

报告显示“未验证”。 该报告未被提供,或路径错误。传给 analyze 的路径相对于当前目录;farol.config.json 中的路径相对于项目。

我更改的页面没有被列出。 运行 npx @winninjs/farol routes 查看 farol 读取的入口点,运行 npx @winninjs/farol impact <file> 查看什么能触达某个文件。无法读取的路由构造会列在诊断信息下。

Go:“farol 离线读取 Go 模块:运行 go mod download”。 模块缓存缺少 base 或 head 的某个依赖。分析前对两者都运行 go mod download。

Monorepo? 将 --cwd 指向该应用。路径保持相对于仓库根目录,项目未使用的应用中的更改会被列为项目之外。

贡献

欢迎贡献:路由适配器、框架、语言、报告格式和更好的启发式规则。有关设置、架构和约定,请参阅 CONTRIBUTING.md;要报告漏洞,请参阅 SECURITY.md。

许可证

MIT © 2026 Winnin

来源:Hacker News · github.com