本教程带你从零打造一个技术债扫描 Skill:它能扫描代码库里所有 TODO / FIXME / HACK / XXX 注释,结合 git blame 算出每条债的作者和债龄(沉淀了多少天),并按"严重度 + 陈旧度"排序,自动产出一份技术债体检报告。适合已经装好 Claude Code、想学会自定义 Skill 这项核心扩展能力的开发者。学完后你将掌握 Skill 的目录结构、frontmatter 字段、动态上下文注入(!command``)、支撑脚本、参数替换与触发调优——这套方法论可直接套用到任何重复性工作流上。
| 项目 | 要求 |
|---|---|
| Claude Code | 已安装,版本建议 v2.1.145+(本教程用到 ! 动态注入与 ${CLAUDE_SKILL_DIR} 替换) |
| 终端环境 | macOS / Linux,或开启了 CLAUDE_CODE_USE_POWERSHELL_TOOL 的 Windows |
| 工具链 | grep、git、awk、date(系统自带) |
| 测试仓库 | 任意一个 git 仓库,有几个 TODO 注释最佳 |
核心认知(一句话):Skill 就是一个带 SKILL.md 的文件夹。SKILL.md 顶部是 YAML frontmatter(告诉 Claude "我是什么、何时启用我"),下面是 Markdown 正文(Claude 执行时要遵循的指令)。文件夹名就是你敲的命令名。
很多人误以为 Skill 是把一大段提示词塞进每次对话——恰恰相反。Skill 的精髓是按需加载:
flowchart LR
A[会话启动] --> B[只加载各 Skill 的<br/>description 摘要<br/>≈ 极低成本]
B --> C{是否被触发?}
C -- 用户敲 /命令 --> D[加载完整 SKILL.md 正文]
C -- Claude 判断描述匹配 --> D
C -- 不匹配 --> E[正文永不加载<br/>零成本]
D --> F[执行动态注入 !命令]
F --> G[把脚本输出 + 指令<br/>一起交给 Claude]
G --> H[Claude 产出最终结果]
关键推论:description 是常驻的"目录索引",正文是触发后才花的钱。所以 description 要写好(决定能否被正确触发),正文要写精(一旦加载就持续占 token)。这与 CLAUDE.md 的"事实常驻"形成互补——当一个 CLAUDE.md 段落长成了"操作流程",就该抽成 Skill。
个人级 Skill 放在 ~/.claude/skills/ 下(所有项目可用),项目级放 .claude/skills/(仅当前项目)。我们做一个个人级的:
mkdir -p ~/.claude/skills/tech-debt-scan
cd ~/.claude/skills/tech-debt-scan
最终目录长这样:
~/.claude/skills/tech-debt-scan/
├── SKILL.md # 必需:frontmatter + 指令
└── scan_tech_debt.sh # 可选:支撑脚本(干脏活的)
文件夹名
tech-debt-scan决定了命令名是/tech-debt-scan。frontmatter 里的name字段只是显示标签,不改命令名。
scan_tech_debt.sh(干脏活)Skill 的设计哲学:让脚本做机械、确定的事(grep + blame),让 Claude 做需要判断的事(排序、归类、给建议)。两者分工,既稳又省 token。
把下面内容存为 ~/.claude/skills/tech-debt-scan/scan_tech_debt.sh:
#!/usr/bin/env bash
# scan_tech_debt.sh — 扫描技术债标记,附加 git blame 的作者与债龄
set -euo pipefail
ROOT="${1:-.}" # $1 为空时默认扫当前目录
cd "$ROOT"
# 1) grep 出所有 TODO/FIXME/HACK/XXX,过滤常见无关目录与二进制后缀
grep -rnIE '\b(TODO|FIXME|HACK|XXX)\b[: ]' \
--include='*.py' --include='*.js' --include='*.ts' --include='*.tsx' \
--include='*.java' --include='*.go' --include='*.rs' \
--exclude-dir=node_modules --exclude-dir=.git \
--exclude-dir=dist --exclude-dir=build --exclude-dir=.next \
. 2>/dev/null || true \
| while IFS=: read -r file lineno content; do
[ -f "$file" ] || continue
# 2) git blame 该行,取作者与提交时间戳
blame=$(git blame --line-porcelain "$file" -L "$lineno,$lineno" 2>/dev/null || true)
author=$(awk '/^author /{print $2" "$3; exit}' <<<"$blame")
ts=$(awk '/^author-time /{print $2; exit}' <<<"$blame")
# 3) 把时间戳换算成"债龄天数"
days="-"
if [[ "$ts" =~ ^[0-9]+$ ]]; then
days=$(( ( $(date +%s) - ts ) / 86400 ))
fi
# 4) 严重度初判:FIXME/HACK = 高,其余 = 低(留给 Claude 再细化)
sev="低"
if grep -qE '\bFIXME\b|\bHACK\b' <<<"$content"; then sev="高"; fi
content=$(printf '%s' "$content" | sed 's/^[[:space:]]*//; s/|/\\|/g')
# 输出:严重度 | 文件:行号 | 债龄 | 作者 | 内容
printf '%s|%s:%s|%s天|%s|%s\n' "$sev" "$file" "$lineno" "$days" "$author" "$content"
done
赋可执行权限:
chmod +x ~/.claude/skills/tech-debt-scan/scan_tech_debt.sh
预期结果:在任一 git 仓库里手工跑 ~/.claude/skills/tech-debt-scan/scan_tech_debt.sh,会吐出形如 高|src/auth.py:42|128天|张三|FIXME 密码明文比对应换 bcrypt 的管道分隔行。脚本能独立跑通,是 Skill 稳定的前提。
常见问题:
- 报 grep: ... No such file → 去掉某个 --include 即可,或确认目录有对应后缀文件。
- days 全是 - → 该目录不是 git 仓库,git blame 失败;先 git init 或换到仓库根。
- 作者显示乱码 → 部分系统 awk 取 $2 $3 不够,可改用 git log 的 %an,但对大多数场景足够。
SKILL.md(frontmatter + 指令 + 动态注入)这是核心文件。把下面内容存为 ~/.claude/skills/tech-debt-scan/SKILL.md:
---
name: 技术债扫描
description: 扫描代码库的 TODO/FIXME/HACK/XXX 注释,结合 git blame 算出债龄与作者,输出按严重度和陈旧度排序的技术债清单。在用户问"技术债""还有多少 TODO""哪些 FIXME 该先还""清理技术债"时使用。
argument-hint: [扫描根目录,默认当前目录]
allowed-tools: Bash Read
---
## 待分析的原始债清单
扫描脚本输出(格式:严重度 | 文件:行号 | 债龄 | 作者 | 内容):
\`\`\`
!`${CLAUDE_SKILL_DIR}/scan_tech_debt.sh "$ARGUMENTS"`
\`\`\`
## 你的任务
把上面的原始清单整理成一份**技术债体检报告**:
1. **总览**:报总数,并按标记类型(TODO/FIXME/HACK/XXX)与严重度分桶统计。
2. **明细表**:输出 Markdown 表格,列 `严重度 | 债龄 | 文件:行号 | 作者 | 内容摘要`,**按"严重度降序、债龄降序"排序**——FIXME/HACK 且超过 90 天的排最前。
3. **Top 3 必还债**:逐条说明为什么危险(FIXME=已知缺陷,HACK=脆弱实现,XXX=待定危险区),并给处理方向(删除 / 重写 / 补测试 / 转工单)。
4. **空结果**:若脚本输出为空,明确回复"未发现技术债标记,工作树干净"。
`$ARGUMENTS` 是用户传入的扫描根目录;为空时脚本默认扫当前目录。
Frontmatter 字段(全部可选,仅 description 强烈推荐):
| 字段 | 作用 |
|---|---|
name |
列表里的显示名(不影响命令名) |
description |
最关键。Claude 用它判断何时自动触发。description + when_to_use 合计在列表里被截断到 1536 字符,所以关键用例放最前 |
argument-hint |
敲命令时自动补全里显示的参数提示,如 [扫描根目录] |
allowed-tools |
该 Skill 激活期间,Claude 用这些工具免确认。这里放 Bash Read,因为要跑脚本、可能读文件 |
动态上下文注入(最该掌握的特性):
!`命令` # 行内注入:Claude Code 先执行命令,再用输出替换这一行
```! # 代码围栏注入:整块执行并替换
我们的脚本通过 ${CLAUDE_SKILL_DIR} 引用——这是 Claude Code 内置替换变量,永远指向 SKILL.md 所在目录,跟当前工作目录无关,保证脚本路径不会因你在哪个项目里而失效。
参数替换:$ARGUMENTS 代表用户敲命令时传的全部参数。例如敲 /tech-debt-scan ./apps/web,$ARGUMENTS 就是 ./apps/web。还有 $0/$1(按位置取)、$name(命名参数,需配合 arguments 字段)等。
进入任意 git 仓库(先故意留几个 TODO),启动 claude,然后:
方式 A——显式调用(最可靠):
/tech-debt-scan
或带参数扫子目录:
/tech-debt-scan ./src
方式 B——自然语言触发(靠 description 匹配):
帮我看看项目里欠了多少技术债,哪些 FIXME 最该先还?
Claude 会自动匹配到这个 Skill 并执行。两种方式都应输出类似下面的报告:
## 技术债体检报告
共发现 7 处技术债标记:TODO×4、FIXME×2、HACK×1;高严重度 3 条。
| 严重度 | 债龄 | 文件:行号 | 作者 | 内容摘要 |
|--------|------|-----------|------|----------|
| 高 | 128天 | src/auth.py:42 | 张三 | FIXME 密码明文比对应换 bcrypt |
| 高 | 96天 | utils/cache.ts:11 | 李四 | HACK 临时绕过空指针,上线前必须修 |
| 低 | 30天 | api/order.go:8 | 王五 | TODO 加幂等校验 |
| ... | | | | |
### Top 3 必还债
1. **auth.py:42 密码明文比对**(FIXME / 128天)——已知安全缺陷且超期 4 个月,建议本周改用 bcrypt,并补一组对照测试。
2. **cache.ts:11 临时绕过**(HACK / 96天)——脆弱实现,建议定位空指针根因后删除该分支。
3. ...
Claude Code 有热加载:你改了
SKILL.md或脚本,无需重启,下一条消息即生效。
这是新手最容易踩的坑。官方给出三条排障口诀:
description 没覆盖用户真实说法。补上用户会怎么问的原话("还有多少 TODO""清理技术债"),就像我们已经做的那样。如果你希望这个 Skill 只手动调用、绝不自动触发(比如它是个会改文件的重型流程),加一行:
disable-model-invocation: true
反之,若想隐藏某个"背景知识型" Skill、不让它出现在 / 菜单,用 user-invocable: false。两者组合能精细控制"谁能触发它"。
新建一个最小演示仓库:
mkdir ~/debt-demo && cd ~/debt-demo
git init -q && git config user.email "[email protected]" && git config user.name "demo"
cat > auth.py <<'EOF'
def login(pwd):
# FIXME 明文比对密码,存在时序攻击风险,换 bcrypt
return pwd == "admin"
# TODO 记录失败次数,连续 5 次锁定账号
EOF
cat > cache.ts <<'EOF'
// HACK 这里直接吞掉了空值,先让 demo 跑起来,上线前必须修
export function get(k){ return store[k] ?? null }
// XXX 缓存过期策略待定
EOF
git add -A && git commit -qm "init demo with debt"
然后:
claude
# 进入交互后输入:
/tech-debt-scan
预期:报告会准确识别 4 条债(2 高 2 低),并指出 auth.py 的 FIXME 因为是已知缺陷应优先处理。注意:因为是当天提交,债龄 会显示 0天——这验证了 git blame 链路打通;真实老仓库里你会看到几十上百天的沉淀债。
代码库很大时,有三个增强方向:
1. 跑在子代理里(隔离上下文)。把扫描的大量原始输出关进独立上下文,只把报告回传主会话:
context: fork
agent: Explore
context: fork 让 Skill 在 fork 出的子代理中运行;agent: Explore 用只读探索型子代理,安全且不污染主对话历史。
2. 用 paths 限定生效范围。只在编辑特定文件时才让 Claude 自动加载它:
paths: "**/*.{py,ts,tsx,go,java,rs}"
3. 拆支撑文件。若脚本膨胀,可拆成 scan_tech_debt.sh + format_report.py,在 SKILL.md 里注明每个文件做什么、何时加载,正文保持精简。
记住一个成本铁律:Skill 正文一旦加载,跨多轮都留在上下文里。所以正文要"说要做什么",而非"讲为什么、讲历史",每多一行都是反复支出的 token。
flowchart TD
U[用户: 帮我看看技术债] --> M{Claude 匹配 description?}
M -->|匹配| L[加载 SKILL.md 正文]
M -->|用户敲 /tech-debt-scan| L
L --> S[替换变量: ${CLAUDE_SKILL_DIR}, $ARGUMENTS]
S --> I[动态注入: 运行 scan_tech_debt.sh]
I --> R[拿到原始管道分隔清单]
R --> C[Claude 排序/归类/点名 Top3]
C --> O[输出技术债体检报告]
I -.失败.-> DBG[排查: chmod? git 仓库? 后缀?]
code.claude.com/docs/en/skills)——字段与替换变量的权威来源,建议通读"Frontmatter reference"和"Available string substitutions"两节。/code-review、/debug、/loop 等内置 Skill——直接看它们怎么写 description 和分工脚本,是最好的范本。skill-creator 工具——想系统化做 Skill 并跑 eval 评估触发准确率,用它一键生成骨架 + 度量效果。| 维度 | 评估 |
|---|---|
| 适合的训练营类型 | AI 工程提效 / 研发效能 / 命令行工具开发类训练营;也适合"AI + DevOps"主题作为实操单元 |
| 学员基础 | 会基本命令行(grep/git)+ 用过 Claude Code 即可;无需编程语言专精 |
| 教学时长 | 约 90–120 分钟:30 分钟讲机制 + 45 分钟动手造 + 30 分钟调优与排障 |
| 难度定位 | ⭐⭐⭐(中偏低)——核心是"配置 + 脚本",不涉及复杂编程,但 frontmatter 调优需反复试错 |
| 可拓展练习 | ① 改成扫描 console.log/print 遗留;② 加 paths 做多语言仓库;③ 接 gh issue create 把 Top 债直接转成 GitHub 工单 |
| 评估产出 | 学员能独立交付一个"扫描+归因+排序+建议"的可用 Skill,并演示自然语言触发 |
[!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 等场景全覆盖。
