Skill: sunnyhot/skill-sync (version 1.5.0)

--- name: skill-sync version: 1.0.0 description: Automatically sync local skills to ClawHub and GitHub. Detects new/modified skills, publishes to ClawHub, commits to GitHub, and maintains sync status. author: sunnyhot license: MIT keywords: - skill-sync - clawhub - github - auto-publish - auto-sync - skill-manager --- # Skill Sync - 自动同步 Skills 到 ClawHub & GitHub **一键同步你的所有 skills** --- ## ✨ 核心功能 ### 🔄 **自动同步** - ✅ 检测新增的 skills - ✅ 检测修改的 skills - ✅ 自动发布到 ClawHub - ✅ 自动提交到 GitHub - ✅ 记录同步状态 ### 📊 **状态管理** - ✅ 跟踪每个 skill 的同步状态 - ✅ 记录 ClawHub 发布 ID - ✅ 记录 GitHub commit hash - ✅ 提供同步报告 ### 🔍 **智能检测** - ✅ 检测 SKILL.md 文件变化 - ✅ 检测 scripts 文件变化 - ✅ 检测 package.json 变化 - ✅ 忽略临时文件和缓存 --- ## 🚀 使用方法 ### 1. **手动触发同步** ```bash node /Users/xufan65/.openclaw/workspace/skills/skill-sync/scripts/sync.cjs ``` **功能**: - 扫描所有 skills - 检测需要同步的 skills - 发布到 ClawHub - 提交到 GitHub - 推送同步报告 --- ### 2. **自动监控模式**（推荐） 创建定时任务，每小时自动检查并同步： ```bash openclaw cron add \ --name "skill-sync" \ --cron "0 * * * *" \ --tz "Asia/Shanghai" \ --session isolated \ --wake now \ --deliver \ --message "运行 skill-sync: 检查所有 skills 的变化，自动同步到 ClawHub 和 GitHub。报告格式用中文。" ``` **运行频率**: - 每小时检查一次（`0 * * * *`） - 或每 30 分钟检查一次（`*/30 * * * *`） --- ### 3. **查看同步状态** ```bash cat /Users/xufan65/.openclaw/workspace/memory/skill-sync-status.json ``` **包含内容**: - 每个 skill 的同步状态 - ClawHub 发布 ID - GitHub commit hash - 最后同步时间 - 版本信息 --- ## 📋 工作流程 ``` ┌─────────────────┐ │ 扫描 Skills │ │ - 检测新增 │ │ - 检测修改 │ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ 生成同步列表 │ │ - 新 skills │ │ - 修改的 skills│ └────────┬────────┘ │ ▼ 有需要同步的？ │ ┌────┴────┐ Yes No │ │ ▼ ▼ ┌───────┐ ┌──────────┐ │同步流程│ │推送报告 │ └───┬───┘ │无需同步 │ │ └──────────┘ ▼ ┌─────────────────┐ │ 1. Git Commit │ │ 2. ClawHub Pub │ │ 3. Git Push │ │ 4. 更新状态 │ └────────┬────────┘ │ ▼ ┌─────────────────┐ │ 推送同步报告 │ │ - 成功列表 │ │ - 失败列表 │ │ - 状态更新 │ └─────────────────┘ ``` --- ## 🔧 配置选项 ### `config/settings.json` ```json { "skillsDir": "/Users/xufan65/.openclaw/workspace/skills", "statusFile": "/Users/xufan65/.openclaw/workspace/memory/skill-sync-status.json", "gitRepo": "/Users/xufan65/.openclaw/workspace", "clawhubConfig": { "autoPublish": true, "requireConfirmation": false }, "gitConfig": { "autoCommit": true, "autoPush": true, "commitMessage": "Auto-sync skills to ClawHub" }, "ignorePatterns": [ "node_modules", ".git", "*.log", "*.tmp" ] } ``` --- ## 📊 同步状态文件 ### `memory/skill-sync-status.json` ```json { "lastSync": "2026-03-12T14:30:00+08:00", "skills": { "deals-hunter": { "version": "3.0.0", "clawhubId": [REDACTED_SECRET], "clawhubPublished": "2026-03-12T10:00:00+08:00", "gitCommit": "7779310", "gitPushed": "2026-03-12T14:30:00+08:00", "status": "synced" }, "failure-monitor": { "version": "1.0.0", "clawhubId": [REDACTED_SECRET], "clawhubPublished": "2026-03-12T14:30:00+08:00", "gitCommit": "22b355d", "gitPushed": "2026-03-12T14:35:00+08:00", "status": "synced" } } } ``` --- ## 🎯 同步条件 ### **需要同步的情况**: 1. **新增 Skill** - `skills/` 目录下出现新的 skill 文件夹 - 包含 `SKILL.md` 文件 2. **版本更新** - `SKILL.md` 中的 `version` 字段变化 - `package.json` 中的 `version` 字段变化 3. **文件修改** - `SKILL.md` 被修改 - `scripts/` 目录下的文件被修改 - `config/` 目录下的文件被修改 4. **未同步状态** - 状态文件中没有该 skill 的记录 - 状态为 `pending` 或 `failed` --- ## 🔒 安全考虑 - ✅ **只同步 skills 目录**：不会修改其他文件 - ✅ **保留 Git 历史**：使用 `git add` + `git commit` - ✅ **ClawHub 验证**：发布前检查 skill 格式 - ✅ **错误处理**：发布失败不会中断整个流程 - ✅ **回滚支持**：可以通过 Git 回滚到之前的状态 --- ## 📝 Discord 推送格式 ### **同步成功** ```markdown # ✅ Skills 同步完成 **同步时间**: 2026-03-12 14:30:00 **总计**: 3 个 skills ## 📦 成功同步 ### 1. **failure-monitor** v1.0.0 - ✅ ClawHub: k973cr1p4k5g3mh36yjp24agg182rcsa - ✅ GitHub: commit 22b355d - 📅 发布时间: 2026-03-12 14:30:00 ### 2. **deals-hunter** v3.0.0 - ✅ ClawHub: k97c0kee4b0t8rmf4490m9r9bn82s082 - ✅ GitHub: commit 7779310 - 📅 发布时间: 2026-03-12 10:00:00 --- **🔗 快速链接**: - ClawHub: https://clawhub.com/skill/failure-monitor - GitHub: https://github.com/sunnyhot/deals-hunter ``` ### **无需同步** ```markdown # ℹ️ Skills 检查完成 **检查时间**: 2026-03-12 15:00:00 **总计**: 10 个 skills ✅ 所有 skills 已是最新状态，无需同步 ``` ### **部分失败** ```markdown # ⚠️ Skills 同步完成（部分失败） **同步时间**: 2026-03-12 14:30:00 **成功**: 2 个 | **失败**: 1 个 ## ✅ 成功同步 - failure-monitor v1.0.0 - deals-hunter v3.0.0 ## ❌ 失败列表 ### skill-name v1.0.0 - **错误**: ClawHub API rate limit exceeded - **建议**: 稍后重试或手动发布 --- **建议**: 检查失败的 skills 并手动修复 ``` --- ## 📁 文件结构 ``` skills/skill-sync/ ├── SKILL.md # 技能说明 ├── scripts/ │ ├── sync.cjs # 主同步脚本 │ ├── scan-skills.cjs # 扫描 skills │ ├── publish-clawhub.cjs # 发布到 ClawHub │ └── git-sync.cjs # Git 同步 ├── config/ │ └── settings.json # 配置选项 └── package.json # 依赖管理 ``` --- ## 🔧 命令行选项 ### **完整同步** ```bash node scripts/sync.cjs --all ``` 同步所有需要同步的 skills ### **指定 Skill** ```bash node scripts/sync.cjs --skill failure-monitor ``` 只同步指定的 skill ### **仅 ClawHub** ```bash node scripts/sync.cjs --clawhub-only ``` 只发布到 ClawHub，不提交到 GitHub ### **仅 GitHub** ```bash node scripts/sync.cjs --git-only ``` 只提交到 GitHub，不发布到 ClawHub ### **Dry Run** ```bash node scripts/sync.cjs --dry-run ``` 只检查需要同步的 skills，不实际执行 --- ## 🎯 最佳实践 ### 1. **运行频率** - ✅ 推荐：每小时检查一次 - ✅ 可接受：每 30 分钟检查一次 - ❌ 不推荐：超过 2 小时（可能错过更新） ### 2. **版本管理** - ✅ 每次修改后更新 `version` 字段 - ✅ 使用语义化版本（SemVer） - ✅ 在 SKILL.md 中记录更新日志 ### 3. **提交信息** - ✅ 使用清晰的 commit message - ✅ 包含 skill 名称和版本 - ✅ 描述主要修改内容 --- ## 🚨 故障排除 ### **Q: ClawHub 发布失败怎么办？** A: 检查以下几点： 1. ClawHub CLI 是否已登录（`clawhub login`） 2. SKILL.md 格式是否正确 3. 是否有网络问题 4. 可以稍后重试或手动发布 ### **Q: Git 推送失败怎么办？** A: 检查以下几点： 1. Git 仓库是否正确配置 2. 是否有网络问题 3. 是否有冲突需要解决 4. 可以手动 `git push` 解决 ### **Q: 如何查看详细日志？** A: 查看日志文件： ```bash cat /Users/xufan65/.openclaw/workspace/memory/skill-sync-log.json ``` --- ## 📝 更新日志 ### v1.0.0 (2026-03-12) - ✅ 初始版本 - ✅ 支持自动检测 skills 变化 - ✅ 支持自动发布到 ClawHub - ✅ 支持自动提交到 GitHub - ✅ 支持同步状态跟踪 - ✅ 支持 Discord 推送通知 --- ## 🤝 贡献 欢迎提交 Issue 和 Pull Request！ **GitHub**: https://github.com/sunnyhot/skill-sync --- **🎉 让你的 skills 管理更加自动化！**

path: config/settings.json
content: {
  "ownSkills": [
    "deals-hunter",
    "failure-monitor",
    "skill-sync",
    "podcast-production-pipeline",
    "autonomous-pm",
    "clawhub-skill-installer",
    "log-analyzer",
    "system-health-monitor",
    "opik-reporter",
    "clawpi-redpacket-monitor"
  ],
  "syncSettings": {
    "autoCommitToGit": true,
    "autoPublishToClawhub": true,
    "checkInterval": 60,
    "retryFailedSync": true,
    "maxRetries": 3,
    "onlySyncOwnSkills": true
  },
  "gitSettings": {
    "autoPush": true,
    "commitMessageTemplate": "Update skill: {name} v{version}",
    "branch": "main"
  },
  "clawhubSettings": {
    "autoLogin": false,
    "tags": ["latest"],
    "timeout": 60000
  },
  "excludeSkills": [
    ".git",
    "node_modules",
    ".DS_Store"
  ],
  "notifySettings": {
    "onSuccess": true,
    "onFailure": true,
    "channel": "discord",
    "to": "channel:1478698808631361647"
  }
}


path: package.json
content: {
  "name": "skill-sync",
  "version": "1.0.0",
  "description": "Automatically sync local skills to ClawHub and GitHub",
  "main": "scripts/sync.cjs",
  "scripts": {
    "start": "node scripts/sync.cjs",
    "sync": "node scripts/sync.cjs"
  },
  "keywords": [
    "skill-sync",
    "clawhub",
    "github",
    "auto-sync",
    "skill-manager",
    "openclaw"
  ],
  "author": "sunnyhot",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/sunnyhot/skill-sync"
  }
}


path: scripts/sync.cjs
content: #!/usr/bin/env node

/**
 * Skill Sync - 自动同步技能到 ClawHub 和 GitHub
 * 
 * 功能：
 * 1. 扫描所有 skills
 * 2. 检测需要同步的（新增/修改/未发布）
 * 3. 发布到 ClawHub
 * 4. 提交到 GitHub（独立仓库模式）
 * 5. 记录同步状态
 */

const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');

// 配置
const CONFIG = {
  skillsDir: '/Users/xufan65/.openclaw/workspace/skills',
  statusFile: '/Users/xufan65/.openclaw/workspace/memory/skill-sync-status.json',
  logFile: '/Users/xufan65/.openclaw/workspace/memory/skill-sync-log.json',
};

class SkillSync {
  constructor() {
    this.skills = [];
    this.toSync = [];
    this.status = this.loadStatus();
    this.log = this.loadLog();
  }

  /**
   * 加载同步状态
   */
  loadStatus() {
    try {
      if (fs.existsSync(CONFIG.statusFile)) {
        return JSON.parse(fs.readFileSync(CONFIG.statusFile, 'utf8'));
      }
    } catch (e) {
      console.error('加载状态失败:', e.message);
    }
    return { skills: {} };
  }

  /**
   * 保存同步状态
   */
  saveStatus() {
    try {
      fs.writeFileSync(CONFIG.statusFile, JSON.stringify(this.status, null, 2));
    } catch (e) {
      console.error('保存状态失败:', e.message);
    }
  }

  /**
   * 加载同步日志
   */
  loadLog() {
    try {
      if (fs.existsSync(CONFIG.logFile)) {
        return JSON.parse(fs.readFileSync(CONFIG.logFile, 'utf8'));
      }
    } catch (e) {
      console.error('加载日志失败:', e.message);
    }
    return { entries: [] };
  }

  /**
   * 保存同步日志
   */
  saveLog() {
    try {
      // 只保留最近 100 条记录
      if (this.log.entries.length > 100) {
        this.log.entries = this.log.entries.slice(-100);
      }
      fs.writeFileSync(CONFIG.logFile, JSON.stringify(this.log, null, 2));
    } catch (e) {
      console.error('保存日志失败:', e.message);
    }
  }

  /**
   * 加载配置
   */
  loadConfig() {
    try {
      const configPath = '/Users/xufan65/.openclaw/workspace/skills/skill-sync/config/settings.json';
      if (fs.existsSync(configPath)) {
        const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
        return config;
      }
    } catch (e) {
      console.error('加载配置失败:', e.message);
    }
    return { ownSkills: [] };
  }

  /**
   * 检查是否有独立 Git 仓库
   */
  hasOwnGitRepo(skillPath) {
    const gitDir = path.join(skillPath, '.git');
    return fs.existsSync(gitDir);
  }

  /**
   * 扫描所有 skills
   */
  scanSkills() {
    console.log('📁 扫描 skills 目录...\n');
    
    // 加载配置
    const config = this.loadConfig();
    const ownSkills = config.ownSkills || [];
    
    console.log(`📋 配置的自己的 skills (${ownSkills.length} 个):`);
    ownSkills.forEach(skill => console.log(`   • ${skill}`));
    console.log('');
    
    try {
      const dirs = fs.readdirSync(CONFIG.skillsDir, { withFileTypes: true });
      
      this.skills = dirs
        .filter(dirent => {
          if (!dirent.isDirectory()) return false;
          
          // 只包含自己创建的 skills
          if (ownSkills.length > 0 && !ownSkills.includes(dirent.name)) {
            return false;
          }
          
          return true;
        })
        .map(dirent => {
          const skillDir = path.join(CONFIG.skillsDir, dirent.name);
          const skillMdPath = path.join(skillDir, 'SKILL.md');
          
          // 检查是否有 SKILL.md
          if (!fs.existsSync(skillMdPath)) {
            return null;
          }

          // 读取 SKILL.md
          const skillMd = fs.readFileSync(skillMdPath, 'utf8');
          
          // 提取元数据
          const metadata = this.parseSkillMetadata(skillMd);
          
          // 检查是否有独立 Git 仓库
          const hasOwnRepo = this.hasOwnGitRepo(skillDir);
          
          return {
            name: dirent.name,
            path: skillDir,
            skillMdPath,
            metadata,
            modified: this.getFileModifiedTime(skillMdPath),
            hasOwnRepo
          };
        })
        .filter(skill => skill !== null);

      console.log(`✅ 找到 ${this.skills.length} 个自己的 skills:\n`);
      this.skills.forEach(skill => {
        const version = skill.metadata.version || 'unknown';
        const repoIcon = skill.hasOwnRepo ? '📦' : '📁';
        console.log(`   ${repoIcon} ${skill.name} (v${version}) ${skill.hasOwnRepo ? '[独立仓库]' : ''}`);
      });
    } catch (e) {
      console.error('❌ 扫描失败:', e.message);
      process.exit(1);
    }
  }

  /**
   * 解析 SKILL.md 元数据
   */
  parseSkillMetadata(content) {
    const metadata = {};
    
    // 提取 YAML front matter
    const frontMatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
    if (frontMatterMatch) {
      const frontMatter = frontMatterMatch[1];
      
      // 提取 name
      const nameMatch = frontMatter.match(/^name:\s*(.+)$/m);
      if (nameMatch) metadata.name = nameMatch[1].trim();
      
      // 提取 version
      const versionMatch = frontMatter.match(/^version:\s*["']?(.+?)["']?\s*$/m);
      if (versionMatch) metadata.version = versionMatch[1].trim();
      
      // 提取 description
      const descMatch = frontMatter.match(/^description:\s*["']?(.+?)["']?\s*$/m);
      if (descMatch) metadata.description = descMatch[1].trim();
    }
    
    return metadata;
  }

  /**
   * 获取文件修改时间
   */
  getFileModifiedTime(filePath) {
    try {
      const stats = fs.statSync(filePath);
      return stats.mtime.toISOString();
    } catch (e) {
      return null;
    }
  }

  /**
   * 检测需要同步的 skills
   */
  detectNeedSync() {
    console.log('\n🔍 检测需要同步的 skills...\n');
    
    this.toSync = [];
    
    this.skills.forEach(skill => {
      // 只同步有独立仓库的 skills
      if (!skill.hasOwnRepo) {
        console.log(`   ⏭️  ${skill.name} - 跳过（无独立仓库）`);
        return;
      }
      
      const status = this.status.skills[skill.name];
      const needSync = this.checkIfNeedSync(skill, status);
      
      if (needSync) {
        this.toSync.push(skill);
        console.log(`   ✅ ${skill.name} - 需要同步`);
        console.log(`      原因: ${needSync}`);
      } else {
        console.log(`   ⏭️  ${skill.name} - 无需同步`);
      }
    });

    console.log(`\n📊 总计: ${this.toSync.length} 个 skills 需要同步\n`);
    
    return this.toSync.length > 0;
  }

  /**
   * 检查是否需要同步
   */
  checkIfNeedSync(skill, status) {
    // 从未同步
    if (!status) {
      return '从未同步';
    }
    
    // 文件已修改
    const lastModified = skill.modified;
    const lastSynced = status.lastSynced;
    
    if (lastModified && lastSynced && new Date(lastModified) > new Date(lastSynced)) {
      return '文件已修改';
    }
    
    // 同步失败
    if (status.status === 'failed') {
      return '上次同步失败';
    }
    
    // 未推送到 GitHub
    if (!status.gitCommit) {
      return '未推送到 GitHub';
    }
    
    return null;
  }

  /**
   * 发布到 ClawHub（单个 skill）
   */
  async publishToClawHub(skill) {
    console.log(`\n📤 发布 ${skill.name} 到 ClawHub...`);
    
    try {
      const cmd = `cd "${skill.path}" && clawhub publish . --slug ${skill.name} --name "${skill.metadata.name || skill.name}" --version ${skill.metadata.version || '1.0.0'}`;
      
      const result = execSync(cmd, { 
        encoding: 'utf8',
        timeout: 60000 
      });
      
      // 提取 ClawHub ID
      const idMatch = result.match(/Published .+? \((.+?)\)/);
      const clawhubId = idMatch ? idMatch[1] : null;
      
      if (clawhubId) {
        console.log(`   ✅ 发布成功！ID: ${clawhubId}`);
        return { success: true, clawhubId };
      } else {
        console.log(`   ⚠️  发布成功，但无法提取 ID`);
        return { success: true, clawhubId: null };
      }
    } catch (e) {
      console.error(`   ❌ 发布失败:`, e.message);
      return { success: false, error: e.message };
    }
  }

  /**
   * 提交到 GitHub（单个 skill，独立仓库）
   */
  async commitToGitHub(skill) {
    console.log(`\n📦 提交 ${skill.name} 到 GitHub...`);
    
    try {
      // 1. Git add
      console.log(`   1️⃣  Git add...`);
      const addCmd = `cd "${skill.path}" && git add .`;
      execSync(addCmd, { encoding: 'utf8' });
      
      // 2. Git commit
      console.log(`   2️⃣  Git commit...`);
      const version = skill.metadata.version || 'unknown';
      const commitMsg = `Update ${skill.name} to v${version}`;
      const commitCmd = `cd "${skill.path}" && git commit -m "${commitMsg}"`;
      
      try {
        const commitResult = execSync(commitCmd, { encoding: 'utf8' });
        
        // 提取 commit hash
        const hashMatch = commitResult.match(/\[main ([a-f0-9]+)\]/);
        const commitHash = hashMatch ? hashMatch[1] : null;
        
        if (commitHash) {
          console.log(`   ✅ 提交成功！Commit: ${commitHash}`);
        } else {
          console.log(`   ✅ 提交成功！`);
        }
        
        // 3. Git push
        console.log(`   3️⃣  Git push...`);
        const pushCmd = `cd "${skill.path}" && git push origin main`;
        execSync(pushCmd, { encoding: 'utf8' });
        console.log(`   ✅ 推送成功！`);
        
        return { success: true, commitHash };
        
      } catch (e) {
        if (e.message.includes('nothing to commit')) {
          console.log(`   ⏭️  没有需要提交的更改`);
          return { success: true, commitHash: null, noChanges: true };
        }
        throw e;
      }
    } catch (e) {
      console.error(`   ❌ 提交失败:`, e.message);
      return { success: false, error: e.message };
    }
  }

  /**
   * 更新同步状态
   */
  updateStatus(skill, clawhubResult, gitResult) {
    const now = new Date().toISOString();
    
    this.status.skills[skill.name] = {
      version: skill.metadata.version || 'unknown',
      clawhubId: clawhubResult.clawhubId || this.status.skills[skill.name]?.clawhubId,
      clawhubPublished: clawhubResult.success ? now : this.status.skills[skill.name]?.clawhubPublished,
      gitCommit: gitResult.commitHash || this.status.skills[skill.name]?.gitCommit,
      gitPushed: gitResult.success ? now : this.status.skills[skill.name]?.gitPushed,
      lastSynced: now,
      status: (clawhubResult.success || clawhubResult.success === undefined) && gitResult.success ? 'synced' : 'failed'
    };
    
    this.saveStatus();
  }

  /**
   * 记录日志
   */
  logSync(skill, clawhubResult, gitResult) {
    this.log.entries.push({
      timestamp: new Date().toISOString(),
      skill: skill.name,
      version: skill.metadata.version || 'unknown',
      clawhub: {
        success: clawhubResult.success || false,
        id: clawhubResult.clawhubId,
        error: clawhubResult.error
      },
      git: {
        success: gitResult.success,
        commit: gitResult.commitHash,
        error: gitResult.error
      }
    });
    
    this.saveLog();
  }

  /**
   * 生成同步报告
   */
  generateReport() {
    const timestamp = new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
    
    let report = `# 🔄 Skill Sync 同步报告\n\n`;
    report += `**时间**: ${timestamp}\n`;
    report += `**检查数量**: ${this.skills.length} 个 skills\n`;
    report += `**独立仓库**: ${this.skills.filter(s => s.hasOwnRepo).length} 个\n`;
    report += `**需要同步**: ${this.toSync.length} 个\n\n`;
    
    if (this.toSync.length === 0) {
      report += `✅ **所有 skills 已是最新状态！**\n`;
    } else {
      report += `## 📊 同步结果\n\n`;
      
      this.toSync.forEach(skill => {
        const status = this.status.skills[skill.name];
        const clawhubIcon = status?.clawhubId ? '✅' : '❌';
        const gitIcon = status?.gitCommit ? '✅' : '❌';
        
        report += `### ${skill.name} (v${skill.metadata.version || 'unknown'})\n`;
        report += `- **ClawHub**: ${clawhubIcon} ${status?.clawhubId ? `已发布 (${status.clawhubId})` : '未发布'}\n`;
        report += `- **GitHub**: ${gitIcon} ${status?.gitCommit ? `已提交 (${status.gitCommit})` : '未提交'}\n`;
        report += `- **状态**: ${status?.status || 'unknown'}\n\n`;
      });
    }
    
    return report;
  }

  /**
   * 主运行函数
   */
  async run() {
    console.log('🚀 Skill Sync 启动\n');
    console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');

    // 1. 扫描 skills
    this.scanSkills();

    // 2. 检测需要同步的 skills
    const hasSkillsToSync = this.detectNeedSync();

    if (!hasSkillsToSync) {
      console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
      console.log('✅ Skill Sync 完成\n');
      return;
    }

    // 3. 同步每个 skill
    console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
    console.log('🔄 开始同步...\n');

    for (const skill of this.toSync) {
      console.log(`\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
      console.log(`📝 处理: ${skill.name}\n`);

      // 发布到 ClawHub
      const clawhubResult = await this.publishToClawHub(skill);

      // 提交到 GitHub
      const gitResult = await this.commitToGitHub(skill);

      // 更新状态
      this.updateStatus(skill, clawhubResult, gitResult);

      // 记录日志
      this.logSync(skill, clawhubResult, gitResult);
    }

    // 4. 生成报告
    console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
    const report = this.generateReport();
    console.log('\n' + report);

    console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
    console.log('✅ Skill Sync 完成\n');
  }
}

// 运行
if (require.main === module) {
  const sync = new SkillSync();
  sync.run().catch(e => {
    console.error('❌ 运行失败:', e);
    process.exit(1);
  });
}

module.exports = SkillSync;


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