Files
2025-11-30 09:01:22 +08:00

360 lines
9.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Ranking Algorithm for Claude Skills
Comprehensive scoring system to rank skills by popularity, freshness, and quality.
## Core Ranking Formula
```
final_score = (base_stars * freshness_multiplier * quality_bonus)
```
## Component 1: Base Stars
Direct indicator of community validation and popularity.
```bash
base_stars = repository.stargazers_count
# Minimum threshold: 10 stars (filter out experiments)
if [ $base_stars -lt 10 ]; then
skip_result=true
fi
```
## Component 2: Freshness Multiplier
Recent updates indicate active maintenance and modern practices.
### Calculate Days Since Last Update
```bash
# Get pushed_at timestamp from repository
pushed_at="2025-10-28T12:00:00Z"
# Calculate days old (macOS)
days_old=$(( ($(date +%s) - $(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$pushed_at" +%s)) / 86400 ))
# Calculate days old (Linux)
days_old=$(( ($(date +%s) - $(date -d "$pushed_at" +%s)) / 86400 ))
```
### Apply Freshness Multiplier
```bash
if [ $days_old -lt 30 ]; then
freshness_multiplier=1.5
freshness_badge="🔥"
freshness_label="Hot"
elif [ $days_old -lt 90 ]; then
freshness_multiplier=1.2
freshness_badge="📅"
freshness_label="Recent"
elif [ $days_old -lt 180 ]; then
freshness_multiplier=1.0
freshness_badge="📆"
freshness_label="Active"
else
freshness_multiplier=0.5
freshness_badge="⏰"
freshness_label="Older"
fi
```
### Freshness Tiers
| Age Range | Multiplier | Badge | Label | Reasoning |
|-----------|------------|-------|-------|-----------|
| < 30 days | 1.5x | 🔥 | Hot | Very active, likely works with latest Claude |
| 30-90 days | 1.2x | 📅 | Recent | Active maintenance |
| 90-180 days | 1.0x | 📆 | Active | Stable, still maintained |
| > 180 days | 0.5x | ⏰ | Older | May be outdated or abandoned |
## Component 3: Quality Bonus (Optional)
Additional signals of skill quality beyond stars and freshness.
### Quality Signals
```bash
quality_bonus=1.0 # Start at neutral
# Has comprehensive description
if [ ${#description} -gt 100 ]; then
quality_bonus=$(echo "$quality_bonus + 0.1" | bc)
fi
# Has reference files
reference_count=$(gh api "repos/$repo/contents" | jq '[.[] | select(.name | test("references|docs|examples"))] | length')
if [ $reference_count -gt 0 ]; then
quality_bonus=$(echo "$quality_bonus + 0.1" | bc)
fi
# Has dependencies documentation
if gh api "repos/$repo/contents" | jq -e '.[] | select(.name == "package.json")' > /dev/null; then
quality_bonus=$(echo "$quality_bonus + 0.05" | bc)
fi
# Active issues/PRs indicate engagement
open_issues=$(gh api "repos/$repo" | jq '.open_issues_count')
if [ $open_issues -gt 0 ] && [ $open_issues -lt 20 ]; then
quality_bonus=$(echo "$quality_bonus + 0.05" | bc)
fi
# Has recent commits (beyond just pushed_at)
recent_commits=$(gh api "repos/$repo/commits?per_page=10" | jq 'length')
if [ $recent_commits -ge 5 ]; then
quality_bonus=$(echo "$quality_bonus + 0.1" | bc)
fi
# Cap quality bonus at 1.5x
if (( $(echo "$quality_bonus > 1.5" | bc -l) )); then
quality_bonus=1.5
fi
```
### Quality Tier Examples
| Quality Bonus | Characteristics |
|---------------|-----------------|
| 1.0x (baseline) | Basic SKILL.md only |
| 1.1x | + Good description |
| 1.2x | + Reference files |
| 1.3x | + Dependencies documented |
| 1.4x | + Active community (issues/PRs) |
| 1.5x (max) | All of the above |
## Complete Scoring Implementation
### Bash Implementation
```bash
#!/bin/bash
calculate_score() {
local repo=$1
local stars=$2
local pushed_at=$3
# Calculate days since last update
days_old=$(( ($(date +%s) - $(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$pushed_at" +%s)) / 86400 ))
# Freshness multiplier
if [ $days_old -lt 30 ]; then
freshness=1.5
badge="🔥"
elif [ $days_old -lt 90 ]; then
freshness=1.2
badge="📅"
elif [ $days_old -lt 180 ]; then
freshness=1.0
badge="📆"
else
freshness=0.5
badge="⏰"
fi
# Calculate final score
score=$(echo "$stars * $freshness" | bc)
echo "$score|$badge|$days_old"
}
# Example usage
result=$(calculate_score "lackeyjb/playwright-skill" 612 "2025-10-28T12:00:00Z")
score=$(echo "$result" | cut -d'|' -f1)
badge=$(echo "$result" | cut -d'|' -f2)
days=$(echo "$result" | cut -d'|' -f3)
echo "Score: $score, Badge: $badge, Days: $days"
# Output: Score: 918.0, Badge: 🔥, Days: 2
```
### JQ Implementation (More Portable)
```bash
gh search repos "claude skills" --json name,stargazersCount,pushedAt --limit 20 | \
jq -r --arg now "$(date -u +%s)" '.[] |
. as $repo |
($repo.pushedAt | fromdateiso8601) as $pushed |
(($now | tonumber) - $pushed) / 86400 | floor as $days |
(if $days < 30 then 1.5 elif $days < 90 then 1.2 elif $days < 180 then 1.0 else 0.5 end) as $multiplier |
(if $days < 30 then "🔥" elif $days < 90 then "📅" elif $days < 180 then "📆" else "⏰" end) as $badge |
($repo.stargazersCount * $multiplier) as $score |
"\($score)|\($repo.name)|\($repo.stargazersCount)|\($badge)|\($days)"
' | sort -t'|' -k1 -nr | head -10
```
## Ranking Examples
### Real-World Scores
| Skill | Stars | Days Old | Freshness | Multiplier | Final Score | Rank |
|-------|-------|----------|-----------|------------|-------------|------|
| playwright-skill | 612 | 2 | 🔥 | 1.5x | **918** | #1 |
| agent-skill-creator | 96 | 5 | 🔥 | 1.5x | **144** | #2 |
| skill-codex | 153 | 9 | 🔥 | 1.5x | **229.5** | Would be #2, but bumped by age |
| ios-simulator-skill | 77 | 2 | 🔥 | 1.5x | **115.5** | #3 |
| claude-skills-mcp | 85 | 2 | 🔥 | 1.5x | **127.5** | #4 |
*With hot multiplier, even 77 stars beats 96 stars from 30+ days ago*
### Score Comparison Scenarios
**Scenario 1: Fresh skill vs. Popular old skill**
- Skill A: 50 stars, 10 days old → 50 × 1.5 = **75 points** 🔥
- Skill B: 100 stars, 200 days old → 100 × 0.5 = **50 points**
- **Winner: Skill A** (freshness wins)
**Scenario 2: Very popular but older skill**
- Skill A: 1000 stars, 365 days old → 1000 × 0.5 = **500 points**
- Skill B: 200 stars, 15 days old → 200 × 1.5 = **300 points** 🔥
- **Winner: Skill A** (massive popularity overcomes age)
**Scenario 3: Moderate skill, regularly updated**
- Skill A: 50 stars, 85 days old → 50 × 1.2 = **60 points** 📅
- Skill B: 60 stars, 95 days old → 60 × 1.0 = **60 points** 📆
- **Tie** (freshness threshold matters)
## Handling Edge Cases
### Newly Created Skills (< 7 days old)
```bash
# New skills may have inflated scores, add small penalty
if [ $days_old -lt 7 ]; then
new_skill_penalty=0.9
score=$(echo "$score * $new_skill_penalty" | bc)
note="⚠️ Very new skill - limited validation"
fi
```
### Archived or Deprecated Skills
```bash
# Check if repository is archived
is_archived=$(gh api "repos/$repo" | jq -r '.archived')
if [ "$is_archived" = "true" ]; then
score=0 # Exclude from results
note="🔒 Archived - no longer maintained"
fi
```
### Fork vs. Original
```bash
# Check if repository is a fork
is_fork=$(gh api "repos/$repo" | jq -r '.fork')
if [ "$is_fork" = "true" ]; then
# Check if fork has more stars than parent
parent_stars=$(gh api "repos/$repo" | jq -r '.parent.stargazers_count')
if [ $stars -gt $parent_stars ]; then
# Fork is more popular, keep it
note="🍴 Popular fork"
else
# Prefer original
score=$(echo "$score * 0.8" | bc)
note="🍴 Fork - see original"
fi
fi
```
## Sorting and Display
### Final Sort Order
```bash
# Sort by score (descending), then by stars (descending)
results | sort -t'|' -k1,1nr -k3,3nr | head -10
```
### Ties
When scores are identical:
1. Sort by stars (higher first)
2. If still tied, sort by freshness (newer first)
3. If still tied, sort alphabetically
```bash
# Multi-level sort
jq -r '.[] | "\(.score)|\(.stars)|\(.days_old)|\(.name)"' | \
sort -t'|' -k1,1nr -k2,2nr -k3,3n -k4,4
```
## Performance Optimization
### Bulk Scoring
```bash
# Score all repos in one pass
gh search repos "claude skills" --json name,stargazersCount,pushedAt,fullName --limit 50 | \
jq -r --arg now "$(date -u +%s)" '
.[] |
. as $repo |
($repo.pushedAt | fromdateiso8601) as $pushed |
(($now | tonumber) - $pushed) / 86400 | floor as $days |
(if $days < 30 then 1.5 elif $days < 90 then 1.2 elif $days < 180 then 1.0 else 0.5 end) as $mult |
($repo.stargazersCount * $mult) as $score |
{
name: $repo.name,
full_name: $repo.fullName,
stars: $repo.stargazersCount,
days_old: $days,
score: $score
}
' | jq -s 'sort_by(-.score) | .[0:10]'
```
### Caching Scores
```bash
# Cache scores to avoid recalculation
score_cache=".skill-scores.json"
# Generate or load cache
if [ ! -f "$score_cache" ] || [ $(($(date +%s) - $(stat -f %m "$score_cache"))) -gt 3600 ]; then
# Regenerate cache (older than 1 hour)
calculate_all_scores > "$score_cache"
fi
# Use cached scores
cat "$score_cache" | jq '.[] | select(.score > 50)'
```
## Customization
### User-Defined Weights
Allow users to adjust ranking preferences:
```bash
# Default weights
STAR_WEIGHT=1.0
FRESHNESS_WEIGHT=1.0
QUALITY_WEIGHT=0.5
# Calculate weighted score
weighted_score=$(echo "$stars * $STAR_WEIGHT + $freshness_score * $FRESHNESS_WEIGHT + $quality_score * $QUALITY_WEIGHT" | bc)
```
### Category-Specific Ranking
Different categories may value different factors:
```bash
# For automation skills: prioritize quality (working code)
if [ "$category" = "automation" ]; then
QUALITY_WEIGHT=1.5
fi
# For research skills: prioritize freshness (up-to-date sources)
if [ "$category" = "research" ]; then
FRESHNESS_WEIGHT=1.5
fi
```
---
**Summary:** The ranking algorithm balances popularity (stars) with recency (freshness), ensuring users see both well-established skills and cutting-edge new capabilities. Quality signals provide additional nuance for edge cases.