一个只审查 Git Diff 新增行 的 TypeScript 代码审查工具。它用 Tree-sitter 补齐函数、类型和调用位置,把确定性问题交给 ESLint/Semgrep,把需要上下文推理的逻辑问题交给 OpenAI 兼容 LLM;最后再次校验文件和行号,无法落到真实新增代码的结果不会发布到 PR。
真实回归样例:Diff 映射 → AST/静态分析/LLM → 行号校验 → GitHub 行内评论。
- 资源已创建但成功、异常或提前返回路径没有释放。
- Promise、Future 或回调中的异常被丢失。
- 提交混入与目标无关的状态变化或业务行为。
- LLM 因缺少上下文而引用不存在的函数、文件或行号。
- 同一个格式/语法问题被静态工具和模型重复报告。
- 已确认的误报在后续扫描中反复出现。
支持 .js、.jsx、.ts、.tsx、.mjs、.cjs 和 .dart。Web 与 Flutter 夹具在每次规则或 Prompt 变更后都会重新运行。
flowchart LR
A[Git Diff] --> B[新增行坐标]
B --> C[Tree-sitter 上下文]
B --> D[ESLint]
B --> E[Semgrep]
C --> F[上下文 LLM]
D --> G[规范化与去重]
E --> G
F --> G
G --> H[误报抑制]
H --> I{文件与行号属于 Diff?}
I -- 是 --> J[CLI 输出或 PR 行内评论]
I -- 否 --> K[过滤并计数]
模型只收到本次变更涉及的 AST 片段、所在符号和有限的相关调用位置,不负责格式、语法和简单模式规则。每个保留结果都包含:file、line、reason、trigger、severity、source、ruleId 和稳定 fingerprint。
要求 Node.js 20+。Semgrep 是可选外部命令;未安装时会给出诊断,ESLint 与 LLM 结果仍会继续处理。
npm install
cp .diff-reviewer.example.yml .diff-reviewer.yml
export LLM_API_KEY="..." # 不设置则自动跳过 LLM
npx tsx src/cli.ts review --base origin/main --head HEAD --format markdown构建后也可以运行:
npm run build
node dist/cli.js review --base origin/main --format json --output review.jsonOpenAI 兼容服务通过 llm.apiUrl 和 llm.model 配置。密钥只从 LLM_API_KEY 读取,不写配置文件,也不会输出到日志。
仓库需要完整 Git 历史、项目依赖和 Semgrep。最小工作流:
name: Code review
on:
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
pull-requests: write
issues: read
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-node@v7
with:
node-version: 22
- run: npm ci
- run: pip install semgrep
- uses: jsdnaasd/context-aware-diff-reviewer@v0.1.0
with:
github-token: ${{ github.token }}
fail-on: never
env:
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}pull-requests: write 用于创建行内 Review,issues: read 用于读取 PR 评论里的误报命令。来自 Fork 的 PR 默认拿不到仓库 Secret,此时 LLM 会被跳过,静态扫描仍可运行。不要改用 pull_request_target 执行不可信分支代码并同时暴露 LLM 密钥。
Action 输出 findings-count 和完整 JSON result。fail-on 支持 never、error、any。
行内评论会显示 finding 指纹。仓库 Owner、Member 或 Collaborator 可以在 PR 普通评论中写:
/diff-reviewer ignore 3a11c09e4f20d834
/diff-reviewer ignore-rule context.subscription-not-cancelled lib/generated/**
命令可以发在 PR 普通评论或行内 Review 回复中。第一次按单条指纹跳过;第二次按规则和文件范围跳过。Action 每次运行都会重新读取这些可信成员的反馈。需要跨 PR 长期保存时,将等价规则加入 .diff-reviewer.yml:
suppressions:
- ruleId: context.subscription-not-cancelled
path: "test/**"
reason: fixture intentionally keeps a process-wide listener完整示例见 .diff-reviewer.example.yml。常用项:
| 配置 | 作用 |
|---|---|
include / exclude |
控制进入流水线的文件 glob |
severityThreshold |
info、warning 或 error |
maxContextChars |
单个 AST 片段上限 |
maxPromptChars |
单次 LLM 请求上限,超出会分批 |
eslint.command |
使用项目自身 ESLint 配置的命令 |
semgrep.config |
Semgrep 规则文件 |
suppressions |
指纹、规则、来源与路径范围抑制 |
仓库自带的 rules/semgrep.yml 只放确定性较高的规则。若目标项目有自己的 ESLint/Semgrep 配置,可以直接覆盖命令或规则路径。
{
"file": "src/order-service.ts",
"line": 42,
"reason": "事务在成功路径和异常路径都没有结束。",
"trigger": "begin 成功后 insert 返回或抛错时,没有 commit/rollback。",
"severity": "error",
"ruleId": "context.transaction-not-finalized",
"source": "llm",
"fingerprint": "3a11c09e4f20d834"
}如果模型给出 src/order-service.ts:99,但第 99 行不是本次新增行,结果会进入 invalidFindings 统计,不会显示或评论。
npm run check
npm test
npm run review:fixtures
npm run build
npm run smoke:actionexamples/web 验证事务未结束,examples/flutter 验证订阅未取消。测试使用假的 LLM/GitHub 边界,不发真实网络请求;规则或 Prompt 改动后会自动验证原有问题仍可定位,同时拒绝不存在的文件和行号。
- 这是 Diff Review 工具,不是完整的跨仓库数据流或 SAST 引擎。
- 相关调用位置目前限于同一文件,避免提示词无界增长。
- ESLint 使用目标仓库的安装与配置;Semgrep 未安装时会降级并给出诊断。
- 只有启用 LLM 时,AST 代码片段才会发送到你配置的 API。使用前应确认代码托管和模型供应商的数据政策。
- 二进制、删除文件、生成文件、锁文件和无法读取的文件默认不进入审查。
项目规格、验收条件和决策记录位于 docs/。许可证为 MIT。
