5 minutes
编写你的第一个 Skill
理论 21 篇讲完了,本章从零写一个真实 Skill,让你看一遍从想法 → 写 SKILL.md → references 拆解 → 测试 → 装入 OpenCode → 上调用的全过程。我们要写的 Skill 叫 tdd-reminder:让主 Agent 在写新代码前先提示用户走 TDD。
22.1 Skill 想法从哪里来
三步法挖掘想法:
-
找你过去 7 天的 OpenCode session log
-
grep 模式 “你忘了 / 你没做 / 应该先”
rg "忘了\|应该\|未做\|先\|要是之前" .opencode/log/ | head -20 -
凡是出现 3 次以上的模式,就是候选 Skill
我自己日志里有一条:“每次写完 Python 函数才发现没写测试,又往回加 test_test_xxx,凑合跑”——这就是 tdd-reminder 要解决的:让 Agent 在写实现代码前主动说一句"咱们先写 test 行吗"。
22.2 拆解 Skill 行为需求
| 维度 | spec |
|---|---|
| Trigger | 主 Agent 即将写 Python / TS / Go / Rust 代码,且函数 / class 存在 |
| 不要触发 | 1)用户问解释性问题 2)改一行小 bug 3)已有现成测试 |
| 行为 | 提醒想先写 unit test;提供一次 brainstorm 机会,而非强制 |
| DONE | 用户明确说 “不用 test” 或 用户已提供 test 或 单元 test 跑通 |
| 失败模式 | 1)用户说"快点别叨叨" → 跳过加 [tdd-reminder skipped] 自报\n2)该语言无 pytest 等价物 → 改用最简化 shell test |
22.3 主 SKILL.md
---
name: tdd-reminder
description: |
在写新 production 代码前显式提醒用户走 TDD。
Provides an opportunity (not enforcement) to write tests first.
---
# tdd-reminder
You MUST read this skill when:
- About to write production code in {Python, TypeScript, Go, Rust}
- The code adds a new function / method / class (≥ 10 lines)
- No existing test for the entity being written
You MUST NOT read this skill when:
- User is asking an explanatory question (e.g., "how does X work")
- The change is a one-line bug fix
- An existing test already covers the entity
## What to do
When triggered, BEFORE writing the production code:
1. Ask in one short line:
> "要不要先写个 test? (yes / skip)"
2. If user says "yes": write the test first, run it (it should fail), then implement.
3. If user says "skip" or "no": continue, but log:
`[tdd-reminder skipped] reason: user-declined`
## DONE
When either:
- Test passes after implementation, or
- User skipped and `[tdd-reminder skipped]` logged
## Failure Modes
- User is in a hurry ("快点别叨叨"):
- Skip and log `[tdd-reminder skipped] reason: user-hurry`
- Language has no pytest-equivalent available:
- Allow fallback to shell-print-based ad-hoc test
- Log `[tdd-reminder partial] reason: no-test-framework`
- User provides an existing test (points you at tests/foo.py):
- Skip the prompt; do not duplicate.
## Output prefix
When triggered, prefix your first line with `[tdd-reminder]` for grep-ability.
When user accepts and you write test, prefix with `[tdd-reminder active]`.
When skipped, prefix with `[tdd-reminder skipped]`.
## References
- See `references/which-test-framework.md` for language-specific frameworks
- See `references/edge-cases.md` for boundary examples (CLI, fixtures, mocks)
把这段 SKILL.md 写到 .opencode/skills/tdd-reminder/SKILL.md。
22.4 references/:拆细节
主 SKILL.md 不写长细节——细节放 references/:
.opencode/skills/tdd-reminder/
├── SKILL.md
└── references/
├── which-test-framework.md
└── edge-cases.md
references/which-test-framework.md:
# Test Framework Quick Pick
| Language | Framework | Single-file command |
|-------------|----------------|--------------------------------------|
| Python | pytest | `pytest -x tests/test_foo.py::test_a`|
| TypeScript | vitest / bun test | `bun test path/to/test.ts` |
| Go | go test | `go test ./pkg/... -run ^TestFoo$` |
| Rust | cargo | `cargo test pkg_name::test_foo` |
| Hono/Node | vitest / jest | `bun test` |
If the user's project lacks any of these, fall back to:
- Node: `node --test`
- Python: built-in `unittest` stdlib module
- Go: stdlib testing
- Rust: stdlib `#[test]`
Only fall back when confirms by checking `Taskfile.yml` / `pyproject.toml` / `go.mod` / `Cargo.toml`.
references/edge-cases.md:
# Edge Cases
## CLI commands
If writing a CLI tool, test with:
- single-command: `subprocess.run(["my-cli", "arg"])` + assert exit code
- long-running: don't test long runs; test setup / parse logic separately
## HTTP handlers
Use `httpx.AsyncClient` (Python) or `supertest` (Node). DO NOT start real server in unit tests.
## Mocks vs fakes
Prefer fakes (in-memory impl) over mocks (patched functions). Mocks hide behavior; fakes reveal it.
22.5 把它 fork 到 OpenCode,跑通
OpenCode 会自动 monitor skills 目录。第一次启动后:
# 测试 Skill 被加载
opencode
> 给 lib/validators.py 的 is_alpha 加一个空字符串返回 true 的分支
# Expected:
[tdd-reminder] About to write new code for is_alpha.
要不要先写个 test? (yes / skip)
如果没触发:
- 检查文件位置:必须是
.opencode/skills/tdd-reminder/SKILL.md或~/.config/opencode/skills/... - 检查
name:与文件夹名相符 - 看 OpenCode session 启动日志是否有 “loaded skill: tdd-reminder”
22.6 测试 trigger 稳定性
跑 5 个 probe prompt:
A. 解释性的:为什么 Python 不需要类型?
B. 加功能: 给 utils.py 加一个 is_alpha 函数,空字符串返回 true
C. 改 bug: 把 src/foo.ts 第 12 行的 == 改成 ===
D. 加 feature:写一个新的 SQL 解析器
E. 一次性补全: 把 print(x) 改成 print(x, end="")
预期:
| Prompt | 触发? |
|---|---|
| A | ❌(解释性跳过) |
| B | ✅(新函数、可能 skip) |
| C | ❌(一行改 bug) |
| D | ✅ |
| E | ❌ |
5 个测试都通过,trigger 信号噪比 acceptable。
22.7 加 self-check:Skill 内自报
让 skill-reminder 自报"我触发了",方便你日后 grep session log:
修改 SKILL.md 平行:
When the skill loaded, your next message MUST start with one of:
- `[tdd-reminder engaged]` (user about to write new code, you offered)
- `[tdd-reminder skipped] reason: <reason>` (you decided not to engage)
7 天后跑:
rg "\[tdd-reminder" .opencode/log/
统计 engaged vs skipped。有数据,才能改 trigger 优化——这就是 14 章 Agent 可观测性 的开始:
本周统计:
engaged : 14 次
skipped - user-skip : 3 次
skipped - haste : 1 次
Skill 沉淀有效
22.8 跨 harness 兼容
复制到 Claude Code:
mkdir -p .claude/skills/tdd-reminder/references
cp .opencode/skills/tdd-reminder/{SKILL.md,references/*} .claude/skills/tdd-reminder/
绝大多数 prompt-only Skill 都能直接复用——SKILL.md 是 markdown。
可能差异的元字段:
- OpenCode 偏好
<trigger>区块 - Claude Code 2026 下半年加了
priorityfield - Codex 二开版用
lifecycle标 BEFORE/AFTER
跨 harness 迁移时只改 meta,主内容不动。
22.9 跨项目共享:把它升 user skill
写好一个 skill 自己用,意识到团队也能用:移到 ~/.config/opencode/skills/tdd-reminder/,所有项目可见。
mv .opencode/skills/tdd-reminder ~/.config/opencode/skills/
更进一步开放:
- 把 skill 单独 git 仓库化:
github.com/you/opencode-skill-tdd-reminder - 用 plugin 机制安装:
opencode plugin install github:you/opencode-skill-tdd-reminder
社区发现:
- 仓库 readme 写 Skill 能力 + 触发 demo + 故意失败 demo
- 加 LICENSE、CHANGELOG、版本标签
- OpenCode plugin 注册中心预计 2026 下半年 acceptance
22.10 一个反面教材:糟糕的 Skill 长什么样
# ❌ 反例
name: vague-skill
description: |
帮助写好代码
trigger: 看情况
flow: 想想,然后写
done: 写完了就 done
failure: 失败就再改
问题:
- trigger 不可机器判别
- flow 无步骤
- done 无标准
- failure 模式省略
- 没 self-check
- 没有 references
段错误的 Skill 会整周把 Agent 误导。低质量 skill 比没 skill 还糟——因为它"有形"但不准。
22.11 小结
- Skill 从 grep 自己 log 找模式挖掘,values 3 次以上的值得沉淀
- SKILL.md 短 + references/ 拆细节 + 输出自报 + 失败模式列
- 5 个 probe 验证 trigger;7 天统计 engaged vs skipped 调
- 跨 harness 复制 paste friendly,跨团队 install 走 plugin
下一篇: 《23 Skill 测试与演化》——把 Skill 当作 software:有 unit test、有 regression test、有 changelog。
Summary: 从 grep 找模式到 trigger 5-probe 验证到 self-report log 反馈,自写第一个 Skill 的完整 workflow。