clawpatch: OpenClaw 生态的代码审查自动修复利器

一句话定位
将代码仓库映射为语义特征切片,用 LLM 逐片审查并持久化发现的问题,再通过显式命令驱动修复。
快速概览
- 仓库: openclaw/clawpatch
- 版本: v0.3.0(2026-05-15 创建,仅 7 天)
- 技术栈: TypeScript (strict, ESM, Node.js),仅有 zod 一个运行时依赖
- 代码量: ~49K LOC(含 31K 测试)
- 协议: MIT
- npm 包:
pnpm add -g clawpatch
OpenClaw 生态中的位置
clawpatch 不是孤立项目——它是 openclaw GitHub 组织下的一个垂直工具。该组织的旗舰产品 OpenClaw(373K stars)是一个自托管的个人 AI 助手,管理你的邮件、日历、消息和文件。openclaw 团队围绕"个人 AI agent"构建了完整工具链,全部使用 TypeScript strict + zod schema 的统一工程标准:
373K stars] --> B[clawpatch
代码审查修复] A --> C[clawsweeper
Issue 自动扫描] A --> D[clownfish
批量修复编排] A --> E[crabpox
热盒环境测试] A --> F[lobster
工作流宏引擎] A --> G[acpx
ACP 协议 CLI] G -->|直接依赖| B style A fill:#e74c3c,stroke:#c0392b,color:#fff style B fill:#2ecc71,stroke:#27ae60,color:#fff style G fill:#f39c12,stroke:#e67e22,color:#fff
技术依赖关系:clawpatch 的 acpx provider 直接使用 openclaw/acpx(2.7K stars)——一个基于 Agent Client Protocol 的无头 CLI,可以让 clawpatch 通过标准协议调用 Codex、Claude、Gemini 等任意编程 agent。
核心架构
clawpatch 的工作流分 6 个阶段,每个阶段都是显式命令而非自动化黑箱:
项目检测] --> B[map
特征切片] B --> C[review
LLM 审查] C --> D[report
发现报告] C --> E[triage
去重分级] D --> F[fix
显式修复] F --> G[revalidate
验证修复] G --> H[open-pr
提交 PR] style C fill:#9b59b6,stroke:#8e44ad,color:#fff style F fill:#e74c3c,stroke:#c0392b,color:#fff
阶段 1:init — 项目检测
零 LLM 调用。自动识别:Git 信息、语言(20+ 种)、框架(Next/Nest/Django/Rails/Phoenix/ASP.NET 等)、包管理器(npm/pnpm/gradle/maven/pip/poetry/mix 等)、项目命令(typecheck/lint/test/format)。
阶段 2:map — 语义特征切片
这是 clawpatch 最核心的设计。不是按文件列表逐行审查,而是将代码库分解为语义特征单元:
- 17 个启发式 mapper:Node、Next.js、React、Go、Python、Ruby、Elixir、Rust、.NET、Swift、Gradle、Maven、Laravel、C/C++、Apple、路由、配置
- 每个特征记录:入口文件、主文件(ownedFiles)、上下文文件(contextFiles)、关联测试、信任边界
- 双层映射决策引擎:先跑启发式 mapper → 评估覆盖率 → 若 coverage < 25% 或 meaningful features ≤ 2,自动切换 agent mapper(LLM 辅助)
阶段 3:review — LLM 审查
组装特征的 owned + context + test 文件为 bounded prompt context(可配 maxOwnedFiles=12, maxContextFiles=24,单文件 24K 字符截断)。通过 strict JSON schema 约束 LLM 输出 10 类发现:bug、security、performance、concurrency、api-contract、data-loss、test-gap、docs-gap、build-release、maintainability。
支持并发审查(默认 10 jobs),provider 支持:Codex、OpenCode、ACpx、Grok、Pi、mock。
阶段 4-6:fix → revalidate → open-pr
修复是显式且安全的:
clawpatch fix --finding <id>— 仅修复指定 finding- 拒绝 dirty worktree(除非 --force)
- 不 commit、不 push、不开 PR — 需显式
open-pr --patch <id> - 修复后必须
revalidate验证才能标记 fixed
深入 map:语义特征切片引擎
map 是 clawpatch 架构中最具设计深度的子系统。17 个语言/框架专用 mapper 并行运行,输出统一的种子结构,经过去重、ID 生成、测试发现、变更检测后落盘为持久化 FeatureRecord。当启发式覆盖不足时,agent-mapper 决策引擎自动唤醒 LLM 补位。
完整数据流
17 个 mapper 并行"] B --> C1["pythonSeeds"] B --> C2["rustSeeds"] B --> C3["goSeeds"] B --> C4["nodeSeeds"] B --> C5["... 13 more"] C1 & C2 & C3 & C4 & C5 --> D["flat + dedupeSeeds()"] D --> E["mapFeatureSeeds()
seed → FeatureRecord"] E --> F["featureIdentity()
stableId('feat', [...])"] E --> G["nearbyTests()
自动测试发现"] E --> H["变更检测
created/changed/stale"] F & G & H --> I["agent-mapper.ts
mapWithSource()"] I --> J{"weakMap()
覆盖率评估"} J -->|"coverage ≥ 25%
meaningful > 2"| K["保留 heuristic"] J -->|weak=true| L["唤醒 LLM
agent map"] L --> M["mergeMapResults()
heuristic + agent"] K & M --> N[".clawpatch/features/*.json"] style B fill:#2ecc71,stroke:#27ae60,color:#fff style I fill:#9b59b6,stroke:#8e44ad,color:#fff style J fill:#f39c12,stroke:#e67e22,color:#fff
第一层:FeatureMapper 统一接口
每个 mapper 实现相同的契约,共享 MapperContext(含 Node 项目发现 + Turborepo task graph):
type FeatureMapper = {
name: string;
map(root: string, context: MapperContext): Promise<FeatureSeed[]>
}
输出统一的 FeatureSeed 结构——这是 map 子系统的"内部通货":
- entryPath:入口文件(如
src/main.rs) - identityKey:去重键——同 source 下的稳定标识
- kind:11 种特征类型(cli-command、route、service、library 等)
- trustBoundaries:信任边界标记(user-input、network、database、secrets 等)
- ownedFiles / contextFiles / tests:三环审查文件体系
Python mapper 解剖(1902 行)
这是最复杂的 mapper,将一个 Python 项目分解为 6 类 feature seed:
- 项目元数据(source: "python-project"):解析 pyproject.toml/setup.py/setup.cfg,ownedFiles 为元数据文件集合
- CLI 入口(source: "python-console-script"):解析 [project.scripts] / [tool.poetry.scripts],逆向解析 module:function → 实际 .py 文件路径
- Flask 路由(source: "python-flask-route"):正则扫描 @app.route("/path") 装饰器
- FastAPI 路由(source: "python-fastapi-route"):正则匹配 @router.get/post/put/delete,处理 include_router() 嵌套
- Django 路由(source: "python-django-route"):解析 urls.py 的 urlpatterns 列表,检测 from django.urls import 导入
- Source Groups(source: "python-source-group"):通过 partitionFileGroups() 将 src/ 文件分组成 ≤12 个文件的审查单元
测试命令推断同样精细——顺序检测 uv.lock → poetry.lock → pdm.lock → hatch.toml,分别返回 "uv run pytest" / "poetry run pytest" / "pdm run pytest" / "hatch run pytest"。
Rust mapper 对比(358 行)
Rust 的 mapper 结构更简洁,体现了"约定优于配置"的语言特点:
- 检测 Cargo.toml → 解析 [workspace] members(含 glob 展开 crates/*)
- src/main.rs → "Rust command"(source: "rust-command")
- src/lib.rs → "Rust library"(source: "rust-library")
- src/bin/*.rs → 每个 bin 独立 feature
- tests/*.rs → 集成测试 feature
- 对 workspace 每个 member 递归上述流程
Python 1902 行 vs Rust 358 行反映了 mapper 深度的现实差异——Python 需要处理 3 个 Web 框架 + 4 个包管理器的组合爆炸,而 Rust 的 Cargo 约定使得工作区发现更统一。这也意味着 Python 项目的审查切片质量优于 Rust。
第二层:Seed → FeatureRecord 的桥接
mapFeatureSeeds() 完成了 4 项关键转换:
① 稳定 ID 生成
featureId = stableId("feat", [
seed.kind, // "route"
seed.source, // "python-django-route"
seed.entryPath, // "config/urls.py"
seed.identityKey ?? seed.command ?? seed.route ?? seed.symbol ?? ""
])
同一 feature 在不同运行中产生相同 ID——这是后续 review → fix → revalidate 链路的锚点。
② 自动测试发现
如果 seed 未设 skipNearbyTests: true,nearbyTests() 自动扫描入口文件所在目录 + test/tests/__tests__/src/,按文件名匹配(stem.test.ts、tests/stem.rs),上限 5 个。覆盖 JS/Rust/Swift/C/C++ 的测试命名约定。
③ 变更检测
const featureChanged =
JSON.stringify(stripVolatile(previous)) !== JSON.stringify(stripVolatile(feature))
// 如果 ownedFiles 变了,且之前是 "reviewed"/"revalidated"/"fixed"
// → 自动重置为 "pending"
④ FileGroup 智能分区
当 mapper 遇到大量源文件(如 Python src/ 下 45 个 .py),partitionFileGroups() 使用层级分区算法:
- 深度 0:按第一层子目录拆分(src/auth/、src/models/、src/api/)
- 超过 maxFiles 的分组进入深度 1:按文件名"家族"前缀分组(user.ts、user.service.ts、user.test.ts → family "user")
- 排除无意义的家族名:index、main、shared、helper、util、type、test、spec
- 仍然超限的用 chunkFiles() 切片,标签追加 #1、#2 后缀
最终每个审查单元 ≤12 个 owned 文件,保持 prompt context 可控。
第三层:Agent Mapper 决策引擎
这是 clawpatch 最务实的设计——启发式优先,仅在覆盖不足时用 LLM 补位。
weakMap() 实现三条判定规则:
| 条件 | 判定 | reason |
|---|---|---|
| features.length === 0 | weak | "heuristic mapper produced no features" |
| sourceFiles ≥ 4 且 sourceCoverage < 0.25 | weak | "heuristic map covers 12% of source files" |
| sourceFiles ≥ 12 且 meaningful features ≤ 2 | weak | "heuristic mapper produced too few meaningful features" |
| 其他情况 | ok | "heuristic map is meaningful" |
repoInventory() 先收集整个仓库的文件清单——首选 git ls-files -z,回退目录遍历——过滤掉 node_modules、dist、.git、vendor 后统计 sourceCoverage(已被 owned 的源文件占比)。
当判定 weak 时,将仓库清单(manifests、topLevelDirs、sourceFileSamples 前 500、testFileSamples 前 200)发给 LLM,要求返回 strict JSON 的 AgentMapOutput。LLM 返回的特征经 Zod 验证后与 heuristic 结果 merge——同 featureId 的 agent 特征优先覆盖。
如果 agent mapper 返回 0 条有效特征且 source="agent"(用户显式要求),抛出错误;如果 source="auto",保留 heuristic 结果。
安全遍历保障
整个 map 子系统共享安全文件遍历基元:
- 符号链接拒绝:
walk()和isSafeFile()检测并跳过所有 symlink - 路径逃逸防护:
realpath()解析后与仓库 root 比较,拒绝越界路径 - 自动跳过清单:node_modules、dist、build、coverage、.git、.clawpatch、.venv、__pycache__、target、Pods、Carthage 等 18 个常见生成/依赖目录
- null byte 检测:拒绝含