Skill: utopiabenben/skill-composer (version 1.0.0)

--- name: skill-composer description: 编排多个OpenClaw技能成自动化工作流，一次命令完成复杂任务。 homepage: https://github.com/utopiabenben/ai-skills version: 1.0.0 # 首次发布时间：2026-03-16 # 作者：小叮当（智脑） # 许可证：MIT --- # Skill Composer - 技能组合器 把多个OpenClaw技能串联成自动化工作流，一个命令完成多步操作！ ## 为什么需要？ 单个技能只能解决一步问题。真实场景往往需要多步： - **视频处理**：整理 → 提取字幕 → 发布到公众号 - **内容创作**：搜索素材 → 总结 → 多平台裁剪 → 发布 - **数据流程**：下载数据 → 分析 → 生成图表 → 插入报告 Skill Composer 让你用 YAML 定义工作流，一键执行！ ## 快速开始 ### 1. 创建工作流文件 创建 `workflow.yaml`： ```yaml name: "示例：视频处理流程" steps: - name: "整理视频" skill: video-organizer args: - --input - /path/to/videos - --output - /tmp/organized output: organized - name: "生成字幕" skill: auto-subtitle args: - --input - "{{organized}}" - --output - /tmp/subtitles output: subtitles - name: "发布到公众号" skill: social-publisher args: - --input - "{{subtitles}}" - --platform - wechat ``` ### 2. 运行工作流 ```bash python3 {baseDir}/scripts/composer.py run workflow.yaml ``` ### 3. 预览（不实际执行） ```bash python3 {baseDir}/scripts/composer.py preview workflow.yaml ``` ## 工作流语法 ### 结构 - `name`: 工作流名称（可选） - `steps`: 步骤列表 - 每个 step 包含： - `name`: 步骤名称（可选，用于日志） - `skill`: 要调用的技能名称 - `args`: 参数列表（字符串数组） - `output`: 输出引用名（用于后续步骤引用） - `if`: 条件表达式（可选） ### 变量插值 使用 `{{变量名}}` 引用前一步的输出目录/文件。 示例： ```yaml args: - --input - "{{organized}}" # 引用名为 'organized' 的前一步输出 ``` ### 条件执行 ```yaml - name: "只在有错误时执行" skill: error-notifier if: "{{previous_step.status}} == 'failed'" ``` ## 命令行接口 ```bash # 运行工作流 python3 {baseDir}/scripts/composer.py run <workflow.yaml> # 预览 python3 {baseDir}/scripts/composer.py preview <workflow.yaml> # 验证语法 python3 {baseDir}/scripts/composer.py validate <workflow.yaml> # 列出可用示例 python3 {baseDir}/scripts/composer.py examples ``` ## 示例工作流 ### 示例1：内容创作流程 ```yaml name: "公众号文章创作" steps: - skill: content-researcher args: ["--topic", "AI技能开发", "--count", "10"] output: research - skill: ai-content-tailor args: ["--input", "{{research}}", "--platform", "wechat"] output: article - skill: wechat-formatter args: ["--input", "{{article}}", "--output", "./final.md"] ``` ### 示例2：数据仪表板生成 ```yaml name: "每周股票报告" steps: - skill: tushare-finance args: ["--get", "daily", "--code", "000001.SZ", "--start", "2025-01-01"] output: raw_data - skill: data-chart-tool args: ["--input", "{{raw_data}}", "--type", "line", "--output", "chart.png"] output: chart - skill: social-publisher args: ["--input", "{{chart}}", "--template", "weekly-report"] ``` ## 错误处理 - 默认：任何步骤失败则停止整个工作流 - 可配置：`continue-on-error: true` 在 workflow 级别 - 每个步骤状态可用：`{{step_name.status}}`（success/failed） ## 限制 - 当前仅支持串行步骤（下一步依赖前一步完成） - 不支持并行执行（未来版本） - 步骤间传递的是文件路径，不是内容 ## 与现有技能配合 Skill Composer 不重复造轮子，它是指挥官： - 复用所有已安装的技能 - 专注于步骤编排 - 让单个技能的价值加倍 **示例技能组合**： - `video-organizer` + `auto-subtitle` + `social-publisher` → 完整视频发布流水线 - `content-researcher` + `ai-content-tailor` + `wechat-formatter` → 内容生产流水线 - `data-chart-tool` + `social-publisher` → 数据报告自动化 ## 技术实现 - Python 3 + PyYAML - 调用 OpenClaw exec 工具运行每个技能 - 自动处理依赖顺序 - 详细日志输出 ## 开发状态 - [ ] 核心 YAML 解析 - [ ] 步骤顺序执行 - [ ] 变量插值 {{output}} - [ ] 错误处理和状态跟踪 - [ ] 示例工作流 - [ ] 单元测试 - [ ] skill-creator 验证 - [ ] clawhub 发布 ## 待办 未来增强： - [ ] 并行步骤支持（无依赖的步骤可以同时执行） - [ ] 可视化工作流编辑器 - [ ] 工作flow模板库 - [ ] 步骤结果缓存（避免重复执行） - [ ] 更好的错误恢复机制 --- ## 📞 支持 - GitHub Issues: https://github.com/utopiabenben/ai-skills/issues - 作品集网站: https://utopiabenben.github.io/ai-skills/

path: examples/content-creation-pipeline.yaml
content: # 内容创作流水线：素材收集 → 总结 → 多平台裁剪 → 格式化
# 使用技能：content-researcher, ai-content-tailor, wechat-formatter

name: "content-creation-pipeline"
steps:
  - name: "Step 1: 收集素材"
    skill: content-researcher
    args:
      - --topic
      - "AI技能开发"
      - --sources
      - "web,news"
      - --count
      - "10"
      - --output
      - /tmp/research.json
    output: research

  - name: "Step 2: 裁剪为公众号文章"
    skill: ai-content-tailor
    args:
      - --input
      - "{{research}}"
      - --platform
      - wechat
      - --output
      - /tmp/wechat_article.md
    output: wechat_article

  - name: "Step 3: 格式化为公众号可用格式"
    skill: wechat-formatter
    args:
      - --input
      - "{{wechat_article}}"
      - --output
      - /tmp/final_wechat.md
    output: final

# 可选步骤：同时生成小红书笔记
#  - name: "Step 4: 裁剪为小红书笔记"
#    skill: ai-content-tailor
#    args:
#      - --input
#      - "{{research}}"
#      - --platform
#      - xiaohongshu
#      - --output
#      - /tmp/xhs_note.md
#    output: xhs_note

# 执行：
#   composer.py run content-creation-pipeline.yaml

path: examples/video-processing-pipeline.yaml
content: # 视频处理完整流程：整理 → 字幕 → 格式化
# 使用技能：video-organizer, auto-subtitle, wechat-formatter

name: "video-processing-pipeline"
steps:
  - name: "Step 1: 整理视频文件"
    skill: video-organizer
    args:
      - --input
      - /path/to/raw/videos
      - --output
      - /tmp/organized_videos
      - --by
      - date
    output: organized_videos

  - name: "Step 2: 生成字幕文件"
    skill: auto-subtitle
    args:
      - --input
      - "{{organized_videos}}"
      - --output
      - /tmp/subtitles
      - --format
      - srt
    output: subtitles

  - name: "Step 3: 格式化为公众号文章"
    skill: wechat-formatter
    args:
      - --input
      - "{{subtitles}}"
      - --output
      - /tmp/article.md
    output: formatted_article

# 说明：
# 1. 修改 --input 路径为你的实际视频目录
# 2. run: composer.py run video-processing-pipeline.yaml
# 3. 输出：/tmp/article.md 包含格式化好的公众号文章草稿

path: examples/weekly-stock-report.yaml
content: # 数据报告自动化：获取数据 → 生成图表 → 插入模板 → 发布
# 使用技能：tushare-finance, data-chart-tool, social-publisher

name: "weekly-stock-report"
steps:
  - name: "Step 1: 获取股票数据"
    skill: tushare-finance
    args:
      - pro
      - ts_daily
      - -t
      - "000001.SZ"
      - -s
      - "2025-01-01"
      - -e
      - "2025-12-31"
      - -o
      - /tmp/stock_data.csv
    output: raw_data

  - name: "Step 2: 生成趋势图"
    skill: data-chart-tool
    args:
      - --input
      - "{{raw_data}}"
      - --type
      - line
      - --x
      - trade_date
      - -y
      - close,volume
      - --output
      - /tmp/chart.png
      - --title
      - "平安银行股价走势"
    output: chart

  - name: "Step 3: 生成报告草稿"
    skill: social-publisher
    args:
      - --input
      - "{{chart}}"
      - --template
      - weekly-report
      - --output
      - /tmp/report.md
    output: report

# 说明：
# 需要安装 tushare-finance 并配置 token
# 执行：composer.py run weekly-stock-report.yaml

path: install.sh
content: #!/bin/bash
set -e

# Skill Composer 安装脚本
# 安装依赖：PyYAML

echo "🔗 Installing Skill Composer dependencies..."

# 检查 python3
if ! command -v python3 &> /dev/null; then
    echo "❌ Error: python3 is not installed"
    exit 1
fi

# 检测系统包管理器，尝试安装 PyYAML
if command -v apt-get &> /dev/null; then
    echo "📦 Installing PyYAML via apt..."
    apt-get update -qq
    apt-get install -y -qq python3-yaml
elif command -v pip3 &> /dev/null; then
    echo "📦 Installing PyYAML via pip..."
    pip3 install --user PyYAML
else
    echo "❌ Cannot install PyYAML: no apt-get or pip3 found"
    exit 1
fi

echo "✅ Skill Composer dependencies installed!"
echo ""
echo "📚 Usage:"
echo "   python3 {baseDir}/scripts/composer.py run <workflow.yaml>"
echo "   python3 {baseDir}/scripts/composer.py preview <workflow.yaml>"
echo ""
echo "📖 See SKILL.md for detailed documentation."

path: skill.json
content: {
  "name": "skill-composer",
  "version": "1.0.0",
  "description": "编排多个 OpenClaw 技能成自动化工作流，一次命令完成复杂任务。",
  "homepage": "https://github.com/utopiabenben/ai-skills",
  "author": "小叮当 (智脑)",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/utopiabenben/ai-skills"
  },
  "keywords": [
    "openclaw",
    "workflow",
    "automation",
    "composer",
    "pipeline",
    "orchestration"
  ],
  "openclaw": {
    "emoji": "🔗",
    "minOpenClawVersion": "0.7.0",
    "tags": ["automation", "productivity", "devops"],
    "skills": ["*"],  # 可以编排任何已安装的技能
    "ui": {
      "type": "cli"
    },
    "requirements": {
      "bins": ["python3"],
      "python": ["PyYAML>=5.4"]
    },
    "install": [
      {
        "id": "python-pyyaml",
        "kind": "pip",
        "package": "PyYAML",
        "label": "Install PyYAML dependency"
      }
    ],
    "commands": [
      {
        "name": "run",
        "description": "Run a workflow definition",
        "params": [
          {
            "name": "workflow",
            "type": "file",
            "required": true,
            "description": "Path to workflow YAML file"
          }
        ]
      },
      {
        "name": "preview",
        "description": "Preview workflow steps without executing",
        "params": [
          {
            "name": "workflow",
            "type": "file",
            "required": true,
            "description": "Path to workflow YAML file"
          }
        ]
      },
      {
        "name": "validate",
        "description": "Validate workflow syntax",
        "params": [
          {
            "name": "workflow",
            "type": "file",
            "required": true,
            "description": "Path to workflow YAML file"
          }
        ]
      },
      {
        "name": "examples",
        "description": "List example workflows",
        "params": []
      }
    ]
  }
}

path: source/composer.py
content: #!/usr/bin/env python3
"""
Skill Composer - 编排多个 OpenClaw 技能成自动化工作流
"""

import os
import sys
import json
import yaml
import subprocess
from pathlib import Path
from typing import Dict, List, Any

class WorkflowStep:
    """工作流步骤"""
    def __init__(self, step_data: Dict, step_index: int):
        self.name = step_data.get('name', f'Step {step_index+1}')
        self.skill = step_data['skill']
        self.args = step_data.get('args', [])
        self.output = step_data.get('output')  # 输出引用名
        self.condition = step_data.get('if')   # 条件表达式
        self.status = 'pending'
        self.output_path = None  # 实际输出路径

    def __str__(self):
        return f"{self.name}: {self.skill} {' '.join(self.args)}"

class Workflow:
    """工作流"""
    def __init__(self, filepath: str):
        self.filepath = Path(filepath)
        self.name = 'Untitled Workflow'
        self.steps: List[WorkflowStep] = []
        self.continue_on_error = False
        self.variables = {}  # 变量存储：{output_name: path}
        self.base_dir = self.filepath.parent

    def load(self):
        """加载并解析 YAML"""
        with open(self.filepath, 'r', encoding='utf-8') as f:
            data = yaml.safe_load(f)

        self.name = data.get('name', self.name)
        self.continue_on_error = data.get('continue-on-error', False)

        steps_data = data.get('steps', [])
        for idx, step_data in enumerate(steps_data):
            step = WorkflowStep(step_data, idx)
            self.steps.append(step)

    def interpolate(self, args: List[str]) -> List[str]:
        """替换参数中的变量 {{var}}"""
        result = []
        for arg in args:
            # 简单的变量插值
            for var_name, var_value in self.variables.items():
                placeholder = f"{{{{{var_name}}}}}"  # {{var}}
                if isinstance(arg, str) and placeholder in arg:
                    arg = arg.replace(placeholder, var_value)
            result.append(arg)
        return result

    def evaluate_condition(self, condition: str, step: WorkflowStep) -> bool:
        """评估条件表达式（简化版）"""
        if not condition:
            return True
        # 简单的状态检查：{{step_name.status}} == 'success'
        for var_name, var_value in self.variables.items():
            placeholder = f"{{{{{var_name}}}}}"
            if placeholder in condition:
                condition = condition.replace(placeholder, str(var_value))
        # 安全评估
        try:
            return eval(condition, {"__builtins__": {}})
        except:
            return True  # 默认执行

    def exec_skill(self, skill: str, args: List[str]) -> tuple:
        """执行一个技能"""
        cmd = ['claw', 'skill', 'exec', skill] + args
        print(f"  ▶️  Executing: {skill}")
        print(f"     Args: {args}")

        try:
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
            if result.returncode == 0:
                return 'success', result.stdout
            else:
                return 'failed', result.stderr
        except subprocess.TimeoutExpired:
            return 'timeout', 'Execution timed out'
        except Exception as e:
            return 'error', str(e)

    def run(self):
        """运行工作流"""
        print(f"🚀 Running workflow: {self.name}")
        print(f"📁 File: {self.filepath}")
        print(f"📊 Total steps: {len(self.steps)}")
        print()

        for idx, step in enumerate(self.steps, 1):
            print(f"🔹 Step {idx}/{len(self.steps)}: {step.name}")

            # 检查条件
            if not self.evaluate_condition(step.condition, step):
                print(f"  ⏭️  Skipped (condition not met)")
                step.status = 'skipped'
                continue

            # 插值参数
            args = self.interpolate(step.args)

            # 执行
            status, output = self.exec_skill(step.skill, args)
            step.status = status

            if status == 'success':
                print(f"  ✅ Success")
                # 记录输出路径（如果定义了output）
                if step.output:
                    # 假设输出是最后一个参数指定的路径（简化）
                    # 实际应该解析技能的返回信息
                    self.variables[step.output] = args[-1] if args else '.'
            else:
                print(f"  ❌ Failed: {output}")
                if not self.continue_on_error:
                    print(f"\n⛔ Workflow stopped at step {idx}")
                    break

            print()

        # 总结
        self.summary()

    def preview(self):
        """预览工作流"""
        print(f"👀 Previewing workflow: {self.name}")
        print(f"📁 File: {self.filepath}")
        print(f"📊 Total steps: {len(self.steps)}")
        print()
        for idx, step in enumerate(self.steps, 1):
            args = self.interpolate(step.args)
            print(f"{idx}. {step.name}")
            print(f"   Skill: {step.skill}")
            print(f"   Args: {args}")
            if step.output:
                print(f"   Output var: {step.output}")
            print()

    def validate(self) -> bool:
        """验证工作流语法"""
        print(f"🔍 Validating workflow: {self.filepath}")
        errors = []

        # 检查必需字段
        for step in self.steps:
            if not step.skill:
                errors.append(f"Step '{step.name}' missing 'skill' field")
            if not step.args and step.args != []:
                errors.append(f"Step '{step.name}' has invalid 'args'")

        if errors:
            print("❌ Validation failed:")
            for err in errors:
                print(f"  - {err}")
            return False
        else:
            print("✅ Workflow is valid!")
            return True

    def summary(self):
        """输出总结"""
        print("="*50)
        print("📋 Workflow Summary")
        print("="*50)
        success_count = sum(1 for s in self.steps if s.status == 'success')
        failed_count = sum(1 for s in self.steps if s.status == 'failed')
        skipped_count = sum(1 for s in self.steps if s.status == 'skipped')

        for step in self.steps:
            status_icon = {'success': '✅', 'failed': '❌', 'skipped': '⏭️', 'pending': '⏳'}.get(step.status, '❓')
            print(f"  {status_icon} {step.name} ({step.status})")

        print()
        print(f"✅ Success: {success_count}")
        print(f"❌ Failed: {failed_count}")
        print(f"⏭️  Skipped: {skipped_count}")
        print("="*50)

def main():
    if len(sys.argv) < 2:
        print("Usage:")
        print("  composer.py run <workflow.yaml>")
        print("  composer.py preview <workflow.yaml>")
        print("  composer.py validate <workflow.yaml>")
        print("  composer.py examples")
        sys.exit(1)

    command = sys.argv[1]
    workflow_file = sys.argv[2] if len(sys.argv) > 2 else None

    base_dir = Path(__file__).parent.parent

    if command == 'examples':
        examples_dir = base_dir / 'examples'
        if examples_dir.exists():
            print("📚 Example Workflows:")
            for ex in examples_dir.glob('*.yaml'):
                print(f"  - {ex.name}")
                with open(ex, 'r') as f:
                    first_line = f.readline().strip()
                    if first_line.startswith('# '):
                        print(f"    {first_line[2:]}")
        else:
            print("ℹ️  No examples directory found.")
        return

    if not workflow_file:
        print("❌ Workflow file is required")
        sys.exit(1)

    workflow_path = Path(workflow_file)
    if not workflow_path.exists():
        print(f"❌ Workflow file not found: {workflow_file}")
        sys.exit(1)

    workflow = Workflow(workflow_path)
    workflow.load()

    if command == 'run':
        workflow.run()
    elif command == 'preview':
        workflow.preview()
    elif command == 'validate':
        workflow.validate()
    else:
        print(f"❌ Unknown command: {command}")
        sys.exit(1)

if __name__ == '__main__':
    main()

Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)