Communitygithub.com

Varnan-Tech/opendirectory

Analysiert öffentliche GitHub-Repos von Mitbewerbern, filtert Issues, clustert sie in 6 Nachfragekategorien, bewertet sie und erstellt einen priorisierten Bericht zu Nachfragelücken mit GTM-Messaging-Brief.

Was ist opendirectory?

Dieses Skill extrahiert aus öffentlichen GitHub-Issues von Mitbewerber-Repos wertvolle Nachfragesignale. Es ruft über die GitHub REST API bis zu 200 offene Issues ab, filtert lokal Rauschen (Bots, PRs, null Interaktionen) und klassifiziert die verbleibenden Issues in sechs Nachfragekategorien: Feature-Lücke, Fehlermuster, UX-Beschwerde, Leistung, fehlende Integration und fehlende Dokumentation. Anschließend werden die Issues in Cluster-Themen gruppiert, ein Nachfragescore (basierend auf Reaktionen und Kommentaren) berechnet und ein priorisierter Bericht über Nachfragelücken erstellt. Das Skill generiert zudem einen präzisen GTM-Messaging-Brief mit präzisen, unveränderten Issue-Titeln. Es ist ideal für Produktmanager, Growth-Marketer und Gründer, die schnell verstehen wollen, was Nutzer von Mitbewerbern wirklich vermissen, und diese Erkenntnisse in Produktpositionierung und Vertriebsbotschaften umwandeln möchten. Das Skill arbeitet plattformunabhängig über die GitHub API und erfordert nur ein öffentliches Repo sowie optional einen GITHUB_TOKEN für höhere Ratenlimits.

Funktioniert mit✓Claude Code~Codex CLI~Cursor✓Gemini CLI
npx skills add https://github.com/Varnan-Tech/opendirectory/tree/main/skills/gh-issue-to-demand-signal

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

GitHub Issue Demand Signal (Nachfragesignal aus GitHub Issues)

Analysiere ein öffentliches GitHub-Repo eines Mitbewerbers. Rufe die offenen Issues ab. Filtere Rauschen lokal. Ordne sie 6 Nachfragekategorien zu. Bewerte sie anhand echter Interaktion. Erstelle einen priorisierten Bericht zu Nachfragelücken und einen GTM-Messaging-Brief.


Wichtige Regel: Jeder Issue-Titel im Output muss wörtlich aus der GitHub-API-Antwort stammen. Jeder Cluster-Theme-Name muss aus tatsächlichen Issue-Titeln in diesem Cluster abgeleitet werden. Wenn nach der Rauschfilterung weniger als 10 Issues übrig bleiben, stoppe und teile dem Benutzer mit – das Repo ist zu klein für eine zuverlässige Clusterung. Keine erfundenen Issue-Inhalte.


Häufige Fehler

Der Agent wird wahrscheinlich...Warum das falsch ist
Alle 200 rohen Issues ohne Filter an die KI sendenBot-Issues, PRs und Null-Interaktions-Rauschen blähen die Clusterzahlen auf und verschwenden Kontext. Filtere zuerst lokal.
Die Kommentaranzahl als primäres Nachfragesignal verwendenKommentare enthalten Maintainer-Antworten, Off-Topic-Diskussionen und Spam. reactions["+1"] ist das sauberste Käufersignal.
Issue-Titel beim Zusammenfassen von Clustern umschreibenUmschreiben verliert die genaue Sprache des Käufers, was der gesamte Zweck ist. Verwende wörtliche Issue-Titel.
Nach Schritt 4 fortfahren, wenn nach dem Filtern weniger als 10 Issues übrig sindWeniger als 10 Issues bedeuten, dass das Repo zu klein ist oder die falsche URL angegeben wurde. Eine Clusterung mit spärlichen Daten erzeugt bedeutungslose Kategorien.
Pull Requests in die Analyse einbeziehenDer GitHub Issues-Endpunkt gibt auch PRs zurück. Filtere, indem du prüfst, ob der pull_request-Schlüssel im Issue-Objekt fehlt.
Ein Issue als ignorierte Nachfrage markieren, ohne alle 3 Kriterien zu prüfenAlle drei müssen zutreffen: Reaktionen >= 10, Alter >= 180 Tage, kein „planned/in-progress/roadmap“-Label. Fehlt ein Kriterium, ist das Issue disqualifiziert.

Schritt 1: Einrichtung prüfen

echo "GITHUB_TOKEN: ${GITHUB_TOKEN:-nicht gesetzt, unauthentifiziertes Ratenlimit gilt (60 Anfragen/Std)}"

Wenn GITHUB_TOKEN nicht gesetzt ist: Fahre fort. Teile dem Benutzer mit: „GITHUB_TOKEN ist nicht gesetzt. Das unauthentifizierte Ratenlimit beträgt 60 Anfragen/Stunde – genug für 2 Abrufe, bevor das Limit erreicht ist. Füge für wiederholte Nutzung einen Token unter github.com/settings/tokens hinzu (keine Bereiche für öffentliche Repos erforderlich).“


Schritt 2: Eingabe sammeln

Du benötigst:

Parse Owner und Repo aus der Eingabe:

python3 << 'PYEOF'
import re, sys, os

raw = "REPO_INPUT_HERE"

# Normalisieren zu owner/repo
if raw.startswith("http"):
    m = re.search(r"github\.com/([^/]+)/([^/?\s]+)", raw)
    if not m:
        print("ERROR: Konnte GitHub-URL nicht parsen. Erwartetes Format: https://github.com/owner/repo")
        sys.exit(1)
    owner, repo = m.group(1), m.group(2).rstrip("/")
elif "/" in raw:
    parts = raw.strip().split("/")
    owner, repo = parts[0], parts[1]
else:
    print("ERROR: Eingabe muss eine GitHub-URL oder ein owner/repo-Slug sein (z.B. vercel/next.js)")
    sys.exit(1)

print(f"Owner: {owner}")
print(f"Repo: {repo}")

with open("/tmp/ghd-target.txt", "w") as f:
    f.write(f"{owner}/{repo}")
PYEOF

Wenn das Parsen fehlschlägt: Stoppe. Frage: „Bitte gib das GitHub-Repo als URL (https://github.com/owner/repo) oder als owner/repo-Slug an (z.B. vercel/next.js).“


Schritt 3: Issues von der GitHub REST API abrufen

Rufe bis zu 200 Issues ab (2 Seiten mit je 100). Überprüfe das Ratenlimit nach dem ersten Abruf.

python3 << 'PYEOF'
import json, urllib.request, os, sys
from datetime import datetime, timezone

target = open("/tmp/ghd-target.txt").read().strip()
owner_repo = target
token = os.environ.get("GITHUB_TOKEN", "")

headers = {"Accept": "application/vnd.github+json", "User-Agent": "gh-issue-demand-signal/1.0"}
if token:
    headers["Authorization"] = f"Bearer {token}"

all_issues = []
rate_limit_hit = False

for page in [1, 2]:
    url = f"https://api.github.com/repos/{owner_repo}/issues?state=open&per_page=100&page={page}"
    req = urllib.request.Request(url, headers=headers)

    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            # Ratenlimit nach der ersten Seite prüfen
            if page == 1:
                remaining = int(resp.headers.get("X-RateLimit-Remaining", 999))
                reset_ts = resp.headers.get("X-RateLimit-Reset", "")
                if remaining == 0:
                    reset_str = datetime.fromtimestamp(int(reset_ts), tz=timezone.utc).strftime("%H:%M UTC") if reset_ts else "unbekannt"
                    print(f"ERROR: GitHub-Ratenlimit erschöpft. Setzt zurück um {reset_str}.")
                    print("Füge GITHUB_TOKEN zu deiner .env-Datei hinzu, um 5000 Anfragen/Std zu erhalten. Siehe github.com/settings/tokens (keine Bereiche erforderlich).")
                    sys.exit(1)
                print(f"Ratenlimit verbleibend: {remaining}")

                # Auf 404/403 prüfen
                status = resp.status
                if status == 404:
                    print(f"ERROR: Repo '{owner_repo}' nicht gefunden. Überprüfe die URL oder den Slug.")
                    sys.exit(1)

            page_data = json.loads(resp.read())
            if not page_data:
                print(f"Seite {page}: leer, breche ab.")
                break
            all_issues.extend(page_data)
            print(f"Seite {page}: {len(page_data)} Issues abgerufen")
    except urllib.error.HTTPError as e:
        if e.code == 404:
            print(f"ERROR: Repo '{owner_repo}' nicht gefunden (404). Überprüfe die URL oder den Slug.")
        elif e.code == 403:
            print(f"ERROR: Zugriff verweigert (403). Das Repo könnte privat sein oder das Ratenlimit wurde erreicht.")
        else:
            print(f"ERROR: GitHub-API gab HTTP {e.code} zurück")
        sys.exit(1)
    except Exception as e:
        print(f"ERROR: Fehler beim Abrufen von Seite {page}: {e}")
        sys.exit(1)

print(f"Insgesamt rohe Issues abgerufen: {len(all_issues)}")
json.dump(all_issues, open("/tmp/ghd-raw-issues.json", "w"), indent=2)
PYEOF

Wenn GitHub 404 zurückgibt: Stoppe. Teile dem Benutzer mit: „Repo nicht gefunden. Überprüfe die URL oder den Slug und versuche es erneut. Private Repos sind ohne Authentifizierung und expliziten Repo-Bereich nicht zugänglich.“

Wenn GitHub 403 mit Ratenlimit-Header zurückgibt: Stoppe. Zeige die Rückkehrzeit und teile dem Benutzer mit, GITHUB_TOKEN hinzuzufügen.


Schritt 4: Lokale Vorverarbeitung – Filtern, Bewerten, Ignorierte Nachfrage erkennen

Kein API-Aufruf. Reines Python. Führe aus, bevor etwas an die KI gesendet wird.

python3 << 'PYEOF'
import json, re
from datetime import datetime, timezone

raw = json.load(open("/tmp/ghd-raw-issues.json"))
target = open("/tmp/ghd-target.txt").read().strip()
now = datetime.now(tz=timezone.utc)

noise_patterns = re.compile(
    r"^(chore|deps|bump|renovate|dependabot|release|ci|build|revert)[\s:\[]",
    re.IGNORECASE
)
pr_title_patterns = re.compile(
    r"^(feat|fix|refactor|docs|test|style|perf|chore)(\(.+\))?:",
    re.IGNORECASE
)

filtered = []
noise_count = 0
noise_reasons = {}

for issue in raw:
    # Pull Requests überspringen (GitHub Issues-Endpunkt gibt auch PRs zurück)
    if "pull_request" in issue:
        noise_count += 1
        noise_reasons["pull_request"] = noise_reasons.get("pull_request", 0) + 1
        continue

    title = issue.get("title", "")
    reactions = issue.get("reactions", {}).get("+1", 0)
    comments = issue.get("comments", 0)
    user_type = (issue.get("user") or {}).get("type", "User")

    # Von Bots erstellte Issues überspringen
    if user_type == "Bot":
        noise_count += 1
        noise_reasons["bot_author"] = noise_reasons.get("bot_author", 0) + 1
        continue

    # Bot-Muster-Titel überspringen
    if noise_patterns.match(title):
        noise_count += 1
        noise_reasons["bot_title"] = noise_reasons.get("bot_title", 0) + 1
        continue

    # PR-als-Issue-Titel überspringen
    if pr_title_patterns.match(title):
        noise_count += 1
        noise_reasons["pr_as_issue"] = noise_reasons.get("pr_as_issue", 0) + 1
        continue

    # Null-Signal-Issues überspringen
    if reactions == 0 and comments == 0:
        noise_count += 1
        noise_reasons["zero_signal"] = noise_reasons.get("zero_signal", 0) + 1
        continue

    # Nachfragescore berechnen
    demand_score = (reactions * 2) + (comments * 0.5)

    # Ignorierte Nachfrage erkennen
    created_at = issue.get("created_at", "")
    if created_at:
        created = datetime.fromisoformat(created_at.replace("Z", "+00:00"))
        age_days = (now - created).days
    else:
        age_days = 0

    labels = [l.get("name", "").lower() for l in issue.get("labels", [])]
    has_planned_label = any(
        kw in label for label in labels
        for kw in ["in-progress", "planned", "roadmap", "wip", "in progress"]
    )

    ignored_demand = (
        reactions >= 10 and
        age_days >= 180 and
        not has_planned_label
    )

    filtered.append({
        "number": issue["number"],
        "title": title,
        "url": issue.get("html_url", f"https://github.com/{target}/issues/{issue['number']}"),
        "reactions_plus1": reactions,
        "comments": comments,
        "demand_score": demand_score,
        "age_days": age_days,
        "labels": labels,
        "ignored_demand": ignored_demand,
        "body_snippet": (issue.get("body") or "")[:300]
    })

# Nach Demand-Score absteigend sortieren
filtered.sort(key=lambda x: x["demand_score"], reverse=True)

print(f"Rohe Issues: {len(raw)}")
print(f"Rauschen gefiltert: {noise_count} ({', '.join(f'{k}: {v}' for k, v in noise_reasons.items())})")
print(f"Issues zur Analyse: {len(filtered)}")

if len(filtered) < 10:
    print(f"ERROR: Nur {len(filtered)} Issues nach dem Filtern übrig. Dieses Repo hat zu wenige engagierte Issues für eine zuverlässige Clusterung.")
    print("Versuche ein größeres Repo oder ein Repo mit mehr Community-Engagement.")
    import sys; sys.exit(1)

# Zusammenfassung ignorierter Nachfrage
ignored = [i for i in filtered if i["ignored_demand"]]
print(f"Ignorierte Nachfrage-Issues: {len(ignored)}")

json.dump(filtered, open("/tmp/ghd-filtered-issues.json", "w"), indent=2)
print("Vorverarbeitung abgeschlossen.")
PYEOF

Wenn nach dem Filtern weniger als 10 Issues übrig sind: Stoppe. Teile dem Benutzer genau mit, wie viele Issues gefunden und gefiltert wurden und warum das Repo für eine zuverlässige Nachfrageclusterung zu klein ist.


Schritt 5: Issues clustern

Gib die gefilterten Issues zur Analyse aus:

python3 << 'PYEOF'
import json
filtered = json.load(open("/tmp/ghd-filtered-issues.json"))
target = open("/tmp/ghd-target.txt").read().strip()

issue_list = filtered[:150]
print(f"Repo: {target}")
print(f"Zu clusternde Issues: {len(issue_list)}")
print()
for i in issue_list:
    labels_str = f" [{', '.join(i['labels'][:3])}]" if i['labels'] else ""
    print(f"#{i['number']} [Score:{round(i['demand_score'],1)} Reaktionen:{i['reactions_plus1']}] {i['title']}{labels_str}"
PYEOF

Klassifiziere jedes oben ausgegebene Issue in eine dieser 6 Kategorien: feature_gap, bug_pattern, ux_complaint, performance, integration_missing, docs_missing

Regeln:

  • Klassifiziere jedes Issue in genau eine Kategorie
  • Extrahiere eine ein-sätzige Schmerzaussage unter Verwendung der genauen Sprache des Benutzers aus dem Titel – nicht umschreiben
  • Identifiziere 5–8 Cluster-Themen: kurze Phrasen (3–6 Wörter), die die dominierenden Beschwerdemuster über alle Issues hinweg erfassen
  • Keine Gedankenstriche. Keine Marketingsprache.

Schreibe deine Analyse in /tmp/ghd-clusters.json mit dieser exakten Struktur:

{
  "classified_issues": [
    {"number": 123, "category": "feature_gap", "pain_statement": "Benutzer benötigen X, das es noch nicht gibt"}
  ],
  "cluster_themes": [
    {"theme_name": "Fehlende Exportoptionen", "category": "feature_gap", "issue_numbers": [123, 456, 789]}
  ],
  "category_counts": {"feature_gap": 5, "bug_pattern": 3, "ux_complaint": 4, "performance": 2, "integration_missing": 6, "docs_missing": 1}
}

Bestätige nach dem Schreiben der Datei mit:

python3 -c "
import json
d = json.load(open('/tmp/ghd-clusters.json'))
print(f'Klassifiziert: {len(d[\"classified_issues\"])} Issues, {len(d[\"cluster_themes\"])} Themen')
print('Kategorien:', d['category_counts'])
"

Schritt 6: Messaging-Brief

Berechne den gesamten Nachfragescore pro Cluster und gib die Top 3 aus:

python3 << 'PYEOF'
import json

filtered = json.load(open("/tmp/ghd-filtered-issues.json"))
clusters = json.load(open("/tmp/ghd-clusters.json"))
target = open("/tmp/ghd-target.txt").read().strip()

demand_by_issue = {i["number"]: i["demand_score"] for i in filtered}
issue_titles = {i["number"]: i["title"] for i in filtered}
issue_reactions = {i["number"]: i["reactions_plus1"] for i in filtered}

enriched_themes = []
for theme in clusters.get("cluster_themes", []):
    issue_nums = theme.get("issue_numbers", [])
    total_demand = sum(demand_by_issue.get(n, 0) for n in issue_nums)
    top_issues = sorted(issue_nums, key=lambda n: demand_by_issue.get(n, 0), reverse=True)[:3]
    enriched_themes.append({
        "theme_name": theme["theme_name"],
        "category": theme["category"],
        "issue_count": len(issue_nums),
        "total_demand_score": round(total_demand, 1),
        "top_issues": [
            {"number": n, "title": issue_titles.get(n, ""), "reactions": issue_reactions.get(n, 0)}
            for n in top_issues
        ]
    })

enriched_themes.sort(key=lambda x: x["total_demand_score"], reverse=True)
json.dump(enriched_themes, open("/tmp/ghd-enriched-themes.json", "w"), indent=2)

print(f"Top 3 Cluster für Messaging-Brief (Repo: {target}):")
for t in enriched_themes[:3]:
    print(f"\n  {t['theme_name']} ({t['category']}) -- Gesamtnachfrage: {t['total_demand_score']}")
    for ti in t["top_issues"]:
        print(f"    #{ti['number']}: \"{ti['title']}\" ({ti['reactions']} Reaktionen)")
PYEOF

Generiere einen GTM-Messaging-Brief aus den oben ausgegebenen Top-3-Clustern.

Regeln:

  • Jeder Positionierungswinkel muss das spezifische Cluster nennen, aus dem er stammt
  • Jeder Outreach-Hook muss einen wörtlichen Issue-Titel in Anführungszeichen zitieren
  • Überschriften müssen eine Zahl oder einen spezifischen benannten Schmerz enthalten – keine allgemeinen Aussagen
  • Keine Gedankenstriche. Keine verbotenen Wörter: leistungsstark, robust, nahtlos, innovativ, revolutionär, optimieren, nutzen, transformieren

Schreibe deinen Brief in /tmp/ghd-brief.json mit dieser exakten Struktur:

…

[gekürzt für die Übersetzung]

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

Varnan-Tech/opendirectory

Use when the user asks to generate a blog cover image, thumbnail, or article header. Automatically uses modern typography, brand logos, and Google Search grounding to create beautiful 16:9 images with Gemini 3.1 Flash Image Preview.

Varnan-Tech/opendirectory

World-class brand strategist and naming expert. Uses an interrogation-led discovery phase to extract your brand's DNA, then applies scientific naming frameworks (Phonosemantics) and automated multi-TLD domain checking.

Varnan-Tech/opendirectory

Use when the user asks to generate or update a project's CLAUDE or AGENTS context file from a codebase scan. Writes a focused file under 100 lines containing only the non-obvious build commands, conventions, and gotchas Claude Code needs.

Varnan-Tech/opendirectory

Use when the user wants to verify cold emails, enrich a lead list, or autonomously guess email addresses from a CSV using ValidEmail.co or the open-source Reacher engine.

Varnan-Tech/opendirectory

Competitive intelligence orchestrator tracking companies across 8+ platforms (GitHub, Twitter, Reddit, HN, PH, YC Jobs) with heat scores and AI briefings.

Varnan-Tech/opendirectory

Give it your product URL or description. It finds your top 5 competitors, runs three-track PR research across all of them (editorial, podcasts, communities), identifies which channels appear most frequently, looks up the journalist or host for each, and returns a tiered outreach list with story angles and ready-to-send cold pitch drafts tailored to your product. Use when asked to find PR opportunities, discover where competitors got featured, build a media outreach list, find which journalists cover my space, or get pitch templates for press coverage.

Varnan-Tech/opendirectory

Generate high-converting, deep-dive growth case studies in MDX format. Use this skill when asked to write a case study or blog post about a company's growth, tech stack, or product-led strategy. It handles the full pipeline (researching the company via Tavily, generating a 16:9 cover image, quality checking the draft, uploading assets to cloud storage, and pushing directly to the target repository).

Varnan-Tech/opendirectory

Scans your project for outdated npm, pip, Cargo, Go, or Ruby packages. Runs a CVE security audit. Fetches changelogs, summarizes breaking changes with Gemini, and opens one PR per risk group (patch, minor, major). Includes Diagnosis Mode for install conflicts. Use when asked to update dependencies, check for outdated packages, open dependency PRs, scan for package updates, audit for CVEs, or flag breaking changes in upgrades. Trigger when a user says "check for outdated packages", "update my dependencies", "open PRs for dependency updates", "scan for CVEs", or "which packages need upgrading".

Varnan-Tech/opendirectory

Generates and updates README.md and API reference docs by reading your codebase's functions, routes, types, schemas, and architecture. Uses graphify to build a knowledge graph first, then writes accurate docs from it. Use when asked to write docs, generate a README, document an API, update stale docs, create an API reference from code, add an architecture section, or document a project in any language. Trigger when a user says their docs are missing, outdated, or wants to document their codebase without writing it manually.

Varnan-Tech/opendirectory

Evaluates expired domain candidates against a target niche, scores them by topical relevance, historical activity level, and history cleanliness, then outputs a ranked shortlist with explainable reasoning and risk flags.

Varnan-Tech/opendirectory

Brutally honest developer-experience audit for a GitHub repo. Scores 10 DX dimensions (time-to-first-success, README clarity, visual proof, install, quick-start, docs, examples, community, trust, marketing), writes a shareable roast in the requested tone (brutal/honest/kind), produces a prioritized action plan ranked by impact × effort, and sketches an ideal README. Trigger when user says "roast my repo", "audit my README", "dx audit", "developer experience review", "score my GitHub project", "before launch checklist", or "make my repo shareable".

Varnan-Tech/opendirectory

Drafts and designs a complete HTML email newsletter from a topic or content brief. Outputs paste-ready HTML for Loops, Mailchimp, Beehiiv, Resend, or any standard email platform. Includes subject line options and plain-text fallback. Trigger when a user says "write a newsletter", "draft an email newsletter", "create an HTML email", "design an email for my subscribers", or "write a newsletter about [topic]".

Varnan-Tech/opendirectory

Takes a GitHub PR URL or the current branch and writes a plain-English explanation of what it does and why, then posts it as a PR comment. Use when asked to explain a PR, summarize a pull request, write a plain-English description of a PR, add a summary comment to a PR, or understand what a PR changes. Trigger when a user says "explain this PR", "summarize this pull request", "what does this PR do", "add a comment explaining the PR", or shares a GitHub PR URL and asks what it does.

Varnan-Tech/opendirectory

Audit how often LLMs recommend your brand vs competitors and generate a GEO action plan.

Varnan-Tech/opendirectory

Find recurring confusion in your repo's GitHub Discussions, rank it by urgency, and draft the actual docs fixes and content angles — with verbatim community quotes and source links as evidence.

Varnan-Tech/opendirectory

SEO keyword research workflow for blog generation using Google Trends data. Use when writing blog posts, planning content calendars, or optimizing articles for search engines. Finds breakout keywords, builds content structure, and generates SEO-optimized blog outlines targeting tech and developer audiences.

Varnan-Tech/opendirectory

Generates a professionally designed case study PDF for B2B SaaS sales and marketing. Supports 7 page layouts, 9 style presets, 1-4 page output. Trigger when user says "create a case study", "write a customer story", "make a case study PDF", "design a success story", "turn this customer win into a PDF".

Varnan-Tech/opendirectory

Generates data visualization charts (bar, line, area, pie, doughnut, scatter, radar, treemap) as PNG using Apache ECharts v6. 1080×1080px default, 5 style presets, highlight annotations. Trigger when user says "create a chart", "visualize data", "make a bar chart", "line graph", "pie chart", "data visualization", "chart this data", "plot", "graph", or "visualize these numbers".

Varnan-Tech/opendirectory

Creates professionally designed B2B SaaS e-books in HTML + CSS, exported as print-ready PDF. 3–10 pages, 9 style presets, 11 page layout types. Trigger when user says "create an ebook", "design a lead magnet", "make a PDF guide", "build a gated content piece", "write a B2B ebook", "design a white paper", "create a nurture asset", or "make a PDF report".

Varnan-Tech/opendirectory

Fetches low-star App Store and Google Play reviews, clusters them into broken-promise patterns, and generates a ranked copy brief with positioning opportunities.

Verwandte Skills