npm Downloads zu Leads
Nimm eine Liste von npm-Paketen, hole 12 Wochen Download-Daten, berechne die Breakout-Velocity, reichere Maintainer-Profile an und gebe eine priorisierte Liste pro Breakout-Paket mit Kontakthinweisen und einer Outreach-Nachricht aus.
Wichtige Regel: Jede Download-Zahl im Ergebnis muss aus der npm-API-Antwort stammen. Jeder GitHub-Handle oder Twitter-Username eines Maintainers muss aus der GitHub-API-Antwort stammen – nicht aus dem npm-Username geraten. Wenn die GitHub-API kein Feld twitter_username zurückgibt, schreibe „not found on GitHub“ – erfinde keinen.
Häufige Fehler
| Der Agent wird dazu neigen... | Warum das falsch ist |
|---|---|
| GitHub-Profile für jedes Paket in der Liste abrufen | Das Rate-Limit liegt bei 60 req/h ohne Token. Das Anreichern stabiler oder rückläufiger Pakete verbraucht das Budget, bevor man zu den Breakout-Paketen kommt. Hole nur Profile für Breakout- und Watching-Pakete. |
| Pakete nach rohen wöchentlichen Downloads ordnen | Rohe Downloads begünstigen React und Lodash, die keine Leads sind. Ein Paket, das von 1K auf 8K/Woche steigt, ist relevanter als React mit 50M/Woche. Die Velocity-Kennzahl ist das Signal. |
| URL-Encoding für Scoped Packages überspringen | @org/pkg ohne Encoding führt zu einem 404 von der npm-API. Kodiere @ als %40 und / als %2F für jedes Scoped Package. |
| Die Fähigkeit abbrechen, wenn das GitHub-Rate-Limit erreicht ist | Mache einen Graceful-Downgrade. Präsentiere die Velocity-Rangliste aus npm-Daten, überspringe verbleibende GitHub-Anreicherungen und füge ein Flag zu data_quality_flags hinzu. Brich nicht ab. |
| Outreach-Nachrichten ohne Nennung des spezifischen Pakets schreiben | Generische „Ich habe Ihr Projekt gesehen“-Nachrichten bleiben unbeantwortet. Jede Outreach-Nachricht muss das Paket, seine Wachstumszahlen und einen spezifischen Bezug zum vom Benutzer gegebenen Kontext nennen. |
| Pakete unter 500 wöchentlichen Downloads als Leads einschließen | Unter 500/Woche ist Rauschen. Der Maintainer hat noch keine relevante Zielgruppe. Markiere als „too early“, aber präsentiere es nicht als Lead. |
Schritt 1: Setup-Check
echo "GITHUB_TOKEN: ${GITHUB_TOKEN:-nicht gesetzt, unauthentifiziertes Rate-Limit gilt (60 req/h – genug für ~10 Pakete)}"
Wenn GITHUB_TOKEN nicht gesetzt ist: Fahre fort. Informiere den Benutzer: „GITHUB_TOKEN ist nicht gesetzt. Die GitHub-Anreicherung ist auf ~10 Pakete beschränkt, bevor das Rate-Limit erreicht wird. Füge ein Token unter github.com/settings/tokens hinzu (keine Berechtigungen nötig).“
Keine erforderlichen Schlüssel. Die npm-API und das npm-Registry sind vollständig öffentlich und benötigen keine Authentifizierung.
Schritt 2: Eingabe sammeln
Sammle aus dem Gespräch:
- Einen oder mehrere npm-Paketnamen (unscoped wie
esbuildoder scoped wie@hono/hono) - Optional: einen kurzen Produktkontext-String (wird für personalisierte Outreach-Nachrichten verwendet)
Wenn der Benutzer eine npmjs.com-URL angibt, extrahiere nur den Paketnamen. Bewahre den vollständigen Scoped-Namen inklusive @ und Org-Präfix – das Encoding erfolgt in Schritt 3.
Wenn keine Pakete angegeben werden: Frage: „Welche npm-Pakete möchtest du analysieren? Gib eigene, Konkurrenz- oder eine Mischung an. Beispiel: esbuild, @hono/hono, zod, valibot“
python3 << 'PYEOF'
import json, sys
packages_raw = "PACKAGES_HERE" # komma- oder zeilenumbruch-getrennt
product_context = "CONTEXT_HERE" # optional, kann leerer String sein
packages = [p.strip() for p in packages_raw.replace("\n", ",").split(",") if p.strip()]
if not packages:
print("ERROR: Keine Pakete angegeben.")
sys.exit(1)
print(f"Zu analysierende Pakete: {len(packages)}")
for p in packages:
print(f" {p}")
with open("/tmp/npl-input.json", "w") as f:
json.dump({"packages": packages, "product_context": product_context}, f)
PYEOF
Schritt 3: 12-Wochen-Download-Daten abrufen
Verwende das eigenständige Skript, falls verfügbar – es erledigt die Schritte 3, 4 und 5 in einem Aufruf, sodass du die Inline-Code-Blöcke unten nicht ausführen musst.
# Prüfe, ob das Skript existiert
ls scripts/fetch.py 2>/dev/null && echo "Skript verfügbar" || echo "Skript nicht gefunden"
Wenn das Skript verfügbar ist, führe es direkt aus und springe zu Schritt 6:
python3 scripts/fetch.py PAKETE_HIER --context "KONTEXT_HIER" --output /tmp/npl-script-out.json
Lade dann die Ausgabe in das Format, das Schritt 6 erwartet:
python3 << 'PYEOF'
import json
out = json.load(open("/tmp/npl-script-out.json"))
# Skript-Ausgabe hat ein results-Array – teile es in scored und enriched für Schritte 6-8 auf
enriched = [r for r in out["results"] if "profile" in r]
scored = out["results"]
json.dump(scored, open("/tmp/npl-scored.json", "w"), indent=2)
json.dump(enriched, open("/tmp/npl-enriched.json", "w"), indent=2)
json.dump({"packages": [r["package"] for r in scored], "product_context": out.get("product_context", "")},
open("/tmp/npl-input.json", "w"), indent=2)
print(f"Geladen: {len(scored)} Pakete | {out['breakout_count']} Breakout | {out['watching_count']} Watching")
PYEOF
Wenn das Skript nicht verfügbar ist, führe den Inline-Code unten aus.
Rufe tägliche Download-Daten für jedes Paket von der npm Downloads API ab. Aggregiere zu wöchentlichen Buckets.
python3 << 'PYEOF'
import json, urllib.request, sys, time
from datetime import datetime, timedelta, timezone
from collections import defaultdict
import urllib.parse
data = json.load(open("/tmp/npl-input.json"))
packages = data["packages"]
end_date = datetime.now(tz=timezone.utc)
start_date = end_date - timedelta(weeks=13) # extra Pufferwoche für Teilwochen
start_str = start_date.strftime("%Y-%m-%d")
end_str = end_date.strftime("%Y-%m-%d")
results = []
failed = []
for pkg in packages:
# URL-Encoding für Scoped Packages: @ -> %40, / -> %2F
encoded = pkg.replace("@", "%40").replace("/", "%2F")
url = f"https://api.npmjs.org/downloads/range/{start_str}:{end_str}/{encoded}"
try:
req = urllib.request.Request(url, headers={"User-Agent": "npm-downloads-to-leads/1.0"})
with urllib.request.urlopen(req, timeout=20) as resp:
raw = json.loads(resp.read())
# Täglich auf ISO-Woche aggregieren
weekly = defaultdict(int)
for entry in raw.get("downloads", []):
day = datetime.strptime(entry["day"], "%Y-%m-%d")
week_key = day.isocalendar()[:2] # (year, week_num)
weekly[week_key] += entry["downloads"]
weeks = [v for k, v in sorted(weekly.items())]
# Nimm die letzten 12 vollständigen wöchentlichen Buckets
weeks = weeks[-12:]
results.append({
"package": pkg,
"weeks": weeks,
"total_weeks": len(weeks),
"current_weekly": weeks[-1] if weeks else 0,
"status": "ok"
})
print(f" {pkg}: {len(weeks)} Wochen, letzte Woche {weeks[-1]:,} Downloads")
except urllib.error.HTTPError as e:
if e.code == 404:
failed.append(pkg)
results.append({"package": pkg, "weeks": [], "total_weeks": 0, "current_weekly": 0, "status": "not_found"})
print(f" {pkg}: NICHT GEFUNDEN (404) – wird übersprungen")
else:
failed.append(pkg)
results.append({"package": pkg, "weeks": [], "total_weeks": 0, "current_weekly": 0, "status": f"error_{e.code}"})
print(f" {pkg}: HTTP {e.code} Fehler")
except Exception as e:
failed.append(pkg)
results.append({"package": pkg, "weeks": [], "total_weeks": 0, "current_weekly": 0, "status": f"error"})
print(f" {pkg}: Abruf fehlgeschlagen ({e})")
time.sleep(0.2) # sanftes Rate-Limiting
json.dump(results, open("/tmp/npl-download-data.json", "w"), indent=2)
print(f"\nAbruf abgeschlossen. OK: {len(results) - len(failed)} | Fehlgeschlagen/Nicht gefunden: {len(failed)}")
if failed:
print(f"Übersprungen: {', '.join(failed)}")
PYEOF
Wenn alle Pakete 404 oder Fehler zurückgeben: Stoppe. Sage dem Benutzer: „Es konnten keine Download-Daten abgerufen werden. Überprüfe, ob die Paketnamen korrekt sind und auf npmjs.com existieren. Scoped Packages müssen den vollständigen Namen enthalten: @org/package.“
Schritt 4: Velocity-Scores berechnen
Kein API-Aufruf. Reines Python. Berechne Velocity-Score, Wachstumsrate und klassifiziere jedes Paket.
python3 << 'PYEOF'
import json
raw_results = json.load(open("/tmp/npl-download-data.json"))
scored = []
for item in raw_results:
pkg = item["package"]
weeks = item["weeks"]
status = item["status"]
if status != "ok" or len(weeks) < 4:
scored.append({**item, "velocity_score": 0, "growth_pct": 0, "tier": "insufficient_data",
"recent_4_avg": 0, "prior_4_avg": 0})
continue
recent_4 = sum(weeks[-4:]) / 4
prior_4 = sum(weeks[-8:-4]) / max(len(weeks) - 4, 1) if len(weeks) >= 8 else sum(weeks[:4]) / max(len(weeks[:4]), 1)
recent_2 = sum(weeks[-2:]) / 2
mid_2 = sum(weeks[-4:-2]) / 2 if len(weeks) >= 4 else recent_2
growth_ratio = recent_4 / max(prior_4, 1)
acceleration = recent_2 / max(mid_2, 1)
growth_pct = round((growth_ratio - 1) * 100, 1)
# Sweet-Spot-Multiplikator: 500-500K wöchentliche Downloads
if recent_4 < 500:
noise_factor = max(recent_4 / 500, 0.1)
elif recent_4 > 500_000:
noise_factor = max(500_000 / recent_4, 0.1)
else:
noise_factor = 1.0
velocity_score = round(growth_ratio * acceleration * noise_factor * 100, 1)
# Klassifizierung
if velocity_score > 80 and 500 < recent_4 < 500_000 and growth_ratio >= 1.5:
tier = "breakout"
elif velocity_score > 40 and recent_4 >= 500 and growth_ratio >= 1.2:
tier = "watching"
elif recent_4 < 500:
tier = "too_early"
elif recent_4 >= 500_000:
tier = "established"
else:
tier = "steady"
scored.append({
**item,
"velocity_score": velocity_score,
"growth_pct": growth_pct,
"recent_4_avg": round(recent_4),
"prior_4_avg": round(prior_4),
"tier": tier
})
# Sortiere nach velocity_score absteigend
scored.sort(key=lambda x: x["velocity_score"], reverse=True)
json.dump(scored, open("/tmp/npl-scored.json", "w"), indent=2)
breakout = [p for p in scored if p["tier"] == "breakout"]
watching = [p for p in scored if p["tier"] == "watching"]
too_early = [p for p in scored if p["tier"] == "too_early"]
print(f"Velocity-Bewertung abgeschlossen:")
print(f" BREAKOUT: {len(breakout)}")
print(f" WATCHING: {len(watching)}")
print(f" STEADY/ESTABLISHED: {len([p for p in scored if p['tier'] in ('steady','established')])}")
print(f" TOO EARLY (<500/Woche): {len(too_early)}")
print()
for p in scored[:10]:
print(f" {p['tier'].upper():12} {p['package']:30} score={p['velocity_score']:6.1f} "
f"{p['recent_4_avg']:>8,}/wk growth={p['growth_pct']:+.0f}%")
# Stoppe, wenn nichts Analysierenswertes vorhanden ist
if not breakout and not watching:
all_too_early = all(p["tier"] in ("too_early", "insufficient_data") for p in scored)
if all_too_early:
print("\nERROR: Alle Pakete liegen unter dem Schwellenwert von 500 wöchentlichen Downloads für eine zuverlässige Velocity-Analyse.")
print("Versuche Pakete mit stärkerer Community-Adoption.")
import sys; sys.exit(1)
PYEOF
Wenn alle Pakete unter 500/Woche liegen: Stoppe mit der obigen Meldung.
Schritt 5: Maintainer-Profile abrufen
Nur für Breakout- und Watching-Pakete. Rufe npm-Registry-Metadaten und dann GitHub-Benutzerprofile ab.
python3 << 'PYEOF'
import json, urllib.request, re, os, time
scored = json.load(open("/tmp/npl-scored.json"))
token = os.environ.get("GITHUB_TOKEN", "")
gh_headers = {"Accept": "application/vnd.github+json", "User-Agent": "npm-downloads-to-leads/1.0"}
if token:
gh_headers["Authorization"] = f"Bearer {token}"
target_packages = [p for p in scored if p["tier"] in ("breakout", "watching")]
print(f"Hole Profile für {len(target_packages)} Pakete (Breakout + Watching)...")
gh_rate_remaining = 999
enriched = []
for item in target_packages:
pkg = item["package"]
profile = {"package": pkg, "npm_maintainers": [], "description": "", "keywords": [],
"github_owner": None, "github_repo": None, "github_users": [], "npm_homepage": ""}
# --- npm Registry ---
encoded = pkg.replace("@", "%40").replace("/", "%2F")
reg_url = f"https://registry.npmjs.org/{encoded}"
try:
req = urllib.request.Request(reg_url, headers={"User-Agent": "npm-downloads-to-leads/1.0"})
with urllib.request.urlopen(req, timeout=20) as resp:
reg = json.loads(resp.read())
profile["description"] = reg.get("description", "")
profile["keywords"] = (reg.get("keywords") or [])[:6]
profile["npm_homepage"] = reg.get("homepage", "")
profile["npm_maintainers"] = [m.get("name", "") for m in reg.get("maintainers", []) if m.get("name")]
# Extrahiere GitHub-Besitzer aus Repository-URL
repo_field = reg.get("repository") or {}
if isinstance(repo_field, dict):
repo_url = repo_field.get("url", "")
else:
repo_url = str(repo_field)
gh_match = re.search(r"github\.com[/:]([^/]+)/([^/.]+)", repo_url)
if gh_match:
profile["github_owner"] = gh_match.group(1)
profile["github_repo"] = gh_match.group(2).rstrip(".git")
print(f" {pkg}: Registry OK | Maintainer={profile['npm_maintainers'][:3]} | "
f"github_owner={profile['github_owner']}")
except Exception as e:
print(f" {pkg}: Registry-Abruf fehlgeschlagen ({e})")
time.sleep(0.1)
# --- GitHub-Benutzerprofile ---
candidates = []
if profile["github_owner"]:
candidates.append(profile["github_owner"])
# Versuche auch npm-Maintainer-Usernamen (oft identisch mit GitHub)
for m in profile["npm_maintainers"][:2]:
if m and m not in candidates:
candidates.append(m)
for username in candidates[:3]:
if gh_rate_remaining <= 5:
print(f" GitHub-Rate-Limit niedrig ({gh_rate_remaining} verbleibend) – überspringe {username}")
break
gh_url = f"https://api.github.com/users/{username}"
req = urllib.request.Request(gh_url, headers=gh_headers)
try:
with urllib.request.urlopen(req, timeout=15) as resp:
gh_rate_remaining = int(resp.headers.get("X-RateLimit-Remaining", 0))
user_data = json.loads(resp.read())
user_info = {
"github_login": user_data.get("login"),
"github_name": user_data.get("name") or "",
"github_bio": (user_data.get("bio") or "")[:200],
"github_location": user_data.get("location") or "",
"github_twitter": user_data.get("twitter_username") or "not found on GitHub",
"github_blog": user_data.get("blog") or "",
"github_followers": user_data.get("followers", 0),
"github_public_repos": user_data.get("public_repos", 0),
}
profile["github_users"].append(user_info)
print(f" GitHub: {username} | name={user_info['github_name']} | "
f"twitter={user_info['github_twitter'][:20]} | followers={user_info['github_followers']}")
except urllib.error.HTTPError as e:
if e.code == 403:
print(f" GitHub: Rate-Limit für {username} erreicht (403)")
break
elif e.code == 404:
print(f" GitHub: {username} nicht gefunden (404)")
else:
print(f" GitHub: HTTP {e.code} für {username}")
except Exception as e:
print(f" GitHub: Abruf für {username} fehlgeschlagen ({e})")
time.sleep(0.1)
enriched.append({**item, "profile": profile})
json.dump(enriched, open("/tmp/npl-enriched.json", "w"), indent=2)
print(f"\nProfilabruf abgeschlossen.")
print(f"Anreicherungsversuche: {len(enriched)}")
print(f"Verbleibendes GitHub-Rate-Limit: {gh_rate_remaining}")
print(f"Letzten 5 Profile: {[p['profile']['npm_maintainers'][:2] for p in enriched[-5:]]}")
# Datenqualitäts-Flags
flags = []
if gh_rate_remaining <= 10:
flags.append("GitHub-Rate-Limit fast erreicht – einige Profile könnten unvollständig sein")
if len(enriched) < len(target_packages):
flags.append(f"Nur {len(enriched)} von {len(target_packages)} Zielpaketen konnten angereichert werden")
if flags:
print(f"Datenqualitäts-Flags: {flags}")
PYEOF
Schritt 6: Lead-Brief ausgeben
Formatiere die Ergebnisse als lesbaren Lead-Brief. Jeder Eintrag enthält: Paketname, Velocity-Score, wöchentliches Wachstum, Maintainer-Details und eine Outreach-Nachricht.
python3 << 'PYEOF'
import json
scored = json.load(open("/tmp/npl-scored.json"))
enriched = json.load(open("/tmp/npl-enriched.json"))
data = json.load(open("/tmp/npl-input.json"))
product_context = data.get("product_context", "")
print("=" * 60)
print(" NPM DOWLOADS TO LEADS – LEAD-BRIEF")
print("=" * 60)
print()
data_quality_flags = []
# --- Zusammenfassung ---
all_scored = sorted(scored, key=lambda x: x["velocity_score"], reverse=True)
breakout = [p for p in all_scored if p["tier"] == "breakout"]
watching = [p for p in all_scored if p["tier"] == "watching"]
steady = [p for p in all_scored if p["tier"] in ("steady", "established")]
too_early = [p for p in all_scored if p["tier"] in ("too_early", "insufficient_data")]
print(f"Analysierte Pakete: {len(all_scored)}")
print(f" Breakout (AKTION): {len(breakout)}")
print(f" Watching (BEOBACHTEN): {len(watching)}")
print(f" Etabliert/Stabil: {len(steady)}")
print(f" Zu früh (<500/Woche): {len(too_early)}")
print()
# --- Breakout-Details ---
if breakout:
print("-" * 60)
print(" BREAKOUT-PAKETE – Höchste Priorität")
print("-" * 60)
print()
for i, p in enumerate(breakout, 1):
enr = next((e for e in enriched if e["package"] == p["package"]), None)
prof = enr.get("profile", {}) if enr else {}
maintainers = prof.get("npm_maintainers", [])
description = prof.get("description", "")
keywords = prof.get("keywords", [])
homepage = prof.get("npm_homepage", "")
gh_users = prof.get("github_users", [])
print(f" #{i}: {p['package']}")
print(f" " + "-" * 56)
print(f" Velocity-Score: {p['velocity_score']:.1f}")
print(f" Aktuell: ~{p['current_weekly']:,}/Woche (4-Wochen-Schnitt: ~{p['recent_4_avg']:,})")
print(f" Vorher: ~{p['prior_4_avg']:,}/Woche")
print(f" Wachstum: {p['growth_pct']:+.0f}%")
print(f" Beschreibung: {description[:150] if description else 'Nicht verfügbar'}")
if keywords:
print(f" Schlagwörter: {', '.join(keywords[:5])}")
if maintainers:
print(f" npm-Maintainer: {', '.join(maintainers[:4])}")
if gh_users:
for u in gh_users:
print(f" GitHub: {u['github_login']} ({u['github_name']})")
print(f" Bio: {u['github_bio'][:100] if u['github_bio'] else 'Nicht verfügbar'}")
print(f" Standort: {u['github_location'] if u['github_location'] else 'Nicht verfügbar'}")
print(f" Twitter: {u['github_twitter'] if u['github_twitter'] != 'not found on GitHub' else 'Nicht auf GitHub gefunden'}")
print(f" Blog: {u['github_blog'] if u['github_blog'] else 'Nicht verfügbar'}")
print(f" Follower: {u['github_followers']} | Repos: {u['github_public_repos']}")
# Erstelle Outreach-Nachricht
if maintainers:
maintainer_name = gh_users[0]['github_name'] if gh_users and gh_users[0]['github_name'] else maintainers[0]
outreach = f"Hallo {maintainer_name.split()[0] if ' ' in maintainer_name else maintainer_name},\n\n"
outreach += f"Ich bin auf dein Paket **{p['package']}** gestoßen und bin beeindruckt vom jüngsten Wachstum: "
outreach += f"von {p['prior_4_avg']:,} auf {p['recent_4_avg']:,} Downloads pro Woche "
outreach += f"– ein Anstieg von {p['growth_pct']:+.0f}% in den letzten Wochen. "
if description:
outreach += f"Insbesondere {description[:120]} "
if product_context:
outreach += f"\n\nDa ich an {product_context} arbeite, glaube ich, dass sich eine tolle Gelegenheit für eine Zusammenarbeit ergibt. "
outreach += "\n\nHättest du in den nächsten Tagen kurz Zeit für ein Gespräch?"
print(f"\n -> Outreach-Nachricht:")
print(f" {outreach}")
print()
# --- Watching-Details (verkürzt) ---
if watching:
print("-" * 60)
print(f" WATCHING-PAKETE ({len(watching)}) – Beobachten für zukünftige Leads")
print("-" * 60)
for p in watching[:10]:
enr = next((e for e in enriched if e["package"] == p["package"]), None)
maintainer_info = " | ".join(enr["profile"]["npm_maintainers"][:3]) if enr and enr.get("profile", {}).get("npm_maintainers") else "-"
print(f" {p['package']:30} score={p['velocity_score']:6.1f} {p['recent_4_avg']:>8,}/wk growth={p['growth_pct']:+.0f}% maintainers={maintainer_info}")
if len(watching) > 10:
print(f" ... und {len(watching) - 10} weitere Watching-Pakete")
print()
# --- Datenqualitäts-Flags sammeln ---
print("-" * 60)
print(" QUALITÄTSINDIKATOREN & HINWEISE")
print("-" * 60)
breakout_count = len(breakout)
if breakout_count == 0:
data_quality_flags.append("Keine Breakout-Pakete gefunden – überprüfe die Liste oder den Schwellenwert")
missing_github = sum(1 for e in enriched if not e.get("profile", {}).get("github_users"))
if missing_github:
data_quality_flags.append(f"{missing_github} Pakete haben keine GitHub-Benutzerdaten – das Erstellen maßgeschneiderter Outreach-Nachrichten ist eingeschränkt")
if product_context:
data_quality_flags.append(f"Verwendete Produkt- oder Projektkontext: '{product_context}'")
for flag in data_quality_flags:
print(f" * {flag}")
if not data_quality_flags:
print(" Keine Flags – Datenqualität ist gut")
print()
print("=" * 60)
print(f" SKILL ABGESCHLOSSEN – {breakout_count} Breakout-Leads gefunden")
print("=" * 60)
PYEOF
Schritt 7: Ergebnisse dem Benutzer präsentieren
Fasse nach der Ausführung von Schritt 6 die Ergebnisse in natürlicher Sprache zusammen:
- Anzahl der Breakout-Leads und ihre Namen
- Wichtigste Erkenntnisse aus den Wachstumsdaten
- Kontaktdaten für jeden Lead (Twitter, GitHub, Blog)
- Bereits formulierte Outreach-Nachrichten zur Verwendung oder Anpassung
- Qualitätsflags, wenn Daten fehlen (z. B. GitHub-Rate-Limit erreicht, keine Twitter-Informationen)
Wenn keine Breakout- und keine Watching-Pakete gefunden wurden: Sage es dem Benutzer deutlich. Biete an, die Analyse mit einer anderen Liste oder nach einer gewissen Zeit erneut durchzuführen.
Schritt 8: Wichtige Überlegungen
- GitHub-Rate-Limit: Ohne Token sind ~10 API-Aufrufe pro Stunde möglich. Das reicht für eine kleine Liste von Breakout-Paketen. Für größere Listen wird ein Token benötigt.
- Scoped Packages: Müssen URL-kodiert werden (@ → %40, / → %2F). Dieses Handbuch behandelt das korrekt.
- Pakete unter 500/Woche: Werden als „too_early“ markiert und nicht als Leads präsentiert.
- Große Frameworks: Werden als „established“ eingestuft, da ihr Wachstum selten signifikant genug ist, um einen Lead zu rechtfertigen.
- Outreach-Nachrichten: Müssen spezifisch sein. Ein Paketnamen und konkrete Zahlen sind der Schlüssel. Generische Nachrichten werden ignoriert.