Communitygithub.com

Varnan-Tech/opendirectory

掃描競爭對手的公開 GitHub 倉庫,獲取其開放議題,過濾雜訊,將議題聚類為 6 個需求類別,計算每個議題和聚類的需求分數,並輸出排名需求缺口報告與 GTM 訊息簡報。

opendirectory 是什麼?

這是一個針對開發者工具和 AI Agent 的強大技能,用於分析競爭對手的 GitHub 公開倉庫,以識別使用者未滿足的需求。它會呼叫 GitHub REST API 獲取開放議題,並透過多層本地過濾機制(如排除機器人、PR、零互動議題)清理雜訊。隨後,它會將剩餘的議題聚類為六大類別:功能缺口、錯誤模式、 UX 抱怨、效能問題、整合缺失和文件不足,並基於 +1 反應和評論數量計算每個議題與聚類的需求分數。最終輸出包含高需求議題排名、被忽略的長期需求以及一份可直接用於產品定位與行銷的 GTM 訊息簡報。此技能支援 Claude Code、 Cursor、 Codex 等平台,並透過環境變數 `GITHUB_TOKEN` 來提升 API 速率限制,非常適合進行競爭對手分析、找出產品差異化點、或從真實使用者回饋中產生行銷素材。

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

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

GitHub Issue 需求信號

取得競爭對手的公開 GitHub 倉庫。獲取其開放議題。在本地過濾雜訊。聚類為 6 個需求類別。根據真實參與度評分。輸出排名需求缺口報告與 GTM 訊息簡報。


關鍵規則: 輸出中的每個議題標題必須直接來自 GitHub API 回應,不得修改。每個聚類主題名稱必須源自該聚類中的實際議題標題。如果雜訊過濾後剩餘的議題少於 10 個,則停止並告知使用者——該倉庫太小,無法進行可靠的聚類。禁止在任何地方使用虛構的議題內容。


常見錯誤

Agent 可能會想這麼做...為什麼這是錯的
將全部 200 個原始議題未經篩選就送給 AIBot 議題、 PR 和零參與度的雜訊會膨脹聚類數量並浪費上下文。應先在本地過濾。
使用評論數量作為主要需求信號評論包含維護者回覆、離題討論和垃圾訊息。 reactions["+1"] 是最乾淨的買家信號。
在總結聚類時改寫議題標題改寫會失去買家的原始表達,而這正是核心價值。應使用原封不動的議題標題。
在步驟 4 之後,如果過濾後剩餘少於 10 個議題仍繼續執行少於 10 個議題表示倉庫太小或提供了錯誤的 URL。在稀疏資料上進行聚類會產生無意義的分類。
在分析中包含 Pull RequestsGitHub Issues 端點也會回傳 PR。需要透過檢查 issue 物件中是否缺少 pull_request 鍵來進行過濾。
在未檢查所有 3 個條件的情況下就將議題標記為被忽略的需求三個條件必須同時成立: reactions >= 10、 age >= 180 天、沒有 planned/in-progress/roadmap 標籤。缺少任何一個條件就取消該議題的資格。

步驟 1:設定檢查

echo "GITHUB_TOKEN: ${GITHUB_TOKEN:-not set, unauthenticated rate limit applies (60 req/hr)}"

如果 GITHUB_TOKEN 未設定: 繼續執行。告訴使用者:"GITHUB_TOKEN 未設定。未經認證的速率限制為每小時 60 次請求——大約足夠在達到限制前進行 2 次獲取。如需重複使用,請到 github.com/settings/tokens 新增一個 token(存取公開倉庫不需要任何權限範圍)。"


步驟 2:收集輸入

你需要:

從輸入中解析 owner 和 repo:

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

raw = "REPO_INPUT_HERE"

# 標準化為 owner/repo 格式
if raw.startswith("http"):
    m = re.search(r"github\.com/([^/]+)/([^/?\s]+)", raw)
    if not m:
        print("ERROR: Could not parse GitHub URL. Expected 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: Input must be a GitHub URL or owner/repo slug (e.g. 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

如果解析失敗: 停止。詢問:"請提供 GitHub 倉庫作為 URL (https://github.com/owner/repo) 或 owner/repo 縮寫 (例如 vercel/next.js)。"


步驟 3:從 GitHub REST API 獲取議題

獲取最多 200 個議題(2 頁,每頁 100 個)。在第一次獲取後檢查速率限制。

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:
            # 在第一頁之後檢查速率限制
            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 "unknown"
                    print(f"ERROR: GitHub rate limit exhausted. Resets at {reset_str}.")
                    print("Add GITHUB_TOKEN to your .env file to get 5000 req/hr. See github.com/settings/tokens (no scopes needed).")
                    sys.exit(1)
                print(f"Rate limit remaining: {remaining}")

                # 檢查 404/403 狀態
                status = resp.status
                if status == 404:
                    print(f"ERROR: Repo '{owner_repo}' not found. Check the URL or slug.")
                    sys.exit(1)

            page_data = json.loads(resp.read())
            if not page_data:
                print(f"Page {page}: empty, stopping.")
                break
            all_issues.extend(page_data)
            print(f"Page {page}: {len(page_data)} issues fetched")
    except urllib.error.HTTPError as e:
        if e.code == 404:
            print(f"ERROR: Repo '{owner_repo}' not found (404). Check the URL or slug.")
        elif e.code == 403:
            print(f"ERROR: Access denied (403). Repo may be private or rate limit hit.")
        else:
            print(f"ERROR: GitHub API returned HTTP {e.code}")
        sys.exit(1)
    except Exception as e:
        print(f"ERROR: Failed to fetch page {page}: {e}")
        sys.exit(1)

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

如果 GitHub 回傳 404: 停止。告訴使用者:"找不到倉庫。請檢查 URL 或縮寫並重試。若未經認證和明確的 repo 權限,無法存取私有倉庫。"

如果 GitHub 回傳 403 並附帶速率限制標頭: 停止。顯示重置時間,並告訴使用者新增 GITHUB_TOKEN。


步驟 4:在本地進行預處理——過濾、評分、檢測被忽略的需求

不進行 API 呼叫。純 Python。在將任何資料送給 AI 之前執行。

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(GitHub Issues 端點也會回傳 PR)
    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")

    # 跳過機器人建立的議題
    if user_type == "Bot":
        noise_count += 1
        noise_reasons["bot_author"] = noise_reasons.get("bot_author", 0) + 1
        continue

    # 跳過機器人模式的標題
    if noise_patterns.match(title):
        noise_count += 1
        noise_reasons["bot_title"] = noise_reasons.get("bot_title", 0) + 1
        continue

    # 跳過 PR 風格的議題標題
    if pr_title_patterns.match(title):
        noise_count += 1
        noise_reasons["pr_as_issue"] = noise_reasons.get("pr_as_issue", 0) + 1
        continue

    # 跳過零信號的議題
    if reactions == 0 and comments == 0:
        noise_count += 1
        noise_reasons["zero_signal"] = noise_reasons.get("zero_signal", 0) + 1
        continue

    # 計算需求分數
    demand_score = (reactions * 2) + (comments * 0.5)

    # 檢測被忽略的需求
    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]
    })

# 按需求分數降序排序
filtered.sort(key=lambda x: x["demand_score"], reverse=True)

print(f"Raw issues: {len(raw)}")
print(f"Noise filtered: {noise_count} ({', '.join(f'{k}: {v}' for k, v in noise_reasons.items())})")
print(f"Issues for analysis: {len(filtered)}")

if len(filtered) < 10:
    print(f"ERROR: Only {len(filtered)} issues remain after filtering. This repo has too few engaged issues for reliable clustering.")
    print("Try a larger repo or a repo with more community engagement.")
    import sys; sys.exit(1)

# 被忽略的需求摘要
ignored = [i for i in filtered if i["ignored_demand"]]
print(f"Ignored demand issues: {len(ignored)}")

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

如果過濾後剩餘少於 10 個議題: 停止。告訴使用者總共找到和過濾了多少議題,以及為什麼該倉庫對於可靠的需求聚類來說太小了。


步驟 5:聚類議題

列印已過濾的議題以供分析:

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"Issues to cluster: {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)} reactions:{i['reactions_plus1']}] {i['title']}{labels_str}")
PYEOF

將上面列印的每個議題分類到以下 6 個類別之一: feature_gap、 bug_pattern、 ux_complaint、 performance、 integration_missing、 docs_missing

規則:

  • 將每個議題分類到恰好一個類別
  • 使用使用者標題中的原始語言提取一句話的痛苦陳述——不要改寫
  • 識別 5-8 個聚類主題:簡短詞組(3-6 個單字),捕捉所有議題中的主要抱怨模式
  • 不要使用破折號。不要使用行銷語言。

將你的分析寫入 /tmp/ghd-clusters.json,使用以下精確結構:

{
  "classified_issues": [
    {"number": 123, "category": "feature_gap", "pain_statement": "Users need X which does not exist yet"}
  ],
  "cluster_themes": [
    {"theme_name": "Missing export options", "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}
}

寫入檔案後,使用以下指令確認:

python3 -c "
import json
d = json.load(open('/tmp/ghd-clusters.json'))
print(f'Classified: {len(d[\"classified_issues\"])} issues, {len(d[\"cluster_themes\"])} themes')
print('Categories:', d['category_counts'])
"

步驟 6:訊息簡報

計算每個聚類的總需求分數並列印前 3 名:

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 clusters for messaging brief (repo: {target}):")
for t in enriched_themes[:3]:
    print(f"\n  {t['theme_name']} ({t['category']}) -- total demand: {t['total_demand_score']}")
    for ti in t["top_issues"]:
        print(f"    #{ti['number']}: \"{ti['title']}\" ({ti['reactions']} reactions)")
PYEOF

根據上面列印的前 3 個聚類生成 GTM 訊息簡報。

規則:

  • 每個定位角度必須引用其來源的特定聚類
  • 每個外展鉤子必須引用一個完全一致的議題標題,並加上引號
  • 標題必須包含一個數字或具體命名的痛苦——禁止使用通用陳述
  • 不要使用破折號。禁止使用以下詞彙:powerful、 robust、 seamless、 innovative、 game-changing、 streamline、 leverage、 transform

將你的簡報寫入 /tmp/ghd-brief.json,使用以下精確結構:

...

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.

相關技能