GitHub Repo Discovery
Find the best GitHub repositories for any task, technology, or domain. Goes far beyond gh search repos — generates multi-angle queries, scores candidates on quality signals, cross-references external validation (awesome lists, HN, Reddit), and outputs a ranked comparison with actionable verdicts.
PITFALL: GitHub search API returns different results for authenticated vs. unauthenticated requests. The gh CLI uses your auth token and returns more results. Always prefer gh search repos over curl when available.
Prerequisites
# Verify gh is authenticated
gh auth status
# If not: gh auth login (web browser OAuth — tokens in shell are blocked)
If gh is unavailable, set GITHUB_TOKEN and use the curl fallback patterns from github-repo-management.
Phase 1: Query Decomposition
For a given task, generate 4-6 search angles. Do NOT run a single broad search — different angles surface different repos.
| Angle | Query pattern | Example |
|---|---|---|
| Topic match | topic:<keyword> |
topic:nootropics |
| Name match | <keyword> in:name |
peptide in:name |
| README content | <keyword> in:readme |
"cognitive enhancement" in:readme |
| Description content | <keyword> in:description |
"BCI" in:description |
| Language-scoped | <keyword> language:<lang> |
brain-computer language:python |
| Awesome list | awesome <keyword> in:name |
awesome nootropics in:name |
| Framework-specific | <framework> <keyword> |
react bioinformatics |
Filter Qualifiers (append to all queries)
stars:>=10 # Minimum quality bar (lower for niche topics)
pushed:>=2025-01-01 # Active in the last 18 months
archived:false # Not archived
is:public # Only public repos
fork:only OR fork:true depending # Include or exclude forks
sort:stars # Sort by stars
Adjust stars: threshold by domain maturity:
- Mature domain (React, Python tools):
stars:>=100 - Mid domain (ML frameworks, dev tools):
stars:>=25 - Niche domain (research chemicals, obscure protocols):
stars:>=5or omit entirely
Phase 2: Run Searches
Run searches using direct terminal() calls — NOT execute_code batches. The execute_code sandbox auto-parses gh --json output into Python dicts, which breaks subsequent json.loads() processing. Individual terminal() calls return plain strings that parse cleanly.
Run all query angles in parallel (separate terminal() calls in one turn). Each search returns top 10-15 results.
Parse combined results with --json flag for structured data:
gh search repos "topic:machine-learning stars:>=50" \
--sort stars --limit 20 \
--json name,fullName,url,stargazersCount,language,description,updatedAt,license,topics,defaultBranch,openIssuesCount,forkCount
Parse with Python:
import json
# After collecting gh search --json output
for repo in json.loads(output):
stars = repo['stargazersCount']
last_push = repo['updatedAt']
language = repo.get('language', 'N/A')
topics = ', '.join(repo.get('topics', [])[:5])
desc = (repo.get('description') or '')[:100]
Phase 3: Quality Scoring
For each unique repo across all search results, fetch detailed metrics and score on these dimensions. Use direct terminal() calls (NOT execute_code) — same sandbox auto-parse issue as Phase 2. Batch API calls by running multiple terminal() calls in a single turn.
Metrics to Collect
from hermes_tools import terminal
import json
def get_repo_details(owner, name):
"""Get extended repo stats via gh API."""
cmd = f'gh api repos/{owner}/{name} --jq "{{stars:.stargazers_count,forks:.forks_count,open_issues:.open_issues_count,created:.created_at,pushed:.pushed_at,license:.license.spdx_id or \\"none\\",language:.language,topics:.topics,description:.description,archived:.archived,watchers:.subscribers_count,default_branch:.default_branch}}"'
return json.loads(terminal(cmd, timeout=10))
def get_commit_activity(owner, name):
"""Last 4 weeks of commits and contributor count."""
cmd = f'gh api repos/{owner}/{name}/stats/commit_activity --jq ".[-4:]"'
weeks = json.loads(terminal(cmd, timeout=10))
total_commits = sum(w['total'] for w in weeks)
return total_commits
def get_contributors(owner, name):
"""Contributor count (bus factor proxy)."""
cmd = f'gh api repos/{owner}/{name}/contributors --jq "length"'
return int(terminal(cmd, timeout=10))
def get_issue_velocity(owner, name):
"""Issues opened vs closed in last 90 days."""
since = (__import__('datetime').datetime.now() - __import__('datetime').timedelta(days=90)).isoformat()
cmd_closed = f'gh api "search/issues?q=repo:{owner}/{name}+is:issue+closed:>={since}" --jq ".total_count"'
cmd_opened = f'gh api "search/issues?q=repo:{owner}/{name}+is:issue+created:>={since}" --jq ".total_count"'
closed = int(terminal(cmd_closed, timeout=10) or 0)
opened = int(terminal(cmd_opened, timeout=10) or 0)
return {'opened': opened, 'closed': closed}
Scoring Rubric (0-100)
Score each dimension 0-20, then sum:
| Dimension | Weight | 0-5 pts | 6-12 pts | 13-17 pts | 18-20 pts |
|---|---|---|---|---|---|
| Stars | 20 | <10 | 10-99 | 100-999 | 1000+ |
| Recency | 20 | >2yr stale | 1-2yr | 3-12mo | <3mo |
| Maintainers | 20 | 1 person | 2-3 | 4-9 | 10+ |
| Community | 20 | 0 contribs | 1-5 contribs | 6-20 | 20+ |
| Velocity | 20 | 0 commits/month | 1-10/month | 11-50/month | 50+/month |
Bonus/Penalty modifiers:
| Signal | Adjustment |
|---|---|
| Has license (MIT/Apache/BSD/GPL) | +10 |
| No license | -15 |
| Archived | -100 (disqualify) |
| >50% open issue ratio (issues piling up) | -10 |
| README has installation steps | +5 |
| Has CONTRIBUTING.md | +5 |
| Active discussions tab | +5 |
| Stars/week growth >1 (rising, not stagnant) | +10 |
Stars/Week Calculation (Growth Signal)
from datetime import datetime, timezone
def stars_per_week(created_at, stars):
"""Stars divided by weeks since creation. >1 is healthy growth."""
created = datetime.fromisoformat(created_at.replace('Z', '+00:00'))
weeks = max((datetime.now(timezone.utc) - created).days / 7, 1)
return stars / weeks
Phase 4: External Validation
Cross-reference top candidates against external signals for quality confirmation.
Awesome Lists
# Search for the repo name in awesome-list repos (aggregate curation)
gh search repos "REPO_NAME in:readme topic:awesome-list" --sort stars --limit 5
# Or search awesome lists directly
gh search repos "KEYWORD awesome in:name" --sort stars --limit 10
Hacker News Mentions
# Use Algolia HN API (free, no auth)
curl -s "https://hn.algolia.com/api/v1/search?query=REPO_NAME&tags=story&hitsPerPage=5"
Parse for: points, num_comments, date — these signal real-world validation.
Reddit Mentions (if reddit-data-extraction skill available)
Use the Reddit skill's patterns to check r/programming, r/opensource, and domain-specific subreddits for discussion threads mentioning the repo.
GitHub "Used By" / Dependency Count
# Check how many repos depend on it (strong signal for libraries)
gh api repos/OWNER/REPO --jq '.network_count' # forks network
Phase 5: Ranked Output
Produce a comparison table ordered by final score, with actionable verdicts.
Output Template
## GitHub Repo Discovery: <TASK DESCRIPTION>
### Search Angles Used
- topic:X (stars:>=Y)
- X in:readme (stars:>=Y)
- X in:name
- awesome X in:name
- X in:description language:Z
### Results — Ranked by Quality Score
| # | Repo | ★ | Score | Last Push | Maintainers | Language | Verdict |
|---|------|---|-------|-----------|-------------|----------|---------|
| 1 | owner/repo | 2.1k | 85 | 2025-06-15 | 8 | Python | **Pick this.** Active, well-maintained, strong docs. |
| 2 | owner/repo2 | 450 | 72 | 2025-04-20 | 3 | Rust | Solid alternative. Smaller community but cleaner code. |
| 3 | owner/repo3 | 8.5k | 65 | 2024-11-01 | 15 | JS | Popular but slowing down. Watch for staleness. |
| 4 | owner/repo4 | 120 | 55 | 2025-07-01 | 2 | Python | New and rising. Promising but unproven. |
| 5 | owner/repo5 | 35 | 40 | 2023-08-10 | 1 | C | Niche but unique. Only option for X. |
### Verdict Legend
- **Pick this.** — Best overall choice for most users.
- **Solid alternative.** — Good, pick if #1 doesn't fit your stack.
- **Watch.** — Promising but needs 3-6 months to mature.
- **Legacy pick.** — Feature-complete but unmaintained. Use if stable is more important than new features.
- **Niche.** — Only option for a specific sub-problem.
### Quality Details
**#1 owner/repo (Score: 85/100)**
- Stars/week: 3.2 (rising)
- HN mentions: 4 (top post: 340pts)
- In awesome lists: awesome-python, awesome-ml
- License: MIT
- Issue velocity: 85% closed (healthy)
- Community: 40+ contributors, active discussions
**#2 owner/repo2 (Score: 72/100)**
- Stars/week: 1.1 (steady)
- HN mentions: 1
- License: Apache 2.0
- Issue velocity: 60% closed (some backlog)
- Community: 8 contributors
When to Recommend "No Clear Winner"
If no repo scores above 50, or if all top candidates are archived/slowly dying:
### Verdict: No clear winner
The top candidate (owner/repo, score: 48) has issues:
- Last push: 14 months ago
- Single maintainer
- Growing issue backlog (35% closed)
Alternatives to consider:
1. Build from scratch using <underlying library> as foundation
2. Check <adjacent domain> for repos that could be adapted
3. Monitor <rising-new-repo> (created 2 months ago, 80★, active) — too early to recommend but trajectory is strong
Quick Reference
| Step | Command/Tool | Output |
|---|---|---|
| Generate queries | Reasoning (4-6 angles) | List of gh search repos commands |
| Run searches | Direct terminal() calls (NOT execute_code) |
10-15 results × N angles |
| Deduplicate | Python: set of (owner/name) | Unique repo list |
| Score repos | Direct terminal(gh api ...) per repo |
Quality metrics |
| Cross-reference | Algolia HN + gh search awesome | External validation signals |
| Output table | Markdown table | Ranked comparison + verdicts |
PITFALLS
GitHub API rate limits. Authenticated: 30 req/min. Unauthenticated: 10 req/min. With ~10 repos to score and 3-4 API calls each, batch them and add 2-second delays between calls. If you hit a 403, wait 60 seconds and retry.
gh search reposreturns max 1000 results across 34 pages. Use--limitto cap at what you actually need (15-20 per query angle is plenty).Stars alone are a trap. A 5-year-old repo with 5000 stars and no commits since 2023 is worse than a 6-month-old repo with 200 stars that ships weekly. Always apply recency and velocity.
stars:>=10filters too aggressively for niche domains. If zero results, drop the stars filter entirely and re-run. Some excellent niche repos have <10 stars because the audience is tiny.Forks can be more maintained than originals. If a popular repo is stale, check if an active fork exists with
fork:onlyin the search.gh search repostext matching is fuzzy."cognitive enhancement" in:readmematches the phrase but NOT individual words — for broader matching, usecognitive in:readme enhancement in:readmeor justcognitive enhancement(without qualifier, searches name+description+readme).The JSON output flag is your friend. Always use
--jsonfor parsing. The default text output is human-readable but a nightmare to parse reliably.execute_codesandbox auto-parsesgh --jsonoutput into dicts. Whenterminal()insideexecute_codereceivesgh search repos --jsonorgh api --jqoutput, it returns Python dicts/lists, not strings. Callingjson.loads()on a dict raisesTypeError. Workaround: use directterminal()calls from the main conversation loop instead of batching searches insideexecute_code. Direct calls return plain strings that can be parsed. For Phase 3 scoring, rungh apicalls individually viaterminal()— they're fast and you can parallelize 6-8 calls per turn.execute_codesandbox auto-parsesgh --jsonoutput into dicts. Whenterminal()insideexecute_codereceivesgh search repos --jsonorgh api --jqoutput, it returns Python dicts/lists, not strings. Callingjson.loads()on a dict raisesTypeError. Workaround: use directterminal()calls from the main conversation loop instead of batching insideexecute_code. Direct calls return plain strings that parse correctly.