Back to skills
SkillHub ClubShip Full StackFull Stack

github-topics

Fetches GitHub topic trending repositories. Use when asking about GitHub trending repos or open source projects.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
3,117
Hot score
99
Updated
March 20, 2026
Overall rating
C4.0
Composite score
4.0
Best-practice grade
B84.0

Install command

npx @skill-hub/cli install openclaw-skills-github-topics

Repository

openclaw/skills

Skill path: skills/hjw21century/github-topics

Fetches GitHub topic trending repositories. Use when asking about GitHub trending repos or open source projects.

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: openclaw.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install github-topics into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/openclaw/skills before adding github-topics to shared team environments
  • Use github-topics for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: github-topics
description: Fetches GitHub topic trending repositories. Use when asking about GitHub trending repos or open source projects.
---

# GitHub Topics Trending

Fetch GitHub topic trending repositories and README summaries.

## Quick Start

```
# View rankings
今天 claude-code 话题排行榜
Top 10 GitHub 项目
热门仓库

# View repository details
anthropics/claude-code 介绍
这个仓库是做什么的
```

## Query Types

| Type | Examples | Description |
|------|----------|-------------|
| Rankings | `热门仓库` `Top 10` | Current rankings by stars |
| Detail | `xxx/xxx 介绍` | Repository README summary |
| Topic | `python 话题排行榜` | Custom topic search |

## Workflow

```
- [ ] Step 1: Parse query type
- [ ] Step 2: Fetch data from GitHub
- [ ] Step 3: Format and display results
```

---

## Step 1: Parse Query Type

| User Input | Query Type | Action |
|------------|------------|--------|
| `热门仓库` | rankings | Show top N repos |
| `Top 10 项目` | rankings | Show top N repos |
| `xxx/xxx 介绍` | detail | Get README summary |
| `python 话题` | rankings | Search python topic |

---

## Step 2: Fetch Data

### Fetch Rankings

```bash
cd skills/github-topics
python src/github_fetcher.py
```

**Requirements**:
```bash
pip install requests
```

### Fetch README (Optional)

```bash
python src/readme_fetcher.py
```

---

## Step 3: Format Results

### Rankings Output

```markdown
# GitHub Trending - python

| # | Repository | Stars | Language |
|---|------------|-------|----------|
| 1 | donnemartin/system-design-primer | 334K | Python |
| 2 | vinta/awesome-python | 281K | Python |
| 3 | project-based-learning | 257K | - |
```

### Detail Output

```markdown
# anthropics/claude-code

**Stars**: 15.2K
**Language**: TypeScript
**URL**: https://github.com/anthropics/claude-code

## README Summary
Official Claude Code CLI for AI-powered software development. Claude Code is Anthropic's official CLI tool...
```

---

## Configuration

| Variable | Description | Default |
|----------|-------------|---------|
| `GH_TOKEN` | GitHub Personal Access Token (optional, for higher rate limits) | - |
| `TOPIC` | GitHub topic to track | `claude-code` |

**Note**: `GH_TOKEN` is optional but recommended:
- With token: 5,000 requests/hour
- Without token: 60 requests/hour

Create token at: https://github.com/settings/tokens

---

## GitHub API Notes

| Limit Type | Rate |
|------------|------|
| Authenticated | 5,000 requests/hour |
| Unauthenticated | 60 requests/hour |

**Recommendation**: Use `GH_TOKEN` for higher rate limits.

---

## Troubleshooting

| Issue | Solution |
|-------|----------|
| Rate limit | Set `GH_TOKEN` env var |
| Network timeout | Check internet connection |
| Empty results | Check topic name exists |

---

## CLI Reference

```bash
# Fetch rankings (default topic: claude-code)
python skills/github-topics/src/github_fetcher.py

# Fetch README
python skills/github-topics/src/readme_fetcher.py
```


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### src/github_fetcher.py

```python
"""
GitHub Fetcher - 从 GitHub API 获取仓库数据
使用 GitHub Search API 按话题获取仓库
"""
import time
import requests
from typing import Dict, List, Optional
from datetime import datetime, timedelta

from src.config import (
    GITHUB_TOKEN, TOPIC, GITHUB_API_BASE,
    GITHUB_PER_PAGE, GITHUB_MAX_PAGES, GITHUB_SEARCH_SORT,
    GITHUB_SEARCH_ORDER, FETCH_REQUEST_DELAY
)


class GitHubFetcher:
    """从 GitHub API 获取仓库数据"""

    def __init__(self, token: str = None, topic: str = None):
        """
        初始化

        Args:
            token: GitHub Personal Access Token
            topic: 要搜索的 GitHub Topic
        """
        self.token = token or GITHUB_TOKEN
        self.topic = topic or TOPIC
        self.api_base = GITHUB_API_BASE
        self.per_page = GITHUB_PER_PAGE
        self.max_pages = GITHUB_MAX_PAGES
        self.delay = FETCH_REQUEST_DELAY

        self.session = requests.Session()
        self.session.headers.update({
            "Accept": "application/vnd.github.v3+json",
            "User-Agent": "GitHub-Topics-Trending/1.0"
        })

        if self.token:
            self.session.headers.update({
                "Authorization": f"Bearer {self.token}"
            })

        self.rate_limit_remaining = 5000
        self.rate_limit_reset = None

    def fetch(self, sort_by: str = None, limit: int = None) -> List[Dict]:
        """
        获取指定话题下的仓库列表

        Args:
            sort_by: 排序方式 (stars, forks, updated)
            limit: 最大返回数量

        Returns:
            [
                {
                    "rank": 1,
                    "repo_name": "owner/repo",
                    "owner": "owner",
                    "stars": 1000,
                    "forks": 100,
                    "issues": 10,
                    "language": "Python",
                    "url": "https://github.com/owner/repo",
                    "description": "...",
                    "topics": ["topic1", "topic2"],
                    "created_at": "2024-01-01T00:00:00Z",
                    "updated_at": "2024-01-01T00:00:00Z"
                },
                ...
            ]
        """
        sort_by = sort_by or GITHUB_SEARCH_SORT
        limit = limit or (self.per_page * self.max_pages)

        print(f"📡 正在获取话题 '{self.topic}' 的仓库列表...")
        print(f"   排序方式: {sort_by}")

        repos = []
        page = 1

        while page <= self.max_pages and len(repos) < limit:
            # 检查速率限制
            if self.rate_limit_remaining < 10:
                self._wait_for_rate_limit()

            data = self._fetch_page(page, sort_by)

            if not data or "items" not in data:
                break

            items = data["items"]
            if not items:
                break

            for item in items:
                repo = self._parse_repo_item(item, len(repos) + 1)
                repos.append(repo)

                if len(repos) >= limit:
                    break

            # 更新速率限制信息
            self._update_rate_limit(data)

            print(f"   第 {page} 页: 获取 {len(items)} 个仓库 (累计 {len(repos)})")

            # 如果返回数量少于 per_page,说明已经到最后一页
            if len(items) < self.per_page:
                break

            page += 1

            # 请求间隔
            if page <= self.max_pages and len(repos) < limit:
                time.sleep(self.delay)

        print(f"✅ 成功获取 {len(repos)} 个仓库")
        return repos

    def _fetch_page(self, page: int, sort_by: str) -> Optional[Dict]:
        """
        获取单页数据

        Args:
            page: 页码
            sort_by: 排序方式

        Returns:
            API 响应数据
        """
        query = f"topic:{self.topic}"
        url = f"{self.api_base}/search/repositories"

        params = {
            "q": query,
            "sort": sort_by,
            "order": GITHUB_SEARCH_ORDER,
            "per_page": self.per_page,
            "page": page
        }

        try:
            response = self.session.get(url, params=params, timeout=30)
            response.raise_for_status()
            return response.json()

        except requests.RequestException as e:
            print(f"   ⚠️ 请求失败 (页 {page}): {e}")
            return None

    def _parse_repo_item(self, item: Dict, rank: int) -> Dict:
        """
        解析仓库数据

        Args:
            item: GitHub API 返回的仓库项
            rank: 排名

        Returns:
            仓库信息字典
        """
        owner_data = item.get("owner") or {}
        owner = owner_data.get("login", "")
        name = item.get("name", "")
        repo_name = f"{owner}/{name}"

        return {
            "rank": rank,
            "repo_name": repo_name,
            "owner": owner,
            "name": name,
            "stars": item.get("stargazers_count", 0),
            "forks": item.get("forks_count", 0),
            "issues": item.get("open_issues_count", 0),
            "language": item.get("language", ""),
            "url": item.get("html_url", ""),
            "description": item.get("description", ""),
            "topics": item.get("topics", []),
            "created_at": item.get("created_at", ""),
            "updated_at": item.get("updated_at", ""),
            "pushed_at": item.get("pushed_at", ""),
            "homepage": item.get("homepage", ""),
            "archived": item.get("archived", False),
        }

    def _update_rate_limit(self, response_data: Dict):
        """
        更新速率限制信息

        Args:
            response_data: API 响应数据
        """
        # 注意:这些信息在实际请求中从响应头获取
        # 这里只是一个简化版本
        pass

    def _wait_for_rate_limit(self):
        """等待速率限制重置"""
        if self.rate_limit_reset:
            now = int(time.time())
            wait_time = self.rate_limit_reset - now + 1

            if wait_time > 0:
                print(f"⏳ 速率限制已用尽,等待 {wait_time} 秒后重试...")
                time.sleep(wait_time)

    def fetch_new_repos(self, days: int = 7) -> List[Dict]:
        """
        获取最近创建的仓库

        Args:
            days: 最近多少天

        Returns:
            仓库列表
        """
        cutoff_date = (datetime.now() - timedelta(days=days)).strftime("%Y-%m-%d")
        query = f"topic:{self.topic} created:>{cutoff_date}"

        print(f"📡 正在获取最近 {days} 天创建的仓库...")

        repos = []
        page = 1

        while page <= self.max_pages:
            url = f"{self.api_base}/search/repositories"
            params = {
                "q": query,
                "sort": "stars",
                "order": "desc",
                "per_page": self.per_page,
                "page": page
            }

            try:
                response = self.session.get(url, params=params, timeout=30)
                response.raise_for_status()
                data = response.json()

                if not data or "items" not in data:
                    break

                items = data["items"]
                if not items:
                    break

                for item in items:
                    repo = self._parse_repo_item(item, len(repos) + 1)
                    repos.append(repo)

                print(f"   第 {page} 页: 获取 {len(items)} 个仓库")

                if len(items) < self.per_page:
                    break

                page += 1
                time.sleep(self.delay)

            except requests.RequestException as e:
                print(f"   ⚠️ 请求失败: {e}")
                break

        print(f"✅ 获取到 {len(repos)} 个新仓库")
        return repos

    def fetch_repo_details(self, owner: str, repo: str) -> Optional[Dict]:
        """
        获取单个仓库的详细信息

        Args:
            owner: 仓库拥有者
            repo: 仓库名称

        Returns:
            仓库详细信息
        """
        url = f"{self.api_base}/repos/{owner}/{repo}"

        try:
            response = self.session.get(url, timeout=30)
            response.raise_for_status()
            return response.json()

        except requests.RequestException as e:
            print(f"   ⚠️ 获取仓库详情失败 {owner}/{repo}: {e}")
            return None


def fetch_repos(sort_by: str = "stars", limit: int = 100) -> List[Dict]:
    """便捷函数:获取仓库列表"""
    fetcher = GitHubFetcher()
    return fetcher.fetch(sort_by=sort_by, limit=limit)

```

### src/readme_fetcher.py

```python
"""
README Fetcher - 获取仓库 README 内容
使用 GitHub API 获取仓库的 README 文件
"""
import time
import re
import requests
from typing import Dict, List, Optional

from src.config import GITHUB_TOKEN, GITHUB_API_BASE, FETCH_REQUEST_DELAY


class ReadmeFetcher:
    """获取仓库 README 内容"""

    def __init__(self, token: str = None):
        """
        初始化

        Args:
            token: GitHub Personal Access Token
        """
        self.token = token or GITHUB_TOKEN
        self.api_base = GITHUB_API_BASE
        self.delay = FETCH_REQUEST_DELAY

        self.session = requests.Session()
        self.session.headers.update({
            "Accept": "application/vnd.github.v3+json",
            "User-Agent": "GitHub-Topics-Trending/1.0"
        })

        if self.token:
            self.session.headers.update({
                "Authorization": f"Bearer {self.token}"
            })

    def fetch_readme(self, owner: str, repo: str, html: bool = False) -> Optional[str]:
        """
        获取仓库 README 内容

        Args:
            owner: 仓库拥有者
            repo: 仓库名称
            html: 是否返回 HTML 格式

        Returns:
            README 内容
        """
        url = f"{self.api_base}/repos/{owner}/{repo}/readme"

        if html:
            self.session.headers["Accept"] = "application/vnd.github.html"

        try:
            response = self.session.get(url, timeout=30)
            response.raise_for_status()

            # GitHub 返回的是 base64 编码的内容
            data = response.json()

            if data.get("encoding") == "base64":
                import base64
                content = base64.b64decode(data.get("content", "")).decode("utf-8", errors="ignore")
                return content
            else:
                return data.get("content", "")

        except requests.RequestException as e:
            print(f"   ⚠️ 获取 README 失败 {owner}/{repo}: {e}")
            return None

    def fetch_readme_summary(self, owner: str, repo: str, max_length: int = 500) -> Optional[str]:
        """
        获取 README 摘要

        Args:
            owner: 仓库拥有者
            repo: 仓库名称
            max_length: 最大长度

        Returns:
            README 摘要文本
        """
        readme = self.fetch_readme(owner, repo)

        if not readme:
            return None

        # 移除 Markdown 标记,提取纯文本
        summary = self._extract_text_from_markdown(readme)

        # 截断到指定长度
        if len(summary) > max_length:
            summary = summary[:max_length].rsplit(" ", 1)[0] + "..."

        return summary

    def _extract_text_from_markdown(self, markdown: str) -> str:
        """
        从 Markdown 中提取纯文本

        Args:
            markdown: Markdown 内容

        Returns:
            纯文本
        """
        # 移除代码块
        markdown = re.sub(r'```.*?```', '', markdown, flags=re.DOTALL)
        markdown = re.sub(r'`.*?`', '', markdown)

        # 移除链接
        markdown = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', markdown)

        # 移除图片
        markdown = re.sub(r'!\[([^\]]*)\]\([^\)]+\)', '', markdown)

        # 移除标题标记
        markdown = re.sub(r'^#+\s+', '', markdown, flags=re.MULTILINE)

        # 移除加粗/斜体
        markdown = re.sub(r'\*\*([^*]+)\*\*', r'\1', markdown)
        markdown = re.sub(r'\*([^*]+)\*', r'\1', markdown)
        markdown = re.sub(r'__([^_]+)__', r'\1', markdown)
        markdown = re.sub(r'_([^_]+)_', r'\1', markdown)

        # 移除水平线
        markdown = re.sub(r'^---+$', '', markdown, flags=re.MULTILINE)
        markdown = re.sub(r'^\*\*\*+$', '', markdown, flags=re.MULTILINE)

        # 移除多余的空行
        lines = [line.strip() for line in markdown.split('\n')]
        lines = [line for line in lines if line]

        return ' '.join(lines)

    def batch_fetch_readmes(self, repos: List[Dict], delay: float = None) -> Dict[str, str]:
        """
        批量获取 README 内容

        Args:
            repos: 仓库列表
            delay: 请求间隔

        Returns:
            {repo_name: readme_summary} 字典
        """
        delay = delay if delay is not None else self.delay
        summaries = {}

        print(f"📥 开始批量获取 README...")

        for i, repo in enumerate(repos, 1):
            repo_name = repo.get("repo_name") or repo.get("name", "")

            if not repo_name or "/" not in repo_name:
                continue

            owner, name = repo_name.split("/", 1)

            print(f"  [{i}/{len(repos)}] {repo_name}")

            summary = self.fetch_readme_summary(owner, name)
            if summary:
                summaries[repo_name] = summary

            # 请求间隔
            if i < len(repos):
                time.sleep(delay)

        print(f"✅ 成功获取 {len(summaries)} 个 README 摘要")
        return summaries

    def fetch_from_github_raw(self, owner: str, repo: str, branch: str = "main") -> Optional[str]:
        """
        直接从 GitHub raw 内容获取 README

        Args:
            owner: 仓库拥有者
            repo: 仓库名称
            branch: 分支名

        Returns:
            README 内容
        """
        # 尝试常见的 README 文件名
        readme_names = ["README.md", "README.markdown", "README.rst", "README.txt"]

        for name in readme_names:
            url = f"https://raw.githubusercontent.com/{owner}/{repo}/{branch}/{name}"

            try:
                response = requests.get(url, timeout=10)
                if response.status_code == 200:
                    return response.text
            except requests.RequestException:
                continue

        # 尝试 master 分支
        if branch == "main":
            return self.fetch_from_github_raw(owner, repo, "master")

        return None


def fetch_readme_summary(owner: str, repo: str) -> Optional[str]:
    """便捷函数:获取 README 摘要"""
    fetcher = ReadmeFetcher()
    return fetcher.fetch_readme_summary(owner, repo)


def batch_fetch_readmes(repos: List[Dict]) -> Dict[str, str]:
    """便捷函数:批量获取 README"""
    fetcher = ReadmeFetcher()
    return fetcher.batch_fetch_readmes(repos)

```



---

## Skill Companion Files

> Additional files collected from the skill directory layout.

### _meta.json

```json
{
  "owner": "hjw21century",
  "slug": "github-topics",
  "displayName": "Github Topics",
  "latest": {
    "version": "0.1.0",
    "publishedAt": 1770998142045,
    "commit": "https://github.com/openclaw/skills/commit/be5325607c0f244ae4e5f8ae64a6d00edb05d01b"
  },
  "history": []
}

```

github-topics | SkillHub