| """ |
| GitHub List Repositories Tool - List and sort repositories for any user or organization |
| |
| Efficiently discover repositories with flexible sorting options. |
| """ |
|
|
| import os |
| from typing import Any, Dict, Literal, Optional |
|
|
| import requests |
|
|
| from agent.tools.types import ToolResult |
|
|
|
|
| def list_repos( |
| owner: str, |
| owner_type: Literal["user", "org"] = "org", |
| sort: Literal["stars", "forks", "updated", "created"] = "stars", |
| order: Literal["asc", "desc"] = "desc", |
| limit: Optional[int] = 30, |
| ) -> ToolResult: |
| """ |
| List repositories for a user or organization using GitHub REST API. |
| |
| Args: |
| owner: GitHub username or organization name |
| owner_type: Whether the owner is a "user" or "org" (default: "org") |
| sort: Sort field - "stars", "forks", "updated", or "created" |
| order: Sort order - "asc" or "desc" (default: "desc") |
| limit: Maximum number of repositories to return |
| |
| Returns: |
| ToolResult with repository information |
| """ |
| token = os.environ.get("GITHUB_TOKEN") |
| if not token: |
| return { |
| "formatted": "Error: GITHUB_TOKEN environment variable is required", |
| "totalResults": 0, |
| "resultsShared": 0, |
| "isError": True, |
| } |
|
|
| if owner_type == "org": |
| url = f"https://api.github.com/orgs/{owner}/repos" |
| else: |
| url = f"https://api.github.com/users/{owner}/repos" |
|
|
| headers = { |
| "Accept": "application/vnd.github+json", |
| "X-GitHub-Api-Version": "2022-11-28", |
| "Authorization": f"Bearer {token}", |
| } |
|
|
| all_repos = [] |
| page = 1 |
| per_page = 100 |
|
|
| |
| |
| |
| api_sort_map = { |
| "created": "created", |
| "updated": "updated", |
| "stars": None, |
| "forks": None, |
| } |
|
|
| api_sort = api_sort_map.get(sort) |
| need_manual_sort = api_sort is None |
|
|
| try: |
| while True: |
| params = { |
| "page": page, |
| "per_page": per_page, |
| } |
|
|
| |
| if api_sort: |
| params["sort"] = api_sort |
| params["direction"] = order |
|
|
| response = requests.get( |
| url, |
| headers=headers, |
| params=params, |
| timeout=30, |
| ) |
|
|
| if response.status_code == 403: |
| error_data = response.json() |
| return { |
| "formatted": f"GitHub API rate limit or permission error: {error_data.get('message', 'Unknown error')}", |
| "totalResults": 0, |
| "resultsShared": 0, |
| "isError": True, |
| } |
|
|
| if response.status_code != 200: |
| error_msg = f"GitHub API error (status {response.status_code})" |
| try: |
| error_data = response.json() |
| if "message" in error_data: |
| error_msg += f": {error_data['message']}" |
| except Exception: |
| pass |
| return { |
| "formatted": error_msg, |
| "totalResults": 0, |
| "resultsShared": 0, |
| "isError": True, |
| } |
|
|
| items = response.json() |
|
|
| if not items: |
| break |
|
|
| for item in items: |
| all_repos.append( |
| { |
| "name": item.get("name"), |
| "full_name": item.get("full_name"), |
| "description": item.get("description"), |
| "html_url": item.get("html_url"), |
| "language": item.get("language"), |
| "stars": item.get("stargazers_count", 0), |
| "forks": item.get("forks_count", 0), |
| "open_issues": item.get("open_issues_count", 0), |
| "topics": item.get("topics", []), |
| "updated_at": item.get("updated_at"), |
| "created_at": item.get("created_at"), |
| } |
| ) |
|
|
| |
| if len(items) < per_page: |
| break |
|
|
| |
| if limit and len(all_repos) >= limit: |
| break |
|
|
| page += 1 |
|
|
| except requests.exceptions.RequestException as e: |
| return { |
| "formatted": f"Failed to connect to GitHub API: {str(e)}", |
| "totalResults": 0, |
| "resultsShared": 0, |
| "isError": True, |
| } |
|
|
| |
| if need_manual_sort and all_repos: |
| reverse = order == "desc" |
| all_repos.sort(key=lambda x: x[sort], reverse=reverse) |
|
|
| |
| if limit: |
| all_repos = all_repos[:limit] |
|
|
| if not all_repos: |
| return { |
| "formatted": f"No repositories found for {owner_type} '{owner}'", |
| "totalResults": 0, |
| "resultsShared": 0, |
| } |
|
|
| |
| lines = [f"**Found {len(all_repos)} repositories for {owner}:**\n"] |
|
|
| for i, repo in enumerate(all_repos, 1): |
| lines.append(f"{i}. **{repo['full_name']}**") |
| lines.append( |
| f" ⭐ {repo['stars']:,} stars | 🍴 {repo['forks']:,} forks | Language: {repo['language'] or 'N/A'}" |
| ) |
| if repo["description"]: |
| desc = ( |
| repo["description"][:100] + "..." |
| if len(repo["description"]) > 100 |
| else repo["description"] |
| ) |
| lines.append(f" {desc}") |
| lines.append(f" URL: {repo['html_url']}") |
| if repo["topics"]: |
| lines.append(f" Topics: {', '.join(repo['topics'][:5])}") |
|
|
| |
| lines.append(f" Use in tools: {{'repo': '{repo['full_name']}'}}") |
| lines.append("") |
|
|
| return { |
| "formatted": "\n".join(lines), |
| "totalResults": len(all_repos), |
| "resultsShared": len(all_repos), |
| } |
|
|
|
|
| |
| GITHUB_LIST_REPOS_TOOL_SPEC = { |
| "name": "github_list_repos", |
| "description": ( |
| "List and discover repositories for GitHub organizations or users with flexible sorting. " |
| "**Use when:** (1) Exploring what libraries exist for a task, (2) Finding the right library to use, " |
| "(3) Discovering popular or active projects, (4) Checking recently updated repos for latest features, " |
| "(5) Finding alternative libraries in an organization. " |
| "**Pattern:** github_list_repos (discover libraries) → github_find_examples (find usage examples) → implement. " |
| "Returns: Comprehensive repository information (stars, forks, language, topics, URLs), sorted by preference. " |
| "**Then:** Use github_find_examples on selected repo to discover example code. " |
| "Sorts by: stars (popularity), forks (community), updated (activity), created (age).\n\n" |
| "## When to use this tool\n\n" |
| "- When you need to find libraries to use in your implementation\n" |
| "- When exploring what repositories exist for a task or domain\n" |
| "- When debugging an error and looking up if others have similar issues in repos\n" |
| "- When finding the most popular or actively maintained projects for a user/org\n" |
| "## Examples\n\n" |
| "<example>\n" |
| "// ML Workflow Step: Discover HF libraries for RLHF/alignment\n" |
| "// Use case: Find the right library for training with human feedback\n" |
| "{\n" |
| " owner: 'huggingface',\n" |
| " owner_type: 'org',\n" |
| " sort: 'stars',\n" |
| " limit: 10\n" |
| "}\n" |
| "// Returns: transformers, trl, peft, accelerate, diffusers...\n" |
| "</example>\n\n" |
| "<example>\n" |
| "// ML Workflow Step: Check for recently updated HF repos\n" |
| "// Use case: Find actively maintained libraries with latest features\n" |
| "{\n" |
| " owner: 'huggingface',\n" |
| " owner_type: 'org',\n" |
| " sort: 'updated',\n" |
| " order: 'desc',\n" |
| " limit: 15\n" |
| "}\n" |
| "// Helps identify which repos have recent improvements/fixes\n" |
| "</example>" |
| ), |
| "parameters": { |
| "type": "object", |
| "properties": { |
| "owner": { |
| "type": "string", |
| "description": "GitHub username or organization name. Required.", |
| }, |
| "owner_type": { |
| "type": "string", |
| "enum": ["user", "org"], |
| "description": "Whether the owner is a 'user' or 'org'. Default: 'org'.", |
| }, |
| "sort": { |
| "type": "string", |
| "enum": ["stars", "forks", "updated", "created"], |
| "description": "Sort field. Options: 'stars', 'forks', 'updated', 'created'. Default: 'stars'.", |
| }, |
| "order": { |
| "type": "string", |
| "enum": ["asc", "desc"], |
| "description": "Sort order. Options: 'asc', 'desc'. Default: 'desc'.", |
| }, |
| "limit": { |
| "type": "integer", |
| "description": "Maximum number of repositories to return. No limit if not specified. Default: 30.", |
| }, |
| }, |
| "required": ["owner"], |
| }, |
| } |
|
|
|
|
| async def github_list_repos_handler(arguments: Dict[str, Any]) -> tuple[str, bool]: |
| """Handler for agent tool router""" |
| try: |
| result = list_repos( |
| owner=arguments["owner"], |
| owner_type=arguments.get("owner_type", "org"), |
| sort=arguments.get("sort", "stars"), |
| order=arguments.get("order", "desc"), |
| limit=arguments.get("limit"), |
| ) |
| return result["formatted"], not result.get("isError", False) |
| except Exception as e: |
| return f"Error listing repositories: {str(e)}", False |
|
|