agentsclimarketplace

Github metrics processing

Skill cxcscmu/SkillLearnBench/skills/b1-one-shot-claude-haiku-4-5/github-repo-analytics/github-metrics-processing

Process GitHub API data to calculate PR/issue metrics including merge time averages, author rankings, and bug categorization.From its SKILL.md

Install
npx -y skills add cxcscmu/SkillLearnBench --skill github-metrics-processing

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

SKILL.md

5.0 KB, ~1.2k tokens by cl100k_base, as published. Nobody here has run it

GitHub Metrics Processing Skill

Overview

Process JSON data from GitHub queries to calculate publication metrics, including PR merge times, issue categorization, and contributor analysis.

Prerequisites

  • Python 3.7+ with json and datetime modules (standard library)
  • Raw GitHub API JSON data from gh CLI queries

Key Metrics to Calculate

Pull Request Metrics

1. Total PR Count

total_prs = len(prs_data)

2. Merged vs Closed PRs

merged_prs = [pr for pr in prs_data if pr.get('mergedAt') is not None]
closed_prs = [pr for pr in prs_data if pr.get('closedAt') is not None and pr.get('mergedAt') is None]

merged_count = len(merged_prs)
closed_count = len(closed_prs)

3. Average Merge Time (days)

from datetime import datetime

def parse_iso8601(timestamp_str):
    """Parse ISO 8601 timestamp to datetime object"""
    return datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))

def calculate_merge_days(prs):
    """Calculate average days from creation to merge"""
    merge_times = []
    for pr in prs:
        if pr.get('mergedAt'):  # Only count merged PRs
            created = parse_iso8601(pr['createdAt'])
            merged = parse_iso8601(pr['mergedAt'])
            days = (merged - created).days
            merge_times.append(days)

    if not merge_times:
        return 0.0

    avg = sum(merge_times) / len(merge_times)
    return round(avg, 1)  # Round to one decimal place

4. Top Contributor (Most PRs)

from collections import Counter

def get_top_contributor(prs):
    """Find author who opened the most PRs"""
    authors = [pr['author']['login'] for pr in prs if pr.get('author')]
    if not authors:
        return None

    author_counts = Counter(authors)
    top_author, _ = author_counts.most_common(1)[0]
    return top_author

Issue Metrics

1. Total Issue Count

total_issues = len(issues_data)

2. Bug Reports (label matching)

def count_bug_issues(issues):
    """Count issues with 'bug' in any label name"""
    bug_count = 0
    for issue in issues:
        labels = issue.get('labels', [])
        has_bug = any('bug' in label.get('name', '').lower() for label in labels)
        if has_bug:
            bug_count += 1
    return bug_count

bug_count = count_bug_issues(issues_data)

3. Resolved Bugs (closed during month)

def count_resolved_bugs(issues, period_end):
    """Count bugs that were closed during the specified month"""
    resolved_count = 0
    for issue in issues:
        # Must have 'bug' in a label
        labels = issue.get('labels', [])
        has_bug = any('bug' in label.get('name', '').lower() for label in labels)

        # Must be closed
        closed_at = issue.get('closedAt')
        if has_bug and closed_at:
            resolved_count += 1

    return resolved_count

Complete Processing Example

import json
from datetime import datetime
from collections import Counter

def parse_iso8601(timestamp_str):
    return datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))

def process_github_data(prs_json, issues_json):
    """Process GitHub data and return metrics"""

    prs = json.loads(prs_json)
    issues = json.loads(issues_json)

    # PR Metrics
    merged_prs = [pr for pr in prs if pr.get('mergedAt')]
    closed_prs = [pr for pr in prs
                  if pr.get('closedAt') and not pr.get('mergedAt')]

    merge_times = []
    for pr in merged_prs:
        created = parse_iso8601(pr['createdAt'])
        merged = parse_iso8601(pr['mergedAt'])
        days = (merged - created).days
        merge_times.append(days)

    avg_merge_days = round(sum(merge_times) / len(merge_times), 1) if merge_times else 0.0

    authors = [pr['author']['login'] for pr in prs if pr.get('author')]
    top_contributor = Counter(authors).most_common(1)[0][0] if authors else None

    # Issue Metrics
    bug_issues = []
    for issue in issues:
        labels = issue.get('labels', [])
        if any('bug' in label.get('name', '').lower() for label in labels):
            bug_issues.append(issue)

    resolved_bugs = sum(1 for issue in bug_issues if issue.get('closedAt'))

    return {
        'pr': {
            'total': len(prs),
            'merged': len(merged_prs),
            'closed': len(closed_prs),
            'avg_merge_days': avg_merge_days,
            'top_contributor': top_contributor
        },
        'issue': {
            'total': len(issues),
            'bug': len(bug_issues),
            'resolved_bugs': resolved_bugs
        }
    }

Edge Cases to Handle

  • Null values in timestamps (still open/not merged items)
  • Case-insensitive label matching for "bug"
  • Authors from external contributors (verify .login field exists)
  • PRs created but never merged or closed (neither count toward closed)
  • Division by zero when no merged PRs exist

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.