Skill: antonia-sz/github-analyzer (version 1.0.0)

--- name: github-analyzer displayName: GitHub 项目分析助手 version: 1.0.0 description: > 输入项目想法或 GitHub 链接，自动搜索相关开源项目，生成结构化分析报告（技术栈/优缺点/评分）， 并可下载评分最高的前3名代码包。支持意图搜索和直链分析两种模式。 author: antonia-sz tags: [github, analysis, developer-tools, open-source, research] --- # GitHub 项目分析助手 🔍 ## 你能做什么 **模式一：意图搜索** > "我想做一个 XXX 项目，帮我找找 GitHub 上有没有相关开源项目" **模式二：直链分析** > "帮我分析这几个项目：https://github.com/xxx/yyy https://github.com/aaa/bbb" **模式三：对比分析** > "帮我对比这几个项目，哪个更适合我的需求" --- ## 工作流程 ### 模式一：意图搜索模式 1. 解析用户描述，提取 2-4 个核心关键词 2. 调用 GitHub Search API 搜索相关仓库（按 stars 降序，取 Top 10） 3. 过滤：排除 fork、归档、1年内未更新、stars < 50 的项目 4. 对每个项目调用 GitHub API 获取详情 5. AI 分析生成报告 6. 询问是否需要下载代码包 ### 模式二：直链分析模式 1. 提取 URL 中的 owner/repo 2. 调用 GitHub API 获取仓库详情、README、语言统计 3. AI 分析生成报告 4. 询问是否需要下载代码包 --- ## 报告格式 每个项目输出： ``` ## [项目名](链接) > 一句话描述 | 维度 | 详情 | |------|------| | ⭐ Stars | 12,345 | | 🍴 Forks | 1,234 | | 🔤 语言 | Python / TypeScript | | 📅 最近更新 | 2024-01-15 | | 📜 License | MIT | ### 核心功能 - 功能点1 - 功能点2 - 功能点3 ### 优点 ✅ - ... ### 缺点 / 注意事项 ⚠️ - ... ### 适用场景 ... ### 综合评分：8.5 / 10 评分依据：活跃度高（★★★★）、文档完善（★★★★）、社区活跃（★★★）、上手难度低（★★★★） ``` 多个项目后附对比表格： ``` | 项目 | Stars | 语言 | 活跃度 | 文档 | 上手难度 | 综合分 | |------|-------|------|--------|------|---------|--------| ``` --- ## 下载功能 分析完成后询问用户是否下载： - "需要下载评分最高的前3名代码包吗？" - 用户确认后，执行 `python3 SKILL_DIR/scripts/download_repos.py <repo1> <repo2> <repo3>` - 下载到 `~/Downloads/github-analyzer/` 目录 - 打包为 zip，告知文件路径 --- ## 工具调用 ```python # 搜索 GitHub exec: python3 SKILL_DIR/scripts/search_github.py "<query>" [--limit 10] # 分析单个仓库 exec: python3 SKILL_DIR/scripts/analyze_repo.py "<owner/repo>" # 批量下载 exec: python3 SKILL_DIR/scripts/download_repos.py "<owner/repo1>" "<owner/repo2>" ... ``` --- ## 注意事项 - GitHub API 未认证时限速 60次/小时，认证后 5000次/小时 - 如有 `GITHUB_TOKEN` 环境变量则自动使用 - README 超长时只取前 3000 字符分析 - 项目极少时（<3个）告知用户并说明可能原因

path: README.md
content: # GitHub 项目分析助手

> 输入项目想法或 GitHub 链接，自动分析相关开源项目，生成结构化报告，支持代码包下载。

## ✨ 功能

- **意图搜索**：描述你的想法 → 自动搜索相关 GitHub 项目
- **直链分析**：粘贴 GitHub URL → 深度分析项目
- **结构化报告**：技术栈 / 优缺点 / 适用场景 / 综合评分
- **横向对比**：多项目对比表格
- **代码下载**：一键下载评分最高的前3名代码包

## 🚀 使用方式

### 作为 OpenClaw Skill 使用

安装到 OpenClaw 后，直接对话：

```
我想做一个在线文档协作工具，帮我找找 GitHub 上有没有相关开源项目
```

```
帮我分析这个项目：https://github.com/facebook/react
```

### 作为 CLI 工具使用

```bash
# 搜索相关项目
python3 scripts/search_github.py "online document collaboration" --limit 10

# 分析指定仓库
python3 scripts/analyze_repo.py "microsoft/vscode"
python3 scripts/analyze_repo.py "https://github.com/facebook/react" "https://github.com/vuejs/vue"

# 下载代码包
python3 scripts/download_repos.py "microsoft/vscode" "facebook/react"
```

## 📦 环境要求

- Python 3.8+
- 无需安装额外依赖（只用标准库）
- 可选：设置 `GITHUB_TOKEN` 环境变量以提高 API 限速（5000次/小时 vs 60次/小时）

```bash
export GITHUB_TOKEN=your_token_here
```

## 📊 报告示例

```
## microsoft/vscode

> Visual Studio Code

| 维度 | 详情 |
|------|------|
| ⭐ Stars | 158,000 |
| 🍴 Forks | 27,000 |
| 🔤 语言 | TypeScript |
| 📅 最近更新 | 2024-01-20 |

### 核心功能
- 跨平台代码编辑器
- 插件生态系统
- 内置 Git 支持
- 调试功能

### 优点 ✅
- 生态极其丰富，插件数量超10万
- 微软持续维护，更新频繁
- 开源免费，MIT 协议

### 缺点 ⚠️
- 内存占用较高
- 对大文件处理性能一般

### 综合评分：9.5 / 10
```

## 📝 作者

[antonia-sz](https://github.com/antonia-sz) · Powered by OpenClaw

## 📄 License

MIT


path: scripts/analyze_repo.py
content: #!/usr/bin/env python3
"""
分析单个 GitHub 仓库
用法: python3 analyze_repo.py "owner/repo"
"""
import sys
import json
import os
import urllib.request
import urllib.parse

[REDACTED_SECRET]"GITHUB_TOKEN", "")
HEADERS = {
    "Authorization": f"token {TOKEN}",
    "Accept": "application/vnd.github.v3+json",
    "User-Agent": "github-analyzer/1.0"
}

def gh_get(url):
    req = urllib.request.Request(url, headers=HEADERS)
    try:
        with urllib.request.urlopen(req, timeout=15) as r:
            return json.loads(r.read())
    except urllib.error.HTTPError as e:
        return {"error": str(e), "url": url}

def get_readme(owner, repo):
    url = f"https://api.github.com/repos/{owner}/{repo}/readme"
    data = gh_get(url)
    if "error" in data:
        return ""
    import base64
    content = data.get("content", "")
    try:
        decoded = base64.b64decode(content).decode("utf-8", errors="ignore")
        # 只取前 3000 字符
        return decoded[:3000]
    except Exception:
        return ""

def get_languages(owner, repo):
    url = f"https://api.github.com/repos/{owner}/{repo}/languages"
    data = gh_get(url)
    if "error" in data:
        return {}
    return data

def get_contributors_count(owner, repo):
    url = f"https://api.github.com/repos/{owner}/{repo}/contributors?per_page=1&anon=true"
    req = urllib.request.Request(url, headers=HEADERS)
    try:
        with urllib.request.urlopen(req, timeout=10) as r:
            # 从 Link header 获取总页数
            link = r.getheader("Link", "")
            if 'rel="last"' in link:
                import re
                m = re.search(r'page=(\d+)>; rel="last"', link)
                if m:
                    return int(m.group(1))
            items = json.loads(r.read())
            return len(items)
    except Exception:
        return 0

def analyze_repo(full_name: str):
    owner, repo = full_name.strip("/").split("/")[-2:]

    # 基础信息
    repo_data = gh_get(f"https://api.github.com/repos/{owner}/{repo}")
    if "error" in repo_data:
        return {"error": f"仓库不存在或无法访问: {full_name}"}

    # README
    readme = get_readme(owner, repo)

    # 语言统计
    languages = get_languages(owner, repo)
    lang_list = sorted(languages.items(), key=lambda x: -x[1])
    top_langs = [l[0] for l in lang_list[:5]]

    # 贡献者数量
    contributors = get_contributors_count(owner, repo)

    result = {
        "full_name": repo_data["full_name"],
        "url": repo_data["html_url"],
        "description": repo_data.get("description") or "",
        "stars": repo_data["stargazers_count"],
        "forks": repo_data["forks_count"],
        "watchers": repo_data["watchers_count"],
        "open_issues": repo_data["open_issues_count"],
        "language": repo_data.get("language") or "Unknown",
        "languages": top_langs,
        "topics": repo_data.get("topics", []),
        "license": (repo_data.get("license") or {}).get("spdx_id", "Unknown"),
        "created_at": repo_data["created_at"][:10],
        "updated_at": repo_data["updated_at"][:10],
        "pushed_at": repo_data["pushed_at"][:10],
        "size_kb": repo_data.get("size", 0),
        "default_branch": repo_data.get("default_branch", "main"),
        "homepage": repo_data.get("homepage") or "",
        "has_wiki": repo_data.get("has_wiki", False),
        "has_pages": repo_data.get("has_pages", False),
        "contributors_approx": contributors,
        "readme_excerpt": readme,
        "clone_url": repo_data["clone_url"],
        "zip_url": f"https://github.com/{owner}/{repo}/archive/refs/heads/{repo_data.get('default_branch','main')}.zip",
    }

    return result

def main():
    if len(sys.argv) < 2:
        print("用法: python3 analyze_repo.py owner/repo [owner/repo ...]")
        sys.exit(1)

    results = []
    for arg in sys.argv[1:]:
        # 支持完整 URL 或 owner/repo
        if "github.com" in arg:
            parts = arg.rstrip("/").split("github.com/")[-1].split("/")
            full_name = "/".join(parts[:2])
        else:
            full_name = arg
        results.append(analyze_repo(full_name))

    if len(results) == 1:
        print(json.dumps(results[0], ensure_ascii=False, indent=2))
    else:
        print(json.dumps(results, ensure_ascii=False, indent=2))

if __name__ == "__main__":
    main()


path: scripts/download_repos.py
content: #!/usr/bin/env python3
"""
下载 GitHub 仓库代码包（zip）
用法: python3 download_repos.py "owner/repo1" "owner/repo2" ...
"""
import sys
import os
import urllib.request
import zipfile
import json

[REDACTED_SECRET]"GITHUB_TOKEN", "")
DOWNLOAD_DIR = os.path.expanduser("~/Downloads/github-analyzer")

def download_repo(full_name: str, out_dir: str):
    owner, repo = full_name.strip("/").split("/")[-2:]

    # 获取默认分支
    headers = {
        "Authorization": f"token {TOKEN}",
        "User-Agent": "github-analyzer/1.0"
    }
    req = urllib.request.Request(
        f"https://api.github.com/repos/{owner}/{repo}",
        headers=headers
    )
    with urllib.request.urlopen(req, timeout=15) as r:
        repo_data = json.loads(r.read())
    branch = repo_data.get("default_branch", "main")

    zip_url = f"https://github.com/{owner}/{repo}/archive/refs/heads/{branch}.zip"
    out_path = os.path.join(out_dir, f"{owner}_{repo}.zip")

    print(f"⬇️  下载 {full_name} ...")
    req = urllib.request.Request(zip_url, headers=headers)
    with urllib.request.urlopen(req, timeout=60) as r:
        with open(out_path, "wb") as f:
            f.write(r.read())

    size_mb = os.path.getsize(out_path) / 1024 / 1024
    print(f"✅ 已保存: {out_path} ({size_mb:.1f} MB)")
    return out_path

def main():
    if len(sys.argv) < 2:
        print("用法: python3 download_repos.py owner/repo [owner/repo ...]")
        sys.exit(1)

    os.makedirs(DOWNLOAD_DIR, exist_ok=True)
    results = []

    for arg in sys.argv[1:]:
        if "github.com" in arg:
            parts = arg.rstrip("/").split("github.com/")[-1].split("/")
            full_name = "/".join(parts[:2])
        else:
            full_name = arg
        try:
            path = download_repo(full_name, DOWNLOAD_DIR)
            results.append({"repo": full_name, "path": path, "status": "ok"})
        except Exception as e:
            print(f"❌ 下载失败 {full_name}: {e}")
            results.append({"repo": full_name, "error": str(e), "status": "failed"})

    print(f"\n📦 下载完成，文件保存在: {DOWNLOAD_DIR}")
    for r in results:
        if r["status"] == "ok":
            print(f"  ✅ {r['repo']} → {r['path']}")
        else:
            print(f"  ❌ {r['repo']} → {r['error']}")

if __name__ == "__main__":
    main()


path: scripts/search_github.py
content: #!/usr/bin/env python3
"""
搜索 GitHub 仓库
用法: python3 search_github.py "关键词" [--limit 10]
"""
import sys
import json
import os
import urllib.request
import urllib.parse
import argparse

[REDACTED_SECRET]"GITHUB_TOKEN", "")
HEADERS = {
    "Authorization": f"token {TOKEN}",
    "Accept": "application/vnd.github.v3+json",
    "User-Agent": "github-analyzer/1.0"
}

def gh_get(url):
    req = urllib.request.Request(url, headers=HEADERS)
    with urllib.request.urlopen(req, timeout=15) as r:
        return json.loads(r.read())

def search_repos(query: str, limit: int = 10):
    encoded = urllib.parse.quote(query)
    url = f"https://api.github.com/search/repositories?q={encoded}&sort=stars&order=desc&per_page={min(limit * 2, 30)}"
    data = gh_get(url)
    items = data.get("items", [])

    results = []
    for repo in items:
        # 过滤
        if repo.get("fork"):
            continue
        if repo.get("archived"):
            continue
        if repo.get("stargazers_count", 0) < 50:
            continue

        results.append({
            "full_name": repo["full_name"],
            "url": repo["html_url"],
            "description": repo.get("description") or "",
            "stars": repo["stargazers_count"],
            "forks": repo["forks_count"],
            "language": repo.get("language") or "Unknown",
            "updated_at": repo["updated_at"][:10],
            "topics": repo.get("topics", []),
            "license": (repo.get("license") or {}).get("spdx_id", "Unknown"),
            "open_issues": repo.get("open_issues_count", 0),
        })

        if len(results) >= limit:
            break

    return results

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("query", help="搜索关键词")
    parser.add_argument("--limit", type=int, default=10)
    args = parser.parse_args()

    results = search_repos(args.query, args.limit)
    print(json.dumps(results, ensure_ascii=False, indent=2))

if __name__ == "__main__":
    main()


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