我给 Understand-Anything 提了第一个 PR:为 /understand 加上 --exclude 参数
一个开源项目的 "good first issue",17 个测试变 22 个,5 个文件,107 行增改。记录一下这次从看 issue 到提 PR 的完整过程。
起因:一个被置顶的 issue
Understand-Anything 是一个用 LLM + 静态分析把代码库变成交互式知识图谱的工具。我用了之后觉得效果很好------运行 /understand 之后,/understand-chat 和 /understand-explain 的返回质量比裸用 Claude Code 高很多。
但它有一个明显的痛点:首次运行 /understand 太慢了,token 消耗也很大。
这个问题被 @Antoliny0919 提在了 issue #76,标题是 "How to make /understand run faster ?"。
这个 issue 热度很高------10 个 reaction、16 条评论、被项目作者 Lum1104 置顶 ,还被标记为 good first issue。评论区里 Lum1104 把改进方向拆得非常清楚:
.understandignore文件 --- 类似.gitignore,让用户自定义排除规则 ✅(已实现).understandinclude文件 --- 强制包含被默认规则过滤掉的文件- CLI 参数 ---
/understand --exclude "tests/*,docs/*"← 我要做的 - 非 git 项目的
.gitignore支持
四个方向互不冲突,我选了第 3 个------CLI 参数。改动范围清晰,技术难度适中,非常适合第一次贡献。
动手之前:先读懂现有代码
在写任何代码之前,我把相关文件通读了一遍:
| 文件 | 作用 |
|---|---|
packages/core/src/ignore-filter.ts |
核心的忽略过滤器,加载默认规则 + .understandignore 文件 |
packages/core/src/__tests__/ignore-filter.test.ts |
已有 17 个测试,覆盖默认规则和用户文件 |
skills/understand/scan-project.mjs |
项目扫描脚本,枚举文件 → 过滤 → 语言检测 → 分类 |
agents/project-scanner.md |
Agent 定义,告诉 LLM 怎么调用扫描脚本 |
skills/understand/SKILL.md |
Skill 定义,/understand 命令的完整工作流 |
读完发现,项目已经有一套成熟的 ignore 机制:
优先级(低 → 高):
1. 内置默认规则(node_modules/, dist/, *.lock, ...)
2. .understand-anything/.understandignore
3. .understandignore
三层的 Ignore 实例通过 createIgnoreFilter() 统一管理,调用方只需 filter.isIgnored(path) 即可判断。我的任务是在这个基础上加第四层。
实现:加一层,改五处
最核心的改动只有一行------给 createIgnoreFilter 加一个可选参数:
typescript
// Before
export function createIgnoreFilter(projectRoot: string): IgnoreFilter
// After
export function createIgnoreFilter(
projectRoot: string,
extraPatterns: string[] = []
): IgnoreFilter
用默认空数组保证向后兼容------不传第二个参数的所有旧调用方完全不受影响。
然后在这四个位置串联起来:
1. ignore-filter.ts --- 新增 Layer 4
typescript
// Layer 4: CLI --exclude patterns (highest priority)
if (extraPatterns.length > 0) {
ig.add(extraPatterns);
}
2. scan-project.mjs --- 解析 CLI 参数
原来脚本只接受两个位置参数。我加了 --exclude 的解析逻辑:
javascript
// Parse: <projectRoot> <outputPath> [--exclude <patterns>]
for (let i = 2; i < process.argv.length; i++) {
if (arg === '--exclude' && i + 1 < process.argv.length) {
excludePatterns = process.argv[++i]
.split(',')
.map(p => p.trim())
.filter(Boolean);
}
}
然后在过滤阶段把 excludePatterns 传给 createIgnoreFilter:
javascript
const combined = createIgnoreFilter(projectRoot, excludePatterns);
3. project-scanner.md --- Agent 文档
告诉 LLM agent 如果有 exclude patterns 要在调用脚本时加上 --exclude 参数:
bash
node scan-project.mjs "$PROJECT_ROOT" "$OUTPUT_PATH" --exclude "tests/*,docs/*"
4. SKILL.md --- Skill 文档
- 更新
argument-hint:加入--exclude <patterns> - Phase 0 新增 3.7 节:解析
$ARGUMENTS中的--exclude - Phase 1 把
$EXCLUDE_PATTERNS传给 project-scanner agent
测试:5 个新用例,22/22 全绿
测试文件原来有 17 个用例。我加了 5 个,覆盖所有关键路径:
typescript
describe("createIgnoreFilter with CLI --exclude patterns", () => {
// 1. 基本排除
it("applies CLI exclude patterns alongside defaults", () => {
const filter = createIgnoreFilter(testDir, ["tests/", "e2e/"]);
expect(filter.isIgnored("tests/foo.test.ts")).toBe(true);
expect(filter.isIgnored("src/index.ts")).toBe(false); // 不排除源码
});
// 2. CLI 优先级高于 .understandignore 的取反
it("CLI patterns have highest priority over .understandignore files", () => {
// .understandignore 写的是 !docs/(取反,要包含 docs/)
// CLI 传的是 docs/(要排除 docs/)
// → CLI 赢,docs/ 被排除
});
// 3. ! 取反可以重新包含被默认排除的文件
it("CLI ! negation can re-include files excluded by defaults", () => {
const filter = createIgnoreFilter(testDir, ["!dist/"]);
expect(filter.isIgnored("dist/bundle.js")).toBe(false); // 被取反了
expect(filter.isIgnored("node_modules/foo.js")).toBe(true); // 默认规则依然生效
});
// 4. CLI + .understandignore 同时生效
// 5. 空数组无副作用
});
跑测试:
bash
pnpm --filter @understand-anything/core test
✓ 33 test files passed (33)
✓ 675 tests passed (675) ← 含 ignore-filter.test.ts 的 22 个
提交 & Push
bash
git checkout -b feat/add-exclude-flag
git add [5个文件]
git commit -m "feat: add --exclude CLI flag to /understand for user-defined ignore patterns"
git push myfork feat/add-exclude-flag
5 files changed, 107 insertions(+), 10 deletions(-)
回过头来看
为什么这个 issue 适合新手
- 改动范围明确:Lum1104 在评论里把要做的事情列得清清楚楚,不需要自己猜测
- 代码隔离性好:改动集中在 ignore 模块,不涉及分析引擎、图组装、dashboard 等复杂部分
- 有现成的测试可以参考:对着已有测试的结构写新用例,不会跑偏
- 向后兼容:加的是可选参数,不会破坏任何现有功能
学到了什么
- 读代码比写代码花的时间更长------但绝对值。理解现有架构后再下手,改动会很小很精准
- 开源项目的
good first issue真的是为新贡献者设计的,别怕点进去 - 一个高质量的 issue 描述 + 作者清晰的拆解 = 降低一半的贡献门槛
和 #76 的其他 PR 是什么关系
同一个 issue 下还有其他贡献者在做不同方向的优化:
| PR | 作者 | 方向 |
|---|---|---|
| #314 | parkerault | file-analyzer 用便宜模型降成本 |
| #321 | KennyUMN | 修正 bin/ 排除的注释 |
| #346, #353 | tirth8205 | I/O 并行化加速 |
| 我的 PR | yiziff | --exclude CLI 参数 |
方向互不冲突,各做一个维度------这也是开源协作的魅力。
附:改动文件一览
understand-anything-plugin/
├── agents/
│ └── project-scanner.md (+11 lines, 文档)
├── packages/core/src/
│ ├── ignore-filter.ts (+10 lines, 核心逻辑)
│ └── __tests__/
│ └── ignore-filter.test.ts (+52 lines, 5 个新测试)
└── skills/understand/
├── SKILL.md (+13 lines, skill 定义)
└── scan-project.mjs (+31 lines, CLI 解析)
- Commit:
5afdbc6 - Tests: 22 passed (17 + 5 new)
- Build: ✅ core + skill
- Linked issue: #76