# Github Repo Discovery

> GitHub Repo Discovery

- Skill: `lucadominguez/github-repo-discovery` (Agent Skill)
- Install (CLI): `npx skillmds@latest add lucadominguez/github-repo-discovery`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lucadominguez/github-repo-discovery/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- Author: lucadominguez (https://skillmd.com/u/lucadominguez)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lucadominguez/github-repo-discovery

---

# 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

```bash
# 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:>=5` or 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:

```bash
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:

```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

```python
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)

```python
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

```bash
# 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

```bash
# 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

```bash
# 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

1. **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.

2. **`gh search repos` returns max 1000 results across 34 pages.** Use `--limit` to cap at what you actually need (15-20 per query angle is plenty).

3. **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.

4. **`stars:>=10` filters 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.

5. **Forks can be more maintained than originals.** If a popular repo is stale, check if an active fork exists with `fork:only` in the search.

6. **`gh search repos` text matching is fuzzy.** `"cognitive enhancement" in:readme` matches the phrase but NOT individual words — for broader matching, use `cognitive in:readme enhancement in:readme` or just `cognitive enhancement` (without qualifier, searches name+description+readme).

7. **The JSON output flag is your friend.** Always use `--json` for parsing. The default text output is human-readable but a nightmare to parse reliably.

8. **`execute_code` sandbox auto-parses `gh --json` output into dicts.** When `terminal()` inside `execute_code` receives `gh search repos --json` or `gh api --jq` output, it returns Python dicts/lists, not strings. Calling `json.loads()` on a dict raises `TypeError`. Workaround: use direct `terminal()` calls from the main conversation loop instead of batching searches inside `execute_code`. Direct calls return plain strings that can be parsed. For Phase 3 scoring, run `gh api` calls individually via `terminal()` — they're fast and you can parallelize 6-8 calls per turn.

8. **`execute_code` sandbox auto-parses `gh --json` output into dicts.** When `terminal()` inside `execute_code` receives `gh search repos --json` or `gh api --jq` output, it returns Python dicts/lists, not strings. Calling `json.loads()` on a dict raises `TypeError`. Workaround: use direct `terminal()` calls from the main conversation loop instead of batching inside `execute_code`. Direct calls return plain strings that parse correctly.

