<!-- title: 从零构建技术债扫描Skill全流程 -->

> 本教程面向已能在 Claude Code 里完成基本对话、但从未写过自定义 Skill 的开发者。学完后你将独立交付一个可在真实仓库里运行的 `tech-debt-scanner` Skill：扫描 TODO/FIXME/HACK 等技术债标记、按严重度归类、产出一份可追踪的技术债登记表，并掌握 Skill 的解剖结构、热加载调试、自动触发调优与多层级分发。

## 前置条件

- Claude Code v2.1.145 或更高版本（本教程用到的 `allowed-tools`、`paths`、`context: fork` 等字段在新版本稳定可用）。
- 一台装有 `git`、`bash`、`python3` 的 macOS / Linux / WSL 终端；Windows 用户建议在 Git Bash 或 WSL 下操作。
- 任意一个含若干 `TODO` / `FIXME` 注释的 Git 仓库（没有的话，文章末尾的实战案例会教你临时造一个）。
- 阅读理解 YAML 与 Markdown 即可，不要求精通 Python。

---

## 第一步：先吃透 Skill 的解剖结构

很多人写 Skill 的第一反应是"往 `SKILL.md` 里堆提示词"。这是错的起点。Skill 是一个**目录**而不是一个文件，它的真正力量来自**三层渐进式披露（progressive disclosure）**——上下文按需加载，而不是一次性灌满。

官方定义的目录骨架如下：

```
tech-debt-scanner/
├── SKILL.md            # 必需：入口指令
├── references/         # 可选：按需加载的参考资料
│   └── severity.md     # 严重度分级标准
├── scripts/            # 可选：被"执行"而非"阅读"的脚本
│   └── scan.sh
└── assets/             # 可选：模板、图标等输出素材
    └── report-template.md
```

关键认知：Claude 在每个会话里只**常驻**所有 Skill 的 `name` + `description`（约占 100 tokens/个），这是"第一层"；当某个 Skill 被触发时，`SKILL.md` 正文进入上下文，这是"第二层"（官方建议 < 500 行）；`scripts/` 与 `references/` 是"第三层"，**只有在 Claude 主动读取或执行时才消耗 token**，脚本的输出可以无限长而不挤占正文预算。

```mermaid
flowchart TB
    subgraph L1["第一层：元数据 始终常驻 ~100 tokens"]
        A["name: tech-debt-scanner"]
        B["description: 扫描技术债标记…"]
    end
    subgraph L2["第二层：触发后加载 < 500 行"]
        C["SKILL.md 正文<br/>指令 + 动态注入"]
    end
    subgraph L3["第三层：按需访问 无上限"]
        D["scripts/scan.sh 执行"]
        E["references/severity.md 阅读"]
        F["assets/report-template.md 引用"]
    end
    A --> C
    B --> C
    C --> D
    C --> E
    C --> F
    style L1 fill:#e8f5e9
    style L2 fill:#fff8e1
    style L3 fill:#fce4ec
```

把这个三层模型刻进脑子里，后面所有的设计取舍（为什么 description 要短、为什么大段参考要外置、为什么确定性逻辑要写成脚本）都源于它。

## 第二步：决定 Skill 放在哪一层目录

Claude Code 有四类 Skill 存放位置，**位置决定了谁能用**：

| 层级 | 路径 | 生效范围 |
| --- | --- | --- |
| 个人级 | `~/.claude/skills/tech-debt-scanner/SKILL.md` | 你的所有项目 |
| 项目级 | `.claude/skills/tech-debt-scanner/SKILL.md` | 仅当前项目（随仓库提交给团队） |
| 插件级 | `<plugin>/skills/tech-debt-scanner/SKILL.md` | 启用该插件的环境 |
| 企业级 | 通过 managed settings 下发 | 全组织 |

同名时优先级为：企业 > 个人 > 项目 > 内置。技术债扫描这类"强项目相关、需要随仓库走"的工具，我们放到**项目级**，这样团队成员 clone 仓库后即用。如果你只是想自己全项目复用，放个人级即可。

创建骨架（项目级）：

```bash
mkdir -p .claude/skills/tech-debt-scanner/{scripts,references,assets}
```

## 第三步：写 frontmatter——决定"何时触发"的唯一钥匙

`SKILL.md` 顶部是 YAML frontmatter，夹在两行 `---` 之间。其中 `description` 字段是**决定 Claude 是否调用该 Skill 的主要机制**，所有"何时使用"的信息都必须放在这里，而不是正文。

技术债扫描 Skill 的初版 frontmatter：

```yaml
---
name: tech-debt-scanner
description: 扫描代码库中的技术债标记（TODO/FIXME/HACK/XXX/@deprecated），按严重度归类并生成可追踪的技术债登记表。当用户想了解项目欠了多少技术债、盘点 TODO、排查遗留问题、做技术债清理规划，或提到"扫一下 TODO""有哪些 FIXME"时使用。
allowed-tools: Bash(bash *) Bash(grep *) Bash(git *)
paths:
  - "**/*.py"
  - "**/*.ts"
  - "**/*.tsx"
  - "**/*.js"
  - "**/*.go"
  - "**/*.java"
---
```

写 description 时有三个务必遵守的原则：

1. **关键词要覆盖用户真实说法**。"扫一下 TODO""有哪些 FIXME""项目欠了多少债"这类口语化、带标点的句子都要被覆盖到，因为 Claude 是按语义匹配 description 的。
2. **适度"推一把"（be pushy）**。官方 skill-creator 明确指出当前模型有**欠触发**倾向——该用 Skill 时不用。所以可以在 description 末尾补一句"即使用户没有明确说'技术债'，只要在讨论遗留问题、代码质量盘点、重构前评估，也应使用本 Skill"。
3. **关键用例放最前**。`description` + `when_to_use` 合并后在 Skill 列表里被截断到 **1536 字符**，且整个列表还要受"预算"约束（默认约为模型上下文窗口的 1%），越靠前的内容越容易被保留。

`paths` 字段是一个常被忽略的利器：设置后，Claude **只有在编辑或讨论匹配这些 glob 的文件时**才会自动加载该 Skill。对技术债扫描来说，它只关心源码文件，所以列出常见源码后缀，能避免在改文档、改配置时被无谓触发。

## 第四步：写正文，并用动态注入把"实况"喂进去

`SKILL.md` 正文是 Skill 被触发后进入上下文的"第二层"。技术债扫描的正文不需要长，核心是用 `!` 反引号语法做**动态上下文注入**——在 Claude 读到 Skill 之前，先由 Claude Code 跑命令、把输出原地替换进提示词：

```markdown
# 技术债扫描

## 当前仓库实况

下列数据在 Skill 触发时由 Claude Code 自动采集并内联，请基于真实数据而非猜测工作：

- 当前分支：!`git rev-parse --abbrev-ref HEAD`
- 待扫描源码文件数：!`git ls-files '*.py' '*.ts' '*.tsx' '*.js' '*.go' '*.java' | wc -l`

## 任务

1. 执行扫描脚本（无需重新发明扫描逻辑）：
   ```bash
   bash ${CLAUDE_SKILL_DIR}/scripts/scan.sh
   ```
2. 读取 `references/severity.md`，把脚本输出的每一类标记按 P0–P3 分级。
3. 用 `assets/report-template.md` 作为模板，生成 `TECH_DEBT.md`，写到仓库根目录。
4. 在报告开头用两三句话给出总体评估：债务规模、最该优先处理的 3 项、近期新增趋势。
```

这里有三个要点：

- `!` 反引号是**预处理**，不是 Claude 自己执行的命令。Claude 只看到替换后的结果。多行命令要用 ` ```! ` 围栏而非内联形式。
- `${CLAUDE_SKILL_DIR}` 是官方字符串替换变量，会被替换成 Skill 自身目录的绝对路径，让你无论从哪个工作目录调用都能找到随包的脚本。其他常用变量还有 `$ARGUMENTS`、`$0`/`$1`（位置参数）、`${CLAUDE_SESSION_ID}`、`${CLAUDE_EFFORT}`。
- 如果你担心 `!` 注入有安全隐患（在团队仓库里跑别人提交的 Skill），可以在 settings 里设 `"disableSkillShellExecution": true`，此时注入命令会被替换成一行策略提示而不执行。bundled 与 managed Skill 不受此设置影响。

## 第五步：把"确定性的脏活"下沉成脚本

正文里反复出现的、可机械化的步骤，应当写成 `scripts/` 下的脚本——这是 skill-creator 总结的核心规律："如果几个测试用例都在独立地写类似的辅助脚本，那就把它固化进 `scripts/`，省得每次调用都重新发明轮子。"

`scripts/scan.sh`（扫描并按标记聚类）：

```bash
#!/usr/bin/env bash
# 扫描技术债标记，输出 TSV：行号前缀\t标记类型\t内容
set -euo pipefail
ROOT="${1:-.}"
PATTERN='TODO|FIXME|HACK|XXX|@deprecated'

echo -e "file\tline\tmarker\ttext"
# 只扫 Git 跟踪的文件，避开 node_modules、构建产物
git -C "$ROOT" ls-files -z \
  | grep -zE '\.(py|ts|tsx|js|go|java|rb|rs|c|cpp|h)$' \
  | xargs -0 grep -rnIE "$PATTERN" "$ROOT" 2>/dev/null \
  | sed -E 's/^([^:]+):([0-9]+):[^:]*('"$PATTERN"')[[:space:]:]*[[:space:]]*(.*)/\1\t\2\t\3\t\4/' \
  | sort
```

`references/severity.md`（按需加载的分级标准，第三层）：

```markdown
# 技术债严重度分级

- **P0 阻断**：FIXME 涉及安全、数据丢失、生产崩溃；HACK 绕过鉴权或事务。
- **P1 高**：FIXME 涉及核心逻辑正确性；TODO 标注的未实现关键功能。
- **P2 中**：XXX 性能/可维护性隐患；@deprecated 仍在被新代码调用。
- **P3 低**：普通 TODO（文档、小重构、命名优化）。

判定优先看"影响面"再看"发生频率"，单点高频标记可升一级。
```

`assets/report-template.md`（输出模板）：

```markdown
# 技术债登记表 · {{date}}

> 总体评估：{{summary}}

| 优先级 | 数量 | 代表文件 |
| --- | --- | --- |
| P0 | {{p0}} | {{p0_files}} |
| P1 | {{p1}} | {{p1_files}} |
| P2 | {{p2}} | {{p2_files}} |
| P3 | {{p3}} | {{p3_files}} |

## 建议优先处理
1. ...
```

为什么把分级标准外置到 `references/`？因为它是"长尾参考"，每次只在生成报告时读一次，外置后不占 SKILL.md 正文预算，也方便团队单独维护这一份标准。

## 第六步：热加载，两种方式触发它

Claude Code 会**监听 Skill 目录的文件变化**，新增、编辑、删除都无需重启即在当前会话生效（仅限 `SKILL.md` 文本；若同时是插件目录，`hooks/`、`agents/` 等变更需要 `/reload-plugins`）。这意味着你可以边改边测。

测试有两种路径，都要验证：

**路径 A：直接斜杠调用**

```
/tech-debt-scanner
```

这条最稳，能确认 Skill 本身能跑通。

**路径 B：自然语言自动触发**

```
我们这个项目欠了多少技术债？最近 TODO 多不多，列一下最该先还的
```

如果这条触发了你的 Skill，说明 description 写到位了。如果没触发，先问一句 `当前有哪些可用的 Skill？` 确认它出现在列表里；若出现却不触发，多半是 description 关键词没覆盖到你的措辞，或者问题太"轻"——Claude 只在它判断"靠自己处理不好"时才咨询 Skill，简单的一步式提问往往不触发。

预期结果：仓库根目录生成 `TECH_DEBT.md`，终端打印一段两三句话的总评 + P0–P3 分布表。

## 第七步：触发调优——这是 Skill 区别于普通提示词的关键

写完能跑，只是及格。Skill 的工程价值在于**触发既不漏也不过激**。三组工具按场景选用：

**1. 控制谁能调用**

| frontmatter | 你能调用 | Claude 自动调用 |
| --- | --- | --- |
| 默认 | ✅ | ✅ |
| `disable-model-invocation: true` | ✅ | ❌（仅手动） |
| `user-invocable: false` | ❌（隐藏菜单） | ✅ |

技术债扫描希望 Claude 在合适时主动用，也允许你 `/tech-debt-scanner` 手动跑，所以保持默认即可。对比之下，`/deploy`、`/commit` 这类有副作用的，就该加 `disable-model-invocation: true`，免得 Claude 看"代码差不多了"就自作主张部署。

**2. 收紧工具与执行范围**

- `allowed-tools: Bash(grep *) Bash(git *)`：在该 Skill 激活期间，这些工具免二次确认。注意它是"授权"而非"限制"，其他工具仍可调用。
- `disallowed-tools`：把不想让该 Skill 碰的工具整个移出可用池（下次发消息后清除）。无人值守循环里常用它屏蔽 `AskUserQuestion`。
- `paths`：前面用过的 glob 限定，只在编辑匹配文件时自动激活。

**3. 让它跑在子代理里**

如果仓库很大、扫描耗时长，加 `context: fork` 让 Skill 在隔离的子代理里跑，不污染主对话上下文：

```yaml
context: fork
agent: Explore
```

`agent` 可选内置的 `Explore`（只读、上下文最小）、`Plan` 或自定义 agent。注意 `context: fork` 只适合"有明确任务"的 Skill；纯约定参考型 Skill（如"API 命名规范"）在子代理里拿不到可执行任务，会空手返回。

**4. 描述级优化（进阶）**

官方 `skill-creator` 提供一条 `description` 自动优化链路：生成 20 条"应触发/不应触发"的真实查询（应触发的要多变措辞、含文件路径等细节；不应触发的要用"近义但不该命中"的棘手负例），再用 `run_loop.py` 做 60% 训练 / 40% 留出的 5 轮迭代，按测试集得分（而非训练集）选出 `best_description` 回填 frontmatter。如果你的 Skill 要分发给很多人，这步值得做。

## 第八步：分发与排错

- **个人复用**：留在 `~/.claude/skills/`。
- **团队共享**：提交 `.claude/skills/` 到仓库，同事 clone 后首次接受"工作区信任"对话框即可用。务必在 README 里提醒：项目级 Skill 的 `allowed-tools` 在信任后才生效，**review 别人仓库的 Skill 时要当心**——一个 Skill 可以给自己授予很宽的工具权限。
- **跨项目打包**：把 Skill 目录加一个 `.claude-plugin/plugin.json`，它就变成一个能捆绑 agent、hooks、MCP 的插件，用 `plugin-name:skill-name` 命名空间加载，不会与各层同名 Skill 冲突。

排错速查：

- **Skill 不触发**：查 description 是否含用户自然说法；`/doctor` 看描述是否被预算截断；太简单的提问本来就不会触发，把问题改"重"一点再试。
- **Skill 触发太频繁**：把 description 写更窄，或加 `disable-model-invocation: true` 改纯手动。
- **描述被截断**：描述合并上限 1536 字符，整体列表预算约为上下文窗口的 1%。可调 `skillListingBudgetFraction`（如 `0.02`）或环境变量 `SLASH_COMMAND_TOOL_CHAR_BUDGET`；或在 `skillOverrides` 里把低优先 Skill 设为 `"name-only"` 腾出预算。

---

## 实战案例：在真实仓库里跑通 tech-debt-scanner

准备一个最小复现仓库：

```bash
mkdir demo-debt && cd demo-debt && git init -q
cat > app.py <<'EOF'
def login(user, pwd):
    # FIXME 明文比对密码，上线前必须换成 bcrypt
    return user.pwd == pwd

def cache_get(key):
    # TODO 命中率统计还没接，先返回 None
    return None

# HACK 跳过鉴权方便联调，记得删
def dev_bypass(): ...
EOF
git add -A && git commit -qm "demo with debt"
```

把前面八步的 `tech-debt-scanner/` 目录建到 `.claude/skills/` 下，给 `scripts/scan.sh` 加可执行权限：

```bash
chmod +x .claude/skills/tech-debt-scanner/scripts/scan.sh
```

启动 `claude`，输入：

```
这个项目技术债情况怎么样？帮我扫一遍并出个登记表
```

预期 `TECH_DEBT.md` 里 P0 命中那条 FIXME 明文密码、P0/P1 命中 HACK 鉴权绕过、P3 命中 TODO 命中率。这个闭环跑通，你就掌握了 Skill 从"零"到"可分发"的完整链路。

```mermaid
flowchart LR
    M["1 明确意图<br/>扫描+归类+登记"] --> S["2 建目录骨架<br/>skills/scanner/{scripts,references,assets}"]
    S --> F["3 写 frontmatter<br/>name+description+paths"]
    F --> B["4 写正文+动态注入<br/>!`git ls-files`"]
    B --> C["5 下沉脚本/参考<br/>scan.sh / severity.md"]
    C --> T["6 热加载测试<br/>/slash 与自然语言双验"]
    T --> O{"触发准确?"}
    O -- "漏触发" --> F
    O -- "过触发" --> F
    O -- "准确" --> D["7 调优<br/>invocation/tools/fork"]
    D --> SH["8 分发<br/>personal/project/plugin"]
    style O fill:#fff8e1
```

---

## 常见问题与解法

**Q1：`!` 反引号里的命令没执行，原样出现在回复里。**
多半是 `!` 紧跟在别的字符后面（如 `KEY=!`cmd`）。内联形式只在 `!` 出现在行首或紧跟空白时才被识别。多行命令改用 ` ```! ` 围栏。

**Q2：`${CLAUDE_SKILL_DIR}` 没被替换。**
确认你写的是 `${CLAUDE_SKILL_DIR}`（带 `${}`），它指向 `SKILL.md` 所在目录；插件 Skill 指向插件内该 Skill 子目录而非插件根。

**Q3：Skill 跑一次后后面就不"听话"了。**
Skill 内容作为单条消息进入对话后会**常驻**，Claude Code 不会每轮重读文件。若开了自动压缩，会按"最近优先 + 每 Skill 前 5000 tokens、合计 25000 tokens 预算"重新挂载，老的可能被丢。压缩后若发现失效，重新 `/tech-debt-scanner` 一次即可恢复全文。

**Q4：脚本在 macOS 上 `grep` 报错。**
BSD grep 不支持 GNU 的 `-z`/`-P`。脚本里用了 POSIX 友好的写法，若仍遇问题，可 `brew install grep` 后用 `ggrep`，或在团队里统一用 Docker 跑扫描。

---

## 延伸阅读

- Claude Code 官方 Skills 文档：<https://code.claude.com/docs/en/skills> —— frontmatter 字段全集、动态注入、子代理执行。
- `skill-creator` 源 Skill：<https://github.com/anthropics/skills/blob/main/skills/skill-creator/SKILL.md> —— 官方"造 Skill 的 Skill"，含描述优化链路与评测方法。
- Agent Skills 开放标准与 API 总览：<https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview> —— 跨工具兼容性说明。

---

## 训练营适用性评估

- **企业内部 Claude Code 实战训练营（中级，2 天制）**：高度契合。本教程可作为"自定义能力扩展"半天的核心动手模块，学员自带仓库当场产出可用的项目级 Skill。
- **AI 编程效率提升工作坊（1 天速成）**：可压缩为 3 小时精简版——跳过第八步分发与 description 自动优化，聚焦第一到第六步，保证每人交付一个能自动触发的 Skill。
- **团队工程规范落地培训**：把 `references/severity.md` 换成团队自己的代码规范，本教程即变成"用 Skill 沉淀团队规范"的范式教学。
- **预计教学时长**：理论讲授 45 分钟 + 跟练动手 90 分钟 + 触发调优答疑 30 分钟，合计约 2.5–3 小时。

## 参考来源

1. [Extend Claude with skills — Claude Code Docs](https://code.claude.com/docs/en/skills) — 官方 Skill 创建、frontmatter 字段、动态注入与分发的权威说明。
2. [skill-creator SKILL.md — anthropics/skills](https://github.com/anthropics/skills/blob/main/skills/skill-creator/SKILL.md) — 官方"造 Skill 的 Skill"，含三层解剖、description 调优与评测闭环。
3. [Best Claude Code Skills to Try in 2026 — Firecrawl](https://www.firecrawl.dev/blog/best-claude-code-skills) — 渐进式披露与"每 Skill 约 100 tokens 常驻"的实践解读。
4. [Agent Skills Overview — Claude API Docs](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview) — Agent Skills 开放标准的结构与跨工具兼容性。

> [!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 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
