mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
feat: 为 skill-generator 添加脚本执行能力
- 默认创建 scripts/ 目录用于存放确定性脚本 - 新增 specs/scripting-integration.md 脚本集成规范 - 新增 templates/script-python.md 和 script-bash.md 脚本模板 - 模板中添加 ## Scripts 声明和 ExecuteScript 调用示例 - 支持命名即ID、扩展名即运行时的约定 - 参数自动转换: snake_case → kebab-case - Bash 模板使用 jq 构建 JSON 输出 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -34,10 +34,8 @@ if (config.execution_mode === 'autonomous' || config.execution_mode === 'hybrid'
|
||||
Bash(`mkdir -p "${skillDir}/phases/actions"`);
|
||||
}
|
||||
|
||||
// 可选: scripts 目录
|
||||
if (config.needs_scripts) {
|
||||
// scripts 目录(默认创建,用于存放确定性脚本)
|
||||
Bash(`mkdir -p "${skillDir}/scripts"`);
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: 生成 SKILL.md
|
||||
@@ -199,9 +197,10 @@ function generateReferenceTable(config) {
|
||||
- **Directory**: `.claude/skills/{skill-name}/`
|
||||
- **Files**:
|
||||
- `SKILL.md` (入口文件)
|
||||
- `phases/` (空目录)
|
||||
- `specs/` (空目录)
|
||||
- `templates/` (空目录)
|
||||
- `phases/` (执行阶段目录)
|
||||
- `specs/` (规范文档目录)
|
||||
- `templates/` (模板目录)
|
||||
- `scripts/` (脚本目录,存放 Python/Bash 确定性脚本)
|
||||
|
||||
## Next Phase
|
||||
|
||||
|
||||
265
.claude/skills/skill-generator/specs/scripting-integration.md
Normal file
265
.claude/skills/skill-generator/specs/scripting-integration.md
Normal file
@@ -0,0 +1,265 @@
|
||||
# Scripting Integration Spec
|
||||
|
||||
技能脚本集成规范,定义如何在技能中使用外部脚本执行确定性任务。
|
||||
|
||||
## 核心原则
|
||||
|
||||
1. **约定优于配置**:命名即 ID,扩展名即运行时
|
||||
2. **极简调用**:一行完成脚本调用
|
||||
3. **标准输入输出**:命令行参数输入,JSON 标准输出
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
.claude/skills/<skill-name>/
|
||||
├── scripts/ # 脚本专用目录
|
||||
│ ├── process-data.py # id: process-data
|
||||
│ ├── validate-output.sh # id: validate-output
|
||||
│ └── transform-json.js # id: transform-json
|
||||
├── phases/
|
||||
└── specs/
|
||||
```
|
||||
|
||||
## 命名约定
|
||||
|
||||
| 扩展名 | 运行时 | 执行命令 |
|
||||
|--------|--------|----------|
|
||||
| `.py` | python | `python scripts/{id}.py` |
|
||||
| `.sh` | bash | `bash scripts/{id}.sh` |
|
||||
| `.js` | node | `node scripts/{id}.js` |
|
||||
|
||||
## 声明格式
|
||||
|
||||
在 Phase 或 Action 文件的 `## Scripts` 部分声明:
|
||||
|
||||
```yaml
|
||||
## Scripts
|
||||
|
||||
- process-data
|
||||
- validate-output
|
||||
```
|
||||
|
||||
## 调用语法
|
||||
|
||||
### 基础调用
|
||||
|
||||
```javascript
|
||||
const result = await ExecuteScript('script-id', { key: value });
|
||||
```
|
||||
|
||||
### 参数命名转换
|
||||
|
||||
调用时 JS 对象中的键会**自动转换**为 `kebab-case` 命令行参数:
|
||||
|
||||
| JS 键名 | 转换后参数 |
|
||||
|---------|-----------|
|
||||
| `input_path` | `--input-path` |
|
||||
| `output_dir` | `--output-dir` |
|
||||
| `max_count` | `--max-count` |
|
||||
|
||||
脚本中使用 `--input-path` 接收,调用时使用 `input_path` 传入。
|
||||
|
||||
### 完整调用(含错误处理)
|
||||
|
||||
```javascript
|
||||
const result = await ExecuteScript('process-data', {
|
||||
input_path: `${workDir}/data.json`,
|
||||
threshold: 0.9
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(`脚本执行失败: ${result.stderr}`);
|
||||
}
|
||||
|
||||
const { output_file, count } = result.outputs;
|
||||
```
|
||||
|
||||
## 返回格式
|
||||
|
||||
```typescript
|
||||
interface ScriptResult {
|
||||
success: boolean; // exit code === 0
|
||||
stdout: string; // 完整标准输出
|
||||
stderr: string; // 完整标准错误
|
||||
outputs: { // 从 stdout 最后一行解析的 JSON
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## 脚本编写规范
|
||||
|
||||
### 输入:命令行参数
|
||||
|
||||
```bash
|
||||
# Python: argparse
|
||||
--input-path /path/to/file --threshold 0.9
|
||||
|
||||
# Bash: 手动解析
|
||||
--input-path /path/to/file
|
||||
```
|
||||
|
||||
### 输出:标准输出 JSON
|
||||
|
||||
脚本最后一行必须打印单行 JSON:
|
||||
|
||||
```json
|
||||
{"output_file": "/tmp/result.json", "count": 42}
|
||||
```
|
||||
|
||||
### Python 模板
|
||||
|
||||
```python
|
||||
import argparse
|
||||
import json
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--input-path', required=True)
|
||||
parser.add_argument('--threshold', type=float, default=0.9)
|
||||
args = parser.parse_args()
|
||||
|
||||
# 执行逻辑...
|
||||
result_path = "/tmp/result.json"
|
||||
|
||||
# 输出 JSON
|
||||
print(json.dumps({
|
||||
"output_file": result_path,
|
||||
"items_processed": 100
|
||||
}))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
### Bash 模板
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# 解析参数
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
--input-path) INPUT_PATH="$2"; shift ;;
|
||||
*) echo "Unknown: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# 执行逻辑...
|
||||
LOG_FILE="/tmp/process.log"
|
||||
echo "Processing $INPUT_PATH" > "$LOG_FILE"
|
||||
|
||||
# 输出 JSON
|
||||
echo "{\"log_file\": \"$LOG_FILE\", \"status\": \"done\"}"
|
||||
```
|
||||
|
||||
## ExecuteScript 实现
|
||||
|
||||
```javascript
|
||||
async function ExecuteScript(scriptId, inputs = {}) {
|
||||
const skillDir = GetSkillDir();
|
||||
|
||||
// 查找脚本文件
|
||||
const extensions = ['.py', '.sh', '.js'];
|
||||
let scriptPath, runtime;
|
||||
|
||||
for (const ext of extensions) {
|
||||
const path = `${skillDir}/scripts/${scriptId}${ext}`;
|
||||
if (FileExists(path)) {
|
||||
scriptPath = path;
|
||||
runtime = ext === '.py' ? 'python' : ext === '.sh' ? 'bash' : 'node';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!scriptPath) {
|
||||
throw new Error(`Script not found: ${scriptId}`);
|
||||
}
|
||||
|
||||
// 构建命令行参数
|
||||
const args = Object.entries(inputs)
|
||||
.map(([k, v]) => `--${k.replace(/_/g, '-')} "${v}"`)
|
||||
.join(' ');
|
||||
|
||||
// 执行脚本
|
||||
const cmd = `${runtime} "${scriptPath}" ${args}`;
|
||||
const { stdout, stderr, exitCode } = await Bash(cmd);
|
||||
|
||||
// 解析输出
|
||||
let outputs = {};
|
||||
try {
|
||||
const lastLine = stdout.trim().split('\n').pop();
|
||||
outputs = JSON.parse(lastLine);
|
||||
} catch (e) {
|
||||
// 无法解析 JSON,保持空对象
|
||||
}
|
||||
|
||||
return {
|
||||
success: exitCode === 0,
|
||||
stdout,
|
||||
stderr,
|
||||
outputs
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## 使用场景
|
||||
|
||||
### 适合脚本化的任务
|
||||
|
||||
- 数据处理和转换
|
||||
- 文件格式转换
|
||||
- 批量文件操作
|
||||
- 复杂计算逻辑
|
||||
- 调用外部工具/库
|
||||
|
||||
### 不适合脚本化的任务
|
||||
|
||||
- 需要用户交互的任务
|
||||
- 需要访问 Claude 工具的任务
|
||||
- 简单的文件读写
|
||||
- 需要动态决策的任务
|
||||
|
||||
## 路径约定
|
||||
|
||||
### 脚本路径
|
||||
|
||||
脚本路径相对于 `SKILL.md` 所在目录(技能根目录):
|
||||
|
||||
```
|
||||
.claude/skills/<skill-name>/ # 技能根目录(SKILL.md 所在位置)
|
||||
├── SKILL.md
|
||||
├── scripts/ # 脚本目录
|
||||
│ └── process-data.py # 相对路径: scripts/process-data.py
|
||||
└── phases/
|
||||
```
|
||||
|
||||
`ExecuteScript` 自动从技能根目录查找脚本:
|
||||
```javascript
|
||||
// 实际执行: python .claude/skills/<skill-name>/scripts/process-data.py
|
||||
await ExecuteScript('process-data', { ... });
|
||||
```
|
||||
|
||||
### 输出目录
|
||||
|
||||
**推荐**:由调用方传递输出目录,而非脚本默认 `/tmp`:
|
||||
|
||||
```javascript
|
||||
// 调用时指定输出目录(在工作流工作目录内)
|
||||
const result = await ExecuteScript('process-data', {
|
||||
input_path: `${workDir}/data.json`,
|
||||
output_dir: `${workDir}/output` // 明确指定输出位置
|
||||
});
|
||||
```
|
||||
|
||||
脚本应接受 `--output-dir` 参数,而非硬编码输出路径。
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **单一职责**:每个脚本只做一件事
|
||||
2. **无副作用**:脚本不应修改全局状态
|
||||
3. **幂等性**:相同输入产生相同输出
|
||||
4. **错误明确**:错误信息写入 stderr,正常输出写入 stdout
|
||||
5. **快速失败**:参数验证失败立即退出
|
||||
6. **路径参数化**:输出路径由调用方指定,不硬编码
|
||||
@@ -17,11 +17,22 @@
|
||||
|
||||
{{preconditions_list}}
|
||||
|
||||
## Scripts
|
||||
|
||||
\`\`\`yaml
|
||||
# 声明本动作使用的脚本(可选)
|
||||
# - script-id # 对应 scripts/script-id.py 或 .sh
|
||||
\`\`\`
|
||||
|
||||
## Execution
|
||||
|
||||
\`\`\`javascript
|
||||
async function execute(state) {
|
||||
{{execution_code}}
|
||||
|
||||
// 调用脚本示例
|
||||
// const result = await ExecuteScript('script-id', { input: state.context.data });
|
||||
// if (!result.success) throw new Error(result.stderr);
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
|
||||
277
.claude/skills/skill-generator/templates/script-bash.md
Normal file
277
.claude/skills/skill-generator/templates/script-bash.md
Normal file
@@ -0,0 +1,277 @@
|
||||
# Bash Script Template
|
||||
|
||||
Bash 脚本模板,用于生成技能中的确定性脚本。
|
||||
|
||||
## 模板代码
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# {{script_description}}
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ============================================================
|
||||
# 参数解析
|
||||
# ============================================================
|
||||
|
||||
INPUT_PATH=""
|
||||
OUTPUT_DIR="" # 由调用方指定,不设默认值
|
||||
|
||||
show_help() {
|
||||
echo "用法: $0 --input-path <path> --output-dir <dir>"
|
||||
echo ""
|
||||
echo "参数:"
|
||||
echo " --input-path 输入文件路径 (必需)"
|
||||
echo " --output-dir 输出目录 (必需,由调用方指定)"
|
||||
echo " --help 显示帮助信息"
|
||||
}
|
||||
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
--input-path)
|
||||
INPUT_PATH="$2"
|
||||
shift
|
||||
;;
|
||||
--output-dir)
|
||||
OUTPUT_DIR="$2"
|
||||
shift
|
||||
;;
|
||||
--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "错误: 未知参数 $1" >&2
|
||||
show_help >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# ============================================================
|
||||
# 参数验证
|
||||
# ============================================================
|
||||
|
||||
if [[ -z "$INPUT_PATH" ]]; then
|
||||
echo "错误: --input-path 是必需参数" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$OUTPUT_DIR" ]]; then
|
||||
echo "错误: --output-dir 是必需参数" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$INPUT_PATH" ]]; then
|
||||
echo "错误: 输入文件不存在: $INPUT_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查 jq 是否可用(用于 JSON 输出)
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "错误: 需要安装 jq" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# ============================================================
|
||||
# 核心逻辑
|
||||
# ============================================================
|
||||
|
||||
OUTPUT_FILE="$OUTPUT_DIR/result.txt"
|
||||
ITEMS_COUNT=0
|
||||
|
||||
# TODO: 实现处理逻辑
|
||||
# 示例:处理输入文件
|
||||
while IFS= read -r line; do
|
||||
echo "$line" >> "$OUTPUT_FILE"
|
||||
((ITEMS_COUNT++))
|
||||
done < "$INPUT_PATH"
|
||||
|
||||
# ============================================================
|
||||
# 输出 JSON 结果(使用 jq 构建,避免特殊字符问题)
|
||||
# ============================================================
|
||||
|
||||
jq -n \
|
||||
--arg output_file "$OUTPUT_FILE" \
|
||||
--argjson items_processed "$ITEMS_COUNT" \
|
||||
'{output_file: $output_file, items_processed: $items_processed, status: "success"}'
|
||||
```
|
||||
|
||||
## 变量说明
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `{{script_description}}` | 脚本功能描述 |
|
||||
|
||||
## 使用规范
|
||||
|
||||
### 脚本头部
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -euo pipefail # 严格模式:出错退出、未定义变量报错、管道错误传递
|
||||
```
|
||||
|
||||
### 参数解析模式
|
||||
|
||||
```bash
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
--param-name)
|
||||
PARAM_VAR="$2"
|
||||
shift
|
||||
;;
|
||||
--flag)
|
||||
FLAG_VAR=true
|
||||
;;
|
||||
*)
|
||||
echo "Unknown: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
```
|
||||
|
||||
### 输出格式
|
||||
|
||||
- 最后一行打印单行 JSON
|
||||
- **强烈推荐使用 `jq`**:自动处理转义和类型
|
||||
|
||||
```bash
|
||||
# 推荐:使用 jq 构建(安全、可靠)
|
||||
jq -n \
|
||||
--arg file "$FILE" \
|
||||
--argjson count "$COUNT" \
|
||||
'{output_file: $file, items_processed: $count}'
|
||||
|
||||
# 备选:简单场景手动拼接(注意特殊字符转义)
|
||||
echo "{\"file\": \"$FILE\", \"count\": $COUNT}"
|
||||
```
|
||||
|
||||
**jq 参数类型**:
|
||||
- `--arg name value`:字符串类型
|
||||
- `--argjson name value`:数字/布尔/null 类型
|
||||
|
||||
### 错误处理
|
||||
|
||||
```bash
|
||||
# 验证错误
|
||||
if [[ -z "$PARAM" ]]; then
|
||||
echo "错误: 参数不能为空" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 命令错误
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "错误: 需要安装 jq" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 运行时错误
|
||||
if ! some_command; then
|
||||
echo "错误: 命令执行失败" >&2
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
## 常用模式
|
||||
|
||||
### 文件遍历
|
||||
|
||||
```bash
|
||||
for file in "$INPUT_DIR"/*.json; do
|
||||
[[ -f "$file" ]] || continue
|
||||
echo "处理: $file"
|
||||
# 处理逻辑...
|
||||
done
|
||||
```
|
||||
|
||||
### 临时文件
|
||||
|
||||
```bash
|
||||
TEMP_FILE=$(mktemp)
|
||||
trap "rm -f $TEMP_FILE" EXIT
|
||||
|
||||
echo "data" > "$TEMP_FILE"
|
||||
```
|
||||
|
||||
### 调用其他工具
|
||||
|
||||
```bash
|
||||
# 检查工具存在
|
||||
require_command() {
|
||||
if ! command -v "$1" &> /dev/null; then
|
||||
echo "错误: 需要 $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
require_command jq
|
||||
require_command curl
|
||||
```
|
||||
|
||||
### JSON 处理(使用 jq)
|
||||
|
||||
```bash
|
||||
# 读取 JSON 字段
|
||||
VALUE=$(jq -r '.field' "$INPUT_PATH")
|
||||
|
||||
# 修改 JSON
|
||||
jq '.field = "new_value"' "$INPUT_PATH" > "$OUTPUT_FILE"
|
||||
|
||||
# 合并 JSON 文件
|
||||
jq -s 'add' file1.json file2.json > merged.json
|
||||
```
|
||||
|
||||
## 生成函数
|
||||
|
||||
```javascript
|
||||
function generateBashScript(scriptConfig) {
|
||||
return `#!/bin/bash
|
||||
# ${scriptConfig.description}
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# 参数定义
|
||||
${scriptConfig.inputs.map(i =>
|
||||
`${i.name.toUpperCase().replace(/-/g, '_')}="${i.default || ''}"`
|
||||
).join('\n')}
|
||||
|
||||
# 参数解析
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
${scriptConfig.inputs.map(i =>
|
||||
` --${i.name})
|
||||
${i.name.toUpperCase().replace(/-/g, '_')}="$2"
|
||||
shift
|
||||
;;`
|
||||
).join('\n')}
|
||||
*)
|
||||
echo "未知参数: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# 参数验证
|
||||
${scriptConfig.inputs.filter(i => i.required).map(i =>
|
||||
`if [[ -z "$${i.name.toUpperCase().replace(/-/g, '_')}" ]]; then
|
||||
echo "错误: --${i.name} 是必需参数" >&2
|
||||
exit 1
|
||||
fi`
|
||||
).join('\n\n')}
|
||||
|
||||
# TODO: 实现处理逻辑
|
||||
|
||||
# 输出结果
|
||||
echo "{${scriptConfig.outputs.map(o =>
|
||||
`\\"${o.name}\\": \\"\\$${o.name.toUpperCase().replace(/-/g, '_')}\\"`
|
||||
).join(', ')}}"
|
||||
`;
|
||||
}
|
||||
```
|
||||
198
.claude/skills/skill-generator/templates/script-python.md
Normal file
198
.claude/skills/skill-generator/templates/script-python.md
Normal file
@@ -0,0 +1,198 @@
|
||||
# Python Script Template
|
||||
|
||||
Python 脚本模板,用于生成技能中的确定性脚本。
|
||||
|
||||
## 模板代码
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
{{script_description}}
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
# 1. 定义参数
|
||||
parser = argparse.ArgumentParser(description='{{script_description}}')
|
||||
parser.add_argument('--input-path', type=str, required=True,
|
||||
help='输入文件路径')
|
||||
parser.add_argument('--output-dir', type=str, required=True,
|
||||
help='输出目录(由调用方指定)')
|
||||
# 添加更多参数...
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# 2. 验证输入
|
||||
input_path = Path(args.input_path)
|
||||
if not input_path.exists():
|
||||
print(f"错误: 输入文件不存在: {input_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
output_dir = Path(args.output_dir)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 3. 执行核心逻辑
|
||||
try:
|
||||
result = process(input_path, output_dir)
|
||||
except Exception as e:
|
||||
print(f"错误: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# 4. 输出 JSON 结果
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
def process(input_path: Path, output_dir: Path) -> dict:
|
||||
"""
|
||||
核心处理逻辑
|
||||
|
||||
Args:
|
||||
input_path: 输入文件路径
|
||||
output_dir: 输出目录
|
||||
|
||||
Returns:
|
||||
dict: 包含输出结果的字典
|
||||
"""
|
||||
# TODO: 实现处理逻辑
|
||||
|
||||
output_file = output_dir / 'result.json'
|
||||
|
||||
# 示例:读取并处理数据
|
||||
with open(input_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# 处理数据...
|
||||
processed_count = len(data) if isinstance(data, list) else 1
|
||||
|
||||
# 写入输出
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
return {
|
||||
'output_file': str(output_file),
|
||||
'items_processed': processed_count,
|
||||
'status': 'success'
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
## 变量说明
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `{{script_description}}` | 脚本功能描述 |
|
||||
|
||||
## 使用规范
|
||||
|
||||
### 输入参数
|
||||
|
||||
- 使用 `argparse` 定义参数
|
||||
- 参数名使用 kebab-case:`--input-path`
|
||||
- 必需参数设置 `required=True`
|
||||
- 可选参数提供 `default` 值
|
||||
|
||||
### 输出格式
|
||||
|
||||
- 最后一行打印单行 JSON
|
||||
- 包含所有输出文件路径和关键数据
|
||||
- 错误信息输出到 stderr
|
||||
|
||||
### 错误处理
|
||||
|
||||
```python
|
||||
# 验证错误 - 直接退出
|
||||
if not valid:
|
||||
print("错误信息", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# 运行时错误 - 捕获并退出
|
||||
try:
|
||||
result = process()
|
||||
except Exception as e:
|
||||
print(f"错误: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
```
|
||||
|
||||
## 常用模式
|
||||
|
||||
### 文件处理
|
||||
|
||||
```python
|
||||
def process_files(input_dir: Path, pattern: str = '*.json') -> list:
|
||||
results = []
|
||||
for file in input_dir.glob(pattern):
|
||||
with open(file, 'r') as f:
|
||||
data = json.load(f)
|
||||
results.append({'file': str(file), 'data': data})
|
||||
return results
|
||||
```
|
||||
|
||||
### 数据转换
|
||||
|
||||
```python
|
||||
def transform_data(data: dict) -> dict:
|
||||
return {
|
||||
'id': data.get('id'),
|
||||
'name': data.get('name', '').strip(),
|
||||
'timestamp': datetime.now().isoformat()
|
||||
}
|
||||
```
|
||||
|
||||
### 调用外部命令
|
||||
|
||||
```python
|
||||
import subprocess
|
||||
|
||||
def run_command(cmd: list) -> str:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(result.stderr)
|
||||
return result.stdout
|
||||
```
|
||||
|
||||
## 生成函数
|
||||
|
||||
```javascript
|
||||
function generatePythonScript(scriptConfig) {
|
||||
return `#!/usr/bin/env python3
|
||||
"""
|
||||
${scriptConfig.description}
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='${scriptConfig.description}')
|
||||
${scriptConfig.inputs.map(i =>
|
||||
` parser.add_argument('--${i.name}', type=${i.type || 'str'}, ${i.required ? 'required=True' : `default='${i.default}'`},
|
||||
help='${i.description}')`
|
||||
).join('\n')}
|
||||
args = parser.parse_args()
|
||||
|
||||
# TODO: 实现处理逻辑
|
||||
result = {
|
||||
${scriptConfig.outputs.map(o =>
|
||||
` '${o.name}': None # ${o.description}`
|
||||
).join(',\n')}
|
||||
}
|
||||
|
||||
print(json.dumps(result))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
`;
|
||||
}
|
||||
```
|
||||
@@ -18,6 +18,13 @@
|
||||
- 依赖: `{{input_dependency}}`
|
||||
- 配置: `{workDir}/skill-config.json`
|
||||
|
||||
## Scripts
|
||||
|
||||
\`\`\`yaml
|
||||
# 声明本阶段使用的脚本(可选)
|
||||
# - script-id # 对应 scripts/script-id.py 或 .sh
|
||||
\`\`\`
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1: {{step_1_name}}
|
||||
@@ -32,10 +39,13 @@
|
||||
{{step_2_code}}
|
||||
\`\`\`
|
||||
|
||||
### Step 3: {{step_3_name}}
|
||||
### Step 3: 执行脚本(可选)
|
||||
|
||||
\`\`\`javascript
|
||||
{{step_3_code}}
|
||||
// 调用脚本示例
|
||||
// const result = await ExecuteScript('script-id', { input_path: `${workDir}/data.json` });
|
||||
// if (!result.success) throw new Error(result.stderr);
|
||||
// console.log(result.outputs.output_file);
|
||||
\`\`\`
|
||||
|
||||
## Output
|
||||
@@ -68,6 +78,44 @@
|
||||
| `{{quality_checklist}}` | 质量检查项 |
|
||||
| `{{next_phase_link}}` | 下一阶段链接 |
|
||||
|
||||
## 脚本调用说明
|
||||
|
||||
### 目录约定
|
||||
|
||||
```
|
||||
scripts/
|
||||
├── process-data.py # id: process-data, runtime: python
|
||||
├── validate.sh # id: validate, runtime: bash
|
||||
└── transform.js # id: transform, runtime: node
|
||||
```
|
||||
|
||||
- **命名即 ID**:文件名(不含扩展名)= 脚本 ID
|
||||
- **扩展名即运行时**:`.py` → python, `.sh` → bash, `.js` → node
|
||||
|
||||
### 调用语法
|
||||
|
||||
```javascript
|
||||
// 一行调用
|
||||
const result = await ExecuteScript('script-id', { key: value });
|
||||
|
||||
// 检查结果
|
||||
if (!result.success) throw new Error(result.stderr);
|
||||
|
||||
// 获取输出
|
||||
const { output_file } = result.outputs;
|
||||
```
|
||||
|
||||
### 返回格式
|
||||
|
||||
```typescript
|
||||
interface ScriptResult {
|
||||
success: boolean; // exit code === 0
|
||||
stdout: string; // 标准输出
|
||||
stderr: string; // 标准错误
|
||||
outputs: object; // 从 stdout 解析的 JSON 输出
|
||||
}
|
||||
```
|
||||
|
||||
## Phase 类型模板
|
||||
|
||||
### 1. 收集型 Phase (Collection)
|
||||
|
||||
Reference in New Issue
Block a user