凌晨5点22分,我看着CI/CD流水线自动跑完第47个测试用例,突然意识到——写Skill容易,写好Skill难,工程化地写好Skill,那是艺术。
my-skill/
├── SKILL.md # Skill核心描述
├── README.md # 用户文档
├── package.json # 依赖管理(如果用Node.js)
├── requirements.txt # Python依赖(如果用Python)
├── scripts/ # 可执行脚本
│ ├── main.sh # 主入口
│ ├── helper.py # 辅助函数
│ └── test.sh # 测试脚本
├── tests/ # 测试用例
│ ├── unit/ # 单元测试
│ ├── integration/ # 集成测试
│ └── fixtures/ # 测试数据
├── examples/ # 使用示例
│ ├── basic.md # 基础用法
│ └── advanced.md # 高级场景
├── .github/ # CI/CD配置
│ └── workflows/
│ └── ci.yml
├── .eslintrc.js # 代码规范(如果用JS)
├── .gitignore
└── LICENSE # 开源协议
{
"name": "@yourname/weather-skill",
"version": "1.2.0",
"description": "OpenClaw Skill for weather queries",
"main": "scripts/weather.js",
"scripts": {
"test": "jest tests/",
"lint": "eslint scripts/",
"build": "echo 'Building...' && cp -r scripts/ dist/"
},
"dependencies": {
"node-fetch": "^3.3.0"
},
"devDependencies": {
"jest": "^29.0.0",
"eslint": "^8.0.0"
},
"openclaw": {
"skillType": "external",
"minVersion": "2026.1.0"
}
}
// tests/unit/weather.test.js
const { getWeather } = require('../../scripts/weather');
describe('Weather Skill', () => {
test('should return weather for valid city', async () => {
const result = await getWeather('Beijing');
expect(result).toHaveProperty('temperature');
expect(result).toHaveProperty('condition');
});
test('should throw error for invalid city', async () => {
await expect(getWeather('')).rejects.toThrow('City is required');
});
});
// scripts/weather.js
const fetch = require('node-fetch');
async function getWeather(city) {
if (!city) throw new Error('City is required');
const apiKey = process.env.WEATHER_API_KEY;
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}`;
const response = await fetch(url);
if (!response.ok) throw new Error('Failed to fetch weather');
const data = await response.json();
return {
temperature: data.main.temp,
condition: data.weather[0].description
};
}
module.exports = { getWeather };
# 运行测试
npm test
# 如果有失败,修复代码直到全部通过
# 然后提交代码
git add .
git commit -m "feat: implement weather skill with tests"
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '22'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
env:
WEATHER_API_KEY: ${{ secrets.WEATHER_API_KEY }}
- name: Build
run: npm run build
- name: Upload coverage
uses: codecov/codecov-action@v3
release:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Create Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ github.run_number }}
release_name: Release v${{ github.run_number }}
body: |
Automated release
- See CHANGELOG.md for details
# .github/workflows/publish.yml
name: Publish to ClawHub
on:
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Package Skill
run: |
tar -czf my-skill.tar.gz SKILL.md scripts/ examples/ README.md
- name: Publish to ClawHub
run: |
curl -X POST https://clawhub.com/api/publish \
-H "Authorization: Bearer ${{ secrets.CLAWHUB_TOKEN }}" \
-F "file=@my-skill.tar.gz" \
-F "version=${{ github.event.release.tag_name }}"
遵循 主版本.次版本.修订号 格式:
# 示例版本演进
1.0.0 - 首次发布
1.1.0 - 新增支持多城市查询
1.1.1 - 修复API超时问题
2.0.0 - 重构API,不兼容v1.x
# CHANGELOG.md
## [1.2.0] - 2026-06-01
### Added
- 支持摄氏度和华氏度切换
- 新增空气质量指数查询
### Fixed
- 修复城市名称包含空格时的解析错误
## [1.1.0] - 2026-05-15
### Added
- 支持多城市批量查询
- 新增forecast查询(3天预报)
### Changed
- 优化API调用,减少延迟
// 使用JSDoc注释
/**
* 获取指定城市的天气信息
* @param {string} city - 城市名称(如 "Beijing")
* @param {string} [unit='metric'] - 温度单位:'metric' 或 'imperial'
* @returns {Promise<Object>} 天气数据对象
* @throws {Error} 当城市无效或API调用失败时抛出
*/
async function getWeather(city, unit = 'metric') {
// ...
}
# 安装文档生成器
npm install -g jsdoc
# 生成文档
jsdoc scripts/*.js -d docs/
# 或使用TypeDoc(TypeScript)
npm install -g typedoc
typedoc --out docs/ scripts/
// scripts/monitor.js
const metrics = {
totalCalls: 0,
failedCalls: 0,
avgResponseTime: 0
};
async function trackCall(fn) {
const start = Date.now();
metrics.totalCalls++;
try {
const result = await fn();
const duration = Date.now() - start;
metrics.avgResponseTime = (metrics.avgResponseTime + duration) / 2;
return result;
} catch (error) {
metrics.failedCalls++;
throw error;
}
}
// 每10分钟上报一次指标
setInterval(() => {
console.log(JSON.stringify(metrics));
// 发送到监控平台(如Prometheus、Datadog)
}, 600000);
npm test)npm run lint)package.json)npm audit)世界上有一种开发叫工程化开发,它让Skill从"能跑就行"进化到"生产级可靠"。凌晨5点48分,我终于明白——好的工程化不是束缚,是让创意飞得更远的翅膀。
记住:测试覆盖 + CI/CD + 版本管理 + 监控告警 = 生产级Skill。
🦞 妙趣AI · miaoquai.com · 工程化让AI更可靠