17.2 Skills 项目结构
项目结构概述
良好的项目结构是开发高质量 Skills 的基础。本节将详细介绍 Skills 项目的标准结构和最佳实践。
python
### 1\. 基础结构
bash
my-skill/
├── src/ # 源代码目录
│ └── skills/ # Skills 模块
│ ├── __init__.py # 模块初始化
│ ├── my_skill.py # 主 Skill 实现
│ ├── utils/ # 工具函数
│ │ ├── __init__.py
│ │ ├── helpers.py # 辅助函数
│ │ └── validators.py # 验证器
│ └── models/ # 数据模型
│ ├── __init__.py
│ └── schemas.py # 数据模式
├── tests/ # 测试目录
│ ├── __init__.py
│ ├── conftest.py # pytest 配置
│ ├── test_my_skill.py # Skill 测试
│ ├── test_utils.py # 工具测试
│ └── fixtures/ # 测试固件
│ ├── __init__.py
│ └── data/ # 测试数据
├── docs/ # 文档目录
│ ├── api.md # API 文档
│ ├── usage.md # 使用文档
│ └── development.md # 开发文档
├── examples/ # 示例目录
│ ├── basic_usage.py # 基本使用示例
│ └── advanced_usage.py # 高级使用示例
├── scripts/ # 脚本目录
│ ├── build.py # 构建脚本
│ ├── deploy.py # 部署脚本
│ └── test.sh # 测试脚本
├── configs/ # 配置目录
│ ├── development.yaml # 开发配置
│ ├── production.yaml # 生产配置
│ └── testing.yaml # 测试配置
├── resources/ # 资源目录
│ ├── templates/ # 模板文件
│ ├── static/ # 静态文件
│ └── data/ # 数据文件
├── .gitignore # Git 忽略文件
├── .pylintrc # Pylint 配置
├── .mypy.ini # MyPy 配置
├── pyproject.toml # 项目配置
├── setup.py # 安装脚本
├── requirements.txt # 依赖列表
├── requirements-dev.txt # 开发依赖
├── README.md # 项目说明
├── LICENSE # 许可证
└── CHANGELOG.md # 变更日志
~~~### 2. 详细说明
#### 2.1 源代码目录(src/)
# src/skills/__init__.py
"""
Skills 模块
"""
from skills.my_skill import MySkill
__all__ = ["MySkill"]
__version__ = "0.1.0"
```python
# src/skills/my_skill.py
```python
"""
主 Skill 实现
"""
from typing import Dict, Any
from claude_code_sdk import Skill, SkillContext, SkillResult
class MySkill(Skill):
"""自定义 Skill 实现"""
def __init__(self):
super().__init__(
name="my-skill",
version="0.1.0",
description="A custom Claude Code skill"
)
def get_parameters_schema(self) -> Dict[str, Any]:
"""获取参数模式"""
return {
"type": "object",
"properties": {
"input": {
"type": "string",
"description": "Input text to process"
}
},
"required": ["input"]
}
def execute(self, parameters: Dict[str, Any], context: SkillContext) -> SkillResult:
"""执行 Skill"""
input_text = parameters["input"]
output = self.process_input(input_text)
return SkillResult(
success=True,
data={
"input": input_text,
"output": output
}
)
def process_input(self, input_text: str) -> str:
"""处理输入文本"""
return input_text.upper()
pythonpython
# src/skills/utils/__init__.py
"""
工具函数模块
"""
from skills.utils.helpers import format_output, validate_input
from skills.utils.validators import validate_parameters
__all__ = ["format_output", "validate_input", "validate_parameters"]
pythonpython
# src/skills/utils/helpers.py
"""
辅助函数
"""
from typing import Any
def format_output(data: Any, format_type: str = "json") -> str:
"""格式化输出"""
if format_type == "json":
import json
return json.dumps(data, indent=2)
elif format_type == "yaml":
import yaml
return yaml.dump(data)
else:
return str(data)
def validate_input(input_text: str) -> bool:
"""验证输入"""
return isinstance(input_text, str) and len(input_text) > 0
pythonpython
# src/skills/utils/validators.py
"""
验证器
"""
from typing import Dict, Any
def validate_parameters(parameters: Dict[str, Any], schema: Dict[str, Any]) -> bool:
"""验证参数"""
required = schema.get("required", [])
properties = schema.get("properties", {})
for param in required:
if param not in parameters:
return False
param_schema = properties.get(param, {})
param_type = param_schema.get("type")
if param_type:
if param_type == "string" and not isinstance(parameters[param], str):
return False
elif param_type == "number" and not isinstance(parameters[param], (int, float)):
return False
elif param_type == "boolean" and not isinstance(parameters[param], bool):
return False
return True
pythonpython
# src/skills/models/__init__.py
"""
数据模型模块
"""
from skills.models.schemas import InputSchema, OutputSchema
__all__ = ["InputSchema", "OutputSchema"]
pythonpython
# src/skills/models/schemas.py
"""
数据模式
"""
from dataclasses import dataclass
from typing import Optional
@dataclass
class InputSchema:
"""输入模式"""
input: str
options: Optional[dict] = None
@dataclass
class OutputSchema:
"""输出模式"""
success: bool
data: dict
message: Optional[str] = None
#### 2.2 测试目录(tests/)
python# tests/__init__.py
"""
测试模块
"""
python
python
# tests/conftest.py
"""
pytest 配置
"""
import pytest
from claude_code_sdk import SkillContext
@pytest.fixture
def skill_context():
"""Skill 上下文固件"""
return SkillContext()
@pytest.fixture
def sample_parameters():
"""示例参数固件"""
return {
"input": "test input"
}
@pytest.fixture
def sample_context():
"""示例上下文固件"""
return {
"project": {
"name": "test-project",
"path": "/tmp/test-project"
},
"user": {
"id": "user123",
"name": "Test User"
}
}
```python
# tests/test_my_skill.py
"""
Skill 测试
"""
import pytest
from skills.my_skill import MySkill
class TestMySkill:
"""MySkill 测试类"""
def setup_method(self):
"""设置方法"""
self.skill = MySkill()
def test_skill_initialization(self):
"""测试 Skill 初始化"""
assert self.skill.name == "my-skill"
assert self.skill.version == "0.1.0"
assert self.skill.description == "A custom Claude Code skill"
def test_get_parameters_schema(self):
"""测试获取参数模式"""
schema = self.skill.get_parameters_schema()
assert "properties" in schema
assert "input" in schema["properties"]
assert "required" in schema
assert "input" in schema["required"]
def test_execute_success(self, skill_context):
"""测试成功执行"""
parameters = {"input": "hello"}
result = self.skill.execute(parameters, skill_context)
assert result.success
assert result.data["output"] == "HELLO"
def test_execute_missing_parameter(self, skill_context):
"""测试缺少参数"""
parameters = {}
with pytest.raises(KeyError):
self.skill.execute(parameters, skill_context)
def test_process_input(self):
"""测试处理输入"""
assert self.skill.process_input("hello") == "HELLO"
assert self.skill.process_input("world") == "WORLD"
assert self.skill.process_input("123") == "123"
```python
# tests/test_utils.py
"""
工具函数测试
"""
import pytest
from skills.utils.helpers import format_output, validate_input
from skills.utils.validators import validate_parameters
class TestHelpers:
"""辅助函数测试"""
def test_format_output_json(self):
"""测试 JSON 格式化"""
data = {"key": "value"}
result = format_output(data, "json")
assert '"key": "value"' in result
def test_format_output_yaml(self):
"""测试 YAML 格式化"""
data = {"key": "value"}
result = format_output(data, "yaml")
assert "key: value" in result
def test_validate_input_valid(self):
"""测试有效输入"""
assert validate_input("hello") == True
assert validate_input("world") == True
def test_validate_input_invalid(self):
"""测试无效输入"""
assert validate_input("") == False
assert validate_input(123) == False
class TestValidators:
"""验证器测试"""
def test_validate_parameters_valid(self):
"""测试有效参数"""
parameters = {"input": "test"}
schema = {
"properties": {
"input": {"type": "string"}
},
"required": ["input"]
}
assert validate_parameters(parameters, schema) == True
def test_validate_parameters_missing_required(self):
"""测试缺少必需参数"""
parameters = {}
schema = {
"properties": {
"input": {"type": "string"}
},
"required": ["input"]
}
assert validate_parameters(parameters, schema) == False
def test_validate_parameters_wrong_type(self):
"""测试错误类型"""
parameters = {"input": 123}
schema = {
"properties": {
"input": {"type": "string"}
},
"required": ["input"]
}
assert validate_parameters(parameters, schema) == False
#### 2.3 配置文件
yamlpython
```yaml
# configs/development.yaml
# 开发环境配置
version: "1.0"
skills:
my-skill:
enabled: true
debug: true
log_level: DEBUG
logging:
level: DEBUG
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
output: console
cache:
enabled: true
ttl: 3600
storage: memory
```> # configs/production.yaml
# 生产环境配置
version: "1.0"
skills:
my-skill:
enabled: true
debug: false
log_level: INFO
logging:
level: INFO
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
output: file
file_path: /var/log/my-skill.log
cache:
enabled: true
ttl: 7200
storage: redis
redis_host: localhost
redis_port: 6379
```
~~~yaml
```yaml
```
# configs/testing.yaml
# 测试环境配置
version: "1.0"
skills:
my-skill:
enabled: true
debug: true
log_level: DEBUG
logging:
level: DEBUG
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
output: console
cache:
enabled: false
testing:
mock_external_calls: true
use_test_data: true
```## 项目组织原则
### 1. 模块化设计
## 模块化原则
### 单一职责
每个模块只负责一个功能
- my_skill.py: 主 Skill 逻辑
- helpers.py: 辅助函数
- validators.py: 验证逻辑
### 高内聚低耦合
模块内部紧密相关,模块之间松散耦合
- 使用接口定义
- 依赖注入
- 事件驱动
### 可复用性
设计可复用的组件
- 工具函数独立
- 数据模型通用
- 配置灵活
```
### 2. 命名规范
~~~markdown
```markdown
## 命名规范
### 文件命名
- 使用小写字母和下划线
- 描述性名称
- 避免缩写
示例:
- my_skill.py ✓
- helpers.py ✓
- validators.py ✓
- MySkill.py ✗
- helper.py ✗
### 类命名
- 使用大驼峰命名法
- 名词或名词短语
- 描述性名称
示例:
- MySkill ✓
- SkillContext ✓
- InputSchema ✓
- mySkill ✗
- skill_context ✗
### 函数命名
- 使用小写字母和下划线
- 动词或动词短语
- 描述性名称
示例:
- execute() ✓
- process_input() ✓
- validate_parameters() ✓
- Execute() ✗
- processInput() ✗
### 变量命名
- 使用小写字母和下划线
- 描述性名称
- 避免单字母变量
示例:
- input_text ✓
- result_data ✓
- skill_context ✓
- txt ✗
- data ✗
```### 3. 目录组织
## 目录组织原则
### 按功能组织
将相关功能放在同一目录
- utils/: 工具函数
- models/: 数据模型
- tests/: 测试代码
### 按层次组织
保持清晰的层次结构
- src/: 源代码
- tests/: 测试代码
- docs/: 文档
### 避免过深嵌套
目录层次不超过 3 层
- src/skills/my_skill.py ✓
- src/skills/utils/helpers.py ✓
- src/skills/utils/data/processors/helpers.py ✗
```
## 文档组织
### 1. API 文档
~~~markdown
```markdown
# docs/api.md
# MySkill API 文档
## 概述
MySkill 是一个自定义的 Claude Code Skill,用于处理文本输入。
## 参数
### input (必需)
- 类型: string
- 描述: 要处理的输入文本
- 示例: "hello world"
### options (可选)
- 类型: object
- 描述: 附加选项
- 属性:
- format: 输出格式 (json, yaml, text)
- case: 大小写转换 (upper, lower, title)
## 返回值
### success
- 类型: boolean
- 描述: 操作是否成功
### data
- 类型: object
- 描述: 返回的数据
- 属性:
- input: 原始输入
- output: 处理后的输出
### message
- 类型: string (可选)
- 描述: 附加消息
## 示例
### 基本使用
```python
```python
from skills.my_skill import MySkill
skill = MySkill()
result = skill.execute(
{"input": "hello world"},
context
)
print(result.data["output"]) # "HELLO WORLD"
### 高级使用
```python
result = skill.execute(
{
"input": "hello world",
"options": {
"format": "json",
"case": "upper"
}
},
context
)
### 2. 使用文档
# docs/usage.md
# MySkill 使用指南
## 安装
~~~`bash
`bash
pip install my-skill
## 基本使用
### 命令行
~~~bash
bash
claude --skill my-skill --input "hello world"
### Python API
```python
from skills.my_skill import MySkill
from claude_code_sdk import SkillContext
skill = MySkill()
context = SkillContext()
result = skill.execute(
{"input": "hello world"},
context
)
print(result.data["output"])
## 高级用法
### 自定义选项
```python
python
result = skill.execute(
{
"input": "hello world",
"options": {
"format": "yaml",
"case": "title"
}
},
context
)
### 批量处理
```python
inputs = ["hello", "world", "test"]
results = []
for input_text in inputs:
result = skill.execute(
{"input": input_text},
context
)
results.append(result)
## 错误处理
### 缺少参数
```python
python
try:
result = skill.execute({}, context)
except KeyError as e:
print(f"Missing parameter: {e}")
### 无效输入
```python
from skills.utils.validators import validate_parameters
parameters = {"input": 123}
schema = skill.get_parameters_schema()
if not validate_parameters(parameters, schema):
print("Invalid parameters")
### 3. 开发文档
```
# docs/development.md
# MySkill 开发指南
## 开发环境设置
### 安装依赖
~~~`bash
`bash
pip install -e ".[dev]"
### 运行测试
~~~bash
bash
pytest
### 代码格式化
```bash
black src/skills
### 代码检查
~~~bash
bash
pylint src/skills
## 添加新功能
### 1. 创建新方法
```python
def new_feature(self, data: str) -> str:
"""新功能"""
# 实现逻辑
return data
### 2. 添加测试
```python
python
def test_new_feature(self):
"""测试新功能"""
result = self.skill.new_feature("test")
assert result == "expected"
### 3. 更新文档
更新 API 文档和使用文档。
## 发布流程
### 1. 更新版本号
```python
# setup.py
version="0.2.0"
### 2. 更新变更日志
~~~markdown
markdown
# CHANGELOG.md
## [0.2.0] - 2024-01-15
### Added
- 新功能描述
### Changed
- 变更描述
### Fixed
- 修复描述
### 3. 创建发布
```bash
git tag v0.2.0
git push origin v0.2.0
### 4. 发布到 PyPI
~~~bash
bash
python setup.py sdist bdist_wheel
twine upload dist/*
```
```