17.2 Skills 项目结构

项目结构概述

良好的项目结构是开发高质量 Skills 的基础。本节将详细介绍 Skills 项目的标准结构和最佳实践。

1. 基础结构

 
    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()
 
```python
    # src/skills/utils/__init__.py
    """
    工具函数模块
    """
```python
    from skills.utils.helpers import format_output, validate_input
    from skills.utils.validators import validate_parameters
    __all__ = ["format_output", "validate_input", "validate_parameters"]
 
    # 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
 
```python
    # src/skills/utils/validators.py
    """
    验证器
    """
```python
    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
 
    # src/skills/models/__init__.py
    """
    数据模型模块
    """
    from skills.models.schemas import InputSchema, OutputSchema
 
    __all__ = ["InputSchema", "OutputSchema"]
 
```python
    # src/skills/models/schemas.py
    """
    数据模式
    """
```python
    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
    # 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 测试
    """
```python
    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"

# 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 配置文件

 
# 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"
 

高级使用

 
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
 
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
 
```bash
try:
    result = skill.execute({}, context)
except KeyError as e:
    print(f"Missing parameter: {e}")
 

无效输入

 
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. 更新版本号

 
# 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/*

```

```