当你提交一个 Pull Request 时,最常被问到的一句话是:「这次改动会影响哪些功能?」人工回答这个问题既慢又容易遗漏。本教程带你从零构建一个 Claude Code Skill——code-impact-analyzer,它能自动解析 git diff,沿着调用链与依赖关系扩散,输出一份「受影响文件 / 函数 / 测试 / 风险等级」的结构化报告。你将完整掌握 Skill 的三要素:SKILL.md 元数据触发、渐进式披露正文、脚本封装,并学会用 subagent 并行加速分析。适合已跑通 Claude Code、想从「会写 Skill」进阶到「能写出工业级 Skill」的开发者。
claude --version 有输出)ripgrep(rg)、git、Node.js 18+ 或 Python 3.10+(脚本二选一)写 Skill 之前,先用一句话定义它的输入、动作、输出,这决定了后续所有设计:
HEAD 与目标分支的差,或两个 commit)为什么用 Skill 而不是写个普通脚本?因为「沿调用链扩散」这一步需要语言语义理解,纯正则会漏;而让模型来判断「谁调用了被改的函数」时,又需要先把仓库相关文件喂给它——这正是 Skill「按需加载上下文」机制的用武之地。脚本能精确做到的(grep 导入、解析符号)交给脚本,需要语义的(判断调用关系、评级)交给模型。
Skill 就是一个带 SKILL.md 的目录。个人级放在 ~/.claude/skills/,项目级放在 .claude/skills/。我们用项目级,因为它跟着仓库走、可被团队共享:
mkdir -p .claude/skills/code-impact-analyzer/{scripts,references}
cd .claude/skills/code-impact-analyzer
创建 SKILL.md,先只写最关键的 frontmatter:
---
name: code-impact-analyzer
description: Analyzes the blast radius of a git diff — extracts changed symbols,
expands one hop along call/import edges, flags related tests, and writes a
structured impact report. Use when the user asks "what does this change
affect", before opening a PR, during code review, or when sizing a refactor.
---
这一小段 frontmatter 就是 Skill 的全部「常驻开销」。 Claude 在每个会话里只会看到所有 Skill 的 name + description(即 Level 1 加载),看不到正文。所以 description 是 Skill 的「触发广告位」,必须同时包含:
写好这两点,模型才能在用户说「这个改动稳吗」时自动联想到你的 Skill。
预期结果:在仓库里执行 claude,输入「分析一下我当前改动的影响」,模型应该能自动加载本 Skill。如果没触发,多半是 description 里缺了用户真实会说的话术。
Skill 设计的黄金法则:凡是确定性逻辑,封装成脚本让模型调用,而不是让模型现场推理。 这能省 token、提准确率、可回归测试。
创建 scripts/extract-symbols.sh,它负责从 diff 里抽出被改动的符号名:
#!/usr/bin/env bash
# 从 git diff 中提取被改动的函数/方法/类符号名
# 输入: 通过 stdin 传入 diff,或读取第一个参数指向的 diff 文件
set -euo pipefail
if [[ $# -ge 1 ]]; then
diff_text="$(cat "$1")"
else
diff_text="$(cat)"
fi
# 提取 hunk 中新增/删除行,再用启发式匹配符号定义
echo "$diff_text" \
| grep -E '^\+' \
| grep -oE '(def|function|func|class|async def|public|private|protected)\s+[A-Za-z0-9_]+' \
| awk '{print $NF}' \
| sort -u
创建 scripts/find-references.sh,给定一个符号名,找出仓库里所有引用它的位置(即「影响扩散」的第一跳):
#!/usr/bin/env bash
# 用法: find-references.sh <symbol> [search_root]
# 依赖: ripgrep (rg)
# depends: ripgrep
set -euo pipefail
symbol="${1:?usage: find-references.sh <symbol>}"
root="${2:-.}"
rg --no-heading -n "\b${symbol}\b" "$root" || true
两个关键细节:
# depends: ripgrep 这种「依赖声明」写在脚本头部,模型在调用前会检查依赖是否满足,不满足会先帮你装或换方案。这是 Claude Code 对 Skill 脚本的约定。set -euo pipefail 严格模式、清晰的标准输入/输出、可机器解析的结果(file:line:content)、--help 友好的参数校验。模型读起来顺,人调试也顺。赋予执行权限:
chmod +x scripts/*.sh
预期结果:echo "def foo():" | ./scripts/extract-symbols.sh 输出 foo;./scripts/find-references.sh foo src/ 列出所有引用 foo 的行。
frontmatter 之下,开始写正文。正文不要一次性堆满所有细节——那是反模式,会让模型在被触发时被迫读一大段用不上的内容。正确做法是把 SKILL.md 写成「目录 + 操作步骤」,重资料放进 references/ 按需加载。
这是 SKILL.md 的完整正文:
# Code Impact Analyzer
You are performing a change impact (blast radius) analysis on the current git
working tree. Produce a report that lets a reviewer answer "what does this
change touch?" in under 30 seconds.
## Procedure
1. **Get the diff.** Default to `git diff $(git merge-base HEAD main)` to compare
against the main branch. If the user names two commits/branches, use those.
Save it to a temp file: `git diff <base> > /tmp/impact.diff`.
2. **Extract changed symbols.** Run:
`${CLAUDE_SKILL_DIR}/scripts/extract-symbols.sh /tmp/impact.diff`
Keep the unique symbol list as `CHANGED`.
3. **Expand one hop.** For each symbol in CHANGED, run:
`${CLAUDE_SKILL_DIR}/scripts/find-references.sh <symbol> <repo-root>`
Collect non-test files into `AFFECTED_PROD`, test files into `RELATED_TESTS`.
De-duplicate. Stop at ONE hop — do not recurse, it inflates the report.
4. **Classify risk.** Score each AFFECTED_PROD file:
- High: public API / entry point / shared utility touched
- Medium: internal module with >5 callers
- Low: leaf or test-only change
See `references/risk-rubric.md` for the full rubric if a case is ambiguous.
5. **Write the report** to `IMPACT-REPORT.md` in the repo root and also summarize
inline. Use the four-section template in `references/report-template.md`.
## Output sections (in order)
直接改动 | 间接影响(一跳) | 关联测试 | 风险摘要
## Guardrails
- Never claim a file is affected without a grep hit from step 3.
- If the diff is empty, say so and stop — do not invent impact.
- Cap the report at 40 affected files; if more, summarize and warn.
三个值得注意的设计:
${CLAUDE_SKILL_DIR} 是 Skill 专用的环境变量,永远指向当前 Skill 目录。脚本用它来定位,比写死路径更可移植。references/。模型只在需要时(步骤 4 遇到模糊情况、步骤 5 写报告时)才读它们——这就是 Level 3 的按需加载,能省下大量 token。创建 references/risk-rubric.md:
# Risk Classification Rubric
| 维度 | High | Medium | Low |
|------|------|--------|-----|
| 暴露面 | 公开 API / 路由入口 / SDK 导出 | 内部服务模块 | 叶子函数 |
| 调用方数量 | >10 | 2-10 | 0-1 |
| 是否有测试覆盖 | 无 | 部分 | 完整 |
| 并发/状态 | 改了共享状态 | 改了纯逻辑 | 改了常量/注释 |
判定规则:任一维度命中 High 即整体 High;否则取最高维度。
创建 references/report-template.md:
# 影响面报告
## 直接改动({{count}} 个文件)
- `path/to/file.py` — 新增 `foo()`,删除 `bar()`
## 间接影响(一跳,{{count}} 个文件)
- `src/api/handler.ts:42` — 调用 `foo`
- `src/utils/helper.py:88` — 导入 `foo`
## 关联测试({{count}} 个)
- `tests/test_foo.py` — 建议优先重跑
## 风险摘要
整体风险:**High/Medium/Low**。最需关注:`<file>`,原因:`<one line>`。
建议回归范围:`<test command>`。
这些文件不会默认进上下文。只有当模型执行到「评分」或「写报告」时才会主动读——这正是「98% Token 节省」架构的精髓:常驻的只是元数据,重资料延迟到真正需要的那一刻。
当 CHANGED 符号很多时,串行 grep 会慢。Claude Code 允许 Skill 在正文里指示模型派发 subagent 并行处理。在 SKILL.md 末尾补一段:
## Scaling up
If CHANGED contains more than 8 symbols, do NOT call find-references.sh serially.
Instead, spawn one subagent per batch of symbols, each running the script on its
batch and returning a deduped JSON array of {symbol, file, line}. Merge results.
This keeps wall-clock under 20s even for large diffs.
并行 subagent 的收益是双重的:速度快(多符号同时扩散),上下文干净(每个 subagent 在自己的上下文里读 grep 结果,主对话只收回精简的 JSON,不会被海量 file:line 行撑爆)。
Skill 写完,分两层验证。
层一:脚本单测(确定性逻辑必须可回归):
# 准备一个迷你 diff
printf 'def process_order():\n return None\n' > /tmp/sample.diff
./scripts/extract-symbols.sh /tmp/sample.diff
# 期望输出: process_order
./scripts/find-references.sh process_order src/ | head
# 期望输出: src/checkout/service.py:55: result = process_order()
层二:端到端触发(验证 description 写得好不好):
cd <你的仓库>
# 人为制造一个改动
git checkout -b feat/demo
echo "def new_endpoint(): pass" >> src/api.py
git add src/api.py
claude
# 在对话里输入这些「自然话术」,看 Skill 是否被自动加载:
# 1. "这次改动会影响哪些功能?" ← 应触发
# 2. "帮我看看这个 PR 的 blast radius" ← 应触发
# 3. "评估一下重构 auth 模块的工作量" ← 应触发
# 4. "重命名这个变量" ← 不该触发(与影响面无关)
预期结果:前三句应看到 Skill 被加载(界面会提示 loaded code-impact-analyzer),并最终在仓库根目录生成 IMPACT-REPORT.md。第四句不应触发——如果触发了,说明 description 里 refactor 这个词太宽泛,需要收窄。
模拟一次「改了底层工具函数」的危险改动:
git checkout -b feat/touch-shared-util
# 改动一个被广泛调用的工具函数
cat >> src/utils/format.py <<'EOF'
def format_currency(amount: float) -> str:
# 行为变更:原来返回 "$1.00",现在返回 "USD 1.00"
return f"USD {amount:.2f}"
EOF
git add src/utils/format.py
在 Claude Code 里说:「分析这次改动的影响面」。模型会:
git diff main 拿到改动,发现符号 format_currencyfind-references.sh format_currency src/,发现 invoice.py、receipt.py、report.py 等 12 处调用test_format.py、test_invoice.py 归入关联测试IMPACT-REPORT.md,建议重跑 pytest tests/test_invoice.py tests/test_receipt.py这份报告直接能贴进 PR description——而这正是 reviewer 想要的第一屏信息。
flowchart TD
U[用户: 这次改动影响哪些功能?] -->|description 命中| L1[Level1: 仅 name+description 常驻]
L1 --> L2[Level2: 加载 SKILL.md 正文]
L2 --> S1[脚本: git diff]
S1 --> S2[脚本: extract-symbols.sh]
S2 --> SYM[CHANGED 符号列表]
SYM -->|符号>8| PARA[并行 subagent 批量扩散]
SYM -->|符号<=8| SERIAL[串行 find-references.sh]
PARA --> HIT[命中文件:行 + 测试]
SERIAL --> HIT
HIT --> L3A[Level3 按需读 risk-rubric.md 评级]
L3A --> L3B[Level3 按需读 report-template.md]
L3B --> R[生成 IMPACT-REPORT.md]
R --> PR[贴入 PR description]
三层加载的层次一眼可见:L1/L2 是 Skill 框架的常驻与按需,L3A/L3B 是 references 的延迟加载,脚本与 subagent 则承担确定性工作与并行加速。
Q1:Skill 没被触发,模型直接自己 grep 了。
description 没覆盖用户真实话术。把用户最近一周问过的原话逐条写进 When 子句。避免用「analyze」「handle」这类模型已有强先验的泛词。
Q2:脚本里 ${CLAUDE_SKILL_DIR} 没展开。
它只在模型调用脚本时由 Skill 运行时注入,你在终端手动 bash 跑不会展开。手动调试时用 CLAUDE_SKILL_DIR="$PWD" bash scripts/find-references.sh … 临时模拟。
Q3:报告说某个文件受影响,但其实只是注释里提到了符号。
find-references.sh 用的是词边界正则,会误伤字符串/注释。可在脚本里加 --type-not 排除,或在 SKILL.md 的 Guardrails 里要求「调用点需在函数体内」,让模型做语义二次确认。
Q4:大仓库 grep 太慢。
让 subagent 并行(第五步),并给 rg 加 --max-count 50 限制每个文件的命中数。Skill 设计要始终考虑「仓库规模放大 10 倍后还能跑」。
Q5:团队里别人怎么用上这个 Skill?
项目级 Skill(.claude/skills/)跟着 git 走,提交后队友 pull 即得。若想跨项目复用,放 ~/.claude/skills/,或打包成 Plugin 通过 marketplace 分发。
https://claude.com/blog/steering-claude-code-skills-hooks-rules-subagents-and-more — Skills、Hooks、Subagents、Rules 七大定制手段的总览https://claude-with-skills.vercel.app/03-advanced/ — ${CLAUDE_SKILL_DIR}、bundled scripts、subagent 编排的官方写法https://duet.so/guides/claude-code-skills-complete-guide — SKILL.md、MCP、Subagents、团队协作全景https://smithhorngroup.substack.com/p/choosing-between-skills-subagents — 决策矩阵,帮你判断「这个需求到底该用哪种机制」https://alexop.dev/posts/claude-code-customization-guide-claudemd-skills-subagents/ — 三者边界与组合用法${CLAUDE_SKILL_DIR}、脚本封装与 subagent 编排的官方进阶写法[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
