首页 › tutorials › 造环境变量Skill:扫代码秒出env模板

概述

本教程带你从零开发一个生产级 Claude Code 自定义 Skill——env-scan,它能扫描任意项目代码、自动提取所有环境变量引用,并生成 .env.example 模板与变量清单。通过这个真实案例,你将掌握 Skill 的渐进式披露(Progressive Disclosure)机制、description 触发工程、支持文件分层、动态上下文注入与 frontmatter 精准控场。适用人群:已跑通 Claude Code 基础用法、想把重复提示词沉淀为可复用能力的中级开发者。

前置条件

第一步:理解 Skill 的"渐进式披露"内核

在动手前,必须想清楚一件事:Skill 不是一段常驻提示词,而是一套按需加载的能力包。

这与 CLAUDE.md 有本质区别。CLAUDE.md 的每一行在每次对话都会被读入上下文,属于"固定成本";而 Skill 采用分层披露:

一句话记住设计原则:首层要"勾得到",正文要"压得紧",深水区要"藏得住"。 长篇参考资料不该污染每次对话的上下文。

常见问题:我把详细规则全塞进 description,为什么 Skill 反而不触发? 解决:description 超过 1,536 字符会被截断,关键触发词可能被切掉。把"做什么+何时用"压在前两句,细节挪到正文。

第二步:用"description 触发工程"想清楚定位

Skill 能否被自动调用,90% 取决于 description 写得好不好。写之前,先回答三个问题:

  1. 这个 Skill 解决什么具体动作? —— "扫描代码里的环境变量引用,生成 .env.example 模板"
  2. 用户会说什么话时该触发? —— "我需要哪些环境变量""帮我生成 env 模板""这个项目要配什么变量"
  3. 什么情况下绝不该触发? —— 用户只是在读某个变量值、或在讨论配置架构时

把答案浓缩成一句话描述,把触发短语放进 when_to_use。env-scan 的定位草稿:

description: Scans a project for environment variable references and generates a .env.example template with a variable inventory. Use when the user asks what env vars they need, wants a .env template, or onboards to a codebase.
when_to_use: 触发短语如 "需要哪些环境变量" "生成 .env 模板" "项目要配什么变量" "onboarding env"

注意 description 我刻意用英文写主句——技能清单匹配对英文语义更稳;when_to_use 里塞中文触发短语,能提升中文场景下的命中率。两种语言混用是实战中常见的取巧手法。

第三步:建立目录骨架

Skill 的存放位置决定它的作用域。本教程做一个个人级 Skill(跨所有项目可用):

mkdir -p ~/.claude/skills/env-scan/scripts
mkdir -p ~/.claude/skills/env-scan/examples

得到如下结构(这是官方推荐的多文件布局):

env-scan/
├── SKILL.md              # 入口指令(必需)
├── reference.md          # 各语言提取规则详解(按需加载)
├── examples/
│   └── sample-output.md  # 期望输出样例
└── scripts/
    └── scan.sh           # 实际执行扫描的脚本

官方文档明确:SKILL.md 建议控制在 500 行以内,详细参考材料拆到同级文件,由正文里写链接告诉 Claude "什么时候去读它"。这就是渐进式披露的落地。

第四步:写 SKILL.md——入口要薄,指令要硬

打开 ~/.claude/skills/env-scan/SKILL.md,写入下面的内容。注意 frontmatter 与正文之间用 --- 分隔:

---
name: env-scan
description: Scans a project for environment variable references and generates a .env.example template with a variable inventory. Use when the user asks what env vars they need, wants a .env template, or onboards to a codebase.
allowed-tools: Bash(grep:*), Bash(rg:*), Read, Write
---

# 环境变量扫描

## 目标
找出当前项目中所有环境变量引用,产出:
1. 一份按"变量名 → 用途推断 → 来源文件"组织的清单(Markdown 表格)
2. 一个可直接使用的 `.env.example`,每行带注释占位

## 输入采集
先执行下面这行(动态上下文注入),把扫描结果内联进上下文:

!`${CLAUDE_SKILL_DIR}/scripts/scan.sh`

## 处理规则
- 变量名去重,按字母序排列
- 同名变量出现在多文件时,合并到一行,来源用逗号分隔
- 用途从最近的赋值/注释/上下文推断,不确定时标注 `(unknown)`
- 标准变量(PORT、NODE_ENV、DATABASE_URL)给出推荐默认值

## 输出
- 在仓库根写入 `.env.example`(已存在则先备份为 `.env.example.bak`)
- 在回复中粘贴清单表格
- 对任何看起来像密钥的变量(含 KEY/SECRET/TOKEN/PASSWORD)额外提醒"勿提交真实值"

## 深度参考
- 各语言的精确提取正则见 [reference.md](reference.md)
- 期望输出格式见 [examples/sample-output.md](examples/sample-output.md)

这里有几个关键机制值得逐一点破:

第五步:补齐支持文件

把"重而少用"的内容挪出 SKILL.md。先写 scripts/scan.sh(脚本是被执行而非被读入的,所以可以写得详尽):

#!/usr/bin/env bash
# 扫描常见环境变量引用模式,输出 "变量名<TAB>文件:行号"
set -euo pipefail

ROOT="${CLAUDE_PROJECT_DIR:-$PWD}"
cd "$ROOT"

# 用 ripgrep 优先,回落到 grep -rE
if command -v rg >/dev/null 2>&1; then
  GREP="rg --no-heading -n"
else
  GREP="grep -rnE"
fi

{
  $GREP 'process\.env\.([A-Z0-9_]+)'       -g '!node_modules' -g '!.git' . \
    | sed -E 's/.*process\.env\.([A-Z0-9_]+).*/\1/'            || true
  $GREP 'os\.(environ|getenv)\(["\x27]?([A-Z0-9_]+)' -g '!.git' . \
    | sed -E "s/.*(?:environ|getenv)[\"\x27]?([A-Z0-9_]+).*/\1/" || true
  $GREP '\$\{?([A-Z][A-Z0-9_]*)\}? *[:=-]' -g '*.sh' -g '*.env*' -g 'Makefile' . \
    | sed -E 's/.*\$\{?([A-Z][A-Z0-9_]+).*/\1/'                || true
} | sort -u

再写一份精简的 reference.md,专门放"哪种语言用什么正则"的查表内容,供 Claude 在遇到生僻框架(比如 Go 的 os.LookupEnv、Java 的 System.getenv)时按需查阅:

# 环境变量提取规则速查

| 语言/运行时 | 匹配模式 | 示例 |
|---|---|---|
| Node.js | `process\.env\.([A-Z0-9_]+)` | `process.env.DATABASE_URL` |
| Python | `os\.(?:environ|getenv)\(["']?([A-Z0-9_]+)` | `os.getenv("API_KEY")` |
| Shell | `\$\{?([A-Z][A-Z0-9_]*)\}?` | `${HOME}` |
| Go | `os\.LookupEnv\("([A-Z0-9_]+)"\)` / `os\.Getenv` | `os.LookupEnv("PORT")` |
| Java/Kotlin | `System\.getenv\("([A-Z0-9_]+)"\)` | `System.getenv("JWT_SECRET")` |
| .NET | `Environment\.GetEnvironmentVariable\("([A-Z0-9_]+)"\)` | — |

## 边界情况
- 动态变量名(`process.env[dynamic]`)无法静态提取,报告为 `(dynamic)`
- 测试桩里的假变量(mock)容易误报,优先排除 `__tests__`、`*.spec.*`、`*.test.*`
- `.env.example` 本身也要被解析,作为"已知声明变量"的补充来源

最后放一个 examples/sample-output.md,给 Claude 一个"好答案长什么样"的锚点,这对输出稳定性帮助极大:

## 变量清单(节选)

| 变量名 | 推荐默认/用途 | 来源 |
|---|---|---|
| DATABASE_URL | postgresql://user:pass@host:5432/db | src/db.ts:12 |
| JWT_SECRET | ⚠️ 勿提交真实值,生成 64 位随机串 | src/auth.ts:8 |
| PORT | 3000 | src/server.ts:5, docker-compose.yml:7 |
| NODE_ENV | development \| production | package.json:9 |

## 生成的 .env.example
DATABASE_URL=postgresql://user:pass@host:5432/db
JWT_SECRET=replace-with-64-char-random-string
PORT=3000
NODE_ENV=development

第六步:用 frontmatter 给 Skill 装"护栏"

回到 SKILL.md 的 frontmatter,按需加上控制字段。env-scan 已用了 allowed-tools,再补两个高频字段说明用法:

判断口诀:想自动→不设 disable;想手动→设 disable;想隐形→设 user-invocable: false;想限场景→设 paths。

第七步:联调——三种方式验证

Skill 不需要重启 Claude Code,保存即生效(官方称为 Live Change Detection)。打开一个含环境变量的项目,启动 claude,按下面顺序验证:

方式 A:自动触发(最该测)

输入一句贴近 when_to_use 的自然语言:

我刚接手这个项目,需要配置哪些环境变量才能跑起来?

预期:Claude 自主加载 env-scan,执行扫描脚本,给出清单并生成 .env.example。如果没触发,问题几乎都在 description——回到第二步重写。

方式 B:手动调用

/env-scan

预期:直接执行,无需 Claude 判断相关性。这条一定能跑通,可用于隔离"是脚本坏了还是描述没写好"。

方式 C:带参数调用(若声明了 arguments)

若把 frontmatter 加上 arguments: [path],正文里用 $0 或 $path 引用,就能 /env-scan src/ 限定扫描范围。

常见问题:方式 A 不触发,方式 B 正常。 排查清单:① description 是否前两句就点明"做什么+何时用";② 是否被 1,536 字符截断;③ when_to_use 的触发短语是否贴合你真实的问法;④ 是否误设了 paths 把当前文件挡在门外;⑤ 项目里确实有环境变量吗(脚本空输出时 Claude 可能判为无关)。

第八步:用官方 skill-creator 做评测与迭代

写到能跑只是及格。要稳定,得做触发评测。Claude Code 内置 /skill-creator(bundled skill)支持为 Skill 跑 eval:准备一批"应该触发"和"不该触发"的问法,看命中率与误报率,再据此微调 description。

迭代重点永远是 description:把漏触发的真实问法补进 when_to_use,把误触发的场景用更精确的措辞排除。这是一个"写-测-改"的小循环,通常 2–3 轮就能收敛。


实战案例:在一个真实 Node 项目里跑通 env-scan

以一个用了 process.env 的 Express 项目为例。在项目根目录启动 Claude Code 后,输入"帮我生成环境变量模板"。完整时序如下:

  1. Claude 匹配到 env-scan 的 description,决定加载该 Skill
  2. 读入 SKILL.md,遇到 !`${CLAUDE_SKILL_DIR}/scripts/scan.sh`,先执行脚本
  3. 脚本 ripgrep 扫描 src/,输出 DATABASE_URL\nJWT_SECRET\nPORT\n...
  4. 扫描结果替换进正文,Claude 据此生成 Markdown 清单
  5. Claude 调用 Write 写出 .env.example(发现已存在则备份)
  6. 在回复中贴出清单,并对 JWT_SECRET 给出"勿提交真实值"的安全提醒

整个流程对你而言是一句话,对 Claude 而言是"加载能力→采数据→产出→落盘"的完整闭环。这正是 Skill 把"重复多步操作"压成"一句话指令"的价值。

flowchart TD
    U[用户:帮我生成 env 模板] --> M{Claude 匹配 description?}
    M -->|命中| L[加载 SKILL.md 正文]
    M -->|未命中| N[不触发,普通对话]
    L --> I[动态注入:执行 scan.sh]
    I --> S[扫描结果内联进上下文]
    S --> R{需要正则细节?}
    R -->|是| D[读 reference.md 第三层]
    R -->|否| G[直接生成清单]
    D --> G
    G --> W[Write 写出 .env.example]
    W --> O[回复清单 + 安全提醒]

    style M fill:#fef3c7,stroke:#d97706
    style I fill:#dbeafe,stroke:#2563eb
    style W fill:#dcfce7,stroke:#16a34a
flowchart LR
    subgraph 第一层[常驻 · 每次会话加载]
        A1[name] --- A2[description / when_to_use ≤1536 字符]
    end
    subgraph 第二层[按需 · 触发时加载]
        B1[SKILL.md 正文]
    end
    subgraph 第三层[深度 · Claude 主动读取]
        C1[reference.md] --- C2[examples/] --- C3[scripts/]
    end
    第一层 -->|相关/手动调用| 第二层
    第二层 -->|需要细节| 第三层

    style 第一层 fill:#eff6ff
    style 第二层 fill:#fef9c3
    style 第三层 fill:#f3e8ff

进阶:把 Skill 做成可分发的插件

当 env-scan 稳定后,你可能想分发给团队。两种途径:

  1. 提交到项目仓库:把 .claude/skills/env-scan/ 整个目录提交,团队成员 clone 后自动可用。注意 .claude/skills/ 是项目级作用域,仅对该项目生效。
  2. 打包为插件:在 Skill 目录加一个 .claude-plugin/plugin.json,它就升级为可挂载 agents、hooks、MCP 服务器的插件包,通过 marketplace 分发,并用 plugin-name:skill-name 命名空间避免冲突。

分发前务必检查 scripts/ 里没有硬编码的本地路径,统一用 ${CLAUDE_SKILL_DIR} 与 ${CLAUDE_PROJECT_DIR} 保持可移植。

延伸阅读

训练营适用性评估

参考来源

  1. Extend Claude with skills — Claude Code 官方文档 — Skill 创建、frontmatter 字段全表、渐进式披露与触发控制的权威定义
  2. Claude Code Skill Frontmatter: Every YAML Option Explained — 对全部 frontmatter 字段(含 disable-model-invocation、allowed-tools、paths 等)的逐项图解
  3. How to Create Custom Claude Code Skills: Complete Tutorial (2026) — SKILL.md 结构与从零创建流程的社区教程
  4. claude-code-skills.md — gregpriday/claude-code-docs — 基于渐进式披露原则的 Skill 文件系统能力机制解读
  5. Agent Skills 开放标准 — Skill 跨 AI 工具互通的规范,Claude Code 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学通信知识星球海报-扫码加入