# Skill Advisor

> Evaluate OpenClaw skills before installation. Use when user wants to check a skill's safety, dependencies, popularity, or get an installation recommendation. Generates a pre-install assessment report with security status, metrics, and usage guidance.

- Skill: `leoyeai-openclaw-master-skills/skill-advisor` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add leoyeai-openclaw-master-skills/skill-advisor`
- Raw SKILL.md: https://api.skillmd.com/api/skills/leoyeai-openclaw-master-skills/skill-advisor/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- License: MIT-0
- Author: LeoYeAI (https://skillmd.com/u/leoyeai-openclaw-master-skills)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/leoyeai-openclaw-master-skills/skill-advisor

---


# Skill Advisor

Pre-install assessment tool for OpenClaw skills. Generate comprehensive evaluation reports to help users decide whether to install a skill.

## Features

- 🔒 **Security Status**: Check ClawHub official scan results
- 📊 **Popularity Metrics**: Downloads, stars, install count
- 🔄 **Maintenance Status**: Last update time, activity level
- 📦 **Dependency Analysis**: Required tools, libraries, complexity
- 💰 **API Cost Assessment**: Free/paid API requirements
- 🎯 **Installation Recommendation**: Clear go/no-go guidance

## Trigger Conditions

- "Check this skill before installing" / "安装前检查这个skill"
- "Is {skill-name} safe?" / "{skill-name}安全吗"
- "Evaluate skill {skill-name}" / "评估skill {skill-name}"
- "What does {skill-name} need?" / "{skill-name}需要什么依赖"
- "Should I install {skill-name}?" / "要不要安装{skill-name}"
- "skill-advisor {skill-name}"
- "帮我看看这个skill怎么样"

---

## Language Support

This skill supports multiple languages. Output language automatically matches the user's conversation language with OpenClaw.

**Supported Languages:**
- 中文 (Chinese)
- English

**Detection:**
- If user writes in Chinese → Output Chinese report
- If user writes in English → Output English report
- Default: Match the user's input language

---

## Step 1: Get Skill Information

When user provides a skill name, fetch its metadata:

```bash
SKILL_NAME="user-provided-skill-name"

# Get skill metadata from ClawHub
echo "📡 Fetching skill information..."
clawhub inspect "$SKILL_NAME" 2>/dev/null

# Alternative: Use ClawHub API directly
curl -s "https://clawhub.ai/api/v1/skills/$SKILL_NAME" | python3 -m json.tool 2>/dev/null
```

---

## Step 2: Fetch SKILL.md Content

Get the actual SKILL.md to analyze dependencies and functionality:

```bash
# Fetch SKILL.md content
SKILL_CONTENT=$(curl -s "https://clawhub.ai/api/v1/skills/$SKILL_NAME" | python3 -c "
import sys, json
try:
    data = json.load(sys.stdin)
    if 'skill' in data and 'content' in data['skill']:
        print(data['skill']['content'])
    elif 'content' in data:
        print(data['content'])
    else:
        print('CONTENT_NOT_FOUND')
except:
    print('PARSE_ERROR')
" 2>/dev/null)

echo "$SKILL_CONTENT"
```

---

## Step 3: Analyze and Generate Report

Parse the skill data and generate the assessment report in the user's language:

```bash
python3 << 'PYEOF'
import json
import re
import sys
from datetime import datetime

# Detect user language from input (passed as argument)
USER_LANG = "en"  # Default to English, will be overridden by agent

# Simulated skill data (replace with actual API response)
skill_data = {
    "name": "SKILL_NAME_PLACEHOLDER",
    "description": "DESCRIPTION_PLACEHOLDER",
    "version": "1.0.0",
    "owner": "AUTHOR_PLACEHOLDER",
    "created_at": "2026-01-01T00:00:00Z",
    "updated_at": "2026-03-20T00:00:00Z",
    "downloads": 0,
    "stars": 0,
    "license": "MIT-0",
    "security_status": "unknown"
}

skill_content = """
SKILL_MD_CONTENT_PLACEHOLDER
"""

# Labels for different languages
LABELS = {
    "zh": {
        "conclusion_header": "🎯 结论",
        "recommend_install": "✅ 推荐安装",
        "caution_install": "⚠️ 可以安装，请注意事项",
        "not_recommend": "❌ 不建议安装",
        "score": "综合评分",
        "security_pass": "通过",
        "security_review": "需审查",
        "deps_light": "轻量",
        "deps_heavy": "较重",
        "report_title": "📋 Skill 评估报告",
        "metrics": "综合指标",
        "official_security": "🔒 官方安全",
        "benign": "✅ Benign（ClawHub扫描通过）",
        "suspicious": "⚠️ Suspicious（需要审查）",
        "security_unknown": "❓ 未知（未扫描）",
        "popularity": "📈 流行度",
        "downloads": "downloads",
        "stars": "stars",
        "maintenance": "🔄 维护状态",
        "active_maintain": "天前更新（活跃维护）",
        "normal_maintain": "天前更新（正常维护）",
        "less_maintain": "天前更新（维护较少）",
        "stop_maintain": "天前更新（可能停止维护）",
        "maintenance_unknown": "❓ 未知",
        "dep_burden": "📦 依赖负担",
        "no_dep": "无需额外依赖",
        "light_dep": "轻量",
        "heavy_dep": "较重",
        "deps_count": "个依赖",
        "api_cost": "💰 API成本",
        "no_api": "无需外部API",
        "api_required": "需要",
        "description": "📝 功能说明",
        "env_req": "⚙️ 环境要求",
        "cli_tools": "命令行工具",
        "python_pkg": "Python包",
        "node_pkg": "Node包",
        "api_key": "API密钥",
        "no_special_req": "无特殊要求",
        "usage_note": "⚠️ 使用注意",
        "data_upload": "📤 文档/数据会上传到",
        "external_service": "📤 会访问外部服务",
        "sensitive_data": "🔐 敏感数据请谨慎处理",
        "install_advice": "💡 安装建议",
        "safe_install": "可安全安装。",
        "caution_install_text": "可以安装，但请注意上述事项。",
        "not_install_text": "不建议安装，请寻找替代方案。",
        "need_config": "需要配置API Key",
        "meta_info": "ℹ️ 元信息",
        "author": "作者",
        "version": "版本",
        "license": "许可证",
        "score_time": "评分时间",
    },
    "en": {
        "conclusion_header": "🎯 Conclusion",
        "recommend_install": "✅ Recommended",
        "caution_install": "⚠️ Install with Caution",
        "not_recommend": "❌ Not Recommended",
        "score": "Overall Score",
        "security_pass": "Passed",
        "security_review": "Review Needed",
        "deps_light": "Light",
        "deps_heavy": "Heavy",
        "report_title": "📋 Skill Assessment Report",
        "metrics": "Metrics",
        "official_security": "🔒 Official Security",
        "benign": "✅ Benign (ClawHub scan passed)",
        "suspicious": "⚠️ Suspicious (needs review)",
        "security_unknown": "❓ Unknown (not scanned)",
        "popularity": "📈 Popularity",
        "downloads": "downloads",
        "stars": "stars",
        "maintenance": "🔄 Maintenance",
        "active_maintain": "days ago (actively maintained)",
        "normal_maintain": "days ago (normal maintenance)",
        "less_maintain": "days ago (less maintained)",
        "stop_maintain": "days ago (possibly abandoned)",
        "maintenance_unknown": "❓ Unknown",
        "dep_burden": "📦 Dependencies",
        "no_dep": "No dependencies required",
        "light_dep": "Light",
        "heavy_dep": "Heavy",
        "deps_count": "dependencies",
        "api_cost": "💰 API Cost",
        "no_api": "No external API required",
        "api_required": "Requires",
        "description": "📝 Description",
        "env_req": "⚙️ Requirements",
        "cli_tools": "CLI Tools",
        "python_pkg": "Python Packages",
        "node_pkg": "Node Packages",
        "api_key": "API Keys",
        "no_special_req": "No special requirements",
        "usage_note": "⚠️ Usage Notes",
        "data_upload": "📤 Data uploads to",
        "external_service": "📤 Accesses external services",
        "sensitive_data": "🔐 Handle sensitive data with care",
        "install_advice": "💡 Installation Advice",
        "safe_install": "Safe to install.",
        "caution_install_text": "Can be installed, but please note the above.",
        "not_install_text": "Not recommended. Consider alternatives.",
        "need_config": "API Key configuration required",
        "meta_info": "ℹ️ Metadata",
        "author": "Author",
        "version": "Version",
        "license": "License",
        "score_time": "Scored at",
    }
}

def get_label(key, lang="en"):
    """Get localized label"""
    return LABELS.get(lang, LABELS["en"]).get(key, key)

def calculate_security_score(content, security_status):
    score = 100
    if security_status == "benign":
        return 100
    elif security_status == "suspicious":
        return 50
    
    dangerous_patterns = [
        (r'curl\s+-fsSL.*\|.*bash', 30),
        (r'sudo\s+(apt|yum|dnf)\s+install', 15),
        (r'eval\s*\(', 20),
        (r'exec\s*\(', 15),
    ]
    
    for pattern, penalty in dangerous_patterns:
        if re.search(pattern, content, re.IGNORECASE):
            score -= penalty
    
    return max(0, score)

def calculate_popularity_score(downloads, stars):
    score = 0
    if downloads >= 10000: score += 60
    elif downloads >= 1000: score += 50
    elif downloads >= 100: score += 40
    elif downloads >= 10: score += 30
    else: score += 20
    
    if stars >= 50: score += 40
    elif stars >= 10: score += 30
    elif stars >= 1: score += 20
    else: score += 10
    
    return score

def calculate_maintenance_score(updated_at):
    try:
        last_update = datetime.fromisoformat(updated_at.replace('Z', '+00:00'))
        now = datetime.now(last_update.tzinfo)
        days = (now - last_update).days
        
        if days <= 7: return 100
        elif days <= 30: return 80
        elif days <= 90: return 60
        else: return 40
    except:
        return 50

def extract_dependencies(content):
    deps = {"bins": [], "env_vars": [], "pip_packages": [], "npm_packages": [], "complexity": "unknown"}
    
    bins_match = re.search(r'"bins"\s*:\s*\[(.*?)\]', content)
    if bins_match:
        deps["bins"] = [b.strip().strip('"') for b in bins_match.group(1).split(',') if b.strip()]
    
    env_match = re.search(r'"env"\s*:\s*\[(.*?)\]', content)
    if env_match:
        deps["env_vars"] = [e.strip().strip('"') for e in env_match.group(1).split(',') if e.strip()]
    
    pip_patterns = re.findall(r'pip\s+install\s+([\w-]+)', content)
    deps["pip_packages"] = list(set(pip_patterns))
    
    npm_patterns = re.findall(r'npm\s+(?:install|i)\s+(?:-g\s+)?([\w-]+)', content)
    deps["npm_packages"] = list(set(npm_patterns))
    
    lines = len(content.split('\n'))
    if lines < 100: deps["complexity"] = "simple"
    elif lines < 250: deps["complexity"] = "medium"
    else: deps["complexity"] = "complex"
    
    return deps

def calculate_dependency_score(deps):
    score = 100
    bin_count = len(deps["bins"])
    if bin_count <= 2: score -= 10
    elif bin_count <= 4: score -= 20
    elif bin_count > 4: score -= 30
    
    pkg_count = len(deps["pip_packages"]) + len(deps["npm_packages"])
    if pkg_count > 5: score -= 20
    elif pkg_count > 0: score -= 10
    
    return max(0, score)

def calculate_api_cost_score(deps, content):
    if not deps["env_vars"]:
        return 100
    
    free_indicators = ["免费", "free", "免费额度", "new user", "free tier"]
    has_free = any(indicator in content.lower() for indicator in free_indicators)
    
    return 80 if has_free else 60

def generate_report(skill_data, skill_content, lang="en"):
    L = lambda k: get_label(k, lang)
    
    name = skill_data.get("name", "unknown")
    description = skill_data.get("description", "No description")
    version = skill_data.get("version", "?.?.?")
    owner = skill_data.get("owner", "unknown")
    downloads = skill_data.get("downloads", 0)
    stars = skill_data.get("stars", 0)
    updated_at = skill_data.get("updated_at", "")
    security_status = skill_data.get("security_status", "unknown")
    license_type = skill_data.get("license", "unknown")
    
    security_score = calculate_security_score(skill_content, security_status)
    popularity_score = calculate_popularity_score(downloads, stars)
    maintenance_score = calculate_maintenance_score(updated_at)
    deps = extract_dependencies(skill_content)
    dependency_score = calculate_dependency_score(deps)
    api_cost_score = calculate_api_cost_score(deps, skill_content)
    
    total_score = (
        security_score * 0.30 +
        popularity_score * 0.25 +
        maintenance_score * 0.20 +
        dependency_score * 0.15 +
        api_cost_score * 0.10
    )
    
    if total_score >= 80:
        grade = "A"
        recommendation = L("recommend_install")
        rec_icon = "✅"
    elif total_score >= 60:
        grade = "B"
        recommendation = L("caution_install")
        rec_icon = "⚠️"
    else:
        grade = "C"
        recommendation = L("not_recommend")
        rec_icon = "❌"
    
    if security_status == "benign":
        security_text = L("benign")
    elif security_status == "suspicious":
        security_text = L("suspicious")
    else:
        security_text = L("security_unknown")
    
    try:
        last_update = datetime.fromisoformat(updated_at.replace('Z', '+00:00'))
        now = datetime.now(last_update.tzinfo)
        days = (now - last_update).days
        if days <= 7:
            maintenance_text = f"✅ {days}{L('active_maintain')}"
        elif days <= 30:
            maintenance_text = f"✅ {days}{L('normal_maintain')}"
        elif days <= 90:
            maintenance_text = f"⚠️ {days}{L('less_maintain')}"
        else:
            maintenance_text = f"⚠️ {days}{L('stop_maintain')}"
    except:
        maintenance_text = L("maintenance_unknown")
    
    all_deps = deps["bins"] + deps["pip_packages"] + deps["npm_packages"]
    if not all_deps:
        deps_text = L("no_dep")
    elif len(all_deps) <= 3:
        deps_text = f"{L('light_dep')}（{', '.join(all_deps)}）"
    else:
        deps_text = f"{L('heavy_dep')}（{len(all_deps)}{L('deps_count')}）"
    
    if not deps["env_vars"]:
        api_text = L("no_api")
    else:
        api_text = f"{L('api_required')} {', '.join(deps['env_vars'])}"
    
    data_warnings = []
    if "api.siliconflow.cn" in skill_content or "siliconflow" in skill_content.lower():
        data_warnings.append(f"{L('data_upload')} api.siliconflow.cn")
    if "curl" in skill_content and "https://" in skill_content:
        urls = re.findall(r'https?://[^\s"'\)]+', skill_content)
        external_domains = set()
        for url in urls:
            if "clawhub" not in url and "github" not in url and "openclaw" not in url:
                domain = url.split("//")[1].split("/")[0]
                external_domains.add(domain)
        if external_domains:
            data_warnings.append(f"{L('external_service')}：{', '.join(list(external_domains)[:3])}")
    
    report = f"""
┌─────────────────────────────────────────────────────────┐
│  {L('conclusion_header')}：{rec_icon} {recommendation.ljust(30)}│
│  {L('score')}：{total_score:.0f}/100 ({grade}级) | {L('official_security').replace('🔒 ', '')}：{L('security_pass') if security_score >= 80 else L('security_review')} | {L('dep_burden').replace('📦 ', '')}：{L('deps_light') if dependency_score >= 80 else L('deps_heavy')}           │
└─────────────────────────────────────────────────────────┘

━━━ {L('report_title')}：{name} ━━━━━━━━━━━━━━━━━━━━━━━━━

📊 {L('metrics')}
├─ {L('official_security')}：{security_text}
├─ {L('popularity')}：⭐ {downloads} {L('downloads')} | ⭐ {stars} {L('stars')}
├─ {L('maintenance')}：{maintenance_text}
├─ {L('dep_burden')}：{deps_text}
└─ {L('api_cost')}：{api_text}

{L('description')}
{description[:150]}{'...' if len(description) > 150 else ''}

{L('env_req')}"""

    if deps["bins"]:
        report += f"\n├─ {L('cli_tools')}：{', '.join(deps['bins'])}"
    if deps["pip_packages"]:
        report += f"\n├─ {L('python_pkg')}：{', '.join(deps['pip_packages'])}"
    if deps["npm_packages"]:
        report += f"\n├─ {L('node_pkg')}：{', '.join(deps['npm_packages'])}"
    if deps["env_vars"]:
        report += f"\n└─ {L('api_key')}：{', '.join(deps['env_vars'])}"
    elif not deps["bins"] and not deps["pip_packages"] and not deps["npm_packages"]:
        report += f"\n└─ {L('no_special_req')}"

    if data_warnings:
        report += f"\n\n{L('usage_note')}"
        for warning in data_warnings:
            report += f"\n├─ {warning}"
        report += f"\n└─ {L('sensitive_data')}"

    report += f"\n\n{L('install_advice')}"
    if total_score >= 80:
        report += f"\n{L('safe_install')}"
    elif total_score >= 60:
        report += f"\n{L('caution_install_text')}"
    else:
        report += f"\n{L('not_install_text')}"
    
    if deps["env_vars"]:
        report += f"\n{L('need_config')}：{', '.join(deps['env_vars'])}"
    
    report += f"""
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

{L('meta_info')}
├─ {L('author')}：{owner}
├─ {L('version')}：{version}
├─ {L('license')}：{license_type}
└─ {L('score_time')}：{datetime.now().strftime('%Y-%m-%d %H:%M')}
"""
    
    return report

# Example usage - English
print("=== English Report ===")
print(generate_report(skill_data, skill_content, "en"))

print("\n\n=== 中文报告 ===")
print(generate_report(skill_data, skill_content, "zh"))
PYEOF
```

---

## Step 4: Output Report

The report is displayed directly to the user. No files are created.

```
🎯 结论：✅ 推荐安装
综合评分：85/100 (A级) | 安全：通过 | 依赖：轻量

━━━ 📋 Skill 评估报告：{skill-name} ━━━━━━━━━━━━━━━━━
...
```

---

## Usage Flow

```
User (Chinese): "帮我看看 china-doc-ocr 这个skill怎么样"

Agent:
1. Detect user language → Chinese
2. Run: clawhub inspect china-doc-ocr
3. Fetch SKILL.md content from ClawHub API
4. Parse and analyze
5. Generate report in Chinese
6. Display report to user

---

User (English): "Is skill-xyz safe to install?"

Agent:
1. Detect user language → English
2. Run: clawhub inspect skill-xyz
3. Fetch SKILL.md content from ClawHub API
4. Parse and analyze
5. Generate report in English
6. Display report to user
```

---

## Error Handling

```
Skill not found        → "❌ 未找到该skill，请检查名称"
ClawHub API error      → "❌ 无法连接ClawHub，请稍后重试"
Parse error            → "⚠️ 部分信息解析失败，报告可能不完整"
```

---

## Notes

- No files are generated (pure text output)
- Report shows conclusion first, then details
- Security status uses ClawHub's official scan results
- Scores are calculated dynamically based on multiple factors
- All data fetched in real-time from ClawHub

